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

processone / ejabberd / 603

17 Oct 2023 01:57PM UTC coverage: 32.654% (-0.4%) from 33.021%
603

push

github

badlop
Fixing minor typos in CHANGELOG

13497 of 41333 relevant lines covered (32.65%)

646.75 hits per line

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

67.63
/src/mod_privacy_sql.erl
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_privacy_sql.erl
3
%%% Author  : Evgeny Khramtsov <ekhramtsov@process-one.net>
4
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
5
%%%
6
%%%
7
%%% ejabberd, Copyright (C) 2002-2023   ProcessOne
8
%%%
9
%%% This program is free software; you can redistribute it and/or
10
%%% modify it under the terms of the GNU General Public License as
11
%%% published by the Free Software Foundation; either version 2 of the
12
%%% License, or (at your option) any later version.
13
%%%
14
%%% This program is distributed in the hope that it will be useful,
15
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
16
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
%%% General Public License for more details.
18
%%%
19
%%% You should have received a copy of the GNU General Public License along
20
%%% with this program; if not, write to the Free Software Foundation, Inc.,
21
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
%%%
23
%%%----------------------------------------------------------------------
24

25
-module(mod_privacy_sql).
26

27

28
-behaviour(mod_privacy).
29

30
%% API
31
-export([init/2, set_default/3, unset_default/2, set_lists/1,
32
         set_list/4, get_lists/2, get_list/3, remove_lists/2,
33
         remove_list/3, import/1, export/1]).
34

35
-export([item_to_raw/1, raw_to_item/1]).
36

37
-include_lib("xmpp/include/xmpp.hrl").
38
-include("mod_privacy.hrl").
39
-include("logger.hrl").
40
-include("ejabberd_sql_pt.hrl").
41

42
%%%===================================================================
43
%%% API
44
%%%===================================================================
45
init(Host, _Opts) ->
46
    ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()),
3✔
47
    ok.
3✔
48

49
schemas() ->
50
    [#sql_schema{
3✔
51
        version = 1,
52
        tables =
53
            [#sql_table{
54
                name = <<"privacy_default_list">>,
55
                columns =
56
                    [#sql_column{name = <<"username">>, type = text},
57
                     #sql_column{name = <<"server_host">>, type = text},
58
                     #sql_column{name = <<"name">>, type = text}],
59
                indices = [#sql_index{
60
                              columns = [<<"server_host">>, <<"username">>],
61
                              unique = true}]},
62
             #sql_table{
63
                name = <<"privacy_list">>,
64
                columns =
65
                    [#sql_column{name = <<"username">>, type = text},
66
                     #sql_column{name = <<"server_host">>, type = text},
67
                     #sql_column{name = <<"name">>, type = text},
68
                     #sql_column{name = <<"id">>, type = bigserial},
69
                     #sql_column{name = <<"created_at">>, type = timestamp,
70
                                 default = true}],
71
                indices = [#sql_index{
72
                              columns = [<<"id">>],
73
                              unique = true},
74
                           #sql_index{
75
                              columns = [<<"server_host">>, <<"username">>,
76
                                         <<"name">>],
77
                              unique = true}]},
78
             #sql_table{
79
                name = <<"privacy_list_data">>,
80
                columns =
81
                    [#sql_column{name = <<"id">>, type = bigint,
82
                                 opts = [#sql_references{
83
                                            table = <<"privacy_list">>,
84
                                            column = <<"id">>}]},
85
                     #sql_column{name = <<"t">>, type = {char, 1}},
86
                     #sql_column{name = <<"value">>, type = text},
87
                     #sql_column{name = <<"action">>, type = {char, 1}},
88
                     #sql_column{name = <<"ord">>, type = numeric},
89
                     #sql_column{name = <<"match_all">>, type = boolean},
90
                     #sql_column{name = <<"match_iq">>, type = boolean},
91
                     #sql_column{name = <<"match_message">>, type = boolean},
92
                     #sql_column{name = <<"match_presence_in">>, type = boolean},
93
                     #sql_column{name = <<"match_presence_out">>, type = boolean}],
