./ct_report/coverage/mod_inbox_utils.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @copyright (C) 2018, Erlang-Solutions
3 %%% @doc
4 %%%
5 %%% @end
6 %%% Created : 30. Jan 2018 13:22
7 %%%-------------------------------------------------------------------
8 -module(mod_inbox_utils).
9
10 -include("mod_inbox.hrl").
11 -include("jlib.hrl").
12
13 -type inbox_fun() :: fun((mongooseim:host_type(),
14 jid:jid(),
15 jid:jid(),
16 exml:element(),
17 mongoose_acc:t()) -> mod_inbox:count_res()).
18
19 %%%%%%%%%%%%%%%%%%%
20 %% DB Operations shared by mod_inbox_one2one and mod_inbox_muclight
21 -export([maybe_reset_unread_count/5,
22 reset_unread_count_to_zero/3,
23 maybe_write_to_inbox/6,
24 write_to_sender_inbox/5,
25 write_to_receiver_inbox/5,
26 clear_inbox/3,
27 get_reset_markers/1,
28 if_chat_marker_get_id/2,
29 has_chat_marker/1,
30 get_option_write_aff_changes/1,
31 get_option_remove_on_kicked/1,
32 extract_attr_jid/1,
33 maybe_binary_to_positive_integer/1,
34 encode_rsm_id/2,
35 decode_rsm_id/1,
36 binary_to_bool/1,
37 bool_to_binary/1,
38 build_inbox_entry_key/2,
39 build_inbox_result_elements/2,
40 build_entry_result_elements/2,
41 all_valid_boxes_for_query/1,
42 calculate_ts_from/2
43 ]).
44
45 -ignore_xref([get_reset_markers/1, if_chat_marker_get_id/2]).
46
47 -spec maybe_reset_unread_count(HostType :: mongooseim:host_type(),
48 User :: jid:jid(),
49 Remote :: jid:jid(),
50 Packet :: exml:element(),
51 Acc :: mongoose_acc:t()) -> ok.
52 maybe_reset_unread_count(HostType, User, Remote, Packet, Acc) ->
53 481 ResetMarkers = get_reset_markers(HostType),
54 481 case if_chat_marker_get_id(Packet, ResetMarkers) of
55 undefined ->
56 298 ok;
57 Id ->
58 183 TS = mongoose_acc:timestamp(Acc),
59 183 reset_unread_count(HostType, User, Remote, Id, TS)
60 end.
61
62 -spec reset_unread_count_to_zero(mongoose_acc:t(), jid:jid(), jid:jid()) -> ok.
63 reset_unread_count_to_zero(Acc, From, Remote) ->
64 6 TS = mongoose_acc:timestamp(Acc),
65 6 HostType = mongoose_acc:host_type(Acc),
66 6 InboxEntryKey = build_inbox_entry_key(From, Remote),
67 6 ok = mod_inbox_backend:reset_unread(HostType, InboxEntryKey, undefined, TS).
68
69 -spec reset_unread_count(HostType ::mongooseim:host_type(),
70 From :: jid:jid(),
71 Remote :: jid:jid(),
72 MsgId :: id(),
73 TS :: integer()) -> ok.
74 reset_unread_count(HostType, From, Remote, MsgId, TS) ->
75 183 InboxEntryKey = build_inbox_entry_key(From, Remote),
76 183 ok = mod_inbox_backend:reset_unread(HostType, InboxEntryKey, MsgId, TS).
77
78 -spec write_to_sender_inbox(HostType :: mongooseim:host_type(),
79 Sender :: jid:jid(),
80 Receiver :: jid:jid(),
81 Packet :: exml:element(),
82 Acc :: mongoose_acc:t()) -> ok.
83 write_to_sender_inbox(HostType, Sender, Receiver, Packet, Acc) ->
84 290 MsgId = get_msg_id(Packet),
85 290 Timestamp = mongoose_acc:timestamp(Acc),
86 %% no unread for a user because he writes new messages which assumes he read all previous messages.
87 290 Count = 0,
88 290 InboxEntryKey = build_inbox_entry_key(Sender, Receiver),
89 290 mod_inbox_backend:set_inbox(HostType, InboxEntryKey, Packet, Count, MsgId, Timestamp).
90
91 -spec write_to_receiver_inbox(HostType :: mongooseim:host_type(),
92 Sender :: jid:jid(),
93 Receiver :: jid:jid(),
94 Packet :: exml:element(),
95 Acc :: mongoose_acc:t()) -> ok | {ok, integer()}.
96 write_to_receiver_inbox(HostType, Sender, Receiver, Packet, Acc) ->
97 553 MsgId = get_msg_id(Packet),
98 553 Timestamp = mongoose_acc:timestamp(Acc),
99 553 InboxEntryKey = build_inbox_entry_key(Receiver, Sender),
100 553 mod_inbox_backend:set_inbox_incr_unread(HostType, InboxEntryKey,
101 Packet, MsgId, Timestamp).
102
103 -spec clear_inbox(HostType :: mongooseim:host_type(),
104 User :: jid:user(),
105 Server :: jid:server()) -> mod_inbox:write_res().
106 clear_inbox(HostType, User, Server) when is_binary(User) ->
107 718 LUser = jid:nodeprep(User),
108 718 LServer = jid:nameprep(Server),
109 718 ok = mod_inbox_backend:clear_inbox(HostType, LUser, LServer).
110
111 %%%%%%%%%%%%%%%%%%%
112 %% Helpers
113
114 -spec get_reset_markers(HostType :: mongooseim:host_type()) -> list(marker()).
115 get_reset_markers(HostType) ->
116 481 gen_mod:get_module_opt(HostType, mod_inbox, reset_markers).
117
118 -spec if_chat_marker_get_id(Packet :: exml:element(),
119 Markers :: list(marker())) -> undefined | id().
120 if_chat_marker_get_id(Packet, Markers) when is_list(Markers) ->
121 481 Ids = [if_chat_marker_get_id(Packet, M) || M <- Markers],
122 481 Filtered = [El || El <- Ids, El /= undefined],
123 481 case Filtered of
124 [] ->
125 298 undefined;
126 [H | _] ->
127 183 H
128 end;
129 if_chat_marker_get_id(Packet, Marker) ->
130 481 case exml_query:paths(Packet, [{element, Marker}, {attr, <<"id">>}]) of
131 [Id] ->
132 183 Id;
133 _ ->
134 298 undefined
135 end.
136
137
138 -spec has_chat_marker(Packet :: exml:element()) -> boolean().
139 has_chat_marker(Packet) ->
140 1493 mongoose_chat_markers:has_chat_markers(Packet).
141
142 -spec maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) ->
143 mod_inbox:count_res() when
144 HostType ::mongooseim:host_type(),
145 User :: jid:jid(),
146 Remote :: jid:jid(),
147 Packet :: exml:element(),
148 Acc :: mongoose_acc:t(),
149 %% WriteF is write_to_receiver_inbox/5 or write_to_sender_inbox/5
150 WriteF :: inbox_fun().
151 maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) ->
152 594 case has_chat_marker(Packet) of
153 true ->
154 80 ok;
155 false ->
156 514 Packet2 = fill_from_attr(Packet, User),
157 514 WriteF(HostType, User, Remote, Packet2, Acc)
158 end.
159
160 -spec get_msg_id(Msg :: exml:element()) -> binary().
161 get_msg_id(#xmlel{name = <<"message">>} = Msg) ->
162 843 exml_query:attr(Msg, <<"id">>, <<>>).
163
164 -spec fill_from_attr(Msg :: exml:element(), From :: jid:jid()) -> exml:element().
165 fill_from_attr(Msg = #xmlel{attrs = Attrs}, From) ->
166 514 case exml_query:attr(Msg, <<"from">>, undefined) of
167 undefined ->
168 474 FromBin = jid:to_binary(From),
169 474 Msg#xmlel{attrs = [{<<"from">>, FromBin} | Attrs]};
170 _ ->
171 40 Msg
172 end.
173
174 -spec get_option_write_aff_changes(HostType :: mongooseim:host_type()) -> boolean().
175 get_option_write_aff_changes(HostType) ->
176 231 gen_mod:get_module_opt(HostType, mod_inbox, aff_changes).
177
178 -spec get_option_remove_on_kicked(HostType :: mongooseim:host_type()) -> boolean().
179 get_option_remove_on_kicked(HostType) ->
180 61 gen_mod:get_module_opt(HostType, mod_inbox, remove_on_kicked).
181
182 extract_attr_jid(ResetStanza) ->
183 134 case exml_query:attr(ResetStanza, <<"jid">>) of
184 undefined ->
185 6 {error, <<"jid-required">>};
186 Value ->
187 128 case jid:from_binary(Value) of
188 error ->
189 4 {error, <<"invalid-jid">>};
190 124 JID -> JID
191 end
192 end.
193
194 -spec maybe_binary_to_positive_integer(binary()) -> non_neg_integer() | {error, atom()}.
195 maybe_binary_to_positive_integer(Bin) ->
196 30 try erlang:binary_to_integer(Bin) of
197 24 N when N >= 0 -> N;
198 4 _ -> {error, non_positive_integer}
199 2 catch error:badarg -> {error, 'NaN'}
200 end.
201
202 -spec encode_rsm_id(integer(), binary()) -> binary().
203 encode_rsm_id(Int, BinJid) ->
204 1762 BinInt = integer_to_binary(Int),
205 1762 EncodedJid = base64:encode(BinJid),
206 1762 <<BinInt/binary, "/", EncodedJid/binary>>.
207
208 -spec decode_rsm_id(binary()) -> {integer(), binary()} | error.
209 decode_rsm_id(Bin) ->
210 8 case binary:split(Bin, <<"/">>) of
211 [BinInt, BinJid] ->
212 6 Int = maybe_binary_to_positive_integer(BinInt),
213 6 case Int of
214 Int when is_integer(Int) ->
215 4 Jid = base64:decode(BinJid),
216 4 {Int, Jid};
217 2 _ -> error
218 end;
219 2 _ -> error
220 end.
221
222 -spec binary_to_bool(binary()) -> true | false | error.
223 33 binary_to_bool(<<"true">>) -> true;
224 1104 binary_to_bool(<<"false">>) -> false;
225 4 binary_to_bool(_) -> error.
226
227 -spec bool_to_binary(integer() | boolean()) -> binary() | error.
228
:-(
bool_to_binary(1) -> <<"true">>;
229
:-(
bool_to_binary(0) -> <<"false">>;
230 498 bool_to_binary(true) -> <<"true">>;
231 562 bool_to_binary(false) -> <<"false">>;
232
:-(
bool_to_binary(_) -> error.
233
234 build_inbox_entry_key(FromJid, ToJid) ->
235 1195 {LUser, LServer} = jid:to_lus(FromJid),
236 1195 ToBareJid = jid:nameprep(jid:to_bare_binary(ToJid)),
237 1195 {LUser, LServer, ToBareJid}.
238
239 -spec build_inbox_result_elements(inbox_res(), integer()) -> [exml:element()].
240 build_inbox_result_elements(#{msg := Content, timestamp := Timestamp, unread_count := UnreadCount,
241 box := Box, muted_until := MutedUntil,
242 extra := Extra}, AccTS) ->
243 964 [ #xmlel{name = <<"forwarded">>, attrs = [{<<"xmlns">>, ?NS_FORWARD}],
244 children = [build_delay_el(Timestamp), Content]},
245 kv_to_el(<<"read">>, mod_inbox_utils:bool_to_binary(0 =:= UnreadCount)),
246 kv_to_el(<<"box">>, Box),
247 kv_to_el(<<"archive">>, is_archive(Box)),
248 kv_to_el(<<"mute">>, maybe_muted_until(MutedUntil, AccTS))
249 | Extra ].
250
251 -spec build_entry_result_elements(entry_properties(), integer()) -> [exml:element()].
252 build_entry_result_elements(#{box := Box, muted_until := MutedUntil,
253 unread_count := UnreadCount, extra := Extra}, AccTS) ->
254 96 [ kv_to_el(<<"read">>, mod_inbox_utils:bool_to_binary(0 =:= UnreadCount)),
255 kv_to_el(<<"box">>, Box), kv_to_el(<<"archive">>, is_archive(Box)),
256 kv_to_el(<<"mute">>, maybe_muted_until(MutedUntil, AccTS))
257 | Extra ].
258
259 -spec kv_to_el(binary(), binary()) -> exml:element().
260 kv_to_el(Key, Value) ->
261 4240 #xmlel{name = Key, children = [#xmlcdata{content = Value}]}.
262
263 -spec is_archive(binary()) -> binary().
264 89 is_archive(<<"archive">>) -> <<"true">>;
265 971 is_archive(_) -> <<"false">>.
266
267 -spec maybe_muted_until(integer(), integer()) -> binary().
268 1012 maybe_muted_until(0, _) -> <<"0">>;
269 maybe_muted_until(MutedUntil, CurrentTS) ->
270 48 case CurrentTS =< MutedUntil of
271 46 true -> list_to_binary(calendar:system_time_to_rfc3339(MutedUntil, [{offset, "Z"}, {unit, microsecond}]));
272 2 false -> <<"0">>
273 end.
274
275 -spec build_delay_el(Timestamp :: integer()) -> exml:element().
276 build_delay_el(Timestamp) ->
277 964 TS = calendar:system_time_to_rfc3339(Timestamp, [{offset, "Z"}, {unit, microsecond}]),
278 964 jlib:timestamp_to_xml(TS, undefined, undefined).
279
280 all_valid_boxes_for_query(HostType) ->
281 195 [<<"all">> | gen_mod:get_module_opt(HostType, mod_inbox, boxes)].
282
283 -spec calculate_ts_from(integer(), non_neg_integer()) -> integer().
284 calculate_ts_from(Now, Days) ->
285 25 DaysInMicroSeconds = 86400000000 * Days, % 8.64e+10 microseconds in a day
286 25 Now - DaysInMicroSeconds.
Line Hits Source