./ct_report/coverage/mod_adhoc.COVER.html

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 401 [gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_COMMANDS, Component, Fn, #{}, IQDisc) ||
60 401 {Component, Fn} <- iq_handlers()],
61 401 ok.
62
63 -spec stop(mongooseim:host_type()) -> ok.
64 stop(HostType) ->
65 401 [gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_COMMANDS, Component) ||
66 401 {Component, _Fn} <- iq_handlers()],
67 401 ok.
68
69 iq_handlers() ->
70 802 [{ejabberd_local, fun ?MODULE:process_local_iq/5},
71 {ejabberd_sm, fun ?MODULE:process_sm_iq/5}].
72
73 hooks(HostType) ->
74 802 [{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 202 #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 198 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 29 Items = case are_commands_visible(HostType) of
148 false ->
149 28 [];
150 _ ->
151 1 [item(LServer, ?NS_COMMANDS, <<"Commands">>, Lang)]
152 end,
153 29 {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 36 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 217 {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 216 {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}.
Line Hits Source