• 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

53.09
/src/mod_roster_sql.erl
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_roster_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_roster_sql).
26

27

28
-behaviour(mod_roster).
29

30
%% API
31
-export([init/2, read_roster_version/2, write_roster_version/4,
32
         get_roster/2, get_roster_item/3, roster_subscribe/4,
33
         read_subscription_and_groups/3, remove_user/2,
34
         update_roster/4, del_roster/3, transaction/2,
35
         process_rosteritems/5,
36
         import/3, export/1, raw_to_record/2]).
37

38
-include("mod_roster.hrl").
39
-include("ejabberd_sql_pt.hrl").
40
-include("logger.hrl").
41
-include_lib("xmpp/include/jid.hrl").
42

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

50
schemas() ->
51
    [#sql_schema{
3✔
52
        version = 1,
53
        tables =
54
            [#sql_table{
55
                name = <<"rosterusers">>,
56
                columns =
57
                    [#sql_column{name = <<"username">>, type = text},
58
                     #sql_column{name = <<"server_host">>, type = text},
59
                     #sql_column{name = <<"jid">>, type = text},
60
                     #sql_column{name = <<"nick">>, type = text},
61
                     #sql_column{name = <<"subscription">>, type = {char, 1}},
62
                     #sql_column{name = <<"ask">>, type = {char, 1}},
63
                     #sql_column{name = <<"askmessage">>, type = text},
64
                     #sql_column{name = <<"server">>, type = {char, 1}},
65
                     #sql_column{name = <<"subscribe">>, type = text},
66
                     #sql_column{name = <<"type">>, type = text},
67
                     #sql_column{name = <<"created_at">>, type = timestamp,
68
                                 default = true}],
69
                indices = [#sql_index{
70
                              columns = [<<"server_host">>, <<"username">>,
71
                                         <<"jid">>],
72
                              unique = true},
73
                           #sql_index{
74
                              columns = [<<"server_host">>, <<"jid">>]}]},
75
             #sql_table{
76
                name = <<"rostergroups">>,
77
                columns =
78
                    [#sql_column{name = <<"username">>, type = text},
79
                     #sql_column{name = <<"server_host">>, type = text},
80
                     #sql_column{name = <<"jid">>, type = text},
81
                     #sql_column{name = <<"grp">>, type = text}],
82
                indices = [#sql_index{
83
                              columns = [<<"server_host">>, <<"username">>,
84
                                         <<"jid">>]}]},
85
             #sql_table{
86
                name = <<"roster_version">>,
87
                columns =
88
                    [#sql_column{name = <<"username">>, type = text},
89
                     #sql_column{name = <<"server_host">>, type = text},
90
                     #sql_column{name = <<"version">>, type = text}],
91
                indices = [#sql_index{
92
                              columns = [<<"server_host">>, <<"username">>],
93
                              unique = true}]}]}].
94

95
read_roster_version(LUser, LServer) ->
96
    case ejabberd_sql:sql_query(
1,191✔
97
           LServer,
98
           ?SQL("select @(version)s from roster_version"
1,191✔
99
                " where username = %(LUser)s and %(LServer)H")) of
100
        {selected, [{Version}]} -> {ok, Version};
1,191✔
101
        {selected, []} -> error;
×
102
        _ -> {error, db_failure}
×
103
    end.
104

105
write_roster_version(LUser, LServer, InTransaction, Ver) ->
106
    if InTransaction ->
537✔
107
            set_roster_version(LUser, LServer, Ver);
537✔
108
       true ->
109
            transaction(
×
110
              LServer,
111
              fun () -> set_roster_version(LUser, LServer, Ver) end)
×
112
    end.
113

114
get_roster(LUser, LServer) ->
115
    case ejabberd_sql:sql_query(
1,209✔
116
           LServer,
117
           ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s, "
1,209✔
118
                "@(ask)s, @(askmessage)s, @(server)s, @(subscribe)s, "
119
                "@(type)s from rosterusers "
120
                "where username=%(LUser)s and %(LServer)H")) of
121
        {selected, Items} when is_list(Items) ->
122
            JIDGroups = case get_roster_jid_groups(LServer, LUser) of
1,209✔
123
                            {selected, JGrps} when is_list(JGrps) ->
124
                                JGrps;
1,209✔
125
                            _ ->
126
                                []
×
127
                        end,
128
            GroupsDict = lists:foldl(fun({J, G}, Acc) ->
1,209✔
129
                                             Gs = maps:get(J, Acc, []),
21✔
130
                                             maps:put(J, [G | Gs], Acc)
21✔
131
                                     end,
132
                                     maps:new(), JIDGroups),
