• 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

23.08
/src/mod_vcard_sql.erl
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_vcard_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_vcard_sql).
26

27

28
-behaviour(mod_vcard).
29

30
%% API
31
-export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4, remove_user/2,
32
         search_fields/1, search_reported/1, import/3, export/1]).
33
-export([is_search_supported/1]).
34
-export([sql_schemas/0]).
35

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

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

49
sql_schemas() ->
UNCOV
50
    [#sql_schema{
7✔
51
        version = 1,
52
        tables =
53
            [#sql_table{
54
                name = <<"vcard">>,
55
                columns =
56
                    [#sql_column{name = <<"username">>, type = text},
57
                     #sql_column{name = <<"server_host">>, type = text},
58
                     #sql_column{name = <<"vcard">>, type = {text, big}},
59
                     #sql_column{name = <<"created_at">>, type = timestamp,
60
                                 default = true}],
61
                indices = [#sql_index{
62
                              columns = [<<"server_host">>, <<"username">>],
63
                              unique = true}]},
64
             #sql_table{
65
                name = <<"vcard_search">>,
66
                columns =
67
                    [#sql_column{name = <<"username">>, type = text},
68
                     #sql_column{name = <<"lusername">>, type = text},
69
                     #sql_column{name = <<"server_host">>, type = text},
70
                     #sql_column{name = <<"fn">>, type = text},
71
                     #sql_column{name = <<"lfn">>, type = text},
72
                     #sql_column{name = <<"family">>, type = text},
73
                     #sql_column{name = <<"lfamily">>, type = text},
74
                     #sql_column{name = <<"given">>, type = text},
75
                     #sql_column{name = <<"lgiven">>, type = text},
76
                     #sql_column{name = <<"middle">>, type = text},
77
                     #sql_column{name = <<"lmiddle">>, type = text},
78
                     #sql_column{name = <<"nickname">>, type = text},
79
                     #sql_column{name = <<"lnickname">>, type = text},
80
                     #sql_column{name = <<"bday">>, type = text},
81
                     #sql_column{name = <<"lbday">>, type = text},
82
                     #sql_column{name = <<"ctry">>, type = text},
83
                     #sql_column{name = <<"lctry">>, type = text},
84
                     #sql_column{name = <<"locality">>, type = text},
85
                     #sql_column{name = <<"llocality">>, type = text},
86
                     #sql_column{name = <<"email">>, type = text},
87
                     #sql_column{name = <<"lemail">>, type = text},
88
                     #sql_column{name = <<"orgname">>, type = text},
89
                     #sql_column{name = <<"lorgname">>, type = text},
90
                     #sql_column{name = <<"orgunit">>, type = text},
91
                     #sql_column{name = <<"lorgunit">>, type = text}],
92
                indices = [#sql_index{
93
                              columns = [<<"server_host">>, <<"lusername">>],
94
                              unique = true},
95
                           #sql_index{
96
                              columns = [<<"server_host">>, <<"lfn">>]},
97
                           #sql_index{
98
                              columns = [<<"server_host">>, <<"lfamily">>]},
99
                           #sql_index{
100
                              columns = [<<"server_host">>, <<"lgiven">>]},
101
                           #sql_index{
102
                              columns = [<<"server_host">>, <<"lmiddle">>]},
103
                           #sql_index{
104
                              columns = [<<"server_host">>, <<"lnickname">>]},
105
                           #sql_index{
106
                              columns = [<<"server_host">>, <<"lbday">>]},
107
                           #sql_index{
108
                              columns = [<<"server_host">>, <<"lctry">>]},
109
                           #sql_index{
110
                              columns = [<<"server_host">>, <<"llocality">>]},
111
                           #sql_index{
112
                              columns = [<<"server_host">>, <<"lemail">>]},
113
                           #sql_index{
114
                              columns = [<<"server_host">>, <<"lorgname">>]},
115
                           #sql_index{
116
                              columns = [<<"server_host">>, <<"lorgunit">>]}]}]}].
117

118
stop(_Host) ->
UNCOV
119
    ok.
7✔
120

121
is_search_supported(_LServer) ->
122
    true.
×
123

124
get_vcard(LUser, LServer) ->
UNCOV
125
    case ejabberd_sql:sql_query(
28✔
126
           LServer,
UNCOV
127
           ?SQL("select @(vcard)s from vcard"
28✔
128
                " where username=%(LUser)s and %(LServer)H")) of
129
        {selected, [{SVCARD}]} ->
