24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-26 22:35:31 +02:00

Merge from trunk (r1649 to r1692).

PR:		EJABP-1

SVN Revision: 1695
This commit is contained in:
Jean-Sébastien Pédron 2008-12-01 15:53:30 +00:00
parent bd300e870c
commit 9e96043255
32 changed files with 801 additions and 234 deletions

132
ChangeLog
View File

@ -1,3 +1,74 @@
2008-12-01 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
Merge from trunk (r1649 to r1692).
2008-11-28 Alexey Shchepin <alexey@process-one.net>
* src/mod_muc/mod_muc_room.erl: Clean user activity after timeout
(EJAB-804)
2008-11-26 Badlop <badlop@process-one.net>
* src/ejabberdctl.template: Fix detection of ejabberdctl.cfg path
* src/mod_irc/mod_irc.erl: Announce disco#info (thanks to Spike)
* src/mod_muc/mod_muc.erl: Announce disco#info disco#items
* src/mod_proxy65/mod_proxy65_service.erl: No announce disco#items
* src/mod_pubsub/mod_pubsub.erl: Announce disco#info disco#items
* src/mod_vcard.erl: Announce disco#info
* src/gen_mod.erl: First store module options in ETS and Mnesia,
then start the module. In case of failure, remove options from
ETS. Until now the module was started before the options were
stored in database, and some modules started incorrectly because
they couldn't access the options from database; for instance
mod_muc_room required this for reading max_users option.
* src/mod_muc/mod_muc_room.erl: Include the value of max_users
service option and the current max_users room option in the list
of allowed room limit values.
2008-10-17 Christophe Romain <christophe.romain@process-one.net>
* src/mod_pubsub/node_pep.erl: Fix get_node_affiliations resultset to
owner (Thanks to Michal Schmidt)
2008-11-24 Evgeniy Khramtsov <ekhramtsov@process-one.net>
* src/eldap/Makefile.in: added +optimize and +driver
compilation options
* src/eldap/Makefile.win32: Likewise
2008-11-23 Alexey Shchepin <alexey@process-one.net>
* src/ejabberd_receiver.erl: Hibernate after timeout
* src/ejabberd_frontend_socket.erl: Likewise
2008-11-12 Badlop <badlop@process-one.net>
* src/web/ejabberd_http.erl: Include recognized headers in
request_headers as atoms, and others as strings (EJAB-778).
URL path should be tokenized by / and then decoded (EJAB-786).
* doc/guide.tex: Improve legibility of mod_irc example config
2008-11-10 Alexey Shchepin <alexey@process-one.net>
* src/tls/tls_drv.c: Don't create a SSL context on every
connection and disable SSLv2 on outgoing connections (EJAB-781)
2008-11-08 Mickael Remond <mremond@process-one.net>
* src/ejabberd_s2s_out.erl: exports the DNS resolution
function.
2008-11-06 Badlop <badlop@process-one.net>
* src/extauth.erl: When the extauth call fails or timeouts, deny
authorization. Use two timeouts: 60s for script initialization and
10s for regular calls. (thanks to Kevin Crosbie from
Ravenpack) (EJAB-627)
2008-11-04 Pablo Polvorin <pablo.polvorin@process-one.net> 2008-11-04 Pablo Polvorin <pablo.polvorin@process-one.net>
* src/mod_muc/mod_muc_log.erl: Convert to exmpp, fix recursive * src/mod_muc/mod_muc_log.erl: Convert to exmpp, fix recursive
@ -13,6 +84,10 @@
* src/mod_muc/mod_muc.erl, src/mod_muc/mod_muc_room.erl: Convert to * src/mod_muc/mod_muc.erl, src/mod_muc/mod_muc_room.erl: Convert to
exmpp. exmpp.
2008-11-03 Alexey Shchepin <alexey@process-one.net>
* src/ejabberd_c2s.erl: Disable zlib when STARTTLS is required
2008-10-31 Jean-Sébastien Pédron <js.pedron@meetic-corp.com> 2008-10-31 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/mod_last_odbc.erl (store_last_info/4): Fix a bug where the * src/mod_last_odbc.erl (store_last_info/4): Fix a bug where the
@ -21,11 +96,64 @@
(get_last_info/2): Fix a bug where the status was returned as a list (get_last_info/2): Fix a bug where the status was returned as a list
instead of a binary. instead of a binary.
2008-10-27 Badlop <badlop@process-one.net>
* src/Makefile.in (clean-local): Delete also ejabberdctl.example
2008-10-25 Badlop <badlop@process-one.net>
* src/translate.erl: When a translation file can't be loaded, show
detailed error message
* src/ejabberd_ctl.erl: If ejabberd didn't start correctly:
'ejabberdctl status' suggests to look in log files; any other
ejabberdctl command shows 'status'.
* src/ejabberd_app.erl: Open ejabberd.log sooner, so errors during
ejabberd initialization are logged in that file (EJAB-777). Write
a log message when ejabberd finishes the start or stop.
2008-10-24 Badlop <badlop@process-one.net>
* src/ejabberd_c2s.erl: Ensure unique ID in roster push (EJAB-721)
* src/mod_roster.erl: Likewise
* src/mod_roster_odbc.erl: Likewise
* src/mod_shared_roster.erl: Likewise
* src/ejabberd_listener.erl: Fix listeners
* src/ejabberd_sup.erl: Likewise
* src/gen_mod.erl: Likewise
2008-10-23 Alexey Shchepin <alexey@process-one.net>
* src/ejabberd_frontend_socket.erl: Fixed SSL sockets
2008-10-20 Jean-Sébastien Pédron <js.pedron@meetic-corp.com> 2008-10-20 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/mod_irc/mod_irc.erl, src/mod_irc/mod_irc_connection.erl: Convert * src/mod_irc/mod_irc.erl, src/mod_irc/mod_irc_connection.erl: Convert
to exmpp. to exmpp.
2008-10-17 Badlop <badlop@process-one.net>
* src/Makefile.in: docdir should be prefixed with DESTDIR (thanks
to Jack Moffitt)(EJAB-775)
2008-10-17 Christophe Romain <christophe.romain@process-one.net>
* src/mod_pubsub/mod_pubsub.erl: fix badarg issue on get_roster_info
when allowed roster groups is not defined
* src/mod_pubsub/mod_pubsub.erl: fix remove_user not unsubscribing
user (EJAB-684)
* src/mod_pubsub/node_default.erl: does not write item when max_items
set to 0 (solves EJAB-768)
2008-10-14 Christophe Romain <christophe.romain@process-one.net>
* src/mod_pubsub/mod_pubsub.erl: fix pubsub_publish_item hook
ServerHost parameter (EJAB-772)
2008-10-13 Jean-Sébastien Pédron <js.pedron@meetic-corp.com> 2008-10-13 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/web/ejabberd_http.erl, src/web/ejabberd_http_poll.erl, * src/web/ejabberd_http.erl, src/web/ejabberd_http_poll.erl,
@ -49,6 +177,10 @@
* src/ejd2odbc.erl, src/jd2ejd.erl: Convert to exmpp. * src/ejd2odbc.erl, src/jd2ejd.erl: Convert to exmpp.
2008-10-13 Jerome Sautret <jerome.sautret@process-one.net>
* src/odbc/ejabberd_odbc.erl: log MySQL driver messages.
2008-10-13 Badlop <badlop@process-one.net> 2008-10-13 Badlop <badlop@process-one.net>
* src/web/ejabberd_web_admin.erl: When requesting page of * src/web/ejabberd_web_admin.erl: When requesting page of

View File

@ -1928,19 +1928,18 @@ able to use the transport. The default encoding is set to "iso8859-15".
... ...
]}. ]}.
</PRE></LI><LI CLASS="li-itemize">In next example the IRC transport is available with JIDs with prefix <TT>irc-t.net</TT>. </PRE></LI><LI CLASS="li-itemize">In next example the IRC transport is available with JIDs with prefix <TT>irc-t.net</TT>.
Moreover, the transport is only accessible by paying customers registered on Moreover, the transport is only accessible to two users
our domains and on other servers. of <TT>example.org</TT>, and any user of <TT>example.com</TT>:
<PRE CLASS="verbatim">{acl, paying_customers, {user, "customer1", "example.net"}}. <PRE CLASS="verbatim">{acl, paying_customers, {user, "customer1", "example.org"}}.
{acl, paying_customers, {user, "customer2", "example.com"}}. {acl, paying_customers, {user, "customer2", "example.org"}}.
{acl, paying_customers, {user, "customer3", "example.org"}}. {acl, paying_customers, {server, "example.com"}}.
{access, paying_customers, [{allow, paying_customers}, {access, irc_users, [{allow, paying_customers}, {deny, all}]}.
{deny, all}]}.
{modules, {modules,
[ [
... ...
{mod_irc, [{access, paying_customers}, {mod_irc, [{access, irc_users},
{host, "irc.example.net"}]}, {host, "irc.example.net"}]},
... ...
]}. ]}.

View File

@ -2543,20 +2543,19 @@ Examples:
]}. ]}.
\end{verbatim} \end{verbatim}
\item In next example the IRC transport is available with JIDs with prefix \jid{irc-t.net}. \item In next example the IRC transport is available with JIDs with prefix \jid{irc-t.net}.
Moreover, the transport is only accessible by paying customers registered on Moreover, the transport is only accessible to two users
our domains and on other servers. of \term{example.org}, and any user of \term{example.com}:
\begin{verbatim} \begin{verbatim}
{acl, paying_customers, {user, "customer1", "example.net"}}. {acl, paying_customers, {user, "customer1", "example.org"}}.
{acl, paying_customers, {user, "customer2", "example.com"}}. {acl, paying_customers, {user, "customer2", "example.org"}}.
{acl, paying_customers, {user, "customer3", "example.org"}}. {acl, paying_customers, {server, "example.com"}}.
{access, paying_customers, [{allow, paying_customers}, {access, irc_users, [{allow, paying_customers}, {deny, all}]}.
{deny, all}]}.
{modules, {modules,
[ [
... ...
{mod_irc, [{access, paying_customers}, {mod_irc, [{access, irc_users},
{host, "irc.example.net"}]}, {host, "irc.example.net"}]},
... ...
]}. ]}.

View File

@ -85,7 +85,7 @@ EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
# /share/doc/ejabberd # /share/doc/ejabberd
PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
datarootdir = @datarootdir@ datarootdir = @datarootdir@
DOCDIR = @docdir@ DOCDIR = $(DESTDIR)@docdir@
# /usr/lib/ejabberd/ebin/ # /usr/lib/ejabberd/ebin/
BEAMDIR = $(EJABBERDDIR)/ebin BEAMDIR = $(EJABBERDDIR)/ebin
@ -257,7 +257,7 @@ uninstall-all: uninstall-binary
clean: clean-recursive clean-local clean: clean-recursive clean-local
clean-local: clean-local:
rm -f *.beam $(ERLSHLIBS) epam rm -f *.beam $(ERLSHLIBS) epam ejabberdctl.example
rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl
distclean: distclean-recursive clean-local distclean: distclean-recursive clean-local

