./ct_report/coverage/mongoose_session_api.COVER.html

1 %% @doc Provide an interface for frontends (like graphql or ctl) to manage sessions.
2 -module(mongoose_session_api).
3
4 -export([list_sessions/0,
5 list_sessions/1,
6 count_sessions/0,
7 count_sessions/1,
8 list_user_sessions/1,
9 list_resources/1,
10 list_user_resources/1,
11 num_resources/1,
12 get_user_resource/2,
13 list_status_users/1,
14 list_status_users/2,
15 num_status_users/1,
16 num_status_users/2,
17 set_presence/5,
18 kick_session/2,
19 kick_sessions/2,
20 prepare_reason/1]).
21
22 -ignore_xref([prepare_reason/1,
23 get_user_resources/2,
24 list_user_sessions/1,
25 num_resources/1]).
26
27 -include("session.hrl").
28 -include_lib("jid/include/jid.hrl").
29 -include_lib("exml/include/exml.hrl").
30
31 -type status() :: binary().
32 -type session() :: #session{}.
33 -type status_user_info() :: {JID :: jid:jid(),
34 Prio :: ejabberd_sm:priority(),
35 Status :: status()}.
36
37 -type session_info() :: {USR :: jid:jid(),
38 Conn :: atom(),
39 Address :: {inet:ip_address(), inet:port_number()} | undefined,
40 Prio :: ejabberd_sm:priority(),
41 NodeS :: node(),
42 Uptime :: integer()}.
43
44 -type res_number_result() :: {ok | wrong_res_number | user_not_found, binary()}.
45
46 -type kick_user_result() :: #{binary() => term()}.
47
48 -type set_presence_result() :: {ok | empty_resource | user_not_found, binary()}.
49
50 -export_type([res_number_result/0,
51 set_presence_result/0,
52 kick_user_result/0,
53 status_user_info/0,
54 session_info/0,
55 status/0]).
56
57 -spec list_sessions() -> {ok, [session_info()]}.
58 list_sessions() ->
59
:-(
USRIs = ejabberd_sm:get_full_session_list(),
60
:-(
{ok, lists:map(fun format_user_info/1, USRIs)}.
61
62 -spec list_sessions(jid:server()) -> {ok, [session_info()]} | {domain_not_found, binary()}.
63 list_sessions(Host) ->
64
:-(
case check_domain(Host) of
65 ok ->
66
:-(
USRIs = ejabberd_sm:get_vh_session_list(Host),
67
:-(
{ok, lists:map(fun format_user_info/1, USRIs)};
68 Error ->
69
:-(
Error
70 end.
71
72 -spec count_sessions() -> {ok, non_neg_integer()}.
73 count_sessions() ->
74
:-(
{ok, ejabberd_sm:get_total_sessions_number()}.
75
76 -spec count_sessions(jid:server()) -> {ok, non_neg_integer()} | {domain_not_found, binary()}.
77 count_sessions(Host) ->
78
:-(
case check_domain(Host) of
79 ok ->
80
:-(
{ok, ejabberd_sm:get_vh_session_number(Host)};
81 Error ->
82
:-(
Error
83 end.
84
85 -spec list_resources(jid:server()) -> {ok, [jid:literal_jid()]} | {domain_not_found, binary()}.
86 list_resources(Host) ->
87
:-(
case check_domain(Host) of
88 ok ->
89
:-(
Lst = ejabberd_sm:get_vh_session_list(Host),
90
:-(
{ok, [jid:to_binary(USR) || #session{usr = USR} <- Lst]};
91 Error ->
92
:-(
Error
93 end.
94
95 -spec list_user_resources(jid:jid()) -> {ok, [jid:literal_jid()]} | {user_not_found, binary()}.
96 list_user_resources(JID) ->
97
:-(
case check_user(JID) of
98 ok ->
99
:-(
{ok, ejabberd_sm:get_user_resources(JID)};
100 Error ->
101
:-(
Error
102 end.
103
104 -spec list_user_sessions(jid:jid()) -> {ok, [session_info()]} | {user_not_found, binary()}.
105 list_user_sessions(JID) ->
106
:-(
case check_user(JID) of
107 ok ->
108
:-(
Resources = ejabberd_sm:get_user_resources(JID),
109
:-(
{ok, lists:foldl(
110 fun(Res, Acc) ->
111
:-(
RJID = jid:replace_resource(JID, Res),
112
:-(
case ejabberd_sm:get_session(RJID) of
113
:-(
offline -> Acc;
114
:-(
Session -> [format_user_info(Session) | Acc]
115 end
116 end,
117 [],
118 Resources)};
119 Error ->
120
:-(
Error
121 end.
122
123 -spec num_resources(jid:jid()) -> {ok, non_neg_integer()} | {user_not_found, binary()}.
124 num_resources(JID) ->
125
:-(
case check_user(JID) of
126 ok ->
127
:-(
{ok, length(ejabberd_sm:get_user_resources(JID))};
128 Error ->
129
:-(
Error
130 end.
131
132 -spec get_user_resource(jid:jid(), integer()) -> res_number_result().
133 get_user_resource(JID, Num) ->
134
:-(
case check_user(JID) of
135 ok ->
136
:-(
Resources = ejabberd_sm:get_user_resources(JID),
137
:-(
case (0 < Num) and (Num =< length(Resources)) of
138 true ->
139
:-(
{ok, lists:nth(Num, Resources)};
140 false ->
141
:-(
{wrong_res_number,
142 iolist_to_binary(io_lib:format("Wrong resource number: ~p", [Num]))}
143 end;
144 Error ->
145
:-(
Error
146 end.
147
148 -spec num_status_users(jid:server(), status()) -> {ok, non_neg_integer()}
149 | {domain_not_found, binary()}.
150 num_status_users(Host, Status) ->
151
:-(
case check_domain(Host) of
152 ok ->
153
:-(
{ok, Sessions} = list_status_users(Host, Status),
154
:-(
{ok, length(Sessions)};
155 Error ->
156
:-(
Error
157 end.
158
159 -spec num_status_users(status()) -> {ok, non_neg_integer()}.
160 num_status_users(Status) ->
161
:-(
{ok, Sessions} = list_status_users(Status),
162
:-(
{ok, length(Sessions)}.
163
164 -spec list_status_users(jid:server(), status()) -> {ok, [status_user_info()]}
165 | {domain_not_found, binary()}.
166 list_status_users(Host, Status) ->
167
:-(
case check_domain(Host) of
168 ok ->
169
:-(
Sessions = ejabberd_sm:get_vh_session_list(Host),
170
:-(
{ok, get_status_list(Sessions, Status)};
171 Error ->
172
:-(
Error
173 end.
174
175 -spec list_status_users(status()) -> {ok, [status_user_info()]}.
176 list_status_users(Status) ->
177
:-(
Sessions = ejabberd_sm:get_full_session_list(),
178
:-(
{ok, get_status_list(Sessions, Status)}.
179
180 -spec set_presence(jid:jid(), Type :: binary(), Show :: binary(),
181 Status :: binary(), Prio :: binary()) -> set_presence_result().
182 set_presence(#jid{lresource = <<>>}, _Type, _Show, _Status, _Priority) ->
183
:-(
{empty_resource, <<"The resource is empty. You need to provide a full JID">>};
184 set_presence(JID, Type, Show, Status, Priority) ->
185
:-(
case check_user(JID) of
186 ok ->
187
:-(
Pid = ejabberd_sm:get_session_pid(JID),
188
:-(
USR = jid:to_binary(JID),
189
:-(
US = jid:to_binary(jid:to_bare(JID)),
190
191
:-(
Children = maybe_pres_status(Status,
192 maybe_pres_priority(Priority,
193 maybe_pres_show(Show, []))),
194
:-(
Message = #xmlel{name = <<"presence">>,
195 attrs = [{<<"from">>, USR}, {<<"to">>, US} | maybe_type_attr(Type)],
196 children = Children},
197
:-(
ok = mod_presence:set_presence(Pid, Message),
198
:-(
{ok, <<"Presence set successfully">>};
199 Error ->
200
:-(
Error
201 end.
202
203 -spec kick_sessions(jid:jid(), binary()) -> {ok, [kick_user_result()]} | {user_not_found, binary()}.
204 kick_sessions(JID, Reason) ->
205
:-(
case check_user(JID) of
206 ok ->
207
:-(
{ok, lists:map(
208 fun(Resource) ->
209
:-(
FullJID = jid:replace_resource(JID, Resource),
210
:-(
case kick_session_internal(FullJID, Reason) of
211 {ok, Result} ->
212
:-(
{ok, Result};
213 {Code, Message} ->
214
:-(
{ok, #{<<"jid">> => FullJID,
215 <<"kicked">> => false,
216 <<"code">> => atom_to_binary(Code),
217 <<"message">> => Message}}
218 end
219 end,
220 ejabberd_sm:get_user_resources(JID))};
221 Error ->
222
:-(
Error
223 end.
224
225 -spec kick_session(jid:jid(), binary() | null) -> {ok, kick_user_result()}
226 | {no_session | user_not_found, binary()}.
227 kick_session(JID, Reason) ->
228
:-(
case check_user(JID) of
229 ok ->
230
:-(
kick_session_internal(JID, Reason);
231 Error ->
232
:-(
Error
233 end.
234
235 -spec kick_session_internal(jid:jid(), binary() | null) -> {ok, kick_user_result()}
236 | {no_session, binary()}.
237 kick_session_internal(JID, Reason) ->
238
:-(
case ejabberd_sm:terminate_session(JID, prepare_reason(Reason)) of
239 no_session ->
240
:-(
{no_session, <<"No active session">>};
241 ok ->
242
:-(
{ok, #{<<"jid">> => JID,
243 <<"kicked">> => true,
244 <<"message">> => <<"Session kicked">>}}
245 end.
246
247 -spec prepare_reason(binary() | null) -> binary().
248 prepare_reason(Reason) when Reason == <<>>; Reason == null ->
249
:-(
<<"Kicked by administrator">>;
250 prepare_reason(Reason) when is_binary(Reason) ->
251
:-(
Reason.
252
253 %% Internal
254
255 -spec get_status_list([session()], status()) -> [status_user_info()].
256 get_status_list(Sessions0, StatusRequired) ->
257
:-(
Sessions = [ {catch mod_presence:get_presence(Pid), S, P}
258
:-(
|| #session{sid = {_, Pid}, usr = {_, S, _}, priority = P} <- Sessions0],
259
260
:-(
[{jid:make_noprep(User, Server, Resource), Priority, StatusText}
261
:-(
|| {{User, Resource, Status, StatusText}, Server, Priority} <- Sessions,
262
:-(
Status == StatusRequired].
263
264 -spec format_user_info(ejabberd_sm:session()) -> session_info().
265 format_user_info(#session{sid = {Microseconds, Pid}, usr = Usr,
266 priority = Priority, info = Info}) ->
267
:-(
Conn = maps:get(conn, Info, undefined),
268
:-(
Address = maps:get(ip, Info, undefined),
269
:-(
Node = node(Pid),
270
:-(
Uptime = (erlang:system_time(microsecond) - Microseconds) div 1000000,
271
:-(
{Usr, Conn, Address, Priority, Node, Uptime}.
272
273 -spec maybe_type_attr(binary())-> list().
274 maybe_type_attr(<<"available">>) ->
275
:-(
[];
276 maybe_type_attr(Type) ->
277
:-(
[{<<"type">>, Type}].
278
279 -spec maybe_pres_show(binary(), list()) -> list().
280 maybe_pres_show(Show, Children) when Show =:= <<>>;
281 Show =:= <<"online">> ->
282
:-(
Children;
283 maybe_pres_show(Show, Children) ->
284
:-(
[#xmlel{name = <<"show">>,
285 children = [#xmlcdata{content = Show}]} | Children].
286
287 -spec maybe_pres_priority(binary(), list()) -> list().
288 maybe_pres_priority(<<>>, Children) ->
289
:-(
Children;
290 maybe_pres_priority(Prio, Children) ->
291
:-(
[#xmlel{name = <<"priority">>,
292 children = [#xmlcdata{content = Prio}]} | Children].
293
294 -spec maybe_pres_status(binary(), list()) -> list().
295 maybe_pres_status(<<>>, Children) ->
296
:-(
Children;
297 maybe_pres_status(Status, Children) ->
298
:-(
[#xmlel{name = <<"status">>,
299 children = [#xmlcdata{content = Status}]} | Children].
300
301 -spec check_domain(jid:server()) -> ok | {domain_not_found, binary()}.
302 check_domain(Domain) ->
303
:-(
case mongoose_domain_api:get_domain_host_type(Domain) of
304
:-(
{ok, _} -> ok;
305
:-(
{error, not_found} -> {domain_not_found, <<"Domain not found">>}
306 end.
307
308 -spec check_user(jid:jid()) -> ok | {user_not_found, binary()}.
309 check_user(JID = #jid{lserver = LServer}) ->
310
:-(
case mongoose_domain_api:get_domain_host_type(LServer) of
311 {ok, HostType} ->
312
:-(
case ejabberd_auth:does_user_exist(HostType, JID, stored) of
313
:-(
true -> ok;
314
:-(
false -> {user_not_found, <<"Given user does not exist">>}
315 end;
316 {error, not_found} ->
317
:-(
{user_not_found, <<"User's domain does not exist">>}
318 end.
Line Hits Source