From 357554265e4c86c5cd0dc3b6ae2f69f20c85786b Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sun, 8 Aug 2004 19:07:55 +0000 Subject: [PATCH] * src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch offline messages * src/mod_offline.erl: Likewise * src/mod_offline.erl: Added table locking in remove_old_messages/1 * src/ejabberd_sm.erl: Use offline_message_hook to store offline messages * src/mod_offline.erl: Likewise * src/ejabberd_hooks.erl: Hooks support * src/ejabberd_sup.erl: Added ejabberd_hooks * doc/guide.tex: Updated * src/ejabberd.cfg.example: Updated * src/ejabberd_c2s.erl: Changed TLS options (thanks to Sergei Golovan) SVN Revision: 255 --- ChangeLog | 23 +++++ doc/guide.html | 16 +++- doc/guide.tex | 18 ++-- src/ejabberd.cfg.example | 9 +- src/ejabberd_c2s.erl | 12 ++- src/ejabberd_hooks.erl | 183 +++++++++++++++++++++++++++++++++++++++ src/ejabberd_sm.erl | 20 ++--- src/ejabberd_sup.erl | 78 ++++++++++------- src/mod_offline.erl | 43 +++++---- 9 files changed, 327 insertions(+), 75 deletions(-) create mode 100644 src/ejabberd_hooks.erl diff --git a/ChangeLog b/ChangeLog index 05cd2c438..e6043d3b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2004-08-08 Alexey Shchepin + + * src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch + offline messages + * src/mod_offline.erl: Likewise + + * src/mod_offline.erl: Added table locking in + remove_old_messages/1 + + * src/ejabberd_sm.erl: Use offline_message_hook to store offline + messages + * src/mod_offline.erl: Likewise + + * src/ejabberd_hooks.erl: Hooks support + * src/ejabberd_sup.erl: Added ejabberd_hooks + + * doc/guide.tex: Updated + + * src/ejabberd.cfg.example: Updated + + * src/ejabberd_c2s.erl: Changed TLS options (thanks to Sergei + Golovan) + 2004-08-05 Alexey Shchepin * src/aclocal.m4: Updated to check for openssl library (thanks to diff --git a/doc/guide.html b/doc/guide.html index 2db534258..851bb0384 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -142,7 +142,7 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and The misfeatures of ejabberd are:
  • No support for virtual domains -
  • No support for STARTTLS +
  • No support for authentification and STARTTLS in S2S connections
@@ -164,7 +164,8 @@ To compile ejabberd, you will need the following packages: GNU Make;
  • GCC;
  • libexpat 1.95 or later; -
  • Erlang/OTP R8B or later. +
  • Erlang/OTP R8B or later; +
  • OpenSSL 0.9.6 or later (optional). @@ -473,8 +474,15 @@ The following options are defined:
    {ip, IPAddress}
    This option specifies which network interface to listen on. For example {ip, {192, 168, 1, 1}}.
    inet6
    Set up the socket for IPv6. +
    tls
    This option specifies that STARTTLS extension is available on + connections to this port. You should also set ``certfile'' option. +
    tls_from_start
    Among with tls this option specifies that + traffic on this port will be encrypted using SSL immediately after + connecting.
    ssl
    This option specifies that traffic on this port will be - encrypted using SSL. You should also set ``certfile'' option. + encrypted using SSL. You should also set ``certfile'' option. It + is recommended to use tls and tls_from_start options + instead.
    {certfile, Path}
    Path to a file containing the SSL certificate.
    ejabberd_s2s_in
    This module serves incoming S2S connections. @@ -495,7 +503,7 @@ The following additional options are defined for ejabberd_service
    The following options are defined:
    - http_poll
    This option enables HTTP Polling . + http_poll
    This option enables HTTP Polling support. It is available then at http://server:port/http-poll/.

    web_admin
    This option enables web-based interface for ejabberd diff --git a/doc/guide.tex b/doc/guide.tex index 732aec45f..dd78dbb98 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -126,7 +126,7 @@ The main features of \ejabberd{} are: The misfeatures of \ejabberd{} are: \begin{itemize} \item No support for virtual domains -\item No support for STARTTLS +\item No support for authentification and STARTTLS in S2S connections \end{itemize} @@ -144,7 +144,8 @@ To compile \ejabberd{}, you will need the following packages: \item GNU Make; \item GCC; \item libexpat 1.95 or later; -\item Erlang/OTP R8B or later. +\item Erlang/OTP R8B or later; +\item OpenSSL 0.9.6 or later (optional). \end{itemize} \subsubsection{Windows} @@ -285,8 +286,6 @@ But in this case \ejabberd{} can start to work slower. \subsection{Initial Configuration} \label{sec:initconfig} -%\verbatiminput{../src/ejabberd.cfg} - The configuration file is initially loaded the first time \ejabberd{} is executed, when it is parsed and stored in a database. Subsequently the configuration is loaded from the database and any commands in the configuration @@ -477,8 +476,15 @@ Currently these modules are implemented: \titem{\{ip, IPAddress\}} This option specifies which network interface to listen on. For example \verb|{ip, {192, 168, 1, 1}}|. \titem{inet6} Set up the socket for IPv6. + \titem{starttls} This option specifies that STARTTLS extension is available + on connections to this port. You should also set ``\verb|certfile|'' + option. + \titem{tls} This option specifies that traffic on this port will be + encrypted using SSL immediately after connecting. You should also set + ``\verb|certfile|'' option. \titem{ssl} This option specifies that traffic on this port will be - encrypted using SSL. You should also set ``\verb|certfile|'' option. + encrypted using SSL. You should also set ``\verb|certfile|'' option. It + is recommended to use \term{tls} option instead. \titem{\{certfile, Path\}} Path to a file containing the SSL certificate. \end{description} \titem{ejabberd\_s2s\_in} This module serves incoming S2S connections. @@ -499,7 +505,7 @@ Currently these modules are implemented: The following options are defined: \begin{description} - \titem{http\_poll} This option enables \tjepref{0025}{HTTP Polling} . + \titem{http\_poll} This option enables \tjepref{0025}{HTTP Polling} support. It is available then at \verb|http://server:port/http-poll/|. \titem{web\_admin} This option enables web-based interface for \ejabberd{} diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example index 405f4a2b2..82e1cc43c 100644 --- a/src/ejabberd.cfg.example +++ b/src/ejabberd.cfg.example @@ -91,8 +91,13 @@ % Listened ports: {listen, - [{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]}, - {5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]}, + [{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}, + starttls, {certfile, "./ssl.pem"}]}, + {5223, ejabberd_c2s, [{access, c2s}, + tls, {certfile, "./ssl.pem"}]}, + % Use these two lines instead if TLS support is not compiled + %{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]}, + %{5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]}, {5269, ejabberd_s2s_in, [{shaper, s2s_shaper}]}, {5280, ejabberd_http, [http_poll, web_admin]}, {8888, ejabberd_service, [{access, all}, diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index b5b3128da..844e7bcfb 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -80,8 +80,6 @@ -define(INVALID_NS_ERR, xml:element_to_string(?SERR_INVALID_NAMESPACE)). -%-define(INVALID_XML_ERR, -% "Invalid XML"). -define(INVALID_XML_ERR, xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)). @@ -118,8 +116,9 @@ init([{SockMod, Socket}, Opts]) -> {value, {_, S}} -> S; _ -> none end, - TLS = lists:member(tls, Opts), - TLSEnabled = lists:member(tls_from_start, Opts), + StartTLS = lists:member(starttls, Opts), + TLSEnabled = lists:member(tls, Opts), + TLS = StartTLS orelse TLSEnabled, TLSOpts = lists:filter(fun({certfile, _}) -> true; (_) -> false end, Opts), @@ -1387,9 +1386,8 @@ process_privacy_iq(From, To, resend_offline_messages(StateData) -> - case catch mod_offline:pop_offline_messages(StateData#state.user) of - {'EXIT', _Reason} -> - ok; + case ejabberd_hooks:run_fold(resend_offline_messages_hook, [], + [StateData#state.user]) of Rs when list(Rs) -> lists:foreach( fun({route, From, To, {xmlelement, Name, Attrs, Els}}) -> diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl new file mode 100644 index 000000000..c00035b41 --- /dev/null +++ b/src/ejabberd_hooks.erl @@ -0,0 +1,183 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_hooks.erl +%%% Author : Alexey Shchepin +%%% Purpose : Manage hooks +%%% Created : 8 Aug 2004 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(ejabberd_hooks). +-author('alexey@sevcom.net'). + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, + add/4, + delete/4, + run/2, + run_fold/3]). + +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + code_change/3, + handle_info/2, + terminate/2]). + +-include("ejabberd.hrl"). + +-record(state, {}). + +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start_link() -> + gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []). + +add(Hook, Module, Function, Seq) -> + gen_server:call(ejabberd_hooks, {add, Hook, Module, Function, Seq}). + +delete(Hook, Module, Function, Seq) -> + gen_server:call(ejabberd_hooks, {delete, Hook, Module, Function, Seq}). + +run(Hook, Args) -> + case ets:lookup(hooks, Hook) of + [{_, Ls}] -> + run1(Ls, Hook, Args); + [] -> + ok + end. + +run_fold(Hook, Val, Args) -> + case ets:lookup(hooks, Hook) of + [{_, Ls}] -> + run_fold1(Ls, Hook, Val, Args); + [] -> + ok + end. + +%%%---------------------------------------------------------------------- +%%% Callback functions from gen_server +%%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Func: init/1 +%% Returns: {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%%---------------------------------------------------------------------- +init([]) -> + ets:new(hooks, [named_table]), + {ok, #state{}}. + +%%---------------------------------------------------------------------- +%% Func: handle_call/3 +%% Returns: {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | (terminate/2 is called) +%% {stop, Reason, State} (terminate/2 is called) +%%---------------------------------------------------------------------- +handle_call({add, Hook, Module, Function, Seq}, From, State) -> + Reply = case ets:lookup(hooks, Hook) of + [{_, Ls}] -> + El = {Seq, Module, Function}, + case lists:member(El, Ls) of + true -> + ok; + false -> + NewLs = lists:merge(Ls, [El]), + ets:insert(hooks, {Hook, NewLs}), + ok + end; + [] -> + NewLs = [{Seq, Module, Function}], + ets:insert(hooks, {Hook, NewLs}), + ok + end, + {reply, Reply, State}; +handle_call({delete, Hook, Module, Function, Seq}, From, State) -> + Reply = case ets:lookup(hooks, Hook) of + [{_, Ls}] -> + NewLs = lists:delete({Seq, Module, Function}, Ls), + ets:insert(hooks, {Hook, NewLs}), + ok; + [] -> + ok + end, + {reply, Reply, State}; +handle_call(Request, From, State) -> + Reply = ok, + {reply, Reply, State}. + +%%---------------------------------------------------------------------- +%% Func: handle_cast/2 +%% Returns: {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} (terminate/2 is called) +%%---------------------------------------------------------------------- +handle_cast(Msg, State) -> + {noreply, State}. + +%%---------------------------------------------------------------------- +%% Func: handle_info/2 +%% Returns: {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} (terminate/2 is called) +%%---------------------------------------------------------------------- +handle_info(Info, State) -> + {noreply, State}. + +%%---------------------------------------------------------------------- +%% Func: terminate/2 +%% Purpose: Shutdown the server +%% Returns: any (ignored by gen_server) +%%---------------------------------------------------------------------- +terminate(Reason, State) -> + ok. + + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- + +run1([], Hook, Args) -> + ok; +run1([{_Seq, Module, Function} | Ls], Hook, Args) -> + case catch apply(Module, Function, Args) of + {'EXIT', Reason} -> + ?ERROR_MSG("~p~nrunning hook: ~p", + [Reason, {Hook, Args}]), + run1(Ls, Hook, Args); + stop -> + ok; + _ -> + run1(Ls, Hook, Args) + end. + + +run_fold1([], Hook, Val, Args) -> + Val; +run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) -> + case catch apply(Module, Function, [Val | Args]) of + {'EXIT', Reason} -> + ?ERROR_MSG("~p~nrunning hook: ~p", + [Reason, {Hook, Args}]), + run_fold1(Ls, Hook, Val, Args); + stop -> + stopped; + {stop, NewVal} -> + NewVal; + NewVal -> + run_fold1(Ls, Hook, NewVal, Args) + end. + + + diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 882c61e0e..d4a972518 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -11,6 +11,7 @@ -vsn('$Revision$ '). -export([start_link/0, init/0, open_session/2, close_session/2, + bounce_offline_message/3, get_user_resources/1, set_presence/3, unset_presence/3, @@ -44,6 +45,8 @@ init() -> mnesia:add_table_index(presence, user), mnesia:subscribe(system), ets:new(sm_iqtable, [named_table]), + ejabberd_hooks:add(offline_message_hook, + ejabberd_sm, bounce_offline_message, 100), loop(). loop() -> @@ -263,18 +266,11 @@ route_message(From, To, Packet) -> _ -> case ejabberd_auth:is_user_exists(LUser) of true -> - case catch mod_offline:store_packet( - From, To, Packet) of - {'EXIT', _} -> - Err = jlib:make_error_reply( - Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err); - _ -> - ok - end; + ejabberd_hooks:run(offline_message_hook, + [From, To, Packet]); _ -> Err = jlib:make_error_reply( - Packet, ?ERR_ITEM_NOT_FOUND), + Packet, ?ERR_SERVICE_UNAVAILABLE), ejabberd_router:route(To, From, Err) end end; @@ -291,6 +287,10 @@ route_message(From, To, Packet) -> end end. +bounce_offline_message(From, To, Packet) -> + Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE), + ejabberd_router:route(To, From, Err), + stop. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index bda9b79a1..9a2cc74d5 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -19,36 +19,48 @@ start_link() -> init([]) -> - Router = {ejabberd_router, - {ejabberd_router, start_link, []}, - permanent, - brutal_kill, - worker, - [ejabberd_router]}, - SM = {ejabberd_sm, - {ejabberd_sm, start_link, []}, - permanent, - brutal_kill, - worker, - [ejabberd_sm]}, - S2S = {ejabberd_s2s, - {ejabberd_s2s, start_link, []}, - permanent, - brutal_kill, - worker, - [ejabberd_s2s]}, - Local = {ejabberd_local, - {ejabberd_local, start_link, []}, - permanent, - brutal_kill, - worker, - [ejabberd_local]}, - Listener = {ejabberd_listener, - {ejabberd_listener, start_link, []}, - permanent, - infinity, - supervisor, - [ejabberd_listener]}, + Hooks = + {ejabberd_hooks, + {ejabberd_hooks, start_link, []}, + permanent, + brutal_kill, + worker, + [ejabberd_hooks]}, + Router = + {ejabberd_router, + {ejabberd_router, start_link, []}, + permanent, + brutal_kill, + worker, + [ejabberd_router]}, + SM = + {ejabberd_sm, + {ejabberd_sm, start_link, []}, + permanent, + brutal_kill, + worker, + [ejabberd_sm]}, + S2S = + {ejabberd_s2s, + {ejabberd_s2s, start_link, []}, + permanent, + brutal_kill, + worker, + [ejabberd_s2s]}, + Local = + {ejabberd_local, + {ejabberd_local, start_link, []}, + permanent, + brutal_kill, + worker, + [ejabberd_local]}, + Listener = + {ejabberd_listener, + {ejabberd_listener, start_link, []}, + permanent, + infinity, + supervisor, + [ejabberd_listener]}, StringPrep = {stringprep, {stringprep, start_link, []}, @@ -112,7 +124,11 @@ init([]) -> supervisor, [ejabberd_tmp_sup]}, {ok, {{one_for_one, 10, 1}, - [Router, SM, S2S, Local, + [Hooks, + Router, + SM, + S2S, + Local, StringPrep, C2SSupervisor, S2SInSupervisor, diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 7af72442b..22e9b85ee 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -16,7 +16,7 @@ stop/0, store_packet/3, resend_offline_messages/1, - pop_offline_messages/1, + pop_offline_messages/2, remove_old_messages/1, remove_user/1]). @@ -31,6 +31,10 @@ start(_) -> [{disc_only_copies, [node()]}, {type, bag}, {attributes, record_info(fields, offline_msg)}]), + ejabberd_hooks:add(offline_message_hook, + ?MODULE, store_packet, 50), + ejabberd_hooks:add(resend_offline_messages_hook, + ?MODULE, pop_offline_messages, 50), register(?PROCNAME, spawn(?MODULE, init, [])). init() -> @@ -61,23 +65,31 @@ receive_all(Msgs) -> stop() -> + ejabberd_hooks:delete(offline_message_hook, + ?MODULE, store_packet, 50), + ejabberd_hooks:delete(resend_offline_messages_hook, + ?MODULE, pop_offline_messages, 50), exit(whereis(?PROCNAME), stop), ok. store_packet(From, To, Packet) -> - true = is_process_alive(whereis(?PROCNAME)), Type = xml:get_tag_attr_s("type", Packet), - true = Type /= "error" andalso Type /= "groupchat", - case check_event(From, To, Packet) of + if + (Type /= "error") and (Type /= "groupchat") -> + case check_event(From, To, Packet) of + true -> + #jid{luser = LUser} = To, + TimeStamp = now(), + ?PROCNAME ! #offline_msg{user = LUser, + timestamp = TimeStamp, + from = From, + to = To, + packet = Packet}, + stop; + _ -> + ok + end; true -> - #jid{luser = LUser} = To, - TimeStamp = now(), - ?PROCNAME ! #offline_msg{user = LUser, - timestamp = TimeStamp, - from = From, - to = To, - packet = Packet}; - _ -> ok end. @@ -154,7 +166,7 @@ resend_offline_messages(User) -> ok end. -pop_offline_messages(User) -> +pop_offline_messages(Ls, User) -> LUser = jlib:nodeprep(User), F = fun() -> Rs = mnesia:wread({offline_msg, LUser}), @@ -175,9 +187,9 @@ pop_offline_messages(User) -> calendar:now_to_universal_time( R#offline_msg.timestamp))]}} end, - lists:keysort(#offline_msg.timestamp, Rs)); + Ls ++ lists:keysort(#offline_msg.timestamp, Rs)); _ -> - [] + Ls end. remove_old_messages(Days) -> @@ -187,6 +199,7 @@ remove_old_messages(Days) -> Secs1 = S rem 1000000, TimeStamp = {MegaSecs1, Secs1, 0}, F = fun() -> + mnesia:write_lock_table(offline_msg), mnesia:foldl( fun(#offline_msg{timestamp = TS} = Rec, _Acc) when TS < TimeStamp ->