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

processone / ejabberd / 1212

18 Nov 2025 12:37PM UTC coverage: 33.784% (+0.003%) from 33.781%
1212

push

github

badlop
mod_conversejs: Improve link to conversejs in WebAdmin (#4495)

Until now, the WebAdmin menu included a link to the first request handler
with mod_conversejs that the admin configured in ejabberd.yml
That link included the authentication credentials hashed as URI arguments
if using HTTPS. Then process/2 extracted those arguments and passed them
as autologin options to Converse.

From now, mod_conversejs automatically adds a request_handler nested in
webadmin subpath. The webadmin menu links to that converse URI; this allows
to access the HTTP auth credentials, no need to explicitly pass them.
process/2 extracts this HTTP auth and passes autologin options to Converse.
Now scram password storage is supported too.

This minimum configuration allows WebAdmin to access Converse:

listen:
  -
    port: 5443
    module: ejabberd_http
    tls: true
    request_handlers:
      /admin: ejabberd_web_admin
      /ws: ejabberd_http_ws
modules:
  mod_conversejs:
    conversejs_resources: "/home/conversejs/12.0.0/dist"

0 of 12 new or added lines in 1 file covered. (0.0%)

11290 existing lines in 174 files now uncovered.

15515 of 45924 relevant lines covered (33.78%)

1277.8 hits per line

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

66.99
/src/mod_blocking.erl
1
%%%----------------------------------------------------------------------
2
%%% File    : mod_blocking.erl
3
%%% Author  : Stephan Maka
4
%%% Purpose : XEP-0191: Simple Communications Blocking
5
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
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_blocking).
27

28
-behaviour(gen_mod).
29

30
-protocol({xep, 191, '1.2', '2.1.7', "complete", ""}).
31

32
-export([start/2, stop/1, reload/3, process_iq/1, depends/2,
33
         disco_features/5, mod_options/1, mod_doc/0]).
34

35
-include("logger.hrl").
36
-include_lib("xmpp/include/xmpp.hrl").
37
-include("mod_privacy.hrl").
38
-include("translate.hrl").
39

40
start(_Host, _Opts) ->
UNCOV
41
    {ok, [{hook, disco_local_features, disco_features, 50},
9✔
42
          {iq_handler, ejabberd_sm, ?NS_BLOCKING, process_iq}]}.
43

44
stop(_Host) ->
UNCOV
45
    ok.
9✔
46

47
reload(_Host, _NewOpts, _OldOpts) ->
48
    ok.
×
49

50
depends(_Host, _Opts) ->
UNCOV
51
    [{mod_privacy, hard}].
9✔
52

53
-spec disco_features({error, stanza_error()} | {result, [binary()]} | empty,
54
                     jid(), jid(), binary(), binary()) ->
55
                            {error, stanza_error()} | {result, [binary()]}.
56
disco_features({error, Err}, _From, _To, _Node, _Lang) ->
57
    {error, Err};
×
58
disco_features(empty, _From, _To, <<"">>, _Lang) ->
UNCOV
59
    {result, [?NS_BLOCKING]};
2,101✔
60
disco_features({result, Feats}, _From, _To, <<"">>, _Lang) ->
61
    {result, [?NS_BLOCKING|Feats]};
×
62
disco_features(Acc, _From, _To, _Node, _Lang) ->
63
    Acc.
×
64

65
-spec process_iq(iq()) -> iq().
66
process_iq(#iq{type = Type,
67
               from = #jid{luser = U, lserver = S},
68
               to = #jid{luser = U, lserver = S}} = IQ) ->
UNCOV
69
    case Type of
144✔
UNCOV
70
        get -> process_iq_get(IQ);
54✔
UNCOV
71
        set -> process_iq_set(IQ)
90✔
72
    end;
73
process_iq(#iq{lang = Lang} = IQ) ->
74
    Txt = ?T("Query to another users is forbidden"),
×
75
    xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)).
×
76

