• 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

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

25
%% API
26
-export([init/2]).
27
-export([set_channel/6, get_channels/2, get_channel/3, del_channel/3]).
28
-export([set_participant/6, get_participant/4, get_participants/3, del_participant/4]).
29
-export([subscribe/5, unsubscribe/4, unsubscribe/5, get_subscribed/4]).
30
-export([sql_schemas/0]).
31

32
-include("logger.hrl").
33
-include("ejabberd_sql_pt.hrl").
34

35
%%%===================================================================
36
%%% API
37
%%%===================================================================
38
init(Host, _Opts) ->
UNCOV
39
    ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()),
4✔
UNCOV
40
    ok.
4✔
41

42
sql_schemas() ->
UNCOV
43
    [#sql_schema{
4✔
44
        version = 1,
45
        tables =
46
            [#sql_table{
47
                name = <<"mix_channel">>,
48
                columns =
49
                    [#sql_column{name = <<"channel">>, type = text},
50
                     #sql_column{name = <<"service">>, type = text},
51
                     #sql_column{name = <<"username">>, type = text},
52
                     #sql_column{name = <<"domain">>, type = text},
53
                     #sql_column{name = <<"jid">>, type = text},
54
                     #sql_column{name = <<"hidden">>, type = boolean},
55
                     #sql_column{name = <<"hmac_key">>, type = text},
56
                     #sql_column{name = <<"created_at">>, type = timestamp,
57
                                 default = true}],
58
                indices = [#sql_index{
59
                              columns = [<<"channel">>, <<"service">>],
60
                              unique = true},
61
                           #sql_index{
62
                              columns = [<<"service">>]}]},
63
             #sql_table{
64
                name = <<"mix_participant">>,
65
                columns =
66
                    [#sql_column{name = <<"channel">>, type = text},
67
                     #sql_column{name = <<"service">>, type = text},
68
                     #sql_column{name = <<"username">>, type = text},
69
                     #sql_column{name = <<"domain">>, type = text},
70
                     #sql_column{name = <<"jid">>, type = text},
71
                     #sql_column{name = <<"id">>, type = text},
72
                     #sql_column{name = <<"nick">>, type = text},
73
                     #sql_column{name = <<"created_at">>, type = timestamp,
74
                                 default = true}],
75
                indices = [#sql_index{
76
                              columns = [<<"channel">>, <<"service">>,
77
                                         <<"username">>, <<"domain">>],
78
                              unique = true}]},
79
             #sql_table{
80
                name = <<"mix_subscription">>,
81
                columns =
82
                    [#sql_column{name = <<"channel">>, type = text},
83
                     #sql_column{name = <<"service">>, type = {text, 75}},
84
                     #sql_column{name = <<"username">>, type = text},
85
                     #sql_column{name = <<"domain">>, type = {text, 75}},
86
                     #sql_column{name = <<"node">>, type = text},
87
                     #sql_column{name = <<"jid">>, type = text}],
88
                indices = [#sql_index{
89
                              columns = [<<"channel">>, <<"service">>,
90
                                         <<"username">>, <<"domain">>,
91
                                         <<"node">>],
92
                              unique = true},
93
                           #sql_index{
94
                              columns = [<<"channel">>, <<"service">>,
95
                                         <<"node">>]}]}]}].
96

97
set_channel(LServer, Channel, Service, CreatorJID, Hidden, Key) ->
98
    {User, Domain, _} = jid:tolower(CreatorJID),
×
99
    RawJID = jid:encode(jid:remove_resource(CreatorJID)),
×
100
    case ?SQL_UPSERT(LServer, "mix_channel",
×
101
                     ["!channel=%(Channel)s",
102
                      "!service=%(Service)s",
103
                      "username=%(User)s",
104
                      "domain=%(Domain)s",
105
                      "jid=%(RawJID)s",
106
                      "hidden=%(Hidden)b",
107
                      "hmac_key=%(Key)s"]) of
108
        ok -> ok;
×
109
        _Err -> {error, db_failure}
×
110
    end.
111

112
get_channels(LServer, Service) ->
113
    case ejabberd_sql:sql_query(
×
114
           LServer,
115
           ?SQL("select @(channel)s, @(hidden)b from mix_channel "
×
116
                "where service=%(Service)s")) of
