./ct_report/coverage/mongoose_lib.COVER.html

1 -module(mongoose_lib).
2
3 -export([log_if_backend_error/4]).
4 %% Maps
5 -export([maps_append/3]).
6 -export([maps_foreach/2]).
7 -export([pairs_foreach/2]).
8 -export([maps_or_pairs_foreach/2]).
9 %% Busy Wait
10 -export([wait_until/2, wait_until/3]).
11 -export([parse_ip_netmask/1]).
12
13 -export([get_message_type/1, does_local_user_exist/3]).
14
15 %% Private, just for warning
16 -export([deprecated_logging/1]).
17 -deprecated({deprecated_logging, 1, eventually}).
18
19 -ignore_xref([pairs_foreach/2, wait_until/3]).
20
21 -export_type([microseconds/0]).
22 -export([pmap/2, pmap/3]).
23 -ignore_xref([pmap/3]).
24
25 -export([is_exported/3]).
26
27 -include("mongoose.hrl").
28 -include("jlib.hrl").
29
30 -type microseconds() :: integer().
31 -type message_type() :: one2one | groupchat.
32
33 %% ------------------------------------------------------------------
34 %% Logging
35 %% ------------------------------------------------------------------
36
37 %% @doc Database backends for various modules return ok, {atomic, ok}
38 %% or {atomic, []} on success, and usually {error, ...} on failure.
39 %% All we need is to log an error if such occurred, and proceed normally.
40 -spec log_if_backend_error(V :: any(), % value return by called backend fun
41 Module :: atom(), % caller
42 Line :: integer(),
43 Args :: any() ) -> ok.
44 77 log_if_backend_error(ok, _Module, _Line, _Args) -> ok;
45 18 log_if_backend_error({ok, _}, _Module, _Line, _Args) -> ok;
46 114 log_if_backend_error({atomic, _}, _Module, _Line, _Args) -> ok;
47 29 log_if_backend_error({updated, _}, _Module, _Line, _Args) -> ok;
48 log_if_backend_error({error, E}, Module, Line, Args) ->
49
:-(
?LOG_ERROR(#{what => backend_error,
50 text => <<"Error calling backend module">>,
51 caller_module => Module, caller_line => Line,
52
:-(
reason => E, args => Args}),
53
:-(
ok;
54 log_if_backend_error(E, Module, Line, Args) ->
55
:-(
?LOG_ERROR(#{what => backend_error,
56 text => <<"Unexpected return from backend">>,
57 caller_module => Module, caller_line => Line,
58
:-(
reason => E, args => Args}),
59
:-(
ok.
60
61 %% ------------------------------------------------------------------
62 %% Maps
63 %% ------------------------------------------------------------------
64
65 %% Appends a new Value to the current list of values associated with Key.
66 maps_append(Key, Value, Map) ->
67 699 Values = maps:get(Key, Map, []),
68 699 maps:put(Key, Values ++ [Value], Map).
69
70 -spec maps_foreach(fun(), map()) -> ok.
71 maps_foreach(Fun, Map) when is_function(Fun, 1) ->
72 700 maps:fold(fun(Key, Value, Acc) ->
73 1087 Fun({Key, Value}), Acc
74 end, ok, Map);
75 maps_foreach(Fun, Map) when is_function(Fun, 2) ->
76 2533 maps:fold(fun(Key, Value, Acc) ->
77 3354 Fun(Key, Value), Acc
78 end, ok, Map).
79
80 -spec pairs_foreach(Fun, [{Key, Value}]) -> ok
81 when
82 Fun :: fun((Key, Value) -> term())
83 | fun(({Key, Value}) -> term()),
84 Key :: term(),
85 Value :: term().
86 pairs_foreach(Fun, List) when is_function(Fun, 1) ->
87
:-(
lists:foreach(Fun, List);
88 pairs_foreach(Fun, List) when is_function(Fun, 2) ->
89 4 lists:foreach(fun({K,V}) -> Fun(K,V) end, List).
90
91 maps_or_pairs_foreach(Fun, Map) when is_map(Map) ->
92 1422 maps_foreach(Fun, Map);
93 maps_or_pairs_foreach(Fun, List) when is_list(List) ->
94 4 pairs_foreach(Fun, List).
95
96
97 %% Busy wait
98 wait_until(Fun, ExpectedValue) ->
99 279 wait_until(Fun, ExpectedValue, #{}).
100
101 wait_until(Fun, ExpectedValue, Opts) ->
102 279 Defaults = #{time_left => timer:seconds(5), sleep_time => 100},
103 279 do_wait_until(Fun, ExpectedValue, maps:merge(Defaults, Opts)).
104
105 do_wait_until(_, _, #{time_left := TimeLeft}) when TimeLeft =< 0 ->
106
:-(
ok;
107 do_wait_until(Fun, ExpectedValue, Opts) ->
108 279 case Fun() of
109 279 ExpectedValue -> {ok, ExpectedValue};
110
:-(
_OtherValue -> wait_and_continue(Fun, ExpectedValue, Opts)
111 end.
112
113 wait_and_continue(Fun, ExpectedValue, #{time_left := TimeLeft, sleep_time := SleepTime} = Opts) ->
114
:-(
timer:sleep(SleepTime),
115
:-(
do_wait_until(Fun, ExpectedValue, Opts#{time_left => TimeLeft - SleepTime}).
116
117
118 deprecated_logging(Location) ->
119 2 Map = #{what => deprecated_logging_macro,
120 text => <<"Deprecated logging macro is used in your code">>},
121 2 mongoose_deprecations:log(Location, Map, [{log_level, warning}]).
122
123 %% ------------------------------------------------------------------
124 %% Parse IP
125 %% ------------------------------------------------------------------
126 parse_ip_netmask(S) ->
127 220 case string:tokens(S, "/") of
128
:-(
[IPStr] -> parse_ip_netmask(IPStr, undefined);
129 220 [IPStr, MaskStr] -> parse_ip_netmask(IPStr, MaskStr);
130
:-(
_ -> error
131 end.
132
133 parse_ip_netmask(IPStr, undefined) ->
134
:-(
case inet_parse:address(IPStr) of
135 {ok, {_, _, _, _} = IP} ->
136
:-(
{ok, {IP, 32}};
137 {ok, {_, _, _, _, _, _, _, _} = IP} ->
138
:-(
{ok, {IP, 128}};
139 _ ->
140
:-(
error
141 end;
142 parse_ip_netmask(IPStr, MaskStr) ->
143 220 case catch list_to_integer(MaskStr) of
144 Mask when is_integer(Mask),
145 Mask >= 0 ->
146 220 case inet_parse:address(IPStr) of
147 {ok, {_, _, _, _} = IP} when Mask =< 32 ->
148 220 {ok, {IP, Mask}};
149 {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 ->
150
:-(
{ok, {IP, Mask}};
151 _ ->
152
:-(
error
153 end;
154 _ ->
155
:-(
error
156 end.
157
158 %% ------------------------------------------------------------------
159 %% does_local_user_exist
160 %% ------------------------------------------------------------------
161 -spec get_message_type(mongoose_acc:t()) -> message_type().
162 get_message_type(Acc) ->
163 13636 case mongoose_acc:stanza_type(Acc) of
164 2113 <<"groupchat">> -> groupchat;
165 11523 _ -> one2one
166 end.
167
168 -spec does_local_user_exist(mongooseim:host_type(), jid:jid(), message_type()) -> boolean().
169 does_local_user_exist(HostType, To, groupchat) ->
170 1902 (not is_to_room(To)) andalso ejabberd_auth:does_user_exist(HostType, To, stored);
171 does_local_user_exist(HostType, To, _) ->
172 11146 ejabberd_auth:does_user_exist(HostType, To, stored).
173
174 %% WHY: filter_local_packet is executed twice in the pipeline of muc messages. in two routing steps:
175 %% - From the sender to the room: runs filter_local_packet with From=Sender, To=Room
176 %% - For each member of the room:
177 %% From the room to each member: runs with From=Room/Sender, To=Member
178 %% So, as inbox is a per-user concept, it is on the second routing step only when we want to do act.
179 %% NOTE: ideally for groupchats, we could instead act on `filter_room_packet`, like MAM.
180 -spec is_to_room(jid:jid()) -> boolean().
181 is_to_room(Jid) ->
182 1902 {error, not_found} =:= mongoose_domain_api:get_domain_host_type(Jid#jid.lserver).
183
184 %% ------------------------------------------------------------------
185 %% parallel map
186 %% ------------------------------------------------------------------
187
188 %% Runs a function for each element on the same node
189 pmap(F, Es) ->
190 250 pmap(F, Es, 5000).
191
192 pmap(F, Es, Timeout) ->
193 250 TimerRef = erlang:start_timer(Timeout, self(), pmap_timeout),
194 250 Running =
195 3650 [spawn_monitor(fun() -> exit({pmap_result, F(E)}) end)
196 250 || E <- Es],
197 250 Result = collect(Running, TimerRef),
198 250 cancel_and_flush_timer(TimerRef),
199 250 Result.
200
201 250 collect([], _TimerRef) -> [];
202 collect([{Pid, MRef} | Next] = In, TimerRef) ->
203 3650 receive
204 {'DOWN', MRef, process, Pid, Reason} ->
205 3650 [reason_to_result(Reason) | collect(Next, TimerRef)];
206 {timeout, TimerRef, pmap_timeout} ->
207
:-(
stop_processes(In),
208
:-(
collect(In, TimerRef)
209 end.
210
211 stop_processes(In) ->
212
:-(
[erlang:exit(Pid, timeout) || {Pid, _} <- In].
213
214 reason_to_result({pmap_result, Result}) ->
215 3650 {ok, Result};
216 reason_to_result(Reason) ->
217
:-(
{error, Reason}.
218
219 cancel_and_flush_timer(TimerRef) ->
220 250 erlang:cancel_timer(TimerRef),
221 250 receive
222
:-(
{timeout, TimerRef, _} -> ok
223 250 after 0 -> ok
224 end.
225
226 -spec is_exported(Module :: module(), Function :: atom(),
227 Arity :: integer()) -> boolean().
228 is_exported(Module, Function, Arity) ->
229 146114 code:ensure_loaded(Module),
230 146114 erlang:function_exported(Module, Function, Arity).
Line Hits Source