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

processone / ejabberd / 714

07 May 2024 09:28AM UTC coverage: 31.229% (-0.02%) from 31.246%
714

push

github

badlop
Bump exsync from 0.4.0 to 0.4.1

Bumps [exsync](https://github.com/falood/exsync) from 0.4.0 to 0.4.1.
- [Changelog](https://github.com/falood/exsync/blob/main/CHANGELOG.md)
- [Commits](https://github.com/falood/exsync/compare/v0.4.0...v0.4.1)

---
updated-dependencies:
- dependency-name: exsync
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

13574 of 43466 relevant lines covered (31.23%)

618.35 hits per line

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

40.74
/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-2024   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
         css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
40
         read_css/1, read_img/1, read_js/1, read_lua/1,
41
         intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0,
42
         is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
43
         get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1,
44
         crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1,
45
         json_encode/1, json_decode/1,
46
         match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1,
47
         semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]).
48

49
%% Deprecated functions
50
-export([decode_base64/1, encode_base64/1]).
51
-deprecated([{decode_base64, 1},
52
             {encode_base64, 1}]).
53

54
-include("logger.hrl").
55
-include_lib("xmpp/include/xmpp.hrl").
56
-include_lib("kernel/include/file.hrl").
57

58
%% Copied from erlang/otp/lib/stdlib/src/re.erl
59
-type re_mp() :: {re_pattern, _, _, _, _}.
60
-export_type([re_mp/0]).
61

62
-ifdef(OTP_BELOW_27).
63
-type json_value() :: jiffy:json_value().
64
-else.
65
-type json_value() :: json:encode_value().
66
-endif.
67
-export_type([json_value/0]).
68

69
-type distance_cache() :: #{{string(), string()} => non_neg_integer()}.
70

71
-spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}.
72
-ifdef(USE_OLD_HTTP_URI).
73
uri_parse(URL) when is_binary(URL) ->
74
    uri_parse(binary_to_list(URL));
75
uri_parse(URL) ->
76
    uri_parse(URL, []).
77

78
uri_parse(URL, Protocols) when is_binary(URL) ->
79
    uri_parse(binary_to_list(URL), Protocols);
80
uri_parse(URL, Protocols) ->
81
    case http_uri:parse(URL, [{scheme_defaults, Protocols}]) of
