• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

processone / ejabberd / 1258

12 Dec 2025 03:57PM UTC coverage: 33.638% (-0.006%) from 33.644%
1258

push

github

badlop
Container: Apply commit a22c88a

ejabberdctl.template: Show meaningful error when ERL_DIST_PORT is in use

15554 of 46240 relevant lines covered (33.64%)

1078.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.85
/src/mod_adhoc.erl
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-2025   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 along
21
%%% with this program; if not, write to the Free Software Foundation, Inc.,
22
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
%%%
24
%%%----------------------------------------------------------------------
25

26
-module(mod_adhoc).
27

28
-author('henoch@dtek.chalmers.se').
29

30
-protocol({xep, 50, '1.3.0', '1.1.0', "complete", ""}).
31

32
-behaviour(gen_mod).
33

34
-export([start/2, stop/1, reload/3, process_local_iq/1,
35
         process_sm_iq/1, get_local_commands/5,
36
         get_local_identity/5, get_local_features/5,
37
         get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
38
         ping_item/4, ping_command/4, mod_opt_type/1, depends/2,
39
         mod_options/1, mod_doc/0]).
40

41
-include("logger.hrl").
42
-include_lib("xmpp/include/xmpp.hrl").
43
-include("translate.hrl").
44

45
start(_Host, _Opts) ->
46
    {ok, [{iq_handler, ejabberd_local, ?NS_COMMANDS, process_local_iq},
99✔
47
          {iq_handler, ejabberd_sm, ?NS_COMMANDS, process_sm_iq},
48
          {hook, disco_local_identity, get_local_identity, 99},
49
          {hook, disco_local_features, get_local_features, 99},
50
          {hook, disco_local_items, get_local_commands, 99},
51
          {hook, disco_sm_identity, get_sm_identity, 99},
52
          {hook, disco_sm_features, get_sm_features, 99},
53
          {hook, disco_sm_items, get_sm_commands, 99},
54
          {hook, adhoc_local_items, ping_item, 100},
55
          {hook, adhoc_local_commands, ping_command, 100}]}.
56

57
stop(_Host) ->
58
    ok.
99✔
59

60
reload(_Host, _NewOpts, _OldOpts) ->
61
    ok.
×
62

63
%-------------------------------------------------------------------------
64
-spec get_local_commands(mod_disco:items_acc(), jid(), jid(), binary(), binary()) -> mod_disco:items_acc().
65
get_local_commands(Acc, _From,
66
                   #jid{server = Server, lserver = LServer} = _To, <<"">>,
67
                   Lang) ->
68
    Display = mod_adhoc_opt:report_commands_node(LServer),
1✔
69
    case Display of
1✔
70
      false -> Acc;
1✔
71
      _ ->
72
          Items = case Acc of
×
73
                    {result, I} -> I;
×
74
                    _ -> []
×
75
                  end,
76
          Nodes = [#disco_item{jid = jid:make(Server),
×
77
                               node = ?NS_COMMANDS,
78
                               name = translate:translate(Lang, ?T("Commands"))}],
79
          {result, Items ++ Nodes}
×
80
    end;
