• 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

56.48
/src/mod_muc_sql.erl
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_muc_sql.erl
3
%%% Author  : Evgeny Khramtsov <ekhramtsov@process-one.net>
4
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
5
%%%
6
%%%
7
%%% ejabberd, Copyright (C) 2002-2025   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_muc_sql).
26

27

28
-behaviour(mod_muc).
29
-behaviour(mod_muc_room).
30

31
%% API
32
-export([init/2, store_room/5, store_changes/4,
33
         restore_room/3, forget_room/3,
34
         can_use_nick/4, get_rooms/2, get_nick/3, get_nicks/2, set_nick/4,
35
         import/3, export/1]).
36
-export([register_online_room/4, unregister_online_room/4, find_online_room/3,
37
         get_online_rooms/3, count_online_rooms/2, rsm_supported/0,
38
         register_online_user/4, unregister_online_user/4,
39
         count_online_rooms_by_user/3, get_online_rooms_by_user/3,
40
         get_subscribed_rooms/3, get_rooms_without_subscribers/2,
41
         get_hibernated_rooms_older_than/3,
42
         find_online_room_by_pid/2, remove_user/2]).
43
-export([set_affiliation/6, set_affiliations/4, get_affiliation/5,
44
         get_affiliations/3, search_affiliation/4]).
45
-export([sql_schemas/0]).
46

47
-include_lib("xmpp/include/jid.hrl").
48
-include("mod_muc.hrl").
49
-include("logger.hrl").
50
-include("ejabberd_sql_pt.hrl").
51

52
%%%===================================================================
53
%%% API
54
%%%===================================================================
55
init(Host, Opts) ->
56
    ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()),
12✔
57
    case gen_mod:ram_db_mod(Opts, mod_muc) of
12✔
58
        ?MODULE ->
59
            clean_tables(Host);
12✔
60
        _ ->
61
            ok
×
62
    end.
63

64
sql_schemas() ->
65
    [#sql_schema{
12✔
66
        version = 1,
67
        tables =
68
            [#sql_table{
69
                name = <<"muc_room">>,
70
                columns =
71
                    [#sql_column{name = <<"name">>, type = text},
72
                     #sql_column{name = <<"host">>, type = text},
73
                     #sql_column{name = <<"server_host">>, type = text},
74
                     #sql_column{name = <<"opts">>, type = {text, big}},
75
                     #sql_column{name = <<"created_at">>, type = timestamp,
76
                                 default = true}],
77
                indices = [#sql_index{
78
                              columns = [<<"name">>, <<"host">>],
79
                              unique = true},
80
                           #sql_index{
81
                              columns = [<<"host">>, <<"created_at">>]}]},
82
             #sql_table{
83
                name = <<"muc_registered">>,
84
                columns =
85
                    [#sql_column{name = <<"jid">>, type = text},
86
                     #sql_column{name = <<"host">>, type = text},
87
                     #sql_column{name = <<"server_host">>, type = text},
88
                     #sql_column{name = <<"nick">>, type = text},
89
                     #sql_column{name = <<"created_at">>, type = timestamp,
90
                                 default = true}],
91
                indices = [#sql_index{
92
                              columns = [<<"jid">>, <<"host">>],
93
                              unique = true},
94
                           #sql_index{
95
                              columns = [<<"nick">>]}]},
96
             #sql_table{
97
                name = <<"muc_online_room">>,
98
                columns =
99
                    [#sql_column{name = <<"name">>, type = text},
100
                     #sql_column{name = <<"host">>, type = text},
101
                     #sql_column{name = <<"server_host">>, type = text},
102
                     #sql_column{name = <<"node">>, type = text},
103
                     #sql_column{name = <<"pid">>, type = text}],
104
                indices = [#sql_index{
105
                              columns = [<<"name">>, <<"host">>],
106
                              unique = true}]},
107
             #sql_table{
108
                name = <<"muc_online_users">>,
109
                columns =