94
                indices = [#sql_index{columns = [<<"id">>]}]}]}].
95

96
unset_default(LUser, LServer) ->
97
    case unset_default_privacy_list(LUser, LServer) of
3✔
98
        ok ->
99
            ok;
3✔
100
        _Err ->
101
            {error, db_failure}
×
102
    end.
103

104
set_default(LUser, LServer, Name) ->
105
    F = fun () ->
27✔
106
                case get_privacy_list_names_t(LUser, LServer) of
27✔
107
                    {selected, []} ->
108
                        {error, notfound};
3✔
109
                    {selected, Names} ->
110
                        case lists:member({Name}, Names) of
24✔
111
                            true ->
112
                                set_default_privacy_list(LUser, LServer, Name);
24✔
113
                            false ->
114
                                {error, notfound}
×
115
                        end
116
                end
117
        end,
118
    transaction(LServer, F).
27✔
119

120
remove_list(LUser, LServer, Name) ->
121
    F = fun () ->
9✔
122
                case get_default_privacy_list_t(LUser, LServer) of
9✔
123
                    {selected, []} ->
124
                        remove_privacy_list_t(LUser, LServer, Name);
6✔
125
                    {selected, [{Default}]} ->
126
                        if Name == Default ->
3✔
127
                                {error, conflict};
3✔
128
                           true ->
129
                                remove_privacy_list_t(LUser, LServer, Name)
×
130
                        end
131
                end
132
        end,
133
    transaction(LServer, F).
9✔
134

135
set_lists(#privacy{us = {LUser, LServer},
136
                   default = Default,
137
                   lists = Lists}) ->
138
    F = fun() ->
×
139
                lists:foreach(
×
140
                  fun({Name, List}) ->
141
                          add_privacy_list(LUser, LServer, Name),
×
142
                          {selected, [{I}]} =
×
143
                              get_privacy_list_id_t(LUser, LServer, Name),
144
                          RItems = lists:map(fun item_to_raw/1, List),
×
145
                          set_privacy_list(I, RItems),
×
146
                          if is_binary(Default) ->
×
147
                                  set_default_privacy_list(
×
148
                                    LUser, LServer, Default);
149
                             true ->
150
                                  ok
×
151
                          end
152
                  end, Lists)
153
        end,
154
    transaction(LServer, F).
×
155

156
set_list(LUser, LServer, Name, List) ->
157
    RItems = lists:map(fun item_to_raw/1, List),
231✔
158
    F = fun() ->
231✔
159
        {ID, New} = case get_privacy_list_id_t(LUser, LServer, Name) of
231✔
160
                        {selected, []} ->
161
                            add_privacy_list(LUser, LServer, Name),
222✔
162
                            {selected, [{I}]} =
222✔
163
                            get_privacy_list_id_t(LUser, LServer, Name),
164
                            {I, true};
222✔
165
                        {selected, [{I}]} -> {I, false}
9✔
166
                    end,
167
        case New of
231✔
168
            false ->
169
                set_privacy_list(ID, RItems);
9✔
170
            _ ->
171
                set_privacy_list_new(ID, RItems)
222✔
172
        end
173
        end,
174
    transaction(LServer, F).
231✔
175

176
get_list(LUser, LServer, default) ->
177
    case get_default_privacy_list(LUser, LServer) of
231✔
178
        {selected, []} ->
179
            error;
198✔
180
        {selected, [{Default}]} ->
181
            get_list(LUser, LServer, Default);
33✔
182
        _Err ->
183
            {error, db_failure}
×
184
    end;
185
get_list(LUser, LServer, Name) ->
186
    case get_privacy_list_data(LUser, LServer, Name) of
234✔
187
        {selected, []} ->
188
            error;
15✔
189
        {selected, RItems} ->
190
            {ok, {Name, lists:flatmap(fun raw_to_item/1, RItems)}};
219✔
191
        _Err ->
192
            {error, db_failure}
×
193
    end.
194

195
get_lists(LUser, LServer) ->
196
    case get_default_privacy_list(LUser, LServer) of
126✔
197
        {selected, Selected} ->
198
            Default = case Selected of
126✔
199
                          [] -> none;
102✔
200
                          [{DefName}] -> DefName
24✔
201
                      end,
202
            case get_privacy_list_names(LUser, LServer) of
126✔
203
                {selected, Names} ->
204
                    case lists:foldl(
126✔
205
                           fun(_, {error, _} = Err) ->
206
                                   Err;
×
207
                              ({Name}, Acc) ->
208
                                   case get_privacy_list_data(LUser, LServer, Name) of
222✔
209
                                       {selected, RItems} ->
210
                                           Items = lists:flatmap(
222✔
211
                                                     fun raw_to_item/1,
212
                                                     RItems),
213
                                           [{Name, Items}|Acc];
222✔
214
                                       _Err ->
215
                                           {error, db_failure}
×
216
                                   end
217
                           end, [], Names) of
218
                        {error, Reason} ->
219
                            {error, Reason};
×
220
                        Lists ->
