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