• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

processone / ejabberd / 1258

12 Dec 2025 03:57PM UTC coverage: 33.638% (-0.006%) from 33.644%
1258

push

github

badlop
Container: Apply commit a22c88a

ejabberdctl.template: Show meaningful error when ERL_DIST_PORT is in use

15554 of 46240 relevant lines covered (33.64%)

1078.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.94
/src/misc.erl
1
%%%-------------------------------------------------------------------
2
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
3
%%% @doc
4
%%%   This is the place for some unsorted auxiliary functions
5
%%%   Some functions from jlib.erl are moved here
6
%%%   Mild rubbish heap is accepted ;)
7
%%% @end
8
%%% Created : 30 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
9
%%%
10
%%%
11
%%% ejabberd, Copyright (C) 2002-2025   ProcessOne
12
%%%
13
%%% This program is free software; you can redistribute it and/or
14
%%% modify it under the terms of the GNU General Public License as
15
%%% published by the Free Software Foundation; either version 2 of the
16
%%% License, or (at your option) any later version.
17
%%%
18
%%% This program is distributed in the hope that it will be useful,
19
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
20
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21
%%% General Public License for more details.
22
%%%
23
%%% You should have received a copy of the GNU General Public License along
24
%%% with this program; if not, write to the Free Software Foundation, Inc.,
25
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26
%%%
27
%%%-------------------------------------------------------------------
28
-module(misc).
29

30
%% API
31
-export([add_delay_info/3, add_delay_info/4,
32
         unwrap_carbon/1, unwrap_mucsub_message/1, is_standalone_chat_state/1,
33
         tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1,
34
         hex_to_bin/1, hex_to_base64/1, url_encode/1, expand_keyword/3,
35
         atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
36
         l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1,
37
         now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2,
38
         compile_exprs/2, join_atoms/2, try_read_file/1, get_descr/2,
39
         get_home/0, warn_unset_home/0,
40
         css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
41
         read_css/1, read_img/1, read_js/1, read_lua/1,
42
         intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0,
43
         is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
44
         get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1,
45
         uri_parse/1, uri_parse/2,
46
         json_encode/1, json_decode/1,
47
         set_proc_label/1,
48
         match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1,
49
         semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]).
50

51
%% Deprecated functions
52
-export([decode_base64/1, encode_base64/1,
53
        crypto_hmac/3, crypto_hmac/4,
54
        uri_quote/1, uri_decode/1,
55
        lists_uniq/1]).
56
-deprecated([{decode_base64, 1},
57
             {encode_base64, 1},
58
             {crypto_hmac, 3},
59
             {crypto_hmac, 4},
60
             {uri_quote, 1},
61
             {uri_decode, 1},
62
             {lists_uniq, 1}]).
63

64
-include("logger.hrl").
65
-include_lib("xmpp/include/xmpp.hrl").
66
-include_lib("kernel/include/file.hrl").
67

68
-ifdef(OTP_BELOW_27).
69
%% Copied from erlang/otp/lib/stdlib/src/re.erl
70
-type re_mp() :: {re_pattern, _, _, _, _}.
71
-type json_value() :: jiffy:json_value().
72
-else.
73
-type re_mp() :: re:mp().
74
-type json_value() :: json:encode_value().
75
-endif.
76
-export_type([re_mp/0]).
77
-export_type([json_value/0]).
78

79
-type distance_cache() :: #{{string(), string()} => non_neg_integer()}.
80

81
-spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}.
82
uri_parse(URL) ->
83
    yconf:parse_uri(URL).
180✔
84

85
uri_parse(URL, Protocols) ->
86
    yconf:parse_uri(URL, Protocols).
×
87

88
-ifdef(OTP_BELOW_27).
89
json_encode(Term) ->
90
    jiffy:encode(Term).
91
json_decode(Bin) ->
92
    jiffy:decode(Bin, [return_maps]).
93
-else.
94
json_encode({[]}) ->
95
    %% Jiffy was able to handle this case, but Json library does not
96
    <<"{}">>;
×
97
json_encode(Term) ->
98
    iolist_to_binary(json:encode(Term,
58✔
99
                     fun({Val}, Encoder) when is_list(Val) ->
100
                         json:encode_key_value_list(Val, Encoder);
×
101
                        (Val, Encoder) ->
102
                         json:encode_value(Val, Encoder)
430✔
103
                     end)).
