24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-18 22:15:20 +02:00

Merge from trunk (r1649 to r1692).

PR:		EJABP-1

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

132
ChangeLog
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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