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 |
170 |
case V of |
45 |
27 |
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 |
170 |
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 |
584 |
maps:fold(fun(Key, Value, Acc) -> |
78 |
751 |
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 |
388 |
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 |
80 |
wait_until(Fun, ExpectedValue, #{}). |
101 |
|
|
102 |
|
wait_until(Fun, ExpectedValue, Opts) -> |
103 |
80 |
Defaults = #{time_left => timer:seconds(5), sleep_time => 100}, |
104 |
80 |
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 |
81 |
case Fun() of |
110 |
80 |
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 |
384 |
case string:tokens(S, "/") of |
129 |
:-( |
[IPStr] -> parse_ip_netmask(IPStr, undefined); |
130 |
384 |
[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 |
384 |
case catch list_to_integer(MaskStr) of |
145 |
|
Mask when is_integer(Mask), |
146 |
|
Mask >= 0 -> |
147 |
384 |
case inet_parse:address(IPStr) of |
148 |
|
{ok, {_, _, _, _} = IP} when Mask =< 32 -> |
149 |
384 |
{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. |