77
-spec process_iq_get(iq()) -> iq().
78
process_iq_get(#iq{sub_els = [#block_list{}]} = IQ) ->
UNCOV
79
    process_get(IQ);
18✔
80
process_iq_get(#iq{lang = Lang} = IQ) ->
UNCOV
81
    Txt = ?T("No module is handling this query"),
36✔
UNCOV
82
    xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
36✔
83

84
-spec process_iq_set(iq()) -> iq().
85
process_iq_set(#iq{lang = Lang, sub_els = [SubEl]} = IQ) ->
UNCOV
86
    case SubEl of
90✔
87
        #block{items = []} ->
UNCOV
88
            Txt = ?T("No items found in this query"),
9✔
UNCOV
89
            xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
9✔
90
        #block{items = Items} ->
UNCOV
91
            JIDs = [jid:tolower(JID) || #block_item{jid = JID} <- Items],
36✔
UNCOV
92
            process_block(IQ, JIDs);
36✔
93
        #unblock{items = []} ->
UNCOV
94
            process_unblock_all(IQ);
9✔
95
        #unblock{items = Items} ->
UNCOV
96
            JIDs = [jid:tolower(JID) || #block_item{jid = JID} <- Items],
18✔
UNCOV
97
            process_unblock(IQ, JIDs);
18✔
98
        _ ->
UNCOV
99
            Txt = ?T("No module is handling this query"),
18✔
UNCOV
100
            xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang))
18✔
101
    end.
102

103
-spec listitems_to_jids([listitem()], [ljid()]) -> [ljid()].
104
listitems_to_jids([], JIDs) ->
UNCOV
105
    JIDs;
47✔
106
listitems_to_jids([#listitem{type = jid,
107
                             action = deny, value = JID} = Item | Items],
108
                       JIDs) ->
UNCOV
109
    Match = case Item of
18✔
110
                #listitem{match_all = true} ->
UNCOV
111
                    true;
18✔
112
                #listitem{match_iq = true,
113
                          match_message = true,
114
                          match_presence_in = true,
115
                          match_presence_out = true} ->
116
                    true;
×
117
                _ ->
118
                    false
×
119
            end,
UNCOV
120
    if Match -> listitems_to_jids(Items, [JID | JIDs]);
18✔
121
       true -> listitems_to_jids(Items, JIDs)
×
122
    end;
123
% Skip Privacy List items than cannot be mapped to Blocking items
124
listitems_to_jids([_ | Items], JIDs) ->
125
    listitems_to_jids(Items, JIDs).
×
126

127
-spec process_block(iq(), [ljid()]) -> iq().
128
process_block(#iq{from = From} = IQ, LJIDs) ->
UNCOV
129
    #jid{luser = LUser, lserver = LServer} = From,
36✔
UNCOV
130
    case mod_privacy:get_user_list(LUser, LServer, default) of
36✔
131
        {error, _} ->
132
            err_db_failure(IQ);
×
133
        Res ->
UNCOV
134
            {Name, List} = case Res of
36✔
UNCOV
135
                               error -> {<<"Blocked contacts">>, []};
36✔
136
                               {ok, NameList} -> NameList
×
137
                           end,
UNCOV
138
            AlreadyBlocked = listitems_to_jids(List, []),
36✔
UNCOV
139
            NewList = lists:foldr(
36✔
140
                        fun(LJID, List1) ->
UNCOV
141
                                case lists:member(LJID, AlreadyBlocked) of
45✔
142
                                    true ->
143
                                        List1;
×
144
                                    false ->
UNCOV
145
                                        [#listitem{type = jid,
45✔
146
                                                   value = LJID,
147
                                                   action = deny,
148
                                                   order = 0,
149
                                                   match_all = true}|List1]
150
                                end
151
                        end, List, LJIDs),
UNCOV
152
            case mod_privacy:set_list(LUser, LServer, Name, NewList) of
36✔
153
                ok ->
UNCOV
154
                    case (if Res == error ->
36✔
UNCOV
155
                                  mod_privacy:set_default_list(
36✔
156
                                    LUser, LServer, Name);
157
                             true ->
158
                                  ok
×
159
                          end) of
160
                        ok ->
UNCOV
161
                            mod_privacy:push_list_update(From, Name),
36✔
UNCOV
162
                            Items = [#block_item{jid = jid:make(LJID)}
36✔
UNCOV
163
                                     || LJID <- LJIDs],
36✔
UNCOV
164
                            broadcast_event(From, #block{items = Items}),
36✔
UNCOV
165
                            xmpp:make_iq_result(IQ);
36✔
166
                        {error, notfound} ->
167
                            ?ERROR_MSG("Failed to set default list '~ts': "
×
168
                                       "the list should exist, but not found",
169
                                       [Name]),
×
170
                            err_db_failure(IQ);
×
171
                        {error, _} ->
172
                            err_db_failure(IQ)
×
173
                    end;
174
                {error, _} ->
175
                    err_db_failure(IQ)
×
176
            end
177
    end.
178

179
-spec process_unblock_all(iq()) -> iq().
180
process_unblock_all(#iq{from = From} = IQ) ->
UNCOV
181
    #jid{luser = LUser, lserver = LServer} = From,
9✔
UNCOV
182
    case mod_privacy:get_user_list(LUser, LServer, default) of
9✔
183
        {ok, {Name, List}} ->
UNCOV
184
            NewList = lists:filter(
9✔
185
                        fun(#listitem{action = A}) ->
UNCOV
186
                                A /= deny
9✔
187
                        end, List),
UNCOV
188
            case mod_privacy:set_list(LUser, LServer, Name, NewList) of
9✔
189
                ok ->
UNCOV
190
                    mod_privacy:push_list_update(From, Name),
9✔
UNCOV
191
                    broadcast_event(From, #unblock{}),
9✔
UNCOV
192
                    xmpp:make_iq_result(IQ);
9✔
193
                {error, _} ->
194
                    err_db_failure(IQ)
×
195
            end;
196
        error ->
197
            broadcast_event(From, #unblock{}),
×
198
            xmpp:make_iq_result(IQ);
×
199
        {error, _} ->
200
            err_db_failure(IQ)
×
201
    end.
202

203
-spec process_unblock(iq(), [ljid()]) -> iq().
204
process_unblock(#iq{from = From} = IQ, LJIDs) ->
UNCOV
205
    #jid{luser = LUser, lserver = LServer} = From,
18✔
UNCOV
206
    case mod_privacy:get_user_list(LUser, LServer, default) of
18✔
207
        {ok, {Name, List}} ->
UNCOV
208
            NewList = lists:filter(
18✔
209
                        fun(#listitem{action = deny, type = jid,
210
                                      value = LJID}) ->
UNCOV
211
                                not lists:member(LJID, LJIDs);
27✔
212
                           (_) ->
213
                                true
×
214
                        end, List),
UNCOV
215
            case mod_privacy:set_list(LUser, LServer, Name, NewList) of
18✔
216
                ok ->
UNCOV
217
                    mod_privacy:push_list_update(From, Name),
18✔
UNCOV
218
                    Items = [#block_item{jid = jid:make(LJID)}
18✔
UNCOV
219
                             || LJID <- LJIDs],
18✔
UNCOV
220
                    broadcast_event(From, #unblock{items = Items}),
18✔
UNCOV
221
                    xmpp:make_iq_result(IQ);
18✔
222
                {error, _} ->
223
                    err_db_failure(IQ)
×
224
            end;
225
        error ->
226
            Items = [#block_item{jid = jid:make(LJID)}
×
227
                     || LJID <- LJIDs],
×
228
            broadcast_event(From, #unblock{items = Items}),
×
229
            xmpp:make_iq_result(IQ);
×
230
        {error, _} ->
231
            err_db_failure(IQ)
×
232
    end.
233

234
-spec broadcast_event(jid(), block() | unblock()) -> ok.
235
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
UNCOV
236
    BFrom = jid:remove_resource(From),
63✔
UNCOV
237
    lists:foreach(
63✔
238
      fun(R) ->
UNCOV
239
              To = jid:replace_resource(From, R),
63✔
UNCOV
240
              IQ = #iq{type = set, from = BFrom, to = To,
63✔
241
                       id = <<"push", (p1_rand:get_string())/binary>>,
242
                       sub_els = [Event]},
UNCOV
243
              ejabberd_router:route(IQ)
63✔
244
      end, ejabberd_sm:get_user_resources(LUser, LServer)).
245

246
-spec process_get(iq()) -> iq().
247
process_get(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) ->
UNCOV
248
    case mod_privacy:get_user_list(LUser, LServer, default) of
18✔
249
        {ok, {_, List}} ->
UNCOV
250
            LJIDs = listitems_to_jids(List, []),
11✔
UNCOV
251
            Items = [#block_item{jid = jid:make(J)} || J <- LJIDs],
11✔
UNCOV
252
            xmpp:make_iq_result(IQ, #block_list{items = Items});
11✔
253
        error ->
UNCOV
254
            xmpp:make_iq_result(IQ, #block_list{});
7✔
255
        {error, _} ->
256
            err_db_failure(IQ)
×
257
    end.
258

259
-spec err_db_failure(iq()) -> iq().
260
err_db_failure(#iq{lang = Lang} = IQ) ->
261
    Txt = ?T("Database failure"),
×
262
    xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)).
×
263

264
mod_options(_Host) ->
UNCOV
265
    [].
9✔
266

267
mod_doc() ->
268
    #{desc =>
×
269
          [?T("The module implements "
270
              "https://xmpp.org/extensions/xep-0191.html"
271
              "[XEP-0191: Blocking Command]."), "",
272
           ?T("This module depends on _`mod_privacy`_ where "
273
              "all the configuration is performed.")]}.
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