aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_c2s.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/ejabberd_c2s.erl')
-rw-r--r--src/ejabberd_c2s.erl178
1 files changed, 97 insertions, 81 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 6b933ef6d..32e186f3c 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -87,6 +87,7 @@
%% This is the timeout to apply between event when starting a new
%% session:
-define(C2S_OPEN_TIMEOUT, 5000).
+-define(C2S_HIBERNATE_TIMEOUT, 90000).
-define(STREAM_HEADER,
"<?xml version='1.0'?>"
@@ -266,11 +267,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
c2s_stream_features,
Server,
[], [])}),
- {next_state, wait_for_feature_request,
- StateData#state{server = Server,
- sasl_state = SASLState,
- lang = Lang},
- ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_feature_request,
+ StateData#state{
+ server = Server,
+ sasl_state = SASLState,
+ lang = Lang});
_ ->
case StateData#state.resource of
"" ->
@@ -281,17 +282,18 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
[{"xmlns", ?NS_BIND}], []},
{xmlelement, "session",
[{"xmlns", ?NS_SESSION}], []}]}),
- {next_state, wait_for_bind,
- StateData#state{server = Server,
- lang = Lang},
- ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_bind,
+ StateData#state{
+ server = Server,
+ lang = Lang});
_ ->
send_element(
StateData,
{xmlelement, "stream:features", [], []}),
- {next_state, wait_for_session,
- StateData#state{server = Server,
- lang = Lang}}
+ fsm_next_state(wait_for_session,
+ StateData#state{
+ server = Server,
+ lang = Lang})
end
end;
_ ->
@@ -311,9 +313,10 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
{stop, normal, StateData};
true ->
send_text(StateData, Header),
- {next_state, wait_for_auth,
- StateData#state{server = Server,
- lang = Lang}}
+ fsm_next_state(wait_for_auth,
+ StateData#state{
+ server = Server,
+ lang = Lang})
end
end;
_ ->
@@ -385,13 +388,13 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
]}]}
end,
send_element(StateData, Res),
- {next_state, wait_for_auth, StateData, ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_auth, StateData);
{auth, _ID, set, {_U, _P, _D, ""}} ->
Err = jlib:make_error_reply(
El,
?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
send_element(StateData, Err),
- {next_state, wait_for_auth, StateData, ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_auth, StateData);
{auth, _ID, set, {U, P, D, R}} ->
JID = jlib:make_jid(U, StateData#state.server, R),
case (JID /= error) andalso
@@ -428,14 +431,15 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
privacy_get_user_list, StateData#state.server,
none,
[U, StateData#state.server]),
- {next_state, session_established,
- StateData#state{user = U,
+ fsm_next_state(session_established,
+ StateData#state{
+ user = U,
resource = R,
jid = JID,
sid = SID,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
- privacy_list = PrivList}};
+ privacy_list = PrivList});
_ ->
?INFO_MSG(
"(~w) Failed legacy authentication for ~s",
@@ -444,8 +448,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
Err = jlib:make_error_reply(
El, ?ERR_NOT_AUTHORIZED),
send_element(StateData, Err),
- {next_state, wait_for_auth, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_auth, StateData)
end;
_ ->
if
@@ -456,8 +459,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
[StateData#state.socket, U, R]),
Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
send_element(StateData, Err),
- {next_state, wait_for_auth, StateData,
- ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_auth, StateData);
true ->
?INFO_MSG(
"(~w) Forbidden legacy authentication for ~s",
@@ -465,13 +467,12 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
jlib:jid_to_string(JID)]),
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err),
- {next_state, wait_for_auth, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_auth, StateData)
end
end;
_ ->
process_unauthenticated_stanza(StateData, El),
- {next_state, wait_for_auth, StateData, ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_auth, StateData)
end;
wait_for_auth(timeout, StateData) ->
@@ -512,20 +513,20 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
U = xml:get_attr_s(username, Props),
?INFO_MSG("(~w) Accepted authentication for ~s",
[StateData#state.socket, U]),
- {next_state, wait_for_stream,
- StateData#state{streamid = new_id(),
+ fsm_next_state(wait_for_stream,
+ StateData#state{
+ streamid = new_id(),
authenticated = true,
- user = U
- }, ?C2S_OPEN_TIMEOUT};
+ user = U });
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
{xmlelement, "challenge",
[{"xmlns", ?NS_SASL}],
[{xmlcdata,
jlib:encode_base64(ServerOut)}]}),
- {next_state, wait_for_sasl_response,
- StateData#state{sasl_state = NewSASLState},
- ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_sasl_response,
+ StateData#state{
+ sasl_state = NewSASLState});
{error, Error, Username} ->
?INFO_MSG(
"(~w) Failed authentication for ~s@~s",
@@ -542,8 +543,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, "failure",
[{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_feature_request, StateData)
end;
{?NS_TLS, "starttls"} when TLS == true,
TLSEnabled == false,
@@ -562,11 +562,11 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Socket, TLSOpts,
xml:element_to_string(
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
- {next_state, wait_for_stream,
- StateData#state{socket = TLSSocket,
- streamid = new_id(),
- tls_enabled = true
- }, ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_stream,
+ StateData#state{socket = TLSSocket,
+ streamid = new_id(),
+ tls_enabled = true
+ });
{?NS_COMPRESS, "compress"} when Zlib == true,
SockMod == gen_tcp ->
case xml:get_subtag(El, "method") of
@@ -575,8 +575,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, "failure",
[{"xmlns", ?NS_COMPRESS}],
[{xmlelement, "setup-failed", [], []}]}),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_feature_request, StateData);
Method ->
case xml:get_tag_cdata(Method) of
"zlib" ->
@@ -586,18 +585,18 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
xml:element_to_string(
{xmlelement, "compressed",
[{"xmlns", ?NS_COMPRESS}], []})),
- {next_state, wait_for_stream,
+ fsm_next_state(wait_for_stream,
StateData#state{socket = ZlibSocket,
streamid = new_id()
- }, ?C2S_OPEN_TIMEOUT};
+ });
_ ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_COMPRESS}],
[{xmlelement, "unsupported-method",
[], []}]}),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_feature_request,
+ StateData)
end
end;
_ ->
@@ -611,8 +610,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{stop, normal, StateData};
true ->
process_unauthenticated_stanza(StateData, El),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_feature_request, StateData)
end
end;
@@ -647,19 +645,19 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
U = xml:get_attr_s(username, Props),
?INFO_MSG("(~w) Accepted authentication for ~s",
[StateData#state.socket, U]),
- {next_state, wait_for_stream,
- StateData#state{streamid = new_id(),
+ fsm_next_state(wait_for_stream,
+ StateData#state{
+ streamid = new_id(),
authenticated = true,
- user = U
- }, ?C2S_OPEN_TIMEOUT};
+ user = U});
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
{xmlelement, "challenge",
[{"xmlns", ?NS_SASL}],
[{xmlcdata,
jlib:encode_base64(ServerOut)}]}),
- {next_state, wait_for_sasl_response,
- StateData#state{sasl_state = NewSASLState}};
+ fsm_next_state(wait_for_sasl_response,
+ StateData#state{sasl_state = NewSASLState});
{error, Error, Username} ->
?INFO_MSG(
"(~w) Failed authentication for ~s@~s",
@@ -669,20 +667,17 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
{xmlelement, "failure",
[{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_feature_request, StateData);
{error, Error} ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_feature_request, StateData)
end;
_ ->
process_unauthenticated_stanza(StateData, El),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_feature_request, StateData)
end;
wait_for_sasl_response(timeout, StateData) ->
@@ -717,7 +712,7 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
error ->
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
send_element(StateData, Err),
- {next_state, wait_for_bind, StateData, ?C2S_OPEN_TIMEOUT};
+ fsm_next_state(wait_for_bind, StateData);
_ ->
JID = jlib:make_jid(U, StateData#state.server, R),
Res = IQ#iq{type = result,
@@ -727,12 +722,11 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
[{xmlcdata,
jlib:jid_to_string(JID)}]}]}]},
send_element(StateData, jlib:iq_to_xml(Res)),
- {next_state, wait_for_session,
- StateData#state{resource = R, jid = JID},
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_session,
+ StateData#state{resource = R, jid = JID})
end;
_ ->
- {next_state, wait_for_bind, StateData, ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_bind, StateData)
end;
wait_for_bind(timeout, StateData) ->
@@ -783,22 +777,22 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
privacy_get_user_list, StateData#state.server,
none,
[U, StateData#state.server]),
- {next_state, session_established,
- StateData#state{sid = SID,
+ fsm_next_state(session_established,
+ StateData#state{
+ sid = SID,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
- privacy_list = PrivList}};
+ privacy_list = PrivList});
_ ->
?INFO_MSG("(~w) Forbidden session for ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err),
- {next_state, wait_for_session, StateData,
- ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_session, StateData)
end;
_ ->
- {next_state, wait_for_session, StateData, ?C2S_OPEN_TIMEOUT}
+ fsm_next_state(wait_for_session, StateData)
end;
wait_for_session(timeout, StateData) ->
@@ -903,7 +897,16 @@ session_established({xmlstreamelement, El}, StateData) ->
StateData
end
end,
- {next_state, session_established, NewState};
+ fsm_next_state(session_established, NewState);
+
+%% We hibernate the process to reduce memory consumption after a
+%% configurable activity timeout
+session_established(timeout, StateData) ->
+ %% TODO: Options must be stored in state:
+ Options = [],
+ proc_lib:hibernate(gen_fsm, enter_loop,
+ [?MODULE, Options, session_established, StateData]),
+ fsm_next_state(session_established, StateData);
session_established({xmlstreamend, _Name}, StateData) ->
send_text(StateData, ?STREAM_TRAILER),
@@ -938,7 +941,7 @@ session_established(closed, StateData) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(_Event, StateName, StateData) ->
- {next_state, StateName, StateData}.
+ fsm_next_state(StateName, StateData).
%%----------------------------------------------------------------------
%% Func: handle_sync_event/4
@@ -958,12 +961,11 @@ handle_sync_event({get_presence}, _From, StateName, StateData) ->
Resource = StateData#state.resource,
Reply = {User, Resource, Show, Status},
- {reply, Reply, StateName, StateData};
+ fsm_reply(Reply, StateName, StateData);
handle_sync_event(_Event, _From, StateName, StateData) ->
Reply = ok,
- {reply, Reply, StateName, StateData}.
-
+ fsm_reply(Reply, StateName, StateData).
code_change(_OldVsn, StateName, StateData, _Extra) ->
{ok, StateName, StateData}.
@@ -976,7 +978,7 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
%%----------------------------------------------------------------------
handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text),
- {next_state, StateName, StateData};
+ fsm_next_state(StateName, StateData);
handle_info(replaced, _StateName, StateData) ->
Lang = StateData#state.lang,
send_text(StateData,
@@ -1187,16 +1189,16 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
ejabberd_hooks:run(user_receive_packet,
StateData#state.server,
[StateData#state.jid, From, To, FixedPacket]),
- {next_state, StateName, NewState};
+ fsm_next_state(StateName, NewState);
true ->
- {next_state, StateName, NewState}
+ fsm_next_state(StateName, NewState)
end;
handle_info({'DOWN', Monitor, _Type, _Object, _Info}, _StateName, StateData)
when Monitor == StateData#state.socket_monitor ->
{stop, normal, StateData};
handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
- {next_state, StateName, StateData}.
+ fsm_next_state(StateName, StateData).
%%----------------------------------------------------------------------
%% Func: terminate/3
@@ -1858,3 +1860,17 @@ peerip(SockMod, Socket) ->
{ok, IPOK} -> IPOK;
_ -> undefined
end.
+
+%% fsm_next_state: Generate the next_state FSM tuple with different
+%% timeout, depending on the future state
+fsm_next_state(session_established, StateData) ->
+ {next_state, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
+fsm_next_state(StateName, StateData) ->
+ {next_state, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
+
+%% fsm_reply: Generate the reply FSM tuple with different timeout,
+%% depending on the future state
+fsm_reply(Reply, session_established, StateData) ->
+ {reply, Reply, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
+fsm_reply(Reply, StateName, StateData) ->
+ {reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}.