221
                            {ok, #privacy{default = Default,
126✔
222
                                          us = {LUser, LServer},
223
                                          lists = Lists}}
224
                    end;
225
                _Err ->
226
                    {error, db_failure}
×
227
            end;
228
        _Err ->
229
            {error, db_failure}
×
230
    end.
231

232
remove_lists(LUser, LServer) ->
233
    case del_privacy_lists(LUser, LServer) of
120✔
234
        ok ->
235
            ok;
120✔
236
        _Err ->
237
            {error, db_failure}
×
238
    end.
239

240
export(Server) ->
241
    SqlType = ejabberd_option:sql_type(Server),
×
242
    case catch ejabberd_sql:sql_query(jid:nameprep(Server),
×
243
                                 [<<"select id from privacy_list order by "
244
                                    "id desc limit 1;">>]) of
245
        {selected, [<<"id">>], [[I]]} ->
246
            put(id, binary_to_integer(I));
×
247
        _ ->
248
            put(id, 0)
×
249
    end,
250
    [{privacy,
×
251
      fun(Host, #privacy{us = {LUser, LServer}, lists = Lists,
252
                         default = Default})
253
            when LServer == Host ->
254
              if Default /= none ->
255
                      [?SQL("delete from privacy_default_list where"
×
256
                            " username=%(LUser)s and %(LServer)H;"),
257
                       ?SQL_INSERT(
×
258
                          "privacy_default_list",
259
                          ["username=%(LUser)s",
260
                           "server_host=%(LServer)s",
261
                           "name=%(Default)s"])];
262
                 true ->
263
                      []
×
264
              end ++
×
265
                  lists:flatmap(
266
                    fun({Name, List}) ->
267
                            RItems = lists:map(fun item_to_raw/1, List),
×
268
                            ID = get_id(),
×
269
                            [?SQL("delete from privacy_list where"
×
270
                                  " username=%(LUser)s and %(LServer)H and"
271
                                  " name=%(Name)s;"),
272
                             ?SQL_INSERT(
×
273
                                "privacy_list",
274
                                ["username=%(LUser)s",
275
                                 "server_host=%(LServer)s",
276
                                 "name=%(Name)s",
277
                                 "id=%(ID)d"]),
278
                             ?SQL("delete from privacy_list_data where"
×
279
                                  " id=%(ID)d;")] ++
×
280
                            case SqlType of
281
                                pgsql ->
282
                                [?SQL("insert into privacy_list_data(id, t, "
×
283
                                      "value, action, ord, match_all, match_iq, "
284
                                      "match_message, match_presence_in, "
285
                                      "match_presence_out) "
286
                                      "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
287
                                      " %(Order)d, CAST(%(MatchAll)b as boolean), CAST(%(MatchIQ)b as boolean),"
288
                                      " CAST(%(MatchMessage)b as boolean), CAST(%(MatchPresenceIn)b as boolean),"
289
                                      " CAST(%(MatchPresenceOut)b as boolean));")
290
                                 || {SType, SValue, SAction, Order,
291
                                     MatchAll, MatchIQ,
292
                                     MatchMessage, MatchPresenceIn,
293
                                     MatchPresenceOut} <- RItems];
×
294
                                _ ->
295
                                [?SQL("insert into privacy_list_data(id, t, "
×
296
                                      "value, action, ord, match_all, match_iq, "
297
                                      "match_message, match_presence_in, "
298
                                      "match_presence_out) "
299
                                      "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
300
                                      " %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
301
                                      " %(MatchMessage)b, %(MatchPresenceIn)b,"
302
                                      " %(MatchPresenceOut)b);")
303
                                 || {SType, SValue, SAction, Order,
304
                                     MatchAll, MatchIQ,
305
                                     MatchMessage, MatchPresenceIn,
306
                                     MatchPresenceOut} <- RItems]
×
307
                            end
308
                    end,
309
                    Lists);
310
         (_Host, _R) ->
311
              []
×
312
      end}].
313

314
get_id() ->
315
    ID = get(id),
×
316
    put(id, ID + 1),
×
317
    ID + 1.
×
318

319
import(_) ->
320
    ok.
×
321

322
%%%===================================================================
323
%%% Internal functions
324
%%%===================================================================
325
transaction(LServer, F) ->
326
    case ejabberd_sql:sql_transaction(LServer, F) of
267✔
327
        {atomic, Res} -> Res;
267✔
328
        {aborted, _Reason} -> {error, db_failure}
×
329
    end.
330

331
raw_to_item({SType, SValue, SAction, Order, MatchAll,
332
             MatchIQ, MatchMessage, MatchPresenceIn,
333
             MatchPresenceOut} = Row) ->
