./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 2 USRIs = ejabberd_sm:get_full_session_list(),
60 2 {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 7 case check_domain(Host) of
65 ok ->
66 5 USRIs = ejabberd_sm:get_vh_session_list(Host),
67 5 {ok, lists:map(fun format_user_info/1, USRIs)};
68 Error ->
69 2 Error
70 end.
71
72 -spec count_sessions() -> {ok, non_neg_integer()}.
73 count_sessions() ->
74 2 {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 7 case check_domain(Host) of
79 ok ->
80 5 {ok, ejabberd_sm:get_vh_session_number(Host)};
81 Error ->
82 2 Error
83 end.
84
85 -spec list_resources(jid:server()) -> {ok, [jid:literal_jid()]} | {domain_not_found, binary()}.
86 list_resources(Host) ->
87 3 case check_domain(Host) of
88 ok ->
89 3 Lst = ejabberd_sm:get_vh_session_list(Host),
90 3 {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 2 case check_user(JID) of
98 ok ->
99 2 {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 7 case check_user(JID) of
107 ok ->
108 4 Resources = ejabberd_sm:get_user_resources(JID),
109 4 {ok, lists:foldl(
110 fun(Res, Acc) ->
111 7 RJID = jid:replace_resource(JID, Res),
112 7 case ejabberd_sm:get_session(RJID) of
113
:-(
offline -> Acc;
114 7 Session -> [format_user_info(Session) | Acc]
115 end
116 end,
117 [],
118 Resources)};
119 Error ->
120 3 Error
121 end.
122
123 -spec num_resources(jid:jid()) -> {ok, non_neg_integer()} | {user_not_found, binary()}.
124 num_resources(JID) ->
125 6 case check_user(JID) of
126 ok ->
127 3 {ok, length(ejabberd_sm:get_user_resources(JID))};
128 Error ->
129 3 Error
130 end.
131
132 -spec get_user_resource(jid:jid(), integer()) -> res_number_result().
133 get_user_resource(JID, Num) ->
134 14 case check_user(JID) of
135 ok ->
136 11 Resources = ejabberd_sm:get_user_resources(JID),
137 11 case (0 < Num) and (Num =< length(Resources)) of
138 true ->
139 7 {ok, lists:nth(Num, Resources)};
140 false ->
141 4 {wrong_res_number,
142 iolist_to_binary(io_lib:format("Wrong resource number: ~p", [Num]))}
143 end;
144 Error ->
145 3 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 11 case check_domain(Host) of
152 ok ->
153 7 {ok, Sessions} = list_status_users(Host, Status),
154 7 {ok, length(Sessions)};
155 Error ->
156 4 Error
157 end.
158
159 -spec num_status_users(status()) -> {ok, non_neg_integer()}.
160 num_status_users(Status) ->
161 4 {ok, Sessions} = list_status_users(Status),
162 4 {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 14 case check_domain(Host) of
168 ok ->
169 14 Sessions = ejabberd_sm:get_vh_session_list(Host),
170 14 {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 8 Sessions = ejabberd_sm:get_full_session_list(),
178 8 {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 3 {empty_resource, <<"The resource is empty. You need to provide a full JID">>};
184 set_presence(JID, Type, Show, Status, Priority) ->
185 12 case check_user(JID) of
186 ok ->
187 9 Pid = ejabberd_sm:get_session_pid(JID),
188 9 USR = jid:to_binary(JID),
189 9 US = jid:to_binary(jid:to_bare(JID)),
190
191 9 Children = maybe_pres_status(Status,
192 maybe_pres_priority(Priority,
193 maybe_pres_show(Show, []))),
194 9 Message = #xmlel{name = <<"presence">>,
195 attrs = [{<<"from">>, USR}, {<<"to">>, US} | maybe_type_attr(Type)],
196 children = Children},
197 9 ok = mod_presence:set_presence(Pid, Message),
198 9 {ok, <<"Presence set successfully">>};
199 Error ->
200 3 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 7 case check_user(JID) of
206 ok ->
207 5 {ok, lists:map(
208 fun(Resource) ->
209 7 FullJID = jid:replace_resource(JID, Resource),
210 7 case kick_session_internal(FullJID, Reason) of
211 {ok, Result} ->
212 7 {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 2 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 14 case check_user(JID) of
229 ok ->
230 11 kick_session_internal(JID, Reason);
231 Error ->
232 3 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 18 case ejabberd_sm:terminate_session(JID, prepare_reason(Reason)) of
239 no_session ->
240 4 {no_session, <<"No active session">>};
241 ok ->
242 14 {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 3 <<"Kicked by administrator">>;
250 prepare_reason(Reason) when is_binary(Reason) ->
251 15 Reason.
252
253 %% Internal
254
255 -spec get_status_list([session()], status()) -> [status_user_info()].
256 get_status_list(Sessions0, StatusRequired) ->
257 22 Sessions = [ {catch mod_presence:get_presence(Pid), S, P}
258 22 || #session{sid = {_, Pid}, usr = {_, S, _}, priority = P} <- Sessions0],
259
260 22 [{jid:make_noprep(User, Server, Resource), Priority, StatusText}
261 22 || {{User, Resource, Status, StatusText}, Server, Priority} <- Sessions,
262 30 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 19 Conn = maps:get(conn, Info, undefined),
268 19 Address = maps:get(ip, Info, undefined),
269 19 Node = node(Pid),
270 19 Uptime = (erlang:system_time(microsecond) - Microseconds) div 1000000,
271 19 {Usr, Conn, Address, Priority, Node, Uptime}.
272
273 -spec maybe_type_attr(binary())-> list().
274 maybe_type_attr(<<"available">>) ->
275 6 [];
276 maybe_type_attr(Type) ->
277 3 [{<<"type">>, Type}].
278
279 -spec maybe_pres_show(binary(), list()) -> list().
280 maybe_pres_show(Show, Children) when Show =:= <<>>;
281 Show =:= <<"online">> ->
282 6 Children;
283 maybe_pres_show(Show, Children) ->
284 3 [#xmlel{name = <<"show">>,
285 children = [#xmlcdata{content = Show}]} | Children].
286
287 -spec maybe_pres_priority(binary(), list()) -> list().
288 maybe_pres_priority(<<>>, Children) ->
289 6 Children;
290 maybe_pres_priority(Prio, Children) ->
291 3 [#xmlel{name = <<"priority">>,
292 children = [#xmlcdata{content = Prio}]} | Children].
293
294 -spec maybe_pres_status(binary(), list()) -> list().
295 maybe_pres_status(<<>>, Children) ->
296 3 Children;
297 maybe_pres_status(Status, Children) ->
298 6 [#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 42 case mongoose_domain_api:get_domain_host_type(Domain) of
304 34 {ok, _} -> ok;
305 8 {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 62 case mongoose_domain_api:get_domain_host_type(LServer) of
311 {ok, HostType} ->
312 62 case ejabberd_auth:does_user_exist(HostType, JID, stored) of
313 45 true -> ok;
314 17 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