View File

@ -29,7 +29,7 @@
-behaviour(application). -behaviour(application).
-export([start_modules/0,start/2, prep_stop/1, stop/1, init/0]). -export([start_modules/0,start/2, get_log_path/0, prep_stop/1, stop/1, init/0]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
@ -45,6 +45,7 @@ start(normal, _Args) ->
randoms:start(), randoms:start(),
db_init(), db_init(),
sha:start(), sha:start(),
start(),
translate:start(), translate:start(),
acl:start(), acl:start(),
ejabberd_ctl:init(), ejabberd_ctl:init(),
@ -53,7 +54,6 @@ start(normal, _Args) ->
gen_mod:start(), gen_mod:start(),
ejabberd_config:start(), ejabberd_config:start(),
ejabberd_check:config(), ejabberd_check:config(),
start(),
connect_nodes(), connect_nodes(),
Sup = ejabberd_sup:start_link(), Sup = ejabberd_sup:start_link(),
ejabberd_rdbms:start(), ejabberd_rdbms:start(),
@ -65,12 +65,13 @@ start(normal, _Args) ->
%fprof:trace(start, "/tmp/fprof"), %fprof:trace(start, "/tmp/fprof"),
start_modules(), start_modules(),
ejabberd_listener:start_listeners(), ejabberd_listener:start_listeners(),
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
Sup; Sup;
start(_, _) -> start(_, _) ->
{error, badarg}. {error, badarg}.
%% Prepare the application for termination. %% Prepare the application for termination.
%% This function is called when an application is about to be stopped, %% This function is called when an application is about to be stopped,
%% before shutting down the processes of the application. %% before shutting down the processes of the application.
prep_stop(State) -> prep_stop(State) ->
stop_modules(), stop_modules(),
@ -79,6 +80,7 @@ prep_stop(State) ->
%% All the processes were killed when this function is called %% All the processes were killed when this function is called
stop(_State) -> stop(_State) ->
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
ok. ok.
@ -93,18 +95,7 @@ init() ->
register(ejabberd, self()), register(ejabberd, self()),
%erlang:system_flag(fullsweep_after, 0), %erlang:system_flag(fullsweep_after, 0),
%error_logger:logfile({open, ?LOG_PATH}), %error_logger:logfile({open, ?LOG_PATH}),
LogPath = LogPath = get_log_path(),
case application:get_env(log_path) of
{ok, Path} ->
Path;
undefined ->
case os:getenv("EJABBERD_LOG_PATH") of
false ->
?LOG_PATH;
Path ->
Path
end
end,
error_logger:add_report_handler(ejabberd_logger_h, LogPath), error_logger:add_report_handler(ejabberd_logger_h, LogPath),
erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv), erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
case erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl) of case erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl) of
@ -171,3 +162,21 @@ connect_nodes() ->
end, Nodes) end, Nodes)
end. end.
%% @spec () -> string()
%% Returns the full path to the ejabberd log file.
%% It first checks for application configuration parameter 'log_path'.
%% If not defined it checks the environment variable EJABBERD_LOG_PATH.
%% And if that one is neither defined, returns the default value:
%% "ejabberd.log" in current directory.
get_log_path() ->
case application:get_env(log_path) of
{ok, Path} ->
Path;
undefined ->
case os:getenv("EJABBERD_LOG_PATH") of
false ->
?LOG_PATH;
Path ->
Path
end
end.

View File

@ -63,6 +63,8 @@
-define(SETS, gb_sets). -define(SETS, gb_sets).
-define(DICT, dict). -define(DICT, dict).
%% pres_a contains all the presence available send (either through roster mechanism or directed).
%% Directed presence unavailable remove user from pres_a.
-record(state, {socket, -record(state, {socket,
sockmod, sockmod,
socket_monitor, socket_monitor,
@ -170,6 +172,7 @@ init([{SockMod, Socket}, Opts]) ->
TLSOpts = lists:filter(fun({certfile, _}) -> true; TLSOpts = lists:filter(fun({certfile, _}) -> true;
(_) -> false (_) -> false
end, Opts), end, Opts),
Zlib = lists:member(zlib, Opts) andalso (not StartTLSRequired),
IP = peerip(SockMod, Socket), IP = peerip(SockMod, Socket),
%% Check if IP is blacklisted: %% Check if IP is blacklisted:
case is_ip_blacklisted(IP) of case is_ip_blacklisted(IP) of

View File

@ -132,7 +132,10 @@ process(["status"]) ->
[node(), InternalStatus, ProvidedStatus]), [node(), InternalStatus, ProvidedStatus]),
case lists:keysearch(ejabberd, 1, application:which_applications()) of case lists:keysearch(ejabberd, 1, application:which_applications()) of
false -> false ->
?PRINT("ejabberd is not running in that node~n", []), EjabberdLogPath = ejabberd_app:get_log_path(),
?PRINT("ejabberd is not running in that node~n"
"Check for error messages: ~s~n"
"or other files in that directory.~n", [EjabberdLogPath]),
?STATUS_ERROR; ?STATUS_ERROR;
{value, {_, _, Version}} -> {value, {_, _, Version}} ->
?PRINT("ejabberd ~s is running in that node~n", [Version]), ?PRINT("ejabberd ~s is running in that node~n", [Version]),
@ -237,7 +240,12 @@ try_run_ctp(Args) ->
catch catch
exit:Why -> exit:Why ->
print_usage(), print_usage(),
{io_lib:format("Error in ejabberd ctl process: ~p", [Why]), ?STATUS_USAGE} {io_lib:format("Error in ejabberd ctl process: ~p", [Why]), ?STATUS_USAGE};
Error:Why ->
%% In this case probably ejabberd is not started, so let's show Status
process(["status"]),
?PRINT("~n", []),
{io_lib:format("Error in ejabberd ctl process: '~p' ~p", [Error, Why]), ?STATUS_USAGE}
end. end.
%% @spec (Args::[string()]) -> %% @spec (Args::[string()]) ->

View File

@ -53,6 +53,8 @@
-record(state, {sockmod, socket, receiver}). -record(state, {sockmod, socket, receiver}).
-define(HIBERNATE_TIMEOUT, 90000).
%%==================================================================== %%====================================================================
%% API %% API
%%==================================================================== %%====================================================================
@ -93,7 +95,7 @@ start(Module, SockMod, Socket, Opts) ->
end. end.
starttls(FsmRef, TLSOpts) -> starttls(FsmRef, TLSOpts) ->
gen_server:call(FsmRef, {starttls, TLSOpts}), %gen_server:call(FsmRef, {starttls, TLSOpts}),
FsmRef. FsmRef.
starttls(FsmRef, TLSOpts, Data) -> starttls(FsmRef, TLSOpts, Data) ->
@ -136,7 +138,8 @@ sockname(FsmRef) ->
gen_server:call(FsmRef, sockname). gen_server:call(FsmRef, sockname).
peername(FsmRef) -> peername(FsmRef) ->
gen_server:call(FsmRef, peername). %gen_server:call(FsmRef, peername).
{ok, {{0, 0, 0, 0}, 0}}.
%%==================================================================== %%====================================================================
@ -153,11 +156,12 @@ peername(FsmRef) ->
init([Module, SockMod, Socket, Opts, Receiver]) -> init([Module, SockMod, Socket, Opts, Receiver]) ->
%% TODO: monitor the receiver %% TODO: monitor the receiver
Node = ejabberd_node_groups:get_closest_node(backend), Node = ejabberd_node_groups:get_closest_node(backend),
{SockMod2, Socket2} = check_starttls(SockMod, Socket, Receiver, Opts),
{ok, Pid} = {ok, Pid} =
rpc:call(Node, Module, start, [{?MODULE, self()}, Opts]), rpc:call(Node, Module, start, [{?MODULE, self()}, Opts]),
ejabberd_receiver:become_controller(Receiver, Pid), ejabberd_receiver:become_controller(Receiver, Pid),
{ok, #state{sockmod = SockMod, {ok, #state{sockmod = SockMod2,
socket = Socket, socket = Socket2,
receiver = Receiver}}. receiver = Receiver}}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -173,7 +177,8 @@ handle_call({starttls, TLSOpts}, _From, State) ->
{ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts), {ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts),
ejabberd_receiver:starttls(State#state.receiver, TLSSocket), ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
Reply = ok, Reply = ok,
{reply, Reply, State#state{socket = TLSSocket, sockmod = tls}}; {reply, Reply, State#state{socket = TLSSocket, sockmod = tls},
?HIBERNATE_TIMEOUT};
handle_call({starttls, TLSOpts, Data}, _From, State) -> handle_call({starttls, TLSOpts, Data}, _From, State) ->
{ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts), {ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts),
@ -181,7 +186,8 @@ handle_call({starttls, TLSOpts, Data}, _From, State) ->
catch (State#state.sockmod):send( catch (State#state.sockmod):send(
State#state.socket, Data), State#state.socket, Data),
Reply = ok, Reply = ok,
{reply, Reply, State#state{socket = TLSSocket, sockmod = tls}}; {reply, Reply, State#state{socket = TLSSocket, sockmod = tls},
?HIBERNATE_TIMEOUT};
handle_call(compress, _From, State) -> handle_call(compress, _From, State) ->
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib( {ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
@ -189,7 +195,8 @@ handle_call(compress, _From, State) ->
State#state.socket), State#state.socket),
ejabberd_receiver:compress(State#state.receiver, ZlibSocket), ejabberd_receiver:compress(State#state.receiver, ZlibSocket),
Reply = ok, Reply = ok,
{reply, Reply, State#state{socket = ZlibSocket, sockmod = ejabberd_zlib}}; {reply, Reply, State#state{socket = ZlibSocket, sockmod = ejabberd_zlib},
?HIBERNATE_TIMEOUT};
handle_call({compress, Data}, _From, State) -> handle_call({compress, Data}, _From, State) ->
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib( {ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
@ -199,35 +206,36 @@ handle_call({compress, Data}, _From, State) ->
catch (State#state.sockmod):send( catch (State#state.sockmod):send(
State#state.socket, Data), State#state.socket, Data),
Reply = ok, Reply = ok,
{reply, Reply, State#state{socket = ZlibSocket, sockmod = ejabberd_zlib}}; {reply, Reply, State#state{socket = ZlibSocket, sockmod = ejabberd_zlib},
?HIBERNATE_TIMEOUT};
handle_call(reset_stream, _From, State) -> handle_call(reset_stream, _From, State) ->
ejabberd_receiver:reset_stream(State#state.receiver), ejabberd_receiver:reset_stream(State#state.receiver),
Reply = ok, Reply = ok,
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call({send, Data}, _From, State) -> handle_call({send, Data}, _From, State) ->
catch (State#state.sockmod):send( catch (State#state.sockmod):send(
State#state.socket, Data), State#state.socket, Data),
Reply = ok, Reply = ok,
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call({change_shaper, Shaper}, _From, State) -> handle_call({change_shaper, Shaper}, _From, State) ->
ejabberd_receiver:change_shaper(State#state.receiver, Shaper), ejabberd_receiver:change_shaper(State#state.receiver, Shaper),
Reply = ok, Reply = ok,
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(get_sockmod, _From, State) -> handle_call(get_sockmod, _From, State) ->
Reply = State#state.sockmod, Reply = State#state.sockmod,
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(get_peer_certificate, _From, State) -> handle_call(get_peer_certificate, _From, State) ->
Reply = tls:get_peer_certificate(State#state.socket), Reply = tls:get_peer_certificate(State#state.socket),
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(get_verify_result, _From, State) -> handle_call(get_verify_result, _From, State) ->
Reply = tls:get_verify_result(State#state.socket), Reply = tls:get_verify_result(State#state.socket),
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(close, _From, State) -> handle_call(close, _From, State) ->
ejabberd_receiver:close(State#state.receiver), ejabberd_receiver:close(State#state.receiver),
@ -243,7 +251,7 @@ handle_call(sockname, _From, State) ->
_ -> _ ->
SockMod:sockname(Socket) SockMod:sockname(Socket)
end, end,
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(peername, _From, State) -> handle_call(peername, _From, State) ->
#state{sockmod = SockMod, socket = Socket} = State, #state{sockmod = SockMod, socket = Socket} = State,
@ -254,11 +262,11 @@ handle_call(peername, _From, State) ->
_ -> _ ->
SockMod:peername(Socket) SockMod:peername(Socket)
end, end,
{reply, Reply, State}; {reply, Reply, State, ?HIBERNATE_TIMEOUT};
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State, ?HIBERNATE_TIMEOUT}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} | %% Function: handle_cast(Msg, State) -> {noreply, State} |
@ -267,7 +275,7 @@ handle_call(_Request, _From, State) ->
%% Description: Handling cast messages %% Description: Handling cast messages
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State, ?HIBERNATE_TIMEOUT}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} | %% Function: handle_info(Info, State) -> {noreply, State} |
@ -275,8 +283,11 @@ handle_cast(_Msg, State) ->
%% {stop, Reason, State} %% {stop, Reason, State}
%% Description: Handling all non call/cast messages %% Description: Handling all non call/cast messages
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_info(timeout, State) ->
proc_lib:hibernate(gen_server, enter_loop, [?MODULE, [], State]),
{noreply, State, ?HIBERNATE_TIMEOUT};
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State, ?HIBERNATE_TIMEOUT}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void() %% Function: terminate(Reason, State) -> void()
@ -298,3 +309,17 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%%% Internal functions %%% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
check_starttls(SockMod, Socket, Receiver, Opts) ->
TLSEnabled = lists:member(tls, Opts),
TLSOpts = lists:filter(fun({certfile, _}) -> true;
(_) -> false
end, Opts),
if
TLSEnabled ->
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
ejabberd_receiver:starttls(Receiver, TLSSocket),
{tls, TLSSocket};
true ->
{SockMod, Socket}
end.

View File

@ -58,8 +58,9 @@ start_listeners() ->
start(Port, Module, Opts) -> start(Port, Module, Opts) ->
%% Check if the module is an ejabberd listener or an independent listener %% Check if the module is an ejabberd listener or an independent listener
case Module:socket_type() of ModuleRaw = strip_frontend(Module),
independent -> Module:start_listener(Port, Opts); case ModuleRaw:socket_type() of
independent -> ModuleRaw:start_listener(Port, Opts);
_ -> start_dependent(Port, Module, Opts) _ -> start_dependent(Port, Module, Opts)
end. end.
@ -120,12 +121,11 @@ accept(ListenSocket, Module, Opts) ->
_ -> _ ->
ok ok
end, end,
case Module of CallMod = case is_frontend(Module) of
{frontend, Mod} -> true -> ejabberd_frontend_socket;
ejabberd_frontend_socket:start(Mod, gen_tcp, Socket, Opts); false -> ejabberd_socket
_ -> end,
ejabberd_socket:start(Module, gen_tcp, Socket, Opts) CallMod:start(strip_frontend(Module), gen_tcp, Socket, Opts),
end,
accept(ListenSocket, Module, Opts); accept(ListenSocket, Module, Opts);
{error, Reason} -> {error, Reason} ->
?INFO_MSG("(~w) Failed TCP accept: ~w", ?INFO_MSG("(~w) Failed TCP accept: ~w",
@ -137,11 +137,12 @@ start_listener(Port, Module, Opts) ->
start_module_sup(Port, Module), start_module_sup(Port, Module),
start_listener_sup(Port, Module, Opts). start_listener_sup(Port, Module, Opts).
%% Only required for some listeners, but doing for all doesn't hurt
start_module_sup(_Port, Module) -> start_module_sup(_Port, Module) ->
Proc1 = gen_mod:get_module_proc("sup", Module), Proc1 = gen_mod:get_module_proc("sup", Module),
ChildSpec1 = ChildSpec1 =
{Proc1, {Proc1,
{ejabberd_tmp_sup, start_link, [Proc1, Module]}, {ejabberd_tmp_sup, start_link, [Proc1, strip_frontend(Module)]},
permanent, permanent,
infinity, infinity,
supervisor, supervisor,
@ -188,3 +189,10 @@ delete_listener(Port, Module) ->
ejabberd_config:add_local_option(listen, Ports1), ejabberd_config:add_local_option(listen, Ports1),
stop_listener(Port, Module). stop_listener(Port, Module).
is_frontend({frontend, _Module}) -> true;
is_frontend(_) -> false.
%% @doc(FrontMod) -> atom()
%% where FrontMod = atom() | {frontend, atom()}
strip_frontend({frontend, Module}) -> Module;
strip_frontend(Module) when is_atom(Module) -> Module.

View File

@ -54,6 +54,8 @@
xml_stream_state, xml_stream_state,
timeout}). timeout}).
-define(HIBERNATE_TIMEOUT, 90000).
%%==================================================================== %%====================================================================
%% API %% API
%%==================================================================== %%====================================================================
@ -138,7 +140,7 @@ handle_call({starttls, TLSSocket}, _From,
xml_stream_state = NewXMLStreamState}, xml_stream_state = NewXMLStreamState},
case tls:recv_data(TLSSocket, "") of case tls:recv_data(TLSSocket, "") of
{ok, TLSData} -> {ok, TLSData} ->
{reply, ok, process_data(TLSData, NewState)}; {reply, ok, process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT};
{error, _Reason} -> {error, _Reason} ->
{stop, normal, ok, NewState} {stop, normal, ok, NewState}
end; end;
@ -150,7 +152,7 @@ handle_call({compress, ZlibSocket}, _From,
xml_stream_state = NewXMLStreamState}, xml_stream_state = NewXMLStreamState},
case ejabberd_zlib:recv_data(ZlibSocket, "") of case ejabberd_zlib:recv_data(ZlibSocket, "") of
{ok, ZlibData} -> {ok, ZlibData} ->
{reply, ok, process_data(ZlibData, NewState)}; {reply, ok, process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT};
{error, _Reason} -> {error, _Reason} ->
{stop, normal, ok, NewState} {stop, normal, ok, NewState}
end; end;
@ -158,7 +160,8 @@ handle_call(reset_stream, _From,
#state{xml_stream_state = XMLStreamState} = State) -> #state{xml_stream_state = XMLStreamState} = State) ->
NewXMLStreamState = exmpp_xmlstream:reset(XMLStreamState), NewXMLStreamState = exmpp_xmlstream:reset(XMLStreamState),
Reply = ok, Reply = ok,
{reply, Reply, State#state{xml_stream_state = NewXMLStreamState}}; {reply, Reply, State#state{xml_stream_state = NewXMLStreamState},
?HIBERNATE_TIMEOUT};
handle_call({become_controller, C2SPid}, _From, State) -> handle_call({become_controller, C2SPid}, _From, State) ->
Parser = exmpp_xml:start_parser([ Parser = exmpp_xml:start_parser([
{namespace, true}, {namespace, true},
@ -174,10 +177,10 @@ handle_call({become_controller, C2SPid}, _From, State) ->
xml_stream_state = XMLStreamState}, xml_stream_state = XMLStreamState},
activate_socket(NewState), activate_socket(NewState),
Reply = ok, Reply = ok,
{reply, Reply, NewState}; {reply, Reply, NewState, ?HIBERNATE_TIMEOUT};
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State, ?HIBERNATE_TIMEOUT}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} | %% Function: handle_cast(Msg, State) -> {noreply, State} |
@ -187,11 +190,11 @@ handle_call(_Request, _From, State) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_cast({change_shaper, Shaper}, State) -> handle_cast({change_shaper, Shaper}, State) ->
NewShaperState = shaper:new(Shaper), NewShaperState = shaper:new(Shaper),
{noreply, State#state{shaper_state = NewShaperState}}; {noreply, State#state{shaper_state = NewShaperState}, ?HIBERNATE_TIMEOUT};
handle_cast(close, State) -> handle_cast(close, State) ->
{stop, normal, State}; {stop, normal, State};
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State, ?HIBERNATE_TIMEOUT}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} | %% Function: handle_info(Info, State) -> {noreply, State} |
@ -207,19 +210,21 @@ handle_info({Tag, _TCPSocket, Data},
tls -> tls ->
case tls:recv_data(Socket, Data) of case tls:recv_data(Socket, Data) of
{ok, TLSData} -> {ok, TLSData} ->
{noreply, process_data(TLSData, State)}; {noreply, process_data(TLSData, State),
?HIBERNATE_TIMEOUT};
{error, _Reason} -> {error, _Reason} ->
{stop, normal, State} {stop, normal, State}
end; end;
ejabberd_zlib -> ejabberd_zlib ->
case ejabberd_zlib:recv_data(Socket, Data) of case ejabberd_zlib:recv_data(Socket, Data) of
{ok, ZlibData} -> {ok, ZlibData} ->
{noreply, process_data(ZlibData, State)}; {noreply, process_data(ZlibData, State),
?HIBERNATE_TIMEOUT};
{error, _Reason} -> {error, _Reason} ->
{stop, normal, State} {stop, normal, State}
end; end;
_ -> _ ->
{noreply, process_data(Data, State)} {noreply, process_data(Data, State), ?HIBERNATE_TIMEOUT}
end; end;
handle_info({Tag, _TCPSocket}, State) handle_info({Tag, _TCPSocket}, State)
when (Tag == tcp_closed) or (Tag == ssl_closed) -> when (Tag == tcp_closed) or (Tag == ssl_closed) ->
@ -228,15 +233,18 @@ handle_info({Tag, _TCPSocket, Reason}, State)
when (Tag == tcp_error) or (Tag == ssl_error) -> when (Tag == tcp_error) or (Tag == ssl_error) ->
case Reason of case Reason of
timeout -> timeout ->
{noreply, State}; {noreply, State, ?HIBERNATE_TIMEOUT};
_ -> _ ->
{stop, normal, State} {stop, normal, State}
end; end;
handle_info({timeout, _Ref, activate}, State) -> handle_info({timeout, _Ref, activate}, State) ->
activate_socket(State), activate_socket(State),
{noreply, State}; {noreply, State, ?HIBERNATE_TIMEOUT};
handle_info(timeout, State) ->
proc_lib:hibernate(gen_server, enter_loop, [?MODULE, [], State]),
{noreply, State, ?HIBERNATE_TIMEOUT};
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State, ?HIBERNATE_TIMEOUT}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void() %% Function: terminate(Reason, State) -> void()

View File

@ -52,7 +52,8 @@
handle_info/3, handle_info/3,
terminate/3, terminate/3,
code_change/4, code_change/4,
test_get_addr_port/1]). test_get_addr_port/1,
get_addr_port/1]).
-include_lib("exmpp/include/exmpp.hrl"). -include_lib("exmpp/include/exmpp.hrl").

View File

@ -99,6 +99,21 @@ init([]) ->
infinity, infinity,
supervisor, supervisor,
[ejabberd_tmp_sup]}, [ejabberd_tmp_sup]},
C2SSupervisor =
{ejabberd_c2s_sup,
{ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
S2SInSupervisor =
{ejabberd_s2s_in_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_s2s_in_sup, ejabberd_s2s_in]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
S2SOutSupervisor = S2SOutSupervisor =
{ejabberd_s2s_out_sup, {ejabberd_s2s_out_sup,
{ejabberd_tmp_sup, start_link, {ejabberd_tmp_sup, start_link,
@ -115,6 +130,14 @@ init([]) ->
infinity, infinity,
supervisor, supervisor,
[ejabberd_tmp_sup]}, [ejabberd_tmp_sup]},
HTTPSupervisor =
{ejabberd_http_sup,
{ejabberd_tmp_sup, start_link,
[ejabberd_http_sup, ejabberd_http]},
permanent,
infinity,
supervisor,
[ejabberd_tmp_sup]},
HTTPPollSupervisor = HTTPPollSupervisor =
{ejabberd_http_poll_sup, {ejabberd_http_poll_sup,
{ejabberd_tmp_sup, start_link, {ejabberd_tmp_sup, start_link,
@ -148,8 +171,11 @@ init([]) ->
S2S, S2S,
Local, Local,
ReceiverSupervisor, ReceiverSupervisor,
C2SSupervisor,
S2SInSupervisor,
S2SOutSupervisor, S2SOutSupervisor,
ServiceSupervisor, ServiceSupervisor,
HTTPSupervisor,
HTTPPollSupervisor, HTTPPollSupervisor,
IQSupervisor, IQSupervisor,
FrontendSocketSupervisor, FrontendSocketSupervisor,

View File

@ -13,14 +13,6 @@ HOST=localhost
ERLANG_NODE=$NODE@$HOST ERLANG_NODE=$NODE@$HOST
ERL=@erl@ ERL=@erl@
INSTALLUSER=@installuser@ INSTALLUSER=@installuser@
ETCDIR=@SYSCONFDIR@/ejabberd
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd
SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd
# read custom configuration
EJABBERDCTL_CONFIG_PATH=$ETCDIR/ejabberdctl.cfg
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
# parse command line parameters # parse command line parameters
ARGS= ARGS=
@ -30,6 +22,7 @@ while [ $# -ne 0 ] ; do
case $PARAM in case $PARAM in
--) break ;; --) break ;;
--node) ERLANG_NODE=$1; shift ;; --node) ERLANG_NODE=$1; shift ;;
--config-dir) ETCDIR=$1 ; shift ;;
--config) EJABBERD_CONFIG_PATH=$1 ; shift ;; --config) EJABBERD_CONFIG_PATH=$1 ; shift ;;
--ctl-config) EJABBERDCTL_CONFIG_PATH=$1 ; shift ;; --ctl-config) EJABBERDCTL_CONFIG_PATH=$1 ; shift ;;
--logs) LOGS_DIR=$1 ; shift ;; --logs) LOGS_DIR=$1 ; shift ;;
@ -38,6 +31,24 @@ while [ $# -ne 0 ] ; do
esac esac
done done
# Define ejabberd variable if they have not been defined from the command line
if [ "$ETCDIR" = "" ] ; then
ETCDIR=@SYSCONFDIR@/ejabberd
fi
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
fi
if [ "$EJABBERDCTL_CONFIG_PATH" = "" ] ; then
EJABBERDCTL_CONFIG_PATH=$ETCDIR/ejabberdctl.cfg
fi
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
if [ "$LOGS_DIR" = "" ] ; then
LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd
fi
if [ "$SPOOLDIR" = "" ] ; then
SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd
fi
# check the proper system user is used # check the proper system user is used
ID=`id -g` ID=`id -g`
EJID=`id -g $INSTALLUSER` EJID=`id -g $INSTALLUSER`
@ -172,6 +183,7 @@ help ()
echo " live Start an ejabberd node in live (interactive) mode" echo " live Start an ejabberd node in live (interactive) mode"
echo "" echo ""
echo "Optional parameters when starting an ejabberd node:" echo "Optional parameters when starting an ejabberd node:"
echo " --config-dir dir Config ejabberd: $ETCDIR"
echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH" echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH"
echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH" echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH"
echo " --logs dir Directory for logs: $LOGS_DIR" echo " --logs dir Directory for logs: $LOGS_DIR"

View File

@ -6,6 +6,8 @@ CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@ LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ LIBS = @LIBS@
ASN_FLAGS = -bber_bin +optimize +driver
ERLANG_CFLAGS = @ERLANG_CFLAGS@ ERLANG_CFLAGS = @ERLANG_CFLAGS@
ERLANG_LIBS = @ERLANG_LIBS@ ERLANG_LIBS = @ERLANG_LIBS@
@ -27,7 +29,7 @@ all: $(BEAMS) ELDAPv3.beam
ELDAPv3.beam: ELDAPv3.erl ELDAPv3.beam: ELDAPv3.erl
ELDAPv3.erl: ELDAPv3.asn ELDAPv3.erl: ELDAPv3.asn
@ERLC@ -bber_bin -W $(EFLAGS) $< @ERLC@ $(ASN_FLAGS) -W $(EFLAGS) $<
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl $(OUTDIR)/%.beam: %.erl ELDAPv3.erl
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $< @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<

View File

@ -6,6 +6,8 @@ EFLAGS = -I .. -pz ..
OUTDIR = .. OUTDIR = ..
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam
ASN_FLAGS = -bber_bin +optimize +driver
ALL : $(BEAMS) ALL : $(BEAMS)
Clean : Clean :
@ -16,7 +18,7 @@ Clean :
-@erase $(BEAMS) -@erase $(BEAMS)
ELDAPv3.erl : ELDAPv3.asn ELDAPv3.erl : ELDAPv3.asn
erlc -bber_bin -W $(EFLAGS) ELDAPv3.asn erlc $(ASN_FLAGS) -W $(EFLAGS) ELDAPv3.asn
$(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl $(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl

View File

@ -32,7 +32,8 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-define(CALL_TIMEOUT, 30000). % Timeout is in milliseconds: 30 seconds == 30000 -define(INIT_TIMEOUT, 60000). % Timeout is in milliseconds: 60 seconds == 60000
-define(CALL_TIMEOUT, 10000). % Timeout is in milliseconds: 10 seconds == 10000
start(Host, ExtPrg) -> start(Host, ExtPrg) ->
spawn(?MODULE, init, [Host, ExtPrg]). spawn(?MODULE, init, [Host, ExtPrg]).
@ -41,7 +42,7 @@ init(Host, ExtPrg) ->
register(gen_mod:get_module_proc(Host, eauth), self()), register(gen_mod:get_module_proc(Host, eauth), self()),
process_flag(trap_exit,true), process_flag(trap_exit,true),
Port = open_port({spawn, ExtPrg}, [{packet,2}]), Port = open_port({spawn, ExtPrg}, [{packet,2}]),
loop(Port). loop(Port, ?INIT_TIMEOUT).
stop(Host) -> stop(Host) ->
gen_mod:get_module_proc(Host, eauth) ! stop. gen_mod:get_module_proc(Host, eauth) ! stop.
@ -63,21 +64,23 @@ call_port(Server, Msg) ->
Result Result
end. end.
loop(Port) -> loop(Port, Timeout) ->
receive receive
{call, Caller, Msg} -> {call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}}, Port ! {self(), {command, encode(Msg)}},
receive receive
{Port, {data, Data}} -> {Port, {data, Data}} ->
?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]), ?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]),
Caller ! {eauth, decode(Data)}; Caller ! {eauth, decode(Data)};
{Port, Other} -> {Port, Other} ->
?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]) ?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]),
Caller ! {eauth, false}
after after
?CALL_TIMEOUT -> Timeout ->
?ERROR_MSG("extauth call '~p' didn't receive response~n", [Msg]) ?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]),
Caller ! {eauth, false}
end, end,
loop(Port); loop(Port, ?CALL_TIMEOUT);
stop -> stop ->
Port ! {self(), close}, Port ! {self(), close},
receive receive

