From 00a923b14ac2282f3ba09e51079daa3220bb7598 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 17 Dec 2002 20:49:45 +0000 Subject: [PATCH] *** empty log message *** SVN Revision: 18 --- src/ejabberd_c2s.erl | 226 +++++++++++++++++++++++++++++++++++++---- src/ejabberd_local.erl | 2 + src/ejabberd_sm.erl | 185 +++++++++++++++------------------ src/jlib.erl | 11 +- src/mod_roster.erl | 37 ++++++- 5 files changed, 336 insertions(+), 125 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 635873953..c1bb2666b 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -25,10 +25,19 @@ -include("ejabberd.hrl"). --record(state, {socket, sender, receiver, streamid, - user = "", server = ?MYNAME, resource = ""}). +-define(SETS, gb_sets). --define(DBGFSM, true). +-record(state, {socket, sender, receiver, streamid, + user = "", server = ?MYNAME, resource = "", + pres_t = ?SETS:new(), + pres_f = ?SETS:new(), + pres_a = ?SETS:new(), + pres_i = ?SETS:new(), + pres_last, pres_pri, + pres_timestamp, + pres_invis = false}). + +%-define(DBGFSM, true). -ifdef(DBGFSM). -define(FSMOPTS, [{debug, [trace]}]). @@ -110,8 +119,12 @@ wait_for_auth({xmlstreamelement, El}, StateData) -> ejabberd_sm:open_session(U, R), Res = jlib:make_result_iq_reply(El), send_element(StateData#state.sender, Res), + {Fs, Ts} = mod_roster:get_subscription_lists(U), {next_state, session_established, - StateData#state{user = U, resource = R}}; + StateData#state{user = U, + resource = R, + pres_f = ?SETS:from_list(Fs), + pres_t = ?SETS:from_list(Ts)}}; _ -> Err = jlib:make_error_reply(El, "401", "Unauthorized"), send_element(StateData#state.sender, Err), @@ -143,26 +156,39 @@ wait_for_auth(closed, StateData) -> session_established({xmlstreamelement, El}, StateData) -> {xmlelement, Name, Attrs, Els} = El, - % TODO + Server = StateData#state.server, FromJID = {StateData#state.user, - StateData#state.server, + Server, StateData#state.resource}, To = xml:get_attr_s("to", Attrs), ToJID = case To of "" -> - {"", StateData#state.server, ""}; + {"", Server, ""}; _ -> jlib:string_to_jid(To) end, - case ToJID of - error -> - % TODO - error; - _ -> - %?DEBUG("FromJID=~w, ToJID=~w, El=~w~n", [FromJID, ToJID, El]), - ejabberd_router:route(FromJID, ToJID, El) - end, - {next_state, session_established, StateData}; + NewState = + case ToJID of + error -> + % TODO + StateData; + _ -> + case Name of + "presence" -> + case ToJID of + {"", Server, ""} -> + ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)", + [FromJID, El, StateData]), + presence_update(FromJID, El, StateData); + _ -> + StateData + end; + _ -> + ejabberd_router:route(FromJID, ToJID, El), + StateData + end + end, + {next_state, session_established, NewState}; session_established({xmlstreamend, Name}, StateData) -> {stop, normal, StateData}; @@ -216,7 +242,51 @@ handle_sync_event(Event, From, StateName, StateData) -> %%---------------------------------------------------------------------- handle_info({send_text, Text}, StateName, StateData) -> send_text(StateData#state.sender, Text), - {next_state, StateName, StateData}. + {next_state, StateName, StateData}; +handle_info({route, From, To, Packet}, StateName, StateData) -> + {xmlelement, Name, Attrs, Els} = Packet, + % TODO + {Pass, NewAttrs, NewState} = + case Name of + "presence" -> + case xml:get_attr_s("type", Attrs) of + "probe" -> + process_presence_probe(From, To, StateData), + {false, Attrs, StateData}; + "error" -> + case ?SETS:is_element(From, StateData#state.pres_a) of + true -> + A = ?SETS:del_element(From, + StateData#state.pres_a), + {true, Attrs, StateData#state{pres_a = A}}; + _ -> + {true, Attrs, StateData} + end; + "invisible" -> + Attrs1 = lists:keydelete("type", 1, Attrs), + {true, [{"type", "unavailable"} | Attrs1], StateData}; + "subscribe" -> + % TODO + {true, Attrs, StateData}; + "unsubscribe" -> + % TODO + {true, Attrs, StateData}; + _ -> + {true, Attrs, StateData} + end; + _ -> + {true, Attrs, StateData} + end, + if Pass -> + Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From), + jlib:jid_to_string(To), + NewAttrs), + Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}), + send_text(StateData#state.sender, Text); + true -> + ok + end, + {next_state, StateName, NewState}. %%---------------------------------------------------------------------- %% Func: terminate/3 @@ -314,5 +384,127 @@ get_auth_tags([], U, P, D, R) -> {U, P, D, R}. +process_presence_probe(From, To, StateData) -> + case StateData#state.pres_last of + undefined -> + ok; + _ -> + Cond1 = (not StateData#state.pres_invis) + and ?SETS:is_element(From, StateData#state.pres_f) + and (not ?SETS:is_element(From, StateData#state.pres_i)), + Cond2 = StateData#state.pres_invis + and ?SETS:is_element(From, StateData#state.pres_f) + and ?SETS:is_element(From, StateData#state.pres_a), + if + Cond1 -> + ejabberd_router:route(To, From, + StateData#state.pres_last); + Cond2 -> + ejabberd_router:route(To, From, + {xmlelement, "presence", + [], + []}); + true -> + ok + end + end. + +presence_update(From, Packet, StateData) -> + {xmlelement, Name, Attrs, Els} = Packet, + case xml:get_attr_s("type", Attrs) of + "unavailable" -> + presence_broadcast(From, StateData#state.pres_a, Packet), + presence_broadcast(From, StateData#state.pres_i, Packet), + StateData#state{pres_last = undefined, + pres_a = ?SETS:new(), + pres_i = ?SETS:new(), + pres_invis = false}; + "invisible" -> + NewState = + if + not StateData#state.pres_invis -> + presence_broadcast(From, StateData#state.pres_a, + Packet), + presence_broadcast(From, StateData#state.pres_i, + Packet), + S1 = StateData#state{pres_last = undefined, + pres_a = ?SETS:new(), + pres_i = ?SETS:new(), + pres_invis = false}, + presence_broadcast_first(From, S1, Packet); + true -> + StateData + end, + + StateData; + "error" -> + StateData; + _ -> + FromUnavail = (StateData#state.pres_last == undefined) or + StateData#state.pres_invis, + ?DEBUG("from unavail = ~p~n", [FromUnavail]), + NewState = + if + FromUnavail -> + % TODO: watching ourself + + presence_broadcast_first(From, StateData, Packet); + true -> + presence_broadcast_to_trusted(From, + StateData#state.pres_f, + StateData#state.pres_a, + Packet), + StateData + end, + + NewState#state{pres_last = Packet, + pres_invis = false + } + end. + + +presence_broadcast(From, JIDSet, Packet) -> + lists:foreach(fun(JID) -> + ejabberd_router:route(From, JID, Packet) + end, ?SETS:to_list(JIDSet)). + +presence_broadcast_to_trusted(From, T, A, Packet) -> + lists:foreach(fun(JID) -> + case ?SETS:is_element(JID, T) of + true -> + ejabberd_router:route(From, JID, Packet); + _ -> + ok + end + end, ?SETS:to_list(A)). + + +presence_broadcast_first(From, StateData, Packet) -> + {U, S, _} = From, + ?SETS:fold(fun(JID, X) -> + ejabberd_router:route({U, S, ""}, JID, + {xmlelement, "presence", + [{"type", "probe"}], + []}), + X + end, + [], + StateData#state.pres_t), + if + StateData#state.pres_invis -> + StateData; + true -> + As = ?SETS:fold(fun(JID, A) -> + ejabberd_router:route(From, JID, Packet), + ?SETS:add_element(JID, A) + end, + StateData#state.pres_a, + StateData#state.pres_f), + StateData#state{pres_a = As} + end. + + + + diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index ae6d67a4d..b75ffe08c 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -99,6 +99,8 @@ process_iq(State, From, To, Packet) -> % TODO ok end; + reply -> + ok; _ -> Err = jlib:make_error_reply(Packet, "400", "Bad Request"), ejabberd_router ! {route, diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 7eb943d8f..975be95fe 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -16,12 +16,8 @@ -include_lib("mnemosyne/include/mnemosyne.hrl"). -include("ejabberd.hrl"). --record(user_resource, {id, user, resource}). --record(user_resource_id_seq, {name = value, id}). --record(session, {id, node}). --record(mysession, {id, info}). - --record(mysession_info, {pid}). +-record(session, {ur, user, node}). +-record(mysession, {ur, pid}). start() -> @@ -29,17 +25,9 @@ start() -> init() -> register(ejabberd_sm, self()), - mnesia:create_table(user_resource_id_seq, - [{ram_copies, [node()]}, - {attributes, - record_info(fields, user_resource_id_seq)}]), - init_seq(), - mnesia:create_table(user_resource,[{ram_copies, [node()]}, - {attributes, - record_info(fields, user_resource)}]), - mnesia:add_table_index(user_resource, user), mnesia:create_table(session,[{ram_copies, [node()]}, {attributes, record_info(fields, session)}]), + mnesia:add_table_index(session, user), mnesia:add_table_index(session, node), mnesia:create_table(mysession, [{ram_copies, [node()]}, @@ -79,12 +67,9 @@ close_session(User, Resource) -> replace_alien_connection(User, Resource) -> F = fun() -> - [ID] = mnemosyne:eval(query [X.id || X <- table(user_resource), - X.user = User, - X.resource = Resource] - end), - Es = mnesia:read({session, ID}), - mnesia:write(#session{id = ID, node = node()}), + UR = {User, Resource}, + Es = mnesia:read({session, UR}), + mnesia:write(#session{ur = UR, user = User, node = node()}), Es end, case mnesia:transaction(F) of @@ -105,20 +90,16 @@ replace_alien_connection(User, Resource) -> replace_my_connection(User, Resource) -> F = fun() -> - [ID] = mnemosyne:eval(query [X.id || X <- table(user_resource), - X.user = User, - X.resource = Resource] - end), - - Es = mnesia:read({mysession, ID}), - mnesia:delete({mysession, ID}), + UR = {User, Resource}, + Es = mnesia:read({mysession, UR}), + mnesia:delete({mysession, UR}), Es end, case mnesia:transaction(F) of {atomic, Rs} -> lists:foreach( fun(R) -> - (R#mysession.info)#mysession_info.pid ! replaced + R#mysession.pid ! replaced end, Rs); _ -> false @@ -126,50 +107,24 @@ replace_my_connection(User, Resource) -> remove_connection(User, Resource) -> F = fun() -> - [ID] = mnemosyne:eval(query [X.id || X <- table(user_resource), - X.user = User, - X.resource = Resource] - end), - - mnesia:delete({mysession, ID}), - mnesia:delete({session, ID}), - mnesia:delete({user_resource, ID}) + UR = {User, Resource}, + mnesia:delete({mysession, UR}), + mnesia:delete({session, UR}) end, mnesia:transaction(F). replace_and_register_my_connection(User, Resource, Pid) -> F = fun() -> - IDs = mnemosyne:eval(query [X.id || X <- table(user_resource), - X.user = User, - X.resource = Resource] - end), - - ID = case IDs of - [Id] -> Id; - [] -> - [CurID] = - mnemosyne:eval( - query [X.id || - X <- table(user_resource_id_seq)] - end), - mnesia:write( - #user_resource_id_seq{id = CurID + 1}), - mnesia:write( - #user_resource{id = CurID, - user = User, - resource = Resource}), - CurID - end, - Es = mnesia:read({mysession, ID}), - mnesia:write(#mysession{id = ID, - info = #mysession_info{pid = Pid}}), + UR = {User, Resource}, + Es = mnesia:read({mysession, UR}), + mnesia:write(#mysession{ur = UR, pid = Pid}), Es end, case mnesia:transaction(F) of {atomic, Rs} -> lists:foreach( fun(R) -> - (R#mysession.info)#mysession_info.pid ! replaced + R#mysession.pid ! replaced end, Rs); _ -> false @@ -182,76 +137,98 @@ clean_table_from_bad_node(Node) -> lists:foreach(fun(E) -> mnesia:delete_object(session, E, write), mnesia:delete( - {user_resource, E#session.id}) + {user_resource, E#session.ur}) end, Es) end, mnesia:transaction(F). -init_seq() -> - F = fun() -> - [] = mnesia:read({user_resource_id_seq, value}), - mnesia:write(#user_resource_id_seq{id = 0}) - end, - mnesia:transaction(F). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_route(From, To, Packet) -> ?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", [From, To, Packet, 8]), - {User, _, Resource} = To, + {User, Server, Resource} = To, F = fun() -> - IDs = mnemosyne:eval(query [X.id || X <- table(user_resource), - X.user = User, - X.resource = Resource] - end), - case IDs of + UR = {User, Resource}, + Sess = mnesia:read({session, UR}), + case Sess of [] -> not_exists; - [ID] -> - case mnesia:read({mysession, ID}) of + [Ses] -> + case mnesia:read({mysession, UR}) of [] -> - [Er] = mnesia:read({session, ID}), - {remote, Er#session.node}; + {remote, Ses#session.node}; [El] -> - {local, (El#mysession.info)#mysession_info.pid} + {local, El#mysession.pid} end end end, - case mnesia:transaction(F) of - {atomic, {local, Pid}} -> - ?DEBUG("sending to process ~p~n", [Pid]), + {xmlelement, Name, Attrs, Els} = Packet, + case Resource of + "" -> % TODO - {xmlelement, Name, Attrs, Els} = Packet, - NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From), - jlib:jid_to_string(To), - Attrs), - ejabberd_c2s:send_element(Pid, {xmlelement, Name, NewAttrs, Els}), - ?DEBUG("sended~n", []), - ok; - {atomic, {remote, Node}} -> - ?DEBUG("sending to node ~p~n", [Node]), - {ejabberd_sm, Node} ! {route, From, To, Packet}, - ok; - {atomic, not_exists} -> - ?DEBUG("packet droped~n", []), - ok; - {aborted, Reason} -> - ?DEBUG("delivery failed: ~p~n", [Reason]), - false + case Name of + "presence" -> + lists:foreach( + fun(R) -> + if From /= {User, Server, R} -> + ejabberd_sm ! {route, + From, + {User, Server, R}, + Packet}; + true -> + ok + end + end, get_user_resources(User)), + ok; + "message" -> + % TODO + ok; + "iq" -> + % TODO + ok; + _ -> + ok + end; + _ -> + case mnesia:transaction(F) of + {atomic, {local, Pid}} -> + ?DEBUG("sending to process ~p~n", [Pid]), + % TODO + Pid ! {route, From, To, Packet}, + %NewAttrs = + % jlib:replace_from_to_attrs(jlib:jid_to_string(From), + % jlib:jid_to_string(To), + % Attrs), + %ejabberd_c2s:send_element( + % Pid, {xmlelement, Name, NewAttrs, Els}), + %?DEBUG("sended~n", []), + ok; + {atomic, {remote, Node}} -> + ?DEBUG("sending to node ~p~n", [Node]), + {ejabberd_sm, Node} ! {route, From, To, Packet}, + ok; + {atomic, not_exists} -> + % TODO + ?DEBUG("packet droped~n", []), + ok; + {aborted, Reason} -> + ?DEBUG("delivery failed: ~p~n", [Reason]), + false + end end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get_user_resources(User) -> F = fun() -> - mnemosyne:eval(query [X.resource || X <- table(user_resource), - X.user = User] + mnemosyne:eval(query [X.ur || X <- table(session), + X.user = User] end) end, case mnesia:transaction(F) of {atomic, Rs} -> - Rs; + lists:map(fun(R) -> element(2, R) end, Rs); {aborted, Reason} -> ?DEBUG("delivery failed: ~p~n", [Reason]), [] diff --git a/src/jlib.erl b/src/jlib.erl index aa5e3e625..62910a129 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -199,13 +199,20 @@ iq_query_info({xmlelement, Name, Attrs, Els}) when Name == "iq" -> Type1 = case Type of "set" -> set; "get" -> get; + "result" -> reply; + "error" -> reply; _ -> invalid end, if - (Type1 /= invalid) and (XMLNS /= "") -> + (Type1 /= invalid) and (Type1 /= reply) and (XMLNS /= "") -> {iq, ID, Type1, XMLNS, {xmlelement, Name2, Attrs2, Els2}}; true -> - invalid + if + Type1 == reply -> + reply; + true -> + invalid + end end; _ -> invalid diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 436e0ba12..f5c03dd49 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -12,7 +12,9 @@ -export([]). --export([start/0, init/0, process_iq/3]). +-export([start/0, init/0, + process_iq/3, + get_subscription_lists/1]). -include_lib("mnemosyne/include/mnemosyne.hrl"). -include("ejabberd.hrl"). @@ -105,6 +107,8 @@ item_to_xml(Item) -> Attrs3 = case Item#roster.subscription of none -> [{"subscription", "none"} | Attrs2]; + both -> + [{"subscription", "both"} | Attrs2]; remove -> [{"subscription", "remove"} | Attrs2]; _ -> @@ -148,7 +152,8 @@ process_item_set(User, To, XItem) -> xs = []}; [I] -> mnesia:delete_object(I), - I#roster{groups = [], + I#roster{name = "", + groups = [], xattrs = [], xs = []} end, @@ -240,3 +245,31 @@ push_item(User, Resource, From, Item) -> jlib:iq_to_xml(ResIQ)}. +get_subscription_lists(User) -> + LUser = jlib:tolower(User), + F = fun() -> + mnesia:read({roster, LUser}) + end, + case mnesia:transaction(F) of + {atomic, 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}. + +