./ct_report/coverage/ejabberd_c2s.COVER.html

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