./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 536 ResetMarkers = get_reset_markers(HostType),
54 536 case if_chat_marker_get_id(Packet, ResetMarkers) of
55 undefined ->
56 349 ok;
57 Id ->
58 187 TS = mongoose_acc:timestamp(Acc),
59 187 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 187 InboxEntryKey = build_inbox_entry_key(From, Remote),
76 187 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 341 MsgId = get_msg_id(Packet),
85 341 Timestamp = mongoose_acc:timestamp(Acc),
86 %% no unread for a user because he writes new messages which assumes he read all previous messages.
87 341 Count = 0,
88 341 InboxEntryKey = build_inbox_entry_key(Sender, Receiver),
89 341 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 604 MsgId = get_msg_id(Packet),
98 604 Timestamp = mongoose_acc:timestamp(Acc),
99 604 InboxEntryKey = build_inbox_entry_key(Receiver, Sender),
100 604 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 740 LUser = jid:nodeprep(User),
108 740 LServer = jid:nameprep(Server),
109 740 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 536 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 536 Ids = [if_chat_marker_get_id(Packet, M) || M <- Markers],
122 536 Filtered = [El || El <- Ids, El /= undefined],
123 536 case Filtered of
124 [] ->
125 349 undefined;
126 [H | _] ->
127 187 H
128 end;
129 if_chat_marker_get_id(Packet, Marker) ->
130 536 case exml_query:paths(Packet, [{element, Marker}, {attr, <<"id">>}]) of
131 [Id] ->
132 187 Id;
133 _ ->
134 349 undefined
135 end.
136
137
138 -spec has_chat_marker(Packet :: exml:element()) -> boolean().
139 has_chat_marker(Packet) ->
140 1641 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 664 case has_chat_marker(Packet) of
153 true ->
154 88 ok;
155 false ->
156 576 Packet2 = fill_from_attr(Packet, User),
157 576 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 945 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 576 case exml_query:attr(Msg, <<"from">>, undefined) of
167 undefined ->
168 536 FromBin = jid:to_binary(From),
169 536 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 249 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 1770 BinInt = integer_to_binary(Int),
205 1770 EncodedJid = base64:encode(BinJid),
206 1770 <<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 34 binary_to_bool(<<"true">>) -> true;
224 1091 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 504 bool_to_binary(true) -> <<"true">>;
231 567 bool_to_binary(false) -> <<"false">>;
232
:-(
bool_to_binary(_) -> error.
233
234 build_inbox_entry_key(FromJid, ToJid) ->
235 1421 {LUser, LServer} = jid:to_lus(FromJid),
236 1421 ToBareJid = jid:nameprep(jid:to_bare_binary(ToJid)),
237 1421 {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 975 [ #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 4284 #xmlel{name = Key, children = [#xmlcdata{content = Value}]}.
262
263 -spec is_archive(binary()) -> binary().
264 88 is_archive(<<"archive">>) -> <<"true">>;
265 983 is_archive(_) -> <<"false">>.
266
267 -spec maybe_muted_until(integer(), integer()) -> binary().
268 1022 maybe_muted_until(0, _) -> <<"0">>;
269 maybe_muted_until(MutedUntil, CurrentTS) ->
270 49 case CurrentTS =< MutedUntil of
271 47 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 975 TS = calendar:system_time_to_rfc3339(Timestamp, [{offset, "Z"}, {unit, microsecond}]),
278 975 jlib:timestamp_to_xml(TS, undefined, undefined).
279
280 all_valid_boxes_for_query(HostType) ->
281 194 [<<"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 34 DaysInMicroSeconds = 86400000000 * Days, % 8.64e+10 microseconds in a day
286 34 Now - DaysInMicroSeconds.
Line Hits Source