UNCOV
130
            case fxml_stream:parse_element(SVCARD) of
7✔
131
                {error, _Reason} -> error;
×
UNCOV
132
                VCARD -> {ok, [VCARD]}
7✔
133
            end;
UNCOV
134
        {selected, []} -> {ok, []};
21✔
135
        _ -> error
×
136
    end.
137

138
set_vcard(LUser, LServer, VCARD,
139
          #vcard_search{user = {User, _},
140
                        fn = FN,
141
                        lfn = LFN,
142
                        family = Family,
143
                        lfamily = LFamily,
144
                        given = Given,
145
                        lgiven = LGiven,
146
                        middle = Middle,
147
                        lmiddle = LMiddle,
148
                        nickname = Nickname,
149
                        lnickname = LNickname,
150
                        bday = BDay,
151
                        lbday = LBDay,
152
                        ctry = CTRY,
153
                        lctry = LCTRY,
154
                        locality = Locality,
155
                        llocality = LLocality,
156
                        email = EMail,
157
                        lemail = LEMail,
158
                        orgname = OrgName,
159
                        lorgname = LOrgName,
160
                        orgunit = OrgUnit,
161
                        lorgunit = LOrgUnit}) ->
UNCOV
162
    SVCARD = fxml:element_to_binary(VCARD),
7✔
UNCOV
163
    ejabberd_sql:sql_transaction(
7✔
164
      LServer,
165
      fun() ->
UNCOV
166
              ?SQL_UPSERT(LServer, "vcard",
7✔
167
                          ["!username=%(LUser)s",
168
                           "!server_host=%(LServer)s",
169
                           "vcard=%(SVCARD)s"]),
UNCOV
170
              ?SQL_UPSERT(LServer, "vcard_search",
7✔
171
                          ["username=%(User)s",
172
                           "!lusername=%(LUser)s",
173
                           "!server_host=%(LServer)s",
174
                           "fn=%(FN)s",
175
                           "lfn=%(LFN)s",
176
                           "family=%(Family)s",
177
                           "lfamily=%(LFamily)s",
178
                           "given=%(Given)s",
179
                           "lgiven=%(LGiven)s",
180
                           "middle=%(Middle)s",
181
                           "lmiddle=%(LMiddle)s",
182
                           "nickname=%(Nickname)s",
183
                           "lnickname=%(LNickname)s",
184
                           "bday=%(BDay)s",
185
                           "lbday=%(LBDay)s",
186
                           "ctry=%(CTRY)s",
187
                           "lctry=%(LCTRY)s",
188
                           "locality=%(Locality)s",
189
                           "llocality=%(LLocality)s",
190
                           "email=%(EMail)s",
191
                           "lemail=%(LEMail)s",
192
                           "orgname=%(OrgName)s",
193
                           "lorgname=%(LOrgName)s",
194
                           "orgunit=%(OrgUnit)s",
195
                           "lorgunit=%(LOrgUnit)s"])
196
      end).
197

198
search(LServer, Data, AllowReturnAll, MaxMatch) ->
199
    MatchSpec = make_matchspec(LServer, Data),
×
200
    if (MatchSpec == <<"">>) and not AllowReturnAll -> [];
×
201
       true ->
202
            Limit = case MaxMatch of
×
203
                        infinity ->
204
                            <<"">>;
×
205
                        Val ->
206
                            [<<" LIMIT ">>, integer_to_binary(Val)]
×
207
                    end,
208
           case catch ejabberd_sql:sql_query(
×
209
                        LServer,
210
                        [<<"select username, fn, family, given, "
211
                           "middle,        nickname, bday, ctry, "
212
                           "locality,        email, orgname, orgunit "
213
                           "from vcard_search ">>,
214
                         MatchSpec, Limit, <<";">>]) of
215
               {selected,
216
                [<<"username">>, <<"fn">>, <<"family">>, <<"given">>,
217
                 <<"middle">>, <<"nickname">>, <<"bday">>, <<"ctry">>,
218
                 <<"locality">>, <<"email">>, <<"orgname">>,
219
                 <<"orgunit">>], Rs} when is_list(Rs) ->
220
                   [row_to_item(LServer, R) || R <- Rs];
×
221
               Error ->
222
                   ?ERROR_MSG("~p", [Error]), []
×
223
           end
224
    end.
225

