./ct_report/coverage/ejabberd_c2s.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : ejabberd_c2s.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : Serve C2S connection
5 %%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(ejabberd_c2s).
27 -author('alexey@process-one.net').
28 -update_info({update, 0}).
29 %% External exports
30 -export([start/2,
31 stop/1,
32 terminate_session/2,
33 start_link/2,
34 send_text/2,
35 socket_type/0,
36 get_presence/1,
37 get_aux_field/2,
38 set_aux_field/3,
39 del_aux_field/2,
40 get_subscription/2,
41 get_subscribed/1,
42 send_filtered/5,
43 store_session_info/4,
44 remove_session_info/3,
45 get_info/1,
46 run_remote_hook/3,
47 run_remote_hook_after/4]).
48
49 %% gen_fsm callbacks
50 -export([init/1,
51 wait_for_stream/2,
52 wait_for_feature_before_auth/2,
53 wait_for_feature_after_auth/2,
54 wait_for_session_or_sm/2,
55 wait_for_sasl_response/2,
56 session_established/2, session_established/3,
57 resume_session/2, resume_session/3,
58 handle_event/3,
59 handle_sync_event/4,
60 code_change/4,
61 handle_info/3,
62 terminate/4,
63 print_state/1]).
64
65 -ignore_xref([del_aux_field/2, get_info/1, print_state/1, resume_session/2,
66 resume_session/3, send_text/2, session_established/2, session_established/3,
67 socket_type/0, start_link/2, wait_for_feature_after_auth/2,
68 wait_for_feature_before_auth/2, wait_for_sasl_response/2,
69 wait_for_session_or_sm/2, wait_for_stream/2]).
70
71 -include("mongoose.hrl").
72 -include("ejabberd_c2s.hrl").
73 -include("jlib.hrl").
74 -include_lib("exml/include/exml.hrl").
75 -xep([{xep, 18}, {version, "0.2"}]).
76 -behaviour(p1_fsm_old).
77
78 -export_type([broadcast/0, packet/0, state/0]).
79
80 -type packet() :: {jid:jid(), jid:jid(), exml:element()}.
81 -type sock_data() :: term().
82 -type start_result() :: {error, _}
83 | {ok, undefined | pid()}
84 | {ok, undefined | pid(), _}.
85
86 %%%----------------------------------------------------------------------
87 %%% API
88 %%%----------------------------------------------------------------------
89
90 -spec start(sock_data(), ejabberd_listener:opts()) -> start_result().
91 start(SockData, Opts) ->
92 4360 ?SUPERVISOR_START(SockData, Opts).
93
94 -spec start_link(sock_data(), ejabberd_listener:opts()) -> start_result().
95 start_link(SockData, Opts) ->
96 4360 p1_fsm_old:start_link(ejabberd_c2s, {SockData, Opts},
97 ?FSMOPTS ++ fsm_limit_opts(Opts)).
98
99 socket_type() ->
100 4366 xml_stream.
101
102 %% @doc Return Username, Resource and presence information
103 get_presence(FsmRef) ->
104 22 p1_fsm_old:sync_send_all_state_event(FsmRef, get_presence, 1000).
105
106 get_info(FsmRef) ->
107 11 p1_fsm_old:sync_send_all_state_event(FsmRef, get_info, 5000).
108
109
110 -spec get_aux_field(Key :: aux_key(),
111 State :: state()) -> 'error' | {'ok', aux_value()}.
112 get_aux_field(Key, #state{aux_fields = Opts}) ->
113 93 case lists:keyfind(Key, 1, Opts) of
114 {_, Val} ->
115 39 {ok, Val};
116 _ ->
117 54 error
118 end.
119
120
121 -spec set_aux_field(Key :: aux_key(),
122 Val :: aux_value(),
123 State :: state()) -> state().
124 set_aux_field(Key, Val, #state{aux_fields = Opts} = State) ->
125 79 Opts1 = lists:keydelete(Key, 1, Opts),
126 79 State#state{aux_fields = [{Key, Val}|Opts1]}.
127
128
129 -spec del_aux_field(Key :: aux_key(), State :: state()) -> aux_value().
130 del_aux_field(Key, #state{aux_fields = Opts} = State) ->
131
:-(
Opts1 = lists:keydelete(Key, 1, Opts),
132
:-(
State#state{aux_fields = Opts1}.
133
134
135 -spec get_subscription(From :: jid:jid(), State :: state()) ->
136 both | from | 'none' | 'to'.
137 get_subscription(From = #jid{}, StateData) ->
138 165 {LFrom, LBFrom} = lowcase_and_bare(From),
139 165 F = is_subscribed_to_my_presence(LFrom, LBFrom, StateData),
140 165 T = am_i_subscribed_to_presence(LFrom, LBFrom, StateData),
141 165 case {F, T} of
142 131 {true, true} -> both;
143 2 {true, _} -> from;
144 16 {_, true} -> to;
145 16 _ -> none
146 end.
147
148 send_filtered(FsmRef, Feature, From, To, Packet) ->
149 4 FsmRef ! {send_filtered, Feature, From, To, Packet}.
150
151 %% @doc Stops the session gracefully, entering resume state if applicable
152 stop(FsmRef) ->
153 134 p1_fsm_old:send_event(FsmRef, closed).
154
155 %% @doc terminates the session immediately and unconditionally, sending the user a stream conflict
156 %% error specifying the reason
157 terminate_session(none, _Reason) ->
158 3 no_session;
159 terminate_session(#jid{} = Jid, Reason) ->
160 10 terminate_session(ejabberd_sm:get_session_pid(Jid), Reason);
161 terminate_session(Pid, Reason) when is_pid(Pid) ->
162 32 Pid ! {exit, Reason}.
163
164 store_session_info(FsmRef, JID, Key, Value) ->
165 86 FsmRef ! {store_session_info, JID, Key, Value, self()}.
166
167 remove_session_info(FsmRef, JID, Key) ->
168 19 FsmRef ! {remove_session_info, JID, Key, self()}.
169
170 run_remote_hook(Pid, HandlerName, Args) ->
171 98 Pid ! {run_remote_hook, HandlerName, Args}.
172
173 run_remote_hook_after(Delay, Pid, HandlerName, Args) ->
174 76 erlang:send_after(Delay, Pid, {run_remote_hook, HandlerName, Args}).
175
176 %%%----------------------------------------------------------------------
177 %%% Callback functions from gen_fsm
178 %%%----------------------------------------------------------------------
179
180 -spec init({sock_data(), ejabberd_listener:opts()}) ->
181 {stop, normal} | {ok, wait_for_stream, state(), non_neg_integer()}.
182 init({{SockMod, Socket}, Opts}) ->
183 4360 Access = case lists:keyfind(access, 1, Opts) of
184 4193 {_, A} -> A;
185 167 _ -> all
186 end,
187 4360 Shaper = case lists:keyfind(shaper, 1, Opts) of
188 4193 {_, S} -> S;
189 167 _ -> none
190 end,
191 4360 XMLSocket =
192 case lists:keyfind(xml_socket, 1, Opts) of
193 167 {_, XS} -> XS;
194 4193 _ -> false
195 end,
196 4360 Zlib = case lists:keyfind(zlib, 1, Opts) of
197 4035 {_, ZlibLimit} -> {true, ZlibLimit};
198 325 _ -> {false, 0}
199 end,
200 4360 Verify = case lists:member(verify_peer, Opts) of
201 60 true -> verify_peer;
202 4300 false -> verify_none
203 end,
204 4360 HibernateAfter =
205 case lists:keyfind(hibernate_after, 1, Opts) of
206
:-(
{_, HA} -> HA;
207 4360 _ -> 0
208 end,
209 4360 StartTLS = lists:member(starttls, Opts) orelse Verify =:= verify_peer,
210 4360 StartTLSRequired = lists:member(starttls_required, Opts),
211 4360 TLSEnabled = lists:member(tls, Opts),
212 4360 TLS = StartTLS orelse StartTLSRequired orelse TLSEnabled,
213 4360 TLSOpts1 =
214 3993 lists:filter(fun({certfile, _}) -> true;
215 20 ({cafile, _}) -> true;
216 317 ({ciphers, _}) -> true;
217
:-(
({protocol_options, _}) -> true;
218 3834 ({dhfile, _}) -> true;
219 902 ({tls_module, _}) -> true;
220 199 ({ssl_options, _}) -> true;
221 50516 (_) -> false
222 end, Opts),
223 4360 TLSOpts = verify_opts(Verify) ++ TLSOpts1,
224 4360 [ssl_crl_cache:insert({file, CRL}) || CRL <- proplists:get_value(crlfiles, Opts, [])],
225 4360 IP = peerip(SockMod, Socket),
226 %% Check if IP is blacklisted:
227 4360 case is_ip_blacklisted(IP) of
228 true ->
229
:-(
?LOG_INFO(#{what => c2s_blacklisted_ip, ip => IP,
230
:-(
text => <<"Connection attempt from blacklisted IP">>}),
231
:-(
{stop, normal};
232 false ->
233 4360 Socket1 =
234 case TLSEnabled of
235 964 true -> mongoose_transport:starttls(SockMod, Socket, TLSOpts);
236 3396 false -> Socket
237 end,
238 3910 SocketMonitor = mongoose_transport:monitor(SockMod, Socket1),
239 3910 CredOpts = mongoose_credentials:make_opts(Opts),
240 3910 {ok, wait_for_stream, #state{server = ?MYNAME,
241 socket = Socket1,
242 sockmod = SockMod,
243 socket_monitor = SocketMonitor,
244 xml_socket = XMLSocket,
245 zlib = Zlib,
246 tls = TLS,
247 tls_required = StartTLSRequired,
248 tls_enabled = TLSEnabled,
249 tls_options = TLSOpts,
250 tls_verify = Verify,
251 streamid = new_id(),
252 access = Access,
253 shaper = Shaper,
254 ip = IP,
255 lang = ?MYLANG,
256 hibernate_after= HibernateAfter,
257 cred_opts = CredOpts
258 },
259 ?C2S_OPEN_TIMEOUT}
260 end.
261
262 %% @doc Return list of all available resources of contacts,
263 get_subscribed(FsmRef) ->
264 6 p1_fsm_old:sync_send_all_state_event(FsmRef, get_subscribed, 1000).
265
266 %%----------------------------------------------------------------------
267 %% Func: StateName/2
268 %%----------------------------------------------------------------------
269
270 -spec wait_for_stream(Item :: ejabberd:xml_stream_item(),
271 StateData :: state()) -> fsm_return().
272 wait_for_stream({xmlstreamstart, _Name, _} = StreamStart, StateData) ->
273 6541 handle_stream_start(StreamStart, StateData);
274 wait_for_stream(timeout, StateData) ->
275
:-(
{stop, normal, StateData};
276 wait_for_stream(closed, StateData) ->
277 516 {stop, normal, StateData};
278 wait_for_stream(_UnexpectedItem, #state{server = Server} = StateData) ->
279 4 case mongoose_config:get_opt(hide_service_name, false) of
280 true ->
281 1 {stop, normal, StateData};
282 false ->
283 3 send_header(StateData, Server, << "1.0">>, <<"">>),
284 3 c2s_stream_error(mongoose_xmpp_errors:xml_not_well_formed(), StateData)
285 end.
286
287 handle_stream_start({xmlstreamstart, _Name, Attrs}, #state{} = S0) ->
288 6541 Server = jid:nameprep(xml:get_attr_s(<<"to">>, Attrs)),
289 6541 Lang = get_xml_lang(Attrs),
290 6541 S1 = S0#state{server = Server, lang = Lang},
291 6541 case {xml:get_attr_s(<<"xmlns:stream">>, Attrs),
292 mongoose_domain_api:get_domain_host_type(Server)} of
293 {?NS_STREAM, {ok, HostType}} ->
294 6537 StreamMgmtConfig = case gen_mod:is_loaded(HostType, mod_stream_management) of
295 6519 true -> false;
296 18 _ -> disabled
297 end,
298 6537 S = S1#state{host_type = HostType, stream_mgmt = StreamMgmtConfig},
299 6537 change_shaper(S, jid:make_noprep(<<>>, Server, <<>>)),
300 6537 Version = xml:get_attr_s(<<"version">>, Attrs),
301 6537 stream_start_by_protocol_version(Version, S);
302 {?NS_STREAM, {error, not_found}} ->
303 2 stream_start_error(mongoose_xmpp_errors:host_unknown(), S1);
304 {_InvalidNS, _} ->
305 2 stream_start_error(mongoose_xmpp_errors:invalid_namespace(), S1)
306 end.
307
308 stream_start_error(Error, StateData) ->
309 6 send_header(StateData, ?MYNAME, <<"">>, ?MYLANG),
310 6 c2s_stream_error(Error, StateData).
311
312 -spec c2s_stream_error(Error, State) -> Result when
313 Error :: exml:element(),
314 State :: state(),
315 Result :: {stop, normal, state()}.
316 c2s_stream_error(Error, StateData) ->
317 24 ?LOG_DEBUG(#{what => c2s_stream_error, xml_error => Error, c2s_state => StateData}),
318 24 send_element_from_server_jid(StateData, Error),
319 23 send_trailer(StateData),
320 23 {stop, normal, StateData}.
321
322 %% See RFC 6120 4.3.2:
323 %%
324 %% If the initiating entity includes in the initial stream header
325 %% the 'version' attribute set to a value of at least <<"1.0">> [...]
326 %% receiving entity MUST send a <features/> child element [...]
327 %%
328 %% (http://xmpp.org/rfcs/rfc6120.html#streams-negotiation-features)
329 stream_start_by_protocol_version(<<"1.0">>, #state{} = S) ->
330 6535 stream_start_negotiate_features(S);
331 stream_start_by_protocol_version(_Pre1_0, S) ->
332 2 stream_start_error(mongoose_xmpp_errors:unsupported_version(), S).
333
334 stream_start_negotiate_features(#state{} = S) ->
335 6535 send_header(S, S#state.server, <<"1.0">>, ?MYLANG),
336 6535 case {S#state.authenticated, S#state.resource} of
337 {false, _} ->
338 3473 stream_start_features_before_auth(S);
339 {_, <<>>} ->
340 3060 stream_start_features_after_auth(S);
341 {_, _} ->
342 2 send_element_from_server_jid(S, #xmlel{name = <<"stream:features">>}),
343 2 fsm_next_state(wait_for_session_or_sm, S)
344 end.
345
346 stream_start_features_before_auth(#state{server = Server,
347 host_type = HostType,
348 cred_opts = CredOpts} = S) ->
349 3473 Creds0 = mongoose_credentials:new(Server, HostType, CredOpts),
350 3473 Creds = maybe_add_cert(Creds0, S),
351 3473 SASLState = cyrsasl:server_new(<<"jabber">>, Server, HostType, <<>>, [], Creds),
352 3473 SockMod = (S#state.sockmod):get_sockmod(S#state.socket),
353 3473 send_element_from_server_jid(S, stream_features(determine_features(SockMod, S))),
354 3473 fsm_next_state(wait_for_feature_before_auth,
355 S#state{sasl_state = SASLState}).
356
357 stream_start_features_after_auth(#state{} = S) ->
358 3060 SockMod = (S#state.sockmod):get_sockmod(S#state.socket),
359 3060 Features = (maybe_compress_feature(SockMod, S)
360 ++ [#xmlel{name = <<"bind">>,
361 attrs = [{<<"xmlns">>, ?NS_BIND}]},
362 #xmlel{name = <<"session">>,
363 attrs = [{<<"xmlns">>, ?NS_SESSION}]}]
364 ++ maybe_roster_versioning_feature(S)
365 ++ hook_enabled_features(S) ),
366 3060 send_element_from_server_jid(S, stream_features(Features)),
367 3060 fsm_next_state(wait_for_feature_after_auth, S).
368
369 maybe_roster_versioning_feature(#state{host_type = HostType}) ->
370 3060 mongoose_hooks:roster_get_versioning_feature(HostType).
371
372 stream_features(FeatureElements) ->
373 6533 #xmlel{name = <<"stream:features">>,
374 children = FeatureElements}.
375
376 %% From RFC 6120, section 5.3.1:
377 %%
378 %% If TLS is mandatory-to-negotiate, the receiving entity SHOULD NOT
379 %% advertise support for any stream feature except STARTTLS during the
380 %% initial stage of the stream negotiation process, because further stream
381 %% features might depend on prior negotiation of TLS given the order of
382 %% layers in XMPP (e.g., the particular SASL mechanisms offered by the
383 %% receiving entity will likely depend on whether TLS has been negotiated).
384 %%
385 %% http://xmpp.org/rfcs/rfc6120.html#tls-rules-mtn
386 determine_features(SockMod, #state{tls = TLS, tls_enabled = TLSEnabled,
387 tls_required = TLSRequired} = S) ->
388 3473 OtherFeatures = maybe_compress_feature(SockMod, S)
389 ++ maybe_sasl_mechanisms(S)
390 ++ hook_enabled_features(S),
391 3473 case can_use_tls(SockMod, TLS, TLSEnabled) of
392 true ->
393 3216 case TLSRequired of
394 30 true -> [starttls_stanza(required)];
395 3186 _ -> [starttls_stanza(optional)] ++ OtherFeatures
396 end;
397 false ->
398 257 OtherFeatures
399 end.
400
401 maybe_compress_feature(SockMod, #state{zlib = {ZLib, _}}) ->
402 6533 case can_use_zlib_compression(ZLib, SockMod) of
403 5926 true -> [compression_zlib()];
404 607 _ -> []
405 end.
406
407 maybe_sasl_mechanisms(#state{host_type = HostType} = S) ->
408 3473 case cyrsasl:listmech(HostType) of
409
:-(
[] -> [];
410 Mechanisms ->
411 3473 [#xmlel{name = <<"mechanisms">>,
412 attrs = [{<<"xmlns">>, ?NS_SASL}],
413 7474 children = [ mechanism(M) || M <- Mechanisms, filter_mechanism(M, S) ]}]
414 end.
415
416 hook_enabled_features(#state{host_type = HostType, server = LServer}) ->
417 6533 mongoose_hooks:c2s_stream_features(HostType, LServer).
418
419 starttls_stanza(TLSRequired)
420 when TLSRequired =:= required;
421 TLSRequired =:= optional ->
422 3216 #xmlel{name = <<"starttls">>,
423 attrs = [{<<"xmlns">>, ?NS_TLS}],
424 30 children = [ #xmlel{name = <<"required">>} || TLSRequired =:= required ]}.
425
426 can_use_tls(SockMod, TLS, TLSEnabled) ->
427 3473 TLS == true andalso (TLSEnabled == false) andalso SockMod == gen_tcp.
428
429 can_use_zlib_compression(Zlib, SockMod) ->
430 6533 Zlib andalso ( (SockMod == gen_tcp) orelse
431 146 (SockMod == ejabberd_tls) ).
432
433 compression_zlib() ->
434 5926 #xmlel{name = <<"compression">>,
435 attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
436 children = [#xmlel{name = <<"method">>,
437 children = [#xmlcdata{content = <<"zlib">>}]}]}.
438
439 mechanism(S) ->
440 7474 #xmlel{name = <<"mechanism">>,
441 children = [#xmlcdata{content = S}]}.
442
443 filter_mechanism(<<"EXTERNAL">>, S) ->
444 192 case get_peer_cert(S) of
445 70 error -> false;
446 122 _ -> true
447 end;
448 filter_mechanism(<<"SCRAM-SHA-1-PLUS">>, S) ->
449 294 is_channel_binding_supported(S);
450 filter_mechanism(<<"SCRAM-SHA-", _N:3/binary, "-PLUS">>, S) ->
451 7242 is_channel_binding_supported(S);
452 13924 filter_mechanism(_, _) -> true.
453
454 is_channel_binding_supported(State) ->
455 7536 Socket = State#state.socket,
456 7536 SockMod = (State#state.sockmod):get_sockmod(Socket),
457 7536 is_fast_tls_configured(SockMod, Socket).
458
459 is_fast_tls_configured(ejabberd_tls, Socket) ->
460 94 fast_tls == ejabberd_tls:get_sockmod(ejabberd_socket:get_socket(Socket));
461 is_fast_tls_configured(_, _) ->
462 7442 false.
463
464 get_xml_lang(Attrs) ->
465 6541 case xml:get_attr_s(<<"xml:lang">>, Attrs) of
466 Lang when size(Lang) =< 35 ->
467 %% As stated in BCP47, 4.4.1:
468 %% Protocols or specifications that
469 %% specify limited buffer sizes for
470 %% language tags MUST allow for
471 %% language tags of at least 35 characters.
472 6541 Lang;
473 _ ->
474 %% Do not store long language tag to
475 %% avoid possible DoS/flood attacks
476
:-(
<<>>
477 end.
478
479 4300 verify_opts(verify_none) -> [verify_none];
480 60 verify_opts(verify_peer) -> [].
481
482 -spec get_peer_cert(state()) -> any() | error.
483 get_peer_cert(#state{socket = Socket,
484 sockmod = SockMod }) ->
485 3665 case mongoose_transport:get_peer_certificate(SockMod, Socket) of
486 183 {ok, Cert} -> Cert;
487 3482 _ -> error
488 end.
489
490 maybe_add_cert(Creds, S) ->
491 3473 case get_peer_cert(S) of
492 3412 error -> Creds;
493 61 Cert -> mongoose_credentials:set(Creds, client_cert, Cert)
494 end.
495
496 -spec wait_for_feature_before_auth(Item :: ejabberd:xml_stream_item(),
497 State :: state()) -> fsm_return().
498 wait_for_feature_before_auth({xmlstreamelement,
499 #xmlel{name = <<"enable">>} = El}, StateData) ->
500 1 maybe_unexpected_sm_request(wait_for_feature_before_auth, El, StateData);
501 wait_for_feature_before_auth({xmlstreamelement, El}, StateData) ->
502 3588 #xmlel{name = Name, attrs = Attrs, children = Els} = El,
503 3588 {Zlib, _} = StateData#state.zlib,
504 3588 TLS = StateData#state.tls,
505 3588 TLSEnabled = StateData#state.tls_enabled,
506 3588 TLSRequired = StateData#state.tls_required,
507 3588 SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
508 3588 case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
509 {?NS_SASL, <<"auth">>} when TLSEnabled or not TLSRequired ->
510 3103 Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
511 3103 ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
512 3103 SaslState = StateData#state.sasl_state,
513 3103 HostType = StateData#state.host_type,
514 3103 AuthMech = [M || M <- cyrsasl:listmech(HostType), filter_mechanism(M, StateData)],
515 3103 SocketData = #{socket => StateData#state.socket, auth_mech => AuthMech},
516 3103 StepResult = cyrsasl:server_start(SaslState, Mech, ClientIn, SocketData),
517 3103 {NewFSMState, NewStateData} = handle_sasl_step(StateData, StepResult),
518 3101 fsm_next_state(NewFSMState, NewStateData);
519 {?NS_TLS, <<"starttls">>} when TLS == true,
520 TLSEnabled == false,
521 SockMod == gen_tcp ->
522 91 TLSOpts = case mongoose_config:lookup_opt([domain_certfile, StateData#state.host_type]) of
523 {error, not_found} ->
524 91 StateData#state.tls_options;
525 {ok, CertFile} ->
526
:-(
lists:keystore(certfile, 1, StateData#state.tls_options, {certfile, CertFile})
527 end,
528 91 TLSSocket = mongoose_transport:starttls(StateData#state.sockmod,
529 StateData#state.socket,
530 TLSOpts, exml:to_binary(tls_proceed())),
531 89 fsm_next_state(wait_for_stream,
532 StateData#state{socket = TLSSocket,
533 streamid = new_id(),
534 tls_enabled = true
535 });
536 {?NS_COMPRESS, <<"compress">>} when Zlib == true,
537 ((SockMod == gen_tcp) or
538 (SockMod == ejabberd_tls)) ->
539 6 check_compression_auth(El, wait_for_feature_before_auth, StateData);
540 _ ->
541 388 terminate_when_tls_required_but_not_enabled(TLSRequired, TLSEnabled,
542 StateData, El)
543 end;
544 wait_for_feature_before_auth(timeout, StateData) ->
545
:-(
{stop, normal, StateData};
546 wait_for_feature_before_auth({xmlstreamend, _Name}, StateData) ->
547 264 send_trailer(StateData),
548 264 {stop, normal, StateData};
549 wait_for_feature_before_auth({xmlstreamerror, _}, StateData) ->
550
:-(
c2s_stream_error(mongoose_xmpp_errors:xml_not_well_formed(), StateData);
551 wait_for_feature_before_auth(closed, StateData) ->
552 55 {stop, normal, StateData};
553 wait_for_feature_before_auth(_, StateData) ->
554 3 c2s_stream_error(mongoose_xmpp_errors:policy_violation(), StateData).
555
556 compressed() ->
557 10 #xmlel{name = <<"compressed">>,
558 attrs = [{<<"xmlns">>, ?NS_COMPRESS}]}.
559
560 compress_unsupported_method() ->
561
:-(
#xmlel{name = <<"failure">>,
562 attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
563 children = [#xmlel{name = <<"unsupported-method">>}]}.
564
565 tls_proceed() ->
566 91 #xmlel{name = <<"proceed">>,
567 attrs = [{<<"xmlns">>, ?NS_TLS}]}.
568
569 compress_setup_failed() ->
570 6 #xmlel{name = <<"failure">>,
571 attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
572 children = [#xmlel{name = <<"setup-failed">>}]}.
573
574 -spec wait_for_sasl_response(Item :: ejabberd:xml_stream_item(),
575 State :: state()) -> fsm_return().
576 wait_for_sasl_response({xmlstreamelement,
577 #xmlel{name = <<"enable">>} = El}, StateData) ->
578
:-(
maybe_unexpected_sm_request(wait_for_sasl_response, El, StateData);
579 wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
580 13 #xmlel{name = Name, attrs = Attrs, children = Els} = El,
581 13 case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
582 {?NS_SASL, <<"response">>} ->
583 13 ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
584 13 StepResult = cyrsasl:server_step(StateData#state.sasl_state, ClientIn),
585 13 {NewFSMState, NewStateData} = handle_sasl_step(StateData, StepResult),
586 13 fsm_next_state(NewFSMState, NewStateData);
587 _ ->
588
:-(
process_unauthenticated_stanza(StateData, El),
589
:-(
fsm_next_state(wait_for_feature_before_auth, StateData)
590 end;
591 wait_for_sasl_response(timeout, StateData) ->
592
:-(
{stop, normal, StateData};
593 wait_for_sasl_response({xmlstreamend, _Name}, StateData) ->
594
:-(
send_trailer(StateData),
595
:-(
{stop, normal, StateData};
596 wait_for_sasl_response({xmlstreamerror, _}, StateData) ->
597
:-(
c2s_stream_error(mongoose_xmpp_errors:xml_not_well_formed(), StateData);
598 wait_for_sasl_response(closed, StateData) ->
599
:-(
{stop, normal, StateData}.
600
601 -spec wait_for_feature_after_auth(Item :: ejabberd:xml_stream_item(),
602 State :: state()) -> fsm_return().
603 wait_for_feature_after_auth({xmlstreamelement,
604 #xmlel{name = <<"enable">>} = El}, StateData) ->
605 1 maybe_unexpected_sm_request(wait_for_feature_after_auth, El, StateData);
606 wait_for_feature_after_auth({xmlstreamelement,
607 #xmlel{name = <<"resume">>} = El}, StateData) ->
608 15 maybe_resume_session(wait_for_feature_after_auth, El, StateData);
609 wait_for_feature_after_auth({xmlstreamelement, El}, StateData) ->
610 3040 case jlib:iq_query_info(El) of
611 #iq{type = set, xmlns = ?NS_BIND, sub_el = SubEl} = IQ ->
612 3032 R1 = xml:get_path_s(SubEl, [{elem, <<"resource">>}, cdata]),
613 3032 R = case jid:resourceprep(R1) of
614
:-(
error -> error;
615 36 <<>> -> generate_random_resource();
616 2996 Resource -> Resource
617 end,
618 3032 case R of
619 error ->
620
:-(
Err = jlib:make_error_reply(El, mongoose_xmpp_errors:bad_request()),
621
:-(
send_element_from_server_jid(StateData, Err),
622
:-(
fsm_next_state(wait_for_feature_after_auth, StateData);
623 _ ->
624 3032 JID = jid:replace_resource(StateData#state.jid, R),
625 3032 JIDEl = #xmlel{name = <<"jid">>,
626 children = [#xmlcdata{content = jid:to_binary(JID)}]},
627 3032 Res = IQ#iq{type = result,
628 sub_el = [#xmlel{name = <<"bind">>,
629 attrs = [{<<"xmlns">>, ?NS_BIND}],
630 children = [JIDEl]}]},
631 3032 XmlEl = jlib:iq_to_xml(Res),
632 3032 send_element_from_server_jid(StateData, XmlEl),
633 3032 fsm_next_state(wait_for_session_or_sm,
634 StateData#state{resource = R, jid = JID})
635 end;
636 _ ->
637 8 maybe_do_compress(El, wait_for_feature_after_auth, StateData)
638 end;
639
640 wait_for_feature_after_auth(timeout, StateData) ->
641
:-(
{stop, normal, StateData};
642
643 wait_for_feature_after_auth({xmlstreamend, _Name}, StateData) ->
644 3 send_trailer(StateData),
645 3 {stop, normal, StateData};
646
647 wait_for_feature_after_auth({xmlstreamerror, _}, StateData) ->
648
:-(
c2s_stream_error(mongoose_xmpp_errors:xml_not_well_formed(), StateData);
649
650 wait_for_feature_after_auth(closed, StateData) ->
651 5 {stop, normal, StateData};
652
653 wait_for_feature_after_auth(_, StateData) ->
654 1 c2s_stream_error(mongoose_xmpp_errors:policy_violation(), StateData).
655
656 -spec wait_for_session_or_sm(Item :: ejabberd:xml_stream_item(),
657 State :: state()) -> fsm_return().
658 wait_for_session_or_sm({xmlstreamelement,
659 #xmlel{name = <<"enable">>} = El}, StateData) ->
660 5 maybe_enable_stream_mgmt(wait_for_session_or_sm, El, StateData);
661
662 wait_for_session_or_sm({xmlstreamelement,
663 #xmlel{name = <<"r">>} = El}, StateData) ->
664 1 maybe_send_sm_ack(xml:get_tag_attr_s(<<"xmlns">>, El),
665 StateData#state.stream_mgmt,
666 StateData#state.stream_mgmt_in,
667 wait_for_session_or_sm, StateData);
668
669 wait_for_session_or_sm({xmlstreamelement, El}, StateData0) ->
670 3029 StateData = maybe_increment_sm_incoming(StateData0#state.stream_mgmt,
671 StateData0),
672 3029 case jlib:iq_query_info(El) of
673 #iq{type = set, xmlns = ?NS_SESSION} ->
674 3027 Acc = element_to_origin_accum(El, StateData0),
675 3027 {Res, _Acc1, NStateData} = maybe_open_session(Acc, StateData),
676 3027 case Res of
677
:-(
stop -> {stop, normal, NStateData};
678 1 wait -> fsm_next_state(wait_for_session_or_sm, NStateData);
679 3026 established -> fsm_next_state_pack(session_established, NStateData)
680 end;
681 _ ->
682 2 maybe_do_compress(El, wait_for_session_or_sm, StateData)
683 end;
684
685 wait_for_session_or_sm(timeout, StateData) ->
686
:-(
{stop, normal, StateData};
687
688 wait_for_session_or_sm({xmlstreamend, _Name}, StateData) ->
689
:-(
send_trailer(StateData),
690
:-(
{stop, normal, StateData};
691
692 wait_for_session_or_sm({xmlstreamerror, _}, StateData) ->
693
:-(
c2s_stream_error(mongoose_xmpp_errors:xml_not_well_formed(), StateData);
694
695 wait_for_session_or_sm(closed, StateData) ->
696 5 {stop, normal, StateData};
697
698 wait_for_session_or_sm(_, StateData) ->
699 1 c2s_stream_error(mongoose_xmpp_errors:policy_violation(), StateData).
700
701 maybe_do_compress(El = #xmlel{name = Name, attrs = Attrs}, NextState, StateData) ->
702 10 SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
703 10 {Zlib, _} = StateData#state.zlib,
704 10 case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
705 {?NS_COMPRESS, <<"compress">>} when Zlib == true,
706 ((SockMod == gen_tcp) or
707 (SockMod == ejabberd_tls)) ->
708 10 check_compression_auth(El, NextState, StateData);
709 _ ->
710
:-(
process_unauthenticated_stanza(StateData, El),
711
:-(
fsm_next_state(NextState, StateData)
712
713 end.
714
715 check_compression_auth(_El, NextState, StateData = #state{authenticated = false}) ->
716 6 send_element_from_server_jid(StateData, compress_setup_failed()),
717 4 fsm_next_state(NextState, StateData);
718 check_compression_auth(El, NextState, StateData) ->
719 10 check_compression_method(El, NextState, StateData).
720
721 check_compression_method(El, NextState, StateData) ->
722 10 case exml_query:path(El, [{element, <<"method">>}, cdata]) of
723 undefined ->
724
:-(
send_element_from_server_jid(StateData, compress_setup_failed()),
725
:-(
fsm_next_state(NextState, StateData);
726 <<"zlib">> ->
727 10 {_, ZlibLimit} = StateData#state.zlib,
728 10 Socket = StateData#state.socket,
729 10 ZlibSocket
730 = (StateData#state.sockmod):compress(Socket, ZlibLimit, exml:to_binary(compressed())),
731 10 fsm_next_state(wait_for_stream,
732 StateData#state{socket = ZlibSocket, streamid = new_id()});
733 _ ->
734
:-(
send_element_from_server_jid(StateData, compress_unsupported_method()),
735
:-(
fsm_next_state(NextState, StateData)
736 end.
737
738 -spec maybe_open_session(mongoose_acc:t(), state()) ->
739 {wait | stop | established, mongoose_acc:t(), state()}.
740 maybe_open_session(Acc, #state{jid = JID} = StateData) ->
741 3027 case user_allowed(JID, StateData) of
742 true ->
743 3026 do_open_session(Acc, JID, StateData);
744 _ ->
745 1 Acc1 = mongoose_hooks:forbidden_session_hook(StateData#state.host_type,
746 Acc, JID),
747 1 ?LOG_INFO(#{what => forbidden_session,
748 text => <<"User not allowed to open session">>,
749 1 acc => Acc, c2s_state => StateData}),
750 1 {Acc2, Err} = jlib:make_error_reply(Acc1, mongoose_xmpp_errors:not_allowed()),
751 1 Acc3 = send_element(Acc2, Err, StateData),
752 1 {wait, Acc3, StateData}
753 end.
754
755 -spec do_open_session(mongoose_acc:t(), jid:jid(), state()) ->
756 {stop | established, mongoose_acc:t(), state()}.
757 do_open_session(Acc, JID, StateData) ->
758 3026 ?LOG_INFO(#{what => c2s_opened_session, text => <<"Opened session">>,
759 3026 acc => Acc, c2s_state => StateData}),
760 3026 Resp = jlib:make_result_iq_reply(mongoose_acc:element(Acc)),
761 3026 Packet = {jid:to_bare(StateData#state.jid), StateData#state.jid, Resp},
762 3026 case send_and_maybe_buffer_stanza(Acc, Packet, StateData) of
763 {ok, Acc1, NStateData} ->
764 3026 do_open_session_common(Acc1, JID, NStateData);
765 {resume, Acc1, NStateData} ->
766
:-(
case maybe_enter_resume_session(NStateData) of
767 {stop, normal, NextStateData} -> % error, resume not possible
768
:-(
c2s_stream_error(mongoose_xmpp_errors:stream_internal_server_error(), NextStateData),
769
:-(
{stop, Acc1, NStateData};
770 {_, _, NextStateData, _} ->
771
:-(
do_open_session_common(Acc1, JID, NextStateData)
772 end
773 end.
774
775 do_open_session_common(Acc, JID, #state{host_type = HostType,
776 jid = JID} = NewStateData0) ->
777 3026 change_shaper(NewStateData0, JID),
778 3026 Acc1 = mongoose_hooks:roster_get_subscription_lists(HostType, Acc, JID),
779 3026 {Fs, Ts, Pending} = mongoose_acc:get(roster, subscription_lists, {[], [], []}, Acc1),
780 3026 LJID = jid:to_lower(jid:to_bare(JID)),
781 3026 Fs1 = [LJID | Fs],
782 3026 Ts1 = [LJID | Ts],
783 3026 PrivList = mongoose_hooks:privacy_get_user_list(HostType, JID),
784 3026 SID = ejabberd_sm:make_new_sid(),
785 3026 Conn = get_conn_type(NewStateData0),
786 3026 Info = #{ip => NewStateData0#state.ip, conn => Conn,
787 auth_module => NewStateData0#state.auth_module },
788 3026 ReplacedPids = ejabberd_sm:open_session(HostType, SID, JID, Info),
789
790 3026 RefsAndPids = [{monitor(process, PID), PID} || PID <- ReplacedPids],
791 3026 case RefsAndPids of
792 [] ->
793 3017 ok;
794 _ ->
795 9 Timeout = get_replaced_wait_timeout(HostType),
796 9 erlang:send_after(Timeout, self(), replaced_wait_timeout)
797 end,
798
799 3026 NewStateData =
800 NewStateData0#state{sid = SID,
801 conn = Conn,
802 replaced_pids = RefsAndPids,
803 pres_f = gb_sets:from_list(Fs1),
804 pres_t = gb_sets:from_list(Ts1),
805 pending_invitations = Pending,
806 privacy_list = PrivList},
807 3026 {established, Acc1, NewStateData}.
808
809 get_replaced_wait_timeout(HostType) ->
810 9 mongoose_config:get_opt({replaced_wait_timeout, HostType}).
811
812 -spec session_established(Item :: ejabberd:xml_stream_item(),
813 State :: state()) -> fsm_return().
814 session_established({xmlstreamelement,
815 #xmlel{name = <<"enable">>} = El}, StateData) ->
816 56 maybe_enable_stream_mgmt(session_established, El, StateData);
817
818 session_established({xmlstreamelement,
819 #xmlel{name = <<"a">>} = El}, StateData) ->
820 38 stream_mgmt_handle_ack(session_established, El, StateData);
821
822 session_established({xmlstreamelement,
823 #xmlel{name = <<"r">>} = El}, StateData) ->
824 6 maybe_send_sm_ack(xml:get_tag_attr_s(<<"xmlns">>, El),
825 StateData#state.stream_mgmt,
826 StateData#state.stream_mgmt_in,
827 session_established, StateData);
828 session_established({xmlstreamelement,
829 #xmlel{name = <<"inactive">>} = El}, State) ->
830 10 mongoose_metrics:update(State#state.server, modCSIInactive, 1),
831
832 10 maybe_inactivate_session(xml:get_tag_attr_s(<<"xmlns">>, El), State);
833
834 session_established({xmlstreamelement,
835 #xmlel{name = <<"active">>} = El}, State) ->
836 3 mongoose_metrics:update(State#state.server, modCSIActive, 1),
837
838 3 maybe_activate_session(xml:get_tag_attr_s(<<"xmlns">>, El), State);
839
840 session_established({xmlstreamelement, El}, StateData) ->
841 7538 FromJID = StateData#state.jid,
842 % Check 'from' attribute in stanza RFC 3920 Section 9.1.2
843 7538 case check_from(El, FromJID) of
844 'invalid-from' ->
845 2 c2s_stream_error(mongoose_xmpp_errors:invalid_from(), StateData);
846 _NewEl ->
847 7536 NewState = maybe_increment_sm_incoming(StateData#state.stream_mgmt,
848 StateData),
849 % initialise accumulator, fill with data
850 7536 El1 = fix_message_from_user(El, StateData#state.lang),
851 7536 Acc0 = element_to_origin_accum(El1, StateData),
852 7536 Acc1 = mongoose_hooks:c2s_preprocessing_hook(StateData#state.host_type,
853 Acc0, NewState),
854 7536 case mongoose_acc:get(hook, result, undefined, Acc1) of
855 78 drop -> fsm_next_state(session_established, NewState);
856 7458 _ -> process_outgoing_stanza(Acc1, NewState)
857 end
858 end;
859
860 %% We hibernate the process to reduce memory consumption after a
861 %% configurable activity timeout
862 session_established(timeout, StateData) ->
863
:-(
{next_state, session_established, StateData, hibernate()};
864 session_established({xmlstreamend, _Name}, StateData) ->
865 2907 send_trailer(StateData),
866 2907 {stop, normal, StateData};
867
868 session_established({xmlstreamerror, <<"child element too big">> = E}, StateData) ->
869
:-(
PolicyViolationErr = mongoose_xmpp_errors:policy_violation(StateData#state.lang, E),
870
:-(
c2s_stream_error(PolicyViolationErr, StateData);
871 session_established({xmlstreamerror, _}, StateData) ->
872
:-(
c2s_stream_error(mongoose_xmpp_errors:xml_not_well_formed(), StateData);
873 session_established(closed, StateData) ->
874 75 ?LOG_DEBUG(#{what => c2s_closed, c2s_state => StateData,
875 75 text => <<"Session established closed - trying to enter resume_session">>}),
876 75 maybe_enter_resume_session(StateData#state.stream_mgmt_id, StateData).
877
878 %% @doc Process packets sent by user (coming from user on c2s XMPP
879 %% connection)
880 %% eventually it should return {mongoose_acc:t(), fsm_return()} so that the accumulator
881 %% comes back whence it originated
882 -spec process_outgoing_stanza(mongoose_acc:t(), state()) -> fsm_return().
883 process_outgoing_stanza(Acc, StateData) ->
884 7458 ToJID = mongoose_acc:to_jid(Acc),
885 7458 Element = mongoose_acc:element(Acc),
886 7458 NState = process_outgoing_stanza(Acc, ToJID, Element#xmlel.name, StateData),
887 7458 fsm_next_state(session_established, NState).
888
889 process_outgoing_stanza(Acc, ToJID, <<"presence">>, StateData) ->
890 3415 #jid{user = User, server = Server} = FromJID = mongoose_acc:from_jid(Acc),
891 3415 HostType = mongoose_acc:host_type(Acc),
892 3415 Res = mongoose_hooks:c2s_update_presence(HostType, Acc),
893 3415 El = mongoose_acc:element(Res),
894 3415 Res1 = mongoose_hooks:user_send_packet(Res, FromJID, ToJID, El),
895 3415 {_Acc1, NState} = case ToJID of
896 #jid{user = User,
897 server = Server,
898 resource = <<>>} ->
899 3001 presence_update(Res1, FromJID, StateData);
900 _ ->
901 414 presence_track(Res1, StateData)
902 end,
903 3415 NState;
904 process_outgoing_stanza(Acc0, ToJID, <<"iq">>, StateData) ->
905 2184 {XMLNS, Acc} = mongoose_iq:xmlns(Acc0),
906 2184 FromJID = mongoose_acc:from_jid(Acc),
907 2184 El = mongoose_acc:element(Acc),
908 2184 {_Acc, NState} = case XMLNS of
909 ?NS_PRIVACY ->
910 176 process_privacy_iq(Acc, ToJID, StateData);
911 ?NS_BLOCKING ->
912 38 process_privacy_iq(Acc, ToJID, StateData);
913 _ ->
914 1970 Acc2 = mongoose_hooks:user_send_packet(Acc, FromJID,
915 ToJID, El),
916 1970 Acc3 = check_privacy_and_route(Acc2, StateData),
917 1970 {Acc3, StateData}
918 end,
919 2184 NState;
920 process_outgoing_stanza(Acc, ToJID, <<"message">>, StateData) ->
921 1859 FromJID = mongoose_acc:from_jid(Acc),
922 1859 El = mongoose_acc:element(Acc),
923 1859 Acc1 = mongoose_hooks:user_send_packet(Acc, FromJID, ToJID, El),
924 1859 _Acc2 = check_privacy_and_route(Acc1, StateData),
925 1859 StateData;
926 process_outgoing_stanza(_Acc, _ToJID, _Name, StateData) ->
927
:-(
StateData.
928
929 %%-------------------------------------------------------------------------
930 %% session may be terminated for example by mod_ping there is still valid
931 %% connection and resource want to send stanza.
932 resume_session({xmlstreamelement, _}, StateData) ->
933
:-(
Err = mongoose_xmpp_errors:policy_violation(StateData#state.lang,
934 <<"session in resume state cannot accept incoming stanzas">>),
935
:-(
maybe_send_element_from_server_jid_safe(StateData, Err),
936
:-(
maybe_send_trailer_safe(StateData),
937
:-(
{next_state, resume_session, StateData, hibernate()};
938
939 %%-------------------------------------------------------------------------
940 %% ignore mod_ping closed messages because we are already in resume session
941 %% state
942 resume_session(closed, StateData) ->
943 7 {next_state, resume_session, StateData, hibernate()};
944 resume_session(timeout, StateData) ->
945
:-(
{next_state, resume_session, StateData, hibernate()};
946 resume_session(Msg, StateData) ->
947
:-(
?UNEXPECTED_INFO(Msg),
948
:-(
{next_state, resume_session, StateData, hibernate()}.
949
950
951 %%----------------------------------------------------------------------
952 %% Func: StateName/3
953 %% Returns: {next_state, NextStateName, NextStateData} |
954 %% {next_state, NextStateName, NextStateData, Timeout} |
955 %% {reply, Reply, NextStateName, NextStateData} |
956 %% {reply, Reply, NextStateName, NextStateData, Timeout} |
957 %% {stop, Reason, NewStateData} |
958 %% {stop, Reason, Reply, NewStateData}
959 %%----------------------------------------------------------------------
960
961 session_established(resume, From, SD) ->
962 1 handover_session(SD, From).
963
964 resume_session(resume, From, SD) ->
965 10 handover_session(SD, From).
966
967 %%----------------------------------------------------------------------
968 %% Func: handle_event/3
969 %% Returns: {next_state, NextStateName, NextStateData} |
970 %% {next_state, NextStateName, NextStateData, Timeout} |
971 %% {stop, Reason, NewStateData}
972 %%----------------------------------------------------------------------
973
974 handle_event(keep_alive_packet, session_established,
975 #state{host_type = HostType, jid = JID} = StateData) ->
976 7 mongoose_hooks:user_sent_keep_alive(HostType, JID),
977 7 fsm_next_state(session_established, StateData);
978 handle_event(_Event, StateName, StateData) ->
979 460 fsm_next_state(StateName, StateData).
980
981 %%----------------------------------------------------------------------
982 %% Func: handle_sync_event/4
983 %% Returns: {next_state, NextStateName, NextStateData} |
984 %% {next_state, NextStateName, NextStateData, Timeout} |
985 %% {reply, Reply, NextStateName, NextStateData} |
986 %% {reply, Reply, NextStateName, NextStateData, Timeout} |
987 %% {stop, Reason, NewStateData} |
988 %% {stop, Reason, Reply, NewStateData}
989 %%----------------------------------------------------------------------
990 -spec handle_sync_event(Evt :: atom(),
991 From :: any(),
992 StateName :: statename(),
993 State :: state())
994 -> {'reply', Reply :: [any()], statename(), state()}
995 | {'reply', Reply :: 'ok' | {_, _, _, _}, statename(), state(), timeout()}.
996 handle_sync_event(get_presence, _From, StateName, StateData) ->
997 22 User = StateData#state.user,
998 22 PresLast = StateData#state.pres_last,
999
1000 22 Show = get_showtag(PresLast),
1001 22 Status = get_statustag(PresLast),
1002 22 Resource = StateData#state.resource,
1003
1004 22 Reply = {User, Resource, Show, Status},
1005 22 fsm_reply(Reply, StateName, StateData);
1006 handle_sync_event(get_info, _From, StateName, StateData) ->
1007 11 {reply, make_c2s_info(StateData), StateName, StateData};
1008 handle_sync_event(get_subscribed, _From, StateName, StateData) ->
1009 6 Subscribed = gb_sets:to_list(StateData#state.pres_f),
1010 6 {reply, Subscribed, StateName, StateData};
1011 handle_sync_event(_Event, _From, StateName, StateData) ->
1012
:-(
Reply = ok,
1013
:-(
fsm_reply(Reply, StateName, StateData).
1014
1015
1016 code_change(_OldVsn, StateName, StateData, _Extra) ->
1017
:-(
{ok, StateName, StateData}.
1018
1019 %%----------------------------------------------------------------------
1020 %% Func: handle_info/3
1021 %% Returns: {next_state, NextStateName, NextStateData} |
1022 %% {next_state, NextStateName, NextStateData, Timeout} |
1023 %% {stop, Reason, NewStateData}
1024 %%----------------------------------------------------------------------
1025
1026
1027 %%% system events
1028 handle_info({exit, Reason}, _StateName, StateData = #state{lang = Lang}) ->
1029 29 Acc = new_acc(StateData, #{location => ?LOCATION, element => undefined}),
1030 29 send_element(Acc, mongoose_xmpp_errors:stream_conflict(Lang, Reason), StateData),
1031 23 send_trailer(StateData),
1032 23 {stop, normal, StateData};
1033 handle_info(replaced, _StateName, StateData) ->
1034 9 Lang = StateData#state.lang,
1035 9 StreamConflict = mongoose_xmpp_errors:stream_conflict(Lang, <<"Replaced by new connection">>),
1036 9 maybe_send_element_from_server_jid_safe(StateData, StreamConflict),
1037 9 maybe_send_trailer_safe(StateData),
1038 9 {stop, normal, StateData#state{authenticated = replaced}};
1039 handle_info(new_offline_messages, session_established,
1040 #state{pres_last = Presence, pres_invis = Invisible} = StateData)
1041 when Presence =/= undefined orelse Invisible ->
1042 1 Acc = new_acc(StateData, #{location => ?LOCATION, element => undefined}),
1043 1 resend_offline_messages(Acc, StateData),
1044 1 {next_state, session_established, StateData};
1045 handle_info({'DOWN', Monitor, _Type, _Object, _Info}, _StateName, StateData)
1046 when Monitor == StateData#state.socket_monitor ->
1047 34 maybe_enter_resume_session(StateData#state.stream_mgmt_id, StateData);
1048 handle_info({'DOWN', Monitor, _Type, Object, Info}, StateName,
1049 #state{ replaced_pids = ReplacedPids } = StateData) ->
1050 210 case lists:keytake(Monitor, 1, ReplacedPids) of
1051 {value, {Monitor, Object}, NReplacedPids} ->
1052 8 NStateData = StateData#state{ replaced_pids = NReplacedPids },
1053 8 fsm_next_state(StateName, NStateData);
1054 _ ->
1055 202 ?LOG_WARNING(#{what => unexpected_c2s_down_info,
1056 text => <<"C2S process got DOWN message from unknown process">>,
1057 monitor_ref => Monitor, monitor_pid => Object, down_info => Info,
1058
:-(
state_name => StateName, c2s_state => StateData}),
1059 202 fsm_next_state(StateName, StateData)
1060 end;
1061 handle_info(replaced_wait_timeout, StateName, #state{ replaced_pids = [] } = StateData) ->
1062
:-(
fsm_next_state(StateName, StateData);
1063 handle_info(replaced_wait_timeout, StateName, #state{ replaced_pids = ReplacedPids } = StateData) ->
1064 1 lists:foreach(
1065 fun({Monitor, Pid}) ->
1066 1 ?LOG_WARNING(#{what => c2s_replaced_wait_timeout,
1067 text => <<"Some processes are not responding when handling replace messages">>,
1068 monitor_ref => Monitor, replaced_pid => Pid,
1069
:-(
state_name => StateName, c2s_state => StateData})
1070 end, ReplacedPids),
1071 1 fsm_next_state(StateName, StateData#state{ replaced_pids = [] });
1072 handle_info(system_shutdown, StateName, StateData) ->
1073 1 case StateName of
1074 wait_for_stream ->
1075
:-(
send_header(StateData, ?MYNAME, <<"1.0">>, <<"en">>),
1076
:-(
ok;
1077 _ ->
1078 1 ok
1079 end,
1080 1 c2s_stream_error(mongoose_xmpp_errors:system_shutdown(), StateData);
1081 handle_info({force_update_presence, LUser}, StateName,
1082 #state{user = LUser, host_type = HostType} = StateData) ->
1083
:-(
NewStateData =
1084 case StateData#state.pres_last of
1085 #xmlel{name = <<"presence">>} = PresEl ->
1086
:-(
Acc = element_to_origin_accum(PresEl, StateData),
1087
:-(
mongoose_hooks:c2s_update_presence(HostType, Acc),
1088
:-(
{_Acc, StateData2} = presence_update(Acc, StateData#state.jid, StateData),
1089
:-(
StateData2;
1090 _ ->
1091
:-(
StateData
1092 end,
1093
:-(
{next_state, StateName, NewStateData};
1094 handle_info(resume_timeout, resume_session, StateData) ->
1095 4 {stop, normal, StateData};
1096 handle_info(check_buffer_full, StateName, StateData) ->
1097 3 case is_buffer_full(StateData#state.stream_mgmt_buffer_size,
1098 StateData#state.stream_mgmt_buffer_max) of
1099 true ->
1100 2 Err = mongoose_xmpp_errors:stream_resource_constraint((StateData#state.lang),
1101 <<"too many unacked stanzas">>),
1102 2 c2s_stream_error(Err, StateData);
1103 false ->
1104 1 fsm_next_state(StateName,
1105 StateData#state{stream_mgmt_constraint_check_tref = undefined})
1106 end;
1107 handle_info({store_session_info, JID, Key, Value, _FromPid}, StateName, StateData) ->
1108 86 ejabberd_sm:store_info(JID, Key, Value),
1109 86 fsm_next_state(StateName, StateData);
1110 handle_info({remove_session_info, JID, Key, _FromPid}, StateName, StateData) ->
1111 19 ejabberd_sm:remove_info(JID, Key),
1112 19 fsm_next_state(StateName, StateData);
1113 handle_info(Info, StateName, StateData) ->
1114 13812 handle_incoming_message(Info, StateName, StateData).
1115
1116 %%% incoming messages from other users or services to this device
1117 handle_incoming_message({send_text, Text}, StateName, StateData) ->
1118 % it seems to be sometimes, by event sent from s2s
1119
:-(
send_text(StateData, Text),
1120
:-(
fsm_next_state(StateName, StateData);
1121 handle_incoming_message({broadcast, Broadcast}, StateName, StateData) ->
1122 451 Acc = new_acc(StateData, #{location => ?LOCATION, element => undefined}),
1123 451 ?LOG_DEBUG(#{what => c2s_broadcast,
1124 brodcast_data => Broadcast,
1125 451 state_name => StateName, c2s_state => StateData}),
1126 451 {Acc1, Res} = handle_routed_broadcast(Acc, Broadcast, StateData),
1127 451 handle_broadcast_result(Acc1, Res, StateName, StateData);
1128 handle_incoming_message({route, From, To, Acc}, StateName, StateData) ->
1129 13269 process_incoming_stanza_with_conflict_check(From, To, Acc, StateName, StateData);
1130 handle_incoming_message({send_filtered, Feature, From, To, Packet}, StateName, StateData) ->
1131 % this is used by pubsub and should be rewritten when someone rewrites pubsub module
1132 4 Acc = new_acc(StateData, #{location => ?LOCATION,
1133 from_jid => From,
1134 to_jid => To,
1135 element => Packet}),
1136 4 Drop = mongoose_hooks:c2s_filter_packet(StateData, Feature, To, Packet),
1137 4 case {Drop, StateData#state.jid} of
1138 {true, _} ->
1139 1 ?LOG_DEBUG(#{what => c2s_dropped_packet, acc => Acc,
1140 1 text => <<"c2s_filter_packet hook dropped a packet">>}),
1141 1 fsm_next_state(StateName, StateData);
1142 {_, To} ->
1143
:-(
FinalPacket = jlib:replace_from_to(From, To, Packet),
1144
:-(
case privacy_check_packet(FinalPacket, From, To, in, StateData) of
1145 allow ->
1146
:-(
{Act, _, NStateData} = send_and_maybe_buffer_stanza(Acc,
1147 {From, To, FinalPacket},
1148 StateData),
1149
:-(
finish_state(Act, StateName, NStateData);
1150 _ ->
1151
:-(
fsm_next_state(StateName, StateData)
1152 end;
1153 _ ->
1154 3 FinalPacket = jlib:replace_from_to(From, To, Packet),
1155 3 ejabberd_router:route(From, To, FinalPacket),
1156 3 fsm_next_state(StateName, StateData)
1157 end;
1158 handle_incoming_message({run_remote_hook, HandlerName, Args}, StateName, StateData) ->
1159 88 HostType = ejabberd_c2s_state:host_type(StateData),
1160 88 HandlerState = maps:get(HandlerName, StateData#state.handlers, empty_state),
1161 88 case mongoose_hooks:c2s_remote_hook(HostType, HandlerName, Args,
1162 HandlerState, StateData) of
1163 {error, E} ->
1164
:-(
?LOG_ERROR(#{what => custom_c2s_hook_handler_error,
1165 text => <<"c2s_remote_hook failed">>, reason => E,
1166 handler_name => HandlerName, handler_args => Args,
1167 handler_state => HandlerState,
1168
:-(
state_name => StateName, c2s_state => StateData}),
1169
:-(
fsm_next_state(StateName, StateData);
1170 NewHandlerState ->
1171 88 NewStates = maps:put(HandlerName, NewHandlerState, StateData#state.handlers),
1172 88 fsm_next_state(StateName, StateData#state{handlers = NewStates})
1173 end;
1174 handle_incoming_message(Info, StateName, StateData) ->
1175
:-(
?UNEXPECTED_INFO(Info),
1176
:-(
fsm_next_state(StateName, StateData).
1177
1178 process_incoming_stanza_with_conflict_check(From, To, Acc, StateName, StateData) ->
1179 13269 Check1 = check_incoming_accum_for_conflicts(Acc, StateData),
1180 13269 Check2 = check_receiver_sid_conflict(Acc, StateData),
1181 13269 case {Check1, Check2} of
1182 {conflict, _} -> %% A race condition detected
1183 %% Same jid, but different sids
1184 5 OriginSID = mongoose_acc:get(c2s, origin_sid, undefined, Acc),
1185 5 ?LOG_WARNING(#{what => conflict_check_failed,
1186 text => <<"Drop Acc that is addressed to another connection "
1187 "(origin SID check failed)">>,
1188 c2s_sid => StateData#state.sid, origin_sid => OriginSID,
1189
:-(
acc => Acc, state_name => StateName, c2s_state => StateData}),
1190 5 finish_state(ok, StateName, StateData);
1191 {_, conflict} ->
1192 2 ReceiverSID = mongoose_acc:get(c2s, receiver_sid, undefined, Acc),
1193 2 ?LOG_WARNING(#{what => conflict_check_failed,
1194 text => <<"Drop Acc that is addressed to another connection "
1195 "(receiver SID check failed)">>,
1196 c2s_sid => StateData#state.sid, receiver_sid => ReceiverSID,
1197
:-(
acc => Acc, state_name => StateName, c2s_state => StateData}),
1198 2 finish_state(ok, StateName, StateData);
1199 _ -> %% Continue processing
1200 13262 process_incoming_stanza(From, To, Acc, StateName, StateData)
1201 end.
1202
1203 check_receiver_sid_conflict(Acc, #state{sid = Sid}) ->
1204 13269 case mongoose_acc:get(c2s, receiver_sid, undefined, Acc) of
1205 undefined ->
1206 13267 ok;
1207 Sid ->
1208
:-(
ok;
1209 _ ->
1210 2 conflict
1211 end.
1212
1213 %% If jid is the same, but sid is not, then we have a conflict.
1214 %% jid example is alice@localhost/res1.
1215 %% sid example is `{now(), pid()}'.
1216 %% The conflict can happen, when actions with an accumulator were initiated by
1217 %% one process but the resulting stanzas were routed to another process with
1218 %% the same JID but different SID.
1219 %% The conflict usually happens when an user is reconnecting.
1220 %% Both origin_sid and origin_jid props should be defined.
1221 %% But we don't force developers to set both of them, so we should correctly
1222 %% process stanzas, that have only one properly set.
1223 %%
1224 %% "Incoming" means that stanza is coming from ejabberd_router.
1225 -spec check_incoming_accum_for_conflicts(mongoose_acc:t(), state()) ->
1226 unknown_origin | different_origin | same_device | conflict.
1227 check_incoming_accum_for_conflicts(Acc, #state{sid = SID, jid = JID,
1228 stream_mgmt_resumed_from = OldSID}) ->
1229 13269 OriginSID = mongoose_acc:get(c2s, origin_sid, undefined, Acc),
1230 13269 OriginJID = mongoose_acc:get(c2s, origin_jid, undefined, Acc),
1231 13269 AreDefined = OriginJID =/= undefined andalso OriginSID =/= undefined,
1232 13269 case AreDefined of
1233 false ->
1234 3167 unknown_origin;
1235 true ->
1236 10102 SameJID = jid:are_equal(OriginJID, JID),
1237 10102 SameSID = OriginSID =:= SID,
1238 % It's possible to receive a response addressed to a process
1239 % which we resumed from - still valid!
1240 10102 SameOldSession = OriginSID =:= OldSID,
1241 10102 case {SameJID, SameSID or SameOldSession} of
1242 {false, _} ->
1243 2169 different_origin;
1244 {_, true} ->
1245 7928 same_device;
1246 _ ->
1247 5 conflict
1248 end
1249 end.
1250
1251 process_incoming_stanza(From, To, Acc, StateName, StateData) ->
1252 13262 #xmlel{ name = Name } = Packet = mongoose_acc:element(Acc),
1253 13262 {Act, _NextAcc, NextState} = case handle_routed(Name, From, To, Acc, StateData) of
1254 {allow, NewAcc, NewPacket, NewState} ->
1255 2 preprocess_and_ship(NewAcc, From, To, NewPacket, NewState);
1256 {allow, NewAcc, NewState} ->
1257 10304 preprocess_and_ship(NewAcc, From, To, Packet, NewState);
1258 {Reason, NewAcc, NewState} ->
1259 2956 response_negative(Name, Reason, From, To, NewAcc),
1260 2956 {ok, NewAcc, NewState}
1261 end,
1262 13262 finish_state(Act, StateName, NextState).
1263
1264
1265 -spec preprocess_and_ship(Acc :: mongoose_acc:t(),
1266 From :: jid:jid(),
1267 To :: jid:jid(),
1268 El :: exml:element(),
1269 StateData :: state()) -> {ok | resume, mongoose_acc:t(), state()}.
1270 preprocess_and_ship(Acc, From, To, El, StateData) ->
1271 10306 #xmlel{attrs = Attrs} = El,
1272 10306 Attrs2 = jlib:replace_from_to_attrs(jid:to_binary(From),
1273 jid:to_binary(To),
1274 Attrs),
1275 10306 FixedEl = El#xmlel{attrs = Attrs2},
1276 10306 Acc2 = mongoose_hooks:user_receive_packet(StateData#state.host_type, Acc,
1277 StateData#state.jid, From, To, FixedEl),
1278 10306 ship_to_local_user(Acc2, {From, To, FixedEl}, StateData).
1279
1280 response_negative(<<"iq">>, forbidden, From, To, Acc) ->
1281 1 send_back_error(mongoose_xmpp_errors:forbidden(), From, To, Acc);
1282 response_negative(<<"iq">>, deny, From, To, Acc) ->
1283 6 IqType = mongoose_acc:stanza_type(Acc),
1284 6 response_iq_deny(IqType, From, To, Acc);
1285 response_negative(<<"message">>, deny, From, To, Acc) ->
1286 32 Acc1 = mod_amp:check_packet(Acc, delivery_failed),
1287 32 send_back_error(mongoose_xmpp_errors:service_unavailable(), From, To, Acc1);
1288 response_negative(_, _, _, _, Acc) ->
1289 2917 Acc.
1290
1291 response_iq_deny(<<"get">>, From, To, Acc) ->
1292 1 send_back_error(mongoose_xmpp_errors:service_unavailable(), From, To, Acc);
1293 response_iq_deny(<<"set">>, From, To, Acc) ->
1294 1 send_back_error(mongoose_xmpp_errors:service_unavailable(), From, To, Acc);
1295 response_iq_deny(_, _, _, Acc) ->
1296 4 Acc.
1297
1298 send_back_error(Etype, From, To, Acc) ->
1299 35 {Acc1, Err} = jlib:make_error_reply(Acc, Etype),
1300 35 ejabberd_router:route(To, From, Acc1, Err).
1301
1302 handle_routed(<<"presence">>, From, To, Acc, StateData) ->
1303 7238 handle_routed_presence(From, To, Acc, StateData);
1304 handle_routed(<<"iq">>, From, To, Acc, StateData) ->
1305 2634 handle_routed_iq(From, To, Acc, StateData);
1306 handle_routed(<<"message">>, _From, To, Acc, StateData) ->
1307 3390 {Acc1, Res} = privacy_check_packet(Acc, To, in, StateData),
1308 3390 case Res of
1309 allow ->
1310 3358 {allow, Acc1, StateData};
1311 deny ->
1312 22 {deny, Acc1, StateData};
1313 block ->
1314 10 {deny, Acc1, StateData}
1315 end;
1316 handle_routed(_, _From, _To, Acc, StateData) ->
1317
:-(
{ignore, Acc, StateData}.
1318
1319 -spec handle_routed_iq(From :: jid:jid(),
1320 To :: jid:jid(),
1321 Acc :: mongoose_acc:t(),
1322 StateData :: state()) -> routing_result().
1323 handle_routed_iq(From, To, Acc, StateData) ->
1324 2634 {Qi, Acc1} = mongoose_iq:info(Acc),
1325 2634 handle_routed_iq(From, To, Acc1, Qi, StateData).
1326
1327 -spec handle_routed_iq(From :: jid:jid(),
1328 To :: jid:jid(),
1329 Acc :: mongoose_acc:t(),
1330 IQ :: invalid | not_iq | jlib:iq(),
1331 StateData :: state()) -> routing_result().
1332 handle_routed_iq(From, To, Acc0, #iq{ xmlns = ?NS_LAST, type = Type }, StateData)
1333 when Type /= result, Type /= error ->
1334 % we could make iq handlers handle full jids as well, but wouldn't it be an overkill?
1335 1 {Acc, HasFromSub} = case is_subscribed_to_my_presence(From, StateData) of
1336 true ->
1337
:-(
{A, R} = privacy_check_packet(Acc0, To, out, StateData),
1338
:-(
{A, R == 'allow'};
1339 false ->
1340 1 {Acc0, false}
1341 end,
1342 1 case HasFromSub of
1343 true ->
1344
:-(
{Acc1, Res} = privacy_check_packet(Acc, To, in, StateData),
1345
:-(
case Res of
1346 allow ->
1347
:-(
{allow, Acc1, StateData};
1348 _ ->
1349
:-(
{deny, Acc1, StateData}
1350 end;
1351 _ ->
1352 1 {forbidden, Acc, StateData}
1353 end;
1354 handle_routed_iq(_From, To, Acc, #iq{}, StateData) ->
1355 2633 {Acc1, Res} = privacy_check_packet(Acc, To, in, StateData),
1356 2633 case Res of
1357 allow ->
1358 2627 {allow, Acc1, StateData};
1359 deny ->
1360 6 {deny, Acc1, StateData}
1361 end;
1362 handle_routed_iq(_From, _To, Acc, IQ, StateData)
1363 when (IQ == invalid) or (IQ == not_iq) ->
1364
:-(
{invalid, Acc, StateData}.
1365
1366 -spec handle_routed_broadcast(Acc :: mongoose_acc:t(),
1367 Broadcast :: broadcast_type(),
1368 StateData :: state()) ->
1369 {mongoose_acc:t(), broadcast_result()}.
1370 handle_routed_broadcast(Acc, {item, IJID, ISubscription}, StateData) ->
1371 369 {Acc2, NewState} = roster_change(Acc, IJID, ISubscription, StateData),
1372 369 {Acc2, {new_state, NewState}};
1373 handle_routed_broadcast(Acc, {privacy_list, PrivList, PrivListName}, StateData) ->
1374 53 case mongoose_hooks:privacy_updated_list(StateData#state.host_type,
1375 StateData#state.privacy_list, PrivList) of
1376 false ->
1377
:-(
{Acc, {new_state, StateData}};
1378 NewPL ->
1379 53 PrivPushIQ = privacy_list_push_iq(PrivListName),
1380 53 F = jid:to_bare(StateData#state.jid),
1381 53 T = StateData#state.jid,
1382 53 PrivPushEl = jlib:replace_from_to(F, T, jlib:iq_to_xml(PrivPushIQ)),
1383 53 Acc1 = maybe_update_presence(Acc, StateData, NewPL),
1384 53 Res = {send_new, F, T, PrivPushEl, StateData#state{privacy_list = NewPL}},
1385 53 {Acc1, Res}
1386 end;
1387 handle_routed_broadcast(Acc, {blocking, UserList, Action, JIDs}, StateData) ->
1388 29 blocking_push_to_resources(Action, JIDs, StateData),
1389 29 blocking_presence_to_contacts(Action, JIDs, StateData),
1390 29 Res = {new_state, StateData#state{privacy_list = UserList}},
1391 29 {Acc, Res};
1392 handle_routed_broadcast(Acc, _, StateData) ->
1393
:-(
{Acc, {new_state, StateData}}.
1394
1395 -spec handle_broadcast_result(mongoose_acc:t(), broadcast_result(), StateName :: atom(),
1396 StateData :: state()) ->
1397 any().
1398 handle_broadcast_result(Acc, {send_new, From, To, Stanza, NewState}, StateName, _StateData) ->
1399 53 {Act, _, NewStateData} = ship_to_local_user(Acc, {From, To, Stanza}, NewState),
1400 53 finish_state(Act, StateName, NewStateData);
1401 handle_broadcast_result(_Acc, {new_state, NewState}, StateName, _StateData) ->
1402 398 fsm_next_state(StateName, NewState).
1403
1404 privacy_list_push_iq(PrivListName) ->
1405 53 #iq{type = set, xmlns = ?NS_PRIVACY,
1406 id = <<"push", (mongoose_bin:gen_from_crypto())/binary>>,
1407 sub_el = [#xmlel{name = <<"query">>,
1408 attrs = [{<<"xmlns">>, ?NS_PRIVACY}],
1409 children = [#xmlel{name = <<"list">>,
1410 attrs = [{<<"name">>, PrivListName}]}]}]}.
1411
1412 -spec handle_routed_presence(From :: jid:jid(), To :: jid:jid(),
1413 Acc0 :: mongoose_acc:t(), StateData :: state()) -> routing_result().
1414 handle_routed_presence(From, To, Acc, StateData) ->
1415 7238 Packet = mongoose_acc:element(Acc),
1416 7238 State = mongoose_hooks:c2s_presence_in(StateData, From, To, Packet),
1417 7238 case mongoose_acc:stanza_type(Acc) of
1418 <<"probe">> ->
1419 2913 {LFrom, LBFrom} = lowcase_and_bare(From),
1420 2913 NewState = case am_i_available_to(LFrom, LBFrom, State) of
1421 2912 true -> State;
1422 1 false -> make_available_to(LFrom, LBFrom, State)
1423 end,
1424 2913 Acc1 = process_presence_probe(From, To, Acc, NewState),
1425 2913 {probe, Acc1, NewState};
1426 <<"error">> ->
1427 26 NewA = gb_sets:del_element(jid:to_lower(From), State#state.pres_a),
1428 26 {allow, Acc, State#state{pres_a = NewA}};
1429 <<"invisible">> ->
1430 2 #xmlel{ attrs = Attrs } = Packet,
1431 2 Attrs1 = lists:keydelete(<<"type">>, 1, Attrs),
1432 2 Attrs2 = [{<<"type">>, <<"unavailable">>} | Attrs1],
1433 2 NEl = Packet#xmlel{attrs = Attrs2},
1434 2 {allow, Acc, NEl, State};
1435 <<"subscribe">> ->
1436 75 {Acc1, SRes} = privacy_check_packet(Acc, To, in, State),
1437 75 {SRes, Acc1, State};
1438 <<"subscribed">> ->
1439 68 {Acc1, SRes} = privacy_check_packet(Acc, To, in, State),
1440 68 {SRes, Acc1, State};
1441 <<"unsubscribe">> ->
1442 10 {Acc1, SRes} = privacy_check_packet(Acc, To, in, State),
1443 10 {SRes, Acc1, State};
1444 <<"unsubscribed">> ->
1445 10 {Acc1, SRes} = privacy_check_packet(Acc, To, in, State),
1446 10 {SRes, Acc1, State};
1447 _ ->
1448 4134 handle_routed_available_presence(State, From, To, Acc)
1449 end.
1450
1451 -spec handle_routed_available_presence(State :: state(),
1452 From :: jid:jid(),
1453 To :: jid:jid(),
1454 Acc :: mongoose_acc:t()) -> routing_result().
1455 handle_routed_available_presence(State, From, To, Acc) ->
1456 4134 {Acc1, Res} = privacy_check_packet(Acc, To, in, State),
1457 4134 case Res of
1458 allow ->
1459 4130 {LFrom, LBFrom} = lowcase_and_bare(From),
1460 4130 case am_i_available_to(LFrom, LBFrom, State) of
1461 3583 true -> {allow, Acc1, State};
1462 547 false -> {allow, Acc1, make_available_to(LFrom, LBFrom, State)}
1463 end;
1464 _ ->
1465 4 {deny, Acc1, State}
1466 end.
1467
1468 am_i_available_to(LFrom, LBFrom, State) ->
1469 7043 gb_sets:is_element(LFrom, State#state.pres_a)
1470 6717 orelse (LFrom /= LBFrom)
1471 6717 andalso gb_sets:is_element(LBFrom, State#state.pres_a).
1472
1473 make_available_to(LFrom, LBFrom, State) ->
1474 548 case gb_sets:is_element(LFrom, State#state.pres_f) of
1475 true ->
1476
:-(
A = gb_sets:add_element(LFrom, State#state.pres_a),
1477
:-(
State#state{pres_a = A};
1478 false ->
1479 548 case gb_sets:is_element(LBFrom, State#state.pres_f) of
1480 true ->
1481 1 A = gb_sets:add_element(LBFrom, State#state.pres_a),
1482 1 State#state{pres_a = A};
1483 false ->
1484 547 State
1485 end
1486 end.
1487
1488 %%----------------------------------------------------------------------
1489 %% Func: print_state/1
1490 %% Purpose: Prepare the state to be printed on error log
1491 %% Returns: State to print
1492 %%----------------------------------------------------------------------
1493 -spec print_state(state()) -> state().
1494 print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
1495
:-(
State#state{pres_t = {pres_t, gb_sets:size(T)},
1496 pres_f = {pres_f, gb_sets:size(F)},
1497 pres_a = {pres_a, gb_sets:size(A)},
1498 pres_i = {pres_i, gb_sets:size(I)}
1499 }.
1500
1501 %%----------------------------------------------------------------------
1502 %% Func: terminate/4
1503 %% Purpose: Shutdown the fsm
1504 %% Returns: any
1505 %%----------------------------------------------------------------------
1506 -spec terminate(Reason :: any(), statename(), state(), list()) -> ok.
1507 terminate({handover_session, From}, StateName, StateData, UnreadMessages) ->
1508 % do handover first
1509 11 NewStateData = do_handover_session(StateData, UnreadMessages),
1510 11 p1_fsm_old:reply(From, {ok, NewStateData}),
1511 % and then run the normal termination
1512 11 terminate(normal, StateName, NewStateData, []);
1513 terminate(_Reason, StateName, StateData, UnreadMessages) ->
1514 3910 InitialAcc0 = new_acc(StateData, #{location => ?LOCATION, element => undefined}),
1515 3910 Acc = case StateData#state.stream_mgmt of
1516 71 true -> mongoose_acc:set(stream_mgmt, h, StateData#state.stream_mgmt_in, InitialAcc0);
1517 3839 _ -> InitialAcc0
1518 end,
1519 3910 case {should_close_session(StateName), StateData#state.authenticated} of
1520 {false, _} ->
1521 874 ok;
1522 %% if we are in an state which has a session established
1523 {_, replaced} ->
1524 9 ?LOG_INFO(#{what => replaced_session,
1525 text => <<"Replaced by new connection">>,
1526 9 state_name => StateName, c2s_state => StateData}),
1527 9 StatusEl = #xmlel{name = <<"status">>,
1528 children = [#xmlcdata{content = <<"Replaced by new connection">>}]},
1529 9 Packet = #xmlel{name = <<"presence">>,
1530 attrs = [{<<"type">>, <<"unavailable">>}],
1531 children = [StatusEl]},
1532 9 Acc0 = element_to_origin_accum(Packet, StateData),
1533 9 ejabberd_sm:close_session_unset_presence(
1534 Acc,
1535 StateData#state.sid,
1536 StateData#state.jid,
1537 <<"Replaced by new connection">>,
1538 replaced),
1539 9 Acc1 = presence_broadcast(Acc0, StateData#state.pres_a, StateData),
1540 9 presence_broadcast(Acc1, StateData#state.pres_i, StateData),
1541 9 reroute_unacked_messages(StateData, UnreadMessages);
1542 {_, resumed} ->
1543 11 StreamConflict = mongoose_xmpp_errors:stream_conflict(
1544 StateData#state.lang, <<"Resumed by new connection">>),
1545 11 maybe_send_element_from_server_jid_safe(StateData, StreamConflict),
1546 11 maybe_send_trailer_safe(StateData),
1547 11 ?LOG_INFO(#{what => stream_resumed,
1548 stream_mgmt_in => StateData#state.stream_mgmt_id,
1549 11 state_name => StateName, c2s_state => StateData});
1550 _ ->
1551 3016 ?LOG_INFO(#{what => close_session,
1552 3016 state_name => StateName, c2s_state => StateData}),
1553
1554 3016 EmptySet = gb_sets:new(),
1555 3016 case StateData of
1556 #state{pres_last = undefined,
1557 pres_a = EmptySet,
1558 pres_i = EmptySet,
1559 pres_invis = false} ->
1560 433 ejabberd_sm:close_session(Acc,
1561 StateData#state.sid,
1562 StateData#state.jid,
1563 normal);
1564 _ ->
1565 2583 Packet = #xmlel{name = <<"presence">>,
1566 attrs = [{<<"type">>, <<"unavailable">>}]},
1567 2583 Acc0 = element_to_origin_accum(Packet, StateData),
1568 2583 ejabberd_sm:close_session_unset_presence(
1569 Acc,
1570 StateData#state.sid,
1571 StateData#state.jid,
1572 <<"">>,
1573 normal),
1574 2583 Acc1 = presence_broadcast(Acc0, StateData#state.pres_a, StateData),
1575 2583 presence_broadcast(Acc1, StateData#state.pres_i, StateData)
1576 end,
1577 3016 reroute_unacked_messages(StateData, UnreadMessages)
1578 end,
1579 3910 (StateData#state.sockmod):close(StateData#state.socket),
1580 3910 ok.
1581
1582 -spec reroute_unacked_messages(StateData :: state(), list()) -> any().
1583 reroute_unacked_messages(StateData, UnreadMessages) ->
1584 3025 ?LOG_DEBUG(#{what => rerouting_unacked_messages,
1585 3025 unread_messages => UnreadMessages, c2s_state => StateData}),
1586 3025 flush_stream_mgmt_buffer(StateData),
1587 3025 bounce_csi_buffer(StateData),
1588 3025 bounce_messages(UnreadMessages, StateData).
1589
1590 %%%----------------------------------------------------------------------
1591 %%% Internal functions
1592 %%%----------------------------------------------------------------------
1593
1594 fix_message_from_user(#xmlel{attrs = Attrs} = El0, Lang) ->
1595 7536 NewEl1 = jlib:remove_delay_tags(El0),
1596 7536 case xml:get_attr_s(<<"xml:lang">>, Attrs) of
1597 <<>> ->
1598 7532 case Lang of
1599 86 <<>> -> NewEl1;
1600 Lang ->
1601 7446 xml:replace_tag_attr(<<"xml:lang">>, Lang, NewEl1)
1602 end;
1603 _ ->
1604 4 NewEl1
1605 end.
1606
1607 34 should_close_session(resume_session) -> true;
1608 3002 should_close_session(session_established) -> true;
1609 874 should_close_session(_) -> false.
1610
1611 -spec generate_random_resource() -> jid:lresource().
1612 generate_random_resource() ->
1613 36 <<(mongoose_bin:gen_from_crypto())/binary, (mongoose_bin:gen_from_timestamp())/binary>>.
1614
1615 -spec change_shaper(state(), jid:jid()) -> any().
1616 change_shaper(#state{host_type = HostType, server = Server, shaper = ShaperRule,
1617 socket = Socket, sockmod = SockMod}, JID) ->
1618 9563 Shaper = acl:match_rule(HostType, Server, ShaperRule, JID),
1619 9563 SockMod:change_shaper(Socket, Shaper).
1620
1621 -spec send_text(state(), Text :: binary()) -> any().
1622 send_text(StateData, Text) ->
1623 34627 ?LOG_DEBUG(#{what => c2s_send_text, text => <<"Send XML to the socket">>,
1624 34627 send_text => Text, c2s_state => StateData}),
1625 34627 Size = size(Text),
1626 34627 mongoose_metrics:update(global, [data, xmpp, sent, xml_stanza_size], Size),
1627 34627 (StateData#state.sockmod):send(StateData#state.socket, Text).
1628
1629 -spec maybe_send_element_from_server_jid_safe(state(), exml:element()) -> any().
1630 maybe_send_element_from_server_jid_safe(State, El) ->
1631 25 maybe_send_element_from_server_jid_safe(no_acc, State, El).
1632
1633 -spec maybe_send_element_from_server_jid_safe(mongoose_acc:t() | no_acc,
1634 state(),
1635 exml:element()) -> any().
1636 maybe_send_element_from_server_jid_safe(Acc, #state{stream_mgmt = StreamMgmt} = State, El)
1637 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
1638 13195 send_element_from_server_jid(Acc, State, El);
1639 maybe_send_element_from_server_jid_safe(Acc, State, El) ->
1640 215 case catch send_element_from_server_jid(Acc, State, El) of
1641 176 ok -> ok;
1642 39 _ -> error
1643 end.
1644
1645 -spec send_element_from_server_jid(state(), exml:element()) -> any().
1646 send_element_from_server_jid(StateData, #xmlel{} = El) ->
1647 13175 send_element_from_server_jid(no_acc, StateData, El).
1648
1649 -spec send_element_from_server_jid(mongoose_acc:t() | no_acc, state(), exml:element()) -> any().
1650 send_element_from_server_jid(no_acc, #state{server = Server} = StateData, #xmlel{} = El) ->
1651 13200 Acc = new_acc(StateData, #{location => ?LOCATION,
1652 from_jid => jid:make_noprep(<<>>, Server, <<>>),
1653 to_jid => StateData#state.jid,
1654 element => El}),
1655 13200 send_element_from_server_jid(Acc, StateData, El);
1656 send_element_from_server_jid(Acc, StateData, #xmlel{} = El) ->
1657 26585 Acc1 = send_element(Acc, El, StateData),
1658 26541 mongoose_acc:get(c2s, send_result, Acc1).
1659
1660 %% @doc This is the termination point - from here stanza is sent to the user
1661 -spec send_element(mongoose_acc:t(), exml:element(), state()) -> mongoose_acc:t().
1662 send_element(Acc, El, #state{host_type = undefined} = StateData) ->
1663 7 Res = do_send_element(El, StateData),
1664 7 mongoose_acc:set(c2s, send_result, Res, Acc);
1665 send_element(Acc, El, #state{host_type = HostType} = StateData) ->
1666 26690 Acc1 = mongoose_hooks:xmpp_send_element(HostType, Acc, El),
1667 26690 Res = do_send_element(El, StateData),
1668 26639 mongoose_acc:set(c2s, send_result, Res, Acc1).
1669
1670 do_send_element(El, #state{sockmod = SockMod} = StateData)
1671 when StateData#state.xml_socket ->
1672 1367 mongoose_transport:send_xml(SockMod, StateData#state.socket,
1673 {xmlstreamelement, El});
1674 do_send_element(El, StateData) ->
1675 25330 send_text(StateData, exml:to_binary(El)).
1676
1677 -spec send_header(State :: state(),
1678 Server :: jid:server(),
1679 Version :: binary(),
1680 Lang :: ejabberd:lang()) -> any().
1681 send_header(StateData, Server, Version, Lang)
1682 when StateData#state.xml_socket ->
1683 330 VersionAttr = case Version of
1684
:-(
<<>> -> [];
1685 330 _ -> [{<<"version">>, Version}]
1686 end,
1687 330 LangAttr = case Lang of
1688
:-(
<<>> -> [];
1689 330 _ -> [{<<"xml:lang">>, Lang}]
1690 end,
1691 330 Header = {xmlstreamstart,
1692 <<"stream:stream">>,
1693 VersionAttr ++
1694 LangAttr ++
1695 [{<<"xmlns">>, ?NS_CLIENT},
1696 {<<"xmlns:stream">>, <<"http://etherx.jabber.org/streams">>},
1697 {<<"id">>, StateData#state.streamid},
1698 {<<"from">>, Server}]},
1699 330 (StateData#state.sockmod):send_xml(StateData#state.socket, Header);
1700 send_header(StateData, Server, Version, Lang) ->
1701 6214 VersionStr = case Version of
1702 6 <<>> -> <<>>;
1703 6208 _ -> <<" version='", (Version)/binary, "'">>
1704 end,
1705 6214 LangStr = case Lang of
1706 3 <<>> -> <<>>;
1707 6211 _ when is_binary(Lang) -> <<" xml:lang='", (Lang)/binary, "'">>
1708 end,
1709 6214 Header = <<"<?xml version='1.0'?>",
1710 "<stream:stream xmlns='jabber:client' ",
1711 "xmlns:stream='http://etherx.jabber.org/streams' ",
1712 "id='", (StateData#state.streamid)/binary, "' ",
1713 "from='", (Server)/binary, "'",
1714 (VersionStr)/binary,
1715 (LangStr)/binary, ">">>,
1716 6214 send_text(StateData, Header).
1717
1718 -spec maybe_send_trailer_safe(State :: state()) -> any().
1719 maybe_send_trailer_safe(#state{stream_mgmt = StreamMgmt} = State)
1720 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
1721 4 send_trailer(State);
1722 maybe_send_trailer_safe(StateData) ->
1723 21 catch send_trailer(StateData).
1724
1725 send_trailer(StateData) when StateData#state.xml_socket ->
1726 162 (StateData#state.sockmod):send_xml(StateData#state.socket,
1727 {xmlstreamend, <<"stream:stream">>});
1728 send_trailer(StateData) ->
1729 3083 send_text(StateData, ?STREAM_TRAILER).
1730
1731
1732 -spec send_and_maybe_buffer_stanza(mongoose_acc:t(), packet(), state()) ->
1733 {ok | resume, mongoose_acc:t(), state()}.
1734 send_and_maybe_buffer_stanza(Acc, {J1, J2, El}, State)->
1735 13359 {SendResult, _, BufferedStateData} = send_and_maybe_buffer_stanza_no_ack(Acc,
1736 {J1, J2, El},
1737 State),
1738 13359 Acc1 = mod_amp:check_packet(Acc, result_to_amp_event(SendResult)),
1739 13359 case SendResult of
1740 ok ->
1741 13324 try maybe_send_ack_request(Acc1, BufferedStateData) of
1742 ResAcc ->
1743 13323 {ok, ResAcc, BufferedStateData}
1744 catch
1745 _:E ->
1746 1 ?LOG_DEBUG(#{what => send_ack_request_error,
1747 text => <<"maybe_send_ack_request crashed, entering resume session next">>,
1748 1 reason => E, c2s_state => State}),
1749 1 {resume, Acc1, BufferedStateData}
1750 end;
1751 _ ->
1752 35 ?LOG_DEBUG(#{what => send_element_error,
1753 text => <<"Sending element failed, entering resume session next">>,
1754 35 reason => SendResult, c2s_state => State}),
1755 35 {resume, Acc1, BufferedStateData}
1756 end.
1757
1758 13324 result_to_amp_event(ok) -> delivered;
1759 35 result_to_amp_event(_) -> delivery_failed.
1760
1761 -spec send_and_maybe_buffer_stanza_no_ack(mongoose_acc:t(), packet(), state()) ->
1762 {ok | any(), mongoose_acc:t(), state()}.
1763 send_and_maybe_buffer_stanza_no_ack(Acc, {_, _, Stanza} = Packet, State) ->
1764 13385 SendResult = maybe_send_element_from_server_jid_safe(Acc, State, Stanza),
1765 13385 BufferedStateData = buffer_out_stanza(Acc, Packet, State),
1766 13385 {SendResult, Acc, BufferedStateData}.
1767
1768
1769 -spec new_id() -> binary().
1770 new_id() ->
1771 7061 mongoose_bin:gen_from_crypto().
1772
1773 -spec get_conn_type(state()) -> conntype().
1774 get_conn_type(StateData) ->
1775 3037 case (StateData#state.sockmod):get_sockmod(StateData#state.socket) of
1776 2831 gen_tcp -> c2s;
1777 35 ejabberd_tls -> c2s_tls;
1778 ejabberd_zlib ->
1779 8 case ejabberd_zlib:get_sockmod(ejabberd_socket:get_socket(StateData#state.socket)) of
1780
:-(
gen_tcp -> c2s_compressed;
1781 8 ejabberd_tls -> c2s_compressed_tls
1782 end;
1783 163 _ -> unknown
1784 end.
1785
1786
1787 -spec process_presence_probe(From :: jid:simple_jid() | jid:jid(),
1788 To :: jid:jid(),
1789 Acc :: mongoose_acc:t(),
1790 State :: state()) -> mongoose_acc:t().
1791 process_presence_probe(From, To, Acc, StateData) ->
1792 2913 {LFrom, LBareFrom} = lowcase_and_bare(From),
1793 2913 case StateData#state.pres_last of
1794 undefined ->
1795 1 Acc;
1796 _ ->
1797 2912 case {should_retransmit_last_presence(LFrom, LBareFrom, StateData),
1798 specifically_visible_to(LFrom, StateData)} of
1799 {true, _} ->
1800 2912 Timestamp = StateData#state.pres_timestamp,
1801 2912 TS = calendar:system_time_to_rfc3339(Timestamp, [{offset, "Z"}]),
1802 2912 Packet = xml:append_subtags(
1803 StateData#state.pres_last,
1804 %% To is the one sending the presence (the target of the probe)
1805 [jlib:timestamp_to_xml(TS, To, <<>>)]),
1806 2912 check_privacy_and_route_probe(StateData, From, To, Acc, Packet);
1807 {false, true} ->
1808
:-(
ejabberd_router:route(To, From, Acc, #xmlel{name = <<"presence">>});
1809 _ ->
1810
:-(
Acc
1811 end
1812 end.
1813
1814 -spec check_privacy_and_route_probe(StateData :: state(),
1815 From :: jid:jid(),
1816 To :: jid:jid(),
1817 Acc :: mongoose_acc:t(),
1818 Packet :: exml:element()) -> mongoose_acc:t().
1819 check_privacy_and_route_probe(StateData, From, To, Acc, Packet) ->
1820 2912 {Acc1, Res} = privacy_check_packet(Acc, Packet, To, From, out, StateData),
1821 2912 case Res of
1822 allow ->
1823 2912 Pid = element(2, StateData#state.sid),
1824 2912 Acc2 = mongoose_hooks:presence_probe_hook(
1825 StateData#state.host_type,
1826 Acc1,
1827 From, To, Pid),
1828 %% Don't route a presence probe to oneself
1829 2912 case jid:are_equal(From, To) of
1830 false ->
1831 139 ejabberd_router:route(To, From, Acc2, Packet);
1832 true ->
1833 2773 Acc2
1834 end;
1835 _ ->
1836
:-(
Acc1
1837 end.
1838
1839 should_retransmit_last_presence(LFrom, LBareFrom,
1840 #state{pres_invis = Invisible} = S) ->
1841 2912 not Invisible
1842 2912 andalso is_subscribed_to_my_presence(LFrom, LBareFrom, S)
1843 2912 andalso not invisible_to(LFrom, LBareFrom, S).
1844
1845 is_subscribed_to_my_presence(JID, S) ->
1846 34 {Lowcase, Bare} = lowcase_and_bare(JID),
1847 34 is_subscribed_to_my_presence(Lowcase, Bare, S).
1848
1849 is_subscribed_to_my_presence(LFrom, LBareFrom, S) ->
1850 3111 gb_sets:is_element(LFrom, S#state.pres_f)
1851 3103 orelse (LFrom /= LBareFrom)
1852 3046 andalso gb_sets:is_element(LBareFrom, S#state.pres_f).
1853
1854 am_i_subscribed_to_presence(LJID, LBareJID, S) ->
1855 165 gb_sets:is_element(LJID, S#state.pres_t)
1856 150 orelse (LJID /= LBareJID)
1857 133 andalso gb_sets:is_element(LBareJID, S#state.pres_t).
1858
1859 lowcase_and_bare(JID) ->
1860 10155 LJID = jid:to_lower(JID),
1861 10155 { LJID, jid:to_bare(LJID)}.
1862
1863 invisible_to(LFrom, LBareFrom, S) ->
1864 2912 gb_sets:is_element(LFrom, S#state.pres_i)
1865 2912 orelse (LFrom /= LBareFrom)
1866 2912 andalso gb_sets:is_element(LBareFrom, S#state.pres_i).
1867
1868 %% @doc Is generally invisible, but visible to a particular resource?
1869 specifically_visible_to(LFrom, #state{pres_invis = Invisible} = S) ->
1870 2912 Invisible
1871
:-(
andalso gb_sets:is_element(LFrom, S#state.pres_f)
1872
:-(
andalso gb_sets:is_element(LFrom, S#state.pres_a).
1873
1874 %% @doc User updates his presence (non-directed presence packet)
1875 -spec presence_update(Acc :: mongoose_acc:t(),
1876 From :: 'undefined' | jid:jid(),
1877 State :: state()) -> {mongoose_acc:t(), state()}.
1878 presence_update(Acc, From, StateData) ->
1879 3001 Packet = mongoose_acc:element(Acc),
1880 3001 case mongoose_acc:stanza_type(Acc) of
1881 <<"unavailable">> ->
1882 196 Status = exml_query:path(Packet, [{element, <<"status">>}, cdata], <<>>),
1883 196 Info = #{ip => StateData#state.ip, conn => StateData#state.conn,
1884 auth_module => StateData#state.auth_module },
1885 196 Acc1 = ejabberd_sm:unset_presence(Acc,
1886 StateData#state.sid,
1887 StateData#state.jid,
1888 Status,
1889 Info),
1890 196 Acc2 = presence_broadcast(Acc1, StateData#state.pres_a, StateData),
1891 196 Acc3 = presence_broadcast(Acc2, StateData#state.pres_i, StateData),
1892 % and here we reach the end
1893 196 {Acc3, StateData#state{pres_last = undefined,
1894 pres_timestamp = undefined,
1895 pres_a = gb_sets:new(),
1896 pres_i = gb_sets:new(),
1897 pres_invis = false}};
1898 <<"invisible">> ->
1899 1 NewPriority = get_priority_from_presence(Packet),
1900 1 Acc0 = update_priority(Acc, NewPriority, Packet, StateData),
1901 1 case StateData#state.pres_invis of
1902 false ->
1903 1 Acc1 = presence_broadcast(Acc0,
1904 StateData#state.pres_a,
1905 StateData),
1906 1 Acc2 = presence_broadcast(Acc1,
1907 StateData#state.pres_i,
1908 StateData),
1909 1 S1 = StateData#state{pres_last = undefined,
1910 pres_timestamp = undefined,
1911 pres_a = gb_sets:new(),
1912 pres_i = gb_sets:new(),
1913 pres_invis = true},
1914 1 presence_broadcast_first(Acc2, From, S1, Packet);
1915 true ->
1916
:-(
{Acc0, StateData}
1917 end;
1918 <<"error">> ->
1919
:-(
{Acc, StateData};
1920 <<"probe">> ->
1921
:-(
{Acc, StateData};
1922 <<"subscribe">> ->
1923
:-(
{Acc, StateData};
1924 <<"subscribed">> ->
1925
:-(
{Acc, StateData};
1926 <<"unsubscribe">> ->
1927
:-(
{Acc, StateData};
1928 <<"unsubscribed">> ->
1929
:-(
{Acc, StateData};
1930 _ ->
1931 2804 presence_update_to_available(Acc, From, Packet, StateData)
1932 end.
1933
1934 -spec presence_update_to_available(Acc :: mongoose_acc:t(),
1935 From :: jid:jid(),
1936 Packet :: exml:element(),
1937 StateData :: state()) -> {mongoose_acc:t(), state()}.
1938 presence_update_to_available(Acc, From, Packet, StateData) ->
1939 2804 OldPriority = case StateData#state.pres_last of
1940 undefined ->
1941 2781 0;
1942 OldPresence ->
1943 23 get_priority_from_presence(OldPresence)
1944 end,
1945 2804 NewPriority = get_priority_from_presence(Packet),
1946 2804 Timestamp = erlang:system_time(second),
1947 2804 Acc1 = update_priority(Acc, NewPriority, Packet, StateData),
1948
1949 2804 NewStateData = StateData#state{pres_last = Packet,
1950 pres_invis = false,
1951 pres_timestamp = Timestamp},
1952
1953 2804 FromUnavail = (StateData#state.pres_last == undefined) or StateData#state.pres_invis,
1954 2804 ?LOG_DEBUG(#{what => presence_update_to_available,
1955 text => <<"Presence changes from unavailable to available">>,
1956 2804 from_unavail => FromUnavail, acc => Acc, c2s_state => StateData}),
1957 2804 presence_update_to_available(FromUnavail, Acc1, OldPriority, NewPriority, From,
1958 Packet, NewStateData).
1959
1960 %% @doc the first one is run when presence changes from unavailable to anything else
1961 -spec presence_update_to_available(FromUnavailable :: boolean(),
1962 Acc :: mongoose_acc:t(),
1963 OldPriority :: integer(),
1964 NewPriority :: integer(),
1965 From :: jid:jid(),
1966 Packet :: exml:element(),
1967 StateData :: state()) -> {mongoose_acc:t(), state()}.
1968 presence_update_to_available(true, Acc, _, NewPriority, From, Packet, StateData) ->
1969 2781 Acc2 = mongoose_hooks:user_available_hook(StateData#state.host_type,
1970 Acc,
1971 StateData#state.jid),
1972 2781 Res = case NewPriority >= 0 of
1973 true ->
1974 2781 Acc3 = mongoose_hooks:roster_get_subscription_lists(
1975 StateData#state.host_type,
1976 Acc2,
1977 StateData#state.jid),
1978 2781 {_, _, Pending} = mongoose_acc:get(roster, subscription_lists,
1979 {[], [], []}, Acc3),
1980 2781 Acc4 = resend_offline_messages(Acc3, StateData),
1981 2781 resend_subscription_requests(Acc4,
1982 StateData#state{pending_invitations = Pending});
1983 false ->
1984
:-(
{Acc2, StateData}
1985 end,
1986 2781 {Accum, NewStateData1} = Res,
1987 2781 presence_broadcast_first(Accum, From, NewStateData1, Packet);
1988 presence_update_to_available(false, Acc, OldPriority, NewPriority, From, Packet, StateData) ->
1989 23 Acc2 = presence_broadcast_to_trusted(Acc,
1990 StateData,
1991 From,
1992 StateData#state.pres_f,
1993 StateData#state.pres_a,
1994 Packet),
1995 23 Acc3 = case OldPriority < 0 andalso NewPriority >= 0 of
1996 true ->
1997
:-(
resend_offline_messages(Acc2, StateData);
1998 false ->
1999 23 Acc2
2000 end,
2001 23 {Acc3, StateData}.
2002
2003 %% @doc User sends a directed presence packet
2004 -spec presence_track(Acc :: mongoose_acc:t(),
2005 State :: state()) -> {mongoose_acc:t(), state()}.
2006 presence_track(Acc, StateData) ->
2007 414 To = mongoose_acc:to_jid(Acc),
2008 414 LTo = jid:to_lower(To),
2009 414 case mongoose_acc:stanza_type(Acc) of
2010 <<"unavailable">> ->
2011 12 Acc1 = check_privacy_and_route(Acc, StateData),
2012 12 I = gb_sets:del_element(LTo, StateData#state.pres_i),
2013 12 A = gb_sets:del_element(LTo, StateData#state.pres_a),
2014 12 {Acc1, StateData#state{pres_i = I,
2015 pres_a = A}};
2016 <<"invisible">> ->
2017
:-(
Acc1 = check_privacy_and_route(Acc, StateData),
2018
:-(
I = gb_sets:add_element(LTo, StateData#state.pres_i),
2019
:-(
A = gb_sets:del_element(LTo, StateData#state.pres_a),
2020
:-(
{Acc1, StateData#state{pres_i = I,
2021 pres_a = A}};
2022 <<"subscribe">> ->
2023 51 Acc1 = process_presence_subscription_and_route(Acc, subscribe, StateData),
2024 51 {Acc1, StateData};
2025 <<"subscribed">> ->
2026 48 Acc1 = process_presence_subscription_and_route(Acc, subscribed, StateData),
2027 48 {Acc1, StateData};
2028 <<"unsubscribe">> ->
2029 3 Acc1 = process_presence_subscription_and_route(Acc, unsubscribe, StateData),
2030 3 {Acc1, StateData};
2031 <<"unsubscribed">> ->
2032 4 Acc1 = process_presence_subscription_and_route(Acc, unsubscribed, StateData),
2033 4 {Acc1, StateData};
2034 <<"error">> ->
2035 2 Acc1 = check_privacy_and_route(Acc, StateData),
2036 2 {Acc1, StateData};
2037 <<"probe">> ->
2038
:-(
Acc1 = check_privacy_and_route(Acc, StateData),
2039
:-(
{Acc1, StateData};
2040 _ ->
2041 294 Acc1 = check_privacy_and_route(Acc, StateData),
2042 294 I = gb_sets:del_element(LTo, StateData#state.pres_i),
2043 294 A = gb_sets:add_element(LTo, StateData#state.pres_a),
2044 294 {Acc1, StateData#state{pres_i = I,
2045 pres_a = A}}
2046 end.
2047
2048 -spec process_presence_subscription_and_route(Acc :: mongoose_acc:t(),
2049 Type :: subscribe | subscribed | unsubscribe | unsubscribed,
2050 StateData :: state()) -> mongoose_acc:t().
2051 process_presence_subscription_and_route(Acc, Type, StateData) ->
2052 106 From = mongoose_acc:from_jid(Acc),
2053 106 To = mongoose_acc:to_jid(Acc),
2054 106 Acc1 = mongoose_hooks:roster_out_subscription(Acc, From, To, Type),
2055 106 check_privacy_and_route(Acc1, jid:to_bare(From), StateData).
2056
2057 -spec check_privacy_and_route(Acc :: mongoose_acc:t(),
2058 StateData :: state()) -> mongoose_acc:t().
2059 check_privacy_and_route(Acc, StateData) ->
2060 4137 check_privacy_and_route(Acc, mongoose_acc:from_jid(Acc), StateData).
2061
2062 -spec check_privacy_and_route(Acc :: mongoose_acc:t(),
2063 FromRoute :: jid:jid(),
2064 StateData :: state()) -> mongoose_acc:t().
2065 check_privacy_and_route(Acc, FromRoute, StateData) ->
2066 4243 From = mongoose_acc:from_jid(Acc),
2067 4243 To = mongoose_acc:to_jid(Acc),
2068 4243 {Acc1, Res} = privacy_check_packet(Acc, To, out, StateData),
2069 4243 Packet = mongoose_acc:element(Acc1),
2070 4243 case Res of
2071 deny ->
2072 2 {Acc2, Err} = jlib:make_error_reply(Acc1, Packet,
2073 mongoose_xmpp_errors:not_acceptable_cancel()),
2074 2 ejabberd_router:route(To, From, Acc2, Err);
2075 block ->
2076 4 {Acc2, Err} = jlib:make_error_reply(Acc1, Packet, mongoose_xmpp_errors:not_acceptable_blocked()),
2077 4 ejabberd_router:route(To, From, Acc2, Err);
2078 allow ->
2079 4237 ejabberd_router:route(FromRoute, To, Acc1, Packet)
2080 end.
2081
2082
2083 -spec privacy_check_packet(Packet :: exml:element(),
2084 From :: jid:jid(),
2085 To :: jid:jid(),
2086 Dir :: 'in' | 'out',
2087 StateData :: state()) -> allow|deny|block.
2088 privacy_check_packet(#xmlel{} = Packet, From, To, Dir, StateData) ->
2089 % in some cases we need an accumulator-less privacy check
2090 14 Acc = new_acc(StateData, #{location => ?LOCATION,
2091 from_jid => From,
2092 to_jid => To,
2093 element => Packet}),
2094 14 {_, Res} = privacy_check_packet(Acc, To, Dir, StateData),
2095 14 Res.
2096
2097 -spec privacy_check_packet(Acc :: mongoose_acc:t(),
2098 To :: jid:jid(),
2099 Dir :: 'in' | 'out',
2100 StateData :: state()) -> {mongoose_acc:t(), allow|deny|block}.
2101 privacy_check_packet(Acc, To, Dir, StateData) ->
2102 17679 mongoose_privacy:privacy_check_packet(Acc,
2103 StateData#state.jid,
2104 StateData#state.privacy_list,
2105 To,
2106 Dir).
2107
2108 -spec privacy_check_packet(Acc :: mongoose_acc:t(),
2109 Packet :: exml:element(),
2110 From :: jid:jid(),
2111 To :: jid:jid(),
2112 Dir :: 'in' | 'out',
2113 StateData :: state()) -> {mongoose_acc:t(), allow|deny|block}.
2114 privacy_check_packet(Acc, Packet, From, To, Dir, StateData) ->
2115 5955 mongoose_privacy:privacy_check_packet({Acc, Packet},
2116 StateData#state.jid,
2117 StateData#state.privacy_list,
2118 From,
2119 To,
2120 Dir).
2121
2122 -spec presence_broadcast(Acc :: mongoose_acc:t(),
2123 JIDSet :: jid_set(),
2124 State :: state()) -> mongoose_acc:t().
2125 presence_broadcast(Acc, JIDSet, StateData) ->
2126 5578 From = mongoose_acc:from_jid(Acc),
2127 5578 lists:foldl(fun(JID, A) ->
2128 3102 FJID = jid:make(JID),
2129 3102 {A1, Res} = privacy_check_packet(A, FJID, out, StateData),
2130 3102 case Res of
2131 allow ->
2132 3097 ejabberd_router:route(From, FJID, A1);
2133 _ ->
2134 5 A1
2135 end
2136 end, Acc, gb_sets:to_list(JIDSet)).
2137
2138 -spec presence_broadcast_to_trusted(Acc :: mongoose_acc:t(),
2139 State :: state(),
2140 From :: 'undefined' | jid:jid(),
2141 T :: jid_set(),
2142 A :: jid_set(),
2143 Packet :: exml:element()) -> mongoose_acc:t().
2144 presence_broadcast_to_trusted(Acc, StateData, From, T, A, Packet) ->
2145 23 lists:foldl(
2146 fun(JID, Ac) ->
2147 27 case gb_sets:is_element(JID, T) of
2148 true ->
2149 27 FJID = jid:make(JID),
2150 27 check_privacy_and_route_or_ignore(Ac, StateData, From, FJID, Packet, out);
2151 _ ->
2152
:-(
Ac
2153 end
2154 end, Acc, gb_sets:to_list(A)).
2155
2156 -spec presence_broadcast_first(mongoose_acc:t(),
2157 From :: 'undefined' | jid:jid(),
2158 State :: state(),
2159 Packet :: exml:element()) -> {mongoose_acc:t(), state()}.
2160 presence_broadcast_first(Acc0, From, StateData, Packet) ->
2161 2782 Stanza = #xmlel{name = <<"presence">>,
2162 attrs = [{<<"type">>, <<"probe">>}]},
2163 2782 Acc = gb_sets:fold(fun(JID, A) ->
2164 2792 ejabberd_router:route(From, jid:make(JID), A, Stanza)
2165 end,
2166 Acc0,
2167 StateData#state.pres_t),
2168 2782 case StateData#state.pres_invis of
2169 true ->
2170 1 {Acc, StateData};
2171 false ->
2172 2781 {As, AccFinal} = gb_sets:fold(
2173 fun(JID, {A, Accum}) ->
2174 2792 FJID = jid:make(JID),
2175 2792 Accum1 = check_privacy_and_route_or_ignore(Accum, StateData, From, FJID,
2176 Packet, out),
2177 2792 {gb_sets:add_element(JID, A), Accum1}
2178 end,
2179 {StateData#state.pres_a, Acc},
2180 StateData#state.pres_f),
2181 2781 {AccFinal, StateData#state{pres_a = As}}
2182 end.
2183
2184 -spec roster_change(Acc :: mongoose_acc:t(),
2185 IJID :: jid:simple_jid() | jid:jid(),
2186 ISubscription :: from | to | both | none,
2187 State :: state()) -> {mongoose_acc:t(), state()}.
2188 roster_change(Acc, IJID, ISubscription, StateData) ->
2189 369 LIJID = jid:to_lower(IJID),
2190 369 IsSubscribedToMe = (ISubscription == both) or (ISubscription == from),
2191 369 AmISubscribedTo = (ISubscription == both) or (ISubscription == to),
2192 369 WasSubscribedToMe = gb_sets:is_element(LIJID, StateData#state.pres_f),
2193 369 FSet = case IsSubscribedToMe of
2194 true ->
2195 104 gb_sets:add_element(LIJID, StateData#state.pres_f);
2196 false ->
2197 265 gb_sets:del_element(LIJID, StateData#state.pres_f)
2198 end,
2199 369 TSet = case AmISubscribedTo of
2200 true ->
2201 110 gb_sets:add_element(LIJID, StateData#state.pres_t);
2202 false ->
2203 259 gb_sets:del_element(LIJID, StateData#state.pres_t)
2204 end,
2205 369 case StateData#state.pres_last of
2206 undefined ->
2207
:-(
{Acc, StateData#state{pres_f = FSet, pres_t = TSet}};
2208 P ->
2209 369 ?LOG_DEBUG(#{what => roster_changed, roster_jid => LIJID,
2210 369 acc => Acc, c2s_state => StateData}),
2211 369 From = StateData#state.jid,
2212 369 To = jid:make(IJID),
2213 369 IsntInvisible = not StateData#state.pres_invis,
2214 369 ImAvailableTo = gb_sets:is_element(LIJID, StateData#state.pres_a),
2215 369 ImInvisibleTo = gb_sets:is_element(LIJID, StateData#state.pres_i),
2216 369 BecomeAvailable = IsntInvisible and IsSubscribedToMe and not WasSubscribedToMe,
2217 369 BecomeUnavailable = not IsSubscribedToMe and WasSubscribedToMe
2218 and (ImAvailableTo or ImInvisibleTo),
2219 369 case {BecomeAvailable, BecomeUnavailable} of
2220 {true, _} ->
2221 71 ?LOG_DEBUG(#{what => become_available_to, roster_jid => LIJID,
2222 71 acc => Acc, c2s_state => StateData}),
2223 71 Acc1 = check_privacy_and_route_or_ignore(Acc, StateData, From, To, P, out),
2224 71 A = gb_sets:add_element(LIJID,
2225 StateData#state.pres_a),
2226 71 NState = StateData#state{pres_a = A,
2227 pres_f = FSet,
2228 pres_t = TSet},
2229 71 {Acc1, NState};
2230 {_, true} ->
2231 17 ?LOG_DEBUG(#{what => become_unavailable_to, roster_jid => LIJID,
2232 17 acc => Acc, c2s_state => StateData}),
2233 17 PU = #xmlel{name = <<"presence">>,
2234 attrs = [{<<"type">>, <<"unavailable">>}]},
2235 17 Acc1 = check_privacy_and_route_or_ignore(Acc, StateData, From, To, PU, out),
2236 17 I = gb_sets:del_element(LIJID,
2237 StateData#state.pres_i),
2238 17 A = gb_sets:del_element(LIJID,
2239 StateData#state.pres_a),
2240 17 NState = StateData#state{pres_i = I,
2241 pres_a = A,
2242 pres_f = FSet,
2243 pres_t = TSet},
2244 17 {Acc1, NState};
2245 _ ->
2246 281 {Acc, StateData#state{pres_f = FSet, pres_t = TSet}}
2247 end
2248 end.
2249
2250 -spec update_priority(Acc :: mongoose_acc:t(),
2251 Priority :: integer(),
2252 Packet :: exml:element(),
2253 State :: state()) -> mongoose_acc:t().
2254 update_priority(Acc, Priority, Packet, StateData) ->
2255 2805 Info = #{ip => StateData#state.ip, conn => StateData#state.conn,
2256 auth_module => StateData#state.auth_module },
2257 2805 ejabberd_sm:set_presence(Acc,
2258 StateData#state.sid,
2259 StateData#state.jid,
2260 Priority,
2261 Packet,
2262 Info).
2263
2264
2265 -spec get_priority_from_presence(Packet :: exml:element()) -> integer().
2266 get_priority_from_presence(undefined) ->
2267
:-(
0;
2268 get_priority_from_presence(PresencePacket) ->
2269 2838 case xml:get_subtag(PresencePacket, <<"priority">>) of
2270 false ->
2271 2835 0;
2272 SubEl ->
2273 3 try binary_to_integer(xml:get_tag_cdata(SubEl)) of
2274 3 P when is_integer(P) -> P
2275 catch
2276
:-(
error:badarg -> 0
2277 end
2278 end.
2279
2280 -spec process_privacy_iq(Acc :: mongoose_acc:t(),
2281 To :: jid:jid(),
2282 StateData :: state()) -> {mongoose_acc:t(), state()}.
2283 process_privacy_iq(Acc1, To, StateData) ->
2284 214 case mongoose_iq:info(Acc1) of
2285 {#iq{type = Type, sub_el = SubEl} = IQ, Acc2} when Type == get; Type == set ->
2286 210 From = mongoose_acc:from_jid(Acc2),
2287 210 {Acc3, NewStateData} = process_privacy_iq(Acc2, Type, To, StateData),
2288 210 Res = mongoose_acc:get(hook, result,
2289 {error, mongoose_xmpp_errors:feature_not_implemented(
2290 <<"en">>, <<"Failed to handle the privacy IQ request in c2s">>)}, Acc3),
2291 210 IQRes = case Res of
2292 {result, Result} ->
2293 132 IQ#iq{type = result, sub_el = Result};
2294 {result, Result, _} ->
2295 70 IQ#iq{type = result, sub_el = Result};
2296 {error, Error} ->
2297 8 IQ#iq{type = error, sub_el = [SubEl, Error]}
2298 end,
2299 210 Acc4 = ejabberd_router:route(To, From, Acc3, jlib:iq_to_xml(IQRes)),
2300 210 {Acc4, NewStateData};
2301 _ ->
2302 4 {Acc1, StateData}
2303 end.
2304
2305 -spec process_privacy_iq(Acc :: mongoose_acc:t(),
2306 Type :: get | set,
2307 To :: jid:jid(),
2308 StateData :: state()) -> {mongoose_acc:t(), state()}.
2309 process_privacy_iq(Acc, get, To, StateData) ->
2310 66 From = mongoose_acc:from_jid(Acc),
2311 66 {IQ, Acc1} = mongoose_iq:info(Acc),
2312 66 Acc2 = mongoose_hooks:privacy_iq_get(StateData#state.host_type, Acc1,
2313 From, To, IQ, StateData#state.privacy_list),
2314 66 {Acc2, StateData};
2315 process_privacy_iq(Acc, set, To, StateData) ->
2316 144 From = mongoose_acc:from_jid(Acc),
2317 144 {IQ, Acc1} = mongoose_iq:info(Acc),
2318 144 Acc2 = mongoose_hooks:privacy_iq_set(StateData#state.host_type, Acc1,
2319 From, To, IQ),
2320 144 case mongoose_acc:get(hook, result, undefined, Acc2) of
2321 {result, _, NewPrivList} ->
2322 70 maybe_update_presence(Acc2, StateData, NewPrivList),
2323 70 NState = StateData#state{privacy_list = NewPrivList},
2324 70 {Acc2, NState};
2325 74 _ -> {Acc2, StateData}
2326 end.
2327
2328
2329 -spec resend_offline_messages(mongoose_acc:t(), state()) -> mongoose_acc:t().
2330 resend_offline_messages(Acc, StateData) ->
2331 2782 ?LOG_DEBUG(#{what => resend_offline_messages,
2332 2782 acc => Acc, c2s_state => StateData}),
2333 2782 Acc1 = mongoose_hooks:resend_offline_messages_hook(Acc, StateData#state.jid),
2334 2782 Rs = mongoose_acc:get(offline, messages, [], Acc1),
2335 2782 Acc2 = lists:foldl(
2336 fun({route, From, To, MsgAcc}, A) ->
2337 136 resend_offline_message(A, StateData, From, To, MsgAcc, in)
2338 end,
2339 Acc1,
2340 Rs),
2341 2782 mongoose_acc:delete(offline, messages, Acc2). % they are gone from db backend and sent
2342
2343
2344 resend_offline_message(Acc0, StateData, From, To, Acc, in) ->
2345 136 Packet = mongoose_acc:element(Acc),
2346 136 NewAcc = strip_c2s_fields(Acc),
2347 136 check_privacy_and_route_or_ignore(NewAcc, StateData, From, To, Packet, in),
2348 136 Acc0.
2349
2350
2351 -spec check_privacy_and_route_or_ignore(Acc :: mongoose_acc:t(),
2352 StateData :: state(),
2353 From :: jid:jid(),
2354 To :: jid:jid(),
2355 Packet :: exml:element(),
2356 Dir :: in | out) -> any().
2357 check_privacy_and_route_or_ignore(Acc, StateData, From, To, Packet, Dir) ->
2358 3043 {Acc2, Res} = privacy_check_packet(Acc, Packet, From, To, Dir, StateData),
2359 3043 case Res of
2360 3043 allow -> ejabberd_router:route(From, To, Acc2, Packet);
2361
:-(
_ -> Acc2
2362 end.
2363
2364 -spec resend_subscription_requests(mongoose_acc:t(), state()) -> {mongoose_acc:t(), state()}.
2365 resend_subscription_requests(Acc, #state{pending_invitations = Pending} = StateData) ->
2366 2781 {NewAcc, NewState} = lists:foldl(
2367 fun(XMLPacket, {A, #state{} = State}) ->
2368 3 A1 = send_element(A, XMLPacket, State),
2369 % We retrieve From i To from a stanza, because Acc has
2370 % from_jid and to_jid that apply to 'available' stanza sent
2371 % by the client
2372 3 {value, From} = xml:get_tag_attr(<<"from">>, XMLPacket),
2373 3 {value, To} = xml:get_tag_attr(<<"to">>, XMLPacket),
2374 3 PacketTuple = {jid:from_binary(From), jid:from_binary(To), XMLPacket},
2375 3 BufferedStateData = buffer_out_stanza(A1, PacketTuple, State),
2376 % this one will be next to tackle
2377 3 A2 = maybe_send_ack_request(A1, BufferedStateData),
2378 3 {A2, BufferedStateData}
2379 end, {Acc, StateData}, Pending),
2380 2781 {NewAcc, NewState#state{pending_invitations = []}}.
2381
2382
2383 get_showtag(undefined) ->
2384
:-(
<<"unavailable">>;
2385 get_showtag(Presence) ->
2386 22 case xml:get_path_s(Presence, [{elem, <<"show">>}, cdata]) of
2387 8 <<>> -> <<"available">>;
2388 14 ShowTag -> ShowTag
2389 end.
2390
2391
2392 get_statustag(undefined) ->
2393
:-(
<<>>;
2394 get_statustag(Presence) ->
2395 22 case xml:get_path_s(Presence, [{elem, <<"status">>}, cdata]) of
2396 22 ShowTag -> ShowTag
2397 end.
2398
2399
2400 -spec process_unauthenticated_stanza(State :: state(),
2401 El :: exml:element()) -> any().
2402 process_unauthenticated_stanza(StateData, El) ->
2403 384 NewEl = case xml:get_tag_attr_s(<<"xml:lang">>, El) of
2404 <<>> ->
2405 384 case StateData#state.lang of
2406
:-(
<<>> -> El;
2407 L ->
2408 384 xml:replace_tag_attr(<<"xml:lang">>, L, El)
2409 end;
2410 _ ->
2411
:-(
El
2412 end,
2413 384 Lang = xml:get_tag_attr_s(<<"xml:lang">>, NewEl),
2414 384 case jlib:iq_query_info(NewEl) of
2415 #iq{} = IQ ->
2416 380 Res = mongoose_hooks:c2s_unauthenticated_iq(
2417 StateData#state.host_type,
2418 StateData#state.server,
2419 IQ, StateData#state.ip),
2420 380 case Res of
2421 empty ->
2422 % The only reasonable IQ's here are auth and register IQ's
2423 % They contain secrets, so don't include subelements to response
2424
:-(
Text = <<"Forbidden unauthenticated stanza">>,
2425
:-(
ResIQ = IQ#iq{type = error,
2426 sub_el = [mongoose_xmpp_errors:service_unavailable(Lang, Text)]},
2427
:-(
Res1 = jlib:replace_from_to(
2428 jid:make_noprep(<<>>, StateData#state.server, <<>>),
2429 jid:make_noprep(<<>>, <<>>, <<>>),
2430 jlib:iq_to_xml(ResIQ)),
2431
:-(
send_element_from_server_jid(StateData, jlib:remove_attr(<<"to">>, Res1));
2432 _ ->
2433 380 send_element_from_server_jid(StateData, Res)
2434 end;
2435 _ ->
2436 % Drop any stanza, which isn't IQ stanza
2437 4 ok
2438 end.
2439
2440
2441 -spec peerip(SockMod :: ejabberd:sockmod(), inet:socket()) ->
2442 undefined | {inet:ip_address(), inet:port_number()}.
2443 peerip(SockMod, Socket) ->
2444 4377 case mongoose_transport:peername(SockMod, Socket) of
2445 4377 {ok, IPOK} -> IPOK;
2446
:-(
_ -> undefined
2447 end.
2448
2449
2450 %% @doc fsm_next_state_pack: Pack the StateData structure to improve sharing.
2451 fsm_next_state_pack(StateName, StateData) ->
2452 3026 fsm_next_state_gc(StateName, pack(StateData)).
2453
2454
2455 %% @doc fsm_next_state_gc: Garbage collect the process heap to make use of
2456 %% the newly packed StateData structure.
2457 fsm_next_state_gc(StateName, PackedStateData) ->
2458 3026 erlang:garbage_collect(),
2459 3026 fsm_next_state(StateName, PackedStateData).
2460
2461
2462 %% @doc fsm_next_state: Generate the next_state FSM tuple with different
2463 %% timeout, depending on the future state
2464 fsm_next_state(session_established, StateData) ->
2465 24790 {next_state, session_established, StateData, maybe_hibernate(StateData)};
2466 fsm_next_state(StateName, StateData) ->
2467 13641 {next_state, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
2468
2469
2470 %% @doc fsm_reply: Generate the reply FSM tuple with different timeout,
2471 %% depending on the future state
2472 fsm_reply(Reply, session_established, StateData) ->
2473 22 {reply, Reply, session_established, StateData, maybe_hibernate(StateData)};
2474 fsm_reply(Reply, StateName, StateData) ->
2475
:-(
{reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
2476
2477
2478 %% @doc Used by c2s blacklist plugins
2479 -spec is_ip_blacklisted('undefined' | {inet:ip_address(), inet:port_number()}
2480 ) -> boolean().
2481 is_ip_blacklisted(undefined) ->
2482
:-(
false;
2483 is_ip_blacklisted({IP, _Port}) ->
2484 4360 mongoose_hooks:check_bl_c2s(IP).
2485
2486
2487 %% @doc Check from attributes.
2488 -spec check_from(El, C2SJID) -> Result when
2489 El :: exml:element(), C2SJID :: jid:jid(),
2490 Result :: 'invalid-from' | exml:element().
2491 check_from(El, #jid{ luser = C2SU, lserver = C2SS, lresource = C2SR }) ->
2492 7538 case xml:get_tag_attr(<<"from">>, El) of
2493 false ->
2494 6784 El;
2495 {value, SJID} ->
2496 754 case jid:from_binary(SJID) of
2497 #jid{ luser = U, lserver = S, lresource = R }
2498 752 when U == C2SU andalso S == C2SS andalso (R == C2SR orelse R == <<>>) -> El;
2499 2 _ -> 'invalid-from'
2500 end
2501 end.
2502
2503
2504 fsm_limit_opts(Opts) ->
2505 4360 case lists:keyfind(max_fsm_queue, 1, Opts) of
2506 {_, N} when is_integer(N) ->
2507
:-(
[{max_queue, N}];
2508 _ ->
2509 4360 case mongoose_config:lookup_opt(max_fsm_queue) of
2510 {ok, N} ->
2511 4360 [{max_queue, N}];
2512 {error, not_found} ->
2513
:-(
[]
2514 end
2515 end.
2516
2517 -spec bounce_messages(list(), #state{}) -> 'ok'.
2518 bounce_messages(UnreadMessages, StateData) ->
2519 6364 case get_msg(UnreadMessages) of
2520 {ok, {route, From, To, Acc}, RemainedUnreadMessages} ->
2521 70 reroute(From, To, Acc, StateData),
2522 70 bounce_messages(RemainedUnreadMessages, StateData);
2523 {ok, {store_session_info, JID, Key, Value, _FromPid}, _} ->
2524
:-(
ejabberd_sm:store_info(JID, Key, Value);
2525 {ok, _, RemainedUnreadMessages} ->
2526 % ignore this one, get the next message
2527 3269 bounce_messages(RemainedUnreadMessages, StateData);
2528 {error, no_messages} ->
2529 3025 ok
2530 end.
2531
2532 %% Return the messages in reverse order than they were received in!
2533 -spec flush_messages(list()) -> [mongoose_acc:t()].
2534 flush_messages(UnreadMessages) ->
2535 11 flush_messages([], UnreadMessages).
2536
2537 -spec flush_messages([mongoose_acc:t()], list()) -> [mongoose_acc:t()].
2538 flush_messages(Acc, UnreadMessages) ->
2539 13 case get_msg(UnreadMessages) of
2540 {ok, {route, From, To, MongooseAcc}, RemainedUnreadMessages} ->
2541 2 El = mongoose_acc:element(MongooseAcc),
2542 2 NewMongooseAcc = update_stanza(From, To, El, MongooseAcc),
2543 2 NewAcc = [NewMongooseAcc | Acc],
2544 2 flush_messages(NewAcc, RemainedUnreadMessages);
2545 {ok, _, RemainedUnreadMessages} ->
2546 % ignore this one, get the next message
2547
:-(
flush_messages(Acc, RemainedUnreadMessages);
2548 {error, no_messages} ->
2549 11 Acc
2550 end.
2551
2552 get_msg([H | T]) ->
2553 33 {ok, H, T};
2554 get_msg([]) ->
2555 6344 receive
2556 3308 Msg -> {ok, Msg, []}
2557 after 0 ->
2558 3036 {error, no_messages}
2559 end.
2560
2561 %%%----------------------------------------------------------------------
2562 %%% XEP-0016
2563 %%%----------------------------------------------------------------------
2564
2565 maybe_update_presence(Acc, StateData = #state{jid = JID, pres_f = Froms}, NewList) ->
2566 % Our own jid is added to pres_f, even though we're not a "contact", so for
2567 % the purposes of this check we don't want it:
2568 123 SelfJID = jid:to_lower(jid:to_bare(JID)),
2569 123 FromsExceptSelf = gb_sets:del_element(SelfJID, Froms),
2570
2571 123 gb_sets:fold(
2572 fun(T, Ac) ->
2573 7 send_unavail_if_newly_blocked(Ac, StateData, jid:make(T), NewList)
2574 end, Acc, FromsExceptSelf).
2575
2576 send_unavail_if_newly_blocked(Acc, StateData = #state{jid = JID},
2577 ContactJID, NewList) ->
2578 7 Packet = #xmlel{name = <<"presence">>,
2579 attrs = [{<<"type">>, <<"unavailable">>}]},
2580 %% WARNING: we can not use accumulator to cache privacy check result - this is
2581 %% the only place where the list to check against changes
2582 7 OldResult = privacy_check_packet(Packet, JID, ContactJID, out, StateData),
2583 7 NewResult = privacy_check_packet(Packet, JID, ContactJID, out,
2584 StateData#state{privacy_list = NewList}),
2585 7 send_unavail_if_newly_blocked(Acc, OldResult, NewResult, JID,
2586 ContactJID, Packet).
2587
2588 send_unavail_if_newly_blocked(Acc, allow, deny, From, To, Packet) ->
2589 2 ejabberd_router:route(From, To, Acc, Packet);
2590 send_unavail_if_newly_blocked(Acc, _, _, _, _, _) ->
2591 5 Acc.
2592
2593 %%%----------------------------------------------------------------------
2594 %%% XEP-0191
2595 %%%----------------------------------------------------------------------
2596
2597 -spec blocking_push_to_resources(Action :: blocking_type(),
2598 JIDS :: [binary()],
2599 State :: state()) -> ok.
2600 blocking_push_to_resources(Action, JIDs, StateData) ->
2601 29 SubEl =
2602 case Action of
2603 block ->
2604 20 #xmlel{name = <<"block">>,
2605 attrs = [{<<"xmlns">>, ?NS_BLOCKING}],
2606 children = lists:map(
2607 fun(JID) ->
2608 26 #xmlel{name = <<"item">>,
2609 attrs = [{<<"jid">>, JID}]}
2610 end, JIDs)};
2611 unblock ->
2612 9 #xmlel{name = <<"unblock">>,
2613 attrs = [{<<"xmlns">>, ?NS_BLOCKING}],
2614 children = lists:map(
2615 fun(JID) ->
2616 7 #xmlel{name = <<"item">>,
2617 attrs = [{<<"jid">>, JID}]}
2618 end, JIDs)}
2619 end,
2620 29 PrivPushIQ = #iq{type = set, xmlns = ?NS_BLOCKING,
2621 id = <<"push">>,
2622 sub_el = [SubEl]},
2623 29 F = jid:to_bare(StateData#state.jid),
2624 29 T = StateData#state.jid,
2625 29 PrivPushEl = jlib:replace_from_to(F, T, jlib:iq_to_xml(PrivPushIQ)),
2626 29 ejabberd_router:route(F, T, PrivPushEl),
2627 29 ok.
2628
2629 -spec blocking_presence_to_contacts(Action :: blocking_type(),
2630 JIDs :: [binary()],
2631 State :: state()) -> ok.
2632 blocking_presence_to_contacts(_Action, [], _StateData) ->
2633 29 ok;
2634 blocking_presence_to_contacts(Action, [Jid|JIDs], StateData) ->
2635 33 Pres = case Action of
2636 block ->
2637 26 #xmlel{name = <<"presence">>,
2638 attrs = [{<<"xml:lang">>, <<"en">>}, {<<"type">>, <<"unavailable">>}]
2639 };
2640 unblock ->
2641 7 StateData#state.pres_last
2642 end,
2643 33 T = jid:from_binary(Jid),
2644 33 case is_subscribed_to_my_presence(T, StateData) of
2645 true ->
2646
:-(
F = jid:to_bare(StateData#state.jid),
2647
:-(
ejabberd_router:route(F, T, Pres);
2648 false ->
2649 33 ok
2650 end,
2651 33 blocking_presence_to_contacts(Action, JIDs, StateData).
2652
2653 -type pack_tree() :: gb_trees:tree(binary() | jid:simple_jid(),
2654 binary() | jid:simple_jid()).
2655
2656 %% @doc Try to reduce the heap footprint of the four presence sets
2657 %% by ensuring that we re-use strings and Jids wherever possible.
2658 -spec pack(S :: state()) -> state().
2659 pack(S = #state{pres_a=A,
2660 pres_i=I,
2661 pres_f=F,
2662 pres_t=T}) ->
2663 3026 {NewA, Pack1} = pack_jid_set(A, gb_trees:empty()),
2664 3026 {NewI, Pack2} = pack_jid_set(I, Pack1),
2665 3026 {NewF, Pack3} = pack_jid_set(F, Pack2),
2666 3026 {NewT, _Pack4} = pack_jid_set(T, Pack3),
2667 %% Throw away Pack4 so that if we delete references to
2668 %% Strings or Jids in any of the sets there will be
2669 %% no live references for the GC to find.
2670 3026 S#state{pres_a=NewA,
2671 pres_i=NewI,
2672 pres_f=NewF,
2673 pres_t=NewT}.
2674
2675
2676 -spec pack_jid_set(Set :: jid_set(),
2677 Pack :: pack_tree()) -> {jid_set(), pack_tree()}.
2678 pack_jid_set(Set, Pack) ->
2679 12104 Jids = gb_sets:to_list(Set),
2680 12104 {PackedJids, NewPack} = pack_jids(Jids, Pack, []),
2681 12104 {gb_sets:from_list(PackedJids), NewPack}.
2682
2683
2684 -spec pack_jids([{_, _, _}], Pack :: pack_tree(), Acc :: [jid:simple_jid()]) ->
2685 {[jid:simple_jid()], pack_tree()}.
2686 12104 pack_jids([], Pack, Acc) -> {Acc, Pack};
2687 pack_jids([{U, S, R}=Jid | Jids], Pack, Acc) ->
2688 6074 case gb_trees:lookup(Jid, Pack) of
2689 {value, PackedJid} ->
2690 3037 pack_jids(Jids, Pack, [PackedJid | Acc]);
2691 none ->
2692 3037 {NewU, Pack1} = pack_string(U, Pack),
2693 3037 {NewS, Pack2} = pack_string(S, Pack1),
2694 3037 {NewR, Pack3} = pack_string(R, Pack2),
2695 3037 NewJid = {NewU, NewS, NewR},
2696 3037 NewPack = gb_trees:insert(NewJid, NewJid, Pack3),
2697 3037 pack_jids(Jids, NewPack, [NewJid | Acc])
2698 end.
2699
2700
2701 -spec pack_string(String :: binary(), Pack :: pack_tree()) -> {binary(), pack_tree()}.
2702 pack_string(String, Pack) ->
2703 9111 case gb_trees:lookup(String, Pack) of
2704 {value, PackedString} ->
2705 22 {PackedString, Pack};
2706 none ->
2707 9089 {String, gb_trees:insert(String, String, Pack)}
2708 end.
2709
2710 %%%----------------------------------------------------------------------
2711 %%% XEP-0352: Client State Indication
2712 %%%----------------------------------------------------------------------
2713 maybe_inactivate_session(?NS_CSI, #state{csi_state = active} = State) ->
2714 8 fsm_next_state(session_established, State#state{csi_state = inactive});
2715 maybe_inactivate_session(_, State) ->
2716 2 fsm_next_state(session_established, State).
2717
2718 maybe_activate_session(?NS_CSI, #state{csi_state = inactive} = State) ->
2719 3 resend_csi_buffer(State);
2720 maybe_activate_session(_, State) ->
2721
:-(
fsm_next_state(session_established, State).
2722
2723 resend_csi_buffer(State) ->
2724 3 NewState = flush_csi_buffer(State),
2725 3 fsm_next_state(session_established, NewState#state{csi_state=active}).
2726
2727 -spec ship_to_local_user(mongoose_acc:t(), packet(), state()) ->
2728 {ok | resume, mongoose_acc:t(), state()}.
2729 ship_to_local_user(Acc, Packet, State) ->
2730 10359 maybe_csi_inactive_optimisation(Acc, Packet, State).
2731
2732 -spec maybe_csi_inactive_optimisation(mongoose_acc:t(), packet(), state()) ->
2733 {ok | resume, mongoose_acc:t(), state()}.
2734 maybe_csi_inactive_optimisation(Acc, Packet, #state{csi_state = active} = State) ->
2735 10333 send_and_maybe_buffer_stanza(Acc, Packet, State);
2736 maybe_csi_inactive_optimisation(Acc, {From,To,El}, #state{csi_buffer = Buffer} = State) ->
2737 26 NewAcc = update_stanza(From, To, El, Acc),
2738 26 NewBuffer = [NewAcc | Buffer],
2739 26 NewState = flush_or_buffer_packets(State#state{csi_buffer = NewBuffer}),
2740 26 {ok, Acc, NewState}.
2741
2742 flush_or_buffer_packets(State) ->
2743 26 MaxBuffSize = gen_mod:get_module_opt(State#state.host_type, mod_csi,
2744 buffer_max, 20),
2745 26 case length(State#state.csi_buffer) > MaxBuffSize of
2746 true ->
2747 1 flush_csi_buffer(State);
2748 _ ->
2749 25 State
2750 end.
2751
2752 -spec flush_csi_buffer(state()) -> state().
2753 flush_csi_buffer(#state{csi_buffer = BufferOut} = State) ->
2754 %%lists:foldr to preserve order
2755 14 F = fun(Acc, {_, _, OldState}) ->
2756 26 {From, To, El} = mongoose_acc:packet(Acc),
2757 26 send_and_maybe_buffer_stanza_no_ack(Acc, {From, To, El}, OldState)
2758 end,
2759 14 {_, _, NewState} = lists:foldr(F, {ok, ok, State}, BufferOut),
2760 14 NewState#state{csi_buffer = []}.
2761
2762 bounce_csi_buffer(#state{csi_buffer = []}) ->
2763 3023 ok;
2764 bounce_csi_buffer(#state{csi_buffer = Buffer} = State) ->
2765 2 re_route_packets(Buffer, State).
2766
2767 %%%----------------------------------------------------------------------
2768 %%% XEP-0198: Stream Management
2769 %%%----------------------------------------------------------------------
2770 maybe_enable_stream_mgmt(NextState, El, StateData = #state{host_type = HostType}) ->
2771 61 case {xml:get_tag_attr_s(<<"xmlns">>, El),
2772 StateData#state.stream_mgmt,
2773 xml:get_tag_attr_s(<<"resume">>, El)}
2774 of
2775 {?NS_STREAM_MGNT_3, false, Resume} ->
2776 %% turn on
2777 60 {NewSD, EnabledEl} = case lists:member(Resume, [<<"true">>, <<"1">>]) of
2778 false ->
2779 27 {StateData, stream_mgmt_enabled()};
2780 true ->
2781 33 enable_stream_resumption(StateData)
2782 end,
2783 60 send_element_from_server_jid(NewSD, EnabledEl),
2784 60 BufferMax = mod_stream_management:get_buffer_max(HostType),
2785 60 AckFreq = mod_stream_management:get_ack_freq(HostType),
2786 60 ResumeTimeout = mod_stream_management:get_resume_timeout(HostType),
2787 60 fsm_next_state(NextState,
2788 NewSD#state{stream_mgmt = true,
2789 stream_mgmt_buffer_max = BufferMax,
2790 stream_mgmt_ack_freq = AckFreq,
2791 stream_mgmt_resume_timeout = ResumeTimeout});
2792 {?NS_STREAM_MGNT_3, true, _} ->
2793 1 c2s_stream_error(stream_mgmt_failed(<<"unexpected-request">>), StateData);
2794 {?NS_STREAM_MGNT_3, disabled, _} ->
2795
:-(
c2s_stream_error(stream_mgmt_failed(<<"feature-not-implemented">>), StateData);
2796 {_, _, _} ->
2797 %% invalid namespace
2798
:-(
c2s_stream_error(mongoose_xmpp_errors:invalid_namespace(), StateData)
2799 end.
2800
2801 enable_stream_resumption(SD = #state{host_type = HostType}) ->
2802 33 SMID = mod_stream_management:make_smid(),
2803 33 SID = case SD#state.sid of
2804
:-(
undefined -> ejabberd_sm:make_new_sid();
2805 33 RSID -> RSID
2806 end,
2807 33 ok = mod_stream_management:register_smid(HostType, SMID, SID),
2808 33 {SD#state{stream_mgmt_id = SMID, sid = SID},
2809 stream_mgmt_enabled([{<<"id">>, SMID}, {<<"resume">>, <<"true">>}])}.
2810
2811 maybe_unexpected_sm_request(NextState, El, StateData) ->
2812 2 case xml:get_tag_attr_s(<<"xmlns">>, El) of
2813 ?NS_STREAM_MGNT_3 ->
2814 2 send_element_from_server_jid(StateData, stream_mgmt_failed(<<"unexpected-request">>)),
2815 2 fsm_next_state(NextState, StateData);
2816 _ ->
2817
:-(
c2s_stream_error(mongoose_xmpp_errors:invalid_namespace(), StateData)
2818 end.
2819
2820 stream_mgmt_handle_ack(NextState, El, #state{} = SD) ->
2821 49 case {exml_query:attr(El, <<"xmlns">>), stream_mgmt_parse_h(El)} of
2822 {NS, _} when NS =/= ?NS_STREAM_MGNT_3 ->
2823
:-(
maybe_send_element_from_server_jid_safe(SD, mongoose_xmpp_errors:invalid_namespace()),
2824
:-(
maybe_send_trailer_safe(SD),
2825
:-(
{stop, normal, SD};
2826 {_, invalid_h_attribute} ->
2827 1 PolicyViolationErr = mongoose_xmpp_errors:policy_violation(
2828 SD#state.lang, <<"Invalid h attribute">>),
2829 1 maybe_send_element_from_server_jid_safe(SD, PolicyViolationErr),
2830 1 maybe_send_trailer_safe(SD),
2831 1 {stop, normal, SD};
2832 {_, Handled} ->
2833 48 try
2834 48 NSD = #state{} = do_handle_ack(Handled,
2835 SD#state.stream_mgmt_out_acked,
2836 SD#state.stream_mgmt_buffer,
2837 SD#state.stream_mgmt_buffer_size,
2838 SD),
2839 44 fsm_next_state(NextState, NSD)
2840 catch
2841 throw:{undefined_condition, H, OldAcked} ->
2842 4 #xmlel{children = [UndefCond, Text]} = ErrorStanza0
2843 = mongoose_xmpp_errors:undefined_condition(
2844 SD#state.lang, <<"You acknowledged more stanzas that what has been sent">>),
2845 4 HandledCountField = sm_handled_count_too_high_stanza(H, OldAcked),
2846 4 ErrorStanza = ErrorStanza0#xmlel{children = [UndefCond, HandledCountField, Text]},
2847 4 maybe_send_element_from_server_jid_safe(SD, ErrorStanza),
2848 4 maybe_send_trailer_safe(SD),
2849 4 {stop, normal, SD}
2850 end
2851 end.
2852
2853 stream_mgmt_parse_h(El) ->
2854 49 case catch binary_to_integer(exml_query:attr(El, <<"h">>)) of
2855 48 H when is_integer(H) -> H;
2856 1 _ -> invalid_h_attribute
2857 end.
2858
2859 do_handle_ack(Handled, OldAcked, Buffer, BufferSize, SD) ->
2860 48 ToDrop = calc_to_drop(Handled, OldAcked),
2861 48 ToDrop > BufferSize andalso throw({undefined_condition, Handled, OldAcked}),
2862 44 {Dropped, NewBuffer} = drop_last(ToDrop, Buffer),
2863 44 NewSize = BufferSize - Dropped,
2864 44 SD#state{stream_mgmt_out_acked = Handled,
2865 stream_mgmt_buffer = NewBuffer,
2866 stream_mgmt_buffer_size = NewSize}.
2867
2868 calc_to_drop(Handled, OldAcked) when Handled >= OldAcked ->
2869 48 Handled - OldAcked;
2870 calc_to_drop(Handled, OldAcked) ->
2871
:-(
Handled + ?STREAM_MGMT_H_MAX - OldAcked + 1.
2872
2873 maybe_send_sm_ack(?NS_STREAM_MGNT_3, StreamMgmt, _NIncoming, NextState, StateData)
2874 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
2875
:-(
?LOG_WARNING(#{what => unexpected_r, c2s_state => StateData,
2876
:-(
text => <<"received <r/> but stream management is off!">>}),
2877
:-(
fsm_next_state(NextState, StateData);
2878 maybe_send_sm_ack(?NS_STREAM_MGNT_3, true, NIncoming,
2879 NextState, StateData) ->
2880 7 send_element_from_server_jid(StateData, stream_mgmt_ack(NIncoming)),
2881 7 fsm_next_state(NextState, StateData);
2882 maybe_send_sm_ack(_, _, _, _NextState, StateData) ->
2883
:-(
c2s_stream_error(mongoose_xmpp_errors:invalid_namespace(), StateData).
2884
2885 maybe_increment_sm_incoming(StreamMgmt, StateData)
2886 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
2887 10481 StateData;
2888 maybe_increment_sm_incoming(true, StateData) ->
2889 84 Incoming = StateData#state.stream_mgmt_in,
2890 84 StateData#state{stream_mgmt_in = increment_sm_incoming(Incoming)}.
2891
2892 increment_sm_incoming(Incoming) ->
2893 84 increment_sm_counter(Incoming, 1).
2894
2895 increment_sm_counter(Incoming, Increment)
2896 when Incoming + Increment >= ?STREAM_MGMT_H_MAX ->
2897
:-(
Increment - 1;
2898 increment_sm_counter(Incoming, Increment) ->
2899 84 Incoming + Increment.
2900
2901 stream_mgmt_enabled() ->
2902 27 stream_mgmt_enabled([]).
2903
2904 stream_mgmt_enabled(ExtraAttrs) ->
2905 60 #xmlel{name = <<"enabled">>,
2906 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3}] ++ ExtraAttrs}.
2907
2908 stream_mgmt_failed(Reason) ->
2909 5 stream_mgmt_failed(Reason, []).
2910
2911 stream_mgmt_failed(Reason, Attrs) ->
2912 6 ReasonEl = #xmlel{name = Reason,
2913 attrs = [{<<"xmlns">>, ?NS_STANZAS}]},
2914 6 #xmlel{name = <<"failed">>,
2915 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3} | Attrs],
2916 children = [ReasonEl]}.
2917
2918 stream_mgmt_ack(NIncoming) ->
2919 7 #xmlel{name = <<"a">>,
2920 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3},
2921 {<<"h">>, integer_to_binary(NIncoming)}]}.
2922
2923 -spec buffer_out_stanza(mongoose_acc:t(), packet(), state()) -> state().
2924 buffer_out_stanza(_Acc, _Packet, #state{stream_mgmt = StreamMgmt} = S)
2925 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
2926 13193 S;
2927 buffer_out_stanza(_Acc, _Packet, #state{stream_mgmt_buffer_max = no_buffer} = S) ->
2928
:-(
S;
2929 buffer_out_stanza(Acc, Packet, #state{server = Server,
2930 stream_mgmt_buffer = Buffer,
2931 stream_mgmt_buffer_size = BufferSize,
2932 stream_mgmt_buffer_max = BufferMax} = S) ->
2933 195 NewSize = BufferSize + 1,
2934 195 Timestamp = os:system_time(microsecond),
2935 195 {From, To, El} = maybe_add_timestamp(Packet, Timestamp, Server),
2936 195 Acc1 = update_stanza(From, To, El, Acc),
2937 195 NS = case is_buffer_full(NewSize, BufferMax) of
2938 true ->
2939 17 defer_resource_constraint_check(S);
2940 _ ->
2941 178 S
2942 end,
2943 195 Acc2 = notify_unacknowledged_msg_if_in_resume_state(Acc1, NS),
2944 195 NS#state{stream_mgmt_buffer_size = NewSize,
2945 stream_mgmt_buffer = [Acc2 | Buffer]}.
2946
2947 is_buffer_full(_BufferSize, infinity) ->
2948
:-(
false;
2949 is_buffer_full(BufferSize, BufferMax) when BufferSize =< BufferMax ->
2950 179 false;
2951 is_buffer_full(_, _) ->
2952 19 true.
2953
2954 %% @doc Drop last N elements from List.
2955 %% It's not an error if N > length(List).
2956 %% The actual number of dropped elements and an empty list is returned.
2957 %% @end
2958 -spec drop_last(N, List1) -> {Dropped, List2} when
2959 N :: non_neg_integer(),
2960 List1 :: list(),
2961 Dropped :: non_neg_integer(),
2962 List2 :: list().
2963 drop_last(N, List) ->
2964 44 {ToDrop, List2} = lists:foldr(fun(E, {0, Acc}) ->
2965 31 {0, [E | Acc]};
2966 (_, {ToDrop, Acc}) ->
2967 61 {ToDrop-1, Acc}
2968 end, {N, []}, List),
2969 44 {N - ToDrop, List2}.
2970
2971 maybe_send_ack_request(Acc, #state{stream_mgmt = StreamMgmt})
2972 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
2973 13181 Acc;
2974 maybe_send_ack_request(Acc, #state{stream_mgmt_ack_freq = never}) ->
2975 75 Acc;
2976 maybe_send_ack_request(Acc, #state{stream_mgmt_out_acked = Out,
2977 stream_mgmt_buffer_size = BufferSize,
2978 stream_mgmt_ack_freq = AckFreq} = State)
2979 when (Out + BufferSize) rem AckFreq == 0 ->
2980 70 send_element(Acc, stream_mgmt_request(), State);
2981 maybe_send_ack_request(Acc, _) ->
2982 1 Acc.
2983
2984 stream_mgmt_request() ->
2985 70 #xmlel{name = <<"r">>,
2986 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3}]}.
2987
2988 flush_stream_mgmt_buffer(#state{stream_mgmt = StreamMgmt})
2989 when StreamMgmt =:= false; StreamMgmt =:= disabled ->
2990 2968 false;
2991 flush_stream_mgmt_buffer(#state{stream_mgmt_buffer = Buffer} = State) ->
2992 57 re_route_packets(Buffer, State).
2993
2994 re_route_packets(Buffer, StateData) ->
2995 59 [reroute(Acc, StateData) || Acc <- lists:reverse(Buffer)],
2996 59 ok.
2997
2998 reroute(Acc, StateData) ->
2999 132 {From, To, _El} = mongoose_acc:packet(Acc),
3000 132 reroute(From, To, Acc, StateData).
3001
3002 reroute(From, To, Acc, #state{sid = SID}) ->
3003 202 Acc2 = patch_acc_for_reroute(Acc, SID),
3004 202 ejabberd_router:route(From, To, Acc2).
3005
3006 patch_acc_for_reroute(Acc, SID) ->
3007 202 case mongoose_acc:stanza_name(Acc) of
3008 <<"message">> ->
3009 101 Acc;
3010 _ -> %% IQs and presences are allowed to come to the same SID only
3011 101 case mongoose_acc:get(c2s, receiver_sid, undefined, Acc) of
3012 undefined ->
3013 101 mongoose_acc:set_permanent(c2s, receiver_sid, SID, Acc);
3014 _ ->
3015
:-(
Acc
3016 end
3017 end.
3018
3019 notify_unacknowledged_messages(#state{stream_mgmt_buffer = Buffer} = State) ->
3020 34 NewBuffer = [maybe_notify_unacknowledged_msg(Acc, State) || Acc <- lists:reverse(Buffer)],
3021 34 State#state{stream_mgmt_buffer = lists:reverse(NewBuffer)}.
3022
3023 notify_unacknowledged_msg_if_in_resume_state(Acc,
3024 #state{stream_mgmt_resume_tref = TRef,
3025 stream_mgmt = true} = State) when TRef =/= undefined ->
3026 18 maybe_notify_unacknowledged_msg(Acc, State);
3027 notify_unacknowledged_msg_if_in_resume_state(Acc, _) ->
3028 177 Acc.
3029
3030 maybe_notify_unacknowledged_msg(Acc, #state{jid = Jid, host_type = HostType}) ->
3031 99 case mongoose_acc:stanza_name(Acc) of
3032 61 <<"message">> -> notify_unacknowledged_msg(HostType, Acc, Jid);
3033 38 _ -> Acc
3034 end.
3035
3036 notify_unacknowledged_msg(HostType, Acc, Jid) ->
3037 61 NewAcc = mongoose_hooks:unacknowledged_message(HostType, Acc, Jid),
3038 61 mongoose_acc:strip(NewAcc).
3039
3040 finish_state(ok, StateName, StateData) ->
3041 13286 fsm_next_state(StateName, StateData);
3042 finish_state(resume, _, StateData) ->
3043 36 maybe_enter_resume_session(StateData).
3044
3045 maybe_enter_resume_session(StateData) ->
3046 36 maybe_enter_resume_session(StateData#state.stream_mgmt_id, StateData).
3047
3048 maybe_enter_resume_session(undefined, StateData) ->
3049 59 {stop, normal, StateData};
3050 maybe_enter_resume_session(_SMID, #state{} = SD) ->
3051 86 NSD = case SD#state.stream_mgmt_resume_tref of
3052 undefined ->
3053 34 Seconds = timer:seconds(SD#state.stream_mgmt_resume_timeout),
3054 34 TRef = erlang:send_after(Seconds, self(), resume_timeout),
3055 34 NewState = SD#state{stream_mgmt_resume_tref = TRef},
3056 34 notify_unacknowledged_messages(NewState);
3057 _TRef ->
3058 52 SD
3059 end,
3060 86 {next_state, resume_session, NSD, hibernate()}.
3061
3062 maybe_resume_session(NextState, El, StateData = #state{host_type = HostType}) ->
3063 15 case {xml:get_tag_attr_s(<<"xmlns">>, El),
3064 xml:get_tag_attr_s(<<"previd">>, El)} of
3065 {?NS_STREAM_MGNT_3, SMID} ->
3066 14 FromSMID = mod_stream_management:get_session_from_smid(HostType, SMID),
3067 14 do_resume_session(SMID, El, FromSMID, StateData);
3068 {InvalidNS, _} ->
3069 1 ?LOG_INFO(#{what => c2s_ignores_resume,
3070 text => <<"ignoring <resume/> element with invalid namespace">>,
3071 1 invalid_ns => InvalidNS, c2s_state => StateData}),
3072 1 fsm_next_state(NextState, StateData)
3073 end.
3074
3075 -spec do_resume_session(SMID, El, FromSMID, StateData) -> NewState when
3076 SMID :: mod_stream_management:smid(),
3077 El :: exml:element(),
3078 FromSMID :: {sid, ejabberd_sm:sid()} | {stale_h, non_neg_integer()} | {error, smid_not_found},
3079 StateData :: state(),
3080 NewState :: tuple().
3081 do_resume_session(SMID, El, {sid, {_, Pid}}, StateData) ->
3082 12 try
3083 12 {ok, OldState} = p1_fsm_old:sync_send_event(Pid, resume),
3084 11 SID = ejabberd_sm:make_new_sid(),
3085 11 Conn = get_conn_type(StateData),
3086 11 MergedState = merge_state(OldState,
3087 StateData#state{sid = SID, conn = Conn}),
3088 11 case stream_mgmt_handle_ack(session_established, El, MergedState) of
3089 {stop, _, _} = Stop ->
3090 1 Stop;
3091 {next_state, session_established, NSD, _} ->
3092 10 Priority = get_priority_from_presence(NSD#state.pres_last),
3093 10 Info = #{ip => NSD#state.ip, conn => NSD#state.conn,
3094 auth_module => NSD#state.auth_module },
3095 10 ejabberd_sm:open_session(NSD#state.host_type, SID, NSD#state.jid, Priority, Info),
3096 10 ok = mod_stream_management:register_smid(NSD#state.host_type, SMID, SID),
3097 10 try
3098 10 Resumed = stream_mgmt_resumed(NSD#state.stream_mgmt_id,
3099 NSD#state.stream_mgmt_in),
3100 10 send_element_from_server_jid(NSD, Resumed),
3101 10 [begin
3102 9 Elem = mongoose_acc:element(Acc),
3103 9 send_element(Acc, Elem, NSD)
3104 10 end || Acc <- lists:reverse(NSD#state.stream_mgmt_buffer)],
3105
3106 10 NSD2 = flush_csi_buffer(NSD),
3107
3108 10 NSD3 = NSD2#state{ stream_mgmt_resumed_from = OldState#state.sid },
3109
3110 10 fsm_next_state(session_established, NSD3)
3111 catch
3112 %% errors from send_element
3113 _:_ ->
3114
:-(
?LOG_INFO(#{what => resumption_error,
3115 text => <<"resumption error while resending old stanzas"
3116 " entering resume state again">>,
3117
:-(
smid => SMID, c2s_state => NSD}),
3118
:-(
maybe_enter_resume_session(SMID, NSD)
3119 end
3120 end
3121 catch
3122 _Class:Reason:Stacktrace ->
3123 1 ?LOG_WARNING(#{what => resumption_error, reason => invalid_response,
3124 text => <<"Resumption error because of invalid response">>,
3125 error => Reason, stacktrace => Stacktrace,
3126
:-(
c2s_state => StateData}),
3127 1 send_element_from_server_jid(StateData, stream_mgmt_failed(<<"item-not-found">>)),
3128 1 fsm_next_state(wait_for_feature_after_auth, StateData)
3129 end;
3130
3131 do_resume_session(SMID, _El, {stale_h, H}, StateData) when is_integer(H) ->
3132 1 ?LOG_INFO(#{what => resumption_error, reason => session_resumption_timed_out,
3133 1 smid => SMID, stale_h => H, c2s_state => StateData}),
3134 1 send_element_from_server_jid(
3135 StateData, stream_mgmt_failed(<<"item-not-found">>, [{<<"h">>, integer_to_binary(H)}])),
3136 1 fsm_next_state(wait_for_feature_after_auth, StateData);
3137 do_resume_session(SMID, _El, {error, smid_not_found}, StateData) ->
3138 1 ?LOG_INFO(#{what => resumption_error, reason => no_previous_session_for_smid,
3139 1 smid => SMID, c2s_state => StateData}),
3140 1 send_element_from_server_jid(StateData, stream_mgmt_failed(<<"item-not-found">>)),
3141 1 fsm_next_state(wait_for_feature_after_auth, StateData).
3142
3143 merge_state(OldSD, SD) ->
3144 11 Preserve = [#state.jid,
3145 #state.user,
3146 #state.server,
3147 #state.resource,
3148 #state.pres_t,
3149 #state.pres_f,
3150 #state.pres_a,
3151 #state.pres_i,
3152 #state.pres_last,
3153 #state.pres_pri,
3154 #state.pres_timestamp,
3155 #state.pres_invis,
3156 #state.privacy_list,
3157 #state.aux_fields,
3158 #state.csi_buffer,
3159 #state.stream_mgmt,
3160 #state.stream_mgmt_in,
3161 #state.stream_mgmt_id,
3162 #state.stream_mgmt_out_acked,
3163 #state.stream_mgmt_buffer,
3164 #state.stream_mgmt_buffer_size,
3165 #state.stream_mgmt_buffer_max,
3166 #state.stream_mgmt_resume_timeout,
3167 #state.stream_mgmt_ack_freq],
3168 11 Copy = fun(Index, {Stale, Acc}) ->
3169 264 {Stale, setelement(Index, Acc, element(Index, Stale))}
3170 end,
3171 11 element(2, lists:foldl(Copy, {OldSD, SD}, Preserve)).
3172
3173 stream_mgmt_resumed(SMID, Handled) ->
3174 10 #xmlel{name = <<"resumed">>,
3175 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3},
3176 {<<"previd">>, SMID},
3177 {<<"h">>, integer_to_binary(Handled)}]}.
3178
3179 handover_session(SD, From)->
3180 11 true = SD#state.stream_mgmt,
3181 11 Acc = new_acc(SD, #{location => ?LOCATION, element => undefined}),
3182 11 ejabberd_sm:close_session(Acc,
3183 SD#state.sid,
3184 SD#state.jid,
3185 resumed),
3186 %the actual handover to be done on termination
3187 11 {stop, {handover_session, From}, SD}.
3188
3189 do_handover_session(SD, UnreadMessages) ->
3190 11 Messages = flush_messages(UnreadMessages),
3191 11 NewCsiBuffer = Messages ++ SD#state.csi_buffer,
3192 11 SD#state{authenticated = resumed,
3193 csi_buffer = NewCsiBuffer}.
3194
3195 maybe_add_timestamp({F, T, #xmlel{name= <<"message">>}=Packet}=PacketTuple, Timestamp, Server) ->
3196 104 Type = xml:get_tag_attr_s(<<"type">>, Packet),
3197 104 case Type of
3198 <<"error">> ->
3199
:-(
PacketTuple;
3200 <<"headline">> ->
3201 2 PacketTuple;
3202 _ ->
3203 102 {F, T, add_timestamp(Timestamp, Server, Packet)}
3204 end;
3205 maybe_add_timestamp(Packet, _Timestamp, _Server) ->
3206 91 Packet.
3207
3208 add_timestamp(TimeStamp, Server, Packet) ->
3209 102 case xml:get_subtag(Packet, <<"delay">>) of
3210 false ->
3211 83 TimeStampXML = timestamp_xml(Server, TimeStamp),
3212 83 xml:append_subtags(Packet, [TimeStampXML]);
3213 _ ->
3214 19 Packet
3215 end.
3216
3217 timestamp_xml(Server, Time) ->
3218 83 FromJID = jid:make_noprep(<<>>, Server, <<>>),
3219 83 TS = calendar:system_time_to_rfc3339(Time, [{offset, "Z"}, {unit, microsecond}]),
3220 83 jlib:timestamp_to_xml(TS, FromJID, <<"SM Storage">>).
3221
3222 defer_resource_constraint_check(#state{stream_mgmt_constraint_check_tref = undefined} = State)->
3223 14 Seconds = timer:seconds(?CONSTRAINT_CHECK_TIMEOUT),
3224 14 TRef = erlang:send_after(Seconds, self(), check_buffer_full),
3225 14 State#state{stream_mgmt_constraint_check_tref = TRef};
3226 defer_resource_constraint_check(State)->
3227 3 State.
3228
3229 -spec sm_handled_count_too_high_stanza(non_neg_integer(), non_neg_integer()) -> exml:element().
3230 sm_handled_count_too_high_stanza(Handled, OldAcked) ->
3231 4 #xmlel{name = <<"handled-count-too-high">>,
3232 attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3},
3233 {<<"h">>, integer_to_binary(Handled)},
3234 {<<"send-count">>, integer_to_binary(OldAcked)}]}.
3235
3236 -spec sasl_success_stanza(any()) -> exml:element().
3237 sasl_success_stanza(ServerOut) ->
3238 3054 C = case ServerOut of
3239 3044 undefined -> [];
3240 10 _ -> [#xmlcdata{content = jlib:encode_base64(ServerOut)}]
3241 end,
3242 3054 #xmlel{name = <<"success">>,
3243 attrs = [{<<"xmlns">>, ?NS_SASL}],
3244 children = C}.
3245
3246 -spec sasl_failure_stanza(binary() | {binary(), iodata() | undefined}) -> exml:element().
3247 sasl_failure_stanza(Error) when is_binary(Error) ->
3248 48 sasl_failure_stanza({Error, undefined});
3249 sasl_failure_stanza({Error, Text}) ->
3250 49 #xmlel{name = <<"failure">>,
3251 attrs = [{<<"xmlns">>, ?NS_SASL}],
3252 children = [#xmlel{name = Error} | maybe_text_tag(Text)]}.
3253
3254 48 maybe_text_tag(undefined) -> [];
3255 maybe_text_tag(Text) ->
3256 1 [#xmlel{name = <<"text">>,
3257 children = [#xmlcdata{content = Text}]}].
3258
3259 -spec sasl_challenge_stanza(any()) -> exml:element().
3260 sasl_challenge_stanza(Challenge) ->
3261 13 #xmlel{name = <<"challenge">>,
3262 attrs = [{<<"xmlns">>, ?NS_SASL}],
3263 children = Challenge}.
3264
3265 handle_sasl_success(State, Creds) ->
3266 3054 ServerOut = mongoose_credentials:get(Creds, sasl_success_response, undefined),
3267 3054 send_element_from_server_jid(State, sasl_success_stanza(ServerOut)),
3268 3052 User = mongoose_credentials:get(Creds, username),
3269 3052 AuthModule = mongoose_credentials:get(Creds, auth_module),
3270 3052 StreamID = new_id(),
3271 3052 Server = State#state.server,
3272 3052 NewState = State#state{ streamid = StreamID,
3273 authenticated = true,
3274 auth_module = AuthModule,
3275 user = User,
3276 jid = jid:make(User, Server, <<>>)},
3277 3052 ?LOG_INFO(#{what => auth_success, text => <<"Accepted SASL authentication">>,
3278 stream_id => StreamID, auth_module => AuthModule,
3279 3052 c2s_state => NewState}),
3280 3052 {wait_for_stream, NewState}.
3281
3282 handle_sasl_step(#state{host_type = HostType, server = Server, socket = Sock} = State,
3283 StepRes) ->
3284 3116 case StepRes of
3285 {ok, Creds} ->
3286 3054 handle_sasl_success(State, Creds);
3287 {continue, ServerOut, NewSASLState} ->
3288 13 Challenge = [#xmlcdata{content = jlib:encode_base64(ServerOut)}],
3289 13 send_element_from_server_jid(State, sasl_challenge_stanza(Challenge)),
3290 13 {wait_for_sasl_response, State#state{sasl_state = NewSASLState}};
3291 {error, Error, Username} ->
3292 17 IP = peerip(State#state.sockmod, Sock),
3293 17 ?LOG_INFO(#{what => auth_failed,
3294 text => <<"Failed SASL authentication">>,
3295 user => Username, server => Server,
3296 17 ip => IP, c2s_state => State}),
3297 17 mongoose_hooks:auth_failed(HostType, Server, Username),
3298 17 send_element_from_server_jid(State, sasl_failure_stanza(Error)),
3299 17 {wait_for_feature_before_auth, State};
3300 {error, Error} ->
3301 32 mongoose_hooks:auth_failed(HostType, Server, unknown),
3302 32 send_element_from_server_jid(State, sasl_failure_stanza(Error)),
3303 32 {wait_for_feature_before_auth, State}
3304 end.
3305
3306 user_allowed(JID, #state{host_type = HostType, server = Server, access = Access}) ->
3307 3027 case acl:match_rule(HostType, Server, Access, JID) of
3308 allow ->
3309 3026 open_session_allowed_hook(HostType, JID);
3310 deny ->
3311 1 false
3312 end.
3313
3314 open_session_allowed_hook(HostType, JID) ->
3315 3026 allow == mongoose_hooks:session_opening_allowed_for_user(HostType, JID).
3316
3317 terminate_when_tls_required_but_not_enabled(true, false, StateData, _El) ->
3318 4 Lang = StateData#state.lang,
3319 4 c2s_stream_error(mongoose_xmpp_errors:policy_violation(Lang, <<"Use of STARTTLS required">>),
3320 StateData);
3321 terminate_when_tls_required_but_not_enabled(_, _, StateData, El) ->
3322 384 process_unauthenticated_stanza(StateData, El),
3323 384 fsm_next_state(wait_for_feature_before_auth, StateData).
3324
3325 %% @doc This function is executed when c2s receives a stanza from TCP connection.
3326 -spec element_to_origin_accum(jlib:xmlel(), StateData :: state()) ->
3327 mongoose_acc:t().
3328 element_to_origin_accum(El, StateData = #state{sid = SID, jid = JID}) ->
3329 13155 BaseParams = #{
3330 location => ?LOCATION,
3331 element => El,
3332 from_jid => JID
3333 },
3334 13155 Params =
3335 case exml_query:attr(El, <<"to">>) of
3336 9380 undefined -> BaseParams#{ to_jid => jid:to_bare(JID) };
3337 3775 _ToBin -> BaseParams
3338 end,
3339 13155 Acc = new_acc(StateData, Params),
3340 13155 Acc1 = mongoose_acc:set_permanent(c2s, origin_sid, SID, Acc),
3341 13155 mongoose_acc:set_permanent(c2s, origin_jid, JID, Acc1).
3342
3343 -spec hibernate() -> hibernate | infinity.
3344 hibernate() ->
3345 24905 {_, QueueLen} = process_info(self(), message_queue_len),
3346 24905 InternalQueueLen = get('$internal_queue_len'),
3347 24905 case QueueLen + InternalQueueLen of
3348 11566 0 -> hibernate;
3349 13339 _ -> infinity
3350 end.
3351
3352 -spec maybe_hibernate(state()) -> hibernate | infinity | pos_integer().
3353 24812 maybe_hibernate(#state{hibernate_after = 0}) -> hibernate();
3354
:-(
maybe_hibernate(#state{hibernate_after = HA}) -> HA.
3355
3356 make_c2s_info(_StateData = #state{stream_mgmt_buffer_size = SMBufSize}) ->
3357 11 #{stream_mgmt_buffer_size => SMBufSize}.
3358
3359 -spec update_stanza(jid:jid(), jid:jid(), exml:element(), mongoose_acc:t()) ->
3360 mongoose_acc:t().
3361 update_stanza(From, To, #xmlel{} = Element, Acc) ->
3362 223 HostType = mongoose_acc:host_type(Acc),
3363 223 LServer = mongoose_acc:lserver(Acc),
3364 223 Params = #{host_type => HostType, lserver => LServer,
3365 element => Element, from_jid => From, to_jid => To},
3366 223 NewAcc = mongoose_acc:strip(Params, Acc),
3367 223 strip_c2s_fields(NewAcc).
3368
3369 -spec strip_c2s_fields(mongoose_acc:t()) -> mongoose_acc:t().
3370 strip_c2s_fields(Acc) ->
3371 %% TODO: verify if we really need to strip down these 2 fields
3372 359 mongoose_acc:delete_many(c2s, [origin_jid, origin_sid], Acc).
3373
3374 -spec new_acc(state(), mongoose_acc:new_acc_params()) -> mongoose_acc:t().
3375 new_acc(#state{host_type = HostType, server = LServer}, Params) ->
3376 30775 mongoose_acc:new(Params#{host_type => HostType, lserver => LServer}).
Line Hits Source