104
json_decode(Bin) ->
105
    json:decode(Bin).
49✔
106
-endif.
107

108
%%%===================================================================
109
%%% API
110
%%%===================================================================
111
-spec add_delay_info(stanza(), jid(), erlang:timestamp()) -> stanza().
112
add_delay_info(Stz, From, Time) ->
113
    add_delay_info(Stz, From, Time, <<"">>).
613✔
114

115
-spec add_delay_info(stanza(), jid(), erlang:timestamp(), binary()) -> stanza().
116
add_delay_info(Stz, From, Time, Desc) ->
117
    Delays = xmpp:get_subtags(Stz, #delay{stamp = {0,0,0}}),
5,364✔
118
    Matching = lists:any(
5,364✔
119
        fun(#delay{from = OldFrom}) when is_record(OldFrom, jid) ->
120
               jid:tolower(From) == jid:tolower(OldFrom);
2,692✔
121
           (_) ->
122
               false
×
123
        end, Delays),
124
    case Matching of
5,364✔
125
        true ->
126
            Stz;
2,692✔
127
        _ ->
128
            NewDelay = #delay{stamp = Time, from = From, desc = Desc},
2,672✔
129
            xmpp:append_subtags(Stz, [NewDelay])
2,672✔
130
    end.
131

132
-spec unwrap_carbon(stanza()) -> xmpp_element().
133
unwrap_carbon(#message{} = Msg) ->
134
    try
2,006✔
135
        case xmpp:get_subtag(Msg, #carbons_sent{forwarded = #forwarded{}}) of
2,006✔
136
            #carbons_sent{forwarded = #forwarded{sub_els = [El]}} ->
137
                xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
×
138
            _ ->
139
                case xmpp:get_subtag(Msg, #carbons_received{
2,006✔
140
                                              forwarded = #forwarded{}}) of
141
                    #carbons_received{forwarded = #forwarded{sub_els = [El]}} ->
142
                        xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
×
143
                    _ ->
144
                        Msg
2,006✔
145
                end
146
        end
147
    catch _:{xmpp_codec, _} ->
148
            Msg
×
149
    end;
150
unwrap_carbon(Stanza) -> Stanza.
×
151

152
-spec unwrap_mucsub_message(xmpp_element()) -> message() | false.
153
unwrap_mucsub_message(#message{} = OuterMsg) ->
154
    case xmpp:get_subtag(OuterMsg, #ps_event{}) of
15,245✔
155
        #ps_event{
156
            items = #ps_items{
157
                node = Node,
158
                items = [
159
                    #ps_item{
160
                        sub_els = [#message{} = InnerMsg]} | _]}}
161
            when Node == ?NS_MUCSUB_NODES_MESSAGES;
162
                 Node == ?NS_MUCSUB_NODES_SUBJECT ->
163
            InnerMsg;
168✔
164
        _ ->
165
            false
15,077✔
166
    end;
167
unwrap_mucsub_message(_Packet) ->
168
    false.
×
169

170
-spec is_mucsub_message(xmpp_element()) -> boolean().
171
is_mucsub_message(Packet) ->
172
    get_mucsub_event_type(Packet) /= false.
10,165✔
173

174
-spec get_mucsub_event_type(xmpp_element()) -> binary() | false.
175
get_mucsub_event_type(#message{} = OuterMsg) ->
176
    case xmpp:get_subtag(OuterMsg, #ps_event{}) of
13,117✔
177
        #ps_event{
178
            items = #ps_items{
179
                node = Node}}
180
            when Node == ?NS_MUCSUB_NODES_MESSAGES;
181
                 Node == ?NS_MUCSUB_NODES_SUBJECT;
182
                 Node == ?NS_MUCSUB_NODES_AFFILIATIONS;
183
                 Node == ?NS_MUCSUB_NODES_CONFIG;
184
                 Node == ?NS_MUCSUB_NODES_PARTICIPANTS;
185
                 Node == ?NS_MUCSUB_NODES_PRESENCE;
186
                 Node == ?NS_MUCSUB_NODES_SUBSCRIBERS ->
187
            Node;
×
188
        _ ->
189
            false
13,117✔
190
    end;
191
get_mucsub_event_type(_Packet) ->
192
    false.
×
193

194
-spec is_standalone_chat_state(stanza()) -> boolean().
195
is_standalone_chat_state(Stanza) ->
196
    case unwrap_carbon(Stanza) of
2,006✔
197
        #message{body = [], subject = [], sub_els = Els} ->
198
            IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT, ?NS_HINTS],
492✔
199
            Stripped = [El || El <- Els,
492✔
200
                              not lists:member(xmpp:get_ns(El), IgnoreNS)],
716✔
201
            Stripped == [];
492✔
202
        _ ->
203
            false
1,514✔
204
    end.
205

206
-spec tolower(binary()) -> binary().
207
tolower(B) ->
208
    iolist_to_binary(tolower_s(binary_to_list(B))).
83✔
209

210
tolower_s([C | Cs]) ->
211
    if C >= $A, C =< $Z -> [C + 32 | tolower_s(Cs)];
830✔
212
       true -> [C | tolower_s(Cs)]
830✔
213
    end;
214
tolower_s([]) -> [].
83✔
215

216
-spec term_to_base64(term()) -> binary().
217
term_to_base64(Term) ->
218
    encode_base64(term_to_binary(Term)).
6,716✔
219

220
-spec base64_to_term(binary()) -> {term, term()} | error.
221
base64_to_term(Base64) ->
222
    try binary_to_term(base64:decode(Base64), [safe]) of
2✔
223
        Term -> {term, Term}
2✔
224
    catch _:_ ->
225
            error
×
226
    end.
227

228
-spec decode_base64(binary()) -> binary().
229
decode_base64(S) ->
230
    try base64:mime_decode(S)
×
231
    catch _:badarg -> <<>>
×
232
    end.
233

234
-spec encode_base64(binary()) -> binary().
235
encode_base64(Data) ->
236
    base64:encode(Data).
6,716✔
237

238
crypto_hmac(Type, Key, Data) -> crypto:mac(hmac, Type, Key, Data).
×
239
crypto_hmac(Type, Key, Data, MacL) -> crypto:macN(hmac, Type, Key, Data, MacL).
×
240
uri_quote(Data) ->
241
    uri_string:quote(Data).
×
242
uri_decode(Path) ->
243
    uri_string:percent_decode(Path).
×
244
lists_uniq(List) ->
245
    lists:uniq(List).
×
246

247
-spec ip_to_list(inet:ip_address() | undefined |
248
                 {inet:ip_address(), inet:port_number()}) -> binary().
249

250
ip_to_list({IP, _Port}) ->
251
    ip_to_list(IP);
2,124✔
252
%% This function clause could use inet_parse too:
253
ip_to_list(undefined) ->
254
    <<"unknown">>;
×
255
ip_to_list(local) ->
256
    <<"unix">>;
×
257
ip_to_list(IP) ->
258
    list_to_binary(inet_parse:ntoa(IP)).
2,149✔
259

260
-spec hex_to_bin(binary()) -> binary().
261
hex_to_bin(Hex) ->
262
    hex_to_bin(binary_to_list(Hex), []).
×
263

264
-spec hex_to_bin(list(), list()) -> binary().
265
hex_to_bin([], Acc) ->
266
    list_to_binary(lists:reverse(Acc));
×
267
hex_to_bin([H1, H2 | T], Acc) ->
268
    {ok, [V], []} = io_lib:fread("~16u", [H1, H2]),
×
269
    hex_to_bin(T, [V | Acc]).
×
270

271
-spec hex_to_base64(binary()) -> binary().
272
hex_to_base64(Hex) ->
273
    base64:encode(hex_to_bin(Hex)).
×
274

275
-spec url_encode(binary()) -> binary().
276
url_encode(A) ->
277
    url_encode(A, <<>>).
1,869✔
278

279
-spec expand_keyword(iodata(), iodata(), iodata()) -> binary().
280
expand_keyword(Keyword, Input, Replacement) ->
281
    re:replace(Input, Keyword, Replacement,
623,402✔
282
               [{return, binary}, global]).
283

284
binary_to_atom(Bin) ->
285
    erlang:binary_to_atom(Bin, utf8).
95,869✔
286

287
tuple_to_binary(T) ->
288
    iolist_to_binary(tuple_to_list(T)).
×
289

290
%% erlang:atom_to_binary/1 is available since OTP 23
291
%% https://www.erlang.org/doc/apps/erts/erlang#atom_to_binary/1
292
%% Let's use /2 for backwards compatibility.
293
atom_to_binary(A) ->
294
    erlang:atom_to_binary(A, utf8).
48,823✔
295

296
expr_to_term(Expr) ->
297
    Str = binary_to_list(<<Expr/binary, ".">>),
×
298
    {ok, Tokens, _} = erl_scan:string(Str),
×
299
    {ok, Term} = erl_parse:parse_term(Tokens),
×
300
    Term.
×
301

302
term_to_expr(Term) ->
303
    list_to_binary(io_lib:print(Term, 1, 999999, -1)).
8,298✔
304

305
-spec now_to_usec(erlang:timestamp()) -> non_neg_integer().
306
now_to_usec({MSec, Sec, USec}) ->
307
    (MSec*1000000 + Sec)*1000000 + USec.
196✔
308

309
-spec usec_to_now(non_neg_integer()) -> erlang:timestamp().
310
usec_to_now(Int) ->
311
    Secs = Int div 1000000,
5,034✔
312
    USec = Int rem 1000000,
5,034✔
313
    MSec = Secs div 1000000,
5,034✔
314
    Sec = Secs rem 1000000,
5,034✔
315
    {MSec, Sec, USec}.
5,034✔
316

317
l2i(I) when is_integer(I) -> I;
×
318
l2i(L) when is_binary(L) -> binary_to_integer(L).
×
319

320
i2l(I) when is_integer(I) -> integer_to_binary(I);
12,260✔
321
i2l(L) when is_binary(L) -> L.
×
322

323
i2l(I, N) when is_integer(I) -> i2l(i2l(I), N);
1,080✔
324
i2l(L, N) when is_binary(L) ->
325
    case str:len(L) of
1,852✔
326
      N -> L;
1,080✔
327
      C when C > N -> L;
×
328
      _ -> i2l(<<$0, L/binary>>, N)
772✔
329
    end.
330

331
-spec encode_pid(pid()) -> binary().
332
encode_pid(Pid) ->
333
    list_to_binary(erlang:pid_to_list(Pid)).
3,732✔
334

335
-spec decode_pid(binary(), binary()) -> pid().
336
decode_pid(PidBin, NodeBin) ->
337
    PidStr = binary_to_list(PidBin),
4,640✔
338
    Pid = erlang:list_to_pid(PidStr),
4,640✔
339
    case erlang:binary_to_atom(NodeBin, latin1) of
4,640✔
340
        Node when Node == node() ->
341
            Pid;
4,640✔
342
        Node ->
343
            try set_node_id(PidStr, NodeBin)
×
344
            catch _:badarg ->
345
                    erlang:error({bad_node, Node})
×
346
            end
347
    end.
348

349
-spec compile_exprs(module(), [string()]) -> ok | {error, any()}.
350
compile_exprs(Mod, Exprs) ->
351
    try
×
352
        Forms = lists:map(
×
353
                  fun(Expr) ->
354
                          {ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)),
×
355
                          {ok, Form} = erl_parse:parse_form(Tokens),
×
356
                          Form
×
357
                  end, Exprs),
358
        {ok, Code} = case compile:forms(Forms, []) of
×
359
                         {ok, Mod, Bin} -> {ok, Bin};
×
360
                         {ok, Mod, Bin, _Warnings} -> {ok, Bin};
×
361
                         Error -> Error
×
362
                     end,
363
        {module, Mod} = code:load_binary(Mod, "nofile", Code),
×
364
        ok
×
365
    catch _:{badmatch, {error, ErrInfo, _ErrLocation}} ->
366
            {error, ErrInfo};
×
367
          _:{badmatch, {error, _} = Err} ->
368
            Err;
×
369
          _:{badmatch, error} ->
370
            {error, compile_failed}
×
371
    end.
372

373
-spec join_atoms([atom()], binary()) -> binary().
374
join_atoms(Atoms, Sep) ->
375
    str:join([io_lib:format("~p", [A]) || A <- lists:sort(Atoms)], Sep).
×
376

377
%% @doc Checks if the file is readable and converts its name to binary.
378
%%      Fails with `badarg' otherwise. The function is intended for usage
379
%%      in configuration validators only.
380
-spec try_read_file(file:filename_all()) -> binary().
381
try_read_file(Path) ->
382
    case file:open(Path, [read]) of
×
383
        {ok, Fd} ->
384
            file:close(Fd),
×
385
            iolist_to_binary(Path);
×
386
        {error, Why} ->
387
            ?ERROR_MSG("Failed to read ~ts: ~ts", [Path, file:format_error(Why)]),
×
388
            erlang:error(badarg)
×
389
    end.
390

391
-spec css_dir() -> file:filename().
392
css_dir() ->
393
    get_dir("css").
×
394

395
-spec img_dir() -> file:filename().
396
img_dir() ->
397
    get_dir("img").
×
398

399
-spec js_dir() -> file:filename().
400
js_dir() ->
401
    get_dir("js").
×
402

403
-spec msgs_dir() -> file:filename().
404
msgs_dir() ->
405
    get_dir("msgs").
11✔
406

407
-spec sql_dir() -> file:filename().
408
sql_dir() ->
409
    get_dir("sql").
2✔
410

411
-spec lua_dir() -> file:filename().
412
lua_dir() ->
413
    get_dir("lua").
1✔
414

415
-spec read_css(file:filename()) -> {ok, binary()} | {error, file:posix()}.
416
read_css(File) ->
417
    read_file(filename:join(css_dir(), File)).
×
418

419
-spec read_img(file:filename()) -> {ok, binary()} | {error, file:posix()}.
420
read_img(File) ->
421
    read_file(filename:join(img_dir(), File)).
×
422

423
-spec read_js(file:filename()) -> {ok, binary()} | {error, file:posix()}.
424
read_js(File) ->
425
    read_file(filename:join(js_dir(), File)).
×
426

427
-spec read_lua(file:filename()) -> {ok, binary()} | {error, file:posix()}.
428
read_lua(File) ->
429
    read_file(filename:join(lua_dir(), File)).
1✔
430

431
-spec get_descr(binary(), binary()) -> binary().
432
get_descr(Lang, Text) ->
433
    Desc = translate:translate(Lang, Text),
×
434
    Copyright = ejabberd_config:get_copyright(),
×
435
    <<Desc/binary, $\n, Copyright/binary>>.
×
436

437
-spec get_home() -> string().
438
get_home() ->
439
    case init:get_argument(home) of
2,151✔
440
        {ok, [[Home]]} ->
441
            Home;
2,151✔
442
        error ->
443
            mnesia:system_info(directory)
×
444
    end.
445

446
warn_unset_home() ->
447
    case init:get_argument(home) of
11✔
448
        {ok, [[_Home]]} ->
449
            ok;
11✔
450
        error ->
451
            ?INFO_MSG("The 'HOME' environment variable is not set, "
×
452
                 "ejabberd will use as HOME the Mnesia directory: ~s.",
453
                 [mnesia:system_info(directory)])
×
454
    end.
455

456
-spec intersection(list(), list()) -> list().
457
intersection(L1, L2) ->
458
    lists:filter(
31✔
459
      fun(E) ->
460
              lists:member(E, L2)
33✔
461
      end, L1).
462

463
-spec format_val(any()) -> iodata().
464
format_val({yaml, S}) when is_integer(S); is_binary(S); is_atom(S) ->
465
    format_val(S);
×
466
format_val({yaml, YAML}) ->
467
    S = try fast_yaml:encode(YAML)
×
468
        catch _:_ -> YAML
×
469
        end,
470
    format_val(S);
×
471
format_val(I) when is_integer(I) ->
472
    integer_to_list(I);
×
473
format_val(B) when is_atom(B) ->
474
    erlang:atom_to_binary(B, utf8);
×
475
format_val(Term) ->
476
    S = try iolist_to_binary(Term)
×
477
        catch _:_ -> list_to_binary(io_lib:format("~p", [Term]))
×
478
        end,
479
    case binary:match(S, <<"\n">>) of
×
480
        nomatch -> S;
×
481
        _ -> [io_lib:nl(), S]
×
482
    end.
483

484
-spec cancel_timer(reference() | undefined) -> ok.
485
cancel_timer(TRef) when is_reference(TRef) ->
486
    case erlang:cancel_timer(TRef) of
38✔
487
        false ->
488
            receive {timeout, TRef, _} -> ok
×
489
            after 0 -> ok
×
490
            end;
491
        _ ->
492
            ok
38✔
493
    end;
494
cancel_timer(_) ->
495
    ok.
26✔
496

497
-spec best_match(atom() | binary() | string(),
498
                 [atom() | binary() | string()]) -> string().
499
best_match(Pattern, []) ->
500
    Pattern;
×
501
best_match(Pattern, Opts) ->
502
    String = to_string(Pattern),
×
503
    {Ds, _} = lists:mapfoldl(
×
504
                fun(Opt, Cache) ->
505
                        SOpt = to_string(Opt),
×
506
                        {Distance, Cache1} = ld(String, SOpt, Cache),
×
507
                        {{Distance, SOpt}, Cache1}
×
508
                end, #{}, Opts),
509
    element(2, lists:min(Ds)).
×
510

511
-spec logical_processors() -> non_neg_integer().
512
logical_processors() ->
513
    case erlang:system_info(logical_processors) of
568✔
514
        V when is_integer(V), V >= 2  -> V;
568✔
515
        _ -> 1
×
516
    end.
517

518
-spec pmap(fun((T1) -> T2), [T1]) -> [T2].
519
pmap(Fun, [_,_|_] = List) ->
520
    case logical_processors() of
11✔
521
        1 -> lists:map(Fun, List);
×
522
        _ ->
523
            Self = self(),
11✔
524
            lists:map(
11✔
525
              fun({Pid, Ref}) ->
526
                      receive
341✔
527
                          {Pid, Ret} ->
528
                              receive
341✔
529
                                  {'DOWN', Ref, _, _, _} ->
530
                                      Ret
341✔
531
                              end;
532
                          {'DOWN', Ref, _, _, Reason} ->
533
                              exit(Reason)
×
534
                      end
535
              end, [spawn_monitor(
341✔
536
                      fun() -> Self ! {self(), Fun(X)} end)
341✔
537
                    || X <- List])
11✔
538
    end;
539
pmap(Fun, List) ->
540
    lists:map(Fun, List).
×
541

542
-spec peach(fun((T) -> any()), [T]) -> ok.
543
peach(Fun, [_,_|_] = List) ->
544
    case logical_processors() of
11✔
545
        1 -> lists:foreach(Fun, List);
×
546
        _ ->
547
            Self = self(),
11✔
548
            lists:foreach(
11✔
549
              fun({Pid, Ref}) ->
550
                      receive
392✔
551
                          Pid ->
552
                              receive
392✔
553
                                  {'DOWN', Ref, _, _, _} ->
554
                                      ok
392✔
555
                              end;
556
                          {'DOWN', Ref, _, _, Reason} ->
557
                              exit(Reason)
×
558
                      end
559
              end, [spawn_monitor(
392✔
560
                      fun() -> Fun(X), Self ! self() end)
392✔
561
                    || X <- List])
11✔
562
    end;
563
peach(Fun, List) ->
564
    lists:foreach(Fun, List).
×
565

566
format_exception(Level, Class, Reason, Stacktrace) ->
567
    erl_error:format_exception(
×
568
      Level, Class, Reason, Stacktrace,
569
      fun(_M, _F, _A) -> false end,
×
570
      fun(Term, I) ->
571
              io_lib:print(Term, I, 80, -1)
×
572
      end).
573

574
-spec get_my_ipv4_address() -> inet:ip4_address().
575
get_my_ipv4_address() ->
576
    {ok, MyHostName} = inet:gethostname(),
111✔
577
    case inet:getaddr(MyHostName, inet) of
111✔
578
        {ok, Addr} -> Addr;
111✔
579
        {error, _} -> {127, 0, 0, 1}
×
580
    end.
581

582
-spec get_my_ipv6_address() -> inet:ip6_address().
583
get_my_ipv6_address() ->
584
    {ok, MyHostName} = inet:gethostname(),
×
585
    case inet:getaddr(MyHostName, inet6) of
×
586
        {ok, Addr} -> Addr;
×
587
        {error, _} -> {0, 0, 0, 0, 0, 0, 0, 1}
×
588
    end.
589

590
-spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} |
591
                                 {ok, {inet:ip6_address(), 0..128}} |
592
                                 error.
593
parse_ip_mask(S) ->
594
    case econf:validate(econf:ip_mask(), S) of
×
595
        {ok, _} = Ret -> Ret;
×
596
        _ -> error
×
597
    end.
598

599
-spec match_ip_mask(inet:ip_address(), inet:ip_address(), 0..128) -> boolean().
600
match_ip_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
601
    IPInt = ip_to_integer(IP),
×
602
    NetInt = ip_to_integer(Net),
×
603
    M = bnot (1 bsl (32 - Mask) - 1),
×
604
    IPInt band M =:= NetInt band M;
×
605
match_ip_mask({_, _, _, _, _, _, _, _} = IP,
606
                {_, _, _, _, _, _, _, _} = Net, Mask) ->
607
    IPInt = ip_to_integer(IP),
×
608
    NetInt = ip_to_integer(Net),
×
609
    M = bnot (1 bsl (128 - Mask) - 1),
×
610
    IPInt band M =:= NetInt band M;
×
611
match_ip_mask({_, _, _, _} = IP,
612
                {0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
613
    IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
×
614
    NetInt = ip_to_integer(Net),
×
615
    M = bnot (1 bsl (128 - Mask) - 1),
×
616
    IPInt band M =:= NetInt band M;
×
617
match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
618
                {_, _, _, _} = Net, Mask) ->
619
    IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
×
620
    NetInt = ip_to_integer(Net),
×
621
    M = bnot (1 bsl (32 - Mask) - 1),
×
622
    IPInt band M =:= NetInt band M;
×
623
match_ip_mask(_, _, _) ->
624
    false.
×
625

626
-spec format_hosts_list([binary(), ...]) -> iolist().
627
format_hosts_list([Host]) ->
628
    Host;
×
629
format_hosts_list([H1, H2]) ->
630
    [H1, " and ", H2];
×
631
format_hosts_list([H1, H2, H3]) ->
632
    [H1, ", ", H2, " and ", H3];
×
633
format_hosts_list([H1, H2|Hs]) ->
634
    io_lib:format("~ts, ~ts and ~B more hosts",
11✔
635
                  [H1, H2, length(Hs)]).
636

637
-spec format_cycle([atom(), ...]) -> iolist().
638
format_cycle([M1]) ->
639
    atom_to_list(M1);
×
640
format_cycle([M1, M2]) ->
641
    [atom_to_list(M1), " and ", atom_to_list(M2)];
×
642
format_cycle([M|Ms]) ->
643
    atom_to_list(M) ++ ", " ++ format_cycle(Ms).
×
644

645
-spec delete_dir(file:filename_all()) -> ok | {error, file:posix()}.
646
delete_dir(Dir) ->
647
    try
1✔
648
        {ok, Entries} = file:list_dir(Dir),
1✔
649
        lists:foreach(fun(Path) ->
×
650
                              case filelib:is_dir(Path) of
×
651
                                  true ->
652
                                      ok = delete_dir(Path);
×
653
                                  false ->
654
                                      ok = file:delete(Path)
×
655
                              end
656
                      end, [filename:join(Dir, Entry) || Entry <- Entries]),
×
657
        ok = file:del_dir(Dir)
×
658
    catch
659
        _:{badmatch, {error, Error}} ->
660
            {error, Error}
1✔
661
    end.
662

663
-spec semver_to_xxyy(binary()) -> binary().
664
semver_to_xxyy(<<Y1, Y2, $., M2, $., $0>>) ->
665
    <<Y1, Y2, $., $0, M2>>;
×
666
semver_to_xxyy(<<Y1, Y2, $., M2, $., Patch/binary>>) ->
667
    <<Y1, Y2, $., $0, M2, $., Patch/binary>>;
×
668
semver_to_xxyy(<<Y1, Y2, $., M1, M2, $., $0>>) ->
669
    <<Y1, Y2, $., M1, M2>>;
×
670
semver_to_xxyy(Version) when is_binary(Version) ->
671
    Version.
1,899✔
672

673
%%%===================================================================
674
%%% Internal functions
675
%%%===================================================================
676
-spec url_encode(binary(), binary()) -> binary().
677
url_encode(<<H:8, T/binary>>, Acc) when
678
  (H >= $a andalso H =< $z) orelse
679
  (H >= $A andalso H =< $Z) orelse
680
  (H >= $0 andalso H =< $9) orelse
681
  H == $_ orelse
682
  H == $. orelse
683
  H == $- orelse
684
  H == $/ orelse
685
  H == $: ->
686
    url_encode(T, <<Acc/binary, H>>);
29,448✔
687
url_encode(<<H:8, T/binary>>, Acc) ->
688
    case integer_to_list(H, 16) of
1,160✔
689
        [X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
1,160✔
690
        [X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
×
691
    end;
692
url_encode(<<>>, Acc) ->
693
    Acc.
1,869✔
694

695
-spec set_node_id(string(), binary()) -> pid().
696
set_node_id(PidStr, NodeBin) ->
697
    ExtPidStr = erlang:pid_to_list(
×
698
                  binary_to_term(
699
                    <<131,103,100,(size(NodeBin)):16,NodeBin/binary,0:72>>)),
700
    [H|_] = string:tokens(ExtPidStr, "."),
×
701
    [_|T] = string:tokens(PidStr, "."),
×
702
    erlang:list_to_pid(string:join([H|T], ".")).
×
703

704
-spec read_file(file:filename()) -> {ok, binary()} | {error, file:posix()}.
705
read_file(Path) ->
706
    case file:read_file(Path) of
1✔
707
        {ok, Data} ->
708
            {ok, Data};
1✔
709
        {error, Why} = Err ->
710
            ?ERROR_MSG("Failed to read file ~ts: ~ts",
×
711
                       [Path, file:format_error(Why)]),
×
712
            Err
×
713
    end.
714

715
-spec get_dir(string()) -> file:filename().
716
get_dir(Type) ->
717
    Env = "EJABBERD_" ++ string:to_upper(Type) ++ "_PATH",
14✔
718
    case os:getenv(Env) of
14✔
719
        false ->
720
            case code:priv_dir(ejabberd) of
14✔
721
                {error, _} -> filename:join(["priv", Type]);
×
722
                Path -> filename:join([Path, Type])
14✔
723
            end;
724
        Path ->
725
            Path
×
726
    end.
727

728
%% Generates erlang:timestamp() that is guaranteed to unique
729
-spec unique_timestamp() -> erlang:timestamp().
730
unique_timestamp() ->
731
    {MS, S, _} = erlang:timestamp(),
2,012✔
732
    {MS, S, erlang:unique_integer([positive, monotonic]) rem 1000000}.
2,012✔
733

734
%% Levenshtein distance
735
-spec ld(string(), string(), distance_cache()) -> {non_neg_integer(), distance_cache()}.
736
ld([] = S, T, Cache) ->
737
    {length(T), maps:put({S, T}, length(T), Cache)};
×
738
ld(S, [] = T, Cache) ->
739
    {length(S), maps:put({S, T}, length(S), Cache)};
×
740
ld([X|S], [X|T], Cache) ->
741
    ld(S, T, Cache);
×
742
ld([_|ST] = S, [_|TT] = T, Cache) ->
743
    try {maps:get({S, T}, Cache), Cache}
×
744
    catch _:{badkey, _} ->
745
            {L1, C1} = ld(S, TT, Cache),
×
746
            {L2, C2} = ld(ST, T, C1),
×
747
            {L3, C3} = ld(ST, TT, C2),
×
748
            L = 1 + lists:min([L1, L2, L3]),
×
749
            {L, maps:put({S, T}, L, C3)}
×
750
    end.
751

752
-spec ip_to_integer(inet:ip_address()) -> non_neg_integer().
753
ip_to_integer({IP1, IP2, IP3, IP4}) ->
754
    IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
×
755
ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
756
               IP8}) ->
757
    IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
758
        bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8.
×
759

760
-spec to_string(atom() | binary() | string()) -> string().
761
to_string(A) when is_atom(A) ->
762
    atom_to_list(A);
×
763
to_string(B) when is_binary(B) ->
764
    binary_to_list(B);
×
765
to_string(S) ->
766
    S.
×
767

768
-ifdef(OTP_BELOW_27).
769
set_proc_label(_Label) ->
770
    ok.
771
-else.
772
set_proc_label(Label) ->
773
    proc_lib:set_label(Label).
4,616✔
774
-endif.
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