1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : mod_adhoc.erl |
3 |
|
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se> |
4 |
|
%%% Purpose : Handle incoming ad-doc requests (XEP-0050) |
5 |
|
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se> |
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 |
|
-module(mod_adhoc). |
26 |
|
-author('henoch@dtek.chalmers.se'). |
27 |
|
|
28 |
|
-behaviour(gen_mod). |
29 |
|
-behaviour(mongoose_module_metrics). |
30 |
|
|
31 |
|
-type command_hook_acc() :: {error, exml:element()} | exml:element() | ignore | empty. |
32 |
|
-export_type([command_hook_acc/0]). |
33 |
|
|
34 |
|
%% Gen_mod callbacks |
35 |
|
-export([start/2, |
36 |
|
stop/1, |
37 |
|
hooks/1, |
38 |
|
config_spec/0, |
39 |
|
supported_features/0]). |
40 |
|
|
41 |
|
%% IQ and hook handlers |
42 |
|
-export([process_local_iq/5, |
43 |
|
process_sm_iq/5, |
44 |
|
disco_local_items/3, |
45 |
|
disco_local_identity/3, |
46 |
|
disco_local_features/3, |
47 |
|
disco_sm_items/3, |
48 |
|
disco_sm_identity/3, |
49 |
|
disco_sm_features/3, |
50 |
|
ping_command/3]). |
51 |
|
|
52 |
|
-include("mongoose.hrl"). |
53 |
|
-include("jlib.hrl"). |
54 |
|
-include("adhoc.hrl"). |
55 |
|
-include("mongoose_config_spec.hrl"). |
56 |
|
|
57 |
|
-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok. |
58 |
|
start(HostType, #{iqdisc := IQDisc}) -> |
59 |
413 |
[gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_COMMANDS, Component, Fn, #{}, IQDisc) || |
60 |
413 |
{Component, Fn} <- iq_handlers()], |
61 |
413 |
ok. |
62 |
|
|
63 |
|
-spec stop(mongooseim:host_type()) -> ok. |
64 |
|
stop(HostType) -> |
65 |
413 |
[gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_COMMANDS, Component) || |
66 |
413 |
{Component, _Fn} <- iq_handlers()], |
67 |
413 |
ok. |
68 |
|
|
69 |
|
iq_handlers() -> |
70 |
826 |
[{ejabberd_local, fun ?MODULE:process_local_iq/5}, |
71 |
|
{ejabberd_sm, fun ?MODULE:process_sm_iq/5}]. |
72 |
|
|
73 |
|
hooks(HostType) -> |
74 |
826 |
[{disco_local_features, HostType, fun ?MODULE:disco_local_features/3, #{}, 99}, |
75 |
|
{disco_local_identity, HostType, fun ?MODULE:disco_local_identity/3, #{}, 99}, |
76 |
|
{disco_local_items, HostType, fun ?MODULE:disco_local_items/3, #{}, 99}, |
77 |
|
{disco_sm_identity, HostType, fun ?MODULE:disco_sm_identity/3, #{}, 99}, |
78 |
|
{disco_sm_features, HostType, fun ?MODULE:disco_sm_features/3, #{}, 99}, |
79 |
|
{disco_sm_items, HostType, fun ?MODULE:disco_sm_items/3, #{}, 99}, |
80 |
|
{adhoc_local_commands, HostType, fun ?MODULE:ping_command/3, #{}, 100}]. |
81 |
|
|
82 |
|
%%% |
83 |
|
%%% config_spec |
84 |
|
%%% |
85 |
|
|
86 |
|
-spec config_spec() -> mongoose_config_spec:config_section(). |
87 |
|
config_spec() -> |
88 |
208 |
#section{ |
89 |
|
items = #{<<"report_commands_node">> => #option{type = boolean}, |
90 |
|
<<"iqdisc">> => mongoose_config_spec:iqdisc()}, |
91 |
|
defaults = #{<<"report_commands_node">> => false, |
92 |
|
<<"iqdisc">> => one_queue} |
93 |
|
}. |
94 |
|
|
95 |
|
-spec supported_features() -> [atom()]. |
96 |
204 |
supported_features() -> [dynamic_domains]. |
97 |
|
|
98 |
|
%%% |
99 |
|
%%% IQ handlers |
100 |
|
%%% |
101 |
|
|
102 |
|
-spec process_local_iq(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq(), map()) |
103 |
|
-> {mongoose_acc:t(), ignore | jlib:iq()}. |
104 |
|
process_local_iq(Acc, From, To, IQ, _Extra) -> |
105 |
1 |
{Acc, process_adhoc_request(Acc, From, To, IQ, adhoc_local_commands)}. |
106 |
|
|
107 |
|
-spec process_sm_iq(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq(), map()) -> |
108 |
|
{mongoose_acc:t(), ignore | jlib:iq()}. |
109 |
|
process_sm_iq(Acc, From, To, IQ, _Extra) -> |
110 |
:-( |
{Acc, process_adhoc_request(Acc, From, To, IQ, adhoc_sm_commands)}. |
111 |
|
|
112 |
|
-spec process_adhoc_request(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq(), |
113 |
|
Hook :: atom()) -> ignore | jlib:iq(). |
114 |
|
process_adhoc_request(Acc, From, To, #iq{sub_el = SubEl} = IQ, Hook) -> |
115 |
1 |
?LOG_DEBUG(#{what => adhoc_parse_request, iq => IQ, hook => Hook}), |
116 |
1 |
case adhoc:parse_request(IQ) of |
117 |
|
{error, Error} -> |
118 |
:-( |
IQ#iq{type = error, sub_el = [SubEl, Error]}; |
119 |
|
#adhoc_request{} = AdhocRequest -> |
120 |
1 |
HostType = mongoose_acc:host_type(Acc), |
121 |
1 |
case run_request_hook(Hook, HostType, From, To, AdhocRequest) of |
122 |
|
ignore -> |
123 |
:-( |
ignore; |
124 |
|
empty -> |
125 |
:-( |
IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:item_not_found()]}; |
126 |
|
{error, Error} -> |
127 |
:-( |
IQ#iq{type = error, sub_el = [SubEl, Error]}; |
128 |
|
Command -> |
129 |
1 |
IQ#iq{type = result, sub_el = [Command]} |
130 |
|
end |
131 |
|
end. |
132 |
|
|
133 |
|
run_request_hook(adhoc_local_commands, HostType, From, To, AdhocRequest) -> |
134 |
1 |
mongoose_hooks:adhoc_local_commands(HostType, From, To, AdhocRequest); |
135 |
|
run_request_hook(adhoc_sm_commands, HostType, From, To, AdhocRequest) -> |
136 |
:-( |
mongoose_hooks:adhoc_sm_commands(HostType, From, To, AdhocRequest). |
137 |
|
|
138 |
|
%%% |
139 |
|
%%% Hooks handlers |
140 |
|
%%% |
141 |
|
|
142 |
|
-spec disco_local_items(Acc, Params, Extra) -> {ok, Acc} when |
143 |
|
Acc :: mongoose_disco:item_acc(), |
144 |
|
Params :: map(), |
145 |
|
Extra :: #{host_type := mongooseim:host_type()}. |
146 |
|
disco_local_items(Acc = #{to_jid := #jid{lserver = LServer}, node := <<>>, lang := Lang}, _, #{host_type := HostType}) -> |
147 |
43 |
Items = case are_commands_visible(HostType) of |
148 |
|
false -> |
149 |
42 |
[]; |
150 |
|
_ -> |
151 |
1 |
[item(LServer, ?NS_COMMANDS, <<"Commands">>, Lang)] |
152 |
|
end, |
153 |
43 |
{ok, mongoose_disco:add_items(Items, Acc)}; |
154 |
|
disco_local_items(Acc = #{to_jid := #jid{lserver = LServer}, node := ?NS_COMMANDS, lang := Lang}, _, _) -> |
155 |
2 |
Items = [item(LServer, <<"ping">>, <<"Ping">>, Lang)], |
156 |
2 |
{ok, mongoose_disco:add_items(Items, Acc)}; |
157 |
|
disco_local_items(Acc = #{node := <<"ping">>}, _, _) -> |
158 |
:-( |
{ok, Acc#{result := []}}; % override the result |
159 |
|
disco_local_items(Acc, _, _) -> |
160 |
:-( |
{ok, Acc}. |
161 |
|
|
162 |
|
%%------------------------------------------------------------------------- |
163 |
|
|
164 |
|
-spec disco_sm_items(Acc, Params, Extra) -> {ok, Acc} when |
165 |
|
Acc :: mongoose_disco:item_acc(), |
166 |
|
Params :: map(), |
167 |
|
Extra :: #{host_type := mongooseim:host_type()}. |
168 |
|
disco_sm_items(Acc = #{to_jid := To, node := <<>>, lang := Lang}, _, #{host_type := HostType}) -> |
169 |
7 |
Items = case are_commands_visible(HostType) of |
170 |
|
false -> |
171 |
6 |
[]; |
172 |
|
_ -> |
173 |
1 |
[item(jid:to_binary(To), ?NS_COMMANDS, <<"Commands">>, Lang)] |
174 |
|
end, |
175 |
7 |
{ok, mongoose_disco:add_items(Items, Acc)}; |
176 |
|
disco_sm_items(Acc, _, _) -> |
177 |
6 |
{ok, Acc}. |
178 |
|
|
179 |
|
are_commands_visible(HostType) -> |
180 |
50 |
gen_mod:get_module_opt(HostType, ?MODULE, report_commands_node). |
181 |
|
|
182 |
|
item(LServer, Node, Name, Lang) -> |
183 |
4 |
#{jid => LServer, node => Node, name => translate:translate(Lang, Name)}. |
184 |
|
|
185 |
|
%%------------------------------------------------------------------------- |
186 |
|
|
187 |
|
%% @doc On disco info request to the ad-hoc node, return automation/command-list. |
188 |
|
-spec disco_local_identity(Acc, Params, Extra) -> {ok, Acc} when |
189 |
|
Acc :: mongoose_disco:identity_acc(), |
190 |
|
Params :: map(), |
191 |
|
Extra :: gen_hook:extra(). |
192 |
|
disco_local_identity(Acc = #{node := ?NS_COMMANDS, lang := Lang}, _, _) -> |
193 |
2 |
{ok, mongoose_disco:add_identities([command_list_identity(Lang)], Acc)}; |
194 |
|
disco_local_identity(Acc = #{node := <<"ping">>, lang := Lang}, _, _) -> |
195 |
2 |
{ok, mongoose_disco:add_identities([ping_identity(Lang)], Acc)}; |
196 |
|
disco_local_identity(Acc, _, _) -> |
197 |
223 |
{ok, Acc}. |
198 |
|
|
199 |
|
%%------------------------------------------------------------------------- |
200 |
|
|
201 |
|
%% @doc On disco info request to the ad-hoc node, return automation/command-list. |
202 |
|
-spec disco_sm_identity(Acc, Params, Extra) -> {ok, Acc} when |
203 |
|
Acc :: mongoose_disco:identity_acc(), |
204 |
|
Params :: map(), |
205 |
|
Extra :: gen_hook:extra(). |
206 |
|
disco_sm_identity(Acc = #{node := ?NS_COMMANDS, lang := Lang}, _, _) -> |
207 |
2 |
{ok, mongoose_disco:add_identities([command_list_identity(Lang)], Acc)}; |
208 |
|
disco_sm_identity(Acc, _, _) -> |
209 |
10 |
{ok, Acc}. |
210 |
|
|
211 |
|
ping_identity(Lang) -> |
212 |
2 |
#{category => <<"automation">>, |
213 |
|
type => <<"command-node">>, |
214 |
|
name => translate:translate(Lang, <<"Ping">>)}. |
215 |
|
|
216 |
|
command_list_identity(Lang) -> |
217 |
4 |
#{category => <<"automation">>, |
218 |
|
type => <<"command-list">>, |
219 |
|
name => translate:translate(Lang, <<"Commands">>)}. |
220 |
|
|
221 |
|
%%------------------------------------------------------------------------- |
222 |
|
|
223 |
|
-spec disco_local_features(Acc, Params, Extra) -> {ok, Acc} when |
224 |
|
Acc :: mongoose_disco:feature_acc(), |
225 |
|
Params :: map(), |
226 |
|
Extra :: gen_hook:extra(). |
227 |
|
disco_local_features(Acc = #{node := <<>>}, _, _) -> |
228 |
222 |
{ok, mongoose_disco:add_features([?NS_COMMANDS], Acc)}; |
229 |
|
disco_local_features(Acc = #{node := ?NS_COMMANDS}, _, _) -> |
230 |
|
%% override all lesser features... |
231 |
2 |
{ok, Acc#{result := []}}; |
232 |
|
disco_local_features(Acc = #{node := <<"ping">>}, _, _) -> |
233 |
|
%% override all lesser features... |
234 |
2 |
{ok, Acc#{result := [?NS_COMMANDS]}}; |
235 |
|
disco_local_features(Acc, _, _) -> |
236 |
1 |
{ok, Acc}. |
237 |
|
|
238 |
|
%%------------------------------------------------------------------------- |
239 |
|
|
240 |
|
-spec disco_sm_features(Acc, Params, Extra) -> {ok, Acc} when |
241 |
|
Acc :: mongoose_disco:feature_acc(), |
242 |
|
Params :: map(), |
243 |
|
Extra :: gen_hook:extra(). |
244 |
|
disco_sm_features(Acc = #{node := <<>>}, _, _) -> |
245 |
10 |
{ok, mongoose_disco:add_features([?NS_COMMANDS], Acc)}; |
246 |
|
disco_sm_features(Acc = #{node := ?NS_COMMANDS}, _, _) -> |
247 |
|
%% override all lesser features... |
248 |
2 |
{ok, Acc#{result := []}}; |
249 |
|
disco_sm_features(Acc, _, _) -> |
250 |
:-( |
{ok, Acc}. |
251 |
|
|
252 |
|
%%------------------------------------------------------------------------- |
253 |
|
|
254 |
|
-spec ping_command(Acc, Params, Extra) -> {ok, Acc} when |
255 |
|
Acc :: command_hook_acc(), |
256 |
|
Params :: #{adhoc_request := adhoc:request()}, |
257 |
|
Extra :: gen_hook:extra(). |
258 |
|
ping_command(empty, |
259 |
|
#{adhoc_request := #adhoc_request{lang = Lang, node = <<"ping">> = Node, |
260 |
|
session_id = SessionID, action = Action}}, |
261 |
|
_) -> |
262 |
1 |
NewAcc = case Action == <<"">> orelse Action == <<"execute">> of |
263 |
|
true -> |
264 |
1 |
adhoc:produce_response( |
265 |
|
#adhoc_response{lang = Lang, |
266 |
|
node = Node, |
267 |
|
session_id = SessionID, |
268 |
|
status = completed, |
269 |
|
notes = [{<<"info">>, translate:translate(Lang, <<"Pong">>)}]}); |
270 |
|
false -> |
271 |
:-( |
{error, mongoose_xmpp_errors:bad_request()} |
272 |
|
end, |
273 |
1 |
{ok, NewAcc}; |
274 |
|
ping_command(Acc, _, _) -> |
275 |
:-( |
{ok, Acc}. |