110
                    [#sql_column{name = <<"username">>, type = text},
111
                     #sql_column{name = <<"server">>, type = {text, 75}},
112
                     #sql_column{name = <<"resource">>, type = text},
113
                     #sql_column{name = <<"name">>, type = text},
114
                     #sql_column{name = <<"host">>, type = {text, 75}},
115
                     #sql_column{name = <<"server_host">>, type = text},
116
                     #sql_column{name = <<"node">>, type = text}],
117
                indices = [#sql_index{
118
                              columns = [<<"username">>, <<"server">>,
119
                                         <<"resource">>, <<"name">>,
120
                                         <<"host">>],
121
                              unique = true}]},
122
             #sql_table{
123
                name = <<"muc_room_subscribers">>,
124
                columns =
125
                    [#sql_column{name = <<"room">>, type = text},
126
                     #sql_column{name = <<"host">>, type = text},
127
                     #sql_column{name = <<"jid">>, type = text},
128
                     #sql_column{name = <<"nick">>, type = text},
129
                     #sql_column{name = <<"nodes">>, type = text},
130
                     #sql_column{name = <<"created_at">>, type = timestamp,
131
                                 default = true}],
132
                indices = [#sql_index{
133
                              columns = [<<"host">>, <<"room">>, <<"jid">>],
134
                              unique = true},
135
                           #sql_index{
136
                              columns = [<<"host">>, <<"jid">>]},
137
                           #sql_index{
138
                              columns = [<<"jid">>]}]}]}].
139

140
store_room(LServer, Host, Name, Opts, ChangesHints) ->
141
    {Subs, Opts2} = case lists:keytake(subscribers, 1, Opts) of
588✔
142
                        {value, {subscribers, S}, OptN} -> {S, OptN};
588✔
143
                        _ -> {[], Opts}
×
144
                    end,
145
    SOpts = misc:term_to_expr(Opts2),
588✔
146
    Timestamp = case lists:keyfind(hibernation_time, 1, Opts) of
588✔
147
                    false -> <<"1970-01-02 00:00:00">>;
×
148
                    {_, undefined} -> <<"1970-01-02 00:00:00">>;
588✔
149
                    {_, Time} -> usec_to_sql_timestamp(Time)
×
150
                end,
151
    F = fun () ->
588✔
152
                ?SQL_UPSERT_T(
588✔
153
                   "muc_room",
154
                   ["!name=%(Name)s",
155
                    "!host=%(Host)s",
156
                    "server_host=%(LServer)s",
157
                    "opts=%(SOpts)s",
158
                    "created_at=%(Timestamp)t"]),
159
                case ChangesHints of
588✔
160
                    Changes when is_list(Changes) ->
161
                        [change_room(Host, Name, Change) || Change <- Changes];
588✔
162
                    _ ->
163
                        ejabberd_sql:sql_query_t(
×
164
                          ?SQL("delete from muc_room_subscribers where "
×
165
                               "room=%(Name)s and host=%(Host)s")),
166
                        [change_room(Host, Name, {add_subscription, JID, Nick, Nodes})
×
167
                         || {JID, Nick, Nodes} <- Subs]
×
168
                end
169
        end,
170
    ejabberd_sql:sql_transaction(LServer, F).
588✔
171

172
store_changes(LServer, Host, Name, Changes) ->
173
    F = fun () ->
108✔
174
                [change_room(Host, Name, Change) || Change <- Changes]
108✔
175
        end,
176
    ejabberd_sql:sql_transaction(LServer, F).
108✔
177

178
change_room(Host, Room, {add_subscription, JID, Nick, Nodes}) ->
179
    SJID = jid:encode(JID),
54✔
180
    SNodes = misc:term_to_expr(Nodes),
54✔
181
    ?SQL_UPSERT_T(
54✔
182
       "muc_room_subscribers",
183
       ["!jid=%(SJID)s",
184
        "!host=%(Host)s",
185
        "!room=%(Room)s",
186
        "nick=%(Nick)s",
187
        "nodes=%(SNodes)s"]);
188
change_room(Host, Room, {del_subscription, JID}) ->
189
    SJID = jid:encode(JID),
54✔
190
    ejabberd_sql:sql_query_t(?SQL("delete from muc_room_subscribers where "
54✔
191
                                  "room=%(Room)s and host=%(Host)s and jid=%(SJID)s"));
192
change_room(Host, Room, Change) ->
193
    ?ERROR_MSG("Unsupported change on room ~ts@~ts: ~p", [Room, Host, Change]).
×
194

