./ct_report/coverage/mod_inbox_entries.COVER.html

1 -module(mod_inbox_entries).
2
3 -include("mongoose_ns.hrl").
4 -include("jlib.hrl").
5 -include("mod_inbox.hrl").
6
7 % Inbox extensions
8 -export([process_iq_conversation/5]).
9 -export([should_be_stored_in_inbox/1]).
10 -export([extensions_result/3]).
11
12 -spec process_iq_conversation(Acc :: mongoose_acc:t(),
13 From :: jid:jid(),
14 To :: jid:jid(),
15 IQ :: jlib:iq(),
16 Extra :: map()) ->
17 {mongoose_acc:t(), jlib:iq()}.
18 process_iq_conversation(Acc, From, _To, #iq{type = get, sub_el = SubEl} = IQ, _Extra) ->
19
:-(
process_iq_conversation_get(Acc, IQ, From, SubEl);
20 process_iq_conversation(Acc, From, _To, #iq{type = set,
21 sub_el = #xmlel{name = <<"reset">>} = ResetStanza} = IQ,
22 _Extra) ->
23
:-(
maybe_process_reset_stanza(Acc, From, IQ, ResetStanza);
24 process_iq_conversation(Acc, From, _To, #iq{type = set, sub_el = Query} = IQ, _Extra) ->
25
:-(
process_iq_conversation_set(Acc, IQ, From, Query).
26
27 -spec process_iq_conversation_get(mongoose_acc:t(), jlib:iq(), jid:jid(), exml:element()) ->
28 {mongoose_acc:t(), jlib:iq()}.
29 process_iq_conversation_get(Acc, IQ, From, SubEl) ->
30
:-(
case mod_inbox_utils:extract_attr_jid(SubEl) of
31 {error, _} ->
32
:-(
Form = build_inbox_entry_form(),
33
:-(
SubElWithForm = SubEl#xmlel{children = [Form]},
34
:-(
{Acc, IQ#iq{type = result, sub_el = SubElWithForm}};
35 EntryJID ->
36
:-(
get_properties_for_jid(Acc, IQ, From, EntryJID)
37 end.
38
39 -spec build_inbox_entry_form() -> exml:element().
40 build_inbox_entry_form() ->
41
:-(
#xmlel{name = <<"x">>,
42 attrs = [{<<"xmlns">>, ?NS_XDATA},
43 {<<"type">>, <<"form">>}],
44 children = [jlib:form_field({<<"FORM_TYPE">>, <<"hidden">>, ?NS_ESL_INBOX_CONVERSATION}),
45 jlib:form_field({<<"archive">>, <<"boolean">>, <<"false">>}),
46 jlib:form_field({<<"read">>, <<"boolean">>, <<"false">>}),
47 jlib:form_field({<<"mute">>, <<"text-single">>, <<"0">>})]}.
48
49 -spec get_properties_for_jid(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid()) ->
50 {mongoose_acc:t(), jlib:iq()}.
51 get_properties_for_jid(Acc, IQ, From, EntryJID) ->
52
:-(
HostType = mongoose_acc:host_type(Acc),
53
:-(
{_, _, BinEntryJID} = InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, EntryJID),
54
:-(
case mod_inbox_backend:get_entry_properties(HostType, InboxEntryKey) of
55
:-(
[] -> return_error(Acc, IQ, <<"Entry not found">>);
56 Result ->
57
:-(
CurrentTS = mongoose_acc:timestamp(Acc),
58
:-(
Properties = build_result(Result, CurrentTS),
59
:-(
X = [#xmlel{name = <<"query">>,
60 attrs = [{<<"xmlns">>, ?NS_ESL_INBOX_CONVERSATION},
61 {<<"jid">>, BinEntryJID}],
62 children = Properties}],
63
:-(
{Acc, IQ#iq{type = result, sub_el = X}}
64 end.
65
66 -spec process_iq_conversation_set(mongoose_acc:t(), jlib:iq(), jid:jid(), exml:element()) ->
67 {mongoose_acc:t(), jlib:iq()}.
68 process_iq_conversation_set(
69 Acc, #iq{id = IqId} = IQ, From, #xmlel{name = <<"query">>, children = Requests} = Query) ->
70
:-(
case mod_inbox_utils:extract_attr_jid(Query) of
71 {error, Msg} ->
72
:-(
return_error(Acc, IQ, Msg);
73 EntryJID ->
74
:-(
extract_requests(Acc, IQ, From, EntryJID, Requests, exml_query:attr(Query, <<"queryid">>, IqId))
75 end.
76
77 -spec extract_requests(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid(), [exml:element()], binary() | undefined) ->
78 {mongoose_acc:t(), jlib:iq()}.
79 extract_requests(Acc, IQ, From, EntryJID, Requests, QueryId) ->
80
:-(
CurrentTS = mongoose_acc:timestamp(Acc),
81
:-(
case form_to_query(CurrentTS, Requests, #{}) of
82 {error, Msg} ->
83
:-(
return_error(Acc, IQ, Msg);
84 Params ->
85
:-(
process_requests(Acc, IQ, From, EntryJID, CurrentTS, Params, QueryId)
86 end.
87
88 -spec process_requests(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid(), integer(), map(), binary() | undefined) ->
89 {mongoose_acc:t(), jlib:iq()}.
90 process_requests(Acc, IQ, From, EntryJID, CurrentTS, Params, QueryId) ->
91
:-(
HostType = mongoose_acc:host_type(Acc),
92
:-(
InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, EntryJID),
93
:-(
case mod_inbox_backend:set_entry_properties(HostType, InboxEntryKey, Params) of
94 {error, Msg} ->
95
:-(
return_error(Acc, IQ, Msg);
96 Result ->
97
:-(
forward_result(Acc, IQ, From, InboxEntryKey, Result, CurrentTS, QueryId)
98 end.
99
100 -spec forward_result(mongoose_acc:t(), jlib:iq(), jid:jid(), mod_inbox:entry_key(), entry_properties(), integer(), binary() | undefined) ->
101 {mongoose_acc:t(), jlib:iq()}.
102 forward_result(Acc, IQ, From, {_, _, ToBareJidBin}, Result, CurrentTS, QueryId) ->
103
:-(
Properties = build_result(Result, CurrentTS),
104
:-(
Children = prepare_children(ToBareJidBin, Properties, QueryId),
105
:-(
Msg = #xmlel{name = <<"message">>,
106 attrs = [{<<"id">>, IQ#iq.id}],
107 children = Children},
108
:-(
Acc1 = ejabberd_router:route(From, jid:to_bare(From), Acc, Msg),
109
:-(
Res = IQ#iq{type = result, sub_el = []},
110
:-(
{Acc1, Res}.
111
112 -spec prepare_children(binary(), [exml:element()], undefined | binary()) -> [exml:element()].
113 prepare_children(Jid, Properties, undefined) ->
114
:-(
[#xmlel{name = <<"x">>,
115 attrs = [{<<"xmlns">>, ?NS_ESL_INBOX_CONVERSATION},
116 {<<"jid">>, Jid}],
117 children = Properties}];
118 prepare_children(Jid, Properties, QueryId) ->
119
:-(
[#xmlel{name = <<"x">>,
120 attrs = [{<<"xmlns">>, ?NS_ESL_INBOX_CONVERSATION},
121 {<<"jid">>, Jid},
122 {<<"queryid">>, QueryId}],
123 children = Properties}].
124
125 maybe_process_reset_stanza(Acc, From, IQ, ResetStanza) ->
126
:-(
case mod_inbox_utils:extract_attr_jid(ResetStanza) of
127 {error, Msg} ->
128
:-(
{Acc, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:bad_request(<<"en">>, Msg)]}};
129 InterlocutorJID ->
130
:-(
process_reset_stanza(Acc, From, IQ, ResetStanza, InterlocutorJID)
131 end.
132
133 process_reset_stanza(Acc, From, IQ, _ResetStanza, InterlocutorJID) ->
134
:-(
HostType = mongoose_acc:host_type(Acc),
135
:-(
ok = mod_inbox_utils:reset_unread_count_to_zero(HostType, From, InterlocutorJID),
136
:-(
Res = IQ#iq{type = result,
137 sub_el = [#xmlel{name = <<"reset">>,
138 attrs = [{<<"xmlns">>, ?NS_ESL_INBOX_CONVERSATION}],
139 children = []}]},
140
:-(
{Acc, Res}.
141
142 %%--------------------------------------------------------------------
143 %% Helpers
144 %%--------------------------------------------------------------------
145
146 -spec build_result(entry_properties(), integer()) -> [exml:element()].
147 build_result(#{archive := Archived, unread_count := UnreadCount, muted_until := MutedUntil}, CurrentTS) ->
148
:-(
[
149 kv_to_el(<<"archive">>, mod_inbox_utils:bool_to_binary(Archived)),
150 kv_to_el(<<"read">>, mod_inbox_utils:bool_to_binary(0 =:= UnreadCount)),
151 kv_to_el(<<"mute">>, mod_inbox_utils:maybe_muted_until(MutedUntil, CurrentTS))
152 ].
153
154 -spec kv_to_el(binary(), binary()) -> exml:element().
155 kv_to_el(Key, Value) ->
156
:-(
#xmlel{name = Key, children = [#xmlcdata{content = Value}]}.
157
158 -spec return_error(mongoose_acc:t(), jlib:iq(), any()) ->
159 {mongoose_acc:t(), jlib:iq()}.
160 return_error(Acc, IQ, Msg) when is_binary(Msg) ->
161
:-(
{Acc, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:bad_request(<<"en">>, Msg)]}}.
162
163 -spec form_to_query(integer(), [exml:element()], map()) -> map() | {error, binary()}.
164 form_to_query(_, [], Acc) when map_size(Acc) == 0 ->
165
:-(
{error, <<"no-property">>};
166 form_to_query(_, [], Acc) ->
167
:-(
Acc;
168 form_to_query(TS, [#xmlel{name = <<"archive">>,
169 children = [#xmlcdata{content = <<"true">>}]} | Rest], Acc) ->
170
:-(
form_to_query(TS, Rest, Acc#{archive => true});
171 form_to_query(TS, [#xmlel{name = <<"archive">>,
172 children = [#xmlcdata{content = <<"false">>}]} | Rest], Acc) ->
173
:-(
form_to_query(TS, Rest, Acc#{archive => false});
174 form_to_query(TS, [#xmlel{name = <<"read">>,
175 children = [#xmlcdata{content = <<"true">>}]} | Rest], Acc) ->
176
:-(
form_to_query(TS, Rest, Acc#{unread_count => 0});
177 form_to_query(TS, [#xmlel{name = <<"read">>,
178 children = [#xmlcdata{content = <<"false">>}]} | Rest], Acc) ->
179
:-(
form_to_query(TS, Rest, Acc#{unread_count => 1});
180 form_to_query(TS, [#xmlel{name = <<"mute">>,
181 children = [#xmlcdata{content = Value}]} | Rest], Acc) ->
182
:-(
case mod_inbox_utils:maybe_binary_to_positive_integer(Value) of
183
:-(
{error, _} -> {error, <<"bad-request">>};
184 0 ->
185
:-(
form_to_query(TS, Rest, Acc#{muted_until => 0});
186 Num when Num > 0 ->
187
:-(
MutedUntilMicroSec = TS + erlang:convert_time_unit(Num, second, microsecond),
188
:-(
form_to_query(TS, Rest, Acc#{muted_until => MutedUntilMicroSec})
189 end;
190 form_to_query(_, _, _) ->
191
:-(
{error, <<"bad-request">>}.
192
193 -spec should_be_stored_in_inbox(exml:element()) -> boolean().
194 should_be_stored_in_inbox(Msg) ->
195
:-(
not is_inbox_update(Msg).
196
197 -spec is_inbox_update(exml:element()) -> boolean().
198 is_inbox_update(Msg) ->
199
:-(
case exml_query:subelement_with_ns(Msg, ?NS_ESL_INBOX_CONVERSATION, undefined) of
200
:-(
undefined -> false;
201
:-(
_ -> true
202 end.
203
204 -spec extensions_result(boolean(), integer(), integer()) -> [exml:element()].
205 extensions_result(Archive, MutedUntil, AccTS) ->
206
:-(
[#xmlel{name = <<"archive">>,
207 children = [#xmlcdata{content = mod_inbox_utils:bool_to_binary(Archive)}]},
208 #xmlel{name = <<"mute">>,
209 children = [#xmlcdata{content = mod_inbox_utils:maybe_muted_until(MutedUntil, AccTS)}]}].
Line Hits Source