226
search_fields(_LServer) ->
227
    [{?T("User"), <<"user">>},
×
228
     {?T("Full Name"), <<"fn">>},
229
     {?T("Name"), <<"first">>},
230
     {?T("Middle Name"), <<"middle">>},
231
     {?T("Family Name"), <<"last">>},
232
     {?T("Nickname"), <<"nick">>},
233
     {?T("Birthday"), <<"bday">>},
234
     {?T("Country"), <<"ctry">>},
235
     {?T("City"), <<"locality">>},
236
     {?T("Email"), <<"email">>},
237
     {?T("Organization Name"), <<"orgname">>},
238
     {?T("Organization Unit"), <<"orgunit">>}].
239

240
search_reported(_LServer) ->
241
    [{?T("Jabber ID"), <<"jid">>},
×
242
     {?T("Full Name"), <<"fn">>},
243
     {?T("Name"), <<"first">>},
244
     {?T("Middle Name"), <<"middle">>},
245
     {?T("Family Name"), <<"last">>},
246
     {?T("Nickname"), <<"nick">>},
247
     {?T("Birthday"), <<"bday">>},
248
     {?T("Country"), <<"ctry">>},
249
     {?T("City"), <<"locality">>},
250
     {?T("Email"), <<"email">>},
251
     {?T("Organization Name"), <<"orgname">>},
252
     {?T("Organization Unit"), <<"orgunit">>}].
253

254
remove_user(LUser, LServer) ->
UNCOV
255
    ejabberd_sql:sql_transaction(
14✔
256
      LServer,
257
      fun() ->
UNCOV
258
              ejabberd_sql:sql_query_t(
14✔
UNCOV
259
                ?SQL("delete from vcard"
14✔
260
                     " where username=%(LUser)s and %(LServer)H")),
UNCOV
261
              ejabberd_sql:sql_query_t(
14✔
UNCOV
262
                ?SQL("delete from vcard_search"
14✔
263
                     " where lusername=%(LUser)s and %(LServer)H"))
264
      end).
265

266
export(_Server) ->
267
    [{vcard,
×
268
      fun(Host, #vcard{us = {LUser, LServer}, vcard = VCARD})
269
            when LServer == Host ->
270
              SVCARD = fxml:element_to_binary(VCARD),
×
271
              [?SQL("delete from vcard"
×
272
                    " where username=%(LUser)s and %(LServer)H;"),
273
               ?SQL_INSERT("vcard",
×
274
                           ["username=%(LUser)s",
275
                            "server_host=%(LServer)s",
276
                            "vcard=%(SVCARD)s"])];
277
         (_Host, _R) ->
278
              []
×
279
      end},
280
     {vcard_search,
281
      fun(Host, #vcard_search{user = {User, LServer}, luser = LUser,
282
                              fn = FN, lfn = LFN, family = Family,
283
                              lfamily = LFamily, given = Given,
284
                              lgiven = LGiven, middle = Middle,
285
                              lmiddle = LMiddle, nickname = Nickname,
286
                              lnickname = LNickname, bday = BDay,
287
                              lbday = LBDay, ctry = CTRY, lctry = LCTRY,
288
                              locality = Locality, llocality = LLocality,
289
                              email = EMail, lemail = LEMail,
290
                              orgname = OrgName, lorgname = LOrgName,
291
                              orgunit = OrgUnit, lorgunit = LOrgUnit})
292
            when LServer == Host ->
293
              [?SQL("delete from vcard_search"
×
294
                    " where lusername=%(LUser)s and %(LServer)H;"),
295
               ?SQL_INSERT("vcard_search",
×
296
                           ["username=%(User)s",
297
                            "lusername=%(LUser)s",
298
                            "server_host=%(LServer)s",
299
                            "fn=%(FN)s",
300
                            "lfn=%(LFN)s",
301
                            "family=%(Family)s",
302
                            "lfamily=%(LFamily)s",
303
                            "given=%(Given)s",
304
                            "lgiven=%(LGiven)s",
305
                            "middle=%(Middle)s",
306
                            "lmiddle=%(LMiddle)s",
307
                            "nickname=%(Nickname)s",
308
                            "lnickname=%(LNickname)s",
309
                            "bday=%(BDay)s",
310
                            "lbday=%(LBDay)s",
311
                            "ctry=%(CTRY)s",
312
                            "lctry=%(LCTRY)s",
313
                            "locality=%(Locality)s",
314
                            "llocality=%(LLocality)s",
315
                            "email=%(EMail)s",
316
                            "lemail=%(LEMail)s",
317
                            "orgname=%(OrgName)s",
318
                            "lorgname=%(LOrgName)s",
319
                            "orgunit=%(OrgUnit)s",
320
                            "lorgunit=%(LOrgUnit)s"])];
321
         (_Host, _R) ->
322
              []
×
323
      end}].
