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

processone / ejabberd / 444

pending completion
444

push

github

Paweł Chmielowski
Fix typo in last commit

13452 of 40447 relevant lines covered (33.26%)

673.94 hits per line

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

40.07
/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-2022   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,
45
         match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1,
46
         semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]).
47

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

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

57
-type distance_cache() :: #{{string(), string()} => non_neg_integer()}.
58

59
-spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}.
60
-ifdef(USE_OLD_HTTP_URI).
61
uri_parse(URL) when is_binary(URL) ->
62
    uri_parse(binary_to_list(URL));
63
uri_parse(URL) ->
64
    uri_parse(URL, []).
65

66
uri_parse(URL, Protocols) when is_binary(URL) ->
67
    uri_parse(binary_to_list(URL), Protocols);
68
uri_parse(URL, Protocols) ->
69
    case http_uri:parse(URL, [{scheme_defaults, Protocols}]) of
70
        {ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
71
            {ok, atom_to_list(Scheme), UserInfo, Host, Port, Path, Query};
72
        {error, _} = E ->
73
            E
74
    end.
75

76
-else.
77
uri_parse(URL) when is_binary(URL) ->
78
    uri_parse(binary_to_list(URL));
6✔
79
uri_parse(URL) ->
80
    uri_parse(URL, [{http, 80}, {https, 443}]).
6✔
81

82
uri_parse(URL, Protocols) when is_binary(URL) ->
83
    uri_parse(binary_to_list(URL), Protocols);
×
84
uri_parse(URL, Protocols) ->
85
    case uri_string:parse(URL) of
6✔
86
        #{scheme := Scheme, host := Host, port := Port, path := Path} = M1 ->
87
            {ok, Scheme, maps:get(userinfo, M1, ""), Host, Port, Path, maps:get(query, M1, "")};
6✔
88
        #{scheme := Scheme, host := Host, path := Path} = M2 ->
89
            case lists:keyfind(list_to_atom(Scheme), 1, Protocols) of
×
90
                {_, Port} ->
91
                    {ok, Scheme, maps:get(userinfo, M2, ""), Host, Port, Path, maps:get(query, M2, "")};
×
92
                _ ->
93
                    {error, unknown_protocol}
×
94
            end;
95
        {error, Atom, _} ->
96
            {error, Atom}
×
97
    end.
98
-endif.
99

100
-ifdef(USE_OLD_CRYPTO_HMAC).
101
crypto_hmac(Type, Key, Data) -> crypto:hmac(Type, Key, Data).
102
crypto_hmac(Type, Key, Data, MacL) -> crypto:hmac(Type, Key, Data, MacL).
103
-else.
104
crypto_hmac(Type, Key, Data) -> crypto:mac(hmac, Type, Key, Data).
12✔
105
crypto_hmac(Type, Key, Data, MacL) -> crypto:macN(hmac, Type, Key, Data, MacL).
×
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, <<"">>).
378✔
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}}),
3,254✔
118
    Matching = lists:any(
3,254✔
119
        fun(#delay{from = OldFrom}) when is_record(OldFrom, jid) ->
120
               jid:tolower(From) == jid:tolower(OldFrom);
1,582✔
121
           (_) ->
122
               false
×
123
        end, Delays),
124
    case Matching of
3,254✔
125
        true ->
126
            Stz;
1,582✔
127
        _ ->
128
            NewDelay = #delay{stamp = Time, from = From, desc = Desc},
1,672✔
129
            xmpp:append_subtags(Stz, [NewDelay])
1,672✔
130
    end.
131

132
-spec unwrap_carbon(stanza()) -> xmpp_element().
133
unwrap_carbon(#message{} = Msg) ->
134
    try
1,259✔
135
        case xmpp:get_subtag(Msg, #carbons_sent{forwarded = #forwarded{}}) of
1,259✔
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{
1,259✔
140
                                              forwarded = #forwarded{}}) of
141
                    #carbons_received{forwarded = #forwarded{sub_els = [El]}} ->
142
                        xmpp:decode(El, ?NS_CLIENT, [ignore_els]);
×
143
                    _ ->
144
                        Msg
1,259✔
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
9,528✔
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;
105✔
164
        _ ->
165
            false
9,423✔
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.
6,348✔
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
8,198✔
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
8,198✔
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
1,259✔
197
        #message{body = [], subject = [], sub_els = Els} ->
198
            IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT],
312✔
199
            Stripped = [El || El <- Els,
312✔
200
                              not lists:member(xmpp:get_ns(El), IgnoreNS)],
452✔
201
            Stripped == [];
312✔
202
        _ ->
203
            false
947✔
204
    end.
205

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

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

216
-spec term_to_base64(term()) -> binary().
217
term_to_base64(Term) ->
218
    encode_base64(term_to_binary(Term)).
7✔
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).
7✔
237

238
-spec ip_to_list(inet:ip_address() | undefined |
239
                 {inet:ip_address(), inet:port_number()}) -> binary().
240

241
ip_to_list({IP, _Port}) ->
242
    ip_to_list(IP);
1,307✔
243
%% This function clause could use inet_parse too:
244
ip_to_list(undefined) ->
245
    <<"unknown">>;
×
246
ip_to_list(local) ->
247
    <<"unix">>;
×
248
ip_to_list(IP) ->
249
    list_to_binary(inet_parse:ntoa(IP)).
1,313✔
250

251
-spec hex_to_bin(binary()) -> binary().
252
hex_to_bin(Hex) ->
253
    hex_to_bin(binary_to_list(Hex), []).
×
254

255
-spec hex_to_bin(list(), list()) -> binary().
256
hex_to_bin([], Acc) ->
257
    list_to_binary(lists:reverse(Acc));
×
258
hex_to_bin([H1, H2 | T], Acc) ->
259
    {ok, [V], []} = io_lib:fread("~16u", [H1, H2]),
×
260
    hex_to_bin(T, [V | Acc]).
×
261

262
-spec hex_to_base64(binary()) -> binary().
263
hex_to_base64(Hex) ->
264
    base64:encode(hex_to_bin(Hex)).
×
265

266
-spec url_encode(binary()) -> binary().
267
url_encode(A) ->
268
    url_encode(A, <<>>).
52✔
269

270
-spec expand_keyword(iodata(), iodata(), iodata()) -> binary().
271
expand_keyword(Keyword, Input, Replacement) ->
272
    re:replace(Input, Keyword, Replacement,
20✔
273
               [{return, binary}, global]).
274

275
binary_to_atom(Bin) ->
276
    erlang:binary_to_atom(Bin, utf8).
53,142✔
277

278
tuple_to_binary(T) ->
279
    iolist_to_binary(tuple_to_list(T)).
×
280

281
atom_to_binary(A) ->
282
    erlang:atom_to_binary(A, utf8).
24,456✔
283

284
expr_to_term(Expr) ->
285
    Str = binary_to_list(<<Expr/binary, ".">>),
×
286
    {ok, Tokens, _} = erl_scan:string(Str),
×
287
    {ok, Term} = erl_parse:parse_term(Tokens),
×
288
    Term.
×
289

290
term_to_expr(Term) ->
291
    list_to_binary(io_lib:print(Term, 1, 999999, -1)).
4,080✔
292

293
-spec now_to_usec(erlang:timestamp()) -> non_neg_integer().
294
now_to_usec({MSec, Sec, USec}) ->
295
    (MSec*1000000 + Sec)*1000000 + USec.
118✔
296

297
-spec usec_to_now(non_neg_integer()) -> erlang:timestamp().
298
usec_to_now(Int) ->
299
    Secs = Int div 1000000,
2,740✔
300
    USec = Int rem 1000000,
2,740✔
301
    MSec = Secs div 1000000,
2,740✔
302
    Sec = Secs rem 1000000,
2,740✔
303
    {MSec, Sec, USec}.
2,740✔
304

305
l2i(I) when is_integer(I) -> I;
×
306
l2i(L) when is_binary(L) -> binary_to_integer(L).
×
307

308
i2l(I) when is_integer(I) -> integer_to_binary(I);
8,574✔
309
i2l(L) when is_binary(L) -> L.
×
310

311
i2l(I, N) when is_integer(I) -> i2l(i2l(I), N);
504✔
312
i2l(L, N) when is_binary(L) ->
313
    case str:len(L) of
862✔
314
      N -> L;
504✔
315
      C when C > N -> L;
×
316
      _ -> i2l(<<$0, L/binary>>, N)
358✔
317
    end.
318

319
-spec encode_pid(pid()) -> binary().
320
encode_pid(Pid) ->
321
    list_to_binary(erlang:pid_to_list(Pid)).
1,179✔
322

323
-spec decode_pid(binary(), binary()) -> pid().
324
decode_pid(PidBin, NodeBin) ->
325
    PidStr = binary_to_list(PidBin),
2,232✔
326
    Pid = erlang:list_to_pid(PidStr),
2,232✔
327
    case erlang:binary_to_atom(NodeBin, latin1) of
2,232✔
328
        Node when Node == node() ->
329
            Pid;
2,232✔
330
        Node ->
331
            try set_node_id(PidStr, NodeBin)
×
332
            catch _:badarg ->
333
                    erlang:error({bad_node, Node})
×
334
            end
335
    end.
336

337
-spec compile_exprs(module(), [string()]) -> ok | {error, any()}.
338
compile_exprs(Mod, Exprs) ->
339
    try
×
340
        Forms = lists:map(
×
341
                  fun(Expr) ->
342
                          {ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)),
×
343
                          {ok, Form} = erl_parse:parse_form(Tokens),
×
344
                          Form
×
345
                  end, Exprs),
346
        {ok, Code} = case compile:forms(Forms, []) of
×
347
                         {ok, Mod, Bin} -> {ok, Bin};
×
348
                         {ok, Mod, Bin, _Warnings} -> {ok, Bin};
×
349
                         Error -> Error
×
350
                     end,
351
        {module, Mod} = code:load_binary(Mod, "nofile", Code),
×
352
        ok
×
353
    catch _:{badmatch, {error, ErrInfo, _ErrLocation}} ->
354
            {error, ErrInfo};
×
355
          _:{badmatch, {error, _} = Err} ->
356
            Err;
×
357
          _:{badmatch, error} ->
358
            {error, compile_failed}
×
359
    end.
360

361
-spec join_atoms([atom()], binary()) -> binary().
362
join_atoms(Atoms, Sep) ->
363
    str:join([io_lib:format("~p", [A]) || A <- lists:sort(Atoms)], Sep).
×
364

365
%% @doc Checks if the file is readable and converts its name to binary.
366
%%      Fails with `badarg' otherwise. The function is intended for usage
367
%%      in configuration validators only.
368
-spec try_read_file(file:filename_all()) -> binary().
369
try_read_file(Path) ->
370
    case file:open(Path, [read]) of
×
371
        {ok, Fd} ->
372
            file:close(Fd),
×
373
            iolist_to_binary(Path);
×
374
        {error, Why} ->
375
            ?ERROR_MSG("Failed to read ~ts: ~ts", [Path, file:format_error(Why)]),
×
376
            erlang:error(badarg)
×
377
    end.
378

379
-spec css_dir() -> file:filename().
380
css_dir() ->
381
    get_dir("css").
×
382

383
-spec img_dir() -> file:filename().
384
img_dir() ->
385
    get_dir("img").
×
386

387
-spec js_dir() -> file:filename().
388
js_dir() ->
389
    get_dir("js").
×
390

391
-spec msgs_dir() -> file:filename().
392
msgs_dir() ->
393
    get_dir("msgs").
1✔
394

395
-spec sql_dir() -> file:filename().
396
sql_dir() ->
397
    get_dir("sql").
1✔
398

399
-spec lua_dir() -> file:filename().
400
lua_dir() ->
401
    get_dir("lua").
1✔
402

403
-spec read_css(file:filename()) -> {ok, binary()} | {error, file:posix()}.
404
read_css(File) ->
405
    read_file(filename:join(css_dir(), File)).
×
406

407
-spec read_img(file:filename()) -> {ok, binary()} | {error, file:posix()}.
408
read_img(File) ->
409
    read_file(filename:join(img_dir(), File)).
×
410

411
-spec read_js(file:filename()) -> {ok, binary()} | {error, file:posix()}.
412
read_js(File) ->
413
    read_file(filename:join(js_dir(), File)).
×
414

415
-spec read_lua(file:filename()) -> {ok, binary()} | {error, file:posix()}.
416
read_lua(File) ->
417
    read_file(filename:join(lua_dir(), File)).
1✔
418

419
-spec get_descr(binary(), binary()) -> binary().
420
get_descr(Lang, Text) ->
421
    Desc = translate:translate(Lang, Text),
×
422
    Copyright = ejabberd_config:get_copyright(),
×
423
    <<Desc/binary, $\n, Copyright/binary>>.
×
424

425
-spec intersection(list(), list()) -> list().
426
intersection(L1, L2) ->
427
    lists:filter(
1✔
428
      fun(E) ->
429
              lists:member(E, L2)
9✔
430
      end, L1).
431

432
-spec format_val(any()) -> iodata().
433
format_val({yaml, S}) when is_integer(S); is_binary(S); is_atom(S) ->
434
    format_val(S);
×
435
format_val({yaml, YAML}) ->
436
    S = try fast_yaml:encode(YAML)
×
437
        catch _:_ -> YAML
×
438
        end,
439
    format_val(S);
×
440
format_val(I) when is_integer(I) ->
441
    integer_to_list(I);
×
442
format_val(B) when is_atom(B) ->
443
    erlang:atom_to_binary(B, utf8);
×
444
format_val(Term) ->
445
    S = try iolist_to_binary(Term)
×
446
        catch _:_ -> list_to_binary(io_lib:format("~p", [Term]))
×
447
        end,
448
    case binary:match(S, <<"\n">>) of
×
449
        nomatch -> S;
×
450
        _ -> [io_lib:nl(), S]
×
451
    end.
452

453
-spec cancel_timer(reference() | undefined) -> ok.
454
cancel_timer(TRef) when is_reference(TRef) ->
455
    case erlang:cancel_timer(TRef) of
31✔
456
        false ->
457
            receive {timeout, TRef, _} -> ok
×
458
            after 0 -> ok
×
459
            end;
460
        _ ->
461
            ok
31✔
462
    end;
463
cancel_timer(_) ->
464
    ok.
×
465

466
-spec best_match(atom() | binary() | string(),
467
                 [atom() | binary() | string()]) -> string().
468
best_match(Pattern, []) ->
469
    Pattern;
×
470
best_match(Pattern, Opts) ->
471
    String = to_string(Pattern),
×
472
    {Ds, _} = lists:mapfoldl(
×
473
                fun(Opt, Cache) ->
474
                        SOpt = to_string(Opt),
×
475
                        {Distance, Cache1} = ld(String, SOpt, Cache),
×
476
                        {{Distance, SOpt}, Cache1}
×
477
                end, #{}, Opts),
478
    element(2, lists:min(Ds)).
×
479

480
-spec logical_processors() -> non_neg_integer().
481
logical_processors() ->
482
    case erlang:system_info(logical_processors) of
296✔
483
        V when is_integer(V), V >= 2  -> V;
296✔
484
        _ -> 1
×
485
    end.
486

487
-spec pmap(fun((T1) -> T2), [T1]) -> [T2].
488
pmap(Fun, [_,_|_] = List) ->
489
    case logical_processors() of
1✔
490
        1 -> lists:map(Fun, List);
×
491
        _ ->
492
            Self = self(),
1✔
493
            lists:map(
1✔
494
              fun({Pid, Ref}) ->
495
                      receive
29✔
496
                          {Pid, Ret} ->
497
                              receive
29✔
498
                                  {'DOWN', Ref, _, _, _} ->
499
                                      Ret
29✔
500
                              end;
501
                          {'DOWN', Ref, _, _, Reason} ->
502
                              exit(Reason)
×
503
                      end
504
              end, [spawn_monitor(
29✔
505
                      fun() -> Self ! {self(), Fun(X)} end)
29✔
506
                    || X <- List])
1✔
507
    end;
508
pmap(Fun, List) ->
509
    lists:map(Fun, List).
×
510

511
-spec peach(fun((T) -> any()), [T]) -> ok.
512
peach(Fun, [_,_|_] = List) ->
513
    case logical_processors() of
1✔
514
        1 -> lists:foreach(Fun, List);
×
515
        _ ->
516
            Self = self(),
1✔
517
            lists:foreach(
1✔
518
              fun({Pid, Ref}) ->
519
                      receive
33✔
520
                          Pid ->
521
                              receive
33✔
522
                                  {'DOWN', Ref, _, _, _} ->
523
                                      ok
33✔
524
                              end;
525
                          {'DOWN', Ref, _, _, Reason} ->
526
                              exit(Reason)
×
527
                      end
528
              end, [spawn_monitor(
33✔
529
                      fun() -> Fun(X), Self ! self() end)
33✔
530
                    || X <- List])
1✔
531
    end;
532
peach(Fun, List) ->
533
    lists:foreach(Fun, List).
×
534

535
-ifdef(HAVE_ERL_ERROR).
536
format_exception(Level, Class, Reason, Stacktrace) ->
537
    erl_error:format_exception(
×
538
      Level, Class, Reason, Stacktrace,
539
      fun(_M, _F, _A) -> false end,
×
540
      fun(Term, I) ->
541
              io_lib:print(Term, I, 80, -1)
×
542
      end).
543
-else.
544
format_exception(Level, Class, Reason, Stacktrace) ->
545
    lib:format_exception(
546
      Level, Class, Reason, Stacktrace,
547
      fun(_M, _F, _A) -> false end,
548
      fun(Term, I) ->
549
              io_lib:print(Term, I, 80, -1)
550
      end).
551
-endif.
552

553
-spec get_my_ipv4_address() -> inet:ip4_address().
554
get_my_ipv4_address() ->
555
    {ok, MyHostName} = inet:gethostname(),
11✔
556
    case inet:getaddr(MyHostName, inet) of
11✔
557
        {ok, Addr} -> Addr;
11✔
558
        {error, _} -> {127, 0, 0, 1}
×
559
    end.
560

561
-spec get_my_ipv6_address() -> inet:ip6_address().
562
get_my_ipv6_address() ->
563
    {ok, MyHostName} = inet:gethostname(),
×
564
    case inet:getaddr(MyHostName, inet6) of
×
565
        {ok, Addr} -> Addr;
×
566
        {error, _} -> {0, 0, 0, 0, 0, 0, 0, 1}
×
567
    end.
568

569
-spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} |
570
                                 {ok, {inet:ip6_address(), 0..128}} |
571
                                 error.
572
parse_ip_mask(S) ->
573
    case econf:validate(econf:ip_mask(), S) of
×
574
        {ok, _} = Ret -> Ret;
×
575
        _ -> error
×
576
    end.
577

578
-spec match_ip_mask(inet:ip_address(), inet:ip_address(), 0..128) -> boolean().
579
match_ip_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
580
    IPInt = ip_to_integer(IP),
×
581
    NetInt = ip_to_integer(Net),
×
582
    M = bnot (1 bsl (32 - Mask) - 1),
×
583
    IPInt band M =:= NetInt band M;
×
584
match_ip_mask({_, _, _, _, _, _, _, _} = IP,
585
                {_, _, _, _, _, _, _, _} = Net, Mask) ->
586
    IPInt = ip_to_integer(IP),
×
587
    NetInt = ip_to_integer(Net),
×
588
    M = bnot (1 bsl (128 - Mask) - 1),
×
589
    IPInt band M =:= NetInt band M;
×
590
match_ip_mask({_, _, _, _} = IP,
591
                {0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
592
    IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
×
593
    NetInt = ip_to_integer(Net),
×
594
    M = bnot (1 bsl (128 - Mask) - 1),
×
595
    IPInt band M =:= NetInt band M;
×
596
match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
597
                {_, _, _, _} = Net, Mask) ->
598
    IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
×
599
    NetInt = ip_to_integer(Net),
×
600
    M = bnot (1 bsl (32 - Mask) - 1),
×
601
    IPInt band M =:= NetInt band M;
×
602
match_ip_mask(_, _, _) ->
603
    false.
×
604

605
-spec format_hosts_list([binary(), ...]) -> iolist().
606
format_hosts_list([Host]) ->
607
    Host;
×
608
format_hosts_list([H1, H2]) ->
609
    [H1, " and ", H2];
×
610
format_hosts_list([H1, H2, H3]) ->
611
    [H1, ", ", H2, " and ", H3];
×
612
format_hosts_list([H1, H2|Hs]) ->
613
    io_lib:format("~ts, ~ts and ~B more hosts",
1✔
614
                  [H1, H2, length(Hs)]).
615

616
-spec format_cycle([atom(), ...]) -> iolist().
617
format_cycle([M1]) ->
618
    atom_to_list(M1);
×
619
format_cycle([M1, M2]) ->
620
    [atom_to_list(M1), " and ", atom_to_list(M2)];
×
621
format_cycle([M|Ms]) ->
622
    atom_to_list(M) ++ ", " ++ format_cycle(Ms).
×
623

624
-spec delete_dir(file:filename_all()) -> ok | {error, file:posix()}.
625
delete_dir(Dir) ->
626
    try
1✔
627
        {ok, Entries} = file:list_dir(Dir),
1✔
628
        lists:foreach(fun(Path) ->
×
629
                              case filelib:is_dir(Path) of
×
630
                                  true ->
631
                                      ok = delete_dir(Path);
×
632
                                  false ->
633
                                      ok = file:delete(Path)
×
634
                              end
635
                      end, [filename:join(Dir, Entry) || Entry <- Entries]),
×
636
        ok = file:del_dir(Dir)
×
637
    catch
638
        _:{badmatch, {error, Error}} ->
639
            {error, Error}
1✔
640
    end.
641

642
-spec semver_to_xxyy(binary()) -> binary().
643
semver_to_xxyy(<<Y1, Y2, $., M2, $., $0>>) ->
644
    <<Y1, Y2, $., $0, M2>>;
×
645
semver_to_xxyy(<<Y1, Y2, $., M2, $., Patch/binary>>) ->
646
    <<Y1, Y2, $., $0, M2, $., Patch/binary>>;
×
647
semver_to_xxyy(<<Y1, Y2, $., M1, M2, $., $0>>) ->
648
    <<Y1, Y2, $., M1, M2>>;
×
649
semver_to_xxyy(Version) when is_binary(Version) ->
650
    Version.
×
651

652
%%%===================================================================
653
%%% Internal functions
654
%%%===================================================================
655
-spec url_encode(binary(), binary()) -> binary().
656
url_encode(<<H:8, T/binary>>, Acc) when
657
  (H >= $a andalso H =< $z) orelse
658
  (H >= $A andalso H =< $Z) orelse
659
  (H >= $0 andalso H =< $9) orelse
660
  H == $_ orelse
661
  H == $. orelse
662
  H == $- orelse
663
  H == $/ orelse
664
  H == $: ->
665
    url_encode(T, <<Acc/binary, H>>);
2,488✔
666
url_encode(<<H:8, T/binary>>, Acc) ->
667
    case integer_to_list(H, 16) of
725✔
668
        [X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
725✔
669
        [X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
×
670
    end;
671
url_encode(<<>>, Acc) ->
672
    Acc.
52✔
673

674
-spec set_node_id(string(), binary()) -> pid().
675
set_node_id(PidStr, NodeBin) ->
676
    ExtPidStr = erlang:pid_to_list(
×
677
                  binary_to_term(
678
                    <<131,103,100,(size(NodeBin)):16,NodeBin/binary,0:72>>)),
679
    [H|_] = string:tokens(ExtPidStr, "."),
×
680
    [_|T] = string:tokens(PidStr, "."),
×
681
    erlang:list_to_pid(string:join([H|T], ".")).
×
682

683
-spec read_file(file:filename()) -> {ok, binary()} | {error, file:posix()}.
684
read_file(Path) ->
685
    case file:read_file(Path) of
1✔
686
        {ok, Data} ->
687
            {ok, Data};
1✔
688
        {error, Why} = Err ->
689
            ?ERROR_MSG("Failed to read file ~ts: ~ts",
×
690
                       [Path, file:format_error(Why)]),
×
691
            Err
×
692
    end.
693

694
-spec get_dir(string()) -> file:filename().
695
get_dir(Type) ->
696
    Env = "EJABBERD_" ++ string:to_upper(Type) ++ "_PATH",
3✔
697
    case os:getenv(Env) of
3✔
698
        false ->
699
            case code:priv_dir(ejabberd) of
3✔
700
                {error, _} -> filename:join(["priv", Type]);
×
701
                Path -> filename:join([Path, Type])
3✔
702
            end;
703
        Path ->
704
            Path
×
705
    end.
706

707
%% Generates erlang:timestamp() that is guaranteed to unique
708
-spec unique_timestamp() -> erlang:timestamp().
709
unique_timestamp() ->
710
    {MS, S, _} = erlang:timestamp(),
1,230✔
711
    {MS, S, erlang:unique_integer([positive, monotonic]) rem 1000000}.
1,230✔
712

713
%% Levenshtein distance
714
-spec ld(string(), string(), distance_cache()) -> {non_neg_integer(), distance_cache()}.
715
ld([] = S, T, Cache) ->
716
    {length(T), maps:put({S, T}, length(T), Cache)};
×
717
ld(S, [] = T, Cache) ->
718
    {length(S), maps:put({S, T}, length(S), Cache)};
×
719
ld([X|S], [X|T], Cache) ->
720
    ld(S, T, Cache);
×
721
ld([_|ST] = S, [_|TT] = T, Cache) ->
722
    try {maps:get({S, T}, Cache), Cache}
×
723
    catch _:{badkey, _} ->
724
            {L1, C1} = ld(S, TT, Cache),
×
725
            {L2, C2} = ld(ST, T, C1),
×
726
            {L3, C3} = ld(ST, TT, C2),
×
727
            L = 1 + lists:min([L1, L2, L3]),
×
728
            {L, maps:put({S, T}, L, C3)}
×
729
    end.
730

731
-spec ip_to_integer(inet:ip_address()) -> non_neg_integer().
732
ip_to_integer({IP1, IP2, IP3, IP4}) ->
733
    IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
×
734
ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
735
               IP8}) ->
736
    IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
737
        bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8.
×
738

739
-spec to_string(atom() | binary() | string()) -> string().
740
to_string(A) when is_atom(A) ->
741
    atom_to_list(A);
×
742
to_string(B) when is_binary(B) ->
743
    binary_to_list(B);
×
744
to_string(S) ->
745
    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