81
get_local_commands(_Acc, From,
82
                   #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
83
    ejabberd_hooks:run_fold(adhoc_local_items, LServer,
×
84
                            {result, []}, [From, To, Lang]);
85
get_local_commands(_Acc, _From, _To, <<"ping">>,
86
                   _Lang) ->
87
    {result, []};
×
88
get_local_commands(Acc, _From, _To, _Node, _Lang) ->
89
    Acc.
1✔
90

91
%-------------------------------------------------------------------------
92
-spec get_sm_commands(mod_disco:items_acc(), jid(), jid(), binary(), binary()) -> mod_disco:items_acc().
93
get_sm_commands(Acc, _From,
94
                #jid{lserver = LServer} = To, <<"">>, Lang) ->
95
    Display = mod_adhoc_opt:report_commands_node(LServer),
×
96
    case Display of
×
97
      false -> Acc;
×
98
      _ ->
99
          Items = case Acc of
×
100
                    {result, I} -> I;
×
101
                    _ -> []
×
102
                  end,
103
          Nodes = [#disco_item{jid = To,
×
104
                               node = ?NS_COMMANDS,
105
                               name = translate:translate(Lang, ?T("Commands"))}],
106
          {result, Items ++ Nodes}
×
107
    end;
108
get_sm_commands(_Acc, From,
109
                #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
110
    ejabberd_hooks:run_fold(adhoc_sm_items, LServer,
×
111
                            {result, []}, [From, To, Lang]);
112
get_sm_commands(Acc, _From, _To, _Node, _Lang) -> Acc.
8✔
113

114
%-------------------------------------------------------------------------
115
-spec get_local_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()].
116
%% On disco info request to the ad-hoc node, return automation/command-list.
117
get_local_identity(Acc, _From, _To, ?NS_COMMANDS,
118
                   Lang) ->
119
    [#identity{category = <<"automation">>,
×
120
               type = <<"command-list">>,
121
               name = translate:translate(Lang, ?T("Commands"))}
122
     | Acc];
123
get_local_identity(Acc, _From, _To, <<"ping">>, Lang) ->
124
    [#identity{category = <<"automation">>,
×
125
               type = <<"command-node">>,
126
               name = translate:translate(Lang, ?T("Ping"))}
127
     | Acc];
128
get_local_identity(Acc, _From, _To, _Node, _Lang) ->
129
    Acc.
1,883✔
130

131
%-------------------------------------------------------------------------
132
-spec get_sm_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()].
133
%% On disco info request to the ad-hoc node, return automation/command-list.
134
get_sm_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
135
    [#identity{category = <<"automation">>,
×
136
               type = <<"command-list">>,
137
               name = translate:translate(Lang, ?T("Commands"))}
138
     | Acc];
139
get_sm_identity(Acc, _From, _To, _Node, _Lang) -> Acc.
56✔
140

141
%-------------------------------------------------------------------------
142
-spec get_local_features(mod_disco:features_acc(), jid(), jid(), binary(), binary()) -> mod_disco:features_acc().
143
get_local_features(Acc, _From, _To, <<"">>, _Lang) ->
144
    Feats = case Acc of
1,882✔
145
              {result, I} -> I;
1,881✔
146
              _ -> []
1✔
147
            end,
148
    {result, Feats ++ [?NS_COMMANDS]};
1,882✔
149
get_local_features(_Acc, _From, _To, ?NS_COMMANDS,
150
                   _Lang) ->
151
    {result, []};
×
152
get_local_features(_Acc, _From, _To, <<"ping">>,
153
                   _Lang) ->
154
    {result, [?NS_COMMANDS]};
×
155
get_local_features(Acc, _From, _To, _Node, _Lang) ->
156
    Acc.
1✔
157

158
%-------------------------------------------------------------------------
159
-spec get_sm_features(mod_disco:features_acc(), jid(), jid(), binary(), binary()) -> mod_disco:features_acc().
160
get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
161
    Feats = case Acc of
32✔
162
              {result, I} -> I;
32✔
163
              _ -> []
×
164
            end,
165
    {result, Feats ++ [?NS_COMMANDS]};
32✔
166
get_sm_features(_Acc, _From, _To, ?NS_COMMANDS,
167
                _Lang) ->
168
    {result, []};
×
169
get_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc.
24✔
170

171
%-------------------------------------------------------------------------
172
-spec process_local_iq(iq()) -> iq() | ignore.
173
process_local_iq(IQ) ->
174
    process_adhoc_request(IQ, local).
24✔
175

176
-spec process_sm_iq(iq()) -> iq() | ignore.
177
process_sm_iq(IQ) ->
178
    process_adhoc_request(IQ, sm).
×
179