View File

@ -62,14 +62,16 @@ start() ->
start_module(Host, Module, Opts) -> start_module(Host, Module, Opts) ->
set_module_opts_mnesia(Host, Module, Opts),
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
opts = Opts}),
case catch Module:start(Host, Opts) of case catch Module:start(Host, Opts) of
{'EXIT', Reason} -> {'EXIT', Reason} ->
del_module_mnesia(Host, Module),
ets:delete(ejabberd_modules, {Module, Host}),
?ERROR_MSG("~p", [Reason]); ?ERROR_MSG("~p", [Reason]);
_ -> _ ->
set_module_opts_mnesia(Host, Module, Opts),
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
opts = Opts}),
ok ok
end. end.
@ -224,6 +226,8 @@ get_hosts(Opts, Prefix) ->
Hosts Hosts
end. end.
get_module_proc(Host, {frontend, Base}) ->
get_module_proc("frontend_" ++ Host, Base);
get_module_proc(Host, Base) -> get_module_proc(Host, Base) ->
list_to_atom(atom_to_list(Base) ++ "_" ++ Host). list_to_atom(atom_to_list(Base) ++ "_" ++ Host).

View File

@ -305,6 +305,8 @@ iq_disco(Lang) ->
[#xmlattr{name = 'category', value = "conference"}, [#xmlattr{name = 'category', value = "conference"},
#xmlattr{name = 'type', value = "irc"}, #xmlattr{name = 'type', value = "irc"},
#xmlattr{name = 'name', value = translate:translate(Lang, "IRC Transport")}]}, #xmlattr{name = 'name', value = translate:translate(Lang, "IRC Transport")}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
[#xmlattr{name = 'var', value = ?NS_DISCO_INFO_s}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
[#xmlattr{name = 'var', value = ?NS_MUC_s}]}, [#xmlattr{name = 'var', value = ?NS_MUC_s}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =

View File

@ -499,6 +499,12 @@ iq_disco_info(Lang) ->
value = "text"}, value = "text"},
#xmlattr{name = 'name', #xmlattr{name = 'name',
value = translate:translate(Lang, "Chatrooms")}]}, value = translate:translate(Lang, "Chatrooms")}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
[#xmlattr{name = 'var',
value = ?NS_DISCO_INFO_s}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
[#xmlattr{name = 'var',
value = ?NS_DISCO_ITEMS_s}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
[#xmlattr{name = 'var', [#xmlattr{name = 'var',
value = ?NS_MUC_s}]}, value = ?NS_MUC_s}]},

