* 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
This commit is contained in:
Alexey Shchepin 2004-08-08 19:07:55 +00:00
parent 0ef537cc6c
commit 357554265e
9 changed files with 327 additions and 75 deletions

View File

@ -1,3 +1,26 @@
2004-08-08 Alexey Shchepin <alexey@sevcom.net>
* 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 <alexey@sevcom.net>
* src/aclocal.m4: Updated to check for openssl library (thanks to

View File

@ -142,7 +142,7 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
The misfeatures of <TT>ejabberd</TT> are:
<UL><LI>
No support for virtual domains
<LI>No support for STARTTLS
<LI>No support for authentification and STARTTLS in S2S connections
</UL>
<!--TOC section Installation-->
@ -164,7 +164,8 @@ To compile <TT>ejabberd</TT>, you will need the following packages:
GNU Make;
<LI>GCC;
<LI>libexpat 1.95 or later;
<LI>Erlang/OTP R8B or later.
<LI>Erlang/OTP R8B or later;
<LI>OpenSSL 0.9.6 or later (optional).
</UL>
<!--TOC subsubsection Windows-->
@ -473,8 +474,15 @@ The following options are defined:
<DT><B><TT>{ip, IPAddress}</TT></B><DD> This option specifies which network interface to
listen on. For example <CODE>{ip, {192, 168, 1, 1}}</CODE>.
<DT><B><TT>inet6</TT></B><DD> Set up the socket for IPv6.
<DT><B><TT>tls</TT></B><DD> This option specifies that STARTTLS extension is available on
connections to this port. You should also set ``<CODE>certfile</CODE>'' option.
<DT><B><TT>tls_from_start</TT></B><DD> Among with <TT>tls</TT> this option specifies that
traffic on this port will be encrypted using SSL immediately after
connecting.
<DT><B><TT>ssl</TT></B><DD> This option specifies that traffic on this port will be
encrypted using SSL. You should also set ``<CODE>certfile</CODE>'' option.
encrypted using SSL. You should also set ``<CODE>certfile</CODE>'' option. It
is recommended to use <TT>tls</TT> and <TT>tls_from_start</TT> options
instead.
<DT><B><TT>{certfile, Path}</TT></B><DD> Path to a file containing the SSL certificate.
</DL>
<DT><B><TT>ejabberd_s2s_in</TT></B><DD> This module serves incoming S2S connections.
@ -495,7 +503,7 @@ The following additional options are defined for <TT>ejabberd_service</TT>
<BR>
The following options are defined:
<DL COMPACT=compact><DT>
<B><TT>http_poll</TT></B><DD> This option enables <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> .
<B><TT>http_poll</TT></B><DD> This option enables <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A>
support. It is available then at <CODE>http://server:port/http-poll/</CODE>.<BR>
<BR>
<DT><B><TT>web_admin</TT></B><DD> This option enables web-based interface for <TT>ejabberd</TT>

View File

@ -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{}

View File

@ -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},

View File

@ -80,8 +80,6 @@
-define(INVALID_NS_ERR,
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
%-define(INVALID_XML_ERR,
% "<stream:error code='400'>Invalid XML</stream:error>").
-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}}) ->

183
src/ejabberd_hooks.erl Normal file
View File

@ -0,0 +1,183 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_hooks.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : Manage hooks
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@sevcom.net>
%%% 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.

View File

@ -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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View File

@ -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,

View File

@ -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 ->