195
restore_room(LServer, Host, Name) ->
196
    case catch ejabberd_sql:sql_query(
282✔
197
                 LServer,
198
                 ?SQL("select @(opts)s from muc_room where name=%(Name)s"
380✔
199
                      " and host=%(Host)s")) of
200
        {selected, [{Opts}]} ->
201
            OptsD = ejabberd_sql:decode_term(Opts),
×
202
            case catch ejabberd_sql:sql_query(
×
203
                LServer,
204
                ?SQL("select @(jid)s, @(nick)s, @(nodes)s from muc_room_subscribers where room=%(Name)s"
×
205
                     " and host=%(Host)s")) of
206
                {selected, []} ->
207
                    OptsR = mod_muc:opts_to_binary(OptsD),
×
208
                    case lists:keymember(subscribers, 1, OptsD) of
×
209
                        true ->
210
                            store_room(LServer, Host, Name, OptsR, undefined);
×
211
                        _ ->
212
                            ok
×
213
                    end,
214
                    OptsR;
×
215
                {selected, Subs} ->
216
                    SubData = lists:map(
×
217
                             fun({Jid, Nick, Nodes}) ->
218
                                 {jid:decode(Jid), Nick, ejabberd_sql:decode_term(Nodes)}
×
219
                             end, Subs),
220
                    Opts2 = lists:keystore(subscribers, 1, OptsD, {subscribers, SubData}),
×
221
                    mod_muc:opts_to_binary(Opts2);
×
222
                _ ->
223
                    {error, db_failure}
×
224
            end;
225
        {selected, _} ->
226
            error;
282✔
227
        _ ->
228
            {error, db_failure}
×
229
    end.
230

231
forget_room(LServer, Host, Name) ->
232
    F = fun () ->
540✔
233
                ejabberd_sql:sql_query_t(
540✔
234
                  ?SQL("delete from muc_room where name=%(Name)s"
724✔
235
                       " and host=%(Host)s")),
236
                ejabberd_sql:sql_query_t(
540✔
237
                    ?SQL("delete from muc_room_subscribers where room=%(Name)s"
724✔
238
                       " and host=%(Host)s"))
239
        end,
240
    ejabberd_sql:sql_transaction(LServer, F).
540✔
241

242
can_use_nick(LServer, ServiceOrRoom, JID, Nick) ->
243
    SJID = jid:encode(jid:tolower(jid:remove_resource(JID))),
546✔
244
    SqlQuery = case (jid:decode(ServiceOrRoom))#jid.lserver of
546✔
245
                   ServiceOrRoom ->
246
                       ?SQL("select @(jid)s from muc_registered "
×
247
                            "where nick=%(Nick)s"
248
                            " and host=%(ServiceOrRoom)s");
249
                   Service ->
250
                       ?SQL("select @(jid)s from muc_registered "
546✔
251
                            "where nick=%(Nick)s"
252
                            " and (host=%(ServiceOrRoom)s or host=%(Service)s)")
253
               end,
254
    case catch ejabberd_sql:sql_query(LServer, SqlQuery) of
546✔
255
        {selected, [{SJID1}]} -> SJID == SJID1;
×
256
        _ -> true
546✔
257
    end.
258

259
get_rooms_without_subscribers(LServer, Host) ->
260
    case catch ejabberd_sql:sql_query(
×
261
        LServer,
262
        ?SQL("select @(name)s, @(opts)s from muc_room"
×
263
             " where host=%(Host)s")) of
264
        {selected, RoomOpts} ->
265
            lists:map(
×
266
                fun({Room, Opts}) ->
267
                    OptsD = ejabberd_sql:decode_term(Opts),
×
268
                    #muc_room{name_host = {Room, Host},
×
269
                              opts = mod_muc:opts_to_binary(OptsD)}
270
                end, RoomOpts);
271
        _Err ->
272
            []
×
273
    end.
274

275
get_hibernated_rooms_older_than(LServer, Host, Timestamp) ->
276
    TimestampS = usec_to_sql_timestamp(Timestamp),
×
277
    case catch ejabberd_sql:sql_query(
×
278
        LServer,
279
        ?SQL("select @(name)s, @(opts)s from muc_room"
×
280
             " where host=%(Host)s and created_at < %(TimestampS)t and created_at > '1970-01-02 00:00:00'")) of