133
            {ok, lists:flatmap(
1,209✔
134
                   fun(I) ->
135
                           case raw_to_record(LServer, I) of
795✔
136
                               %% Bad JID in database:
137
                               error -> [];
×
138
                               R ->
139
                                   SJID = jid:encode(R#roster.jid),
795✔
140
                                   Groups = maps:get(SJID, GroupsDict, []),
795✔
141
                                   [R#roster{groups = Groups}]
795✔
142
                           end
143
                   end, Items)};
144
        _ ->
145
            error
×
146
    end.
147

148
roster_subscribe(_LUser, _LServer, _LJID, Item) ->
149
    ItemVals = record_to_row(Item),
402✔
150
    roster_subscribe(ItemVals).
402✔
151

152
transaction(LServer, F) ->
153
    ejabberd_sql:sql_transaction(LServer, F).
1,461✔
154

155
get_roster_item(LUser, LServer, LJID) ->
156
    SJID = jid:encode(LJID),
1,275✔
157
    case get_roster_by_jid(LServer, LUser, SJID) of
1,275✔
158
        {selected, [I]} ->
159
            case raw_to_record(LServer, I) of
882✔
160
                error ->
161
                    error;
×
162
                R ->
163
                    Groups = case get_roster_groups(LServer, LUser, SJID) of
882✔
164
                                 {selected, JGrps} when is_list(JGrps) ->
165
                                     [JGrp || {JGrp} <- JGrps];
882✔
166
                                 _ -> []
×
167
                             end,
168
                    {ok, R#roster{groups = Groups}}
882✔
169
            end;
170
        {selected, []} ->
171
            error
393✔
172
    end.
173

174
remove_user(LUser, LServer) ->
175
    transaction(
6✔
176
      LServer,
177
      fun () ->
178
              ejabberd_sql:sql_query_t(
6✔
179
                ?SQL("delete from rosterusers"
6✔
180
                     " where username=%(LUser)s and %(LServer)H")),
181
              ejabberd_sql:sql_query_t(
6✔
182
                ?SQL("delete from rostergroups"
6✔
183
                     " where username=%(LUser)s and %(LServer)H"))
184
      end),
185
    ok.
6✔
186

187
update_roster(LUser, LServer, LJID, Item) ->
188
    SJID = jid:encode(LJID),
99✔
189
    ItemVals = record_to_row(Item),
99✔
190
    ItemGroups = Item#roster.groups,
99✔
191
    roster_subscribe(ItemVals),
99✔
192
    ejabberd_sql:sql_query_t(
99✔
193
      ?SQL("delete from rostergroups"
99✔
194
           " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")),
195
    lists:foreach(
99✔
196
      fun(ItemGroup) ->
197
              ejabberd_sql:sql_query_t(
9✔
198
                ?SQL_INSERT(
9✔
199
                   "rostergroups",
200
                   ["username=%(LUser)s",
201
                    "server_host=%(LServer)s",
202
                    "jid=%(SJID)s",
203
                    "grp=%(ItemGroup)s"]))
204
      end,
205
      ItemGroups).
206

207
del_roster(LUser, LServer, LJID) ->
208
    SJID = jid:encode(LJID),
258✔
209
    ejabberd_sql:sql_query_t(
258✔
210
      ?SQL("delete from rosterusers"
258✔
211
           " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")),
212
    ejabberd_sql:sql_query_t(
258✔
213
      ?SQL("delete from rostergroups"
258✔
214
           " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")).
215

216
read_subscription_and_groups(LUser, LServer, LJID) ->
217
    SJID = jid:encode(LJID),
×
218
    case get_subscription(LServer, LUser, SJID) of
×
219
        {selected, [{SSubscription, SAsk}]} ->
220
            Subscription = decode_subscription(LUser, LServer, SSubscription),
×
221
            Ask = decode_ask(LUser, LServer, SAsk),
×
222
            Groups = case get_rostergroup_by_jid(LServer, LUser, SJID) of
×
223
                         {selected, JGrps} when is_list(JGrps) ->
224
                             [JGrp || {JGrp} <- JGrps];
×
225
                         _ -> []
×
226
                     end,
227
            {ok, {Subscription, Ask, Groups}};
×
228
        _ ->
229
            error
×
230
    end.
231

232
export(_Server) ->
233
    [{roster,
×
234
      fun(Host, #roster{usj = {_LUser, LServer, _LJID}} = R)
235
            when LServer == Host ->
236
              ItemVals = record_to_row(R),
×
237
              ItemGroups = R#roster.groups,
×
238
              update_roster_sql(ItemVals, ItemGroups);
×
239
        (_Host, _R) ->
240
              []
×
241
      end},
242
     {roster_version,
243
      fun(Host, #roster_version{us = {LUser, LServer}, version = Ver})
244
            when LServer == Host ->
245
              [?SQL("delete from roster_version"
×
246
                    " where username=%(LUser)s and %(LServer)H;"),
247
               ?SQL_INSERT(
×
248
                  "roster_version",
249
                  ["username=%(LUser)s",
250
                   "server_host=%(LServer)s",
251
                   "version=%(Ver)s"])];
252
         (_Host, _R) ->
253
              []
×
254
      end}].
255

256
import(_, _, _) ->
257
    ok.
×
258

259
%%%===================================================================
260
%%% Internal functions
261
%%%===================================================================
262
set_roster_version(LUser, LServer, Version) ->
263
    ?SQL_UPSERT_T(
537✔
264
       "roster_version",
265
       ["!username=%(LUser)s",
266
        "!server_host=%(LServer)s",
267
        "version=%(Version)s"]).
268

269
get_roster_jid_groups(LServer, LUser) ->
270
    ejabberd_sql:sql_query(
1,209✔
271
      LServer,
272
      ?SQL("select @(jid)s, @(grp)s from rostergroups where "
1,209✔
273
           "username=%(LUser)s and %(LServer)H")).
274

275
get_roster_groups(LServer, LUser, SJID) ->
276
    ejabberd_sql:sql_query_t(
882✔
277
      ?SQL("select @(grp)s from rostergroups"
882✔
278
           " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")).
279

280
roster_subscribe({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}) ->
281
    ?SQL_UPSERT_T(
501✔
282
       "rosterusers",
283
       ["!username=%(LUser)s",
284
        "!server_host=%(LServer)s",
285
        "!jid=%(SJID)s",
286
        "nick=%(Name)s",
287
        "subscription=%(SSubscription)s",
288
        "ask=%(SAsk)s",
289
        "askmessage=%(AskMessage)s",
290
        "server='N'",
291
        "subscribe=''",
292
        "type='item'"]).
293

294
get_roster_by_jid(LServer, LUser, SJID) ->
295
    ejabberd_sql:sql_query_t(
1,275✔
296
      ?SQL("select @(username)s, @(jid)s, @(nick)s, @(subscription)s,"
1,275✔
297
           " @(ask)s, @(askmessage)s, @(server)s, @(subscribe)s,"
298
           " @(type)s from rosterusers"
299
           " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")).
300

301
get_rostergroup_by_jid(LServer, LUser, SJID) ->
302
    ejabberd_sql:sql_query(
×
303
      LServer,
304
      ?SQL("select @(grp)s from rostergroups"
×
305
           " where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")).
306

307
get_subscription(LServer, LUser, SJID) ->
308
    ejabberd_sql:sql_query(
×
309
      LServer,
310
      ?SQL("select @(subscription)s, @(ask)s from rosterusers "
×
311
           "where username=%(LUser)s and %(LServer)H and jid=%(SJID)s")).
312

313
update_roster_sql({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage},
314
                  ItemGroups) ->
315
    [?SQL("delete from rosterusers where"
×
316
          " username=%(LUser)s and %(LServer)H and jid=%(SJID)s;"),
317
     ?SQL_INSERT(
×
318
        "rosterusers",
319
        ["username=%(LUser)s",
320
         "server_host=%(LServer)s",
321
         "jid=%(SJID)s",
322
         "nick=%(Name)s",
323
         "subscription=%(SSubscription)s",
324
         "ask=%(SAsk)s",
325
         "askmessage=%(AskMessage)s",
326
         "server='N'",
327
         "subscribe=''",
328
         "type='item'"]),
329
     ?SQL("delete from rostergroups where"
×
330
          " username=%(LUser)s and %(LServer)H and jid=%(SJID)s;")]
331
      ++
×
332
      [?SQL_INSERT(
×
333
          "rostergroups",
334
          ["username=%(LUser)s",
335
           "server_host=%(LServer)s",
336
           "jid=%(SJID)s",
337
           "grp=%(ItemGroup)s"])
338
       || ItemGroup <- ItemGroups].
×
339

340
raw_to_record(LServer,
341
              [User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage,
342
               _SServer, _SSubscribe, _SType]) ->
343
    raw_to_record(LServer,
×
344
                  {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage,
345
                   _SServer, _SSubscribe, _SType});
346
raw_to_record(LServer,
347
              {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
348
               _SServer, _SSubscribe, _SType}) ->
349
    raw_to_record(LServer,
1,677✔
350
                  {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage,
351
                   _SServer, _SSubscribe, _SType});
352
raw_to_record(LServer,
353
              {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage,
354
               _SServer, _SSubscribe, _SType}) ->
355
    try jid:decode(SJID) of
1,677✔
356
      JID ->
357
          LJID = jid:tolower(JID),
1,677✔
358
          Subscription = decode_subscription(User, LServer, SSubscription),
1,677✔
359
          Ask = decode_ask(User, LServer, SAsk),
1,677✔
360
          #roster{usj = {User, LServer, LJID},
1,677✔
361
                  us = {User, LServer}, jid = LJID, name = Nick,
362
                  subscription = Subscription, ask = Ask,
363
                  askmessage = SAskMessage}
364
    catch _:{bad_jid, _} ->
365
            ?ERROR_MSG("~ts", [format_row_error(User, LServer, {jid, SJID})]),
×
366
            error
×
367
    end.
368

369
record_to_row(
370
  #roster{us = {LUser, LServer},
371
          jid = JID, name = Name, subscription = Subscription,
372
          ask = Ask, askmessage = AskMessage}) ->
373
    SJID = jid:encode(jid:tolower(JID)),
501✔
374
    SSubscription = case Subscription of
501✔
375
                      both -> <<"B">>;
102✔
376
                      to -> <<"T">>;
39✔
377
                      from -> <<"F">>;
69✔
378
                      none -> <<"N">>
291✔
379
                    end,
380
    SAsk = case Ask of
501✔
381
             subscribe -> <<"S">>;
×
382
             unsubscribe -> <<"U">>;
×
383
             both -> <<"B">>;
15✔
384
             out -> <<"O">>;
99✔
385
             in -> <<"I">>;
78✔
386
             none -> <<"N">>
309✔
387
           end,
388
    {LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}.
501✔
389

390
decode_subscription(User, Server, S) ->
391
    case S of
1,677✔
392
        <<"B">> -> both;
222✔
393
        <<"T">> -> to;
126✔
394
        <<"F">> -> from;
174✔
395
        <<"N">> -> none;
1,155✔
396
        <<"">> -> none;
×
397
        _ ->
398
            ?ERROR_MSG("~ts", [format_row_error(User, Server, {subscription, S})]),
×
399
            none
×
400
    end.
401

402
decode_ask(User, Server, A) ->
403
    case A of
1,677✔
404
        <<"S">> -> subscribe;
×
405
        <<"U">> -> unsubscribe;
×
406
        <<"B">> -> both;
54✔
407
        <<"O">> -> out;
360✔
408
        <<"I">> -> in;
354✔
409
        <<"N">> -> none;
909✔
410
        <<"">> -> none;
×
411
        _ ->
412
            ?ERROR_MSG("~ts", [format_row_error(User, Server, {ask, A})]),
×
413
            none
×
414
    end.
415

416
format_row_error(User, Server, Why) ->
417
    [case Why of
×
418
         {jid, JID} -> ["Malformed 'jid' field with value '", JID, "'"];
×
419
         {subscription, Sub} -> ["Malformed 'subscription' field with value '", Sub, "'"];
×
420
         {ask, Ask} -> ["Malformed 'ask' field with value '", Ask, "'"]
×
421
     end,
422
     " detected for ", User, "@", Server, " in table 'rosterusers'"].
423

424
process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
425
    process_rosteritems_sql(ActionS, list_to_atom(SubsS), list_to_atom(AsksS),
×
426
        list_to_binary(UsersS), list_to_binary(ContactsS)).
427

428
process_rosteritems_sql(ActionS, Subscription, Ask, SLocalJID, SJID) ->
429
    [LUser, LServer] = binary:split(SLocalJID, <<"@">>),
×
430
    SSubscription = case Subscription of
×
431
                      any -> <<"_">>;
×
432
                      both -> <<"B">>;
×
433
                      to -> <<"T">>;
×
434
                      from -> <<"F">>;
×
435
                      none -> <<"N">>
×
436
                    end,
437
    SAsk = case Ask of
×
438
             any -> <<"_">>;
×
439
             subscribe -> <<"S">>;
×
440
             unsubscribe -> <<"U">>;
×
441
             both -> <<"B">>;
×
442
             out -> <<"O">>;
×
443
             in -> <<"I">>;
×
444
             none -> <<"N">>
×
445
           end,
446
    {selected, List} = ejabberd_sql:sql_query(
×
447
      LServer,
448
      ?SQL("select @(username)s, @(jid)s from rosterusers "
×
449
           "where username LIKE %(LUser)s"
450
           " and %(LServer)H"
451
           " and jid LIKE %(SJID)s"
452
           " and subscription LIKE %(SSubscription)s"
453
           " and ask LIKE %(SAsk)s")),
454
    case ActionS of
×
455
        "delete" -> [mod_roster:del_roster(User, LServer, jid:tolower(jid:decode(Contact))) || {User, Contact} <- List];
×
456
        "list" -> ok
×
457
    end,
458
    List.
×
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