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:
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>
|
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
|
||||||
|
|
|
@ -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"}]},
|
||||||
...
|
...
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -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"}]},
|
||||||
...
|
...
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()]) ->
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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").
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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) $<
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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).
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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}]},
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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)].
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) ->
|
||||||
|
|
|
@ -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).
|
||||||
|
|
Loading…
Reference in New Issue
Block a user