281
        {selected, RoomOpts} ->
282
            lists:map(
×
283
                fun({Room, Opts}) ->
284
                    OptsD = ejabberd_sql:decode_term(Opts),
×
285
                    #muc_room{name_host = {Room, Host},
×
286
                              opts = mod_muc:opts_to_binary(OptsD)}
287
                end, RoomOpts);
288
        _Err ->
289
            []
×
290
    end.
291

292
get_rooms(LServer, Host) ->
293
    case catch ejabberd_sql:sql_query(
6✔
294
                 LServer,
295
                 ?SQL("select @(name)s, @(opts)s from muc_room"
12✔
296
                      " where host=%(Host)s")) of
297
        {selected, RoomOpts} ->
298
            case catch ejabberd_sql:sql_query(
6✔
299
                LServer,
300
                ?SQL("select @(room)s, @(jid)s, @(nick)s, @(nodes)s from muc_room_subscribers"
12✔
301
                     " where host=%(Host)s")) of
302
                {selected, Subs} ->
303
                    SubsD = lists:foldl(
6✔
304
                        fun({Room, Jid, Nick, Nodes}, Dict) ->
305
                                Sub = {jid:decode(Jid),
×
306
                                       Nick, ejabberd_sql:decode_term(Nodes)},
307
                                maps:update_with(
×
308
                                  Room,
309
                                  fun(SubAcc) ->
310
                                          [Sub | SubAcc]
×
311
                                  end,
312
                                  [Sub],
313
                                  Dict)
314
                        end, maps:new(), Subs),
315
            lists:map(
6✔
316
              fun({Room, Opts}) ->
317
                            OptsD = ejabberd_sql:decode_term(Opts),
×
318
                            OptsD2 = case {maps:find(Room, SubsD), lists:keymember(subscribers, 1, OptsD)} of
×
319
                                         {_, true} ->
320
                                             store_room(LServer, Host, Room, mod_muc:opts_to_binary(OptsD), undefined),
×
321
                                             OptsD;
×
322
                                         {{ok, SubsI}, false} ->
323
                                             lists:keystore(subscribers, 1, OptsD, {subscribers, SubsI});
×
324
                                         _ ->
325
                                             OptsD
×
326
                            end,
327
                      #muc_room{name_host = {Room, Host},
×
328
                                      opts = mod_muc:opts_to_binary(OptsD2)}
329
              end, RoomOpts);
330
        _Err ->
331
                    []
×
332
            end;
333
        _Err ->
334
            []
×
335
    end.
336

337
get_nick(LServer, Host, From) ->
338
    SJID = jid:encode(jid:tolower(jid:remove_resource(From))),
48✔
339
    case catch ejabberd_sql:sql_query(
48✔
340
                 LServer,
341
                 ?SQL("select @(nick)s from muc_registered where"
68✔
342
                      " jid=%(SJID)s and host=%(Host)s")) of
343
        {selected, [{Nick}]} -> Nick;
30✔
344
        _ -> error
18✔
345
    end.
346

347
get_nicks(LServer, Host) ->
348
    case catch ejabberd_sql:sql_query(LServer,
×
349
                                      ?SQL("select @(jid)s, @(nick)s from muc_registered where"
×
350
                                           " host=%(Host)s"))
351
    of
352
        {selected, Results} ->
353
            lists:map(fun({JidBin, Nick}) ->
×
354
                         {User, Server, _Resource} =
×
355
                             jid:tolower(
356
                                 jid:decode(JidBin)),
357
                         {User, Server, Nick}
×
358
                      end,
359
                      Results);
360
        _ ->
361
            error
×
362
    end.
363

364
set_nick(LServer, ServiceOrRoom, From, Nick) ->
365
    JID = jid:encode(jid:tolower(jid:remove_resource(From))),
36✔
366
    F = fun () ->
36✔
367
                case Nick of
36✔
368
                    <<"">> ->
369
                        ejabberd_sql:sql_query_t(
12✔
370
                          ?SQL("delete from muc_registered where"
20✔
371
                               " jid=%(JID)s and host=%(ServiceOrRoom)s")),
372
                        ok;
12✔
373
                    _ ->
374
                        Service = (jid:decode(ServiceOrRoom))#jid.lserver,
