• 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

5.43
/src/pubsub_subscription_sql.erl
1
%%%----------------------------------------------------------------------
2
%%% File    : pubsub_subscription_sql.erl
3
%%% Author  : Pablo Polvorin <pablo.polvorin@process-one.net>
4
%%% Purpose : Handle pubsub subscriptions options with ODBC backend
5
%%%           based on pubsub_subscription.erl by Brian Cully <bjc@kublai.com>
6
%%% Created :  7 Aug 2009 by Pablo Polvorin <pablo.polvorin@process-one.net>
7
%%%
8
%%%
9
%%% ejabberd, Copyright (C) 2002-2025   ProcessOne
10
%%%
11
%%% This program is free software; you can redistribute it and/or
12
%%% modify it under the terms of the GNU General Public License as
13
%%% published by the Free Software Foundation; either version 2 of the
14
%%% License, or (at your option) any later version.
15
%%%
16
%%% This program is distributed in the hope that it will be useful,
17
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
18
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
%%% General Public License for more details.
20
%%%
21
%%% You should have received a copy of the GNU General Public License along
22
%%% with this program; if not, write to the Free Software Foundation, Inc.,
23
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
%%%
25
%%%----------------------------------------------------------------------
26

27
-module(pubsub_subscription_sql).
28
-author("pablo.polvorin@process-one.net").
29

30
%% API
31
-export([init/3, subscribe_node/3, unsubscribe_node/3,
32
    get_subscription/3, set_subscription/4,
33
    make_subid/0,
34
    get_options_xform/2, parse_options_xform/1]).
35

36
-include("pubsub.hrl").
37
-include_lib("xmpp/include/xmpp.hrl").
38
-include("translate.hrl").
39

40
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
41
-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
42
-define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>).
43
-define(PUBSUB_EXPIRE, <<"pubsub#expire">>).
44
-define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>).
45
-define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
46
-define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
47
-define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
48
-define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
49
-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
50
        "(aggregations) of notifications or all notifications individually">>).
51
-define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
52
        "sending any two notification digests">>).
53
-define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>).
54
-define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an "
55
        "XMPP message body in addition to the payload format">>).
56
-define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>).
57
-define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>).
58
-define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>).
59
-define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>).
60
-define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>).
61
-define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>).
62
-define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>).
63
-define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>).
64
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>).
65
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>).
66
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>).
67
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>).
68

69
-define(DB_MOD, pubsub_db_sql).
70
%%====================================================================
71
%% API
72
%%====================================================================
73

74
init(_Host, _ServerHost, _Opts) -> ok = create_table().
×
75

76
-spec subscribe_node(_JID :: _, _NodeId :: _, Options :: [] | mod_pubsub:subOptions()) ->
77
                            {result, mod_pubsub:subId()}.
78

79
subscribe_node(_JID, _NodeId, Options) ->
80
    SubID = make_subid(),
×
81
    (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}),
×
82
    {result, SubID}.
×
83

84
-spec unsubscribe_node(_JID :: _, _NodeId :: _, SubID :: mod_pubsub:subId()) ->
85
                              {result, mod_pubsub:subscription()} | {error, notfound}.
86

87
unsubscribe_node(_JID, _NodeId, SubID) ->
88
    case (?DB_MOD):read_subscription(SubID) of
×
89
        {ok, Sub} -> (?DB_MOD):delete_subscription(SubID), {result, Sub};
×
90
        notfound -> {error, notfound}
×
91
    end.
92

93
-spec get_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId()) ->
94
                              {result, mod_pubsub:subscription()} | {error, notfound}.
95

96
get_subscription(_JID, _NodeId, SubID) ->
97
    case (?DB_MOD):read_subscription(SubID) of
×
98
        {ok, Sub} -> {result, Sub};
×
99
        notfound -> {error, notfound}
×
100
    end.
101

102
-spec set_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId(),
103
                       Options :: mod_pubsub:subOptions()) -> {result, ok}.