334
    try
471✔
335
        {Type, Value} = case SType of
471✔
336
                            <<"n">> -> {none, none};
93✔
337
                            <<"j">> ->
338
                                JID = jid:decode(SValue),
168✔
339
                                {jid, jid:tolower(JID)};
168✔
340
                            <<"g">> -> {group, SValue};
42✔
341
                            <<"s">> ->
342
                                case SValue of
168✔
343
                                    <<"none">> -> {subscription, none};
42✔
344
                                    <<"both">> -> {subscription, both};
42✔
345
                                    <<"from">> -> {subscription, from};
42✔
346
                                    <<"to">> -> {subscription, to}
42✔
347
                                end
348
                        end,
349
        Action = case SAction of
471✔
350
                     <<"a">> -> allow;
12✔
351
                     <<"d">> -> deny
459✔
352
                 end,
353
        [#listitem{type = Type, value = Value, action = Action,
471✔
354
                   order = Order, match_all = MatchAll, match_iq = MatchIQ,
355
                   match_message = MatchMessage,
356
                   match_presence_in = MatchPresenceIn,
357
                   match_presence_out = MatchPresenceOut}]
358
    catch _:_ ->
359
            ?WARNING_MSG("Failed to parse row: ~p", [Row]),
×
360
            []
×
361
    end.
362

363
item_to_raw(#listitem{type = Type, value = Value,
364
                      action = Action, order = Order, match_all = MatchAll,
365
                      match_iq = MatchIQ, match_message = MatchMessage,
366
                      match_presence_in = MatchPresenceIn,
367
                      match_presence_out = MatchPresenceOut}) ->
368
    {SType, SValue} = case Type of
243✔
369
                        none -> {<<"n">>, <<"">>};
48✔
370
                        jid -> {<<"j">>, jid:encode(Value)};
90✔
371
                        group -> {<<"g">>, Value};
21✔
372
                        subscription ->
373
                            case Value of
84✔
374
                              none -> {<<"s">>, <<"none">>};
21✔
375
                              both -> {<<"s">>, <<"both">>};
21✔
376
                              from -> {<<"s">>, <<"from">>};
21✔
377
                              to -> {<<"s">>, <<"to">>}
21✔
378
                            end
379
                      end,
380
    SAction = case Action of
243✔
381
                allow -> <<"a">>;
6✔
382
                deny -> <<"d">>
237✔
383
              end,
384
    {SType, SValue, SAction, Order, MatchAll, MatchIQ,
243✔
385
     MatchMessage, MatchPresenceIn, MatchPresenceOut}.
386

387
get_default_privacy_list(LUser, LServer) ->
388
    ejabberd_sql:sql_query(
357✔
389
      LServer,
390
      ?SQL("select @(name)s from privacy_default_list "
357✔
391
           "where username=%(LUser)s and %(LServer)H")).
392

393
get_default_privacy_list_t(LUser, LServer) ->
394
    ejabberd_sql:sql_query_t(
9✔
395
      ?SQL("select @(name)s from privacy_default_list "
9✔
396
           "where username=%(LUser)s and %(LServer)H")).
397

398
get_privacy_list_names(LUser, LServer) ->
399
    ejabberd_sql:sql_query(
126✔
400
      LServer,
401
      ?SQL("select @(name)s from privacy_list"
126✔
402
           " where username=%(LUser)s and %(LServer)H")).
403

404
get_privacy_list_names_t(LUser, LServer) ->
405
    ejabberd_sql:sql_query_t(
27✔
406
      ?SQL("select @(name)s from privacy_list"
27✔
407
           " where username=%(LUser)s and %(LServer)H")).
408

409
get_privacy_list_id_t(LUser, LServer, Name) ->
410
    ejabberd_sql:sql_query_t(
453✔
411
      ?SQL("select @(id)d from privacy_list"
453✔
412
           " where username=%(LUser)s and %(LServer)H and name=%(Name)s")).
413

414
get_privacy_list_data(LUser, LServer, Name) ->
415
    ejabberd_sql:sql_query(
456✔
416
      LServer,
417
      ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
456✔
418
           "@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
419
           "@(match_presence_out)b from privacy_list_data "
420
           "where id ="
421
           " (select id from privacy_list"
422
           " where username=%(LUser)s and %(LServer)H and name=%(Name)s) "
423
           "order by ord")).
424

425
set_default_privacy_list(LUser, LServer, Name) ->
426
    ?SQL_UPSERT_T(
24✔
427
       "privacy_default_list",
428
       ["!username=%(LUser)s",
429
        "!server_host=%(LServer)s",
430
        "name=%(Name)s"]).
