From dc57e75e8fcf5c5b741bbc65522bba047c0b2791 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 3 Feb 2006 03:28:15 +0000 Subject: [PATCH] * src/web/ejabberd_http.erl: Authentication check moved to ejabberd_web.erl * src/web/ejabberd_web.erl: Likewise * src/web/Makefile.in: Added ejabberd_http.hrl dependency * src/web/ejabberd_http_poll.erl: Updated to use {active, once} socket mode * src/mod_irc/mod_irc.erl: Updated to use gen_server behaviour and ejabberd supervision tree * src/mod_irc/mod_irc_connection.erl: Likewise SVN Revision: 498 --- ChangeLog | 16 +++ src/mod_irc/mod_irc.erl | 167 +++++++++++++++++++++----- src/mod_irc/mod_irc_connection.erl | 12 +- src/mod_muc/mod_muc_room.erl | 5 +- src/web/Makefile.in | 2 +- src/web/ejabberd_http.erl | 180 ++++++++++------------------- src/web/ejabberd_http.hrl | 1 + src/web/ejabberd_http_poll.erl | 33 ++++-- src/web/ejabberd_web.erl | 47 ++++++-- 9 files changed, 291 insertions(+), 172 deletions(-) diff --git a/ChangeLog b/ChangeLog index 115f6fc31..d02b950f8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2006-02-03 Alexey Shchepin + + * src/web/ejabberd_http.erl: Authentication check moved to + ejabberd_web.erl + * src/web/ejabberd_web.erl: Likewise + + * src/web/Makefile.in: Added ejabberd_http.hrl dependency + + * src/web/ejabberd_http_poll.erl: Updated to use {active, once} + socket mode + + * src/mod_irc/mod_irc.erl: Updated to use gen_server behaviour and + ejabberd supervision tree + * src/mod_irc/mod_irc_connection.erl: Likewise + 2006-02-02 Mickael Remond * src/configure.ac: --prefix option can now override the default @@ -9,6 +24,7 @@ * src/mod_pubsub/mod_pubsub.erl: Updated to use gen_server behaviour and ejabberd supervision tree + * src/mod_echo.erl: Likewise 2006-02-01 Alexey Shchepin diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl index 1192df510..6c36a71b8 100644 --- a/src/mod_irc/mod_irc.erl +++ b/src/mod_irc/mod_irc.erl @@ -10,12 +10,20 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). +-behaviour(gen_server). -behaviour(gen_mod). --export([start/2, init/2, stop/1, +%% API +-export([start_link/2, + start/2, + stop/1, closed_connection/3, get_user_and_encoding/3]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + -include("ejabberd.hrl"). -include("jlib.hrl"). @@ -24,9 +32,51 @@ -record(irc_connection, {jid_server_host, pid}). -record(irc_custom, {us_host, data}). +-record(state, {host, server_host, access}). + -define(PROCNAME, ejabberd_mod_irc). +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- +start_link(Host, Opts) -> + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). + start(Host, Opts) -> + start_supervisor(Host), + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + ChildSpec = + {Proc, + {?MODULE, start_link, [Host, Opts]}, + temporary, + 1000, + worker, + [?MODULE]}, + supervisor:start_child(ejabberd_sup, ChildSpec). + +stop(Host) -> + stop_supervisor(Host), + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + gen_server:call(Proc, stop), + supervisor:delete_child(ejabberd_sup, Proc). + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([Host, Opts]) -> iconv:start(), mnesia:create_table(irc_custom, [{disc_copies, [node()]}, @@ -34,38 +84,97 @@ start(Host, Opts) -> MyHost = gen_mod:get_opt(host, Opts, "irc." ++ Host), update_table(MyHost), Access = gen_mod:get_opt(access, Opts, all), - register(gen_mod:get_module_proc(Host, ?PROCNAME), - spawn(?MODULE, init, [MyHost, Access])). - -init(Host, Access) -> catch ets:new(irc_connection, [named_table, public, {keypos, #irc_connection.jid_server_host}]), - ejabberd_router:register_route(Host), - loop(Host, Access). + ejabberd_router:register_route(MyHost), + {ok, #state{host = MyHost, + server_host = Host, + access = Access}}. -loop(Host, Access) -> - receive - {route, From, To, Packet} -> - case catch do_route(Host, Access, From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - _ -> - ok - end, - loop(Host, Access); - stop -> - ejabberd_router:unregister_route(Host), - ok; +%%-------------------------------------------------------------------- +%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% Description: Handling call messages +%%-------------------------------------------------------------------- +handle_call(stop, _From, State) -> + {stop, normal, ok, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling cast messages +%%-------------------------------------------------------------------- +handle_cast(_Msg, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling all non call/cast messages +%%-------------------------------------------------------------------- +handle_info({route, From, To, Packet}, + #state{host = Host, + server_host = ServerHost, + access = Access} = State) -> + case catch do_route(Host, ServerHost, Access, From, To, Packet) of + {'EXIT', Reason} -> + ?ERROR_MSG("~p", [Reason]); _ -> - loop(Host, Access) - end. + ok + end, + {noreply, State}; +handle_info(_Info, State) -> + {noreply, State}. +%%-------------------------------------------------------------------- +%% Function: terminate(Reason, State) -> void() +%% Description: This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any necessary +%% cleaning up. When it returns, the gen_server terminates with Reason. +%% The return value is ignored. +%%-------------------------------------------------------------------- +terminate(_Reason, State) -> + ejabberd_router:unregister_route(State#state.host), + ok. -do_route(Host, Access, From, To, Packet) -> +%%-------------------------------------------------------------------- +%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +%% Description: Convert process state when code is changed +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +start_supervisor(Host) -> + Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), + ChildSpec = + {Proc, + {ejabberd_tmp_sup, start_link, + [Proc, mod_irc_connection]}, + permanent, + infinity, + supervisor, + [ejabberd_tmp_sup]}, + supervisor:start_child(ejabberd_sup, ChildSpec). + +stop_supervisor(Host) -> + Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup), + supervisor:terminate_child(ejabberd_sup, Proc), + supervisor:delete_child(ejabberd_sup, Proc). + +do_route(Host, ServerHost, Access, From, To, Packet) -> case acl:match_rule(Host, Access, From) of allow -> - do_route1(Host, From, To, Packet); + do_route1(Host, ServerHost, From, To, Packet); _ -> {xmlelement, _Name, Attrs, _Els} = Packet, Lang = xml:get_attr_s("xml:lang", Attrs), @@ -75,7 +184,7 @@ do_route(Host, Access, From, To, Packet) -> ejabberd_router:route(To, From, Err) end. -do_route1(Host, From, To, Packet) -> +do_route1(Host, ServerHost, From, To, Packet) -> #jid{user = ChanServ, resource = Resource} = To, {xmlelement, _Name, Attrs, _Els} = Packet, case ChanServ of @@ -132,7 +241,7 @@ do_route1(Host, From, To, Packet) -> {Username, Encoding} = get_user_and_encoding( Host, From, Server), {ok, Pid} = mod_irc_connection:start( - From, Host, Server, + From, Host, ServerHost, Server, Username, Encoding), ets:insert( irc_connection, @@ -174,12 +283,6 @@ do_route1(Host, From, To, Packet) -> end. -stop(Host) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - Proc ! stop, - {wait, Proc}. - - closed_connection(Host, From, Server) -> ets:delete(irc_connection, {From, Server, Host}). diff --git a/src/mod_irc/mod_irc_connection.erl b/src/mod_irc/mod_irc_connection.erl index 8343a6f92..869b2e864 100644 --- a/src/mod_irc/mod_irc_connection.erl +++ b/src/mod_irc/mod_irc_connection.erl @@ -13,7 +13,7 @@ -behaviour(gen_fsm). %% External exports --export([start/5, route_chan/4, route_nick/3]). +-export([start_link/5, start/6, route_chan/4, route_nick/3]). %% gen_fsm callbacks -export([init/1, @@ -47,8 +47,14 @@ %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- -start(From, Host, Server, Username, Encoding) -> - gen_fsm:start(?MODULE, [From, Host, Server, Username, Encoding], ?FSMOPTS). +start(From, Host, ServerHost, Server, Username, Encoding) -> + Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_irc_sup), + supervisor:start_child( + Supervisor, [From, Host, Server, Username, Encoding]). + +start_link(From, Host, Server, Username, Encoding) -> + gen_fsm:start_link(?MODULE, [From, Host, Server, Username, Encoding], + ?FSMOPTS). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 8942b832f..7c66c2c11 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -97,10 +97,11 @@ start(Host, ServerHost, Access, Room, Opts) -> start_link(Host, ServerHost, Access, Room, Creator, Nick) -> gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, Creator, Nick], - ?FSMOPTS). + ?FSMOPTS). start_link(Host, ServerHost, Access, Room, Opts) -> - gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, Opts], ?FSMOPTS). + gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, Opts], + ?FSMOPTS). %%%---------------------------------------------------------------------- %%% Callback functions from gen_fsm diff --git a/src/web/Makefile.in b/src/web/Makefile.in index 37d4ce806..2656504e8 100644 --- a/src/web/Makefile.in +++ b/src/web/Makefile.in @@ -19,7 +19,7 @@ OBJS = \ all: $(OBJS) -$(OUTDIR)/%.beam: %.erl +$(OUTDIR)/%.beam: %.erl ejabberd_http.hrl @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $< diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index 9e166d694..f51b02e65 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -8,7 +8,7 @@ -module(ejabberd_http). -author('alexey@sevcom.net'). --vsn('$Revision$ '). +-vsn('$Revision$ '). %% External exports -export([start/2, @@ -201,60 +201,34 @@ process_request(#state{request_method = 'GET', request_lang = Lang, use_http_poll = UseHTTPPoll, use_web_admin = UseWebAdmin} = State) -> - US = case Auth of - {SJID, P} -> - case jlib:string_to_jid(SJID) of - error -> - unauthorized; - #jid{user = U, server = S} -> - case ejabberd_auth:check_password(U, S, P) of - true -> - {U, S}; - false -> - unauthorized - end - end; - _ -> - undefined - end, - case US of - unauthorized -> - make_xhtml_output( - State, - 401, - [{"WWW-Authenticate", "basic realm=\"ejabberd\""}], - ejabberd_web:make_xhtml([{xmlelement, "h1", [], - [{xmlcdata, "401 Unauthorized"}]}])); - _ -> - case (catch url_decode_q_split(Path)) of - {'EXIT', _} -> - process_request(false); - {NPath, Query} -> - LQuery = case (catch parse_urlencoded(Query)) of - {'EXIT', _Reason} -> - []; - LQ -> - LQ - end, - LPath = string:tokens(NPath, "/"), - Request = #request{method = 'GET', - path = LPath, - q = LQuery, - us = US, - lang = Lang}, - case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, - Request) of - El when element(1, El) == xmlelement -> - make_xhtml_output(State, 200, [], El); - {Status, Headers, El} when - element(1, El) == xmlelement -> - make_xhtml_output(State, Status, Headers, El); - Text when is_list(Text) -> - make_text_output(State, 200, [], Text); - {Status, Headers, Text} when - is_list(Text) -> - make_text_output(State, Status, Headers, Text) - end + case (catch url_decode_q_split(Path)) of + {'EXIT', _} -> + process_request(false); + {NPath, Query} -> + LQuery = case (catch parse_urlencoded(Query)) of + {'EXIT', _Reason} -> + []; + LQ -> + LQ + end, + LPath = string:tokens(NPath, "/"), + Request = #request{method = 'GET', + path = LPath, + q = LQuery, + auth = Auth, + lang = Lang}, + case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, + Request) of + El when element(1, El) == xmlelement -> + make_xhtml_output(State, 200, [], El); + {Status, Headers, El} when + element(1, El) == xmlelement -> + make_xhtml_output(State, Status, Headers, El); + Text when is_list(Text) -> + make_text_output(State, 200, [], Text); + {Status, Headers, Text} when + is_list(Text) -> + make_text_output(State, Status, Headers, Text) end end; @@ -268,68 +242,42 @@ process_request(#state{request_method = 'POST', use_http_poll = UseHTTPPoll, use_web_admin = UseWebAdmin} = State) when is_integer(Len) -> - US = case Auth of - {SJID, P} -> - case jlib:string_to_jid(SJID) of - error -> - unauthorized; - #jid{user = U, server = S} -> - case ejabberd_auth:check_password(U, S, P) of - true -> - {U, S}; - false -> - unauthorized - end - end; - _ -> - undefined - end, - case US of - unauthorized -> - make_xhtml_output( - State, - 401, - [{"WWW-Authenticate", "basic realm=\"ejabberd\""}], - ejabberd_web:make_xhtml([{xmlelement, "h1", [], - [{xmlcdata, "401 Unauthorized"}]}])); + case SockMod of + gen_tcp -> + inet:setopts(Socket, [{packet, 0}]); _ -> - case SockMod of - gen_tcp -> - inet:setopts(Socket, [{packet, 0}]); - _ -> - ok - end, - Data = recv_data(State, Len), - ?DEBUG("client data: ~p~n", [Data]), - case (catch url_decode_q_split(Path)) of - {'EXIT', _} -> - process_request(false); - {NPath, Query} -> - LPath = string:tokens(NPath, "/"), - LQuery = case (catch parse_urlencoded(Data)) of - {'EXIT', _Reason} -> - []; - LQ -> - LQ - end, - Request = #request{method = 'POST', - path = LPath, - q = LQuery, - us = US, - data = Data, - lang = Lang}, - case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, - Request) of - El when element(1, El) == xmlelement -> - make_xhtml_output(State, 200, [], El); - {Status, Headers, El} when - element(1, El) == xmlelement -> - make_xhtml_output(State, Status, Headers, El); - Text when is_list(Text) -> - make_text_output(State, 200, [], Text); - {Status, Headers, Text} when is_list(Text) -> - make_text_output(State, Status, Headers, Text) - end + ok + end, + Data = recv_data(State, Len), + ?DEBUG("client data: ~p~n", [Data]), + case (catch url_decode_q_split(Path)) of + {'EXIT', _} -> + process_request(false); + {NPath, Query} -> + LPath = string:tokens(NPath, "/"), + LQuery = case (catch parse_urlencoded(Data)) of + {'EXIT', _Reason} -> + []; + LQ -> + LQ + end, + Request = #request{method = 'POST', + path = LPath, + q = LQuery, + auth = Auth, + data = Data, + lang = Lang}, + case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, + Request) of + El when element(1, El) == xmlelement -> + make_xhtml_output(State, 200, [], El); + {Status, Headers, El} when + element(1, El) == xmlelement -> + make_xhtml_output(State, Status, Headers, El); + Text when is_list(Text) -> + make_text_output(State, 200, [], Text); + {Status, Headers, Text} when is_list(Text) -> + make_text_output(State, Status, Headers, Text) end end; diff --git a/src/web/ejabberd_http.hrl b/src/web/ejabberd_http.hrl index dab67dae4..62041fed3 100644 --- a/src/web/ejabberd_http.hrl +++ b/src/web/ejabberd_http.hrl @@ -10,6 +10,7 @@ path, q = [], us, + auth, lang = "", data = "" }). diff --git a/src/web/ejabberd_http_poll.erl b/src/web/ejabberd_http_poll.erl index d76173a30..09d4555f8 100644 --- a/src/web/ejabberd_http_poll.erl +++ b/src/web/ejabberd_http_poll.erl @@ -21,7 +21,8 @@ handle_info/3, terminate/3, send/2, - recv/3, + setopts/2, + controlling_process/2, close/1, process_request/1]). @@ -66,8 +67,16 @@ start_link(ID, Key) -> send({http_poll, FsmRef}, Packet) -> gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}). -recv({http_poll, FsmRef}, _Length, Timeout) -> - gen_fsm:sync_send_all_state_event(FsmRef, recv, Timeout). +setopts({http_poll, FsmRef}, Opts) -> + case lists:member({active, once}, Opts) of + true -> + gen_fsm:sync_send_all_state_event(FsmRef, activate); + _ -> + ok + end. + +controlling_process(_Socket, _Pid) -> + ok. close({http_poll, FsmRef}) -> catch gen_fsm:sync_send_all_state_event(FsmRef, close). @@ -138,7 +147,8 @@ process_request(_Request) -> init([ID, Key]) -> ?INFO_MSG("started: ~p", [{ID, Key}]), Opts = [], % TODO - ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts), + {ok, C2SPid} = ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts), + ejabberd_c2s:become_controller(C2SPid), Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []), {ok, loop, #state{id = ID, key = Key, @@ -188,14 +198,14 @@ handle_sync_event({send, Packet}, From, StateName, StateData) -> Reply = ok, {reply, Reply, StateName, StateData#state{output = Output}}; -handle_sync_event(recv, From, StateName, StateData) -> +handle_sync_event(activate, From, StateName, StateData) -> case StateData#state.input of "" -> - {next_state, StateName, StateData#state{waiting_input = From}}; + {reply, ok, StateName, StateData#state{waiting_input = From}}; Input -> - Reply = {ok, list_to_binary(Input)}, - {reply, Reply, StateName, StateData#state{input = "", - waiting_input = false}} + From ! {tcp, {http_poll, self()}, list_to_binary(Input)}, + {reply, ok, StateName, StateData#state{input = "", + waiting_input = false}} end; handle_sync_event(stop, From, StateName, StateData) -> @@ -225,8 +235,9 @@ handle_sync_event({http_put, Key, NewKey, Packet}, Reply = ok, {reply, Reply, StateName, StateData#state{input = Input, key = NewKey}}; - Receiver -> - gen_fsm:reply(Receiver, {ok, list_to_binary(Packet)}), + {Receiver, _Tag} -> + Receiver ! {tcp, {http_poll, self()}, + list_to_binary(Packet)}, cancel_timer(StateData#state.timer), Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []), Reply = ok, diff --git a/src/web/ejabberd_web.erl b/src/web/ejabberd_web.erl index 70f6e3ee7..89c73830a 100644 --- a/src/web/ejabberd_web.erl +++ b/src/web/ejabberd_web.erl @@ -8,7 +8,7 @@ -module(ejabberd_web). -author('alexey@sevcom.net'). --vsn('$Revision$ '). +-vsn('$Revision$ '). %% External exports -export([make_xhtml/1, @@ -50,13 +50,29 @@ make_xhtml(Els) -> process_get({_, true}, - #request{us = US, + #request{auth = Auth, path = ["admin", "server", SHost | RPath], q = Query, lang = Lang} = Request) -> Host = jlib:nameprep(SHost), case lists:member(Host, ?MYHOSTS) of true -> + US = case Auth of + {SJID, P} -> + case jlib:string_to_jid(SJID) of + error -> + unauthorized; + #jid{user = U, server = S} -> + case ejabberd_auth:check_password(U, S, P) of + true -> + {U, S}; + false -> + unauthorized + end + end; + _ -> + unauthorized + end, case US of {User, Server} -> case acl:match_rule( @@ -65,7 +81,8 @@ process_get({_, true}, {401, [], make_xhtml([?XC("h1", "Not Allowed")])}; allow -> ejabberd_web_admin:process_admin( - Host, Request#request{path = RPath}) + Host, Request#request{path = RPath, + us = US}) end; undefined -> {401, @@ -78,10 +95,26 @@ process_get({_, true}, end; process_get({_, true}, - #request{us = US, + #request{auth = Auth, path = ["admin" | RPath], q = Query, lang = Lang} = Request) -> + US = case Auth of + {SJID, P} -> + case jlib:string_to_jid(SJID) of + error -> + unauthorized; + #jid{user = U, server = S} -> + case ejabberd_auth:check_password(U, S, P) of + true -> + {U, S}; + false -> + unauthorized + end + end; + _ -> + undefined + end, case US of {User, Server} -> case acl:match_rule( @@ -90,7 +123,8 @@ process_get({_, true}, {401, [], make_xhtml([?XC("h1", "Not Allowed")])}; allow -> ejabberd_web_admin:process_admin( - global, Request#request{path = RPath}) + global, Request#request{path = RPath, + us = US}) end; undefined -> {401, @@ -100,8 +134,7 @@ process_get({_, true}, end; process_get({true, _}, - #request{us = _US, - path = ["http-poll" | RPath], + #request{path = ["http-poll" | RPath], q = _Query, lang = _Lang} = Request) -> ejabberd_http_poll:process_request(Request#request{path = RPath});