24✔
375
                        SqlQuery = case (ServiceOrRoom == Service) of
24✔
376
                                       true ->
377
                                           ?SQL("select @(jid)s, @(host)s from muc_registered "
24✔
378
                                                "where nick=%(Nick)s"
379
                                                " and host=%(ServiceOrRoom)s");
380
                                       false ->
381
                                           ?SQL("select @(jid)s, @(host)s from muc_registered "
×
382
                                                "where nick=%(Nick)s"
383
                                                " and (host=%(ServiceOrRoom)s or host=%(Service)s)")
384
                                   end,
385
                        Allow = case ejabberd_sql:sql_query_t(SqlQuery) of
24✔
386
                                    {selected, []}
387
                                      when (ServiceOrRoom == Service) ->
388
                                        %% Registering in the service...
389
                                        %% check if nick is registered for some room in this service
390
                                        {selected, NickRegistrations} =
18✔
391
                                            ejabberd_sql:sql_query_t(
392
                                              ?SQL("select @(jid)s, @(host)s from muc_registered "
28✔
393
                                                   "where nick=%(Nick)s")),
394
                                        not lists:any(fun({_NRJid, NRServiceOrRoom}) ->
18✔
395
                                                              Service == (jid:decode(NRServiceOrRoom))#jid.lserver end,
×
396
                                                      NickRegistrations);
397
                                    {selected, []} ->
398
                                        %% Nick not registered in any service or room
399
                                        true;
×
400
                                    {selected, [{_J, Host}]}
401
                                      when (Host == Service) and (ServiceOrRoom /= Service) ->
402
                                        %% Registering in a room, but the nick is already registered in the service
403
                                        false;
×
404
                                    {selected, [{J, _Host}]} ->
405
                                        %% Registering in room (or service) a nick that is
406
                                        %% already registered in this room (or service)
407
                                        %% Only the owner of this registration can use the nick
408
                                        J == JID
6✔
409
                                end,
410
                        if Allow ->
24✔
411
                                ?SQL_UPSERT_T(
18✔
412
                                  "muc_registered",
413
                                  ["!jid=%(JID)s",
414
                                   "!host=%(ServiceOrRoom)s",
415
                                   "server_host=%(LServer)s",
416
                                   "nick=%(Nick)s"]),
417
                                ok;
18✔
418
                           true ->
419
                                false
6✔
420
                        end
421
                end
422
        end,
423
    ejabberd_sql:sql_transaction(LServer, F).
36✔
424

425
set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) ->
426
    {error, not_implemented}.
54✔
427

428
set_affiliations(_ServerHost, _Room, _Host, _Affiliations) ->
429
    {error, not_implemented}.
264✔
430

431
get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) ->
432
    {error, not_implemented}.
2,478✔
433

434
get_affiliations(_ServerHost, _Room, _Host) ->
435
    {error, not_implemented}.
258✔
436

437
search_affiliation(_ServerHost, _Room, _Host, _Affiliation) ->
438
    {error, not_implemented}.
66✔
439

440
register_online_room(ServerHost, Room, Host, Pid) ->
441
    PidS = misc:encode_pid(Pid),
282✔
442
    NodeS = erlang:atom_to_binary(node(Pid), latin1),
282✔
443
    case ?SQL_UPSERT(ServerHost,
282✔
444
                     "muc_online_room",
445
                     ["!name=%(Room)s",
446
                      "!host=%(Host)s",
447
                      "server_host=%(ServerHost)s",
448
                      "node=%(NodeS)s",
449
                      "pid=%(PidS)s"]) of
450
        ok ->
451
            ok;
282✔
452
        Err ->
453
            Err
×
454
    end.
455

456
unregister_online_room(ServerHost, Room, Host, Pid) ->
457
    %% TODO: report errors
458
    PidS = misc:encode_pid(Pid),
282✔
459
    NodeS = erlang:atom_to_binary(node(Pid), latin1),
282✔
460
    ejabberd_sql:sql_query(
282✔
461
      ServerHost,
462
      ?SQL("delete from muc_online_room where name=%(Room)s and "
380✔
463
           "host=%(Host)s and node=%(NodeS)s and pid=%(PidS)s")).
464

