From 2617433ae30dd7ce014ee68ffdecedd8c77ae4a8 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 7 Jan 2003 19:10:35 +0000 Subject: [PATCH] *** empty log message *** SVN Revision: 32 --- src/ejabberd.cfg | 17 +++ src/ejabberd.erl | 1 + src/ejabberd.hrl | 4 +- src/ejabberd_config.erl | 49 +++++++ src/ejabberd_listener.erl | 39 ++++-- src/ejabberd_router.erl | 18 ++- src/ejabberd_service.erl | 279 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 395 insertions(+), 12 deletions(-) create mode 100644 src/ejabberd.cfg create mode 100644 src/ejabberd_config.erl create mode 100644 src/ejabberd_service.erl diff --git a/src/ejabberd.cfg b/src/ejabberd.cfg new file mode 100644 index 000000000..74f6869ae --- /dev/null +++ b/src/ejabberd.cfg @@ -0,0 +1,17 @@ +% $Id$ + + +{host, "e.localhost"}. + +{listen, [{5522, ejabberd_c2s, start, []}, + {5269, ejabberd_s2s_in, start, []}, + {8888, ejabberd_service, start, ["asd.e.localhost", "asdqwe"]} + ]}. + + + + + +% Local Variables: +% mode: erlang +% End: diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 3e100a6a6..d8a9f9def 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -23,6 +23,7 @@ init() -> db_init(), sha:start(), translate:start(), + ejabberd_config:start(), ejabberd_auth:start(), ejabberd_router:start(), ejabberd_sm:start(), diff --git a/src/ejabberd.hrl b/src/ejabberd.hrl index fa2618001..975056593 100644 --- a/src/ejabberd.hrl +++ b/src/ejabberd.hrl @@ -16,7 +16,9 @@ -endif. --define(MYNAME,"e.localhost"). +%-define(MYNAME,"e.localhost"). +-define(MYNAME, ejabberd_config:get_option(host)). -define(MSGS_DIR, "msgs"). +-define(CONFIG_PATH, "ejabberd.cfg"). diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl new file mode 100644 index 000000000..5fb03ad2b --- /dev/null +++ b/src/ejabberd_config.erl @@ -0,0 +1,49 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_config.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 14 Dec 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd_config). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/0, load_file/1, get_option/1]). + +-include("ejabberd.hrl"). + +start() -> + ets:new(ejabberd_config, [named_table, public]), + load_file(?CONFIG_PATH). + + +load_file(File) -> + {ok, Bin} = file:read_file(File), + Content = binary_to_list(Bin), + parse(Content). + + +parse(String) -> + case erl_scan:tokens([], String, 0) of + {done, Result, Left} -> + {ok, Tokens, _} = Result, + {ok, Term} = erl_parse:parse_term(Tokens), + {Opt, Val} = Term, + ets:insert(ejabberd_config, {Opt, Val}), + parse(Left); + _ -> + ok + end. + +get_option(Opt) -> + case ets:lookup(ejabberd_config, Opt) of + [{_, Val}] -> + Val; + _ -> + undefined + end. + + + diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index d9ff728b7..c09733515 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -10,27 +10,46 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start/0, init/2]). +-export([start/0, init/1, start/4, init/4]). start() -> - register(ejabberd_listener_c2s, - spawn(?MODULE, init, [5522, ejabberd_c2s])), - register(ejabberd_listener_s2s, - spawn(?MODULE, init, [5269, ejabberd_s2s_in])). + supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []). -init(Port, CallbackModule) -> + +init(_) -> + case ejabberd_config:get_option(listen) of + undefined -> + ignore; + Ls -> + {ok, {{one_for_one, 10, 1}, + lists:map( + fun({Port, Module, Fun, Args}) -> + {Port, + {?MODULE, start, [Port, Module, Fun, Args]}, + permanent, + brutal_kill, + worker, + [Module]} + end, Ls)}} + end. + + +start(Port, Module, Fun, Args) -> + {ok, spawn_link(?MODULE, init, [Port, Module, Fun, Args])}. + +init(Port, Module, Fun, Args) -> {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]), - accept(ListenSocket, CallbackModule). + accept(ListenSocket, Module, Fun, Args). -accept(ListenSocket, CallbackModule) -> +accept(ListenSocket, Module, Fun, Args) -> case gen_tcp:accept(ListenSocket) of {ok,Socket} -> - apply(CallbackModule, start, [Socket]), + apply(Module, Fun, [Socket] ++ Args), %ejabberd_c2s:start(Socket), - accept(ListenSocket, CallbackModule) + accept(ListenSocket, Module, Fun, Args) end. diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 5332d00b3..1eb72773b 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -10,7 +10,11 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([route/3, register_route/1, register_local_route/1, +-export([route/3, + register_route/1, + register_local_route/1, + unregister_route/1, + unregister_local_route/1, dirty_get_all_routes/0, dirty_get_all_domains/0 ]). @@ -80,6 +84,12 @@ loop() -> end, mnesia:transaction(F), loop(); + {unregister_local_route, Domain} -> + F = fun() -> + mnesia:delete({local_route, Domain}) + end, + mnesia:transaction(F), + loop(); _ -> loop() end. @@ -128,6 +138,12 @@ register_route(Domain) -> register_local_route(Domain) -> ejabberd_router ! {register_local_route, Domain, self()}. +unregister_route(Domain) -> + ejabberd_router ! {unregister_route, Domain}. + +unregister_local_route(Domain) -> + ejabberd_router ! {unregister_local_route, Domain}. + dirty_get_all_routes() -> lists:delete(?MYNAME, diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl new file mode 100644 index 000000000..b3de12faa --- /dev/null +++ b/src/ejabberd_service.erl @@ -0,0 +1,279 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_service.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 6 Dec 2002 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd_service). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-behaviour(gen_fsm). + +%% External exports +-export([start/3, receiver/2, send_text/2, send_element/2]). + +%% gen_fsm callbacks +-export([init/1, + wait_for_stream/2, + wait_for_handshake/2, + stream_established/2, + handle_event/3, + handle_sync_event/4, + code_change/4, + handle_info/3, + terminate/3]). + +-include("ejabberd.hrl"). + +-record(state, {socket, receiver, streamid, + host, password}). + +-define(DBGFSM, true). + +-ifdef(DBGFSM). +-define(FSMOPTS, [{debug, [trace]}]). +-else. +-define(FSMOPTS, []). +-endif. + +-define(STREAM_HEADER, + "" + "" + ). + +-define(STREAM_TRAILER, ""). + +-define(INVALID_HEADER_ERR, + "" + "Invalid Stream Header" + "" + ). + +-define(INVALID_HANDSHAKE_ERR, + "Invalid Handshake" + "" + ). + +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start(Socket, Host, Password) -> + gen_fsm:start(ejabberd_service, [Socket, Host, Password], ?FSMOPTS). + +%%%---------------------------------------------------------------------- +%%% Callback functions from gen_fsm +%%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Func: init/1 +%% Returns: {ok, StateName, StateData} | +%% {ok, StateName, StateData, Timeout} | +%% ignore | +%% {stop, StopReason} +%%---------------------------------------------------------------------- +init([Socket, Host, Password]) -> + ReceiverPid = spawn(?MODULE, receiver, [Socket, self()]), + {ok, wait_for_stream, #state{socket = Socket, + receiver = ReceiverPid, + streamid = new_id(), + host = Host, + password = Password + }}. + +%%---------------------------------------------------------------------- +%% Func: StateName/2 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +%state_name(Event, StateData) -> +% {next_state, state_name, StateData}. + +wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> + % TODO + case xml:get_attr_s("xmlns", Attrs) of + "jabber:component:accept" -> + Header = io_lib:format(?STREAM_HEADER, + [StateData#state.streamid, ?MYNAME]), + send_text(StateData#state.socket, Header), + {next_state, wait_for_handshake, StateData}; + _ -> + send_text(StateData#state.socket, ?INVALID_HEADER_ERR), + {stop, normal, StateData} + end; + +wait_for_stream(closed, StateData) -> + {stop, normal, StateData}. + + +wait_for_handshake({xmlstreamelement, El}, StateData) -> + {xmlelement, Name, Attrs, Els} = El, + case {Name, xml:get_cdata(Els)} of + {"handshake", Digest} -> + case sha:sha(StateData#state.streamid ++ + StateData#state.password) of + Digest -> + send_text(StateData#state.socket, ""), + ejabberd_router:register_local_route(StateData#state.host), + {next_state, stream_established, StateData}; + _ -> + send_text(StateData#state.socket, ?INVALID_HEADER_ERR), + {stop, normal, StateData} + end; + _ -> + {next_state, wait_for_key, StateData} + end; + +wait_for_handshake({xmlstreamend, Name}, StateData) -> + {stop, normal, StateData}; + +wait_for_handshake(closed, StateData) -> + {stop, normal, StateData}. + + +stream_established({xmlstreamelement, El}, StateData) -> + {xmlelement, Name, Attrs, Els} = El, + From = xml:get_attr_s("from", Attrs), + FromJID1 = jlib:string_to_jid(From), + FromJID = case FromJID1 of + {Node, Server, Resource} -> + if Server == StateData#state.host -> FromJID1; + true -> error + end; + _ -> error + end, + To = xml:get_attr_s("to", Attrs), + ToJID = case To of + "" -> error; + _ -> jlib:string_to_jid(To) + end, + if ((Name == "iq") or + (Name == "message") or + (Name == "presence")) and + (ToJID /= error) and (FromJID /= error) -> + ejabberd_router:route(FromJID, ToJID, El); + true -> + error + end, + {next_state, stream_established, StateData}; + +stream_established({xmlstreamend, Name}, StateData) -> + % TODO + {stop, normal, StateData}; + +stream_established(closed, StateData) -> + % TODO + {stop, normal, StateData}. + + + +%%---------------------------------------------------------------------- +%% Func: StateName/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {reply, Reply, NextStateName, NextStateData} | +%% {reply, Reply, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} | +%% {stop, Reason, Reply, NewStateData} +%%---------------------------------------------------------------------- +%state_name(Event, From, StateData) -> +% Reply = ok, +% {reply, Reply, state_name, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_event/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +handle_event(Event, StateName, StateData) -> + {next_state, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_sync_event/4 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {reply, Reply, NextStateName, NextStateData} | +%% {reply, Reply, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} | +%% {stop, Reason, Reply, NewStateData} +%%---------------------------------------------------------------------- +handle_sync_event(Event, From, StateName, StateData) -> + Reply = ok, + {reply, Reply, StateName, StateData}. + +code_change(OldVsn, StateName, StateData, Extra) -> + {ok, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_info/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +handle_info({send_text, Text}, StateName, StateData) -> + send_text(StateData#state.socket, Text), + {next_state, StateName, StateData}; +handle_info({send_element, El}, StateName, StateData) -> + send_element(StateData#state.socket, El), + {next_state, StateName, StateData}; +handle_info({route, From, To, Packet}, StateName, StateData) -> + {xmlelement, Name, Attrs, Els} = Packet, + Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From), + jlib:jid_to_string(To), + Attrs), + Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}), + send_text(StateData#state.socket, Text), + {next_state, StateName, StateData}. + + +%%---------------------------------------------------------------------- +%% Func: terminate/3 +%% Purpose: Shutdown the fsm +%% Returns: any +%%---------------------------------------------------------------------- +terminate(Reason, StateName, StateData) -> + case StateName of + stream_established -> + ejabberd_router:unregister_local_route(StateData#state.host); + _ -> + ok + end, + gen_tcp:close(StateData#state.socket), + ok. + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- + +receiver(Socket, C2SPid) -> + XMLStreamPid = xml_stream:start(C2SPid), + receiver(Socket, C2SPid, XMLStreamPid). + +receiver(Socket, C2SPid, XMLStreamPid) -> + case gen_tcp:recv(Socket, 0) of + {ok, Text} -> + xml_stream:send_text(XMLStreamPid, Text), + receiver(Socket, C2SPid, XMLStreamPid); + {error, closed} -> + exit(XMLStreamPid, closed), + gen_fsm:send_event(C2SPid, closed), + ok + end. + +send_text(Socket, Text) -> + gen_tcp:send(Socket,Text). + +send_element(Socket, El) -> + send_text(Socket, xml:element_to_string(El)). + + +new_id() -> + randoms:get_string(). +