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

processone / ejabberd / 1173

28 Oct 2025 11:02AM UTC coverage: 33.768% (-0.02%) from 33.79%
1173

push

github

badlop
CHANGELOG.md: Update to 25.10

15513 of 45940 relevant lines covered (33.77%)

1078.01 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) ->
41
    {ok, [{hook, disco_local_features, disco_features, 50},
8✔
42
          {iq_handler, ejabberd_sm, ?NS_BLOCKING, process_iq}]}.
43

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

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

50
depends(_Host, _Opts) ->
51
    [{mod_privacy, hard}].
8✔
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) ->
59
    {result, [?NS_BLOCKING]};
1,872✔
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) ->
69
    case Type of
128✔
70
        get -> process_iq_get(IQ);
48✔
71
        set -> process_iq_set(IQ)
80✔
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) ->
79
    process_get(IQ);
16✔
80
process_iq_get(#iq{lang = Lang} = IQ) ->
81
    Txt = ?T("No module is handling this query"),
32✔
82
    xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
32✔
83

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

103
-spec listitems_to_jids([listitem()], [ljid()]) -> [ljid()].
104
listitems_to_jids([], JIDs) ->
105
    JIDs;
42✔
106
listitems_to_jids([#listitem{type = jid,
107
                             action = deny, value = JID} = Item | Items],
108
                       JIDs) ->
109
    Match = case Item of
16✔
110
                #listitem{match_all = true} ->
111
                    true;
16✔
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,
120
    if Match -> listitems_to_jids(Items, [JID | JIDs]);
16✔
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) ->
129
    #jid{luser = LUser, lserver = LServer} = From,
32✔
130
    case mod_privacy:get_user_list(LUser, LServer, default) of
32✔
131
        {error, _} ->
132
            err_db_failure(IQ);
×
133
        Res ->
134
            {Name, List} = case Res of
32✔
135
                               error -> {<<"Blocked contacts">>, []};
32✔
136
                               {ok, NameList} -> NameList
×
137
                           end,
138
            AlreadyBlocked = listitems_to_jids(List, []),
32✔
139
            NewList = lists:foldr(
32✔
140
                        fun(LJID, List1) ->
141
                                case lists:member(LJID, AlreadyBlocked) of
40✔
142
                                    true ->
143
                                        List1;
×
144
                                    false ->
145
                                        [#listitem{type = jid,
40✔
146
                                                   value = LJID,
147
                                                   action = deny,
148
                                                   order = 0,
149
                                                   match_all = true}|List1]
150
                                end
151
                        end, List, LJIDs),
152
            case mod_privacy:set_list(LUser, LServer, Name, NewList) of
32✔
153
                ok ->
154
                    case (if Res == error ->
32✔
155
                                  mod_privacy:set_default_list(
32✔
156
                                    LUser, LServer, Name);
157
                             true ->
158
                                  ok
×
159
                          end) of
160
                        ok ->
161
                            mod_privacy:push_list_update(From, Name),
32✔
162
                            Items = [#block_item{jid = jid:make(LJID)}
32✔
163
                                     || LJID <- LJIDs],
32✔
164
                            broadcast_event(From, #block{items = Items}),
32✔
165
                            xmpp:make_iq_result(IQ);
32✔
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) ->
181
    #jid{luser = LUser, lserver = LServer} = From,
8✔
182
    case mod_privacy:get_user_list(LUser, LServer, default) of
8✔
183
        {ok, {Name, List}} ->
184
            NewList = lists:filter(
8✔
185
                        fun(#listitem{action = A}) ->
186
                                A /= deny
8✔
187
                        end, List),
188
            case mod_privacy:set_list(LUser, LServer, Name, NewList) of
8✔
189
                ok ->
190
                    mod_privacy:push_list_update(From, Name),
8✔
191
                    broadcast_event(From, #unblock{}),
8✔
192
                    xmpp:make_iq_result(IQ);
8✔
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) ->
205
    #jid{luser = LUser, lserver = LServer} = From,
16✔
206
    case mod_privacy:get_user_list(LUser, LServer, default) of
16✔
207
        {ok, {Name, List}} ->
208
            NewList = lists:filter(
16✔
209
                        fun(#listitem{action = deny, type = jid,
210
                                      value = LJID}) ->
211
                                not lists:member(LJID, LJIDs);
24✔
212
                           (_) ->
213
                                true
×
214
                        end, List),
215
            case mod_privacy:set_list(LUser, LServer, Name, NewList) of
16✔
216
                ok ->
217
                    mod_privacy:push_list_update(From, Name),
16✔
218
                    Items = [#block_item{jid = jid:make(LJID)}
16✔
219
                             || LJID <- LJIDs],
16✔
220
                    broadcast_event(From, #unblock{items = Items}),
16✔
221
                    xmpp:make_iq_result(IQ);
16✔
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) ->
236
    BFrom = jid:remove_resource(From),
56✔
237
    lists:foreach(
56✔
238
      fun(R) ->
239
              To = jid:replace_resource(From, R),
56✔
240
              IQ = #iq{type = set, from = BFrom, to = To,
56✔
241
                       id = <<"push", (p1_rand:get_string())/binary>>,
242
                       sub_els = [Event]},
243
              ejabberd_router:route(IQ)
56✔
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) ->
248
    case mod_privacy:get_user_list(LUser, LServer, default) of
16✔
249
        {ok, {_, List}} ->
250
            LJIDs = listitems_to_jids(List, []),
10✔
251
            Items = [#block_item{jid = jid:make(J)} || J <- LJIDs],
10✔
252
            xmpp:make_iq_result(IQ, #block_list{items = Items});
10✔
253
        error ->
254
            xmpp:make_iq_result(IQ, #block_list{});
6✔
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) ->
265
    [].
8✔
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