117
        {selected, Ret} ->
118
            {ok, [Channel || {Channel, Hidden} <- Ret, Hidden == false]};
×
119
        _Err ->
120
            {error, db_failure}
×
121
    end.
122

123
get_channel(LServer, Channel, Service) ->
124
    SQL = ?SQL("select @(jid)s, @(hidden)b, @(hmac_key)s from mix_channel "
×
125
               "where channel=%(Channel)s and service=%(Service)s"),
126
    case ejabberd_sql:sql_query(LServer, SQL) of
×
127
        {selected, [{RawJID, Hidden, Key}]} ->
128
            try jid:decode(RawJID) of
×
129
                JID -> {ok, {JID, Hidden, Key}}
×
130
            catch _:{bad_jid, _} ->
131
                    report_corrupted(jid, SQL),
×
132
                    {error, db_failure}
×
133
            end;
134
        {selected, []} -> {error, notfound};
×
135
        _Err -> {error, db_failure}
×
136
    end.
137

138
del_channel(LServer, Channel, Service) ->
139
    F = fun() ->
×
140
                ejabberd_sql:sql_query_t(
×
141
                  ?SQL("delete from mix_channel where "
×
142
                       "channel=%(Channel)s and service=%(Service)s")),
143
                ejabberd_sql:sql_query_t(
×
144
                  ?SQL("delete from mix_participant where "
×
145
                       "channel=%(Channel)s and service=%(Service)s")),
146
                ejabberd_sql:sql_query_t(
×
147
                  ?SQL("delete from mix_subscription where "
×
148
                       "channel=%(Channel)s and service=%(Service)s"))
149
        end,
150
    case ejabberd_sql:sql_transaction(LServer, F) of
×
151
        {atomic, _} -> ok;
×
152
        _Err -> {error, db_failure}
×
153
    end.
154

155
set_participant(LServer, Channel, Service, JID, ID, Nick) ->
156
    {User, Domain, _} = jid:tolower(JID),
×
157
    RawJID = jid:encode(jid:remove_resource(JID)),
×
158
    case ?SQL_UPSERT(LServer, "mix_participant",
×
159
                     ["!channel=%(Channel)s",
160
                      "!service=%(Service)s",
161
                      "!username=%(User)s",
162
                      "!domain=%(Domain)s",
163
                      "jid=%(RawJID)s",
164
                      "id=%(ID)s",
165
                      "nick=%(Nick)s"]) of
166
        ok -> ok;
×
167
        _Err -> {error, db_failure}
×
168
    end.
169

170
-spec get_participant(binary(), binary(), binary(), jid:jid()) -> {ok, {binary(), binary()}} | {error, notfound | db_failure}.
171
get_participant(LServer, Channel, Service, JID) ->
172
    {User, Domain, _} = jid:tolower(JID),
×
173
    case ejabberd_sql:sql_query(
×
174
           LServer,
175
           ?SQL("select @(id)s, @(nick)s from mix_participant "
×
176
                "where channel=%(Channel)s and service=%(Service)s "
177
                "and username=%(User)s and domain=%(Domain)s")) of
178
        {selected, [Ret]} -> {ok, Ret};
×
179
        {selected, []} -> {error, notfound};
×
180
        _Err -> {error, db_failure}
×
181
    end.
182

183
get_participants(LServer, Channel, Service) ->
184
    SQL = ?SQL("select @(jid)s, @(id)s, @(nick)s from mix_participant "
×
185
               "where channel=%(Channel)s and service=%(Service)s"),
186
    case ejabberd_sql:sql_query(LServer, SQL) of
×
187
        {selected, Ret} ->
188
            {ok, lists:filtermap(
×
189
                   fun({RawJID, ID, Nick}) ->
190
                           try jid:decode(RawJID) of
×
191
                               JID -> {true, {JID, ID, Nick}}
×
192
                           catch _:{bad_jid, _} ->
193
                                   report_corrupted(jid, SQL),
×
194
                                   false
×
195
                           end
196
                   end, Ret)};
197
        _Err ->
198
            {error, db_failure}
