1 |
|
-module(mod_presence). |
2 |
|
|
3 |
|
-include_lib("exml/include/exml.hrl"). |
4 |
|
-include("jlib.hrl"). |
5 |
|
-include("mongoose_logger.hrl"). |
6 |
|
|
7 |
|
-behavior(gen_mod). |
8 |
|
|
9 |
|
-type jid_set() :: gb_sets:set(jid:jid()). |
10 |
|
-type priority() :: -128..128. |
11 |
|
-type maybe_priority() :: priority() | undefined. |
12 |
|
-record(presences_state, { |
13 |
|
%% We have _subscription to_ these users' presence status; |
14 |
|
%% i.e. they send us presence updates. |
15 |
|
%% This comes from the roster. |
16 |
|
pres_t = gb_sets:new() :: jid_set(), |
17 |
|
%% We have _subscription from_ these users, |
18 |
|
%% i.e. they have subscription to us. |
19 |
|
%% We send them presence updates. |
20 |
|
%% This comes from the roster, and also to ourself |
21 |
|
pres_f = gb_sets:new() :: jid_set(), |
22 |
|
%% We're _available_ to these users, |
23 |
|
%% i.e. we broadcast presence updates to them. |
24 |
|
%% This may change throughout the session. |
25 |
|
pres_a = gb_sets:new() :: jid_set(), |
26 |
|
%% We are _invisible_ to these users. |
27 |
|
%% This may change throughout the session. |
28 |
|
pres_i = gb_sets:new() :: jid_set(), |
29 |
|
pres_pri = 0 :: priority(), |
30 |
|
pres_last :: undefined | exml:element(), |
31 |
|
pres_timestamp :: undefined | integer(), % unix time in microseconds |
32 |
|
pres_invis = false :: boolean() %% Are we invisible? |
33 |
|
}). |
34 |
|
-type presences_state() :: #presences_state{}. |
35 |
|
|
36 |
|
-export([start/2, stop/1, supported_features/0]). |
37 |
|
-export([ |
38 |
|
user_send_presence/3, |
39 |
|
user_receive_presence/3, |
40 |
|
user_terminate/3, |
41 |
|
foreign_event/3 |
42 |
|
]). |
43 |
|
-export([ |
44 |
|
get/2, |
45 |
|
is_subscribed_to_my_presence/3, |
46 |
|
am_i_subscribed_to_presence/3, |
47 |
|
presence_unavailable_stanza/0, |
48 |
|
get_presence/1, |
49 |
|
get_subscribed/1, |
50 |
|
set_presence/2, |
51 |
|
maybe_get_handler/1, |
52 |
|
get_old_priority/1 |
53 |
|
]). |
54 |
|
|
55 |
|
-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok. |
56 |
|
start(HostType, _Opts) -> |
57 |
559 |
gen_hook:add_handlers(c2s_hooks(HostType)). |
58 |
|
|
59 |
|
-spec stop(mongooseim:host_type()) -> ok. |
60 |
|
stop(HostType) -> |
61 |
559 |
gen_hook:delete_handlers(c2s_hooks(HostType)). |
62 |
|
|
63 |
|
-spec supported_features() -> [atom()]. |
64 |
|
supported_features() -> |
65 |
351 |
[dynamic_domains]. |
66 |
|
|
67 |
|
-spec get_presence(pid()) -> {jid:luser(), jid:lresource(), binary(), binary()}. |
68 |
|
get_presence(Pid) -> |
69 |
30 |
mongoose_c2s:call(Pid, ?MODULE, get_presence). |
70 |
|
|
71 |
|
-spec get_subscribed(pid()) -> [jid:jid()]. |
72 |
|
get_subscribed(Pid) -> |
73 |
9 |
mongoose_c2s:call(Pid, ?MODULE, get_subscribed). |
74 |
|
|
75 |
|
-spec set_presence(pid(), exml:element()) -> ok. |
76 |
|
set_presence(Pid, Message) -> |
77 |
9 |
mongoose_c2s:cast(Pid, ?MODULE, {set_presence, Message}). |
78 |
|
|
79 |
|
-spec c2s_hooks(mongooseim:host_type()) -> gen_hook:hook_list(mongoose_c2s_hooks:fn()). |
80 |
|
c2s_hooks(HostType) -> |
81 |
1118 |
[ |
82 |
|
{user_send_presence, HostType, fun ?MODULE:user_send_presence/3, #{}, 50}, |
83 |
|
{user_receive_presence, HostType, fun ?MODULE:user_receive_presence/3, #{}, 50}, |
84 |
|
{user_terminate, HostType, fun ?MODULE:user_terminate/3, #{}, 90}, |
85 |
|
{foreign_event, HostType, fun ?MODULE:foreign_event/3, #{}, 50} |
86 |
|
]. |
87 |
|
|
88 |
|
-spec user_send_presence(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) -> |
89 |
|
mongoose_c2s_hooks:result(). |
90 |
|
user_send_presence(Acc, #{c2s_data := StateData}, _Extra) -> |
91 |
6690 |
{FromJid, ToJid, Packet} = mongoose_acc:packet(Acc), |
92 |
6690 |
StanzaType = mongoose_acc:stanza_type(Acc), |
93 |
6690 |
case jid:are_bare_equal(FromJid, ToJid) of |
94 |
|
true -> |
95 |
5753 |
Acc1 = presence_update(Acc, FromJid, ToJid, Packet, StateData, StanzaType), |
96 |
5753 |
{ok, Acc1}; |
97 |
|
_ -> |
98 |
937 |
Acc1 = presence_track(Acc, FromJid, ToJid, Packet, StateData, StanzaType), |
99 |
937 |
{ok, Acc1} |
100 |
|
end. |
101 |
|
|
102 |
|
-spec user_receive_presence(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) -> |
103 |
|
mongoose_c2s_hooks:result(). |
104 |
|
user_receive_presence(Acc, #{c2s_data := StateData}, _Extra) -> |
105 |
14512 |
case get_mod_state(StateData) of |
106 |
|
{error, not_found} -> |
107 |
4 |
{stop, Acc}; |
108 |
|
Presences -> |
109 |
14508 |
handle_user_received_presence(Acc, Presences, mongoose_acc:stanza_type(Acc)) |
110 |
|
end. |
111 |
|
|
112 |
|
-spec user_terminate(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) -> |
113 |
|
gen_hook:hook_fn_ret(mongoose_acc:t()). |
114 |
|
user_terminate(Acc, #{c2s_data := StateData, reason := Reason}, _Extra) -> |
115 |
6622 |
case get_mod_state(StateData) of |
116 |
1158 |
{error, not_found} -> {ok, Acc}; |
117 |
212 |
#presences_state{pres_last = undefined} -> {ok, Acc}; |
118 |
5252 |
Presences -> handle_user_terminate(Acc, StateData, Presences, Reason) |
119 |
|
end. |
120 |
|
|
121 |
|
-spec handle_user_terminate(mongoose_acc:t(), mongoose_c2s:data(), presences_state(), term()) -> |
122 |
|
gen_hook:hook_fn_ret(mongoose_acc:t()). |
123 |
|
handle_user_terminate(Acc, StateData, Presences, Reason) -> |
124 |
5252 |
Jid = mongoose_c2s:get_jid(StateData), |
125 |
5252 |
Status = close_session_status(Reason), |
126 |
5252 |
PresenceUnavailable = presence_unavailable_stanza(Status), |
127 |
5252 |
ParamsAcc = #{from_jid => Jid, to_jid => jid:to_bare(Jid), element => PresenceUnavailable}, |
128 |
5252 |
Acc1 = mongoose_acc:update_stanza(ParamsAcc, Acc), |
129 |
5252 |
presence_broadcast(Acc1, Presences#presences_state.pres_a), |
130 |
5252 |
presence_broadcast(Acc1, Presences#presences_state.pres_i), |
131 |
5252 |
mongoose_hooks:unset_presence_hook(Acc1, Jid, Status), |
132 |
5252 |
{ok, Acc}. |
133 |
|
|
134 |
|
-spec foreign_event(Acc, Params, Extra) -> Result when |
135 |
|
Acc :: mongoose_acc:t(), |
136 |
|
Params :: mongoose_c2s_hooks:params(), |
137 |
|
Extra :: gen_hook:extra(), |
138 |
|
Result :: mongoose_c2s_hooks:result(). |
139 |
|
foreign_event(Acc, #{c2s_data := StateData, |
140 |
|
event_type := cast, |
141 |
|
event_tag := mod_roster, |
142 |
|
event_content := {item, IJID, ISubscription}}, _Extra) -> |
143 |
845 |
case get_mod_state(StateData) of |
144 |
|
{error, not_found} -> |
145 |
:-( |
{ok, Acc}; |
146 |
|
Presences -> |
147 |
845 |
{stop, handle_subscription_change(Acc, StateData, IJID, ISubscription, Presences)} |
148 |
|
end; |
149 |
|
foreign_event(Acc, #{c2s_data := StateData, |
150 |
|
event_type := {call, From}, |
151 |
|
event_tag := ?MODULE, |
152 |
|
event_content := get_presence}, _Extra) -> |
153 |
30 |
PresLast = case get_mod_state(StateData) of |
154 |
|
{error, not_found} -> |
155 |
:-( |
undefined; |
156 |
|
#presences_state{pres_last = Value} -> |
157 |
30 |
Value |
158 |
|
end, |
159 |
30 |
#jid{luser = User, lresource = Resource} = mongoose_c2s:get_jid(StateData), |
160 |
30 |
Reply = {User, Resource, get_showtag(PresLast), get_statustag(PresLast)}, |
161 |
30 |
Acc1 = mongoose_c2s_acc:to_acc(Acc, actions, [{reply, From, Reply}]), |
162 |
30 |
{stop, Acc1}; |
163 |
|
foreign_event(Acc, #{c2s_data := StateData, |
164 |
|
event_type := {call, From}, |
165 |
|
event_tag := ?MODULE, |
166 |
|
event_content := get_subscribed}, _Extra) -> |
167 |
8 |
Subscribed = case get_mod_state(StateData) of |
168 |
|
{error, not_found} -> |
169 |
:-( |
[]; |
170 |
|
#presences_state{pres_f = PresF} -> |
171 |
8 |
gb_sets:to_list(PresF) |
172 |
|
end, |
173 |
8 |
Acc1 = mongoose_c2s_acc:to_acc(Acc, actions, [{reply, From, Subscribed}]), |
174 |
8 |
{stop, Acc1}; |
175 |
|
foreign_event(Acc, #{c2s_data := StateData, |
176 |
|
event_type := cast, |
177 |
|
event_tag := ?MODULE, |
178 |
|
event_content := {set_presence, Message}}, _Extra) -> |
179 |
9 |
Acc1 = mongoose_acc:update_stanza(#{element => Message}, Acc), |
180 |
9 |
{FromJid, ToJid, Packet} = mongoose_acc:packet(Acc1), |
181 |
9 |
StanzaType = mongoose_acc:stanza_type(Acc1), |
182 |
9 |
{stop, presence_update(Acc1, FromJid, ToJid, Packet, StateData, StanzaType)}; |
183 |
|
foreign_event(Acc, _Params, _Extra) -> |
184 |
136 |
{ok, Acc}. |
185 |
|
|
186 |
|
-spec get_showtag(undefined | exml:element()) -> binary(). |
187 |
|
get_showtag(undefined) -> |
188 |
:-( |
<<"unavailable">>; |
189 |
|
get_showtag(Presence) -> |
190 |
30 |
case xml:get_path_s(Presence, [{elem, <<"show">>}, cdata]) of |
191 |
:-( |
<<>> -> <<"available">>; |
192 |
30 |
ShowTag -> ShowTag |
193 |
|
end. |
194 |
|
|
195 |
|
-spec get_statustag(undefined | exml:element()) -> binary(). |
196 |
|
|
197 |
|
get_statustag(undefined) -> |
198 |
:-( |
<<>>; |
199 |
|
get_statustag(Presence) -> |
200 |
30 |
xml:get_path_s(Presence, [{elem, <<"status">>}, cdata]). |
201 |
|
|
202 |
|
-spec handle_subscription_change( |
203 |
|
mongoose_acc:t(), mongoose_c2s:data(), term(), term(), presences_state()) -> |
204 |
|
mongoose_acc:t(). |
205 |
|
handle_subscription_change(Acc, StateData, IJID, ISubscription, Presences) -> |
206 |
845 |
To = jid:make(IJID), |
207 |
845 |
IsSubscribedToMe = (ISubscription == both) or (ISubscription == from), |
208 |
845 |
AmISubscribedTo = (ISubscription == both) or (ISubscription == to), |
209 |
845 |
WasSubscribedToMe = gb_sets:is_element(To, Presences#presences_state.pres_f), |
210 |
845 |
FSet = case IsSubscribedToMe of |
211 |
|
true -> |
212 |
289 |
gb_sets:add_element(To, Presences#presences_state.pres_f); |
213 |
|
false -> |
214 |
556 |
gb_sets:del_element(To, Presences#presences_state.pres_f) |
215 |
|
end, |
216 |
845 |
TSet = case AmISubscribedTo of |
217 |
|
true -> |
218 |
295 |
gb_sets:add_element(To, Presences#presences_state.pres_t); |
219 |
|
false -> |
220 |
550 |
gb_sets:del_element(To, Presences#presences_state.pres_t) |
221 |
|
end, |
222 |
845 |
case Presences#presences_state.pres_last of |
223 |
|
undefined -> |
224 |
:-( |
NewPresences = Presences#presences_state{pres_f = FSet, pres_t = TSet}, |
225 |
:-( |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}); |
226 |
|
P -> |
227 |
845 |
?LOG_DEBUG(#{what => roster_changed, roster_jid => To, |
228 |
845 |
acc => Acc, c2s_state => StateData}), |
229 |
845 |
From = mongoose_c2s:get_jid(StateData), |
230 |
845 |
IsntInvisible = not Presences#presences_state.pres_invis, |
231 |
845 |
ImAvailableTo = gb_sets:is_element(To, Presences#presences_state.pres_a), |
232 |
845 |
ImInvisibleTo = gb_sets:is_element(To, Presences#presences_state.pres_i), |
233 |
845 |
BecomeAvailable = IsntInvisible and IsSubscribedToMe and not WasSubscribedToMe, |
234 |
845 |
BecomeUnavailable = not IsSubscribedToMe and WasSubscribedToMe |
235 |
|
and (ImAvailableTo or ImInvisibleTo), |
236 |
845 |
case {BecomeAvailable, BecomeUnavailable} of |
237 |
|
{true, _} -> |
238 |
182 |
?LOG_DEBUG(#{what => become_available_to, roster_jid => To, |
239 |
182 |
acc => Acc, c2s_state => StateData}), |
240 |
182 |
ejabberd_router:route(From, To, Acc, P), |
241 |
182 |
A = gb_sets:add_element(To, Presences#presences_state.pres_a), |
242 |
182 |
NewPresences = Presences#presences_state{pres_a = A, pres_f = FSet, pres_t = TSet}, |
243 |
182 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}); |
244 |
|
{_, true} -> |
245 |
19 |
?LOG_DEBUG(#{what => become_unavailable_to, roster_jid => To, |
246 |
19 |
acc => Acc, c2s_state => StateData}), |
247 |
19 |
PU = presence_unavailable_stanza(), |
248 |
19 |
ejabberd_router:route(From, To, Acc, PU), |
249 |
19 |
I = gb_sets:del_element(To, Presences#presences_state.pres_i), |
250 |
19 |
A = gb_sets:del_element(To, Presences#presences_state.pres_a), |
251 |
19 |
NewPresences = Presences#presences_state{pres_i = I, pres_a = A, pres_f = FSet, pres_t = TSet}, |
252 |
19 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}); |
253 |
|
_ -> |
254 |
644 |
NewPresences = Presences#presences_state{pres_f = FSet, pres_t = TSet}, |
255 |
644 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}) |
256 |
|
end |
257 |
|
end. |
258 |
|
|
259 |
|
-spec handle_user_received_presence(mongoose_acc:t(), presences_state(), binary()) -> |
260 |
|
mongoose_c2s_hooks:result(). |
261 |
|
handle_user_received_presence(Acc, Presences, <<"probe">>) -> |
262 |
5684 |
{stop, handle_received_probe(Acc, Presences)}; |
263 |
|
handle_user_received_presence(Acc, Presences, <<"error">>) -> |
264 |
56 |
{ok, handle_received_error(Acc, Presences)}; |
265 |
|
handle_user_received_presence(Acc, _, <<"invisible">>) -> |
266 |
2 |
{ok, handle_received_invisible(Acc)}; |
267 |
|
handle_user_received_presence(Acc, _, <<"subscribe">>) -> |
268 |
192 |
{ok, Acc}; |
269 |
|
handle_user_received_presence(Acc, _, <<"subscribed">>) -> |
270 |
182 |
{ok, Acc}; |
271 |
|
handle_user_received_presence(Acc, _, <<"unsubscribe">>) -> |
272 |
12 |
{ok, Acc}; |
273 |
|
handle_user_received_presence(Acc, _, <<"unsubscribed">>) -> |
274 |
14 |
{ok, Acc}; |
275 |
|
handle_user_received_presence(Acc, Presences, _) -> |
276 |
8366 |
{ok, handle_received_available(Acc, Presences)}. |
277 |
|
|
278 |
|
-spec handle_received_probe(mongoose_acc:t(), presences_state()) -> mongoose_acc:t(). |
279 |
|
handle_received_probe(Acc, Presences) -> |
280 |
5684 |
{FromJid, ToJid, _Packet} = mongoose_acc:packet(Acc), |
281 |
5684 |
BareFromJid = jid:to_bare(FromJid), |
282 |
5684 |
NewPresences = case am_i_available_to(FromJid, BareFromJid, Presences) of |
283 |
5683 |
true -> Presences; |
284 |
1 |
false -> make_available_to(FromJid, BareFromJid, Presences) |
285 |
|
end, |
286 |
5684 |
Acc1 = mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}), |
287 |
5684 |
process_presence_probe(Acc1, NewPresences, FromJid, BareFromJid, ToJid). |
288 |
|
|
289 |
|
-spec process_presence_probe(mongoose_acc:t(), presences_state(), jid:jid(), jid:jid(), jid:jid()) -> mongoose_acc:t(). |
290 |
|
process_presence_probe(Acc, #presences_state{pres_last = undefined}, _, _, _) -> |
291 |
1 |
Acc; |
292 |
|
process_presence_probe(Acc, Presences, FromJid, BareFromJid, ToJid) -> |
293 |
5683 |
case {should_retransmit_last_presence(FromJid, BareFromJid, Presences), |
294 |
|
specifically_visible_to(FromJid, Presences)} of |
295 |
|
{true, _} -> |
296 |
5683 |
route_probe(Acc, Presences, FromJid, ToJid); |
297 |
|
{false, true} -> |
298 |
:-( |
ejabberd_router:route(ToJid, FromJid, Acc, #xmlel{name = <<"presence">>}), |
299 |
:-( |
Acc; |
300 |
|
_ -> |
301 |
:-( |
Acc |
302 |
|
end. |
303 |
|
|
304 |
|
-spec route_probe(mongoose_acc:t(), presences_state(), jid:jid(), jid:jid()) -> mongoose_acc:t(). |
305 |
|
route_probe(Acc, Presences, FromJid, ToJid) -> |
306 |
5683 |
Packet0 = Presences#presences_state.pres_last, |
307 |
5683 |
TS = Presences#presences_state.pres_timestamp, |
308 |
|
%% To is the one sending the presence (the target of the probe) |
309 |
5683 |
Packet1 = jlib:maybe_append_delay(Packet0, ToJid, TS, <<>>), |
310 |
5683 |
HostType = mongoose_acc:host_type(Acc), |
311 |
5683 |
Acc2 = mongoose_hooks:presence_probe_hook(HostType, Acc, FromJid, ToJid, self()), |
312 |
|
%% Don't route a presence probe to oneself |
313 |
5683 |
case jid:are_equal(FromJid, ToJid) of |
314 |
|
false -> |
315 |
182 |
ejabberd_router:route(ToJid, FromJid, Acc2, Packet1), |
316 |
182 |
Acc; |
317 |
|
true -> |
318 |
5501 |
Acc2 |
319 |
|
end. |
320 |
|
|
321 |
|
-spec handle_received_error(mongoose_acc:t(), presences_state()) -> mongoose_acc:t(). |
322 |
|
handle_received_error(Acc, Presences) -> |
323 |
56 |
FromJid = mongoose_acc:from_jid(Acc), |
324 |
56 |
NewA = gb_sets:del_element(FromJid, Presences#presences_state.pres_a), |
325 |
56 |
NewPresences = Presences#presences_state{pres_a = NewA}, |
326 |
56 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}). |
327 |
|
|
328 |
|
-spec handle_received_invisible(mongoose_acc:t()) -> mongoose_acc:t(). |
329 |
|
handle_received_invisible(Acc) -> |
330 |
2 |
{FromJid, ToJid, Packet} = mongoose_acc:packet(Acc), |
331 |
2 |
#xmlel{attrs = Attrs} = Packet, |
332 |
2 |
Attrs1 = lists:keystore(<<"type">>, 1, Attrs, {<<"type">>, <<"unavailable">>}), |
333 |
2 |
NewElement = Packet#xmlel{attrs = Attrs1}, |
334 |
2 |
mongoose_acc:update_stanza( |
335 |
|
#{element => NewElement, from_jid => FromJid, to_jid => ToJid}, Acc). |
336 |
|
|
337 |
|
-spec handle_received_available(mongoose_acc:t(), presences_state()) -> mongoose_acc:t(). |
338 |
|
handle_received_available(Acc, Presences) -> |
339 |
8366 |
FromJid = mongoose_acc:from_jid(Acc), |
340 |
8366 |
BareJid = jid:to_bare(FromJid), |
341 |
8366 |
case am_i_available_to(FromJid, BareJid, Presences) of |
342 |
|
true -> |
343 |
7036 |
Acc; |
344 |
|
false -> |
345 |
1330 |
NewPresences = make_available_to(FromJid, BareJid, Presences), |
346 |
1330 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}) |
347 |
|
end. |
348 |
|
|
349 |
|
%% @doc User updates his presence (non-directed presence packet) |
350 |
|
-spec presence_update( |
351 |
|
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), mongoose_c2s:data(), undefined | binary()) -> |
352 |
|
mongoose_acc:t(). |
353 |
|
presence_update(Acc, FromJid, ToJid, Packet, StateData, Available) |
354 |
|
when Available =:= undefined; Available =:= <<"available">> -> |
355 |
5539 |
Presences = maybe_get_handler(StateData), |
356 |
5539 |
presence_update_to_available(Acc, FromJid, ToJid, Packet, StateData, Presences); |
357 |
|
presence_update(Acc, FromJid, ToJid, Packet, StateData, <<"unavailable">>) -> |
358 |
222 |
Presences = maybe_get_handler(StateData), |
359 |
222 |
presence_update_to_unavailable(Acc, FromJid, ToJid, Packet, StateData, Presences); |
360 |
|
presence_update(Acc, FromJid, ToJid, Packet, StateData, <<"invisible">>) -> |
361 |
1 |
Presences = maybe_get_handler(StateData), |
362 |
1 |
presence_update_to_invisible(Acc, FromJid, ToJid, Packet, StateData, Presences); |
363 |
:-( |
presence_update(Acc, _, _, _, _, <<"error">>) -> Acc; |
364 |
:-( |
presence_update(Acc, _, _, _, _, <<"probe">>) -> Acc; |
365 |
:-( |
presence_update(Acc, _, _, _, _, <<"subscribe">>) -> Acc; |
366 |
:-( |
presence_update(Acc, _, _, _, _, <<"subscribed">>) -> Acc; |
367 |
:-( |
presence_update(Acc, _, _, _, _, <<"unsubscribe">>) -> Acc; |
368 |
:-( |
presence_update(Acc, _, _, _, _, <<"unsubscribed">>) -> Acc. |
369 |
|
|
370 |
|
-spec presence_update_to_available( |
371 |
|
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), mongoose_c2s:data(), presences_state()) -> |
372 |
|
mongoose_acc:t(). |
373 |
|
presence_update_to_available(Acc0, FromJid, ToJid, Packet, StateData, Presences) -> |
374 |
5539 |
Jid = mongoose_c2s:get_jid(StateData), |
375 |
5539 |
HostType = mongoose_c2s:get_host_type(StateData), |
376 |
5539 |
Acc1 = mongoose_hooks:roster_get_subscription_lists(HostType, Acc0, Jid), |
377 |
5539 |
{Fs0, Ts0, Pending} = mongoose_acc:get(roster, subscription_lists, {[], [], []}, Acc1), |
378 |
5539 |
Fs = [jid:make(BJ) || BJ <- Fs0], |
379 |
5539 |
Ts = [jid:make(BJ) || BJ <- Ts0], |
380 |
5539 |
OldPriority = get_old_priority(Presences), |
381 |
5539 |
NewPriority = get_priority_from_presence(Packet), |
382 |
5539 |
Timestamp = erlang:system_time(microsecond), |
383 |
5539 |
Acc2 = update_priority(Acc1, NewPriority, Packet, StateData), |
384 |
5539 |
FromUnavail = (Presences#presences_state.pres_last == undefined) or Presences#presences_state.pres_invis, |
385 |
5539 |
?LOG_DEBUG(#{what => presence_update_to_available, |
386 |
|
text => <<"Presence changes from unavailable to available">>, |
387 |
5539 |
from_unavail => FromUnavail, acc => Acc2, c2s_state => StateData}), |
388 |
5539 |
BareJid = jid:to_bare(Jid), |
389 |
5539 |
NewPresences = Presences#presences_state{pres_f = gb_sets:from_list([BareJid | Fs]), |
390 |
|
pres_t = gb_sets:from_list([BareJid | Ts]), |
391 |
|
pres_pri = NewPriority, |
392 |
|
pres_last = Packet, |
393 |
|
pres_timestamp = Timestamp, |
394 |
|
pres_invis = false}, |
395 |
5539 |
presence_update_to_available( |
396 |
|
Acc2, FromJid, ToJid, Packet, StateData, NewPresences, Pending, |
397 |
|
OldPriority, NewPriority, FromUnavail). |
398 |
|
|
399 |
|
-spec presence_update_to_unavailable( |
400 |
|
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), mongoose_c2s:data(), presences_state()) -> |
401 |
|
mongoose_acc:t(). |
402 |
|
presence_update_to_unavailable(Acc, _FromJid, _ToJid, Packet, StateData, Presences) -> |
403 |
222 |
Status = exml_query:path(Packet, [{element, <<"status">>}, cdata], <<>>), |
404 |
222 |
Sid = mongoose_c2s:get_sid(StateData), |
405 |
222 |
Jid = mongoose_c2s:get_jid(StateData), |
406 |
222 |
Info = mongoose_c2s:get_info(StateData), |
407 |
222 |
Acc1 = ejabberd_sm:unset_presence(Acc, Sid, Jid, Status, Info), |
408 |
222 |
presence_broadcast(Acc1, Presences#presences_state.pres_a), |
409 |
222 |
presence_broadcast(Acc1, Presences#presences_state.pres_i), |
410 |
222 |
NewPresences = Presences#presences_state{pres_last = undefined, |
411 |
|
pres_timestamp = undefined, |
412 |
|
pres_a = gb_sets:new(), |
413 |
|
pres_i = gb_sets:new(), |
414 |
|
pres_invis = false}, |
415 |
222 |
mongoose_c2s_acc:to_acc(Acc1, state_mod, {?MODULE, NewPresences}). |
416 |
|
|
417 |
|
-spec presence_update_to_invisible( |
418 |
|
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), mongoose_c2s:data(), presences_state()) -> |
419 |
|
mongoose_acc:t(). |
420 |
|
presence_update_to_invisible(Acc, FromJid, _ToJid, Packet, StateData, Presences) -> |
421 |
1 |
NewPriority = get_priority_from_presence(Packet), |
422 |
1 |
Acc1 = update_priority(Acc, NewPriority, Packet, StateData), |
423 |
1 |
case Presences#presences_state.pres_invis of |
424 |
|
false -> |
425 |
1 |
presence_broadcast(Acc1, Presences#presences_state.pres_a), |
426 |
1 |
presence_broadcast(Acc1, Presences#presences_state.pres_i), |
427 |
1 |
NewPresences = Presences#presences_state{pres_last = undefined, |
428 |
|
pres_timestamp = undefined, |
429 |
|
pres_a = gb_sets:new(), |
430 |
|
pres_i = gb_sets:new(), |
431 |
|
pres_invis = true}, |
432 |
1 |
presence_broadcast_first(Acc1, FromJid, Packet, NewPresences, []); |
433 |
|
true -> |
434 |
:-( |
Acc1 |
435 |
|
end. |
436 |
|
|
437 |
|
%% @doc User sends a directed presence packet |
438 |
|
-spec presence_track( |
439 |
|
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), mongoose_c2s:data(), undefined | binary()) -> |
440 |
|
mongoose_acc:t(). |
441 |
|
presence_track(Acc, FromJid, ToJid, Packet, StateData, undefined) -> |
442 |
707 |
Presences = maybe_get_handler(StateData), |
443 |
707 |
process_presence_track_available(Acc, FromJid, ToJid, Packet, Presences); |
444 |
|
presence_track(Acc, FromJid, ToJid, Packet, StateData, <<"unavailable">>) -> |
445 |
17 |
Presences = maybe_get_handler(StateData), |
446 |
17 |
process_presence_track_unavailable(Acc, FromJid, ToJid, Packet, Presences); |
447 |
|
presence_track(Acc, FromJid, ToJid, Packet, StateData, <<"invisible">>) -> |
448 |
:-( |
Presences = maybe_get_handler(StateData), |
449 |
:-( |
process_presence_track_invisible(Acc, FromJid, ToJid, Packet, Presences); |
450 |
|
presence_track(Acc, FromJid, ToJid, Packet, _, <<"subscribe">>) -> |
451 |
103 |
process_presence_track_subscription_and_route(Acc, FromJid, ToJid, Packet, subscribe); |
452 |
|
presence_track(Acc, FromJid, ToJid, Packet, _, <<"subscribed">>) -> |
453 |
100 |
process_presence_track_subscription_and_route(Acc, FromJid, ToJid, Packet, subscribed); |
454 |
|
presence_track(Acc, FromJid, ToJid, Packet, _, <<"unsubscribe">>) -> |
455 |
3 |
process_presence_track_subscription_and_route(Acc, FromJid, ToJid, Packet, unsubscribe); |
456 |
|
presence_track(Acc, FromJid, ToJid, Packet, _, <<"unsubscribed">>) -> |
457 |
5 |
process_presence_track_subscription_and_route(Acc, FromJid, ToJid, Packet, unsubscribed); |
458 |
|
presence_track(Acc, FromJid, ToJid, Packet, _, <<"error">>) -> |
459 |
2 |
ejabberd_router:route(FromJid, ToJid, Acc, Packet), |
460 |
2 |
Acc; |
461 |
|
presence_track(Acc, FromJid, ToJid, Packet, _, <<"probe">>) -> |
462 |
:-( |
ejabberd_router:route(FromJid, ToJid, Acc, Packet), |
463 |
:-( |
Acc. |
464 |
|
|
465 |
|
process_presence_track_available(Acc, FromJid, ToJid, Packet, Presences) -> |
466 |
707 |
ejabberd_router:route(FromJid, ToJid, Acc, Packet), |
467 |
707 |
I = gb_sets:del_element(ToJid, Presences#presences_state.pres_i), |
468 |
707 |
A = gb_sets:add_element(ToJid, Presences#presences_state.pres_a), |
469 |
707 |
NewPresences = Presences#presences_state{pres_i = I, pres_a = A}, |
470 |
707 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}). |
471 |
|
|
472 |
|
process_presence_track_unavailable(Acc, FromJid, ToJid, Packet, Presences) -> |
473 |
17 |
ejabberd_router:route(FromJid, ToJid, Acc, Packet), |
474 |
17 |
I = gb_sets:del_element(ToJid, Presences#presences_state.pres_i), |
475 |
17 |
A = gb_sets:del_element(ToJid, Presences#presences_state.pres_a), |
476 |
17 |
NewPresences = Presences#presences_state{pres_i = I, pres_a = A}, |
477 |
17 |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}). |
478 |
|
|
479 |
|
process_presence_track_invisible(Acc, FromJid, ToJid, Packet, Presences) -> |
480 |
:-( |
ejabberd_router:route(FromJid, ToJid, Acc, Packet), |
481 |
:-( |
I = gb_sets:add_element(ToJid, Presences#presences_state.pres_i), |
482 |
:-( |
A = gb_sets:del_element(ToJid, Presences#presences_state.pres_a), |
483 |
:-( |
NewPresences = Presences#presences_state{pres_i = I, pres_a = A}, |
484 |
:-( |
mongoose_c2s_acc:to_acc(Acc, state_mod, {?MODULE, NewPresences}). |
485 |
|
|
486 |
|
process_presence_track_subscription_and_route(Acc, FromJid, ToJid, Packet, Type) -> |
487 |
211 |
Acc1 = mongoose_hooks:roster_out_subscription(Acc, FromJid, ToJid, Type), |
488 |
211 |
ejabberd_router:route(jid:to_bare(FromJid), ToJid, Acc1, Packet), |
489 |
211 |
Acc1. |
490 |
|
|
491 |
|
-spec presence_broadcast(mongoose_acc:t(), jid_set()) -> mongoose_acc:t(). |
492 |
|
presence_broadcast(Acc, JIDSet) -> |
493 |
10950 |
From = mongoose_acc:from_jid(Acc), |
494 |
10950 |
gb_sets:fold(fun(JID, A) -> |
495 |
6279 |
ejabberd_router:route(From, JID, A) |
496 |
|
end, Acc, JIDSet). |
497 |
|
|
498 |
|
-spec presence_update_to_available( |
499 |
|
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), mongoose_c2s:data(), |
500 |
|
presences_state(), list(), maybe_priority(), priority(), boolean()) -> |
501 |
|
mongoose_acc:t(). |
502 |
|
presence_update_to_available(Acc, FromJid, _ToJid, Packet, StateData, Presences, SocketSend, |
503 |
|
_OldPriority, NewPriority, true) -> |
504 |
5502 |
Acc1 = mongoose_hooks:user_available_hook(Acc, FromJid), |
505 |
5502 |
Acc2 = case NewPriority >= 0 of |
506 |
|
true -> |
507 |
5502 |
resend_offline_messages(Acc1, StateData); |
508 |
|
false -> |
509 |
:-( |
Acc1 |
510 |
|
end, |
511 |
5502 |
presence_broadcast_first(Acc2, FromJid, Packet, Presences, SocketSend); |
512 |
|
presence_update_to_available(Acc, FromJid, _ToJid, Packet, StateData, Presences, Pending, |
513 |
|
OldPriority, NewPriority, false) -> |
514 |
37 |
presence_broadcast_to_trusted( |
515 |
|
Acc, FromJid, Presences#presences_state.pres_f, Presences#presences_state.pres_a, Packet), |
516 |
37 |
Acc1 = case (OldPriority < 0 orelse OldPriority =:= undefined) andalso NewPriority >= 0 of |
517 |
|
true -> |
518 |
:-( |
resend_offline_messages(Acc, StateData); |
519 |
|
false -> |
520 |
37 |
Acc |
521 |
|
end, |
522 |
37 |
Accs = create_route_accs(Acc1, FromJid, Pending), |
523 |
37 |
ToAcc = [{route, Accs}, {state_mod, {?MODULE, Presences}}], |
524 |
37 |
mongoose_c2s_acc:to_acc_many(Acc1, ToAcc). |
525 |
|
|
526 |
|
-spec presence_broadcast_to_trusted( |
527 |
|
mongoose_acc:t(), jid:jid(), jid_set(), jid_set(), exml:element()) -> |
528 |
|
mongoose_acc:t(). |
529 |
|
presence_broadcast_to_trusted(Acc, FromJid, T, A, Packet) -> |
530 |
37 |
gb_sets:fold( |
531 |
|
fun(JID, Ac) -> |
532 |
41 |
case gb_sets:is_element(JID, T) of |
533 |
|
true -> |
534 |
41 |
ejabberd_router:route(FromJid, JID, Ac, Packet); |
535 |
|
_ -> |
536 |
:-( |
Ac |
537 |
|
end |
538 |
|
end, Acc, A). |
539 |
|
|
540 |
|
-spec resend_offline_messages(mongoose_acc:t(), mongoose_c2s:data()) -> mongoose_acc:t(). |
541 |
|
resend_offline_messages(Acc, StateData) -> |
542 |
5502 |
?LOG_DEBUG(#{what => resend_offline_messages, acc => Acc, c2s_state => StateData}), |
543 |
5502 |
Jid = mongoose_c2s:get_jid(StateData), |
544 |
5502 |
Acc1 = mongoose_hooks:resend_offline_messages_hook(Acc, Jid), |
545 |
5502 |
Rs = mongoose_acc:get(offline, messages, [], Acc1), |
546 |
5502 |
Acc2 = lists:foldl(fun({route, FromJid, ToJid, MsgAcc}, A) -> |
547 |
149 |
resend_offline_message(A, FromJid, ToJid, MsgAcc, in); |
548 |
|
({route, MsgAcc}, A) -> |
549 |
:-( |
{FromJid, ToJid, _} = mongoose_acc:packet(Acc), |
550 |
:-( |
resend_offline_message(A, FromJid, ToJid, MsgAcc, in) |
551 |
|
end, Acc1, Rs), |
552 |
5502 |
mongoose_acc:delete(offline, messages, Acc2). % they are gone from db backend and sent |
553 |
|
|
554 |
|
resend_offline_message(Acc0, FromJid, To, Acc, in) -> |
555 |
149 |
Packet = mongoose_acc:element(Acc), |
556 |
149 |
NewAcc = strip_c2s_fields(Acc), |
557 |
149 |
ejabberd_router:route(FromJid, To, NewAcc, Packet), |
558 |
149 |
Acc0. |
559 |
|
|
560 |
|
-spec strip_c2s_fields(mongoose_acc:t()) -> mongoose_acc:t(). |
561 |
|
strip_c2s_fields(Acc) -> |
562 |
|
%% TODO: verify if we really need to strip down these 2 fields |
563 |
149 |
mongoose_acc:delete_many(c2s, [origin_jid, origin_sid], Acc). |
564 |
|
|
565 |
|
-spec presence_broadcast_first( |
566 |
|
mongoose_acc:t(), jid:jid(), exml:element(), presences_state(), [exml:element()]) -> |
567 |
|
mongoose_acc:t(). |
568 |
|
presence_broadcast_first(Acc0, FromJid, Packet, Presences, SocketSend) -> |
569 |
5503 |
Probe = presence_probe(), |
570 |
5503 |
_Acc1 = gb_sets:fold(fun(JID, Accum) -> |
571 |
5508 |
ejabberd_router:route(FromJid, JID, Accum, Probe) |
572 |
|
end, Acc0, Presences#presences_state.pres_t), |
573 |
5503 |
case Presences#presences_state.pres_invis of |
574 |
|
true -> |
575 |
1 |
mongoose_c2s_acc:to_acc(Acc0, state_mod, {?MODULE, Presences}); |
576 |
|
false -> |
577 |
5502 |
{As, _Acc2} = gb_sets:fold( |
578 |
|
fun(JID, {A, Accum}) -> |
579 |
5508 |
Accum1 = ejabberd_router:route(FromJid, JID, Accum, Packet), |
580 |
5508 |
{gb_sets:add_element(JID, A), Accum1} |
581 |
|
end, |
582 |
|
{Presences#presences_state.pres_a, Acc0}, Presences#presences_state.pres_f), |
583 |
5502 |
NewPresences = Presences#presences_state{pres_a = As}, |
584 |
5502 |
Accs = create_route_accs(Acc0, FromJid, SocketSend), |
585 |
5502 |
ToAcc = [{route, Accs}, {state_mod, {?MODULE, NewPresences}}], |
586 |
5502 |
mongoose_c2s_acc:to_acc_many(Acc0, ToAcc) |
587 |
|
end. |
588 |
|
|
589 |
|
create_route_accs(Acc0, To, List) when is_list(List) -> |
590 |
5539 |
[ mongoose_acc:update_stanza(#{to_jid => To, element => P}, Acc0) |
591 |
5539 |
|| P <- List ]; |
592 |
|
create_route_accs(Acc0, To, El) -> |
593 |
:-( |
create_route_accs(Acc0, To, [El]). |
594 |
|
|
595 |
|
-spec presence_probe() -> exml:element(). |
596 |
|
presence_probe() -> |
597 |
5503 |
#xmlel{name = <<"presence">>, |
598 |
|
attrs = [{<<"type">>, <<"probe">>}]}. |
599 |
|
|
600 |
|
-spec presence_unavailable_stanza() -> exml:element(). |
601 |
|
presence_unavailable_stanza() -> |
602 |
45 |
presence_unavailable_stanza(<<>>). |
603 |
|
|
604 |
|
-spec presence_unavailable_stanza(binary()) -> exml:element(). |
605 |
|
presence_unavailable_stanza(<<>>) -> |
606 |
45 |
#xmlel{name = <<"presence">>, |
607 |
|
attrs = [{<<"type">>, <<"unavailable">>}]}; |
608 |
|
presence_unavailable_stanza(Status) -> |
609 |
5252 |
StatusEl = #xmlel{name = <<"status">>, |
610 |
|
children = [#xmlcdata{content = Status}]}, |
611 |
5252 |
#xmlel{name = <<"presence">>, |
612 |
|
attrs = [{<<"type">>, <<"unavailable">>}], |
613 |
|
children = [StatusEl]}. |
614 |
|
|
615 |
|
close_session_status(normal) -> |
616 |
:-( |
<<>>; |
617 |
|
close_session_status({shutdown, retries}) -> |
618 |
:-( |
<<"Too many attempts">>; |
619 |
|
close_session_status({shutdown, replaced}) -> |
620 |
:-( |
<<"Replaced by new connection">>; |
621 |
|
close_session_status({shutdown, Reason}) when is_atom(Reason) -> |
622 |
5211 |
<<"Shutdown by reason: ", (atom_to_binary(Reason))/binary>>; |
623 |
|
close_session_status({shutdown, Reason}) when is_binary(Reason) -> |
624 |
38 |
<<"Shutdown by reason: ", Reason/binary>>; |
625 |
|
close_session_status(_) -> |
626 |
3 |
<<"Unknown condition">>. |
627 |
|
|
628 |
|
-spec maybe_get_handler(mongoose_c2s:data()) -> presences_state(). |
629 |
|
maybe_get_handler(StateData) -> |
630 |
6955 |
case mongoose_c2s:get_mod_state(StateData, ?MODULE) of |
631 |
1457 |
{ok, #presences_state{} = Presences} -> Presences; |
632 |
5498 |
{error, not_found} -> #presences_state{} |
633 |
|
end. |
634 |
|
|
635 |
|
-spec get_mod_state(mongoose_c2s:data()) -> presences_state() | {error, not_found}. |
636 |
|
get_mod_state(StateData) -> |
637 |
22017 |
case mongoose_c2s:get_mod_state(StateData, ?MODULE) of |
638 |
20855 |
{ok, Presence} -> Presence; |
639 |
1162 |
Error -> Error |
640 |
|
end. |
641 |
|
|
642 |
|
-spec get_priority_from_presence(exml:element()) -> priority(). |
643 |
|
get_priority_from_presence(PresencePacket) -> |
644 |
5782 |
MaybePriority = exml_query:path(PresencePacket, [{element, <<"priority">>}, cdata], undefined), |
645 |
5782 |
case catch binary_to_integer(MaybePriority) of |
646 |
6 |
P when is_integer(P), -128 =< P, P =< 128 -> P; |
647 |
5776 |
_ -> 0 |
648 |
|
end. |
649 |
|
|
650 |
|
-spec get_old_priority(presences_state()) -> maybe_priority(). |
651 |
|
get_old_priority(Presences) -> |
652 |
5852 |
case Presences#presences_state.pres_last of |
653 |
5610 |
undefined -> undefined; |
654 |
242 |
OldPresence -> get_priority_from_presence(OldPresence) |
655 |
|
end. |
656 |
|
|
657 |
|
-spec update_priority(Acc :: mongoose_acc:t(), |
658 |
|
Priority :: integer(), |
659 |
|
Packet :: exml:element(), |
660 |
|
StateData :: mongoose_c2s:data()) -> mongoose_acc:t(). |
661 |
|
update_priority(Acc, Priority, Packet, StateData) -> |
662 |
5540 |
Sid = mongoose_c2s:get_sid(StateData), |
663 |
5540 |
Jid = mongoose_c2s:get_jid(StateData), |
664 |
5540 |
Info = mongoose_c2s:get_info(StateData), |
665 |
5540 |
ejabberd_sm:set_presence(Acc, Sid, Jid, Priority, Packet, Info). |
666 |
|
|
667 |
|
am_i_subscribed_to_presence(LJID, LBareJID, S) -> |
668 |
274 |
gb_sets:is_element(LJID, S#presences_state.pres_t) |
669 |
244 |
orelse (LJID /= LBareJID) |
670 |
212 |
andalso gb_sets:is_element(LBareJID, S#presences_state.pres_t). |
671 |
|
|
672 |
|
-spec am_i_available_to(jid:jid(), jid:jid(), presences_state()) -> boolean(). |
673 |
|
am_i_available_to(FromJid, BareJid, Presences) -> |
674 |
14050 |
gb_sets:is_element(FromJid, Presences#presences_state.pres_a) |
675 |
13260 |
orelse (FromJid /= BareJid) |
676 |
13260 |
andalso gb_sets:is_element(BareJid, Presences#presences_state.pres_a). |
677 |
|
|
678 |
|
-spec make_available_to(jid:jid(), jid:jid(), presences_state()) -> presences_state(). |
679 |
|
make_available_to(FromJid, BareJid, Presences) -> |
680 |
1331 |
case gb_sets:is_element(FromJid, Presences#presences_state.pres_f) of |
681 |
|
true -> |
682 |
:-( |
A = gb_sets:add_element(FromJid, Presences#presences_state.pres_a), |
683 |
:-( |
Presences#presences_state{pres_a = A}; |
684 |
|
false -> |
685 |
1331 |
case gb_sets:is_element(BareJid, Presences#presences_state.pres_f) of |
686 |
|
true -> |
687 |
1 |
A = gb_sets:add_element(BareJid, Presences#presences_state.pres_a), |
688 |
1 |
Presences#presences_state{pres_a = A}; |
689 |
|
false -> |
690 |
1330 |
Presences |
691 |
|
end |
692 |
|
end. |
693 |
|
|
694 |
|
-spec should_retransmit_last_presence(jid:jid(), jid:jid(), presences_state()) -> boolean(). |
695 |
|
should_retransmit_last_presence(FromJid, BareFromJid, |
696 |
|
#presences_state{pres_invis = Invisible} = Presences) -> |
697 |
5683 |
not Invisible |
698 |
5683 |
andalso is_subscribed_to_my_presence(FromJid, BareFromJid, Presences) |
699 |
5683 |
andalso not invisible_to(FromJid, BareFromJid, Presences). |
700 |
|
|
701 |
|
-spec is_subscribed_to_my_presence(jid:jid(), jid:jid(), presences_state()) -> boolean(). |
702 |
|
is_subscribed_to_my_presence(FromJid, BareFromJid, Presences) -> |
703 |
5990 |
gb_sets:is_element(FromJid, Presences#presences_state.pres_f) |
704 |
5975 |
orelse (FromJid /= BareFromJid) |
705 |
5895 |
andalso gb_sets:is_element(BareFromJid, Presences#presences_state.pres_f). |
706 |
|
|
707 |
|
-spec invisible_to(jid:jid(), jid:jid(), presences_state()) -> boolean(). |
708 |
|
invisible_to(FromJid, BareFromJid, Presences) -> |
709 |
5683 |
gb_sets:is_element(FromJid, Presences#presences_state.pres_i) |
710 |
5683 |
orelse (FromJid /= BareFromJid) |
711 |
5683 |
andalso gb_sets:is_element(BareFromJid, Presences#presences_state.pres_i). |
712 |
|
|
713 |
|
-spec specifically_visible_to(jid:jid(), presences_state()) -> boolean(). |
714 |
|
specifically_visible_to(FromJid, #presences_state{pres_invis = Invisible} = Presences) -> |
715 |
5683 |
Invisible |
716 |
:-( |
andalso gb_sets:is_element(FromJid, Presences#presences_state.pres_f) |
717 |
:-( |
andalso gb_sets:is_element(FromJid, Presences#presences_state.pres_a). |
718 |
|
|
719 |
|
-spec get(presences_state(), s_to) -> jid_set(); |
720 |
|
(presences_state(), s_from) -> jid_set(); |
721 |
|
(presences_state(), s_available) -> jid_set(); |
722 |
|
(presences_state(), s_invisible) -> jid_set(); |
723 |
|
(presences_state(), priority) -> priority(); |
724 |
|
(presences_state(), last) -> undefined | exml:element(); |
725 |
|
(presences_state(), timestamp) -> undefined | integer(); |
726 |
|
(presences_state(), invisible) -> boolean(). |
727 |
:-( |
get(#presences_state{pres_t = Value}, s_to) -> Value; |
728 |
123 |
get(#presences_state{pres_f = Value}, s_from) -> Value; |
729 |
:-( |
get(#presences_state{pres_a = Value}, s_available) -> Value; |
730 |
:-( |
get(#presences_state{pres_i = Value}, s_invisible) -> Value; |
731 |
:-( |
get(#presences_state{pres_pri = Value}, priority) -> Value; |
732 |
7 |
get(#presences_state{pres_last = Value}, last) -> Value; |
733 |
:-( |
get(#presences_state{pres_timestamp = Value}, timestamp) -> Value; |
734 |
:-( |
get(#presences_state{pres_invis = Value}, invisible) -> Value. |