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