diff --git a/TODO b/TODO index 103503880..79522cd48 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,7 @@ presence iq:register -S2S +S2S timeouts ... -iq plugins iq:browse iq:disco ... diff --git a/src/ejabberd.erl b/src/ejabberd.erl index e6e1717c6..e313cec82 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -17,8 +17,7 @@ start() -> init() -> register(ejabberd, self()), - {A1, A2, A3} = now(), - random:seed(A1,A2,A3), + randoms:start(), ok = erl_ddll:load_driver(".", expat_erl), Port = open_port({spawn, expat_erl}, [binary]), db_init(), diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index a6f083132..635873953 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -270,7 +270,7 @@ send_element(Pid, El) -> send_text(Pid, xml:element_to_string(El)). new_id() -> - lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])). + randoms:get_string(). is_auth_packet({xmlelement, Name, Attrs, Els}) when Name == "iq" -> diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 32bbdfbb1..ae6d67a4d 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -21,6 +21,7 @@ start() -> register(ejabberd_local, spawn(ejabberd_local, init, [])), mod_register:start(), + mod_roster:start(), ok. init() -> diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 8583c027d..c106bfe85 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -10,9 +10,10 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start/0, init/0, open_session/2, close_session/2, +-export([start/0, init/0, have_connection/1, - get_key/1]). + get_key/1, + try_register/1]). -include_lib("mnemosyne/include/mnemosyne.hrl"). -include("ejabberd.hrl"). @@ -59,58 +60,12 @@ loop() -> end. -open_session(User, Resource) -> - ejabberd_s2s ! {open_session, User, Resource, self()}. +%open_session(User, Resource) -> +% ejabberd_s2s ! {open_session, User, Resource, self()}. +% +%close_session(User, Resource) -> +% ejabberd_s2s ! {close_session, User, Resource}. -close_session(User, Resource) -> - ejabberd_s2s ! {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()}), -% Es -% end, -% case mnesia:transaction(F) of -% {atomic, Rs} -> -% lists:foreach( -% fun(R) -> -% if R#session.node /= node() -> -% {ejabberd_s2s, R#session.node} ! -% {replace, User, Resource}; -% true -> -% ok -% end -% end, Rs); -% _ -> -% false -% end. -% -% -%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}), -% Es -% end, -% case mnesia:transaction(F) of -% {atomic, Rs} -> -% lists:foreach( -% fun(R) -> -% (R#mysession.info)#mysession_info.pid ! replaced -% end, Rs); -% _ -> -% false -% end. remove_connection(Server) -> F = fun() -> @@ -119,43 +74,6 @@ remove_connection(Server) -> 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}}), -% Es -% end, -% case mnesia:transaction(F) of -% {atomic, Rs} -> -% lists:foreach( -% fun(R) -> -% (R#mysession.info)#mysession_info.pid ! replaced -% end, Rs); -% _ -> -% false -% end. clean_table_from_bad_node(Node) -> @@ -190,6 +108,29 @@ get_key(Server) -> "" end. +try_register(Server) -> + Key = randoms:get_string(), + F = fun() -> + case mnesia:read({s2s, Server}) of + [] -> + mnesia:write(#s2s{server = Server, + node = node(), + key = Key}), + mnesia:write(#mys2s{server = Server, + pid = self()}), + {key, Key}; + _ -> + false + end + end, + case mnesia:transaction(F) of + {atomic, Res} -> + Res; + _ -> + false + end. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -197,7 +138,7 @@ do_route(From, To, Packet) -> ?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", [From, To, Packet, 8]), {User, Server, Resource} = To, - Key = lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])), + Key = randoms:get_string(), F = fun() -> case mnesia:read({mys2s, Server}) of [] -> diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index e282c23aa..ca45cbbca 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -327,7 +327,7 @@ send_queue(Socket, Q) -> new_id() -> - lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])). + randoms:get_string(). is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" -> diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 414e588fc..b5ec1b7c0 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -20,7 +20,6 @@ open_socket/2, wait_for_stream/2, wait_for_validation/2, - wait_for_verification/2, stream_established/2, handle_info/3, terminate/3]). @@ -28,7 +27,8 @@ -include("ejabberd.hrl"). -record(state, {socket, receiver, streamid, - myself = ?MYNAME, server, type, xmlpid, queue}). + myself = ?MYNAME, server, xmlpid, queue, + new = false, verify = false}). -define(DBGFSM, true). @@ -74,9 +74,16 @@ start(Host, Type) -> %%---------------------------------------------------------------------- init([Server, Type]) -> gen_fsm:send_event(self(), init), + {New, Verify} = case Type of + {new, Key} -> + {Key, false}; + {verify, Pid, Key} -> + {false, {Pid, Key}} + end, {ok, open_socket, #state{queue = queue:new(), server = Server, - type = Type}}. + new = New, + verify = Verify}}. %%---------------------------------------------------------------------- %% Func: StateName/2 @@ -109,25 +116,41 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> % TODO case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of {"jabber:server", "jabber:server:dialback"} -> - case StateData#state.type of - {new, Key} -> + Server = StateData#state.server, + New = case StateData#state.new of + false -> + case ejabberd_s2s:try_register(Server) of + {key, Key} -> + Key; + false -> + false + end; + Key -> + Key + end, + case New of + false -> + ok; + Key1 -> send_element(StateData#state.socket, {xmlelement, "db:result", [{"from", ?MYNAME}, - {"to", StateData#state.server}], - [{xmlcdata, Key}]}), - % TODO - {next_state, wait_for_validation, StateData}; - {verify, Pid, Key} -> + {"to", Server}], + [{xmlcdata, Key1}]}) + end, + case StateData#state.verify of + false -> + ok; + {Pid, Key2} -> send_element(StateData#state.socket, {xmlelement, "db:verify", [{"from", ?MYNAME}, {"to", StateData#state.server}], - [{xmlcdata, Key}]}), - {next_state, wait_for_verification, StateData} - end; + [{xmlcdata, Key2}]}) + end, + {next_state, wait_for_validation, StateData#state{new = New}}; _ -> send_text(StateData#state.socket, ?INVALID_HEADER_ERR), {stop, normal, StateData} @@ -143,13 +166,31 @@ wait_for_validation({xmlstreamelement, El}, StateData) -> {result, To, From, Id, Type} -> case Type of "valid" -> - % TODO send_queue(StateData#state.socket, StateData#state.queue), {next_state, stream_established, StateData}; _ -> % TODO: bounce packets {stop, normal, StateData} end; + {verify, To, From, Id, Type} -> + case StateData#state.verify of + false -> + {next_state, wait_for_validation, StateData}; + {Pid, Key} -> + case Type of + "valid" -> + gen_fsm:send_event(Pid, valid); + _ -> + gen_fsm:send_event(Pid, invalid) + end, + case StateData#state.verify of + false -> + {stop, normal, StateData}; + _ -> + {next_state, wait_for_validation, + StateData#state{verify = false}} + end + end; _ -> {next_state, wait_for_validation, StateData} end; @@ -162,33 +203,23 @@ wait_for_validation(closed, StateData) -> {stop, normal, StateData}. -wait_for_verification({xmlstreamelement, El}, StateData) -> - case is_verify_res(El) of - {result, To, From, Id, Type} -> - {verify, Pid, Key} = StateData#state.type, - case Type of - "valid" -> - io:format("VALID KEY~n", []), - gen_fsm:send_event(Pid, valid); - % TODO - _ -> - % TODO - gen_fsm:send_event(Pid, invalid) - end, - {stop, normal, StateData}; - _ -> - {next_state, wait_for_verification, StateData} - end; - -wait_for_verification({xmlstreamend, Name}, StateData) -> - % TODO - {stop, normal, StateData}; - -wait_for_verification(closed, StateData) -> - {stop, normal, StateData}. - - stream_established({xmlstreamelement, El}, StateData) -> + case is_verify_res(El) of + {verify, VTo, VFrom, VId, VType} -> + case StateData#state.verify of + {VPid, VKey} -> + case VType of + "valid" -> + gen_fsm:send_event(VPid, valid); + _ -> + gen_fsm:send_event(VPid, invalid) + end; + _ -> + ok + end; + _ -> + ok + end, {xmlelement, Name, Attrs, Els} = El, % TODO From = xml:get_attr_s("from", Attrs), @@ -293,11 +324,11 @@ handle_info({tcp_error, Socket, Reason}, StateName, StateData) -> %%---------------------------------------------------------------------- terminate(Reason, StateName, StateData) -> ?DEBUG("s2s_out: terminate ~p~n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!~n", [[Reason, StateName, StateData]]), - case StateData#state.type of - {new, Key} -> - ejabberd_s2s ! {closed_conection, StateData#state.server}; - _ -> - ok + case StateData#state.new of + false -> + ok; + Key -> + ejabberd_s2s ! {closed_conection, StateData#state.server} end, case StateData#state.socket of undefined -> @@ -342,7 +373,7 @@ send_queue(Socket, Q) -> end. new_id() -> - lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])). + randoms:get_string(). bounce_messages(Reason) -> receive @@ -385,7 +416,7 @@ is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:result" -> xml:get_attr_s("id", Attrs), xml:get_attr_s("type", Attrs)}; is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:verify" -> - {result, + {verify, xml:get_attr_s("to", Attrs), xml:get_attr_s("from", Attrs), xml:get_attr_s("id", Attrs), diff --git a/src/jlib.erl b/src/jlib.erl index a829b3274..aa5e3e625 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -12,6 +12,7 @@ -export([make_result_iq_reply/1, make_error_reply/3, + make_error_element/2, make_correct_from_to_attrs/3, replace_from_to_attrs/3, replace_from_to/3, @@ -81,6 +82,11 @@ make_error_reply_attrs(Attrs) -> Attrs6 = [{"type", "error"} | Attrs5], Attrs6. +make_error_element(Code, Desc) -> + {xmlelement, "error", + [{"code", Code}], + [{xmlcdata, Desc}]}. + make_correct_from_to_attrs(From, To, Attrs) -> Attrs1 = lists:keydelete("from", 1, Attrs), Attrs2 = case xml:get_attr("to", Attrs) of diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 7e327a51e..a686f9ce0 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -12,36 +12,198 @@ -export([]). --export([start/0]). +-export([start/0, init/0, process_iq/3]). --record(roster, {user, jid, name, subscription, groups, xs}). +-include_lib("mnemosyne/include/mnemosyne.hrl"). +-include("ejabberd.hrl"). +-record(roster, {user, + jid, + name = "", + subscription = none, + groups = [], + xattrs = [], + xs = []}). + +-define(ME, ejabberd_mod_roster). start() -> + register(?ME, spawn(mod_roster, init, [])). + +init() -> mnesia:create_table(roster,[{disc_copies, [node()]}, {type, bag}, {attributes, record_info(fields, roster)}]), + mnesia:add_table_index(roster, jid), ejabberd_local:register_iq_handler("jabber:iq:roster", - ?MODULE, process_iq). - %spawn(mod_roster, init, []). + ?MODULE, process_iq), + loop(). -%init() -> -% loop(). -% -%loop() -> -% receive -% _ -> -% loop() -% end. - - -% TODO -process_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> - case Type of - set -> - {iq, ID, error, XMLNS, []}; - get -> - {iq, ID, error, XMLNS, []} +loop() -> + receive + {process_iq, From, To, {iq, ID, Type, XMLNS, SubEl}} -> + case Type of + set -> + ResIQ = process_iq_set(From, To, + {iq, ID, Type, XMLNS, SubEl}), + ejabberd_router ! {route, + To, + From, + jlib:iq_to_xml(ResIQ)}, + loop(); + get -> + ResIQ = process_iq_get(From, To, + {iq, ID, Type, XMLNS, SubEl}), + ejabberd_router ! {route, + To, + From, + jlib:iq_to_xml(ResIQ)}, + loop() + end end. + +process_iq(From, To, IQ) -> + {iq, ID, Type, XMLNS, SubEl} = IQ, + {_, Server, _} = From, + case ?MYNAME of + Server -> + ?ME ! {process_iq, From, To, IQ}, + ignore; + _ -> + {iq, ID, error, XMLNS, + [SubEl, jlib:make_error_element("404", "Not Found")]} + end. + +process_iq_get(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + {User, _, _} = From, + F = fun() -> + mnesia:read({roster, User}) + end, + case mnesia:transaction(F) of + {atomic, Items} -> + XItems = lists:map(fun item_to_xml/1, Items), + {iq, ID, result, XMLNS, XItems}; + _ -> + {iq, ID, error, XMLNS, + [SubEl, jlib:make_error_element("500", + "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]; + _ -> + % TODO + Attrs2 + end, + Attrs = Attrs3 ++ Item#roster.xattrs, + SubEls1 = lists:map(fun(G) -> + {xmlelement, "group", [], [{xmlcdata, G}]} + end, Item#roster.groups), + SubEls = SubEls1 ++ Item#roster.xs, + {xmlelement, "item", Attrs, SubEls}. + + +process_iq_set(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + {User, _, _} = From, + {xmlelement, Name, Attrs, Els} = SubEl, + lists:foreach(fun(El) -> process_item_set(User, To, El) end, Els), + {iq, ID, result, XMLNS, []}. + +process_item_set(User, To, XItem) -> + {xmlelement, Name, Attrs, Els} = XItem, + % TODO: load existing item + JID = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)), + case JID of + error -> + ok; + _ -> + F = fun() -> + Res = mnemosyne:eval(query [X || X <- table(roster), + X.user = User, + X.jid = JID] + end), + Item = case Res of + [] -> + #roster{user = User, + jid = JID, + groups = [], + xattrs = [], + xs = []}; + [I] -> + mnesia:delete_object(I), + I + end, + Item1 = process_item_attrs(Item, Attrs), + Item2 = process_item_els(Item1, Els), + mnesia:write(Item2), + Item2 + end, + case mnesia:transaction(F) of + {atomic, Item} -> + io:format("ROSTER: push for user ~p: ~p~n", [User, Item]), + ok; + _ -> + ok + end + end. + +process_item_attrs(Item, [{Attr, Val} | Attrs]) -> + case Attr of + "jid" -> + case jlib:string_to_jid(Val) of + error -> + process_item_attrs(Item, [Attrs]); + JID -> + 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]); + _ -> + XAttrs = Item#roster.xattrs, + process_item_attrs(Item#roster{xattrs = [{Attr, Val} | XAttrs]}, + [Attrs]) + end; +process_item_attrs(Item, []) -> + Item. + + +% {user, jid, name, subscription, groups, xattrs, xs} +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. diff --git a/src/randoms.erl b/src/randoms.erl new file mode 100644 index 000000000..b7e4be070 --- /dev/null +++ b/src/randoms.erl @@ -0,0 +1,42 @@ +%%%---------------------------------------------------------------------- +%%% File : randoms.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 13 Dec 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(randoms). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([get_string/0]). + +-export([start/0, init/0]). + + +start() -> + register(random_generator, spawn(randoms, init, [])). + +init() -> + {A1, A2, A3} = now(), + random:seed(A1,A2,A3), + loop(). + +loop() -> + receive + {From, get_random, N} -> + From ! {random, random:uniform(N)}, + loop(); + _ -> + loop() + end. + + +get_string() -> + random_generator ! {self(), get_random, 65536*65536}, + receive + {random, R} -> + integer_to_list(R) + end. +