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_user_sessions/2, |
10 |
|
list_resources/1, |
11 |
|
list_user_resources/1, |
12 |
|
num_resources/1, |
13 |
|
num_resources/2, |
14 |
|
get_user_resource/2, |
15 |
|
get_user_resource/3, |
16 |
|
list_status_users/1, |
17 |
|
list_status_users/2, |
18 |
|
num_status_users/1, |
19 |
|
num_status_users/2, |
20 |
|
set_presence/7, |
21 |
|
set_presence/5, |
22 |
|
kick_session/2, |
23 |
|
kick_session/4, |
24 |
|
kick_sessions/2, |
25 |
|
prepare_reason/1]). |
26 |
|
|
27 |
|
-ignore_xref([prepare_reason/1, |
28 |
|
get_user_resources/2, |
29 |
|
list_user_sessions/1, |
30 |
|
num_resources/1]). |
31 |
|
|
32 |
|
-include("session.hrl"). |
33 |
|
-include_lib("jid/include/jid.hrl"). |
34 |
|
-include_lib("exml/include/exml.hrl"). |
35 |
|
|
36 |
|
-type status() :: binary(). |
37 |
|
-type session() :: #session{}. |
38 |
|
-type status_user_info() :: {User :: jid:user(), |
39 |
|
Server :: jid:server(), |
40 |
|
Res :: jid:resource(), |
41 |
|
Prio :: ejabberd_sm:priority(), |
42 |
|
Status :: status()}. |
43 |
|
|
44 |
|
-type session_info() :: {USR :: binary(), |
45 |
|
Conn :: binary(), |
46 |
|
IPS :: binary(), |
47 |
|
Port :: inet:port_number(), |
48 |
|
Prio :: ejabberd_sm:priority(), |
49 |
|
NodeS :: binary(), |
50 |
|
Uptime :: integer()}. |
51 |
|
|
52 |
|
-type res_number_result() :: {ok | wrong_res_number, binary()}. |
53 |
|
|
54 |
|
-type kick_session_result() :: {ok | no_session, binary()}. |
55 |
|
|
56 |
|
-type set_presence_result() :: {ok | empty_resource, binary()}. |
57 |
|
|
58 |
|
-export_type([res_number_result/0, |
59 |
|
kick_session_result/0, |
60 |
|
set_presence_result/0, |
61 |
|
status_user_info/0, |
62 |
|
session_info/0, |
63 |
|
status/0]). |
64 |
|
|
65 |
|
-spec list_sessions() -> [session_info()]. |
66 |
|
list_sessions() -> |
67 |
2 |
USRIs = ejabberd_sm:get_full_session_list(), |
68 |
2 |
lists:map(fun format_user_info/1, USRIs). |
69 |
|
|
70 |
|
-spec list_sessions(jid:server()) -> [session_info()]. |
71 |
|
list_sessions(Host) -> |
72 |
3 |
USRIs = ejabberd_sm:get_vh_session_list(Host), |
73 |
3 |
lists:map(fun format_user_info/1, USRIs). |
74 |
|
|
75 |
|
-spec count_sessions() -> non_neg_integer(). |
76 |
|
count_sessions() -> |
77 |
1 |
ejabberd_sm:get_total_sessions_number(). |
78 |
|
|
79 |
|
-spec count_sessions(jid:server()) -> non_neg_integer(). |
80 |
|
count_sessions(Host) -> |
81 |
1 |
ejabberd_sm:get_vh_session_number(Host). |
82 |
|
|
83 |
|
-spec list_resources(jid:server()) -> [jid:literal_jid()]. |
84 |
|
list_resources(Host) -> |
85 |
3 |
Lst = ejabberd_sm:get_vh_session_list(Host), |
86 |
3 |
[jid:to_binary(USR) || #session{usr = USR} <- Lst]. |
87 |
|
|
88 |
|
-spec list_user_resources(jid:jid()) -> [jid:literal_jid()]. |
89 |
|
list_user_resources(JID) -> |
90 |
2 |
ejabberd_sm:get_user_resources(JID). |
91 |
|
|
92 |
|
-spec list_user_sessions(jid:user(), jid:server()) -> [session_info()]. |
93 |
|
list_user_sessions(User, Host) -> |
94 |
1 |
JID = jid:make(User, Host, <<>>), |
95 |
1 |
list_user_sessions(JID). |
96 |
|
|
97 |
|
-spec list_user_sessions(jid:jid()) -> [session_info()]. |
98 |
|
list_user_sessions(JID) -> |
99 |
3 |
Resources = ejabberd_sm:get_user_resources(JID), |
100 |
3 |
lists:foldl(fun(Res, Acc) -> |
101 |
4 |
RJID = jid:replace_resource(JID, Res), |
102 |
4 |
case ejabberd_sm:get_session(RJID) of |
103 |
:-( |
offline -> Acc; |
104 |
4 |
Session -> [format_user_info(Session) | Acc] |
105 |
|
end |
106 |
|
end, [], Resources). |
107 |
|
|
108 |
|
-spec num_resources(jid:user(), jid:server()) -> non_neg_integer(). |
109 |
|
num_resources(User, Host) -> |
110 |
1 |
JID = jid:make(User, Host, <<>>), |
111 |
1 |
num_resources(JID). |
112 |
|
|
113 |
|
-spec num_resources(jid:jid()) -> non_neg_integer(). |
114 |
|
num_resources(JID) -> |
115 |
2 |
length(ejabberd_sm:get_user_resources(JID)). |
116 |
|
|
117 |
|
-spec get_user_resource(jid:user(), jid:server(), integer()) -> res_number_result(). |
118 |
|
get_user_resource(User, Host, Num) -> |
119 |
1 |
JID = jid:make(User, Host, <<>>), |
120 |
1 |
get_user_resource(JID, Num). |
121 |
|
|
122 |
|
-spec get_user_resource(jid:jid(), integer()) -> res_number_result(). |
123 |
|
get_user_resource(JID, Num) -> |
124 |
7 |
Resources = ejabberd_sm:get_user_resources(JID), |
125 |
7 |
case (0 < Num) and (Num =< length(Resources)) of |
126 |
|
true -> |
127 |
5 |
{ok, lists:nth(Num, Resources)}; |
128 |
|
false -> |
129 |
2 |
{wrong_res_number, iolist_to_binary(io_lib:format("Wrong resource number: ~p", [Num]))} |
130 |
|
end. |
131 |
|
|
132 |
|
-spec num_status_users(jid:server(), status()) -> non_neg_integer(). |
133 |
|
num_status_users(Host, Status) -> |
134 |
3 |
length(list_status_users(Host, Status)). |
135 |
|
|
136 |
|
-spec num_status_users(status()) -> non_neg_integer(). |
137 |
|
num_status_users(Status) -> |
138 |
3 |
length(list_status_users(Status)). |
139 |
|
|
140 |
|
-spec list_status_users(jid:server(), status()) -> [status_user_info()]. |
141 |
|
list_status_users(Host, Status) -> |
142 |
6 |
Sessions = ejabberd_sm:get_vh_session_list(Host), |
143 |
6 |
get_status_list(Sessions, Status). |
144 |
|
|
145 |
|
-spec list_status_users(status()) -> [status_user_info()]. |
146 |
|
list_status_users(Status) -> |
147 |
6 |
Sessions = ejabberd_sm:get_full_session_list(), |
148 |
6 |
get_status_list(Sessions, Status). |
149 |
|
|
150 |
|
-spec set_presence(jid:user(), jid:server(), jid:resource(), |
151 |
|
Type :: binary(), Show :: binary(), Status :: binary(), |
152 |
|
Prio :: binary()) -> set_presence_result(). |
153 |
|
set_presence(User, Host, Resource, Type, Show, Status, Priority) -> |
154 |
1 |
JID = jid:make(User, Host, Resource), |
155 |
1 |
set_presence(JID, Type, Show, Status, Priority). |
156 |
|
|
157 |
|
-spec set_presence(jid:jid(), Type :: binary(), Show :: binary(), |
158 |
|
Status :: binary(), Prio :: binary()) -> set_presence_result(). |
159 |
|
set_presence(#jid{resource = <<>>}, _Type, _Show, _Status, _Priority) -> |
160 |
1 |
{empty_resource, <<"The resource is empty. You need to provide a full JID">>}; |
161 |
|
set_presence(JID, Type, Show, Status, Priority) -> |
162 |
4 |
Pid = ejabberd_sm:get_session_pid(JID), |
163 |
4 |
USR = jid:to_binary(JID), |
164 |
4 |
US = jid:to_binary(jid:to_bare(JID)), |
165 |
|
|
166 |
4 |
Children = maybe_pres_status(Status, |
167 |
|
maybe_pres_priority(Priority, |
168 |
|
maybe_pres_show(Show, []))), |
169 |
4 |
Message = {xmlstreamelement, |
170 |
|
#xmlel{name = <<"presence">>, |
171 |
|
attrs = [{<<"from">>, USR}, {<<"to">>, US} | maybe_type_attr(Type)], |
172 |
|
children = Children}}, |
173 |
4 |
ok = p1_fsm_old:send_event(Pid, Message), |
174 |
4 |
{ok, <<"Presence set successfully">>}. |
175 |
|
|
176 |
|
-spec kick_sessions(jid:jid(), binary()) -> [kick_session_result()]. |
177 |
|
kick_sessions(JID, Reason) -> |
178 |
3 |
lists:map( |
179 |
|
fun(Resource) -> |
180 |
2 |
service_admin_extra_sessions:kick_session( |
181 |
|
jid:replace_resource(JID, Resource), Reason) |
182 |
|
end, |
183 |
|
ejabberd_sm:get_user_resources(JID)). |
184 |
|
|
185 |
|
-spec kick_session(jid:user(), jid:server(), jid:resource(), binary()) -> kick_session_result(). |
186 |
|
kick_session(User, Server, Resource, ReasonText) -> |
187 |
3 |
kick_session(jid:make(User, Server, Resource), prepare_reason(ReasonText)). |
188 |
|
|
189 |
|
-spec kick_session(jid:jid(), binary()) -> kick_session_result(). |
190 |
|
kick_session(JID, ReasonText) -> |
191 |
9 |
case ejabberd_c2s:terminate_session(JID, prepare_reason(ReasonText)) of |
192 |
|
no_session -> |
193 |
3 |
{no_session, <<"No active session">>}; |
194 |
|
{exit, ReasonText} -> |
195 |
6 |
{ok, <<"Session kicked">>} |
196 |
|
end. |
197 |
|
|
198 |
|
-spec prepare_reason(binary() | string()) -> binary(). |
199 |
|
prepare_reason(<<>>) -> |
200 |
:-( |
<<"Kicked by administrator">>; |
201 |
|
prepare_reason([Reason]) -> |
202 |
:-( |
prepare_reason(Reason); |
203 |
|
prepare_reason(Reason) when is_list(Reason) -> |
204 |
:-( |
list_to_binary(Reason); |
205 |
|
prepare_reason(Reason) when is_binary(Reason) -> |
206 |
15 |
Reason. |
207 |
|
|
208 |
|
%% Internal |
209 |
|
|
210 |
|
-spec get_status_list([session()], status()) -> [status_user_info()]. |
211 |
|
get_status_list(Sessions0, StatusRequired) -> |
212 |
12 |
Sessions = [ {catch ejabberd_c2s:get_presence(Pid), S, P} |
213 |
12 |
|| #session{sid = {_, Pid}, usr = {_, S, _}, priority = P} <- Sessions0 |
214 |
|
], |
215 |
|
|
216 |
12 |
[{User, Server, Resource, Priority, StatusText} |
217 |
12 |
|| {{User, Resource, Status, StatusText}, Server, Priority} <- Sessions, |
218 |
22 |
Status == StatusRequired]. |
219 |
|
|
220 |
|
-spec format_user_info(ejabberd_sm:session()) -> session_info(). |
221 |
|
format_user_info(#session{sid = {Microseconds, Pid}, usr = Usr, |
222 |
|
priority = Priority, info = Info}) -> |
223 |
14 |
Conn = atom_to_binary(maps:get(conn, Info, undefined)), |
224 |
14 |
{Ip, Port} = maps:get(ip, Info, undefined), |
225 |
14 |
IPS = list_to_binary(inet_parse:ntoa(Ip)), |
226 |
14 |
NodeS = atom_to_binary(node(Pid)), |
227 |
14 |
Uptime = (erlang:system_time(microsecond) - Microseconds) div 1000000, |
228 |
14 |
BinJID = jid:to_binary(Usr), |
229 |
14 |
{BinJID, Conn, IPS, Port, Priority, NodeS, Uptime}. |
230 |
|
|
231 |
|
-spec maybe_type_attr(binary())-> list(). |
232 |
|
maybe_type_attr(<<"available">>) -> |
233 |
3 |
[]; |
234 |
|
maybe_type_attr(Type) -> |
235 |
1 |
[{<<"type">>, Type}]. |
236 |
|
|
237 |
|
-spec maybe_pres_show(binary(), list()) -> list(). |
238 |
|
maybe_pres_show(Show, Children) when Show =:= <<>>; |
239 |
|
Show =:= <<"online">> -> |
240 |
2 |
Children; |
241 |
|
maybe_pres_show(Show, Children) -> |
242 |
2 |
[#xmlel{name = <<"show">>, |
243 |
|
children = [#xmlcdata{content = Show}]} | Children]. |
244 |
|
|
245 |
|
-spec maybe_pres_priority(binary(), list()) -> list(). |
246 |
|
maybe_pres_priority(<<>>, Children) -> |
247 |
2 |
Children; |
248 |
|
maybe_pres_priority(Prio, Children) -> |
249 |
2 |
[#xmlel{name = <<"priority">>, |
250 |
|
children = [#xmlcdata{content = Prio}]} | Children]. |
251 |
|
|
252 |
|
-spec maybe_pres_status(binary(), list()) -> list(). |
253 |
|
maybe_pres_status(<<>>, Children) -> |
254 |
1 |
Children; |
255 |
|
maybe_pres_status(Status, Children) -> |
256 |
3 |
[#xmlel{name = <<"status">>, |
257 |
|
children = [#xmlcdata{content = Status}]} | Children]. |