View File

@ -104,7 +104,7 @@
subject = "", subject = "",
subject_author = "", subject_author = "",
just_created = false, just_created = false,
activity = ?DICT:new(), activity = treap:empty(),
room_shaper, room_shaper,
room_queue = queue:new()}). room_queue = queue:new()}).
@ -251,13 +251,12 @@ normal_state({route, From, undefined,
message_time = Now, message_time = Now,
message_shaper = MessageShaper}, message_shaper = MessageShaper},
StateData1 = StateData1 =
StateData#state{ store_user_activity(
activity = ?DICT:store( From, NewActivity, StateData),
jlib:short_prepd_jid(From), StateData2 =
NewActivity, StateData1#state{
StateData#state.activity),
room_shaper = RoomShaper}, room_shaper = RoomShaper},
process_groupchat_message(From, Packet, StateData1); process_groupchat_message(From, Packet, StateData2);
true -> true ->
StateData1 = StateData1 =
if if
@ -278,13 +277,12 @@ normal_state({route, From, undefined,
{message, From}, {message, From},
StateData#state.room_queue), StateData#state.room_queue),
StateData2 = StateData2 =
StateData1#state{ store_user_activity(
activity = ?DICT:store( From, NewActivity, StateData1),
jlib:short_prepd_jid(From), StateData3 =
NewActivity, StateData2#state{
StateData#state.activity),
room_queue = RoomQueue}, room_queue = RoomQueue},
{next_state, normal_state, StateData2} {next_state, normal_state, StateData3}
end; end;
true -> true ->
MessageInterval = MessageInterval =
@ -298,11 +296,8 @@ normal_state({route, From, undefined,
message = Packet, message = Packet,
message_shaper = MessageShaper}, message_shaper = MessageShaper},
StateData1 = StateData1 =
StateData#state{ store_user_activity(
activity = ?DICT:store( From, NewActivity, StateData),
jlib:short_prepd_jid(From),
NewActivity,
StateData#state.activity)},
{next_state, normal_state, StateData1} {next_state, normal_state, StateData1}
end; end;
error -> error ->
@ -444,12 +439,7 @@ normal_state({route, From, Nick,
(Now >= Activity#activity.presence_time + MinPresenceInterval) and (Now >= Activity#activity.presence_time + MinPresenceInterval) and
(Activity#activity.presence == undefined) -> (Activity#activity.presence == undefined) ->
NewActivity = Activity#activity{presence_time = Now}, NewActivity = Activity#activity{presence_time = Now},
StateData1 = StateData1 = store_user_activity(From, NewActivity, StateData),
StateData#state{
activity = ?DICT:store(
jlib:short_prepd_jid(From),
NewActivity,
StateData#state.activity)},
process_presence(From, Nick, Packet, StateData1); process_presence(From, Nick, Packet, StateData1);
true -> true ->
if if
@ -462,12 +452,7 @@ normal_state({route, From, Nick,
ok ok
end, end,
NewActivity = Activity#activity{presence = {Nick, Packet}}, NewActivity = Activity#activity{presence = {Nick, Packet}},
StateData1 = StateData1 = store_user_activity(From, NewActivity, StateData),
StateData#state{
activity = ?DICT:store(
jlib:short_prepd_jid(From),
NewActivity,
StateData#state.activity)},
{next_state, normal_state, StateData1} {next_state, normal_state, StateData1}
end; end;
@ -758,27 +743,25 @@ handle_info(process_room_queue, normal_state = StateName, StateData) ->
Packet = Activity#activity.message, Packet = Activity#activity.message,
NewActivity = Activity#activity{message = undefined}, NewActivity = Activity#activity{message = undefined},
StateData1 = StateData1 =
StateData#state{ store_user_activity(
activity = ?DICT:store( From, NewActivity, StateData),
jlib:short_prepd_jid(From), StateData2 =
NewActivity, StateData1#state{
StateData#state.activity),
room_queue = RoomQueue}, room_queue = RoomQueue},
StateData2 = prepare_room_queue(StateData1), StateData3 = prepare_room_queue(StateData2),
process_groupchat_message(From, Packet, StateData2); process_groupchat_message(From, Packet, StateData3);
{{value, {presence, From}}, RoomQueue} -> {{value, {presence, From}}, RoomQueue} ->
Activity = get_user_activity(From, StateData), Activity = get_user_activity(From, StateData),
{Nick, Packet} = Activity#activity.presence, {Nick, Packet} = Activity#activity.presence,
NewActivity = Activity#activity{presence = undefined}, NewActivity = Activity#activity{presence = undefined},
StateData1 = StateData1 =
StateData#state{ store_user_activity(
activity = ?DICT:store( From, NewActivity, StateData),
jlib:short_prepd_jid(From), StateData2 =
NewActivity, StateData1#state{
StateData#state.activity),
room_queue = RoomQueue}, room_queue = RoomQueue},
StateData2 = prepare_room_queue(StateData1), StateData3 = prepare_room_queue(StateData2),
process_presence(From, Nick, Packet, StateData2); process_presence(From, Nick, Packet, StateData3);
{empty, _} -> {empty, _} ->
{next_state, StateName, StateData} {next_state, StateName, StateData}
end; end;
@ -1301,9 +1284,9 @@ get_max_users_admin_threshold(StateData) ->
mod_muc, max_users_admin_threshold, 5). mod_muc, max_users_admin_threshold, 5).
get_user_activity(JID, StateData) -> get_user_activity(JID, StateData) ->
case ?DICT:find(jlib:short_prepd_jid(JID), case treap:lookup(jlib:short_prepd_jid(JID),
StateData#state.activity) of StateData#state.activity) of
{ok, A} -> A; {ok, _P, A} -> A;
error -> error ->
MessageShaper = MessageShaper =
shaper:new(gen_mod:get_module_opt( shaper:new(gen_mod:get_module_opt(
@ -1317,6 +1300,82 @@ get_user_activity(JID, StateData) ->
presence_shaper = PresenceShaper} presence_shaper = PresenceShaper}
end. end.
store_user_activity(JID, UserActivity, StateData) ->
MinMessageInterval =
gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, min_message_interval, 0),
MinPresenceInterval =
gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, min_presence_interval, 0),
Key = jlib:short_prepd_jid(JID),
Now = now_to_usec(now()),
Activity1 = clean_treap(StateData#state.activity, {1, -Now}),
Activity =
case treap:lookup(Key, Activity1) of
{ok, _P, _A} ->
treap:delete(Key, Activity1);
error ->
Activity1
end,
StateData1 =
case (MinMessageInterval == 0) andalso
(MinPresenceInterval == 0) andalso
(UserActivity#activity.message_shaper == none) andalso
(UserActivity#activity.presence_shaper == none) andalso
(UserActivity#activity.message == undefined) andalso
(UserActivity#activity.presence == undefined) of
true ->
StateData#state{activity = Activity};
false ->
case (UserActivity#activity.message == undefined) andalso
(UserActivity#activity.presence == undefined) of
true ->
{_, MessageShaperInterval} =
shaper:update(UserActivity#activity.message_shaper,
100000),
{_, PresenceShaperInterval} =
shaper:update(UserActivity#activity.presence_shaper,
100000),
Delay = lists:max([MessageShaperInterval,
PresenceShaperInterval,
MinMessageInterval * 1000,
MinPresenceInterval * 1000]) * 1000,
Priority = {1, -(Now + Delay)},
StateData#state{
activity = treap:insert(
Key,
Priority,
UserActivity,
Activity)};
false ->
Priority = {0, 0},
StateData#state{
activity = treap:insert(
Key,
Priority,
UserActivity,
Activity)}
end
end,
StateData1.
clean_treap(Treap, CleanPriority) ->
case treap:is_empty(Treap) of
true ->
Treap;
false ->
{_Key, Priority, _Value} = treap:get_root(Treap),
if
Priority > CleanPriority ->
clean_treap(treap:delete_root(Treap), CleanPriority);
true ->
Treap
end
end.
prepare_room_queue(StateData) -> prepare_room_queue(StateData) ->
case queue:out(StateData#state.room_queue) of case queue:out(StateData#state.room_queue) of
{{value, {message, From}}, _RoomQueue} -> {{value, {message, From}}, _RoomQueue} ->
@ -2700,10 +2759,23 @@ check_allowed_persistent_change(XEl, StateData, From) ->
-define(PRIVATEXFIELD(Label, Var, Val), -define(PRIVATEXFIELD(Label, Var, Val),
?XFIELD("text-private", Label, Var, Val)). ?XFIELD("text-private", Label, Var, Val)).
get_default_room_maxusers(RoomState) ->
DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, ?MAX_USERS_DEFAULT),
RoomState2 = set_opts(DefRoomOpts, RoomState),
(RoomState2#state.config)#config.max_users.
get_config(Lang, StateData, From) -> get_config(Lang, StateData, From) ->
{_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access, {_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access,
ServiceMaxUsers = get_service_max_users(StateData), ServiceMaxUsers = get_service_max_users(StateData),
DefaultRoomMaxUsers = get_default_room_maxusers(StateData),
Config = StateData#state.config, Config = StateData#state.config,
{MaxUsersRoomInteger, MaxUsersRoomString} =
case get_max_users(StateData) of
N when is_integer(N) ->
{N, erlang:integer_to_list(N)};
_ -> {0, "none"}
end,
Res = Res =
[#xmlel{name = 'title', children = [ #xmlcdata{cdata = [#xmlel{name = 'title', children = [ #xmlcdata{cdata =
translate:translate(Lang, "Configuration for ") ++ translate:translate(Lang, "Configuration for ") ++
@ -2750,11 +2822,7 @@ get_config(Lang, StateData, From) ->
#xmlattr{name = 'var', value = "muc#roomconfig_maxusers"}], #xmlattr{name = 'var', value = "muc#roomconfig_maxusers"}],
children = [#xmlel{name = 'value', children = [#xmlel{name = 'value',
children = [#xmlcdata{cdata = children = [#xmlcdata{cdata =
case get_max_users(StateData) of MaxUsersRoomString}]}] ++
N when is_integer(N) ->
erlang:integer_to_list(N);
_ -> "none"
end}]}] ++
if if
is_integer(ServiceMaxUsers) -> []; is_integer(ServiceMaxUsers) -> [];
true -> true ->
@ -2767,7 +2835,8 @@ get_config(Lang, StateData, From) ->
value = erlang:integer_to_list(N)}], value = erlang:integer_to_list(N)}],
children = [#xmlel{name = 'value', children = [ children = [#xmlel{name = 'value', children = [
#xmlcdata{cdata = erlang:integer_to_list(N)}]}]} || #xmlcdata{cdata = erlang:integer_to_list(N)}]}]} ||
N <- ?MAX_USERS_DEFAULT_LIST, N =< ServiceMaxUsers]}, N <- lists:usort([ServiceMaxUsers, DefaultRoomMaxUsers, MaxUsersRoomInteger |
?MAX_USERS_DEFAULT_LIST]), N =< ServiceMaxUsers]},
#xmlel{name = 'field', attrs = [ #xmlel{name = 'field', attrs = [
#xmlattr{name = 'type', value = "list-single"}, #xmlattr{name = 'type', value = "list-single"},
#xmlattr{name = 'label', #xmlattr{name = 'label',