104
set_subscription(_JID, _NodeId, SubID, Options) ->
105
    case (?DB_MOD):read_subscription(SubID) of
×
106
        {ok, _} ->
107
            (?DB_MOD):update_subscription(#pubsub_subscription{subid = SubID,
×
108
                    options = Options}),
109
            {result, ok};
×
110
        notfound ->
111
            (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID,
×
112
                    options = Options}),
113
            {result, ok}
×
114
    end.
115

116
get_options_xform(Lang, Options) ->
117
    Keys = [deliver, show_values, subscription_type, subscription_depth],
×
118
    XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
×
119
    {result,
×
120
     #xdata{type = form,
121
            fields = [#xdata_field{type = hidden,
122
                                   var = <<"FORM_TYPE">>,
123
                                   values = [?NS_PUBSUB_SUB_OPTIONS]}|
124
                      XFields]}}.
125

126
parse_options_xform(XFields) ->
UNCOV
127
    Opts = set_xoption(XFields, []),
119✔
UNCOV
128
    {result, Opts}.
119✔
129

130
%%====================================================================
131
%% Internal functions
132
%%====================================================================
133
create_table() -> ok.
×
134

135
-spec make_subid() -> mod_pubsub:subId().
136
make_subid() ->
UNCOV
137
    {T1, T2, T3} = erlang:timestamp(),
112✔
UNCOV
138
    (str:format("~.16B~.16B~.16B", [T1, T2, T3])).
112✔
139

140
%%
141
%% Subscription XForm processing.
142
%%
143

144
%% Return processed options, with types converted and so forth, using
145
%% Opts as defaults.
UNCOV
146
set_xoption([], Opts) -> Opts;
119✔
147
set_xoption([{Var, Value} | T], Opts) ->
148
    NewOpts = case var_xfield(Var) of
×
149
        {error, _} -> Opts;
×
150
        Key ->
151
            Val = val_xfield(Key, Value),
×
152
            lists:keystore(Key, 1, Opts, {Key, Val})
×
153
    end,
154
    set_xoption(T, NewOpts).
×
155

156
%% Return the options list's key for an XForm var.
157
%% Convert Values for option list's Key.
158
var_xfield(?PUBSUB_DELIVER) -> deliver;
×
159
var_xfield(?PUBSUB_DIGEST) -> digest;
×
160
var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
×
161
var_xfield(?PUBSUB_EXPIRE) -> expire;
×
162
var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
×
163
var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
×
164
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
×
165
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
×
166
var_xfield(_) -> {error, badarg}.
×
167

168
val_xfield(deliver = Opt, [Val]) -> xopt_to_bool(Opt, Val);
×
169
val_xfield(digest = Opt, [Val]) -> xopt_to_bool(Opt, Val);
×
170
val_xfield(digest_frequency = Opt, [Val]) ->
171
    case catch binary_to_integer(Val) of
×
172
        N when is_integer(N) -> N;
×
173
        _ ->
174
            Txt = {?T("Value of '~s' should be integer"), [Opt]},
