1 |
|
%%%------------------------------------------------------------------- |
2 |
|
%%% @author Uvarov Michael <arcusfelis@gmail.com> |
3 |
|
%%% @copyright (C) 2013, Uvarov Michael |
4 |
|
%%% @doc XEP-0313: Message Archive Management |
5 |
|
%%% |
6 |
|
%%% The module uses several backend modules: |
7 |
|
%%% |
8 |
|
%%% <ul> |
9 |
|
%%% <li>Preference manager ({@link mod_mam_muc_rdbms_prefs});</li> |
10 |
|
%%% <li>Writer ({@link mod_mam_muc_rdbms_arch} or {@link mod_mam_muc_rdbms_async_pool_writer});</li> |
11 |
|
%%% <li>Archive manager ({@link mod_mam_muc_rdbms_arch});</li> |
12 |
|
%%% <li>User's ID generator ({@link mod_mam_muc_user}).</li> |
13 |
|
%%% </ul> |
14 |
|
%%% |
15 |
|
%%% Preferences can be also stored in Mnesia ({@link mod_mam_mnesia_prefs}). |
16 |
|
%%% This module handles simple archives. |
17 |
|
%%% |
18 |
|
%%% This module should be started for each host. |
19 |
|
%%% Message archivation is not shaped here (use standard support for this). |
20 |
|
%%% MAM's IQs are shaped inside {@link opuntia_srv}. |
21 |
|
%%% |
22 |
|
%%% Message identifiers (or UIDs in the spec) are generated based on: |
23 |
|
%%% |
24 |
|
%%% <ul> |
25 |
|
%%% <li>date (using `timestamp()');</li> |
26 |
|
%%% <li>node number (using {@link mongoose_node_num}).</li> |
27 |
|
%%% </ul> |
28 |
|
%%% @end |
29 |
|
%%%------------------------------------------------------------------- |
30 |
|
-module(mod_mam_pm). |
31 |
|
-behavior(gen_mod). |
32 |
|
-behaviour(mongoose_module_metrics). |
33 |
|
%% ---------------------------------------------------------------------- |
34 |
|
%% Exports |
35 |
|
|
36 |
|
%% Client API |
37 |
|
-export([delete_archive/1, |
38 |
|
archive_size/2, |
39 |
|
archive_size_with_host_type/3, |
40 |
|
archive_id/2]). |
41 |
|
|
42 |
|
%% gen_mod handlers |
43 |
|
-export([start/2, stop/1, supported_features/0, hooks/1, instrumentation/1]). |
44 |
|
|
45 |
|
%% hook handlers |
46 |
|
-export([disco_local_features/3, |
47 |
|
disco_sm_features/3, |
48 |
|
user_send_message/3, |
49 |
|
filter_packet/3, |
50 |
|
remove_user/3, |
51 |
|
determine_amp_strategy/3, |
52 |
|
sm_filter_offline_message/3]). |
53 |
|
|
54 |
|
%% ejabberd handlers |
55 |
|
-export([process_mam_iq/5]). |
56 |
|
|
57 |
|
%% gdpr callbacks |
58 |
|
-export([get_personal_data/3]). |
59 |
|
|
60 |
|
%%private |
61 |
|
-export([archive_message_from_ct/1]). |
62 |
|
-export([lookup_messages/2]). |
63 |
|
-export([archive_id_int/2]). |
64 |
|
|
65 |
|
-ignore_xref([archive_message_from_ct/1, archive_size/2, |
66 |
|
archive_size_with_host_type/3, delete_archive/1]). |
67 |
|
|
68 |
|
-type host_type() :: mongooseim:host_type(). |
69 |
|
|
70 |
|
-include("mongoose.hrl"). |
71 |
|
-include("jlib.hrl"). |
72 |
|
-include("amp.hrl"). |
73 |
|
|
74 |
|
%% ---------------------------------------------------------------------- |
75 |
|
%% API |
76 |
|
|
77 |
|
-spec delete_archive(jid:jid()) -> 'ok'. |
78 |
|
delete_archive(ArcJID) -> |
79 |
513 |
?LOG_DEBUG(#{what => mam_delete_archive, jid => ArcJID}), |
80 |
513 |
HostType = jid_to_host_type(ArcJID), |
81 |
513 |
ArcID = archive_id_int(HostType, ArcJID), |
82 |
513 |
remove_archive_hook(HostType, ArcID, ArcJID), |
83 |
513 |
ok. |
84 |
|
|
85 |
|
-spec archive_size(jid:server(), jid:user()) -> integer(). |
86 |
|
archive_size(Server, User) |
87 |
|
when is_binary(Server), is_binary(User) -> |
88 |
1605 |
ArcJID = jid:make_bare(User, Server), |
89 |
1605 |
HostType = jid_to_host_type(ArcJID), |
90 |
1605 |
ArcID = archive_id_int(HostType, ArcJID), |
91 |
1605 |
archive_size(HostType, ArcID, ArcJID). |
92 |
|
|
93 |
|
-spec archive_size_with_host_type(host_type(), jid:server(), jid:user()) -> integer(). |
94 |
|
archive_size_with_host_type(HostType, Server, User) -> |
95 |
48 |
ArcJID = jid:make_bare(User, Server), |
96 |
48 |
ArcID = archive_id_int(HostType, ArcJID), |
97 |
48 |
archive_size(HostType, ArcID, ArcJID). |
98 |
|
|
99 |
|
-spec archive_id(jid:server(), jid:user()) -> integer() | undefined. |
100 |
|
archive_id(Server, User) |
101 |
|
when is_binary(Server), is_binary(User) -> |
102 |
397 |
ArcJID = jid:make_bare(User, Server), |
103 |
397 |
HostType = jid_to_host_type(ArcJID), |
104 |
397 |
archive_id_int(HostType, ArcJID). |
105 |
|
|
106 |
|
%% gen_mod callbacks |
107 |
|
%% Starting and stopping functions for users' archives |
108 |
|
|
109 |
|
-spec start(host_type(), gen_mod:module_opts()) -> any(). |
110 |
|
start(HostType, Opts) -> |
111 |
136 |
?LOG_INFO(#{what => mam_starting, host_type => HostType}), |
112 |
136 |
add_iq_handlers(HostType, Opts), |
113 |
136 |
ok. |
114 |
|
|
115 |
|
-spec stop(host_type()) -> any(). |
116 |
|
stop(HostType) -> |
117 |
136 |
?LOG_INFO(#{what => mam_stopping, host_type => HostType}), |
118 |
136 |
remove_iq_handlers(HostType), |
119 |
136 |
ok. |
120 |
|
|
121 |
|
-spec supported_features() -> [atom()]. |
122 |
|
supported_features() -> |
123 |
1 |
[dynamic_domains]. |
124 |
|
|
125 |
|
%% ---------------------------------------------------------------------- |
126 |
|
%% hooks and handlers |
127 |
|
|
128 |
|
%% `To' is an account or server entity hosting the archive. |
129 |
|
%% Servers that archive messages on behalf of local users SHOULD expose archives |
130 |
|
%% to the user on their bare JID (i.e. `From.luser'), |
131 |
|
%% while a MUC service might allow MAM queries to be sent to the room's bare JID |
132 |
|
%% (i.e `To.luser'). |
133 |
|
-spec process_mam_iq(Acc :: mongoose_acc:t(), |
134 |
|
From :: jid:jid(), To :: jid:jid(), IQ :: jlib:iq(), |
135 |
|
_Extra) -> {mongoose_acc:t(), jlib:iq() | ignore}. |
136 |
|
process_mam_iq(Acc, From, To, IQ, _Extra) -> |
137 |
1489 |
HostType = mongoose_acc:host_type(Acc), |
138 |
1489 |
mod_mam_utils:maybe_log_deprecation(IQ), |
139 |
1489 |
Action = mam_iq:action(IQ), |
140 |
1489 |
case is_action_allowed(HostType, Action, From, To) of |
141 |
|
true -> |
142 |
1489 |
case mod_mam_utils:wait_shaper(HostType, To#jid.lserver, Action, From) of |
143 |
|
continue -> |
144 |
1489 |
handle_error_iq(HostType, Acc, To, Action, |
145 |
|
handle_mam_iq(Action, From, To, IQ, Acc)); |
146 |
|
{error, max_delay_reached} -> |
147 |
:-( |
?LOG_WARNING(#{what => mam_max_delay_reached, |
148 |
|
text => <<"Return max_delay_reached error IQ from MAM">>, |
149 |
:-( |
action => Action, acc => Acc}), |
150 |
:-( |
mongoose_instrument:execute(mod_mam_pm_dropped_iq, |
151 |
|
#{host_type => HostType}, #{count => 1}), |
152 |
:-( |
{Acc, return_max_delay_reached_error_iq(IQ)} |
153 |
|
end; |
154 |
|
false -> |
155 |
:-( |
mongoose_instrument:execute(mod_mam_pm_dropped_iq, |
156 |
|
#{host_type => HostType}, #{count => 1}), |
157 |
:-( |
{Acc, return_action_not_allowed_error_iq(IQ)} |
158 |
|
end. |
159 |
|
|
160 |
|
-spec disco_local_features(mongoose_disco:feature_acc(), |
161 |
|
map(), |
162 |
|
map()) -> {ok, mongoose_disco:feature_acc()}. |
163 |
|
disco_local_features(Acc = #{host_type := HostType, node := <<>>}, _, _) -> |
164 |
48 |
{ok, mongoose_disco:add_features(mod_mam_utils:features(?MODULE, HostType), Acc)}; |
165 |
|
disco_local_features(Acc, _, _) -> |
166 |
:-( |
{ok, Acc}. |
167 |
|
|
168 |
|
-spec disco_sm_features(mongoose_disco:feature_acc(), |
169 |
|
map(), map()) -> {ok, mongoose_disco:feature_acc()}. |
170 |
|
disco_sm_features(Acc = #{host_type := HostType, node := <<>>}, _, _) -> |
171 |
21 |
{ok, mongoose_disco:add_features(mod_mam_utils:features(?MODULE, HostType), Acc)}; |
172 |
|
disco_sm_features(Acc, _, _) -> |
173 |
:-( |
{ok, Acc}. |
174 |
|
|
175 |
|
%% @doc Handle an outgoing message. |
176 |
|
%% |
177 |
|
%% Note: for outgoing messages, the server MUST use the value of the 'to' |
178 |
|
%% attribute as the target JID. |
179 |
|
-spec user_send_message(Acc, Args, Extra) -> {ok, Acc} when |
180 |
|
Acc :: mongoose_acc:t(), |
181 |
|
Args :: map(), |
182 |
|
Extra :: gen_hook:extra(). |
183 |
|
user_send_message(Acc, _, _) -> |
184 |
1411 |
{From, To, Packet} = mongoose_acc:packet(Acc), |
185 |
1411 |
?LOG_DEBUG(#{what => mam_user_send_message, acc => Acc}), |
186 |
1411 |
{_, Acc2} = handle_package(outgoing, true, From, To, From, Packet, Acc), |
187 |
1411 |
{ok, Acc2}. |
188 |
|
|
189 |
|
%% @doc Handle an incoming message. |
190 |
|
%% |
191 |
|
%% Note: For incoming messages, the server MUST use the value of the |
192 |
|
%% 'from' attribute as the target JID. |
193 |
|
%% |
194 |
|
%% Return drop to drop the packet, or the original input to let it through. |
195 |
|
%% From and To are jid records. |
196 |
|
-spec filter_packet(FPacketAcc, Params, Extra) -> {ok, FPacketAcc} when |
197 |
|
FPacketAcc :: mongoose_hooks:filter_packet_acc(), |
198 |
|
Params :: map(), |
199 |
|
Extra :: gen_hook:extra(). |
200 |
|
filter_packet({From, To, Acc1, Packet}, _, _) -> |
201 |
11205 |
?LOG_DEBUG(#{what => mam_user_receive_packet, acc => Acc1}), |
202 |
11205 |
HostType = mongoose_acc:host_type(Acc1), |
203 |
11205 |
Type = mongoose_lib:get_message_type(Acc1), |
204 |
11205 |
{AmpEvent, PacketAfterArchive, Acc3} = |
205 |
|
case mongoose_lib:does_local_user_exist(HostType, To, Type) of |
206 |
|
false -> |
207 |
481 |
{mam_failed, Packet, Acc1}; |
208 |
|
true -> |
209 |
10724 |
case process_incoming_packet(From, To, Packet, Acc1) of |
210 |
|
{undefined, Acc2} -> |
211 |
9537 |
{mam_failed, Packet, Acc2}; |
212 |
|
{MessID, Acc2} -> |
213 |
1187 |
Packet2 = mod_mam_utils:maybe_add_arcid_elems( |
214 |
|
To, MessID, Packet, |
215 |
|
mod_mam_params:add_stanzaid_element(?MODULE, HostType)), |
216 |
1187 |
{archived, Packet2, Acc2} |
217 |
|
end |
218 |
|
end, |
219 |
11205 |
Acc4 = mongoose_acc:update_stanza(#{ element => PacketAfterArchive, |
220 |
|
from_jid => From, |
221 |
|
to_jid => To }, Acc3), |
222 |
11205 |
Acc5 = mod_amp:check_packet(Acc4, AmpEvent), |
223 |
11205 |
{ok, {From, To, Acc5, mongoose_acc:element(Acc5)}}. |
224 |
|
|
225 |
|
process_incoming_packet(From, To, Packet, Acc) -> |
226 |
10724 |
handle_package(incoming, true, To, From, From, Packet, Acc). |
227 |
|
|
228 |
|
%% hook handler |
229 |
|
-spec remove_user(Acc, Params, Extra) -> {ok, Acc} when |
230 |
|
Acc :: mongoose_acc:t(), |
231 |
|
Params :: #{jid := jid:jid()}, |
232 |
|
Extra :: gen_hook:extra(). |
233 |
|
remove_user(Acc, #{jid := JID}, _) -> |
234 |
243 |
delete_archive(JID), |
235 |
243 |
{ok, Acc}. |
236 |
|
|
237 |
|
-spec determine_amp_strategy(StrategyAcc, Params, Extra) -> {ok, StrategyAcc} when |
238 |
|
StrategyAcc :: mod_amp:amp_strategy(), |
239 |
|
Params :: #{from := jid:jid(), to := jid:jid(), packet := exml:element(), event := mod_amp:amp_event()}, |
240 |
|
Extra :: gen_hook:extra(). |
241 |
|
determine_amp_strategy(Strategy = #amp_strategy{deliver = Deliver}, |
242 |
|
#{from := FromJID, to := ToJID, packet := Packet, event := initial_check}, |
243 |
|
_) -> |
244 |
96 |
HostType = jid_to_host_type(ToJID), |
245 |
88 |
ShouldBeStored = is_archivable_message(HostType, incoming, Packet) |
246 |
88 |
andalso is_interesting(ToJID, FromJID) |
247 |
88 |
andalso ejabberd_auth:does_user_exist(ToJID), |
248 |
88 |
NewStrategy = case ShouldBeStored of |
249 |
64 |
true -> Strategy#amp_strategy{deliver = amp_deliver_strategy(Deliver)}; |
250 |
24 |
false -> Strategy |
251 |
|
end, |
252 |
88 |
{ok, NewStrategy}; |
253 |
|
determine_amp_strategy(Strategy, _, _) -> |
254 |
144 |
{ok, Strategy}. |
255 |
|
|
256 |
|
-spec sm_filter_offline_message(Acc, Params, Extra) -> {ok, Acc} when |
257 |
|
Acc :: boolean(), |
258 |
|
Params :: #{packet := exml:element()}, |
259 |
|
Extra :: gen_hook:extra(). |
260 |
|
sm_filter_offline_message(_Drop=false, #{packet := Packet}, _) -> |
261 |
|
%% If ... |
262 |
10 |
{ok, mod_mam_utils:is_mam_result_message(Packet)}; |
263 |
|
%% ... than drop the message |
264 |
|
sm_filter_offline_message(Other, _, _) -> |
265 |
:-( |
{ok, Other}. |
266 |
|
|
267 |
|
-spec get_personal_data(Acc, Params, Extra) -> {ok, Acc} when |
268 |
|
Acc :: gdpr:personal_data(), |
269 |
|
Params :: #{jid := jid:jid()}, |
270 |
|
Extra :: #{host_type := mongooseim:host_type()}. |
271 |
|
get_personal_data(Acc, #{jid := ArcJID}, #{host_type := HostType}) -> |
272 |
51 |
Schema = ["id", "from", "message"], |
273 |
51 |
Entries = mongoose_hooks:get_mam_pm_gdpr_data(HostType, ArcJID), |
274 |
51 |
{ok, [{mam_pm, Schema, Entries} | Acc]}. |
275 |
|
|
276 |
|
%% ---------------------------------------------------------------------- |
277 |
|
%% Internal functions |
278 |
|
|
279 |
|
-spec jid_to_host_type(jid:jid()) -> host_type(). |
280 |
|
jid_to_host_type(#jid{lserver=LServer}) -> |
281 |
7959 |
lserver_to_host_type(LServer). |
282 |
|
|
283 |
|
lserver_to_host_type(LServer) -> |
284 |
7959 |
case mongoose_domain_api:get_domain_host_type(LServer) of |
285 |
|
{ok, HostType} -> |
286 |
7951 |
HostType; |
287 |
|
{error, not_found} -> |
288 |
8 |
error({get_domain_host_type_failed, LServer}) |
289 |
|
end. |
290 |
|
|
291 |
|
-spec acc_to_host_type(mongoose_acc:t()) -> host_type(). |
292 |
|
acc_to_host_type(Acc) -> |
293 |
13624 |
case mongoose_acc:host_type(Acc) of |
294 |
|
undefined -> |
295 |
:-( |
lserver_to_host_type(mongoose_acc:lserver(Acc)); |
296 |
|
HostType -> |
297 |
13624 |
HostType |
298 |
|
end. |
299 |
|
|
300 |
|
-spec is_action_allowed(HostType :: host_type(), |
301 |
|
Action :: mam_iq:action(), From :: jid:jid(), |
302 |
|
To :: jid:jid()) -> boolean(). |
303 |
|
is_action_allowed(HostType, Action, From, To) -> |
304 |
1489 |
case acl:match_rule(HostType, To#jid.lserver, Action, From, default) of |
305 |
:-( |
allow -> true; |
306 |
:-( |
deny -> false; |
307 |
1489 |
default -> is_action_allowed_by_default(Action, From, To) |
308 |
|
end. |
309 |
|
|
310 |
|
-spec is_action_allowed_by_default(Action :: mam_iq:action(), From :: jid:jid(), |
311 |
|
To :: jid:jid()) -> boolean(). |
312 |
|
is_action_allowed_by_default(_Action, From, To) -> |
313 |
1489 |
jid:are_bare_equal(From, To). |
314 |
|
|
315 |
|
-spec handle_mam_iq(mam_iq:action(), From :: jid:jid(), To :: jid:jid(), |
316 |
|
IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> |
317 |
|
jlib:iq() | {error, term(), jlib:iq()}. |
318 |
|
handle_mam_iq(Action, From, To, IQ, Acc) -> |
319 |
1489 |
case Action of |
320 |
|
mam_get_prefs -> |
321 |
161 |
handle_get_prefs(To, IQ, Acc); |
322 |
|
mam_set_prefs -> |
323 |
308 |
handle_set_prefs(To, IQ, Acc); |
324 |
|
mam_set_message_form -> |
325 |
964 |
handle_set_message_form(From, To, IQ, Acc); |
326 |
|
mam_get_message_form -> |
327 |
35 |
handle_get_message_form(From, To, IQ, Acc); |
328 |
|
mam_get_metadata -> |
329 |
21 |
handle_get_metadata(From, IQ, Acc) |
330 |
|
end. |
331 |
|
|
332 |
|
-spec handle_set_prefs(jid:jid(), jlib:iq(), mongoose_acc:t()) -> |
333 |
|
jlib:iq() | {error, term(), jlib:iq()}. |
334 |
|
handle_set_prefs(ArcJID=#jid{}, IQ=#iq{sub_el = PrefsEl}, Acc) -> |
335 |
308 |
{DefaultMode, AlwaysJIDs, NeverJIDs} = mod_mam_utils:parse_prefs(PrefsEl), |
336 |
308 |
?LOG_DEBUG(#{what => mam_set_prefs, default_mode => DefaultMode, |
337 |
308 |
always_jids => AlwaysJIDs, never_jids => NeverJIDs, iq => IQ}), |
338 |
308 |
HostType = acc_to_host_type(Acc), |
339 |
308 |
ArcID = archive_id_int(HostType, ArcJID), |
340 |
308 |
Res = set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs), |
341 |
308 |
handle_set_prefs_result(Res, DefaultMode, AlwaysJIDs, NeverJIDs, IQ). |
342 |
|
|
343 |
|
handle_set_prefs_result(ok, DefaultMode, AlwaysJIDs, NeverJIDs, IQ) -> |
344 |
308 |
Namespace = IQ#iq.xmlns, |
345 |
308 |
ResultPrefsEl = mod_mam_utils:result_prefs(DefaultMode, AlwaysJIDs, NeverJIDs, Namespace), |
346 |
308 |
IQ#iq{type = result, sub_el = [ResultPrefsEl]}; |
347 |
|
handle_set_prefs_result({error, Reason}, |
348 |
|
_DefaultMode, _AlwaysJIDs, _NeverJIDs, IQ) -> |
349 |
:-( |
return_error_iq(IQ, Reason). |
350 |
|
|
351 |
|
-spec handle_get_prefs(jid:jid(), IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> |
352 |
|
jlib:iq() | {error, term(), jlib:iq()}. |
353 |
|
handle_get_prefs(ArcJID=#jid{}, IQ=#iq{}, Acc) -> |
354 |
161 |
HostType = acc_to_host_type(Acc), |
355 |
161 |
ArcID = archive_id_int(HostType, ArcJID), |
356 |
161 |
Res = get_prefs(HostType, ArcID, ArcJID, always), |
357 |
161 |
handle_get_prefs_result(Res, IQ). |
358 |
|
|
359 |
|
handle_get_prefs_result({DefaultMode, AlwaysJIDs, NeverJIDs}, IQ) -> |
360 |
161 |
?LOG_DEBUG(#{what => mam_get_prefs_result, default_mode => DefaultMode, |
361 |
161 |
always_jids => AlwaysJIDs, never_jids => NeverJIDs, iq => IQ}), |
362 |
161 |
Namespace = IQ#iq.xmlns, |
363 |
161 |
ResultPrefsEl = mod_mam_utils:result_prefs(DefaultMode, AlwaysJIDs, NeverJIDs, Namespace), |
364 |
161 |
IQ#iq{type = result, sub_el = [ResultPrefsEl]}; |
365 |
|
handle_get_prefs_result({error, Reason}, IQ) -> |
366 |
:-( |
return_error_iq(IQ, Reason). |
367 |
|
|
368 |
|
-spec handle_set_message_form(From :: jid:jid(), ArcJID :: jid:jid(), |
369 |
|
IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> |
370 |
|
jlib:iq() | ignore | {error, term(), jlib:iq()}. |
371 |
|
handle_set_message_form(#jid{} = From, #jid{} = ArcJID, #iq{} = IQ, Acc) -> |
372 |
964 |
HostType = acc_to_host_type(Acc), |
373 |
964 |
ArcID = archive_id_int(HostType, ArcJID), |
374 |
964 |
try iq_to_lookup_params(HostType, IQ) of |
375 |
|
Params0 -> |
376 |
950 |
do_handle_set_message_form(Params0, From, ArcID, ArcJID, IQ, HostType) |
377 |
|
catch _C:R:S -> |
378 |
14 |
report_issue({R, S}, mam_lookup_failed, ArcJID, IQ), |
379 |
14 |
return_error_iq(IQ, R) |
380 |
|
end. |
381 |
|
|
382 |
|
|
383 |
|
-spec do_handle_set_message_form(Params :: mam_iq:lookup_params(), |
384 |
|
From :: jid:jid(), |
385 |
|
ArcId :: mod_mam:archive_id(), |
386 |
|
ArcJID :: jid:jid(), |
387 |
|
IQ :: jlib:iq(), |
388 |
|
HostType :: mongooseim:host_type()) -> |
389 |
|
jlib:iq() | ignore | {error, term(), jlib:iq()}. |
390 |
|
do_handle_set_message_form(Params0, From, ArcID, ArcJID, |
391 |
|
#iq{xmlns=MamNs, sub_el = QueryEl} = IQ, |
392 |
|
HostType) -> |
393 |
950 |
QueryID = exml_query:attr(QueryEl, <<"queryid">>, <<>>), |
394 |
950 |
Params = mam_iq:lookup_params_with_archive_details(Params0, ArcID, ArcJID, From), |
395 |
950 |
case mod_mam_utils:lookup(HostType, Params, fun lookup_messages/2) of |
396 |
|
{error, Reason} -> |
397 |
42 |
report_issue(Reason, mam_lookup_failed, ArcJID, IQ), |
398 |
42 |
return_error_iq(IQ, Reason); |
399 |
|
{ok, #{total_count := TotalCount, offset := Offset, messages := MessageRows, |
400 |
|
is_complete := IsComplete}} -> |
401 |
|
%% Reverse order of messages if the client requested it |
402 |
908 |
MessageRows1 = mod_mam_utils:maybe_reverse_messages(Params0, MessageRows), |
403 |
|
%% Forward messages |
404 |
908 |
{FirstMessID, LastMessID} = forward_messages(HostType, From, ArcJID, MamNs, |
405 |
|
QueryID, MessageRows1, true), |
406 |
|
%% Make fin iq |
407 |
908 |
IsStable = true, |
408 |
908 |
ResultSetEl = mod_mam_utils:result_set(FirstMessID, LastMessID, Offset, TotalCount), |
409 |
908 |
ExtFinMod = mod_mam_params:extra_fin_element_module(?MODULE, HostType), |
410 |
908 |
FinElem = mod_mam_utils:make_fin_element(HostType, Params, IQ#iq.xmlns, |
411 |
|
IsComplete, IsStable, |
412 |
|
ResultSetEl, ExtFinMod), |
413 |
908 |
IQ#iq{type = result, sub_el = [FinElem]} |
414 |
|
end. |
415 |
|
|
416 |
|
iq_to_lookup_params(HostType, IQ) -> |
417 |
964 |
Max = mod_mam_params:max_result_limit(?MODULE, HostType), |
418 |
964 |
Def = mod_mam_params:default_result_limit(?MODULE, HostType), |
419 |
964 |
Ext = mod_mam_params:extra_params_module(?MODULE, HostType), |
420 |
964 |
Sim = mod_mam_params:enforce_simple_queries(?MODULE, HostType), |
421 |
964 |
mam_iq:form_to_lookup_params(IQ, Max, Def, Ext, Sim). |
422 |
|
|
423 |
|
forward_messages(HostType, From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> |
424 |
|
%% Forward messages |
425 |
908 |
{FirstMessID, LastMessID} = |
426 |
|
case MessageRows of |
427 |
163 |
[] -> {undefined, undefined}; |
428 |
745 |
[_|_] -> {message_row_to_ext_id(hd(MessageRows)), |
429 |
|
message_row_to_ext_id(lists:last(MessageRows))} |
430 |
|
end, |
431 |
908 |
SendModule = mod_mam_params:send_message_mod(?MODULE, HostType), |
432 |
908 |
[send_message(SendModule, Row, ArcJID, From, |
433 |
|
message_row_to_xml(MamNs, Row, QueryID, SetClientNs)) |
434 |
908 |
|| Row <- MessageRows], |
435 |
908 |
{FirstMessID, LastMessID}. |
436 |
|
|
437 |
|
send_message(SendModule, Row, ArcJID, From, Packet) -> |
438 |
2796 |
mam_send_message:call_send_message(SendModule, Row, ArcJID, From, Packet). |
439 |
|
|
440 |
|
-spec handle_get_message_form(jid:jid(), jid:jid(), jlib:iq(), mongoose_acc:t()) -> |
441 |
|
jlib:iq(). |
442 |
|
handle_get_message_form(_From=#jid{}, _ArcJID=#jid{}, IQ=#iq{}, Acc) -> |
443 |
35 |
HostType = acc_to_host_type(Acc), |
444 |
35 |
return_message_form_iq(HostType, IQ). |
445 |
|
|
446 |
|
-spec handle_get_metadata(jid:jid(), jlib:iq(), mongoose_acc:t()) -> |
447 |
|
jlib:iq() | {error, term(), jlib:iq()}. |
448 |
|
handle_get_metadata(ArcJID=#jid{}, IQ=#iq{}, Acc) -> |
449 |
21 |
HostType = acc_to_host_type(Acc), |
450 |
21 |
ArcID = archive_id_int(HostType, ArcJID), |
451 |
21 |
case mod_mam_utils:lookup_first_and_last_messages(HostType, ArcID, ArcJID, |
452 |
|
fun lookup_messages/2) of |
453 |
|
{error, Reason} -> |
454 |
:-( |
report_issue(Reason, mam_lookup_failed, ArcJID, IQ), |
455 |
:-( |
return_error_iq(IQ, Reason); |
456 |
|
{FirstMsg, LastMsg} -> |
457 |
14 |
{FirstMsgID, FirstMsgTS} = mod_mam_utils:get_msg_id_and_timestamp(FirstMsg), |
458 |
14 |
{LastMsgID, LastMsgTS} = mod_mam_utils:get_msg_id_and_timestamp(LastMsg), |
459 |
14 |
MetadataElement = |
460 |
|
mod_mam_utils:make_metadata_element(FirstMsgID, FirstMsgTS, LastMsgID, LastMsgTS), |
461 |
14 |
IQ#iq{type = result, sub_el = [MetadataElement]}; |
462 |
|
empty_archive -> |
463 |
7 |
MetadataElement = mod_mam_utils:make_metadata_element(), |
464 |
7 |
IQ#iq{type = result, sub_el = [MetadataElement]} |
465 |
|
end. |
466 |
|
|
467 |
24 |
amp_deliver_strategy([none]) -> [stored, none]; |
468 |
40 |
amp_deliver_strategy([direct, none]) -> [direct, stored, none]. |
469 |
|
|
470 |
|
-spec handle_package(Dir :: incoming | outgoing, ReturnMessID :: boolean(), |
471 |
|
LocJID :: jid:jid(), RemJID :: jid:jid(), SrcJID :: jid:jid(), |
472 |
|
Packet :: exml:element(), Acc :: mongoose_acc:t()) -> |
473 |
|
{MaybeMessID :: binary() | undefined, Acc :: mongoose_acc:t()}. |
474 |
|
handle_package(Dir, ReturnMessID, |
475 |
|
LocJID = #jid{}, RemJID = #jid{}, SrcJID = #jid{}, Packet, Acc) -> |
476 |
12135 |
HostType = acc_to_host_type(Acc), |
477 |
12135 |
MsgType = exml_query:attr(Packet, <<"type">>), |
478 |
12135 |
case is_archivable_message(HostType, Dir, Packet) |
479 |
2857 |
andalso should_archive_if_groupchat(HostType, MsgType) |
480 |
2621 |
andalso should_archive_if_sent_to_yourself(LocJID, RemJID, Dir) of |
481 |
|
true -> |
482 |
2607 |
ArcID = archive_id_int(HostType, LocJID), |
483 |
2607 |
OriginID = mod_mam_utils:get_origin_id(Packet), |
484 |
2607 |
case is_interesting(HostType, LocJID, RemJID, ArcID) of |
485 |
|
true -> |
486 |
2313 |
MessID = mod_mam_utils:get_or_generate_mam_id(Acc), |
487 |
2313 |
IsGroupChat = mod_mam_utils:is_groupchat(MsgType), |
488 |
2313 |
Params = #{message_id => MessID, |
489 |
|
archive_id => ArcID, |
490 |
|
local_jid => LocJID, |
491 |
|
remote_jid => RemJID, |
492 |
|
source_jid => SrcJID, |
493 |
|
origin_id => OriginID, |
494 |
|
direction => Dir, |
495 |
|
packet => Packet, |
496 |
|
is_groupchat => IsGroupChat}, |
497 |
2313 |
Result = archive_message(HostType, Params), |
498 |
2313 |
ExtMessId = return_external_message_id_if_ok(ReturnMessID, Result, MessID), |
499 |
2313 |
{ExtMessId, return_acc_with_mam_id_if_configured(ExtMessId, HostType, Acc)}; |
500 |
|
false -> |
501 |
294 |
{undefined, Acc} |
502 |
|
end; |
503 |
|
false -> |
504 |
9528 |
{undefined, Acc} |
505 |
|
end. |
506 |
|
|
507 |
|
should_archive_if_groupchat(HostType, <<"groupchat">>) -> |
508 |
383 |
gen_mod:get_module_opt(HostType, ?MODULE, archive_groupchats); |
509 |
|
should_archive_if_groupchat(_, _) -> |
510 |
2474 |
true. |
511 |
|
|
512 |
|
%% Only store messages sent to yourself in user_send_message. |
513 |
|
should_archive_if_sent_to_yourself(LocJID, RemJID, incoming) -> |
514 |
1372 |
not jid:are_bare_equal(LocJID, RemJID); |
515 |
|
should_archive_if_sent_to_yourself(_LocJID, _RemJID, _Dir) -> |
516 |
1249 |
true. |
517 |
|
|
518 |
|
-spec return_external_message_id_if_ok(ReturnMessID :: boolean(), |
519 |
|
ArchivingResult :: ok | any(), |
520 |
|
MessID :: integer()) -> binary() | undefined. |
521 |
|
return_external_message_id_if_ok(true, ok, MessID) -> |
522 |
2253 |
mod_mam_utils:mess_id_to_external_binary(MessID); |
523 |
|
return_external_message_id_if_ok(_, _, _MessID) -> |
524 |
60 |
undefined. |
525 |
|
|
526 |
|
return_acc_with_mam_id_if_configured(undefined, _, Acc) -> |
527 |
60 |
Acc; |
528 |
|
return_acc_with_mam_id_if_configured(ExtMessId, HostType, Acc) -> |
529 |
2253 |
case gen_mod:get_module_opt(HostType, ?MODULE, same_mam_id_for_peers) of |
530 |
2211 |
false -> mongoose_acc:set(mam, mam_id, ExtMessId, Acc); |
531 |
42 |
true -> mongoose_acc:set_permanent(mam, mam_id, ExtMessId, Acc) |
532 |
|
end. |
533 |
|
|
534 |
|
is_interesting(LocJID, RemJID) -> |
535 |
88 |
HostType = jid_to_host_type(LocJID), |
536 |
88 |
ArcID = archive_id_int(HostType, LocJID), |
537 |
88 |
is_interesting(HostType, LocJID, RemJID, ArcID). |
538 |
|
|
539 |
|
is_interesting(HostType, LocJID, RemJID, ArcID) -> |
540 |
2695 |
case get_behaviour(HostType, ArcID, LocJID, RemJID) of |
541 |
2359 |
always -> true; |
542 |
252 |
never -> false; |
543 |
84 |
roster -> mod_mam_utils:is_jid_in_user_roster(HostType, LocJID, RemJID) |
544 |
|
end. |
545 |
|
|
546 |
|
%% ---------------------------------------------------------------------- |
547 |
|
%% Backend wrappers |
548 |
|
|
549 |
|
-spec archive_id_int(host_type(), jid:jid()) -> |
550 |
|
non_neg_integer() | undefined. |
551 |
|
archive_id_int(HostType, ArcJID=#jid{}) -> |
552 |
9234 |
mongoose_hooks:mam_archive_id(HostType, ArcJID). |
553 |
|
|
554 |
|
-spec archive_size(host_type(), mod_mam:archive_id(), jid:jid()) -> integer(). |
555 |
|
archive_size(HostType, ArcID, ArcJID=#jid{}) -> |
556 |
1653 |
mongoose_hooks:mam_archive_size(HostType, ArcID, ArcJID). |
557 |
|
|
558 |
|
-spec get_behaviour(host_type(), mod_mam:archive_id(), LocJID :: jid:jid(), |
559 |
|
RemJID :: jid:jid()) -> atom(). |
560 |
|
get_behaviour(HostType, ArcID, LocJID=#jid{}, RemJID=#jid{}) -> |
561 |
2695 |
mongoose_hooks:mam_get_behaviour(HostType, ArcID, LocJID, RemJID). |
562 |
|
|
563 |
|
-spec set_prefs(host_type(), mod_mam:archive_id(), ArcJID :: jid:jid(), |
564 |
|
DefaultMode :: atom(), AlwaysJIDs :: [jid:literal_jid()], |
565 |
|
NeverJIDs :: [jid:literal_jid()]) -> any(). |
566 |
|
set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> |
567 |
308 |
Result = mongoose_hooks:mam_set_prefs(HostType, ArcID, ArcJID, DefaultMode, |
568 |
|
AlwaysJIDs, NeverJIDs), |
569 |
308 |
mongoose_instrument:execute(mod_mam_pm_set_prefs, #{host_type => HostType}, |
570 |
|
#{jid => ArcJID, count => 1}), |
571 |
308 |
Result. |
572 |
|
|
573 |
|
%% @doc Load settings from the database. |
574 |
|
-spec get_prefs(HostType :: host_type(), ArcID :: mod_mam:archive_id(), |
575 |
|
ArcJID :: jid:jid(), GlobalDefaultMode :: mod_mam:archive_behaviour() |
576 |
|
) -> mod_mam:preference() | {error, Reason :: term()}. |
577 |
|
get_prefs(HostType, ArcID, ArcJID, GlobalDefaultMode) -> |
578 |
161 |
Result = mongoose_hooks:mam_get_prefs(HostType, GlobalDefaultMode, ArcID, ArcJID), |
579 |
161 |
mongoose_instrument:execute(mod_mam_pm_get_prefs, #{host_type => HostType}, |
580 |
|
#{jid => ArcJID, count => 1}), |
581 |
161 |
Result. |
582 |
|
|
583 |
|
-spec remove_archive_hook(host_type(), mod_mam:archive_id(), jid:jid()) -> ok. |
584 |
|
remove_archive_hook(HostType, ArcID, ArcJID=#jid{}) -> |
585 |
513 |
mongoose_hooks:mam_remove_archive(HostType, ArcID, ArcJID), |
586 |
513 |
mongoose_instrument:execute(mod_mam_pm_remove_archive, #{host_type => HostType}, |
587 |
|
#{jid => ArcJID, count => 1}). |
588 |
|
|
589 |
|
-spec lookup_messages(HostType :: host_type(), Params :: map()) -> |
590 |
|
{ok, mod_mam:lookup_result()} |
591 |
|
| {error, 'policy-violation'} |
592 |
|
| {error, Reason :: term()}. |
593 |
|
lookup_messages(HostType, Params) -> |
594 |
1029 |
Result = lookup_messages_without_policy_violation_check(HostType, Params), |
595 |
|
%% If a query returns a number of stanzas greater than this limit and the |
596 |
|
%% client did not specify a limit using RSM then the server should return |
597 |
|
%% a policy-violation error to the client. |
598 |
1029 |
mod_mam_utils:check_result_for_policy_violation(Params, Result). |
599 |
|
|
600 |
|
lookup_messages_without_policy_violation_check( |
601 |
|
HostType, #{search_text := SearchText} = Params) -> |
602 |
1029 |
case SearchText /= undefined andalso |
603 |
42 |
not mod_mam_params:has_full_text_search(?MODULE, HostType) of |
604 |
|
true -> %% Use of disabled full text search |
605 |
7 |
{error, 'not-supported'}; |
606 |
|
false -> |
607 |
1022 |
mongoose_instrument:span(mod_mam_pm_lookup, #{host_type => HostType}, |
608 |
|
fun perform_lookup/2, [HostType, Params], |
609 |
1022 |
fun(Time, Result) -> measure_lookup(Params, Time, Result) end) |
610 |
|
end. |
611 |
|
|
612 |
|
perform_lookup(HostType, Params) -> |
613 |
1022 |
case maps:get(message_ids, Params, undefined) of |
614 |
|
undefined -> |
615 |
1001 |
mongoose_hooks:mam_lookup_messages(HostType, Params#{message_id => undefined}); |
616 |
|
IDs -> |
617 |
21 |
mod_mam_utils:lookup_specific_messages(HostType, Params, IDs, |
618 |
|
fun mongoose_hooks:mam_lookup_messages/2) |
619 |
|
end. |
620 |
|
|
621 |
|
measure_lookup(Params, Time, {ok, {_TotalCount, _Offset, MessageRows}}) -> |
622 |
987 |
M = case Params of |
623 |
205 |
#{is_simple := true} -> #{simple => 1}; |
624 |
782 |
#{} -> #{} |
625 |
|
end, |
626 |
987 |
M#{params => Params, count => 1, time => Time, size => length(MessageRows)}; |
627 |
|
measure_lookup(_, _, _OtherResult) -> |
628 |
35 |
#{}. |
629 |
|
|
630 |
|
archive_message_from_ct(Params = #{local_jid := JID}) -> |
631 |
5260 |
HostType = jid_to_host_type(JID), |
632 |
5260 |
archive_message(HostType, Params). |
633 |
|
|
634 |
|
-spec archive_message(host_type(), mod_mam:archive_message_params()) -> |
635 |
|
ok | {error, timeout}. |
636 |
|
archive_message(HostType, Params) -> |
637 |
7573 |
mongoose_instrument:span(mod_mam_pm_archive_message, #{host_type => HostType}, |
638 |
|
fun mongoose_hooks:mam_archive_message/2, [HostType, Params], |
639 |
7573 |
fun(Time, _Result) -> #{params => Params, time => Time, count => 1} end). |
640 |
|
|
641 |
|
%% ---------------------------------------------------------------------- |
642 |
|
%% Helpers |
643 |
|
|
644 |
|
-spec message_row_to_xml(binary(), mod_mam:message_row(), QueryId :: binary(), boolean()) -> |
645 |
|
exml:element(). |
646 |
|
message_row_to_xml(MamNs, #{id := MessID, jid := SrcJID, packet := Packet}, |
647 |
|
QueryID, SetClientNs) -> |
648 |
2796 |
{Microseconds, _NodeMessID} = mod_mam_utils:decode_compact_uuid(MessID), |
649 |
2796 |
TS = calendar:system_time_to_rfc3339(Microseconds, [{offset, "Z"}, {unit, microsecond}]), |
650 |
2796 |
BExtMessID = mod_mam_utils:mess_id_to_external_binary(MessID), |
651 |
2796 |
Packet1 = mod_mam_utils:maybe_set_client_xmlns(SetClientNs, Packet), |
652 |
2796 |
mod_mam_utils:wrap_message(MamNs, Packet1, QueryID, BExtMessID, TS, SrcJID). |
653 |
|
|
654 |
|
-spec message_row_to_ext_id(mod_mam:message_row()) -> binary(). |
655 |
|
message_row_to_ext_id(#{id := MessID}) -> |
656 |
1490 |
mod_mam_utils:mess_id_to_external_binary(MessID). |
657 |
|
|
658 |
|
handle_error_iq(HostType, Acc, _To, _Action, {error, _Reason, IQ}) -> |
659 |
56 |
mongoose_instrument:execute(mod_mam_pm_dropped_iq, #{host_type => HostType}, |
660 |
|
#{acc => Acc, count => 1}), |
661 |
56 |
{Acc, IQ}; |
662 |
|
handle_error_iq(_Host, Acc, _To, _Action, IQ) -> |
663 |
1433 |
{Acc, IQ}. |
664 |
|
|
665 |
|
-spec return_action_not_allowed_error_iq(jlib:iq()) -> jlib:iq(). |
666 |
|
return_action_not_allowed_error_iq(IQ) -> |
667 |
:-( |
ErrorEl = jlib:stanza_errort(<<"">>, <<"cancel">>, <<"not-allowed">>, |
668 |
|
<<"en">>, <<"The action is not allowed.">>), |
669 |
:-( |
IQ#iq{type = error, sub_el = [ErrorEl]}. |
670 |
|
|
671 |
|
-spec return_max_delay_reached_error_iq(jlib:iq()) -> jlib:iq(). |
672 |
|
return_max_delay_reached_error_iq(IQ) -> |
673 |
|
%% Message not found. |
674 |
:-( |
ErrorEl = mongoose_xmpp_errors:resource_constraint( |
675 |
|
<<"en">>, <<"The action is cancelled because of flooding.">>), |
676 |
:-( |
IQ#iq{type = error, sub_el = [ErrorEl]}. |
677 |
|
|
678 |
|
-spec return_error_iq(jlib:iq(), Reason :: term()) -> {error, term(), jlib:iq()}. |
679 |
|
return_error_iq(IQ, {Reason, {stacktrace, _Stacktrace}}) -> |
680 |
:-( |
return_error_iq(IQ, Reason); |
681 |
|
return_error_iq(IQ, timeout) -> |
682 |
:-( |
E = mongoose_xmpp_errors:service_unavailable(<<"en">>, <<"Timeout">>), |
683 |
:-( |
{error, timeout, IQ#iq{type = error, sub_el = [E]}}; |
684 |
|
return_error_iq(IQ, invalid_stanza_id) -> |
685 |
14 |
Text = mongoose_xmpp_errors:not_acceptable(<<"en">>, <<"Invalid stanza id provided">>), |
686 |
14 |
{error, invalid_stanza_id, IQ#iq{type = error, sub_el = [Text]}}; |
687 |
|
return_error_iq(IQ, item_not_found) -> |
688 |
35 |
{error, item_not_found, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:item_not_found()]}}; |
689 |
|
return_error_iq(IQ, not_implemented) -> |
690 |
:-( |
{error, not_implemented, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:feature_not_implemented()]}}; |
691 |
|
return_error_iq(IQ, Reason) -> |
692 |
7 |
{error, Reason, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:internal_server_error()]}}. |
693 |
|
|
694 |
|
return_message_form_iq(HostType, IQ) -> |
695 |
35 |
IQ#iq{type = result, sub_el = [mod_mam_utils:message_form(?MODULE, HostType, IQ#iq.xmlns)]}. |
696 |
|
|
697 |
|
report_issue({Reason, {stacktrace, Stacktrace}}, Issue, ArcJID, IQ) -> |
698 |
:-( |
report_issue(Reason, Stacktrace, Issue, ArcJID, IQ); |
699 |
|
report_issue(Reason, Issue, ArcJID, IQ) -> |
700 |
56 |
report_issue(Reason, [], Issue, ArcJID, IQ). |
701 |
|
|
702 |
|
report_issue(invalid_stanza_id, _Stacktrace, _Issue, _ArcJID, _IQ) -> |
703 |
:-( |
expected; |
704 |
|
report_issue(item_not_found, _Stacktrace, _Issue, _ArcJID, _IQ) -> |
705 |
35 |
expected; |
706 |
|
report_issue(not_implemented, _Stacktrace, _Issue, _ArcJID, _IQ) -> |
707 |
:-( |
expected; |
708 |
|
report_issue(timeout, _Stacktrace, _Issue, _ArcJID, _IQ) -> |
709 |
:-( |
expected; |
710 |
|
report_issue(Reason, Stacktrace, Issue, #jid{lserver=LServer, luser=LUser}, IQ) -> |
711 |
21 |
?LOG_ERROR(#{what => mam_error, |
712 |
|
issue => Issue, server => LServer, user => LUser, |
713 |
:-( |
reason => Reason, iq => IQ, stacktrace => Stacktrace}). |
714 |
|
|
715 |
|
-spec is_archivable_message(HostType :: host_type(), |
716 |
|
Dir :: incoming | outgoing, |
717 |
|
Packet :: exml:element()) -> boolean(). |
718 |
|
is_archivable_message(HostType, Dir, Packet) -> |
719 |
12223 |
M = mod_mam_params:is_archivable_message_module(?MODULE, HostType), |
720 |
12223 |
ArchiveChatMarkers = mod_mam_params:archive_chat_markers(?MODULE, HostType), |
721 |
12223 |
erlang:apply(M, is_archivable_message, [?MODULE, Dir, Packet, ArchiveChatMarkers]). |
722 |
|
|
723 |
|
-spec hooks(mongooseim:host_type()) -> gen_hook:hook_list(). |
724 |
|
hooks(HostType) -> |
725 |
272 |
[ |
726 |
|
{disco_local_features, HostType, fun ?MODULE:disco_local_features/3, #{}, 99}, |
727 |
|
{disco_sm_features, HostType, fun ?MODULE:disco_sm_features/3, #{}, 99}, |
728 |
|
{user_send_message, HostType, fun ?MODULE:user_send_message/3, #{}, 60}, |
729 |
|
{filter_local_packet, HostType, fun ?MODULE:filter_packet/3, #{}, 60}, |
730 |
|
{remove_user, HostType, fun ?MODULE:remove_user/3, #{}, 50}, |
731 |
|
{anonymous_purge_hook, HostType, fun ?MODULE:remove_user/3, #{}, 50}, |
732 |
|
{amp_determine_strategy, HostType, fun ?MODULE:determine_amp_strategy/3, #{}, 20}, |
733 |
|
{sm_filter_offline_message, HostType, fun ?MODULE:sm_filter_offline_message/3, #{}, 50}, |
734 |
|
{get_personal_data, HostType, fun ?MODULE:get_personal_data/3, #{}, 50} |
735 |
|
]. |
736 |
|
|
737 |
|
add_iq_handlers(HostType, Opts) -> |
738 |
136 |
Component = ejabberd_sm, |
739 |
|
%% `parallel' is the only one recommended here. |
740 |
136 |
ExecutionType = gen_mod:get_opt(iqdisc, Opts, parallel), |
741 |
136 |
IQHandlerFn = fun ?MODULE:process_mam_iq/5, |
742 |
136 |
Extra = #{}, |
743 |
136 |
[gen_iq_handler:add_iq_handler_for_domain(HostType, Namespace, |
744 |
|
Component, IQHandlerFn, |
745 |
|
Extra, ExecutionType) |
746 |
136 |
|| Namespace <- [?NS_MAM_04, ?NS_MAM_06]], |
747 |
136 |
ok. |
748 |
|
|
749 |
|
remove_iq_handlers(HostType) -> |
750 |
136 |
Component = ejabberd_sm, |
751 |
136 |
[gen_iq_handler:remove_iq_handler_for_domain(HostType, Namespace, Component) |
752 |
136 |
|| Namespace <- [?NS_MAM_04, ?NS_MAM_06]], |
753 |
136 |
ok. |
754 |
|
|
755 |
|
-spec instrumentation(host_type()) -> [mongoose_instrument:spec()]. |
756 |
|
instrumentation(HostType) -> |
757 |
273 |
[{mod_mam_pm_archive_message, #{host_type => HostType}, |
758 |
|
#{metrics => #{count => spiral, time => histogram}}}, |
759 |
|
{mod_mam_pm_lookup, #{host_type => HostType}, |
760 |
|
#{metrics => #{count => spiral, simple => spiral, size => histogram, time => histogram}}}, |
761 |
|
{mod_mam_pm_dropped_iq, #{host_type => HostType}, |
762 |
|
#{metrics => #{count => spiral}}}, |
763 |
|
{mod_mam_pm_dropped, #{host_type => HostType}, |
764 |
|
#{metrics => #{count => spiral}}}, |
765 |
|
{mod_mam_pm_remove_archive, #{host_type => HostType}, |
766 |
|
#{metrics => #{count => spiral}}}, |
767 |
|
{mod_mam_pm_get_prefs, #{host_type => HostType}, |
768 |
|
#{metrics => #{count => spiral}}}, |
769 |
|
{mod_mam_pm_set_prefs, #{host_type => HostType}, |
770 |
|
#{metrics => #{count => spiral}}}]. |