View File

@ -196,7 +196,6 @@ iq_disco_info(Lang, Name) ->
{"type", "bytestreams"}, {"type", "bytestreams"},
{"name", translate:translate(Lang, Name)}], []}, {"name", translate:translate(Lang, Name)}], []},
?FEATURE(?NS_DISCO_INFO), ?FEATURE(?NS_DISCO_INFO),
?FEATURE(?NS_DISCO_ITEMS),
?FEATURE(?NS_VCARD), ?FEATURE(?NS_VCARD),
?FEATURE(?NS_BYTESTREAMS)]. ?FEATURE(?NS_BYTESTREAMS)].

View File

@ -496,7 +496,7 @@ handle_cast({presence, JID, Pid}, State) ->
whitelist -> false; % subscribers are added manually whitelist -> false; % subscribers are added manually
authorize -> false; % likewise authorize -> false; % likewise
roster -> roster ->
Grps = get_option(Options, roster_groups_allowed), Grps = get_option(Options, roster_groups_allowed, []),
element(2, get_roster_info(User, Server, LJID, Grps)) element(2, get_roster_info(User, Server, LJID, Grps))
end, end,
if Subscribed -> if Subscribed ->
@ -518,8 +518,9 @@ handle_cast({presence, JID, Pid}, State) ->
end, end,
{noreply, State}; {noreply, State};
handle_cast({remove_user, User, Host}, State) -> handle_cast({remove_user, LUser, LServer}, State) ->
Owner = jlib:make_jid(User, Host, ""), Host = State#state.host,
Owner = jlib:make_jid(LUser, LServer, ""),
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
%% remove user's subscriptions %% remove user's subscriptions
lists:foreach(fun(Type) -> lists:foreach(fun(Type) ->
@ -537,7 +538,7 @@ handle_cast({remove_user, User, Host}, State) ->
delete_node(NodeKey, NodeName, Owner) delete_node(NodeKey, NodeName, Owner)
end, tree_action(Host, get_nodes, [OwnerKey])), end, tree_action(Host, get_nodes, [OwnerKey])),
%% remove user's nodes %% remove user's nodes
delete_node(Host, ["home", Host, User], Owner), delete_node(Host, ["home", LServer, LUser], Owner),
{noreply, State}; {noreply, State};
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
@ -759,6 +760,8 @@ iq_disco_info(Host, SNode, From, Lang) ->
[{"category", "pubsub"}, [{"category", "pubsub"},
{"type", "service"}, {"type", "service"},
{"name", translate:translate(Lang, "Publish-Subscribe")}], []}, {"name", translate:translate(Lang, "Publish-Subscribe")}], []},
{xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
{xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []},
{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []}, {xmlelement, "feature", [{"var", ?NS_PUBSUB}], []},
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++ {xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++
lists:map(fun(Feature) -> lists:map(fun(Feature) ->
@ -1349,7 +1352,7 @@ subscribe_node(Host, Node, From, JID) ->
SubscribeConfig = get_option(Options, subscribe), SubscribeConfig = get_option(Options, subscribe),
AccessModel = get_option(Options, access_model), AccessModel = get_option(Options, access_model),
SendLast = get_option(Options, send_last_published_item), SendLast = get_option(Options, send_last_published_item),
AllowedGroups = get_option(Options, roster_groups_allowed), AllowedGroups = get_option(Options, roster_groups_allowed, []),
{PresenceSubscription, RosterGroup} = {PresenceSubscription, RosterGroup} =
case Host of case Host of
{OUser, OServer, _} -> {OUser, OServer, _} ->
@ -1486,7 +1489,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
node_call(Type, publish_item, [Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload]) node_call(Type, publish_item, [Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload])
end end
end, end,
ejabberd_hooks:run(pubsub_publish_item, Host, [Host, Node, Publisher, service_jid(Host), ItemId, Payload]), ejabberd_hooks:run(pubsub_publish_item, ServerHost, [ServerHost, Node, Publisher, service_jid(Host), ItemId, Payload]),
Reply = [], Reply = [],
case transaction(Host, Node, Action, sync_dirty) of case transaction(Host, Node, Action, sync_dirty) of
{error, ?ERR_ITEM_NOT_FOUND} -> {error, ?ERR_ITEM_NOT_FOUND} ->
@ -1660,7 +1663,7 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) ->
RetreiveFeature = lists:member("retrieve-items", Features), RetreiveFeature = lists:member("retrieve-items", Features),
PersistentFeature = lists:member("persistent-items", Features), PersistentFeature = lists:member("persistent-items", Features),
AccessModel = get_option(Options, access_model), AccessModel = get_option(Options, access_model),
AllowedGroups = get_option(Options, roster_groups_allowed), AllowedGroups = get_option(Options, roster_groups_allowed, []),
{PresenceSubscription, RosterGroup} = {PresenceSubscription, RosterGroup} =
case Host of case Host of
{OUser, OServer, _} -> {OUser, OServer, _} ->
@ -2507,7 +2510,7 @@ get_configure_xfields(_Type, Options, Lang, _Owners) ->
{"label", translate:translate(Lang, "Roster groups allowed to subscribe")}, {"label", translate:translate(Lang, "Roster groups allowed to subscribe")},
{"var", "pubsub#roster_groups_allowed"}], {"var", "pubsub#roster_groups_allowed"}],
[{xmlelement, "value", [], [{xmlcdata, Value}]} || [{xmlelement, "value", [], [{xmlcdata, Value}]} ||
Value <- get_option(Options, roster_groups_allowed)]}, Value <- get_option(Options, roster_groups_allowed, [])]},
?ALIST_CONFIG_FIELD("Specify the publisher model", publish_model, ?ALIST_CONFIG_FIELD("Specify the publisher model", publish_model,
[publishers, subscribers, open]), [publishers, subscribers, open]),
?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size), ?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size),

View File