×
199
    end.
200

201
del_participant(LServer, Channel, Service, JID) ->
202
    {User, Domain, _} = jid:tolower(JID),
×
203
    case ejabberd_sql:sql_query(
×
204
           LServer,
205
           ?SQL("delete from mix_participant where "
×
206
                "channel=%(Channel)s and service=%(Service)s "
207
                "and username=%(User)s and domain=%(Domain)s")) of
208
        {updated, _} -> ok;
×
209
        _Err -> {error, db_failure}
×
210
    end.
211

212
subscribe(_LServer, _Channel, _Service, _JID, []) ->
213
    ok;
×
214
subscribe(LServer, Channel, Service, JID, Nodes) ->
215
    {User, Domain, _} = jid:tolower(JID),
×
216
    RawJID = jid:encode(jid:remove_resource(JID)),
×
217
    F = fun() ->
×
218
                lists:foreach(
×
219
                  fun(Node) ->
220
                          ?SQL_UPSERT_T(
×
221
                             "mix_subscription",
222
                             ["!channel=%(Channel)s",
223
                              "!service=%(Service)s",
224
                              "!username=%(User)s",
225
                              "!domain=%(Domain)s",
226
                              "!node=%(Node)s",
227
                              "jid=%(RawJID)s"])
228
                  end, Nodes)
229
        end,
230
    case ejabberd_sql:sql_transaction(LServer, F) of
×
231
        {atomic, _} -> ok;
×
232
        _Err -> {error, db_failure}
×
233
    end.
234

235
get_subscribed(LServer, Channel, Service, Node) ->
236
    SQL = ?SQL("select @(jid)s from mix_subscription "
×
237
               "where channel=%(Channel)s and service=%(Service)s "
238
               "and node=%(Node)s"),
239
    case ejabberd_sql:sql_query(LServer, SQL) of
×
240
        {selected, Ret} ->
241
            {ok, lists:filtermap(
×
242
                   fun({RawJID}) ->
243
                           try jid:decode(RawJID) of
×
244
                               JID -> {true, JID}
×
245
                           catch _:{bad_jid, _} ->
246
                                   report_corrupted(jid, SQL),
×
247
                                   false
×
248
                           end
249
                   end, Ret)};
250
        _Err ->
251
            {error, db_failure}
×
252
    end.
253

254
unsubscribe(LServer, Channel, Service, JID) ->
255
    {User, Domain, _} = jid:tolower(JID),
×
256
    case ejabberd_sql:sql_query(
×
257
           LServer,
258
           ?SQL("delete from mix_subscription "
×
259
                "where channel=%(Channel)s and service=%(Service)s "
260
                "and username=%(User)s and domain=%(Domain)s")) of
261
        {updated, _} -> ok;
×
262
        _Err -> {error, db_failure}
×
263
    end.
264

265
unsubscribe(_LServer, _Channel, _Service, _JID, []) ->
266
    ok;
×
267
unsubscribe(LServer, Channel, Service, JID, Nodes) ->
268
    {User, Domain, _} = jid:tolower(JID),
×
269
    F = fun() ->
×
270
                lists:foreach(
×
271
                  fun(Node) ->
272
                          ejabberd_sql:sql_query_t(
×
273
                            ?SQL("delete from mix_subscription "
×
274
                                 "where channel=%(Channel)s "
275
                                 "and service=%(Service)s "
276
                                 "and username=%(User)s "
277
                                 "and domain=%(Domain)s "
278
                                 "and node=%(Node)s"))
279
                  end, Nodes)
280
        end,
281
    case ejabberd_sql:sql_transaction(LServer, F) of
×
282
        {atomic, ok} -> ok;
×
283
        _Err -> {error, db_failure}
×
284
    end.
285

286
%%%===================================================================
287
%%% Internal functions
288
%%%===================================================================
289
-spec report_corrupted(atom(), #sql_query{}) -> ok.
290
report_corrupted(Column, SQL) ->
291
    ?ERROR_MSG("Corrupted value of '~ts' column returned by "
×
292
               "SQL request: ~ts", [Column, SQL#sql_query.hash]).
×
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

© 2025 Coveralls, Inc