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