1 |
|
-module(mongoose_client_api_contacts). |
2 |
|
-behaviour(cowboy_rest). |
3 |
|
|
4 |
|
-export([trails/0]). |
5 |
|
-export([init/2]). |
6 |
|
-export([content_types_provided/2]). |
7 |
|
-export([content_types_accepted/2]). |
8 |
|
-export([is_authorized/2]). |
9 |
|
-export([allowed_methods/2]). |
10 |
|
|
11 |
|
-export([forbidden_request/2]). |
12 |
|
|
13 |
|
-export([to_json/2]). |
14 |
|
-export([from_json/2]). |
15 |
|
-export([delete_resource/2]). |
16 |
|
|
17 |
|
-ignore_xref([from_json/2, to_json/2, trails/0, forbidden_request/2]). |
18 |
|
|
19 |
|
-include("mongoose.hrl"). |
20 |
|
-include("jlib.hrl"). |
21 |
|
-include_lib("exml/include/exml.hrl"). |
22 |
|
|
23 |
|
trails() -> |
24 |
80 |
mongoose_client_api_contacts_doc:trails(). |
25 |
|
|
26 |
|
init(Req, Opts) -> |
27 |
30 |
mongoose_client_api:init(Req, Opts). |
28 |
|
|
29 |
|
is_authorized(Req, State) -> |
30 |
30 |
mongoose_client_api:is_authorized(Req, State). |
31 |
|
|
32 |
|
content_types_provided(Req, State) -> |
33 |
28 |
{[ |
34 |
|
{{<<"application">>, <<"json">>, '*'}, to_json} |
35 |
|
], Req, State}. |
36 |
|
|
37 |
|
content_types_accepted(Req, State) -> |
38 |
13 |
{[ |
39 |
|
{{<<"application">>, <<"json">>, '*'}, from_json} |
40 |
|
], Req, State}. |
41 |
|
|
42 |
|
allowed_methods(Req, State) -> |
43 |
30 |
{[<<"OPTIONS">>, <<"GET">>, <<"POST">>, <<"PUT">>, <<"DELETE">>], |
44 |
|
Req, State}. |
45 |
|
|
46 |
|
forbidden_request(Req, State) -> |
47 |
:-( |
Req1 = cowboy_req:reply(403, Req), |
48 |
:-( |
{stop, Req1, State}. |
49 |
|
|
50 |
|
to_json(Req, #{jid := Caller} = State) -> |
51 |
12 |
CJid = jid:to_binary(Caller), |
52 |
12 |
Method = cowboy_req:method(Req), |
53 |
12 |
Jid = cowboy_req:binding(jid, Req), |
54 |
12 |
case Jid of |
55 |
|
undefined -> |
56 |
11 |
{ok, Res} = handle_request(Method, Jid, undefined, CJid, State), |
57 |
11 |
{jiffy:encode(lists:flatten([Res])), Req, State}; |
58 |
|
_ -> |
59 |
1 |
Req2 = cowboy_req:reply(404, Req), |
60 |
1 |
{stop, Req2, State} |
61 |
|
end. |
62 |
|
|
63 |
|
|
64 |
|
from_json(Req, #{jid := Caller} = State) -> |
65 |
13 |
CJid = jid:to_binary(Caller), |
66 |
13 |
Method = cowboy_req:method(Req), |
67 |
13 |
{ok, Body, Req1} = cowboy_req:read_body(Req), |
68 |
13 |
case mongoose_client_api:json_to_map(Body) of |
69 |
|
{ok, JSONData} -> |
70 |
13 |
Jid = case maps:get(<<"jid">>, JSONData, undefined) of |
71 |
4 |
undefined -> cowboy_req:binding(jid, Req1); |
72 |
9 |
J when is_binary(J) -> J; |
73 |
:-( |
_ -> undefined |
74 |
|
end, |
75 |
13 |
Action = maps:get(<<"action">>, JSONData, undefined), |
76 |
13 |
handle_request_and_respond(Method, Jid, Action, CJid, Req1, State); |
77 |
|
_ -> |
78 |
:-( |
mongoose_client_api:bad_request(Req1, State) |
79 |
|
end. |
80 |
|
|
81 |
|
%% @doc Called for a method of type "DELETE" |
82 |
|
delete_resource(Req, #{jid := Caller} = State) -> |
83 |
3 |
CJid = jid:to_binary(Caller), |
84 |
3 |
Jid = cowboy_req:binding(jid, Req), |
85 |
3 |
case Jid of |
86 |
|
undefined -> |
87 |
2 |
handle_multiple_deletion(CJid, get_requested_contacts(Req), Req, State); |
88 |
|
_ -> |
89 |
1 |
handle_single_deletion(CJid, Jid, Req, State) |
90 |
|
end. |
91 |
|
|
92 |
|
handle_multiple_deletion(_, undefined, Req, State) -> |
93 |
:-( |
mongoose_client_api:bad_request(Req, State); |
94 |
|
handle_multiple_deletion(CJid, ToDelete, Req, State) -> |
95 |
2 |
case handle_request(<<"DELETE">>, ToDelete, undefined, CJid, State) of |
96 |
|
{ok, NotDeleted} -> |
97 |
2 |
RespBody = #{not_deleted => NotDeleted}, |
98 |
2 |
Req2 = cowboy_req:set_resp_body(jiffy:encode(RespBody), Req), |
99 |
2 |
Req3 = cowboy_req:set_resp_header(<<"content-type">>, <<"application/json">>, Req2), |
100 |
2 |
{true, Req3, State}; |
101 |
|
Other -> |
102 |
:-( |
serve_failure(Other, Req, State) |
103 |
|
end. |
104 |
|
|
105 |
|
handle_single_deletion(_, undefined, Req, State) -> |
106 |
:-( |
mongoose_client_api:bad_request(Req, State); |
107 |
|
handle_single_deletion(CJid, ToDelete, Req, State) -> |
108 |
1 |
case handle_request(<<"DELETE">>, ToDelete, undefined, CJid, State) of |
109 |
|
ok -> |
110 |
1 |
{true, Req, State}; |
111 |
|
Other -> |
112 |
:-( |
serve_failure(Other, Req, State) |
113 |
|
end. |
114 |
|
|
115 |
|
handle_request_and_respond(_, undefined, _, _, Req, State) -> |
116 |
:-( |
mongoose_client_api:bad_request(Req, State); |
117 |
|
handle_request_and_respond(Method, Jid, Action, CJid, Req, State) -> |
118 |
13 |
case handle_request(Method, to_binary(Jid), Action, CJid, State) of |
119 |
|
ok -> |
120 |
11 |
{true, Req, State}; |
121 |
|
not_implemented -> |
122 |
1 |
Req2 = cowboy_req:reply(501, Req), |
123 |
1 |
{stop, Req2, State}; |
124 |
|
not_found -> |
125 |
1 |
Req2 = cowboy_req:reply(404, Req), |
126 |
1 |
{stop, Req2, State} |
127 |
|
end. |
128 |
|
|
129 |
|
serve_failure(not_implemented, Req, State) -> |
130 |
:-( |
Req2 = cowboy_req:reply(501, Req), |
131 |
:-( |
{stop, Req2, State}; |
132 |
|
serve_failure(not_found, Req, State) -> |
133 |
:-( |
Req2 = cowboy_req:reply(404, Req), |
134 |
:-( |
{stop, Req2, State}; |
135 |
|
serve_failure({error, ErrorType, Msg}, Req, State) -> |
136 |
:-( |
?LOG_ERROR(#{what => api_contacts_error, |
137 |
|
text => <<"Error while serving http request. Return code 500">>, |
138 |
:-( |
error_type => ErrorType, msg => Msg, req => Req}), |
139 |
:-( |
Req2 = cowboy_req:reply(500, Req), |
140 |
:-( |
{stop, Req2, State}. |
141 |
|
|
142 |
|
get_requested_contacts(Req) -> |
143 |
2 |
Body = get_whole_body(Req, <<"">>), |
144 |
2 |
case mongoose_client_api:json_to_map(Body) of |
145 |
|
{ok, #{<<"to_delete">> := ResultJids}} when is_list(ResultJids) -> |
146 |
2 |
case [X || X <- ResultJids, is_binary(X)] =:= ResultJids of |
147 |
|
true -> |
148 |
2 |
ResultJids; |
149 |
|
false -> |
150 |
:-( |
undefined |
151 |
|
end; |
152 |
|
_ -> |
153 |
:-( |
undefined |
154 |
|
end. |
155 |
|
|
156 |
|
get_whole_body(Req, Acc) -> |
157 |
2 |
case cowboy_req:read_body(Req) of |
158 |
|
{ok, Data, _Req2} -> |
159 |
2 |
<<Data/binary, Acc/binary>>; |
160 |
|
{more, Data, Req2} -> |
161 |
:-( |
get_whole_body(Req2, <<Data/binary, Acc/binary>>) |
162 |
|
end. |
163 |
|
|
164 |
|
handle_request(<<"GET">>, undefined, undefined, CJid, _State) -> |
165 |
11 |
mongoose_commands:execute(CJid, list_contacts, #{caller => CJid}); |
166 |
|
handle_request(<<"POST">>, Jid, undefined, CJid, _State) -> |
167 |
9 |
mongoose_commands:execute(CJid, add_contact, #{caller => CJid, |
168 |
|
jid => Jid}); |
169 |
|
handle_request(<<"DELETE">>, Jids, _Action, CJid, _State) when is_list(Jids) -> |
170 |
2 |
mongoose_commands:execute(CJid, delete_contacts, #{caller => CJid, |
171 |
|
jids => Jids}); |
172 |
|
handle_request(Method, Jid, Action, CJid, #{jid := CallerJid, creds := Creds}) -> |
173 |
5 |
HostType = mongoose_credentials:host_type(Creds), |
174 |
5 |
case contact_exists(HostType, CallerJid, jid:from_binary(Jid)) of |
175 |
|
true -> |
176 |
4 |
handle_contact_request(Method, Jid, Action, CJid); |
177 |
1 |
false -> not_found |
178 |
|
end. |
179 |
|
|
180 |
|
handle_contact_request(<<"PUT">>, Jid, <<"invite">>, CJid) -> |
181 |
1 |
mongoose_commands:execute(CJid, subscription, #{caller => CJid, |
182 |
|
jid => Jid, action => atom_to_binary(subscribe, latin1)}); |
183 |
|
handle_contact_request(<<"PUT">>, Jid, <<"accept">>, CJid) -> |
184 |
1 |
mongoose_commands:execute(CJid, subscription, #{caller => CJid, |
185 |
|
jid => Jid, action => atom_to_binary(subscribed, latin1)}); |
186 |
|
handle_contact_request(<<"DELETE">>, Jid, undefined, CJid) -> |
187 |
1 |
mongoose_commands:execute(CJid, delete_contact, #{caller => CJid, |
188 |
|
jid => Jid}); |
189 |
|
handle_contact_request(_, _, _, _) -> |
190 |
1 |
not_implemented. |
191 |
|
|
192 |
|
to_binary(S) when is_binary(S) -> |
193 |
13 |
S; |
194 |
|
to_binary(S) -> |
195 |
:-( |
list_to_binary(S). |
196 |
|
|
197 |
|
-spec contact_exists(mongooseim:host_type(), jid:jid(), jid:jid() | error) -> boolean(). |
198 |
:-( |
contact_exists(_, _, error) -> false; |
199 |
|
contact_exists(HostType, CallerJid, Jid) -> |
200 |
5 |
LJid = jid:to_lower(Jid), |
201 |
5 |
Res = mod_roster:get_roster_entry(HostType, CallerJid, LJid, short), |
202 |
5 |
Res =/= does_not_exist andalso Res =/= error. |