mirror of
https://github.com/processone/ejabberd.git
synced 2024-06-18 22:15:20 +02:00
Merge from trunk (r1649 to r1692).
PR: EJABP-1 SVN Revision: 1695
This commit is contained in:
parent
bd300e870c
commit
9e96043255
132
ChangeLog
132
ChangeLog
|
@ -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>
|
||||
|
||||
* 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
|
||||
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>
|
||||
|
||||
* 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
|
||||
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>
|
||||
|
||||
* src/mod_irc/mod_irc.erl, src/mod_irc/mod_irc_connection.erl: Convert
|
||||
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>
|
||||
|
||||
* src/web/ejabberd_http.erl, src/web/ejabberd_http_poll.erl,
|
||||
|
@ -49,6 +177,10 @@
|
|||
|
||||
* 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>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: When requesting page of
|
||||
|
|
|
@ -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>.
|
||||
Moreover, the transport is only accessible by paying customers registered on
|
||||
our domains and on other servers.
|
||||
<PRE CLASS="verbatim">{acl, paying_customers, {user, "customer1", "example.net"}}.
|
||||
{acl, paying_customers, {user, "customer2", "example.com"}}.
|
||||
{acl, paying_customers, {user, "customer3", "example.org"}}.
|
||||
Moreover, the transport is only accessible to two users
|
||||
of <TT>example.org</TT>, and any user of <TT>example.com</TT>:
|
||||
<PRE CLASS="verbatim">{acl, paying_customers, {user, "customer1", "example.org"}}.
|
||||
{acl, paying_customers, {user, "customer2", "example.org"}}.
|
||||
{acl, paying_customers, {server, "example.com"}}.
|
||||
|
||||
{access, paying_customers, [{allow, paying_customers},
|
||||
{deny, all}]}.
|
||||
{access, irc_users, [{allow, paying_customers}, {deny, all}]}.
|
||||
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_irc, [{access, paying_customers},
|
||||
{mod_irc, [{access, irc_users},
|
||||
{host, "irc.example.net"}]},
|
||||
...
|
||||
]}.
|
||||
|
|
|
@ -2543,20 +2543,19 @@ Examples:
|
|||
]}.
|
||||
\end{verbatim}
|
||||
\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
|
||||
our domains and on other servers.
|
||||
Moreover, the transport is only accessible to two users
|
||||
of \term{example.org}, and any user of \term{example.com}:
|
||||
\begin{verbatim}
|
||||
{acl, paying_customers, {user, "customer1", "example.net"}}.
|
||||
{acl, paying_customers, {user, "customer2", "example.com"}}.
|
||||
{acl, paying_customers, {user, "customer3", "example.org"}}.
|
||||
{acl, paying_customers, {user, "customer1", "example.org"}}.
|
||||
{acl, paying_customers, {user, "customer2", "example.org"}}.
|
||||
{acl, paying_customers, {server, "example.com"}}.
|
||||
|
||||
{access, paying_customers, [{allow, paying_customers},
|
||||
{deny, all}]}.
|
||||
{access, irc_users, [{allow, paying_customers}, {deny, all}]}.
|
||||
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_irc, [{access, paying_customers},
|
||||
{mod_irc, [{access, irc_users},
|
||||
{host, "irc.example.net"}]},
|
||||
...
|
||||
]}.
|
||||
|
|
|
@ -85,7 +85,7 @@ EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
|
|||
# /share/doc/ejabberd
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
datarootdir = @datarootdir@
|
||||
DOCDIR = @docdir@
|
||||
DOCDIR = $(DESTDIR)@docdir@
|
||||
|
||||
# /usr/lib/ejabberd/ebin/
|
||||
BEAMDIR = $(EJABBERDDIR)/ebin
|
||||
|
@ -257,7 +257,7 @@ uninstall-all: uninstall-binary
|
|||
clean: clean-recursive clean-local
|
||||
|
||||
clean-local:
|
||||
rm -f *.beam $(ERLSHLIBS) epam
|
||||
rm -f *.beam $(ERLSHLIBS) epam ejabberdctl.example
|
||||
rm -f XmppAddr.asn1db XmppAddr.erl XmppAddr.hrl
|
||||
|
||||
distclean: distclean-recursive clean-local
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
-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").
|
||||
|
||||
|
@ -45,6 +45,7 @@ start(normal, _Args) ->
|
|||
randoms:start(),
|
||||
db_init(),
|
||||
sha:start(),
|
||||
start(),
|
||||
translate:start(),
|
||||
acl:start(),
|
||||
ejabberd_ctl:init(),
|
||||
|
@ -53,7 +54,6 @@ start(normal, _Args) ->
|
|||
gen_mod:start(),
|
||||
ejabberd_config:start(),
|
||||
ejabberd_check:config(),
|
||||
start(),
|
||||
connect_nodes(),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
ejabberd_rdbms:start(),
|
||||
|
@ -65,12 +65,13 @@ start(normal, _Args) ->
|
|||
%fprof:trace(start, "/tmp/fprof"),
|
||||
start_modules(),
|
||||
ejabberd_listener:start_listeners(),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
|
||||
Sup;
|
||||
start(_, _) ->
|
||||
{error, badarg}.
|
||||
|
||||
%% 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.
|
||||
prep_stop(State) ->
|
||||
stop_modules(),
|
||||
|
@ -79,6 +80,7 @@ prep_stop(State) ->
|
|||
|
||||
%% All the processes were killed when this function is called
|
||||
stop(_State) ->
|
||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
|
||||
ok.
|
||||
|
||||
|
||||
|
@ -93,18 +95,7 @@ init() ->
|
|||
register(ejabberd, self()),
|
||||
%erlang:system_flag(fullsweep_after, 0),
|
||||
%error_logger:logfile({open, ?LOG_PATH}),
|
||||
LogPath =
|
||||
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,
|
||||
LogPath = get_log_path(),
|
||||
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
|
||||
erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||
case erl_ddll:load_driver(ejabberd:get_so_path(), expat_erl) of
|
||||
|
@ -171,3 +162,21 @@ connect_nodes() ->
|
|||
end, Nodes)
|
||||
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.
|
||||
|
|
|
@ -63,6 +63,8 @@
|
|||
-define(SETS, gb_sets).
|
||||
-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,
|
||||
sockmod,
|
||||
socket_monitor,
|
||||
|
@ -170,6 +172,7 @@ init([{SockMod, Socket}, Opts]) ->
|
|||
TLSOpts = lists:filter(fun({certfile, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
Zlib = lists:member(zlib, Opts) andalso (not StartTLSRequired),
|
||||
IP = peerip(SockMod, Socket),
|
||||
%% Check if IP is blacklisted:
|
||||
case is_ip_blacklisted(IP) of
|
||||
|
|
|
@ -132,7 +132,10 @@ process(["status"]) ->
|
|||
[node(), InternalStatus, ProvidedStatus]),
|
||||
case lists:keysearch(ejabberd, 1, application:which_applications()) of
|
||||
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;
|
||||
{value, {_, _, Version}} ->
|
||||
?PRINT("ejabberd ~s is running in that node~n", [Version]),
|
||||
|
@ -237,7 +240,12 @@ try_run_ctp(Args) ->
|
|||
catch
|
||||
exit:Why ->
|
||||
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.
|
||||
|
||||
%% @spec (Args::[string()]) ->
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
|
||||
-record(state, {sockmod, socket, receiver}).
|
||||
|
||||
-define(HIBERNATE_TIMEOUT, 90000).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
|
@ -93,7 +95,7 @@ start(Module, SockMod, Socket, Opts) ->
|
|||
end.
|
||||
|
||||
starttls(FsmRef, TLSOpts) ->
|
||||
gen_server:call(FsmRef, {starttls, TLSOpts}),
|
||||
%gen_server:call(FsmRef, {starttls, TLSOpts}),
|
||||
FsmRef.
|
||||
|
||||
starttls(FsmRef, TLSOpts, Data) ->
|
||||
|
@ -136,7 +138,8 @@ sockname(FsmRef) ->
|
|||
gen_server:call(FsmRef, sockname).
|
||||
|
||||
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]) ->
|
||||
%% TODO: monitor the receiver
|
||||
Node = ejabberd_node_groups:get_closest_node(backend),
|
||||
{SockMod2, Socket2} = check_starttls(SockMod, Socket, Receiver, Opts),
|
||||
{ok, Pid} =
|
||||
rpc:call(Node, Module, start, [{?MODULE, self()}, Opts]),
|
||||
ejabberd_receiver:become_controller(Receiver, Pid),
|
||||
{ok, #state{sockmod = SockMod,
|
||||
socket = Socket,
|
||||
{ok, #state{sockmod = SockMod2,
|
||||
socket = Socket2,
|
||||
receiver = Receiver}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -173,7 +177,8 @@ handle_call({starttls, TLSOpts}, _From, State) ->
|
|||
{ok, TLSSocket} = tls:tcp_to_tls(State#state.socket, TLSOpts),
|
||||
ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
|
||||
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) ->
|
||||
{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(
|
||||
State#state.socket, Data),
|
||||
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) ->
|
||||
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
|
||||
|
@ -189,7 +195,8 @@ handle_call(compress, _From, State) ->
|
|||
State#state.socket),
|
||||
ejabberd_receiver:compress(State#state.receiver, ZlibSocket),
|
||||
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) ->
|
||||
{ok, ZlibSocket} = ejabberd_zlib:enable_zlib(
|
||||
|
@ -199,35 +206,36 @@ handle_call({compress, Data}, _From, State) ->
|
|||
catch (State#state.sockmod):send(
|
||||
State#state.socket, Data),
|
||||
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) ->
|
||||
ejabberd_receiver:reset_stream(State#state.receiver),
|
||||
Reply = ok,
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call({send, Data}, _From, State) ->
|
||||
catch (State#state.sockmod):send(
|
||||
State#state.socket, Data),
|
||||
Reply = ok,
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call({change_shaper, Shaper}, _From, State) ->
|
||||
ejabberd_receiver:change_shaper(State#state.receiver, Shaper),
|
||||
Reply = ok,
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call(get_sockmod, _From, State) ->
|
||||
Reply = State#state.sockmod,
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call(get_peer_certificate, _From, State) ->
|
||||
Reply = tls:get_peer_certificate(State#state.socket),
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call(get_verify_result, _From, State) ->
|
||||
Reply = tls:get_verify_result(State#state.socket),
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call(close, _From, State) ->
|
||||
ejabberd_receiver:close(State#state.receiver),
|
||||
|
@ -243,7 +251,7 @@ handle_call(sockname, _From, State) ->
|
|||
_ ->
|
||||
SockMod:sockname(Socket)
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call(peername, _From, State) ->
|
||||
#state{sockmod = SockMod, socket = Socket} = State,
|
||||
|
@ -254,11 +262,11 @@ handle_call(peername, _From, State) ->
|
|||
_ ->
|
||||
SockMod:peername(Socket)
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
|
@ -267,7 +275,7 @@ handle_call(_Request, _From, State) ->
|
|||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
|
@ -275,8 +283,11 @@ handle_cast(_Msg, State) ->
|
|||
%% {stop, Reason, State}
|
||||
%% 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) ->
|
||||
{noreply, State}.
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
|
@ -298,3 +309,17 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%%--------------------------------------------------------------------
|
||||
%%% 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.
|
||||
|
||||
|
|
|
@ -58,8 +58,9 @@ start_listeners() ->
|
|||
|
||||
start(Port, Module, Opts) ->
|
||||
%% Check if the module is an ejabberd listener or an independent listener
|
||||
case Module:socket_type() of
|
||||
independent -> Module:start_listener(Port, Opts);
|
||||
ModuleRaw = strip_frontend(Module),
|
||||
case ModuleRaw:socket_type() of
|
||||
independent -> ModuleRaw:start_listener(Port, Opts);
|
||||
_ -> start_dependent(Port, Module, Opts)
|
||||
end.
|
||||
|
||||
|
@ -120,12 +121,11 @@ accept(ListenSocket, Module, Opts) ->
|
|||
_ ->
|
||||
ok
|
||||
end,
|
||||
case Module of
|
||||
{frontend, Mod} ->
|
||||
ejabberd_frontend_socket:start(Mod, gen_tcp, Socket, Opts);
|
||||
_ ->
|
||||
ejabberd_socket:start(Module, gen_tcp, Socket, Opts)
|
||||
end,
|
||||
CallMod = case is_frontend(Module) of
|
||||
true -> ejabberd_frontend_socket;
|
||||
false -> ejabberd_socket
|
||||
end,
|
||||
CallMod:start(strip_frontend(Module), gen_tcp, Socket, Opts),
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("(~w) Failed TCP accept: ~w",
|
||||
|
@ -137,11 +137,12 @@ start_listener(Port, Module, Opts) ->
|
|||
start_module_sup(Port, Module),
|
||||
start_listener_sup(Port, Module, Opts).
|
||||
|
||||
%% Only required for some listeners, but doing for all doesn't hurt
|
||||
start_module_sup(_Port, Module) ->
|
||||
Proc1 = gen_mod:get_module_proc("sup", Module),
|
||||
ChildSpec1 =
|
||||
{Proc1,
|
||||
{ejabberd_tmp_sup, start_link, [Proc1, Module]},
|
||||
{ejabberd_tmp_sup, start_link, [Proc1, strip_frontend(Module)]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
|
@ -188,3 +189,10 @@ delete_listener(Port, Module) ->
|
|||
ejabberd_config:add_local_option(listen, Ports1),
|
||||
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.
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
xml_stream_state,
|
||||
timeout}).
|
||||
|
||||
-define(HIBERNATE_TIMEOUT, 90000).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
|
@ -138,7 +140,7 @@ handle_call({starttls, TLSSocket}, _From,
|
|||
xml_stream_state = NewXMLStreamState},
|
||||
case tls:recv_data(TLSSocket, "") of
|
||||
{ok, TLSData} ->
|
||||
{reply, ok, process_data(TLSData, NewState)};
|
||||
{reply, ok, process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, ok, NewState}
|
||||
end;
|
||||
|
@ -150,7 +152,7 @@ handle_call({compress, ZlibSocket}, _From,
|
|||
xml_stream_state = NewXMLStreamState},
|
||||
case ejabberd_zlib:recv_data(ZlibSocket, "") of
|
||||
{ok, ZlibData} ->
|
||||
{reply, ok, process_data(ZlibData, NewState)};
|
||||
{reply, ok, process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, ok, NewState}
|
||||
end;
|
||||
|
@ -158,7 +160,8 @@ handle_call(reset_stream, _From,
|
|||
#state{xml_stream_state = XMLStreamState} = State) ->
|
||||
NewXMLStreamState = exmpp_xmlstream:reset(XMLStreamState),
|
||||
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) ->
|
||||
Parser = exmpp_xml:start_parser([
|
||||
{namespace, true},
|
||||
|
@ -174,10 +177,10 @@ handle_call({become_controller, C2SPid}, _From, State) ->
|
|||
xml_stream_state = XMLStreamState},
|
||||
activate_socket(NewState),
|
||||
Reply = ok,
|
||||
{reply, Reply, NewState};
|
||||
{reply, Reply, NewState, ?HIBERNATE_TIMEOUT};
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
{reply, Reply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
|
@ -187,11 +190,11 @@ handle_call(_Request, _From, State) ->
|
|||
%%--------------------------------------------------------------------
|
||||
handle_cast({change_shaper, Shaper}, State) ->
|
||||
NewShaperState = shaper:new(Shaper),
|
||||
{noreply, State#state{shaper_state = NewShaperState}};
|
||||
{noreply, State#state{shaper_state = NewShaperState}, ?HIBERNATE_TIMEOUT};
|
||||
handle_cast(close, State) ->
|
||||
{stop, normal, State};
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
|
@ -207,19 +210,21 @@ handle_info({Tag, _TCPSocket, Data},
|
|||
tls ->
|
||||
case tls:recv_data(Socket, Data) of
|
||||
{ok, TLSData} ->
|
||||
{noreply, process_data(TLSData, State)};
|
||||
{noreply, process_data(TLSData, State),
|
||||
?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, State}
|
||||
end;
|
||||
ejabberd_zlib ->
|
||||
case ejabberd_zlib:recv_data(Socket, Data) of
|
||||
{ok, ZlibData} ->
|
||||
{noreply, process_data(ZlibData, State)};
|
||||
{noreply, process_data(ZlibData, State),
|
||||
?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, State}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, process_data(Data, State)}
|
||||
{noreply, process_data(Data, State), ?HIBERNATE_TIMEOUT}
|
||||
end;
|
||||
handle_info({Tag, _TCPSocket}, State)
|
||||
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) ->
|
||||
case Reason of
|
||||
timeout ->
|
||||
{noreply, State};
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT};
|
||||
_ ->
|
||||
{stop, normal, State}
|
||||
end;
|
||||
handle_info({timeout, _Ref, activate}, 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) ->
|
||||
{noreply, State}.
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
handle_info/3,
|
||||
terminate/3,
|
||||
code_change/4,
|
||||
test_get_addr_port/1]).
|
||||
test_get_addr_port/1,
|
||||
get_addr_port/1]).
|
||||
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
|
||||
|
|
|
@ -99,6 +99,21 @@ init([]) ->
|
|||
infinity,
|
||||
supervisor,
|
||||
[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 =
|
||||
{ejabberd_s2s_out_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
|
@ -115,6 +130,14 @@ init([]) ->
|
|||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPSupervisor =
|
||||
{ejabberd_http_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_http_sup, ejabberd_http]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPPollSupervisor =
|
||||
{ejabberd_http_poll_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
|
@ -148,8 +171,11 @@ init([]) ->
|
|||
S2S,
|
||||
Local,
|
||||
ReceiverSupervisor,
|
||||
C2SSupervisor,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
ServiceSupervisor,
|
||||
HTTPSupervisor,
|
||||
HTTPPollSupervisor,
|
||||
IQSupervisor,
|
||||
FrontendSocketSupervisor,
|
||||
|
|
|
@ -13,14 +13,6 @@ HOST=localhost
|
|||
ERLANG_NODE=$NODE@$HOST
|
||||
ERL=@erl@
|
||||
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
|
||||
ARGS=
|
||||
|
@ -30,6 +22,7 @@ while [ $# -ne 0 ] ; do
|
|||
case $PARAM in
|
||||
--) break ;;
|
||||
--node) ERLANG_NODE=$1; shift ;;
|
||||
--config-dir) ETCDIR=$1 ; shift ;;
|
||||
--config) EJABBERD_CONFIG_PATH=$1 ; shift ;;
|
||||
--ctl-config) EJABBERDCTL_CONFIG_PATH=$1 ; shift ;;
|
||||
--logs) LOGS_DIR=$1 ; shift ;;
|
||||
|
@ -38,6 +31,24 @@ while [ $# -ne 0 ] ; do
|
|||
esac
|
||||
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
|
||||
ID=`id -g`
|
||||
EJID=`id -g $INSTALLUSER`
|
||||
|
@ -172,6 +183,7 @@ help ()
|
|||
echo " live Start an ejabberd node in live (interactive) mode"
|
||||
echo ""
|
||||
echo "Optional parameters when starting an ejabberd node:"
|
||||
echo " --config-dir dir Config ejabberd: $ETCDIR"
|
||||
echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH"
|
||||
echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH"
|
||||
echo " --logs dir Directory for logs: $LOGS_DIR"
|
||||
|
|
|
@ -6,6 +6,8 @@ CPPFLAGS = @CPPFLAGS@
|
|||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
ASN_FLAGS = -bber_bin +optimize +driver
|
||||
|
||||
ERLANG_CFLAGS = @ERLANG_CFLAGS@
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
|
@ -27,7 +29,7 @@ all: $(BEAMS) ELDAPv3.beam
|
|||
ELDAPv3.beam: ELDAPv3.erl
|
||||
|
||||
ELDAPv3.erl: ELDAPv3.asn
|
||||
@ERLC@ -bber_bin -W $(EFLAGS) $<
|
||||
@ERLC@ $(ASN_FLAGS) -W $(EFLAGS) $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
|
|
@ -6,6 +6,8 @@ EFLAGS = -I .. -pz ..
|
|||
OUTDIR = ..
|
||||
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam
|
||||
|
||||
ASN_FLAGS = -bber_bin +optimize +driver
|
||||
|
||||
ALL : $(BEAMS)
|
||||
|
||||
Clean :
|
||||
|
@ -16,7 +18,7 @@ Clean :
|
|||
-@erase $(BEAMS)
|
||||
|
||||
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
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
|
||||
-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) ->
|
||||
spawn(?MODULE, init, [Host, ExtPrg]).
|
||||
|
@ -41,7 +42,7 @@ init(Host, ExtPrg) ->
|
|||
register(gen_mod:get_module_proc(Host, eauth), self()),
|
||||
process_flag(trap_exit,true),
|
||||
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
|
||||
loop(Port).
|
||||
loop(Port, ?INIT_TIMEOUT).
|
||||
|
||||
stop(Host) ->
|
||||
gen_mod:get_module_proc(Host, eauth) ! stop.
|
||||
|
@ -63,21 +64,23 @@ call_port(Server, Msg) ->
|
|||
Result
|
||||
end.
|
||||
|
||||
loop(Port) ->
|
||||
loop(Port, Timeout) ->
|
||||
receive
|
||||
{call, Caller, Msg} ->
|
||||
Port ! {self(), {command, encode(Msg)}},
|
||||
receive
|
||||
{Port, {data, Data}} ->
|
||||
?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]),
|
||||
Caller ! {eauth, decode(Data)};
|
||||
{Port, Other} ->
|
||||
?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other])
|
||||
Caller ! {eauth, decode(Data)};
|
||||
{Port, Other} ->
|
||||
?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]),
|
||||
Caller ! {eauth, false}
|
||||
after
|
||||
?CALL_TIMEOUT ->
|
||||
?ERROR_MSG("extauth call '~p' didn't receive response~n", [Msg])
|
||||
Timeout ->
|
||||
?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]),
|
||||
Caller ! {eauth, false}
|
||||
end,
|
||||
loop(Port);
|
||||
loop(Port, ?CALL_TIMEOUT);
|
||||
stop ->
|
||||
Port ! {self(), close},
|
||||
receive
|
||||
|
|
|
@ -62,14 +62,16 @@ start() ->
|
|||
|
||||
|
||||
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
|
||||
{'EXIT', Reason} ->
|
||||
del_module_mnesia(Host, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
set_module_opts_mnesia(Host, Module, Opts),
|
||||
ets:insert(ejabberd_modules,
|
||||
#ejabberd_module{module_host = {Module, Host},
|
||||
opts = Opts}),
|
||||
ok
|
||||
end.
|
||||
|
||||
|
@ -224,6 +226,8 @@ get_hosts(Opts, Prefix) ->
|
|||
Hosts
|
||||
end.
|
||||
|
||||
get_module_proc(Host, {frontend, Base}) ->
|
||||
get_module_proc("frontend_" ++ Host, Base);
|
||||
get_module_proc(Host, Base) ->
|
||||
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
|
||||
|
||||
|
|
|
@ -305,6 +305,8 @@ iq_disco(Lang) ->
|
|||
[#xmlattr{name = 'category', value = "conference"},
|
||||
#xmlattr{name = 'type', value = "irc"},
|
||||
#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 =
|
||||
[#xmlattr{name = 'var', value = ?NS_MUC_s}]},
|
||||
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||
|
|
|
@ -499,6 +499,12 @@ iq_disco_info(Lang) ->
|
|||
value = "text"},
|
||||
#xmlattr{name = 'name',
|
||||
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 =
|
||||
[#xmlattr{name = 'var',
|
||||
value = ?NS_MUC_s}]},
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
subject = "",
|
||||
subject_author = "",
|
||||
just_created = false,
|
||||
activity = ?DICT:new(),
|
||||
activity = treap:empty(),
|
||||
room_shaper,
|
||||
room_queue = queue:new()}).
|
||||
|
||||
|
@ -251,13 +251,12 @@ normal_state({route, From, undefined,
|
|||
message_time = Now,
|
||||
message_shaper = MessageShaper},
|
||||
StateData1 =
|
||||
StateData#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity),
|
||||
store_user_activity(
|
||||
From, NewActivity, StateData),
|
||||
StateData2 =
|
||||
StateData1#state{
|
||||
room_shaper = RoomShaper},
|
||||
process_groupchat_message(From, Packet, StateData1);
|
||||
process_groupchat_message(From, Packet, StateData2);
|
||||
true ->
|
||||
StateData1 =
|
||||
if
|
||||
|
@ -278,13 +277,12 @@ normal_state({route, From, undefined,
|
|||
{message, From},
|
||||
StateData#state.room_queue),
|
||||
StateData2 =
|
||||
StateData1#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity),
|
||||
store_user_activity(
|
||||
From, NewActivity, StateData1),
|
||||
StateData3 =
|
||||
StateData2#state{
|
||||
room_queue = RoomQueue},
|
||||
{next_state, normal_state, StateData2}
|
||||
{next_state, normal_state, StateData3}
|
||||
end;
|
||||
true ->
|
||||
MessageInterval =
|
||||
|
@ -298,11 +296,8 @@ normal_state({route, From, undefined,
|
|||
message = Packet,
|
||||
message_shaper = MessageShaper},
|
||||
StateData1 =
|
||||
StateData#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity)},
|
||||
store_user_activity(
|
||||
From, NewActivity, StateData),
|
||||
{next_state, normal_state, StateData1}
|
||||
end;
|
||||
error ->
|
||||
|
@ -444,12 +439,7 @@ normal_state({route, From, Nick,
|
|||
(Now >= Activity#activity.presence_time + MinPresenceInterval) and
|
||||
(Activity#activity.presence == undefined) ->
|
||||
NewActivity = Activity#activity{presence_time = Now},
|
||||
StateData1 =
|
||||
StateData#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity)},
|
||||
StateData1 = store_user_activity(From, NewActivity, StateData),
|
||||
process_presence(From, Nick, Packet, StateData1);
|
||||
true ->
|
||||
if
|
||||
|
@ -462,12 +452,7 @@ normal_state({route, From, Nick,
|
|||
ok
|
||||
end,
|
||||
NewActivity = Activity#activity{presence = {Nick, Packet}},
|
||||
StateData1 =
|
||||
StateData#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity)},
|
||||
StateData1 = store_user_activity(From, NewActivity, StateData),
|
||||
{next_state, normal_state, StateData1}
|
||||
end;
|
||||
|
||||
|
@ -758,27 +743,25 @@ handle_info(process_room_queue, normal_state = StateName, StateData) ->
|
|||
Packet = Activity#activity.message,
|
||||
NewActivity = Activity#activity{message = undefined},
|
||||
StateData1 =
|
||||
StateData#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity),
|
||||
store_user_activity(
|
||||
From, NewActivity, StateData),
|
||||
StateData2 =
|
||||
StateData1#state{
|
||||
room_queue = RoomQueue},
|
||||
StateData2 = prepare_room_queue(StateData1),
|
||||
process_groupchat_message(From, Packet, StateData2);
|
||||
StateData3 = prepare_room_queue(StateData2),
|
||||
process_groupchat_message(From, Packet, StateData3);
|
||||
{{value, {presence, From}}, RoomQueue} ->
|
||||
Activity = get_user_activity(From, StateData),
|
||||
{Nick, Packet} = Activity#activity.presence,
|
||||
NewActivity = Activity#activity{presence = undefined},
|
||||
StateData1 =
|
||||
StateData#state{
|
||||
activity = ?DICT:store(
|
||||
jlib:short_prepd_jid(From),
|
||||
NewActivity,
|
||||
StateData#state.activity),
|
||||
store_user_activity(
|
||||
From, NewActivity, StateData),
|
||||
StateData2 =
|
||||
StateData1#state{
|
||||
room_queue = RoomQueue},
|
||||
StateData2 = prepare_room_queue(StateData1),
|
||||
process_presence(From, Nick, Packet, StateData2);
|
||||
StateData3 = prepare_room_queue(StateData2),
|
||||
process_presence(From, Nick, Packet, StateData3);
|
||||
{empty, _} ->
|
||||
{next_state, StateName, StateData}
|
||||
end;
|
||||
|
@ -1301,9 +1284,9 @@ get_max_users_admin_threshold(StateData) ->
|
|||
mod_muc, max_users_admin_threshold, 5).
|
||||
|
||||
get_user_activity(JID, StateData) ->
|
||||
case ?DICT:find(jlib:short_prepd_jid(JID),
|
||||
StateData#state.activity) of
|
||||
{ok, A} -> A;
|
||||
case treap:lookup(jlib:short_prepd_jid(JID),
|
||||
StateData#state.activity) of
|
||||
{ok, _P, A} -> A;
|
||||
error ->
|
||||
MessageShaper =
|
||||
shaper:new(gen_mod:get_module_opt(
|
||||
|
@ -1317,6 +1300,82 @@ get_user_activity(JID, StateData) ->
|
|||
presence_shaper = PresenceShaper}
|
||||
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) ->
|
||||
case queue:out(StateData#state.room_queue) of
|
||||
{{value, {message, From}}, _RoomQueue} ->
|
||||
|
@ -2700,10 +2759,23 @@ check_allowed_persistent_change(XEl, StateData, From) ->
|
|||
-define(PRIVATEXFIELD(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) ->
|
||||
{_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access,
|
||||
ServiceMaxUsers = get_service_max_users(StateData),
|
||||
DefaultRoomMaxUsers = get_default_room_maxusers(StateData),
|
||||
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 =
|
||||
[#xmlel{name = 'title', children = [ #xmlcdata{cdata =
|
||||
translate:translate(Lang, "Configuration for ") ++
|
||||
|
@ -2750,11 +2822,7 @@ get_config(Lang, StateData, From) ->
|
|||
#xmlattr{name = 'var', value = "muc#roomconfig_maxusers"}],
|
||||
children = [#xmlel{name = 'value',
|
||||
children = [#xmlcdata{cdata =
|
||||
case get_max_users(StateData) of
|
||||
N when is_integer(N) ->
|
||||
erlang:integer_to_list(N);
|
||||
_ -> "none"
|
||||
end}]}] ++
|
||||
MaxUsersRoomString}]}] ++
|
||||
if
|
||||
is_integer(ServiceMaxUsers) -> [];
|
||||
true ->
|
||||
|
@ -2767,7 +2835,8 @@ get_config(Lang, StateData, From) ->
|
|||
value = erlang:integer_to_list(N)}],
|
||||
children = [#xmlel{name = 'value', children = [
|
||||
#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 = [
|
||||
#xmlattr{name = 'type', value = "list-single"},
|
||||
#xmlattr{name = 'label',
|
||||
|
|
|
@ -196,7 +196,6 @@ iq_disco_info(Lang, Name) ->
|
|||
{"type", "bytestreams"},
|
||||
{"name", translate:translate(Lang, Name)}], []},
|
||||
?FEATURE(?NS_DISCO_INFO),
|
||||
?FEATURE(?NS_DISCO_ITEMS),
|
||||
?FEATURE(?NS_VCARD),
|
||||
?FEATURE(?NS_BYTESTREAMS)].
|
||||
|
||||
|
|
|
@ -496,7 +496,7 @@ handle_cast({presence, JID, Pid}, State) ->
|
|||
whitelist -> false; % subscribers are added manually
|
||||
authorize -> false; % likewise
|
||||
roster ->
|
||||
Grps = get_option(Options, roster_groups_allowed),
|
||||
Grps = get_option(Options, roster_groups_allowed, []),
|
||||
element(2, get_roster_info(User, Server, LJID, Grps))
|
||||
end,
|
||||
if Subscribed ->
|
||||
|
@ -518,8 +518,9 @@ handle_cast({presence, JID, Pid}, State) ->
|
|||
end,
|
||||
{noreply, State};
|
||||
|
||||
handle_cast({remove_user, User, Host}, State) ->
|
||||
Owner = jlib:make_jid(User, Host, ""),
|
||||
handle_cast({remove_user, LUser, LServer}, State) ->
|
||||
Host = State#state.host,
|
||||
Owner = jlib:make_jid(LUser, LServer, ""),
|
||||
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||
%% remove user's subscriptions
|
||||
lists:foreach(fun(Type) ->
|
||||
|
@ -537,7 +538,7 @@ handle_cast({remove_user, User, Host}, State) ->
|
|||
delete_node(NodeKey, NodeName, Owner)
|
||||
end, tree_action(Host, get_nodes, [OwnerKey])),
|
||||
%% remove user's nodes
|
||||
delete_node(Host, ["home", Host, User], Owner),
|
||||
delete_node(Host, ["home", LServer, LUser], Owner),
|
||||
{noreply, State};
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
|
@ -759,6 +760,8 @@ iq_disco_info(Host, SNode, From, Lang) ->
|
|||
[{"category", "pubsub"},
|
||||
{"type", "service"},
|
||||
{"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_VCARD}], []}] ++
|
||||
lists:map(fun(Feature) ->
|
||||
|
@ -1349,7 +1352,7 @@ subscribe_node(Host, Node, From, JID) ->
|
|||
SubscribeConfig = get_option(Options, subscribe),
|
||||
AccessModel = get_option(Options, access_model),
|
||||
SendLast = get_option(Options, send_last_published_item),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} =
|
||||
case Host of
|
||||
{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])
|
||||
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 = [],
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{error, ?ERR_ITEM_NOT_FOUND} ->
|
||||
|
@ -1660,7 +1663,7 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) ->
|
|||
RetreiveFeature = lists:member("retrieve-items", Features),
|
||||
PersistentFeature = lists:member("persistent-items", Features),
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} =
|
||||
case Host of
|
||||
{OUser, OServer, _} ->
|
||||
|
@ -2507,7 +2510,7 @@ get_configure_xfields(_Type, Options, Lang, _Owners) ->
|
|||
{"label", translate:translate(Lang, "Roster groups allowed to subscribe")},
|
||||
{"var", "pubsub#roster_groups_allowed"}],
|
||||
[{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,
|
||||
[publishers, subscribers, open]),
|
||||
?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size),
|
||||
|
|
|
@ -455,10 +455,12 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
|
|||
OldItem#pubsub_item{modification = PubId,
|
||||
payload = Payload}
|
||||
end,
|
||||
Items = [ItemId | State#pubsub_state.items],
|
||||
Items = [ItemId | State#pubsub_state.items--[ItemId]],
|
||||
{result, {NI, OI}} = remove_extra_items(
|
||||
Host, Node, MaxItems, Items),
|
||||
set_item(Item),
|
||||
if MaxItems > 0 -> set_item(Item);
|
||||
true -> ok
|
||||
end,
|
||||
set_state(State#pubsub_state{items = NI}),
|
||||
{result, {default, broadcast, OI}}
|
||||
end.
|
||||
|
|
|
@ -170,14 +170,9 @@ get_entity_affiliations(_Host, Owner) ->
|
|||
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||
node_default:get_entity_affiliations(OwnerKey, Owner).
|
||||
|
||||
get_node_affiliations(_Host, Node) ->
|
||||
States = mnesia:match_object(
|
||||
#pubsub_state{stateid = {'_', {'_', Node}},
|
||||
_ = '_'}),
|
||||
Tr = fun(#pubsub_state{stateid = {J, {_, _}}, affiliation = A}) ->
|
||||
{J, A}
|
||||
end,
|
||||
{result, lists:map(Tr, States)}.
|
||||
get_node_affiliations(Host, Node) ->
|
||||
OwnerKey = jlib:jid_remove_resource(Host),
|
||||
node_default:get_node_affiliations(OwnerKey, Node).
|
||||
|
||||
get_affiliation(_Host, Node, Owner) ->
|
||||
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||
|
|
|
@ -320,7 +320,8 @@ push_item(User, Server, From, Item) ->
|
|||
push_item(User, Server, Resource, From, Item) ->
|
||||
Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
|
||||
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(
|
||||
From,
|
||||
exmpp_jid:make_jid(User, Server, Resource),
|
||||
|
|
|
@ -355,7 +355,8 @@ push_item(User, Server, From, Item) ->
|
|||
push_item(User, Server, Resource, From, Item) ->
|
||||
Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
|
||||
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(
|
||||
From,
|
||||
exmpp_jid:make_jid(User, Server, Resource),
|
||||
|
|
|
@ -244,7 +244,8 @@ set_new_rosteritems(UserFrom, ServerFrom,
|
|||
set_item(User, Server, Resource, Item) ->
|
||||
Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
|
||||
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(
|
||||
exmpp_jid:make_jid(User, Server, Resource),
|
||||
exmpp_jid:make_bare_jid(Server),
|
||||
|
@ -567,7 +568,8 @@ push_item(User, Server, From, Item) ->
|
|||
Item#roster.subscription}]}),
|
||||
Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
|
||||
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(
|
||||
fun(Resource) ->
|
||||
JID = exmpp_jid:make_jid(User, Server, Resource),
|
||||
|
|
|
@ -141,9 +141,9 @@ get_sm_features(Acc, _From, _To, Node, _Lang) ->
|
|||
[] ->
|
||||
case Acc of
|
||||
{result, Features} ->
|
||||
{result, [?NS_VCARD_s | Features]};
|
||||
{result, [?NS_DISCO_INFO_s, ?NS_VCARD_s | Features]};
|
||||
empty ->
|
||||
{result, [?NS_VCARD_s]}
|
||||
{result, [?NS_DISCO_INFO_s, ?NS_VCARD_s]}
|
||||
end;
|
||||
_ ->
|
||||
Acc
|
||||
|
@ -372,6 +372,10 @@ do_route(ServerHost, From, To, Packet) ->
|
|||
#xmlattr{name = 'name',
|
||||
value = translate:translate(Lang,
|
||||
"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',
|
||||
attrs = [
|
||||
#xmlattr{name = 'var',
|
||||
|
|
|
@ -318,8 +318,7 @@ pgsql_item_to_odbc(_) ->
|
|||
%% part of init/1
|
||||
%% Open a database connection to MySQL
|
||||
mysql_connect(Server, Port, DB, Username, Password, StartInterval) ->
|
||||
NoLogFun = fun(_Level,_Format,_Argument) -> ok end,
|
||||
case mysql_conn:start(Server, Port, Username, Password, DB, NoLogFun) of
|
||||
case mysql_conn:start(Server, Port, Username, Password, DB, fun log/3) of
|
||||
{ok, Ref} ->
|
||||
erlang:monitor(process, Ref),
|
||||
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.
|
||||
keep_alive(PID) ->
|
||||
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.
|
||||
|
|
|
@ -23,24 +23,185 @@
|
|||
#include <erl_driver.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
ErlDrvPort port;
|
||||
SSL_CTX *ctx;
|
||||
BIO *bio_read;
|
||||
BIO *bio_write;
|
||||
SSL *ssl;
|
||||
} 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)
|
||||
{
|
||||
tls_data *d = (tls_data *)driver_alloc(sizeof(tls_data));
|
||||
d->port = port;
|
||||
d->ctx = NULL;
|
||||
d->bio_read = NULL;
|
||||
d->bio_write = NULL;
|
||||
d->ssl = NULL;
|
||||
|
@ -57,12 +218,46 @@ static void tls_drv_stop(ErlDrvData handle)
|
|||
if (d->ssl != NULL)
|
||||
SSL_free(d->ssl);
|
||||
|
||||
if (d->ctx != NULL)
|
||||
SSL_CTX_free(d->ctx);
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -122,29 +317,41 @@ static int tls_drv_control(ErlDrvData handle,
|
|||
switch (command)
|
||||
{
|
||||
case SET_CERTIFICATE_FILE_ACCEPT:
|
||||
case SET_CERTIFICATE_FILE_CONNECT:
|
||||
d->ctx = SSL_CTX_new(SSLv23_method());
|
||||
die_unless(d->ctx, "SSL_CTX_new failed");
|
||||
|
||||
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)
|
||||
case SET_CERTIFICATE_FILE_CONNECT: {
|
||||
time_t mtime = 0;
|
||||
SSL_CTX *ssl_ctx = hash_table_lookup(buf, &mtime);
|
||||
if (is_key_file_modified(buf, &mtime) || ssl_ctx == NULL)
|
||||
{
|
||||
SSL_CTX_set_verify(d->ctx,
|
||||
SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
|
||||
verify_callback);
|
||||
SSL_CTX *ctx;
|
||||
|
||||
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");
|
||||
|
||||
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)
|
||||
SSL_set_accept_state(d->ssl);
|
||||
else
|
||||
else {
|
||||
SSL_set_options(d->ssl, SSL_OP_NO_SSLv2);
|
||||
SSL_set_connect_state(d->ssl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SET_ENCRYPTED_INPUT:
|
||||
die_unless(d->ssl, "SSL not initialized");
|
||||
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_output, called when output descriptor ready */
|
||||
"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 */
|
||||
tls_drv_control, /* F_PTR control, port_command callback */
|
||||
NULL, /* F_PTR timeout, reserved */
|
||||
|
@ -293,6 +503,7 @@ DRIVER_INIT(tls_drv) /* must match name in driver_entry */
|
|||
{
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
SSL_load_error_strings();
|
||||
init_hash_table();
|
||||
return &tls_driver_entry;
|
||||
}
|
||||
|
||||
|
|
|
@ -89,8 +89,16 @@ load_file(Lang, File) ->
|
|||
ets:insert(translations,
|
||||
{{Lang, Orig}, Trans1})
|
||||
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} ->
|
||||
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.
|
||||
|
||||
translate(Lang, Msg) ->
|
||||
|
|
|
@ -202,7 +202,7 @@ process_header(State, Data) ->
|
|||
request_version = Version,
|
||||
request_path = Path,
|
||||
request_keepalive = KeepAlive};
|
||||
{ok, {http_header, _, 'Connection', _, Conn}} ->
|
||||
{ok, {http_header, _, 'Connection'=Name, _, Conn}} ->
|
||||
KeepAlive1 = case exmpp_stringprep:to_lower(Conn) of
|
||||
"keep-alive" ->
|
||||
true;
|
||||
|
@ -211,23 +211,27 @@ process_header(State, Data) ->
|
|||
_ ->
|
||||
State#state.request_keepalive
|
||||
end,
|
||||
State#state{request_keepalive = KeepAlive1};
|
||||
{ok, {http_header, _, 'Authorization', _, Auth}} ->
|
||||
State#state{request_auth = parse_auth(Auth)};
|
||||
{ok, {http_header, _, 'Content-Length', _, SLen}} ->
|
||||
State#state{request_keepalive = KeepAlive1,
|
||||
request_headers=add_header(Name, Conn, State)};
|
||||
{ok, {http_header, _, 'Authorization'=Name, _, Auth}} ->
|
||||
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
|
||||
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
|
||||
end;
|
||||
{ok, {http_header, _, 'Accept-Language', _, Langs}} ->
|
||||
State#state{request_lang = parse_lang(Langs)};
|
||||
{ok, {http_header, _, 'Host', _, Host}} ->
|
||||
State#state{request_host = Host};
|
||||
{ok, {http_header, _, 'Accept-Language'=Name, _, Langs}} ->
|
||||
State#state{request_lang = parse_lang(Langs),
|
||||
request_headers=add_header(Name, Langs, State)};
|
||||
{ok, {http_header, _, 'Host'=Name, _, Host}} ->
|
||||
State#state{request_host = Host,
|
||||
request_headers=add_header(Name, Host, State)};
|
||||
{ok, {http_header, _, Name, _, Value}} ->
|
||||
Headers = [{Name, Value} | State#state.request_headers],
|
||||
State#state{request_headers=Headers};
|
||||
State#state{request_headers=add_header(Name, Value, State)};
|
||||
{ok, http_eoh} ->
|
||||
?DEBUG("(~w) http query: ~w ~s~n",
|
||||
[State#state.socket,
|
||||
|
@ -263,6 +267,9 @@ process_header(State, Data) ->
|
|||
request_handlers = State#state.request_handlers}
|
||||
end.
|
||||
|
||||
add_header(Name, Value, State) ->
|
||||
[{Name, Value} | State#state.request_headers].
|
||||
|
||||
%% @spec (SockMod, HostPort) -> {Host::string(), Port::integer(), TP}
|
||||
%% where
|
||||
%% SockMod = gen_tcp | tls
|
||||
|
@ -321,13 +328,13 @@ process_request(#state{request_method = Method,
|
|||
{'EXIT', _} ->
|
||||
process_request(false);
|
||||
{NPath, Query} ->
|
||||
LPath = [path_decode(NPE) || NPE <- string:tokens(NPath, "/")],
|
||||
LQuery = case (catch parse_urlencoded(Query)) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
LQ ->
|
||||
LQ
|
||||
end,
|
||||
LPath = string:tokens(NPath, "/"),
|
||||
{ok, IP} =
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
|
@ -386,7 +393,7 @@ process_request(#state{request_method = Method,
|
|||
{'EXIT', _} ->
|
||||
process_request(false);
|
||||
{NPath, _Query} ->
|
||||
LPath = string:tokens(NPath, "/"),
|
||||
LPath = [path_decode(NPE) || NPE <- string:tokens(NPath, "/")],
|
||||
LQuery = case (catch parse_urlencoded(Data)) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
|
@ -557,24 +564,30 @@ parse_lang(Langs) ->
|
|||
% 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([$?|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]),
|
||||
if Hex == 0 -> exit(badurl);
|
||||
true -> ok
|
||||
end,
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
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), []}.
|
||||
path_decode(Tail, [Hex|Ack]);
|
||||
path_decode([H|T], Ack) when H /= 0 ->
|
||||
path_decode(T, [H|Ack]);
|
||||
path_decode([], Ack) ->
|
||||
lists:reverse(Ack).
|
||||
|
||||
path_norm_reverse("/" ++ T) -> start_dir(0, "/", T);
|
||||
path_norm_reverse( T) -> start_dir(0, "", T).
|
||||
|
|
Loading…
Reference in New Issue
Block a user