×
175
            {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
×
176
    end;
177
val_xfield(expire = Opt, [Val]) ->
178
    try xmpp_util:decode_timestamp(Val)
×
179
    catch _:{bad_timestamp, _} ->
180
            Txt = {?T("Value of '~s' should be datetime string"), [Opt]},
×
181
            {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
×
182
    end;
183
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
×
184
val_xfield(show_values, Vals) -> Vals;
×
185
val_xfield(subscription_type, [<<"items">>]) -> items;
×
186
val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
×
187
val_xfield(subscription_depth, [<<"all">>]) -> all;
×
188
val_xfield(subscription_depth = Opt, [Depth]) ->
189
    case catch binary_to_integer(Depth) of
×
190
        N when is_integer(N) -> N;
×
191
        _ ->
192
            Txt = {?T("Value of '~s' should be integer"), [Opt]},
×
193
            {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
×
194
    end.
195

196
%% Convert XForm booleans to Erlang booleans.
197
xopt_to_bool(_, <<"0">>) -> false;
×
198
xopt_to_bool(_, <<"1">>) -> true;
×
199
xopt_to_bool(_, <<"false">>) -> false;
×
200
xopt_to_bool(_, <<"true">>) -> true;
×
201
xopt_to_bool(Option, _) ->
202
    Txt = {?T("Value of '~s' should be boolean"), [Option]},
×
203
    {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}.
×
204

205
%% Return a field for an XForm for Key, with data filled in, if
206
%% applicable, from Options.
207
get_option_xfield(Lang, Key, Options) ->
208
    Var = xfield_var(Key),
×
209
    Label = xfield_label(Key),
×
210
    {Type, OptEls} = type_and_options(xfield_type(Key), Lang),
×
211
    Vals = case lists:keysearch(Key, 1, Options) of
×
212
               {value, {_, Val}} ->
213
                   [xfield_val(Key, Val)];
×
214
               false ->
215
                   []
×
216
           end,
217
    #xdata_field{type = Type, var = Var,
×
218
                 label = translate:translate(Lang, Label),
219
                 values = Vals,
220
                 options = OptEls}.
221

222
type_and_options({Type, Options}, Lang) ->
223
    {Type, [tr_xfield_options(O, Lang) || O <- Options]};
×
224
type_and_options(Type, _Lang) -> {Type, []}.
×
225

226
tr_xfield_options({Value, Label}, Lang) ->
227
    #xdata_option{label = translate:translate(Lang, Label),
×
228
                  value = Value}.
229

230
xfield_var(deliver) -> ?PUBSUB_DELIVER;
×
231
%xfield_var(digest) -> ?PUBSUB_DIGEST;
232
%xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
233
%xfield_var(expire) -> ?PUBSUB_EXPIRE;
234
%xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
235
xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
×
236
xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
×
237
xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
×
238

239
xfield_type(deliver) -> boolean;
×
240
%xfield_type(digest) -> boolean;
241
%xfield_type(digest_frequency) -> 'text-single';
242
%xfield_type(expire) -> 'text-single';
243
%xfield_type(include_body) -> boolean;
244
xfield_type(show_values) ->
245
    {'list-multi',
×
246
     [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
247
      {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
248
      {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
249
      {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
250
      {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
251
xfield_type(subscription_type) ->
252
    {'list-single',
×
253
     [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
254
      {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
255
xfield_type(subscription_depth) ->
256
    {'list-single',
×
257
     [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
258
      {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
259

260
%% Return the XForm variable label for a subscription option key.
261
xfield_label(deliver) -> ?DELIVER_LABEL;
×
262
%xfield_label(digest) -> ?DIGEST_LABEL;
263
%xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
264
%xfield_label(expire) -> ?EXPIRE_LABEL;
265
%xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
266
xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
×
267
%% Return the XForm value for a subscription option key.
268
%% Convert erlang booleans to XForms.
269
xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
×
270
xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
×
271

272
xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
×
273
%xfield_val(digest, Val) -> [bool_to_xopt(Val)];
274
%xfield_val(digest_frequency, Val) ->
275
%    [integer_to_binary(Val))];
276
%xfield_val(expire, Val) ->
277
%    [jlib:now_to_utc_string(Val)];
278
%xfield_val(include_body, Val) -> [bool_to_xopt(Val)];
279
xfield_val(show_values, Val) -> Val;
×
280
xfield_val(subscription_type, items) -> [<<"items">>];
×
281
xfield_val(subscription_type, nodes) -> [<<"nodes">>];
×
282
xfield_val(subscription_depth, all) -> [<<"all">>];
×
283
xfield_val(subscription_depth, N) ->
284
    [integer_to_binary(N)].
×
285

286
bool_to_xopt(false) -> <<"false">>;
×
287
bool_to_xopt(true) -> <<"true">>.
×
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