82
        {ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
83
            {ok, atom_to_list(Scheme), UserInfo, Host, Port, Path, Query};
84
        {error, _} = E ->
85
            E
86
    end.
87

88
-else.
89
uri_parse(URL) when is_binary(URL) ->
90
    uri_parse(binary_to_list(URL));
6✔
91
uri_parse(URL) ->
92
    uri_parse(URL, [{http, 80}, {https, 443}]).
6✔
93

94
uri_parse(URL, Protocols) when is_binary(URL) ->
95
    uri_parse(binary_to_list(URL), Protocols);
×
96
uri_parse(URL, Protocols) ->
97
    case uri_string:parse(URL) of
6✔
98
        #{scheme := Scheme, host := Host, port := Port, path := Path} = M1 ->
99
            {ok, Scheme, maps:get(userinfo, M1, ""), Host, Port, Path, maps:get(query, M1, "")};
6✔
100
        #{scheme := Scheme, host := Host, path := Path} = M2 ->
101
            case lists:keyfind(list_to_atom(Scheme), 1, Protocols) of
×
102
                {_, Port} ->
103
                    {ok, Scheme, maps:get(userinfo, M2, ""), Host, Port, Path, maps:get(query, M2, "")};
×
104
                _ ->
105
                    {error, unknown_protocol}
×
106
            end;
107
        {error, Atom, _} ->
108
            {error, Atom}
×
109
    end.
110
-endif.
111

112
-ifdef(OTP_BELOW_25).
113
-ifdef(OTP_BELOW_24).
114
uri_quote(Data) ->
115
    Data.
116
-else.
117
uri_quote(Data) ->
118
    http_uri:encode(Data).
119
-endif.
120
-else.
121
uri_quote(Data) ->
122
    uri_string:quote(Data).
×
123
-endif.
124

125
-ifdef(USE_OLD_CRYPTO_HMAC).
126
crypto_hmac(Type, Key, Data) -> crypto:hmac(Type, Key, Data).
127
crypto_hmac(Type, Key, Data, MacL) -> crypto:hmac(Type, Key, Data, MacL).
128
-else.
129
crypto_hmac(Type, Key, Data) -> crypto:mac(hmac, Type, Key, Data).
12✔
130
crypto_hmac(Type, Key, Data, MacL) -> crypto:macN(hmac, Type, Key, Data, MacL).
×
131
-endif.
132

133
-ifdef(OTP_BELOW_27).
134
json_encode(Term) ->
135
    jiffy:encode(Term).
12✔
136
json_decode(Bin) ->
137
    jiffy:decode(Bin, [return_maps]).
6✔
138
-else.
139
json_encode(Term) ->
140
    iolist_to_binary(json:encode(Term)).
141
json_decode(Bin) ->
142
    json:decode(Bin).
143
-endif.
144

145
%%%===================================================================
146
%%% API
147
%%%===================================================================
148
-spec add_delay_info(stanza(), jid(), erlang:timestamp()) -> stanza().
149
add_delay_info(Stz, From, Time) ->
150
    add_delay_info(Stz, From, Time, <<"">>).
378✔
151

152
-spec add_delay_info(stanza(), jid(), erlang:timestamp(), binary()) -> stanza().
153
add_delay_info(Stz, From, Time, Desc) ->
154
    Delays = xmpp:get_subtags(Stz, #delay{stamp = {0,0,0}}),
3,254✔
155
    Matching = lists:any(
3,254✔
156
        fun(#delay{from = OldFrom}) when is_record(OldFrom, jid) ->
157
               jid:tolower(From) == jid:tolower(OldFrom);
1,582✔
158
           (_) ->
159
               false
×
160
        end, Delays),
161
    case Matching of
3,254✔
162
        true ->
163
            Stz;
1,582✔
164
        _ ->
165
            NewDelay = #delay{stamp = Time, from = From, desc = Desc},
1,672✔
166
            xmpp:append_subtags(Stz, [NewDelay])
1,672✔
167
    end.
168

169
-spec unwrap_carbon(stanza()) -> xmpp_element().
170
unwrap_carbon(#message{} = Msg) ->
171
    try
1,259✔
172
        case xmpp:get_subtag(Msg, #carbons_sent{forwarded = #forwarded{}}) of
1,259✔
173
            #carbons_sent{forwarded = #forwarded{sub_els = [El]}} ->
174
                xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
×
175
            _ ->
176
                case xmpp:get_subtag(Msg, #carbons_received{
1,259✔
177
                                              forwarded = #forwarded{}}) of
178
                    #carbons_received{forwarded = #forwarded{sub_els = [El]}} ->
179
                        xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
×
180
                    _ ->
181
                        Msg
1,259✔
182
                end
183
        end
184
    catch _:{xmpp_codec, _} ->
185
            Msg
×
186
    end;
187
unwrap_carbon(Stanza) -> Stanza.
×
188

189
-spec unwrap_mucsub_message(xmpp_element()) -> message() | false.
190
unwrap_mucsub_message(#message{} = OuterMsg) ->
191
    case xmpp:get_subtag(OuterMsg, #ps_event{}) of
9,578✔
192
        #ps_event{
193
            items = #ps_items{
194
                node = Node,
195
                items = [
196
                    #ps_item{
197
                        sub_els = [#message{} = InnerMsg]} | _]}}
198
            when Node == ?NS_MUCSUB_NODES_MESSAGES;
199
                 Node == ?NS_MUCSUB_NODES_SUBJECT ->
200
            InnerMsg;
105✔
201
        _ ->
202
            false
9,473✔
203
    end;
204
unwrap_mucsub_message(_Packet) ->
205
    false.
×
206

207
-spec is_mucsub_message(xmpp_element()) -> boolean().
208
is_mucsub_message(Packet) ->
209
    get_mucsub_event_type(Packet) /= false.
6,388✔
210

211
-spec get_mucsub_event_type(xmpp_element()) -> binary() | false.
212
get_mucsub_event_type(#message{} = OuterMsg) ->
213
    case xmpp:get_subtag(OuterMsg, #ps_event{}) of
8,248✔
214
        #ps_event{
215
            items = #ps_items{
216
                node = Node}}
217
            when Node == ?NS_MUCSUB_NODES_MESSAGES;
218
                 Node == ?NS_MUCSUB_NODES_SUBJECT;
219
                 Node == ?NS_MUCSUB_NODES_AFFILIATIONS;
220
                 Node == ?NS_MUCSUB_NODES_CONFIG;
221
                 Node == ?NS_MUCSUB_NODES_PARTICIPANTS;
222
                 Node == ?NS_MUCSUB_NODES_PRESENCE;
223
                 Node == ?NS_MUCSUB_NODES_SUBSCRIBERS ->
224
            Node;
×
225
        _ ->
226
            false
8,248✔
227
    end;
228
get_mucsub_event_type(_Packet) ->
229
    false.
×
230

231
-spec is_standalone_chat_state(stanza()) -> boolean().
232
is_standalone_chat_state(Stanza) ->
233
    case unwrap_carbon(Stanza) of
1,259✔
234
        #message{body = [], subject = [], sub_els = Els} ->
235
            IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT, ?NS_HINTS],
312✔
236
            Stripped = [El || El <- Els,
312✔
237
                              not lists:member(xmpp:get_ns(El), IgnoreNS)],
452✔
238
            Stripped == [];
312✔
239
        _ ->
240
            false
947✔
241
    end.
242

243
-spec tolower(binary()) -> binary().
244
tolower(B) ->
245
    iolist_to_binary(tolower_s(binary_to_list(B))).
42✔
246

247
tolower_s([C | Cs]) ->
248
    if C >= $A, C =< $Z -> [C + 32 | tolower_s(Cs)];
420✔
249
       true -> [C | tolower_s(Cs)]
420✔
250
    end;
251
tolower_s([]) -> [].
42✔
252

253
-spec term_to_base64(term()) -> binary().
254
term_to_base64(Term) ->
255
    encode_base64(term_to_binary(Term)).
1,183✔
256

257
-spec base64_to_term(binary()) -> {term, term()} | error.
258
base64_to_term(Base64) ->
259
    try binary_to_term(base64:decode(Base64), [safe]) of
2✔
260
        Term -> {term, Term}
2✔
261
    catch _:_ ->
262
            error
×
263
    end.
264

265
-spec decode_base64(binary()) -> binary().
266
decode_base64(S) ->
267
    try base64:mime_decode(S)
×
268
    catch _:badarg -> <<>>
×
269
    end.
270

271
-spec encode_base64(binary()) -> binary().
272
encode_base64(Data) ->
273
    base64:encode(Data).
1,183✔
274

275
-spec ip_to_list(inet:ip_address() | undefined |
276
                 {inet:ip_address(), inet:port_number()}) -> binary().
277

278
ip_to_list({IP, _Port}) ->
279
    ip_to_list(IP);
1,307✔
280
%% This function clause could use inet_parse too:
281
ip_to_list(undefined) ->
282
    <<"unknown">>;
×
283
ip_to_list(local) ->
284
    <<"unix">>;
×
285
ip_to_list(IP) ->
286
    list_to_binary(inet_parse:ntoa(IP)).
1,313✔
287

288
-spec hex_to_bin(binary()) -> binary().
289
hex_to_bin(Hex) ->
290
    hex_to_bin(binary_to_list(Hex), []).
×
291

292
-spec hex_to_bin(list(), list()) -> binary().
293
hex_to_bin([], Acc) ->
294
    list_to_binary(lists:reverse(Acc));
×
295
hex_to_bin([H1, H2 | T], Acc) ->
296
    {ok, [V], []} = io_lib:fread("~16u", [H1, H2]),
×
297
    hex_to_bin(T, [V | Acc]).
×
298

299
-spec hex_to_base64(binary()) -> binary().
300
hex_to_base64(Hex) ->
301
    base64:encode(hex_to_bin(Hex)).
×
302

303
-spec url_encode(binary()) -> binary().
304
url_encode(A) ->
305
    url_encode(A, <<>>).
52✔
306

307
-spec expand_keyword(iodata(), iodata(), iodata()) -> binary().
308
expand_keyword(Keyword, Input, Replacement) ->
309
    re:replace(Input, Keyword, Replacement,
20✔
310
               [{return, binary}, global]).
311

312
binary_to_atom(Bin) ->
313
    erlang:binary_to_atom(Bin, utf8).
54,112✔
314

315
tuple_to_binary(T) ->
316
    iolist_to_binary(tuple_to_list(T)).
×
317

318
atom_to_binary(A) ->
319
    erlang:atom_to_binary(A, utf8).
25,747✔
320

321
expr_to_term(Expr) ->
322
    Str = binary_to_list(<<Expr/binary, ".">>),
×
323
    {ok, Tokens, _} = erl_scan:string(Str),
×
324
    {ok, Term} = erl_parse:parse_term(Tokens),
×
325
    Term.
×
326

327
term_to_expr(Term) ->
328
    list_to_binary(io_lib:print(Term, 1, 999999, -1)).
4,137✔
329

330
-spec now_to_usec(erlang:timestamp()) -> non_neg_integer().
331
now_to_usec({MSec, Sec, USec}) ->
332
    (MSec*1000000 + Sec)*1000000 + USec.
118✔
333

334
-spec usec_to_now(non_neg_integer()) -> erlang:timestamp().
335
usec_to_now(Int) ->
336
    Secs = Int div 1000000,
2,794✔
337
    USec = Int rem 1000000,
2,794✔
338
    MSec = Secs div 1000000,
2,794✔
339
    Sec = Secs rem 1000000,
2,794✔
340
    {MSec, Sec, USec}.
2,794✔
341

342
l2i(I) when is_integer(I) -> I;
×
343
l2i(L) when is_binary(L) -> binary_to_integer(L).
×
344

345
i2l(I) when is_integer(I) -> integer_to_binary(I);
6,023✔
346
i2l(L) when is_binary(L) -> L.
×
347

348
i2l(I, N) when is_integer(I) -> i2l(i2l(I), N);
540✔
349
i2l(L, N) when is_binary(L) ->
350
    case str:len(L) of
1,094✔
351
      N -> L;
540✔
352
      C when C > N -> L;
×
353
      _ -> i2l(<<$0, L/binary>>, N)
554✔
354
    end.
355

356
-spec encode_pid(pid()) -> binary().
357
encode_pid(Pid) ->
358
    list_to_binary(erlang:pid_to_list(Pid)).
1,179✔
359

360
-spec decode_pid(binary(), binary()) -> pid().
361
decode_pid(PidBin, NodeBin) ->
362
    PidStr = binary_to_list(PidBin),
2,248✔
363
    Pid = erlang:list_to_pid(PidStr),
2,248✔
364
    case erlang:binary_to_atom(NodeBin, latin1) of
2,248✔
365
        Node when Node == node() ->
366
            Pid;
2,248✔
367
        Node ->
368
            try set_node_id(PidStr, NodeBin)
×
369
            catch _:badarg ->
370
                    erlang:error({bad_node, Node})
×
371
            end
372
    end.
373

374
-spec compile_exprs(module(), [string()]) -> ok | {error, any()}.
375
compile_exprs(Mod, Exprs) ->
376
    try
×
377
        Forms = lists:map(
×
378
                  fun(Expr) ->
379
                          {ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)),
×
380
                          {ok, Form} = erl_parse:parse_form(Tokens),
×
381
                          Form
×
382
                  end, Exprs),
383
        {ok, Code} = case compile:forms(Forms, []) of
×
384
                         {ok, Mod, Bin} -> {ok, Bin};
×
385
                         {ok, Mod, Bin, _Warnings} -> {ok, Bin};
×
386
                         Error -> Error
×
387
                     end,
388
        {module, Mod} = code:load_binary(Mod, "nofile", Code),
×
389
        ok
×
390
    catch _:{badmatch, {error, ErrInfo, _ErrLocation}} ->
391
            {error, ErrInfo};
×
392
          _:{badmatch, {error, _} = Err} ->
393
            Err;
×
394
          _:{badmatch, error} ->
395
            {error, compile_failed}
×
396
    end.
397

398
-spec join_atoms([atom()], binary()) -> binary().
399
join_atoms(Atoms, Sep) ->
400
    str:join([io_lib:format("~p", [A]) || A <- lists:sort(Atoms)], Sep).
×
401

402
%% @doc Checks if the file is readable and converts its name to binary.
403
%%      Fails with `badarg' otherwise. The function is intended for usage
404
%%      in configuration validators only.
405
-spec try_read_file(file:filename_all()) -> binary().
406
try_read_file(Path) ->
407
    case file:open(Path, [read]) of
×
408
        {ok, Fd} ->
409
            file:close(Fd),
×
410
            iolist_to_binary(Path);
×
411
        {error, Why} ->
412
            ?ERROR_MSG("Failed to read ~ts: ~ts", [Path, file:format_error(Why)]),
×
413
            erlang:error(badarg)
×
414
    end.
415

416
-spec css_dir() -> file:filename().
417
css_dir() ->
418
    get_dir("css").
×
419

420
-spec img_dir() -> file:filename().
421
img_dir() ->
422
    get_dir("img").
×
423

424
-spec js_dir() -> file:filename().
425
js_dir() ->
426
    get_dir("js").
×
427

428
-spec msgs_dir() -> file:filename().
429
msgs_dir() ->
430
    get_dir("msgs").
1✔
431

432
-spec sql_dir() -> file:filename().
433
sql_dir() ->
434
    get_dir("sql").
1✔
435

436
-spec lua_dir() -> file:filename().
437
lua_dir() ->
438
    get_dir("lua").
1✔
439

440
-spec read_css(file:filename()) -> {ok, binary()} | {error, file:posix()}.
441
read_css(File) ->
442
    read_file(filename:join(css_dir(), File)).
×
443

444
-spec read_img(file:filename()) -> {ok, binary()} | {error, file:posix()}.
445
read_img(File) ->
446
    read_file(filename:join(img_dir(), File)).
×
447

448
-spec read_js(file:filename()) -> {ok, binary()} | {error, file:posix()}.
449
read_js(File) ->
450
    read_file(filename:join(js_dir(), File)).
×
451

452
-spec read_lua(file:filename()) -> {ok, binary()} | {error, file:posix()}.
453
read_lua(File) ->
454
    read_file(filename:join(lua_dir(), File)).
1✔
455

456
-spec get_descr(binary(), binary()) -> binary().
457
get_descr(Lang, Text) ->
458
    Desc = translate:translate(Lang, Text),
×
459
    Copyright = ejabberd_config:get_copyright(),
×
460
    <<Desc/binary, $\n, Copyright/binary>>.
×
461

462
-spec intersection(list(), list()) -> list().
463
intersection(L1, L2) ->
464
    lists:filter(
1✔
465
      fun(E) ->
466
              lists:member(E, L2)
9✔
467
      end, L1).
468

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

490
-spec cancel_timer(reference() | undefined) -> ok.
491
cancel_timer(TRef) when is_reference(TRef) ->
492
    case erlang:cancel_timer(TRef) of
31✔
493
        false ->
494
            receive {timeout, TRef, _} -> ok
×
495
            after 0 -> ok
×
496
            end;
497
        _ ->
498
            ok
31✔
499
    end;
500
cancel_timer(_) ->
501
    ok.
2✔
502

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

517
-spec logical_processors() -> non_neg_integer().
518
logical_processors() ->
519
    case erlang:system_info(logical_processors) of
297✔
520
        V when is_integer(V), V >= 2  -> V;
297✔
521
        _ -> 1
×
522
    end.
523

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

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

572
-ifdef(HAVE_ERL_ERROR).
573
format_exception(Level, Class, Reason, Stacktrace) ->
574
    erl_error:format_exception(
×
575
      Level, Class, Reason, Stacktrace,
576
      fun(_M, _F, _A) -> false end,
×
577
      fun(Term, I) ->
578
              io_lib:print(Term, I, 80, -1)
×
579
      end).
580
-else.
581
format_exception(Level, Class, Reason, Stacktrace) ->
582
    lib:format_exception(
583
      Level, Class, Reason, Stacktrace,
584
      fun(_M, _F, _A) -> false end,
585
      fun(Term, I) ->
586
              io_lib:print(Term, I, 80, -1)
587
      end).
588
-endif.
589

590
-spec get_my_ipv4_address() -> inet:ip4_address().
591
get_my_ipv4_address() ->
592
    {ok, MyHostName} = inet:gethostname(),
11✔
593
    case inet:getaddr(MyHostName, inet) of
11✔
594
        {ok, Addr} -> Addr;
11✔
595
        {error, _} -> {127, 0, 0, 1}
×
596
    end.
597

598
-spec get_my_ipv6_address() -> inet:ip6_address().
599
get_my_ipv6_address() ->
600
    {ok, MyHostName} = inet:gethostname(),
×
601
    case inet:getaddr(MyHostName, inet6) of
×
602
        {ok, Addr} -> Addr;
×
603
        {error, _} -> {0, 0, 0, 0, 0, 0, 0, 1}
×
604
    end.
605

606
-spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} |
607
                                 {ok, {inet:ip6_address(), 0..128}} |
608
                                 error.
609
parse_ip_mask(S) ->
610
    case econf:validate(econf:ip_mask(), S) of
×
611
        {ok, _} = Ret -> Ret;
×
612
        _ -> error
×
613
    end.
614

615
-spec match_ip_mask(inet:ip_address(), inet:ip_address(), 0..128) -> boolean().
616
match_ip_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
617
    IPInt = ip_to_integer(IP),
×
618
    NetInt = ip_to_integer(Net),
×
619
    M = bnot (1 bsl (32 - Mask) - 1),
×
620
    IPInt band M =:= NetInt band M;
×
621
match_ip_mask({_, _, _, _, _, _, _, _} = IP,
622
                {_, _, _, _, _, _, _, _} = Net, Mask) ->
623
    IPInt = ip_to_integer(IP),
×
624
    NetInt = ip_to_integer(Net),
×
625
    M = bnot (1 bsl (128 - Mask) - 1),
×
626
    IPInt band M =:= NetInt band M;
×
627
match_ip_mask({_, _, _, _} = IP,
628
                {0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
629
    IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
×
630
    NetInt = ip_to_integer(Net),
×
631
    M = bnot (1 bsl (128 - Mask) - 1),
×
632
    IPInt band M =:= NetInt band M;
×
633
match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
634
                {_, _, _, _} = Net, Mask) ->
635
    IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
×
636
    NetInt = ip_to_integer(Net),
×
637
    M = bnot (1 bsl (32 - Mask) - 1),
×
638
    IPInt band M =:= NetInt band M;
×
639
match_ip_mask(_, _, _) ->
640
    false.
×
641

642
-spec format_hosts_list([binary(), ...]) -> iolist().
643
format_hosts_list([Host]) ->
644
    Host;
×
645
format_hosts_list([H1, H2]) ->
646
    [H1, " and ", H2];
×
647
format_hosts_list([H1, H2, H3]) ->
648
    [H1, ", ", H2, " and ", H3];
×
649
format_hosts_list([H1, H2|Hs]) ->
650
    io_lib:format("~ts, ~ts and ~B more hosts",
1✔
651
                  [H1, H2, length(Hs)]).
652

653
-spec format_cycle([atom(), ...]) -> iolist().
654
format_cycle([M1]) ->
655
    atom_to_list(M1);
×
656
format_cycle([M1, M2]) ->
657
    [atom_to_list(M1), " and ", atom_to_list(M2)];
×
658
format_cycle([M|Ms]) ->
659
    atom_to_list(M) ++ ", " ++ format_cycle(Ms).
×
660

661
-spec delete_dir(file:filename_all()) -> ok | {error, file:posix()}.
662
delete_dir(Dir) ->
663
    try
1✔
664
        {ok, Entries} = file:list_dir(Dir),
1✔
665
        lists:foreach(fun(Path) ->
×
666
                              case filelib:is_dir(Path) of
×
667
                                  true ->
668
                                      ok = delete_dir(Path);
×
669
                                  false ->
670
                                      ok = file:delete(Path)
×
671
                              end
672
                      end, [filename:join(Dir, Entry) || Entry <- Entries]),
×
673
        ok = file:del_dir(Dir)
×
674
    catch
675
        _:{badmatch, {error, Error}} ->
676
            {error, Error}
1✔
677
    end.
678

679
-spec semver_to_xxyy(binary()) -> binary().
680
semver_to_xxyy(<<Y1, Y2, $., M2, $., $0>>) ->
681
    <<Y1, Y2, $., $0, M2>>;
×
682
semver_to_xxyy(<<Y1, Y2, $., M2, $., Patch/binary>>) ->
683
    <<Y1, Y2, $., $0, M2, $., Patch/binary>>;
×
684
semver_to_xxyy(<<Y1, Y2, $., M1, M2, $., $0>>) ->
685
    <<Y1, Y2, $., M1, M2>>;
×
686
semver_to_xxyy(Version) when is_binary(Version) ->
687
    Version.
×
688

689
%%%===================================================================
690
%%% Internal functions
691
%%%===================================================================
692
-spec url_encode(binary(), binary()) -> binary().
693
url_encode(<<H:8, T/binary>>, Acc) when
694
  (H >= $a andalso H =< $z) orelse
695
  (H >= $A andalso H =< $Z) orelse
696
  (H >= $0 andalso H =< $9) orelse
697
  H == $_ orelse
698
  H == $. orelse
699
  H == $- orelse
700
  H == $/ orelse
701
  H == $: ->
702
    url_encode(T, <<Acc/binary, H>>);
2,488✔
703
url_encode(<<H:8, T/binary>>, Acc) ->
704
    case integer_to_list(H, 16) of
725✔
705
        [X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
725✔
706
        [X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
×
707
    end;
708
url_encode(<<>>, Acc) ->
709
    Acc.
52✔
710

711
-spec set_node_id(string(), binary()) -> pid().
712
set_node_id(PidStr, NodeBin) ->
713
    ExtPidStr = erlang:pid_to_list(
×
714
                  binary_to_term(
715
                    <<131,103,100,(size(NodeBin)):16,NodeBin/binary,0:72>>)),
716
    [H|_] = string:tokens(ExtPidStr, "."),
×
717
    [_|T] = string:tokens(PidStr, "."),
×
718
    erlang:list_to_pid(string:join([H|T], ".")).
×
719

720
-spec read_file(file:filename()) -> {ok, binary()} | {error, file:posix()}.
721
read_file(Path) ->
722
    case file:read_file(Path) of
1✔
723
        {ok, Data} ->
724
            {ok, Data};
1✔
725
        {error, Why} = Err ->
726
            ?ERROR_MSG("Failed to read file ~ts: ~ts",
×
727
                       [Path, file:format_error(Why)]),
×
728
            Err
×
729
    end.
730

731
-spec get_dir(string()) -> file:filename().
732
get_dir(Type) ->
733
    Env = "EJABBERD_" ++ string:to_upper(Type) ++ "_PATH",
3✔
734
    case os:getenv(Env) of
3✔
735
        false ->
736
            case code:priv_dir(ejabberd) of
3✔
737
                {error, _} -> filename:join(["priv", Type]);
×
738
                Path -> filename:join([Path, Type])
3✔
739
            end;
740
        Path ->
741
            Path
×
742
    end.
743

744
%% Generates erlang:timestamp() that is guaranteed to unique
745
-spec unique_timestamp() -> erlang:timestamp().
746
unique_timestamp() ->
747
    {MS, S, _} = erlang:timestamp(),
1,230✔
748
    {MS, S, erlang:unique_integer([positive, monotonic]) rem 1000000}.
1,230✔
749

750
%% Levenshtein distance
751
-spec ld(string(), string(), distance_cache()) -> {non_neg_integer(), distance_cache()}.
752
ld([] = S, T, Cache) ->
753
    {length(T), maps:put({S, T}, length(T), Cache)};
×
754
ld(S, [] = T, Cache) ->
755
    {length(S), maps:put({S, T}, length(S), Cache)};
×
756
ld([X|S], [X|T], Cache) ->
757
    ld(S, T, Cache);
×
758
ld([_|ST] = S, [_|TT] = T, Cache) ->
759
    try {maps:get({S, T}, Cache), Cache}
×
760
    catch _:{badkey, _} ->
761
            {L1, C1} = ld(S, TT, Cache),
×
762
            {L2, C2} = ld(ST, T, C1),
×
763
            {L3, C3} = ld(ST, TT, C2),
×
764
            L = 1 + lists:min([L1, L2, L3]),
×
765
            {L, maps:put({S, T}, L, C3)}
×
766
    end.
767

768
-spec ip_to_integer(inet:ip_address()) -> non_neg_integer().
769
ip_to_integer({IP1, IP2, IP3, IP4}) ->
770
    IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
×
771
ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
772
               IP8}) ->
773
    IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
774
        bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8.
×
775

776
-spec to_string(atom() | binary() | string()) -> string().
777
to_string(A) when is_atom(A) ->
778
    atom_to_list(A);
×
779
to_string(B) when is_binary(B) ->
780
    binary_to_list(B);
×
781
to_string(S) ->
782
    S.
×
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