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)}]}]. |