324

325
import(_, _, _) ->
326
    ok.
×
327

328
%%%===================================================================
329
%%% Internal functions
330
%%%===================================================================
331
make_matchspec(LServer, Data) ->
332
    filter_fields(Data, <<"">>, LServer).
×
333

334
filter_fields([], Match, LServer) ->
335
    case ejabberd_sql:use_multihost_schema() of
×
336
        true ->
337
            SQLType = ejabberd_option:sql_type(LServer),
×
338
            SServer = ejabberd_sql:to_string_literal(SQLType, LServer),
×
339
            case Match of
×
340
                <<"">> -> [<<"where server_host=">>, SServer];
×
341
                _ -> [<<" where server_host=">>, SServer, <<" and ">>, Match]
×
342
            end;
343
        false ->
344
            case Match of
×
345
                <<"">> -> <<"">>;
×
346
                _ -> [<<" where ">>, Match]
×
347
            end
348
    end;
349
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
350
  when is_binary(Val) and (Val /= <<"">>) ->
351
    LVal = mod_vcard:string2lower(Val),
×
352
    NewMatch = case SVar of
×
353
                   <<"user">> -> make_val(LServer, Match, <<"lusername">>, LVal);
×
354
                   <<"fn">> -> make_val(LServer, Match, <<"lfn">>, LVal);
×
355
                   <<"last">> -> make_val(LServer, Match, <<"lfamily">>, LVal);
×
356
                   <<"first">> -> make_val(LServer, Match, <<"lgiven">>, LVal);
×
357
                   <<"middle">> -> make_val(LServer, Match, <<"lmiddle">>, LVal);
×
358
                   <<"nick">> -> make_val(LServer, Match, <<"lnickname">>, LVal);
×
359
                   <<"bday">> -> make_val(LServer, Match, <<"lbday">>, LVal);
×
360
                   <<"ctry">> -> make_val(LServer, Match, <<"lctry">>, LVal);
×
361
                   <<"locality">> ->
362
                       make_val(LServer, Match, <<"llocality">>, LVal);
×
363
                   <<"email">> -> make_val(LServer, Match, <<"lemail">>, LVal);
×
364
                   <<"orgname">> -> make_val(LServer, Match, <<"lorgname">>, LVal);
×
365
                   <<"orgunit">> -> make_val(LServer, Match, <<"lorgunit">>, LVal);
×
366
                   _ -> Match
×
367
               end,
368
    filter_fields(Ds, NewMatch, LServer);
×
369
filter_fields([_ | Ds], Match, LServer) ->
370
    filter_fields(Ds, Match, LServer).
×
371

372
make_val(LServer, Match, Field, Val) ->
373
    Condition = case str:suffix(<<"*">>, Val) of
×
374
                  true ->
375
                      Val1 = str:substr(Val, 1, byte_size(Val) - 1),
×
376
                      SVal = <<(ejabberd_sql:escape(
×
377
                                  ejabberd_sql:escape_like_arg_circumflex(
378
                                    Val1)))/binary,
379
                               "%">>,
380
                      [Field, <<" LIKE '">>, SVal, <<"' ESCAPE '^'">>];
×
381
                  _ ->
382
                      SQLType = ejabberd_option:sql_type(LServer),
×
383
                      SVal = ejabberd_sql:to_string_literal(SQLType, Val),
×
384
                      [Field, <<" = ">>, SVal]
×
385
                end,
386
    case Match of
×
387
      <<"">> -> Condition;
×
388
      _ -> [Match, <<" and ">>, Condition]
×
389
    end.
390

391
row_to_item(LServer, [Username, FN, Family, Given, Middle, Nickname, BDay,
392
                      CTRY, Locality, EMail, OrgName, OrgUnit]) ->
393
    [{<<"jid">>, <<Username/binary, $@, LServer/binary>>},
×
394
     {<<"fn">>, FN},
395
     {<<"last">>, Family},
396
     {<<"first">>, Given},
397
     {<<"middle">>, Middle},
398
     {<<"nick">>, Nickname},
399
     {<<"bday">>, BDay},
400
     {<<"ctry">>, CTRY},
401
     {<<"locality">>, Locality},
402
     {<<"email">>, EMail},
403
     {<<"orgname">>, OrgName},
404
     {<<"orgunit">>, OrgUnit}].
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