465
find_online_room(ServerHost, Room, Host) ->
466
    case ejabberd_sql:sql_query(
3,192✔
467
           ServerHost,
468
           ?SQL("select @(pid)s, @(node)s from muc_online_room where "
4,260✔
469
                "name=%(Room)s and host=%(Host)s")) of
470
        {selected, [{PidS, NodeS}]} ->
471
            try {ok, misc:decode_pid(PidS, NodeS)}
2,616✔
472
            catch _:{bad_node, _} -> error
×
473
            end;
474
        {selected, []} ->
475
            error;
576✔
476
        _Err ->
477
            error
×
478
    end.
479

480
find_online_room_by_pid(ServerHost, Pid) ->
481
    PidS = misc:encode_pid(Pid),
282✔
482
    NodeS = erlang:atom_to_binary(node(Pid), latin1),
282✔
483
    case ejabberd_sql:sql_query(
282✔
484
        ServerHost,
485
        ?SQL("select @(name)s, @(host)s from muc_online_room where "
380✔
486
             "node=%(NodeS)s and pid=%(PidS)s")) of
487
        {selected, [{Room, Host}]} ->
488
            {ok, Room, Host};
282✔
489
        {selected, []} ->
490
            error;
×
491
        _Err ->
492
            error
×
493
    end.
494

495
count_online_rooms(ServerHost, Host) ->
496
    case ejabberd_sql:sql_query(
24✔
497
           ServerHost,
498
           ?SQL("select @(count(*))d from muc_online_room "
36✔
499
                "where host=%(Host)s")) of
500
        {selected, [{Num}]} ->
501
            Num;
24✔
502
        _Err ->
503
            0
×
504
    end.
505

506
get_online_rooms(ServerHost, Host, _RSM) ->
507
    case ejabberd_sql:sql_query(
42✔
508
           ServerHost,
509
           ?SQL("select @(name)s, @(pid)s, @(node)s from muc_online_room "
60✔
510
                "where host=%(Host)s")) of
511
        {selected, Rows} ->
512
            lists:flatmap(
42✔
513
              fun({Room, PidS, NodeS}) ->
514
                      try [{Room, Host, misc:decode_pid(PidS, NodeS)}]
48✔
515
                      catch _:{bad_node, _} -> []
×
516
                      end
517
              end, Rows);
518
        _Err ->
519
            []
×
520
    end.
521

522
rsm_supported() ->
523
    false.
36✔
524

525
register_online_user(ServerHost, {U, S, R}, Room, Host) ->
526
    NodeS = erlang:atom_to_binary(node(), latin1),
456✔
527
    case ?SQL_UPSERT(ServerHost, "muc_online_users",
456✔
528
                     ["!username=%(U)s",
529
                      "!server=%(S)s",
530
                      "!resource=%(R)s",
531
                      "!name=%(Room)s",
532
                      "!host=%(Host)s",
533
                      "server_host=%(ServerHost)s",
534
                      "node=%(NodeS)s"]) of
535
        ok ->
536
            ok;
456✔
537
        Err ->
538
            Err
×
539
    end.
540

541
unregister_online_user(ServerHost, {U, S, R}, Room, Host) ->
542
    %% TODO: report errors
543
    ejabberd_sql:sql_query(
456✔
544
      ServerHost,
545
      ?SQL("delete from muc_online_users where username=%(U)s and "
612✔
546
           "server=%(S)s and resource=%(R)s and name=%(Room)s and "
547
           "host=%(Host)s")).
548

549
count_online_rooms_by_user(ServerHost, U, S) ->
550
    case ejabberd_sql:sql_query(
534✔
551
           ServerHost,
552
           ?SQL("select @(count(*))d from muc_online_users where "
716✔
553
                "username=%(U)s and server=%(S)s")) of
554
        {selected, [{Num}]} ->
555
            Num;
534✔
556
        _Err ->
557
            0
×
558
    end.
559

560
get_online_rooms_by_user(ServerHost, U, S) ->
561
    case ejabberd_sql:sql_query(
×
562
           ServerHost,
563
           ?SQL("select @(name)s, @(host)s from muc_online_users where "
×
564
                "username=%(U)s and server=%(S)s")) of
565
        {selected, Rows} ->
566
            Rows;
×
567
        _Err ->
568
            []
×
569
    end.
570