431

432
unset_default_privacy_list(LUser, LServer) ->
433
    case ejabberd_sql:sql_query(
3✔
434
           LServer,
435
           ?SQL("delete from privacy_default_list"
3✔
436
                " where username=%(LUser)s and %(LServer)H")) of
437
        {updated, _} -> ok;
3✔
438
        Err -> Err
×
439
    end.
440

441
remove_privacy_list_t(LUser, LServer, Name) ->
442
    case ejabberd_sql:sql_query_t(
6✔
443
           ?SQL("delete from privacy_list where"
6✔
444
                " username=%(LUser)s and %(LServer)H and name=%(Name)s")) of
445
        {updated, 0} -> {error, notfound};
3✔
446
        {updated, _} -> ok;
3✔
447
        Err -> Err
×
448
    end.
449

450
add_privacy_list(LUser, LServer, Name) ->
451
    ejabberd_sql:sql_query_t(
222✔
452
      ?SQL_INSERT(
222✔
453
         "privacy_list",
454
         ["username=%(LUser)s",
455
          "server_host=%(LServer)s",
456
          "name=%(Name)s"])).
457

458
set_privacy_list_new(ID, RItems) ->
459
    lists:foreach(
222✔
460
        fun({SType, SValue, SAction, Order, MatchAll, MatchIQ,
461
             MatchMessage, MatchPresenceIn, MatchPresenceOut}) ->
462
            ejabberd_sql:sql_query_t(
243✔
463
                ?SQL("insert into privacy_list_data(id, t, "
326✔
464
                     "value, action, ord, match_all, match_iq, "
465
                     "match_message, match_presence_in, match_presence_out) "
466
                     "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
467
                     " %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
468
                     " %(MatchMessage)b, %(MatchPresenceIn)b,"
469
                     " %(MatchPresenceOut)b)"))
470
        end,
471
        RItems).
472

473
calculate_difference(List1, List2) ->
474
    Set1 = gb_sets:from_list(List1),
9✔
475
    Set2 = gb_sets:from_list(List2),
9✔
476
    {gb_sets:to_list(gb_sets:subtract(Set1, Set2)),
9✔
477
     gb_sets:to_list(gb_sets:subtract(Set2, Set1))}.
478

479
set_privacy_list(ID, RItems) ->
480
    case ejabberd_sql:sql_query_t(
9✔
481
        ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, "
14✔
482
             "@(match_iq)b, @(match_message)b, @(match_presence_in)b, "
483
             "@(match_presence_out)b from privacy_list_data "
484
             "where id=%(ID)d")) of
485
        {selected, ExistingItems} ->
486
            {ToAdd2, ToDelete2} = calculate_difference(RItems, ExistingItems),
9✔
487
            ToAdd3 = if
9✔
488
                         ToDelete2 /= [] ->
489
                             ejabberd_sql:sql_query_t(
9✔
490
                                 ?SQL("delete from privacy_list_data where id=%(ID)d")),
14✔
491
                             RItems;
9✔
492
                         true ->
493
                             ToAdd2
×
494
                     end,
495
            lists:foreach(
9✔
496
                fun({SType, SValue, SAction, Order, MatchAll, MatchIQ,
497
                     MatchMessage, MatchPresenceIn, MatchPresenceOut}) ->
498
                    ejabberd_sql:sql_query_t(
×
499
                        ?SQL("insert into privacy_list_data(id, t, "
×
500
                             "value, action, ord, match_all, match_iq, "
501
                             "match_message, match_presence_in, match_presence_out) "
502
                             "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s,"
503
                             " %(Order)d, %(MatchAll)b, %(MatchIQ)b,"
504
                             " %(MatchMessage)b, %(MatchPresenceIn)b,"
505
                             " %(MatchPresenceOut)b)"))
506
                end,
507
                ToAdd3);
508
        Err ->
509
            Err
×
510
    end.
511

512
del_privacy_lists(LUser, LServer) ->
513
    case ejabberd_sql:sql_query(
120✔
514
           LServer,
515
           ?SQL("delete from privacy_list where username=%(LUser)s and %(LServer)H")) of
120✔
516
        {updated, _} ->
517
            case ejabberd_sql:sql_query(
120✔
518
                   LServer,
519
                   ?SQL("delete from privacy_default_list "
120✔
520
                        "where username=%(LUser)s and %(LServer)H")) of
521
                {updated, _} -> ok;
120✔
522
                Err -> Err
×
523
            end;
524
        Err ->
525
            Err
×
526
    end.
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