@ -455,10 +455,12 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
OldItem#pubsub_item{modification = PubId, OldItem#pubsub_item{modification = PubId,
payload = Payload} payload = Payload}
end, end,
Items = [ItemId | State#pubsub_state.items], Items = [ItemId | State#pubsub_state.items--[ItemId]],
{result, {NI, OI}} = remove_extra_items( {result, {NI, OI}} = remove_extra_items(
Host, Node, MaxItems, Items), Host, Node, MaxItems, Items),
set_item(Item), if MaxItems > 0 -> set_item(Item);
true -> ok
end,
set_state(State#pubsub_state{items = NI}), set_state(State#pubsub_state{items = NI}),
{result, {default, broadcast, OI}} {result, {default, broadcast, OI}}
end. end.

View File

@ -170,14 +170,9 @@ get_entity_affiliations(_Host, Owner) ->
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
node_default:get_entity_affiliations(OwnerKey, Owner). node_default:get_entity_affiliations(OwnerKey, Owner).
get_node_affiliations(_Host, Node) -> get_node_affiliations(Host, Node) ->
States = mnesia:match_object( OwnerKey = jlib:jid_remove_resource(Host),
#pubsub_state{stateid = {'_', {'_', Node}}, node_default:get_node_affiliations(OwnerKey, Node).
_ = '_'}),
Tr = fun(#pubsub_state{stateid = {J, {_, _}}, affiliation = A}) ->
{J, A}
end,
{result, lists:map(Tr, States)}.
get_affiliation(_Host, Node, Owner) -> get_affiliation(_Host, Node, Owner) ->
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),

View File

@ -320,7 +320,8 @@ push_item(User, Server, From, Item) ->
push_item(User, Server, Resource, From, Item) -> push_item(User, Server, Resource, From, Item) ->
Request = #xmlel{ns = ?NS_ROSTER, name = 'query', Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
children = [item_to_xml(Item)]}, children = [item_to_xml(Item)]},
ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request, "push"), ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request,
"push" ++ randoms:get_string()),
ejabberd_router:route( ejabberd_router:route(
From, From,
exmpp_jid:make_jid(User, Server, Resource), exmpp_jid:make_jid(User, Server, Resource),

View File

@ -355,7 +355,8 @@ push_item(User, Server, From, Item) ->
push_item(User, Server, Resource, From, Item) -> push_item(User, Server, Resource, From, Item) ->
Request = #xmlel{ns = ?NS_ROSTER, name = 'query', Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
children = [item_to_xml(Item)]}, children = [item_to_xml(Item)]},
ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request, "push"), ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request,
"push" ++ randoms:get_string()),
ejabberd_router:route( ejabberd_router:route(
From, From,
exmpp_jid:make_jid(User, Server, Resource), exmpp_jid:make_jid(User, Server, Resource),

View File

@ -244,7 +244,8 @@ set_new_rosteritems(UserFrom, ServerFrom,
set_item(User, Server, Resource, Item) -> set_item(User, Server, Resource, Item) ->
Request = #xmlel{ns = ?NS_ROSTER, name = 'query', Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
children = [mod_roster:item_to_xml(Item)]}, children = [mod_roster:item_to_xml(Item)]},
ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request, "push"), ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request,
"push" ++ randoms:get_string()),
ejabberd_router:route( ejabberd_router:route(
exmpp_jid:make_jid(User, Server, Resource), exmpp_jid:make_jid(User, Server, Resource),
exmpp_jid:make_bare_jid(Server), exmpp_jid:make_bare_jid(Server),
@ -567,7 +568,8 @@ push_item(User, Server, From, Item) ->
Item#roster.subscription}]}), Item#roster.subscription}]}),
Request = #xmlel{ns = ?NS_ROSTER, name = 'query', Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
children = [item_to_xml(Item)]}, children = [item_to_xml(Item)]},
Stanza = exmpp_iq:set(?NS_JABBER_CLIENT, Request, "push"), Stanza = exmpp_iq:set(?NS_JABBER_CLIENT, Request,
"push" ++ randoms:get_string()),
lists:foreach( lists:foreach(
fun(Resource) -> fun(Resource) ->
JID = exmpp_jid:make_jid(User, Server, Resource), JID = exmpp_jid:make_jid(User, Server, Resource),

View File

@ -141,9 +141,9 @@ get_sm_features(Acc, _From, _To, Node, _Lang) ->
[] -> [] ->
case Acc of case Acc of
{result, Features} -> {result, Features} ->
{result, [?NS_VCARD_s | Features]}; {result, [?NS_DISCO_INFO_s, ?NS_VCARD_s | Features]};
empty -> empty ->
{result, [?NS_VCARD_s]} {result, [?NS_DISCO_INFO_s, ?NS_VCARD_s]}
end; end;
_ -> _ ->
Acc Acc
@ -372,6 +372,10 @@ do_route(ServerHost, From, To, Packet) ->
#xmlattr{name = 'name', #xmlattr{name = 'name',
value = translate:translate(Lang, value = translate:translate(Lang,
"vCard User Search")}]}, "vCard User Search")}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature',
attrs = [
#xmlattr{name = 'var',
value = ?NS_DISCO_INFO_s}]},
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', #xmlel{ns = ?NS_DISCO_INFO, name = 'feature',
attrs = [ attrs = [
#xmlattr{name = 'var', #xmlattr{name = 'var',

View File

@ -318,8 +318,7 @@ pgsql_item_to_odbc(_) ->
%% part of init/1 %% part of init/1
%% Open a database connection to MySQL %% Open a database connection to MySQL
mysql_connect(Server, Port, DB, Username, Password, StartInterval) -> mysql_connect(Server, Port, DB, Username, Password, StartInterval) ->
NoLogFun = fun(_Level,_Format,_Argument) -> ok end, case mysql_conn:start(Server, Port, Username, Password, DB, fun log/3) of
case mysql_conn:start(Server, Port, Username, Password, DB, NoLogFun) of
{ok, Ref} -> {ok, Ref} ->
erlang:monitor(process, Ref), erlang:monitor(process, Ref),
mysql_conn:fetch(Ref, ["set names 'utf8';"], self()), mysql_conn:fetch(Ref, ["set names 'utf8';"], self()),
@ -359,3 +358,14 @@ mysql_item_to_odbc(Columns, Recs) ->
% perform a harmless query on all opened connexions to avoid connexion close. % perform a harmless query on all opened connexions to avoid connexion close.
keep_alive(PID) -> keep_alive(PID) ->
gen_server:call(PID, {sql_query, ?KEEPALIVE_QUERY}, 60000). gen_server:call(PID, {sql_query, ?KEEPALIVE_QUERY}, 60000).
% log function used by MySQL driver
log(Level, Format, Args) ->
case Level of
debug ->
?DEBUG(Format, Args);
normal ->
?INFO_MSG(Format, Args);
error ->
?ERROR_MSG(Format, Args)
end.

View File

@ -23,24 +23,185 @@
#include <erl_driver.h> #include <erl_driver.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#define BUF_SIZE 1024 #define BUF_SIZE 1024
typedef struct { typedef struct {
ErlDrvPort port; ErlDrvPort port;
SSL_CTX *ctx;
BIO *bio_read; BIO *bio_read;
BIO *bio_write; BIO *bio_write;
SSL *ssl; SSL *ssl;
} tls_data; } tls_data;
#ifdef _WIN32
typedef unsigned __int32 uint32_t;
#endif
/*
* str_hash is based on the public domain code from
* http://www.burtleburtle.net/bob/hash/doobs.html
*/
static uint32_t str_hash(char *s)
{
unsigned char *key = (unsigned char *)s;
uint32_t hash = 0;
size_t i;
for (i = 0; key[i] != 0; i++) {
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
/* Linear hashing */
#define MIN_LEVEL 8
#define MAX_LEVEL 20
struct bucket {
uint32_t hash;
char *key_file;
time_t mtime;
SSL_CTX *ssl_ctx;
struct bucket *next;
};
struct hash_table {
int split;
int level;
struct bucket **buckets;
int size;
};
struct hash_table ht;
static void init_hash_table()
{
size_t size = 1 << (MIN_LEVEL + 1);
size_t i;
ht.buckets = (struct bucket **)driver_alloc(sizeof(struct bucket *) * size);
ht.split = 0;
ht.level = MIN_LEVEL;
for (i = 0; i < size; i++)
ht.buckets[i] = NULL;
}
static void hash_table_insert(char *key_file, time_t mtime,
SSL_CTX *ssl_ctx)
{
int level, split;
uint32_t hash = str_hash(key_file);
size_t bucket;
int do_split = 0;
struct bucket *el;
struct bucket *new_bucket_el;
split = ht.split;
level = ht.level;
bucket = hash & ((1 << level) - 1);
if (bucket < split)
bucket = hash & ((1 << (level + 1)) - 1);
el = ht.buckets[bucket];
while (el != NULL) {
if (el->hash == hash && strcmp(el->key_file, key_file) == 0) {
el->mtime = mtime;
if (el->ssl_ctx != NULL)
SSL_CTX_free(el->ssl_ctx);
el->ssl_ctx = ssl_ctx;
break;
}
el = el->next;
}
if (el == NULL) {
if (ht.buckets[bucket] != NULL)
do_split = !0;
new_bucket_el = (struct bucket *)driver_alloc(sizeof(struct bucket));
new_bucket_el->hash = hash;
new_bucket_el->key_file = (char *)driver_alloc(strlen(key_file) + 1);
strcpy(new_bucket_el->key_file, key_file);
new_bucket_el->mtime = mtime;
new_bucket_el->ssl_ctx = ssl_ctx;
new_bucket_el->next = ht.buckets[bucket];
ht.buckets[bucket] = new_bucket_el;
}
if (do_split) {
struct bucket **el_ptr = &ht.buckets[split];
size_t new_bucket = split + (1 << level);
while (*el_ptr != NULL) {
uint32_t hash = (*el_ptr)->hash;
if ((hash & ((1 << (level + 1)) - 1)) == new_bucket) {
struct bucket *moved_el = *el_ptr;
*el_ptr = (*el_ptr)->next;
moved_el->next = ht.buckets[new_bucket];
ht.buckets[new_bucket] = moved_el;
} else
el_ptr = &(*el_ptr)->next;
}
split++;
if (split == 1 << level) {
size_t size;
size_t i;
split = 0;
level++;
size = 1 << (level + 1);
ht.split = split;
ht.level = level;
ht.buckets = (struct bucket **)
driver_realloc(ht.buckets, sizeof(struct bucket *) * size);
for (i = 1 << level; i < size; i++)
ht.buckets[i] = NULL;
} else
ht.split = split;
}
}
static SSL_CTX *hash_table_lookup(char *key_file, time_t *pmtime)
{
int level, split;
uint32_t hash = str_hash(key_file);
size_t bucket;
struct bucket *el;
split = ht.split;
level = ht.level;
bucket = hash & ((1 << level) - 1);
if (bucket < split)
bucket = hash & ((1 << (level + 1)) - 1);
el = ht.buckets[bucket];
while (el != NULL) {
if (el->hash == hash && strcmp(el->key_file, key_file) == 0) {
*pmtime = el->mtime;
return el->ssl_ctx;
}
el = el->next;
}
return NULL;
}
static ErlDrvData tls_drv_start(ErlDrvPort port, char *buff) static ErlDrvData tls_drv_start(ErlDrvPort port, char *buff)
{ {
tls_data *d = (tls_data *)driver_alloc(sizeof(tls_data)); tls_data *d = (tls_data *)driver_alloc(sizeof(tls_data));
d->port = port; d->port = port;
d->ctx = NULL;
d->bio_read = NULL; d->bio_read = NULL;
d->bio_write = NULL; d->bio_write = NULL;
d->ssl = NULL; d->ssl = NULL;
@ -57,12 +218,46 @@ static void tls_drv_stop(ErlDrvData handle)
if (d->ssl != NULL) if (d->ssl != NULL)
SSL_free(d->ssl); SSL_free(d->ssl);
if (d->ctx != NULL)
SSL_CTX_free(d->ctx);
driver_free((char *)handle); driver_free((char *)handle);
} }
static void tls_drv_finish()
{
int level;
struct bucket *el;
int i;
level = ht.level;
for (i = 0; i < 1 << (level + 1); i++) {
el = ht.buckets[i];
while (el != NULL) {
if (el->ssl_ctx != NULL)
SSL_CTX_free(el->ssl_ctx);
driver_free(el->key_file);
el = el->next;
}
}
driver_free(ht.buckets);
}
static int is_key_file_modified(char *file, time_t *key_file_mtime)
{
struct stat file_stat;
if (stat(file, &file_stat))
{
*key_file_mtime = 0;
return 1;
} else {
if (*key_file_mtime != file_stat.st_mtime)
{
*key_file_mtime = file_stat.st_mtime;
return 1;
} else
return 0;
}
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{ {
@ -122,29 +317,41 @@ static int tls_drv_control(ErlDrvData handle,
switch (command) switch (command)
{ {
case SET_CERTIFICATE_FILE_ACCEPT: case SET_CERTIFICATE_FILE_ACCEPT:
case SET_CERTIFICATE_FILE_CONNECT: case SET_CERTIFICATE_FILE_CONNECT: {
d->ctx = SSL_CTX_new(SSLv23_method()); time_t mtime = 0;
die_unless(d->ctx, "SSL_CTX_new failed"); SSL_CTX *ssl_ctx = hash_table_lookup(buf, &mtime);
if (is_key_file_modified(buf, &mtime) || ssl_ctx == NULL)
res = SSL_CTX_use_certificate_chain_file(d->ctx, buf);
die_unless(res > 0, "SSL_CTX_use_certificate_file failed");
res = SSL_CTX_use_PrivateKey_file(d->ctx, buf, SSL_FILETYPE_PEM);
die_unless(res > 0, "SSL_CTX_use_PrivateKey_file failed");
res = SSL_CTX_check_private_key(d->ctx);
die_unless(res > 0, "SSL_CTX_check_private_key failed");
SSL_CTX_set_default_verify_paths(d->ctx);
if (command == SET_CERTIFICATE_FILE_ACCEPT)
{ {
SSL_CTX_set_verify(d->ctx, SSL_CTX *ctx;
SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
verify_callback); hash_table_insert(buf, mtime, NULL);
ctx = SSL_CTX_new(SSLv23_method());
die_unless(ctx, "SSL_CTX_new failed");
res = SSL_CTX_use_certificate_chain_file(ctx, buf);
die_unless(res > 0, "SSL_CTX_use_certificate_file failed");
res = SSL_CTX_use_PrivateKey_file(ctx, buf, SSL_FILETYPE_PEM);
die_unless(res > 0, "SSL_CTX_use_PrivateKey_file failed");
res = SSL_CTX_check_private_key(ctx);
die_unless(res > 0, "SSL_CTX_check_private_key failed");
SSL_CTX_set_default_verify_paths(ctx);
if (command == SET_CERTIFICATE_FILE_ACCEPT)
{
SSL_CTX_set_verify(ctx,
SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
verify_callback);
}
ssl_ctx = ctx;
hash_table_insert(buf, mtime, ssl_ctx);
} }
d->ssl = SSL_new(d->ctx); d->ssl = SSL_new(ssl_ctx);
die_unless(d->ssl, "SSL_new failed"); die_unless(d->ssl, "SSL_new failed");
d->bio_read = BIO_new(BIO_s_mem()); d->bio_read = BIO_new(BIO_s_mem());
@ -154,9 +361,12 @@ static int tls_drv_control(ErlDrvData handle,
if (command == SET_CERTIFICATE_FILE_ACCEPT) if (command == SET_CERTIFICATE_FILE_ACCEPT)
SSL_set_accept_state(d->ssl); SSL_set_accept_state(d->ssl);
else else {
SSL_set_options(d->ssl, SSL_OP_NO_SSLv2);
SSL_set_connect_state(d->ssl); SSL_set_connect_state(d->ssl);
}
break; break;
}
case SET_ENCRYPTED_INPUT: case SET_ENCRYPTED_INPUT:
die_unless(d->ssl, "SSL not initialized"); die_unless(d->ssl, "SSL not initialized");
BIO_write(d->bio_read, buf, len); BIO_write(d->bio_read, buf, len);
@ -282,7 +492,7 @@ ErlDrvEntry tls_driver_entry = {
NULL, /* F_PTR ready_input, called when input descriptor ready */ NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */ NULL, /* F_PTR ready_output, called when output descriptor ready */
"tls_drv", /* char *driver_name, the argument to open_port */ "tls_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */ tls_drv_finish, /* F_PTR finish, called when unloaded */
NULL, /* handle */ NULL, /* handle */
tls_drv_control, /* F_PTR control, port_command callback */ tls_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */ NULL, /* F_PTR timeout, reserved */
@ -293,6 +503,7 @@ DRIVER_INIT(tls_drv) /* must match name in driver_entry */
{ {
OpenSSL_add_ssl_algorithms(); OpenSSL_add_ssl_algorithms();
SSL_load_error_strings(); SSL_load_error_strings();
init_hash_table();
return &tls_driver_entry; return &tls_driver_entry;
} }

View File

@ -89,8 +89,16 @@ load_file(Lang, File) ->
ets:insert(translations, ets:insert(translations,
{{Lang, Orig}, Trans1}) {{Lang, Orig}, Trans1})
end, Terms); end, Terms);
%% Code copied from ejabberd_config.erl
{error, {_LineNumber, erl_parse, _ParseMessage} = Reason} ->
ExitText = lists:flatten(File ++ " approximately in the line "
++ file:format_error(Reason)),
?ERROR_MSG("Problem loading translation file ~n~s", [ExitText]),
exit(ExitText);
{error, Reason} -> {error, Reason} ->
exit(file:format_error(Reason)) ExitText = lists:flatten(File ++ ": " ++ file:format_error(Reason)),
?ERROR_MSG("Problem loading translation file ~n~s", [ExitText]),
exit(ExitText)
end. end.
translate(Lang, Msg) -> translate(Lang, Msg) ->

View File

@ -202,7 +202,7 @@ process_header(State, Data) ->
request_version = Version, request_version = Version,
request_path = Path, request_path = Path,
request_keepalive = KeepAlive}; request_keepalive = KeepAlive};
{ok, {http_header, _, 'Connection', _, Conn}} -> {ok, {http_header, _, 'Connection'=Name, _, Conn}} ->
KeepAlive1 = case exmpp_stringprep:to_lower(Conn) of KeepAlive1 = case exmpp_stringprep:to_lower(Conn) of
"keep-alive" -> "keep-alive" ->
true; true;
@ -211,23 +211,27 @@ process_header(State, Data) ->
_ -> _ ->
State#state.request_keepalive State#state.request_keepalive
end, end,
State#state{request_keepalive = KeepAlive1}; State#state{request_keepalive = KeepAlive1,
{ok, {http_header, _, 'Authorization', _, Auth}} -> request_headers=add_header(Name, Conn, State)};
State#state{request_auth = parse_auth(Auth)}; {ok, {http_header, _, 'Authorization'=Name, _, Auth}} ->
{ok, {http_header, _, 'Content-Length', _, SLen}} -> State#state{request_auth = parse_auth(Auth),
request_headers=add_header(Name, Auth, State)};
{ok, {http_header, _, 'Content-Length'=Name, _, SLen}} ->
case catch list_to_integer(SLen) of case catch list_to_integer(SLen) of
Len when is_integer(Len) -> Len when is_integer(Len) ->
State#state{request_content_length = Len}; State#state{request_content_length = Len,
request_headers=add_header(Name, SLen, State)};
_ -> _ ->
State State
end; end;
{ok, {http_header, _, 'Accept-Language', _, Langs}} -> {ok, {http_header, _, 'Accept-Language'=Name, _, Langs}} ->
State#state{request_lang = parse_lang(Langs)}; State#state{request_lang = parse_lang(Langs),
{ok, {http_header, _, 'Host', _, Host}} -> request_headers=add_header(Name, Langs, State)};
State#state{request_host = Host}; {ok, {http_header, _, 'Host'=Name, _, Host}} ->
State#state{request_host = Host,
request_headers=add_header(Name, Host, State)};
{ok, {http_header, _, Name, _, Value}} -> {ok, {http_header, _, Name, _, Value}} ->
Headers = [{Name, Value} | State#state.request_headers], State#state{request_headers=add_header(Name, Value, State)};
State#state{request_headers=Headers};
{ok, http_eoh} -> {ok, http_eoh} ->
?DEBUG("(~w) http query: ~w ~s~n", ?DEBUG("(~w) http query: ~w ~s~n",
[State#state.socket, [State#state.socket,
@ -263,6 +267,9 @@ process_header(State, Data) ->
request_handlers = State#state.request_handlers} request_handlers = State#state.request_handlers}
end. end.
add_header(Name, Value, State) ->
[{Name, Value} | State#state.request_headers].
%% @spec (SockMod, HostPort) -> {Host::string(), Port::integer(), TP} %% @spec (SockMod, HostPort) -> {Host::string(), Port::integer(), TP}
%% where %% where
%% SockMod = gen_tcp | tls %% SockMod = gen_tcp | tls
@ -321,13 +328,13 @@ process_request(#state{request_method = Method,
{'EXIT', _} -> {'EXIT', _} ->
process_request(false); process_request(false);
{NPath, Query} -> {NPath, Query} ->
LPath = [path_decode(NPE) || NPE <- string:tokens(NPath, "/")],
LQuery = case (catch parse_urlencoded(Query)) of LQuery = case (catch parse_urlencoded(Query)) of
{'EXIT', _Reason} -> {'EXIT', _Reason} ->
[]; [];
LQ -> LQ ->
LQ LQ
end, end,
LPath = string:tokens(NPath, "/"),
{ok, IP} = {ok, IP} =
case SockMod of case SockMod of
gen_tcp -> gen_tcp ->
@ -386,7 +393,7 @@ process_request(#state{request_method = Method,
{'EXIT', _} -> {'EXIT', _} ->
process_request(false); process_request(false);
{NPath, _Query} -> {NPath, _Query} ->
LPath = string:tokens(NPath, "/"), LPath = [path_decode(NPE) || NPE <- string:tokens(NPath, "/")],
LQuery = case (catch parse_urlencoded(Data)) of LQuery = case (catch parse_urlencoded(Data)) of
{'EXIT', _Reason} -> {'EXIT', _Reason} ->
[]; [];
@ -557,24 +564,30 @@ parse_lang(Langs) ->
% notice as well as this list of conditions. % notice as well as this list of conditions.
%% url decode the path and return {Path, QueryPart} %% @doc Split the URL and return {Path, QueryPart}
url_decode_q_split(Path) -> url_decode_q_split(Path) ->
url_decode_q_split(Path, []). url_decode_q_split(Path, []).
url_decode_q_split([$?|T], Ack) ->
%% Don't decode the query string here, that is parsed separately.
{path_norm_reverse(Ack), T};
url_decode_q_split([H|T], Ack) when H /= 0 ->
url_decode_q_split(T, [H|Ack]);
url_decode_q_split([], Ack) ->
{path_norm_reverse(Ack), []}.
url_decode_q_split([$%, Hi, Lo | Tail], Ack) -> %% @doc Decode a part of the URL and return string()
path_decode(Path) ->
path_decode(Path, []).
path_decode([$%, Hi, Lo | Tail], Ack) ->
Hex = hex_to_integer([Hi, Lo]), Hex = hex_to_integer([Hi, Lo]),
if Hex == 0 -> exit(badurl); if Hex == 0 -> exit(badurl);
true -> ok true -> ok
end, end,
url_decode_q_split(Tail, [Hex|Ack]); path_decode(Tail, [Hex|Ack]);
url_decode_q_split([$?|T], Ack) -> path_decode([H|T], Ack) when H /= 0 ->
%% Don't decode the query string here, that is parsed separately. path_decode(T, [H|Ack]);
{path_norm_reverse(Ack), T}; path_decode([], Ack) ->
url_decode_q_split([H|T], Ack) when H /= 0 -> lists:reverse(Ack).
url_decode_q_split(T, [H|Ack]);
url_decode_q_split([], Ack) ->
{path_norm_reverse(Ack), []}.
path_norm_reverse("/" ++ T) -> start_dir(0, "/", T); path_norm_reverse("/" ++ T) -> start_dir(0, "/", T);
path_norm_reverse( T) -> start_dir(0, "", T). path_norm_reverse( T) -> start_dir(0, "", T).