180
-spec process_adhoc_request(iq(), sm | local) -> iq() | ignore.
181
process_adhoc_request(#iq{from = From, to = To,
182
                          type = set, lang = Lang,
183
                          sub_els = [#adhoc_command{} = SubEl]} = IQ, Type) ->
184
    Host = To#jid.lserver,
24✔
185
    Res = case Type of
24✔
186
              local ->
187
                  ejabberd_hooks:run_fold(adhoc_local_commands, Host, empty,
24✔
188
                                          [From, To, fix_lang(Lang, SubEl)]);
189
              sm ->
190
                  ejabberd_hooks:run_fold(adhoc_sm_commands, Host, empty,
×
191
                                          [From, To, fix_lang(Lang, SubEl)])
192
          end,
193
    case Res of
24✔
194
        ignore ->
195
            ignore;
×
196
        empty ->
197
            Txt = ?T("No hook has processed this command"),
×
198
            xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
×
199
        {error, Error} ->
200
            xmpp:make_error(IQ, Error);
×
201
        Command ->
202
            xmpp:make_iq_result(IQ, Command)
24✔
203
    end;
204
process_adhoc_request(#iq{} = IQ, _Hooks) ->
205
    xmpp:make_error(IQ, xmpp:err_bad_request()).
×
206

207
-spec ping_item(mod_disco:items_acc(), jid(), jid(), binary()) -> {result, [disco_item()]}.
208
ping_item(Acc, _From, #jid{server = Server} = _To,
209
          Lang) ->
210
    Items = case Acc of
×
211
              {result, I} -> I;
×
212
              _ -> []
×
213
            end,
214
    Nodes = [#disco_item{jid = jid:make(Server),
×
215
                         node = <<"ping">>,
216
                         name = translate:translate(Lang, ?T("Ping"))}],
217
    {result, Items ++ Nodes}.
×
218

219
-spec ping_command(adhoc_command(), jid(), jid(), adhoc_command()) ->
220
                          adhoc_command() | {error, stanza_error()}.
221
ping_command(_Acc, _From, _To,
222
             #adhoc_command{lang = Lang, node = <<"ping">>,
223
                            action = Action} = Request) ->
224
    if Action == execute ->
×
225
            xmpp_util:make_adhoc_response(
×
226
              Request,
227
              #adhoc_command{
228
                 status = completed,
229
                 notes = [#adhoc_note{
230
                             type = info,
231
                             data = translate:translate(Lang, ?T("Pong"))}]});
232
       true ->
233
            Txt = ?T("Incorrect value of 'action' attribute"),
×
234
            {error, xmpp:err_bad_request(Txt, Lang)}
×
235
    end;
236
ping_command(Acc, _From, _To, _Request) -> Acc.
24✔
237

238
-spec fix_lang(binary(), adhoc_command()) -> adhoc_command().
239
fix_lang(Lang, #adhoc_command{lang = <<>>} = Cmd) ->
240
    Cmd#adhoc_command{lang = Lang};
24✔
241
fix_lang(_, Cmd) ->
242
    Cmd.
×
243

244
depends(_Host, _Opts) ->
245
    [].
117✔
246

247
mod_opt_type(report_commands_node) ->
248
    econf:bool().
117✔
249

250
mod_options(_Host) ->
251
    [{report_commands_node, false}].
117✔
252

253
mod_doc() ->
254
    #{desc =>
×
255
          [?T("def:ad-hoc command"), "",
256
           ?T(": Command that can be executed by an XMPP client using XEP-0050."), "",
257
           ?T("This module implements https://xmpp.org/extensions/xep-0050.html"
258
             "[XEP-0050: Ad-Hoc Commands]. It's an auxiliary module and is "
259
             "only needed by some of the other modules.")],
260
      opts =>
261
          [{report_commands_node,
262
            #{value => "true | false",
263
              desc =>
264
                  ?T("Provide the Commands item in the Service Discovery. "
265
                     "Default value: 'false'.")}}]}.
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc