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