571
export(_Server) ->
572
    [{muc_room,
×
573
      fun(Host, #muc_room{name_host = {Name, RoomHost}, opts = Opts}) ->
574
              case str:suffix(Host, RoomHost) of
×
575
                  true ->
576
                      SOpts = misc:term_to_expr(Opts),
×
577
                      [?SQL("delete from muc_room where name=%(Name)s"
×
578
                            " and host=%(RoomHost)s;"),
579
                       ?SQL_INSERT(
×
580
                          "muc_room",
581
                          ["name=%(Name)s",
582
                           "host=%(RoomHost)s",
583
                           "server_host=%(Host)s",
584
                           "opts=%(SOpts)s"])];
585
                  false ->
586
                      []
×
587
              end
588
      end},
589
     {muc_registered,
590
      fun(Host, #muc_registered{us_host = {{U, S}, RoomHost},
591
                                nick = Nick}) ->
592
              case str:suffix(Host, RoomHost) of
×
593
                  true ->
594
                      SJID = jid:encode(jid:make(U, S)),
×
595
                      [?SQL("delete from muc_registered where"
×
596
                            " jid=%(SJID)s and host=%(RoomHost)s;"),
597
                       ?SQL_INSERT(
×
598
                          "muc_registered",
599
                          ["jid=%(SJID)s",
600
                           "host=%(RoomHost)s",
601
                           "server_host=%(Host)s",
602
                           "nick=%(Nick)s"])];
603
                  false ->
604
                      []
×
605
              end
606
      end}].
607

608
import(_, _, _) ->
609
    ok.
×
610

611
get_subscribed_rooms(LServer, Host, Jid) ->
612
    JidS = jid:encode(Jid),
6✔
613
    case ejabberd_sql:sql_query(
6✔
614
           LServer,
615
           ?SQL("select @(room)s, @(nick)s, @(nodes)s from muc_room_subscribers "
12✔
616
                "where jid=%(JidS)s and host=%(Host)s")) of
617
        {selected, Subs} ->
618
            {ok, [{jid:make(Room, Host), Nick, ejabberd_sql:decode_term(Nodes)}
6✔
619
                  || {Room, Nick, Nodes} <- Subs]};
6✔
620
        _Error ->
621
            {error, db_failure}
×
622
    end.
623

624
remove_user(LUser, LServer) ->
625
    SJID = jid:encode(jid:make(LUser, LServer)),
12✔
626
    ejabberd_sql:sql_query(
12✔
627
      LServer,
628
      ?SQL("delete from muc_room_subscribers where jid=%(SJID)s")),
20✔
629
    ok.
12✔
630

631
%%%===================================================================
632
%%% Internal functions
633
%%%===================================================================
634
clean_tables(ServerHost) ->
635
    NodeS = erlang:atom_to_binary(node(), latin1),
12✔
636
    ?DEBUG("Cleaning SQL muc_online_room table...", []),
12✔
637
    case ejabberd_sql:sql_query(
12✔
638
           ServerHost,
639
           ?SQL("delete from muc_online_room where node=%(NodeS)s")) of
20✔
640
        {updated, _} ->
641
            ok;
12✔
642
        Err1 ->
643
            ?ERROR_MSG("Failed to clean 'muc_online_room' table: ~p", [Err1]),
×
644
            Err1
×
645
    end,
646
    ?DEBUG("Cleaning SQL muc_online_users table...", []),
12✔
647
    case ejabberd_sql:sql_query(
12✔
648
           ServerHost,
649
           ?SQL("delete from muc_online_users where node=%(NodeS)s")) of
20✔
650
        {updated, _} ->
651
            ok;
12✔
652
        Err2 ->
653
            ?ERROR_MSG("Failed to clean 'muc_online_users' table: ~p", [Err2]),
×
654
            Err2
×
655
    end.
656

657
usec_to_sql_timestamp(Timestamp) ->
658
    TS = misc:usec_to_now(Timestamp),
×
659
    case calendar:now_to_universal_time(TS) of
×
660
        {{Year, Month, Day}, {Hour, Minute, Second}} ->
661
            list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0B "
×
662
                                         "~2..0B:~2..0B:~2..0B",
663
                                         [Year, Month, Day, Hour, Minute, Second]))
664
    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