1 |
|
-module(mongoose_client_api_messages). |
2 |
|
-behaviour(cowboy_rest). |
3 |
|
|
4 |
|
-export([trails/0]). |
5 |
|
|
6 |
|
-export([init/2]). |
7 |
|
-export([content_types_provided/2]). |
8 |
|
-export([content_types_accepted/2]). |
9 |
|
-export([is_authorized/2]). |
10 |
|
-export([allowed_methods/2]). |
11 |
|
|
12 |
|
-export([to_json/2]). |
13 |
|
-export([send_message/2]). |
14 |
|
|
15 |
|
-export([encode/2]). |
16 |
|
|
17 |
|
-export([maybe_integer/1]). |
18 |
|
-export([maybe_before_to_us/2]). |
19 |
|
|
20 |
|
-ignore_xref([send_message/2, to_json/2, trails/0, |
21 |
|
maybe_before_to_us/2]). |
22 |
|
|
23 |
|
-include("mongoose.hrl"). |
24 |
|
-include("jlib.hrl"). |
25 |
|
-include("mongoose_rsm.hrl"). |
26 |
|
-include_lib("exml/include/exml.hrl"). |
27 |
|
|
28 |
|
trails() -> |
29 |
80 |
mongoose_client_api_messages_doc:trails(). |
30 |
|
|
31 |
|
init(Req, Opts) -> |
32 |
2 |
mongoose_client_api:init(Req, Opts). |
33 |
|
|
34 |
|
is_authorized(Req, State) -> |
35 |
2 |
mongoose_client_api:is_authorized(Req, State). |
36 |
|
|
37 |
|
content_types_provided(Req, State) -> |
38 |
2 |
{[ |
39 |
|
{{<<"application">>, <<"json">>, '*'}, to_json} |
40 |
|
], Req, State}. |
41 |
|
|
42 |
|
content_types_accepted(Req, State) -> |
43 |
2 |
{[ |
44 |
|
{{<<"application">>, <<"json">>, '*'}, send_message} |
45 |
|
], Req, State}. |
46 |
|
|
47 |
|
allowed_methods(Req, State) -> |
48 |
2 |
{[<<"OPTIONS">>, <<"GET">>, <<"POST">>], Req, State}. |
49 |
|
|
50 |
|
to_json(Req, #{jid := JID} = State) -> |
51 |
:-( |
With = cowboy_req:binding(with, Req), |
52 |
:-( |
WithJID = maybe_jid(With), |
53 |
:-( |
maybe_to_json_with_jid(WithJID, JID, Req, State). |
54 |
|
|
55 |
|
maybe_to_json_with_jid(error, _, Req, State) -> |
56 |
:-( |
Req2 = cowboy_req:reply(404, Req), |
57 |
:-( |
{stop, Req2, State}; |
58 |
|
maybe_to_json_with_jid(WithJID, #jid{} = JID, Req, State = #{creds := Creds}) -> |
59 |
:-( |
HostType = mongoose_credentials:host_type(Creds), |
60 |
:-( |
Now = os:system_time(microsecond), |
61 |
:-( |
ArchiveID = mod_mam:archive_id_int(HostType, JID), |
62 |
:-( |
QS = cowboy_req:parse_qs(Req), |
63 |
:-( |
PageSize = maybe_integer(proplists:get_value(<<"limit">>, QS, <<"50">>)), |
64 |
:-( |
Before = maybe_integer(proplists:get_value(<<"before">>, QS)), |
65 |
:-( |
End = maybe_before_to_us(Before, Now), |
66 |
:-( |
RSM = #rsm_in{direction = before, id = undefined}, |
67 |
:-( |
R = mod_mam:lookup_messages(HostType, |
68 |
|
#{archive_id => ArchiveID, |
69 |
|
owner_jid => JID, |
70 |
|
rsm => RSM, |
71 |
|
borders => undefined, |
72 |
|
start_ts => undefined, |
73 |
|
end_ts => End, |
74 |
|
now => Now, |
75 |
|
with_jid => WithJID, |
76 |
|
search_text => undefined, |
77 |
|
page_size => PageSize, |
78 |
|
limit_passed => true, |
79 |
|
max_result_limit => 50, |
80 |
|
is_simple => true}), |
81 |
:-( |
{ok, {_, _, Msgs}} = R, |
82 |
:-( |
Resp = [make_json_msg(Msg, MAMId) || #{id := MAMId, packet := Msg} <- Msgs], |
83 |
:-( |
{jiffy:encode(Resp), Req, State}. |
84 |
|
|
85 |
|
send_message(Req, #{user := RawUser, jid := FromJID, creds := Creds} = State) -> |
86 |
2 |
{ok, Body, Req2} = cowboy_req:read_body(Req), |
87 |
2 |
case mongoose_client_api:json_to_map(Body) of |
88 |
|
{ok, #{<<"to">> := To, <<"body">> := MsgBody}} when is_binary(To), is_binary(MsgBody) -> |
89 |
2 |
ToJID = jid:from_binary(To), |
90 |
2 |
UUID = uuid:uuid_to_string(uuid:get_v4(), binary_standard), |
91 |
2 |
XMLMsg0 = build_message(RawUser, To, UUID, MsgBody), |
92 |
2 |
Acc0 = mongoose_acc:new(#{ location => ?LOCATION, |
93 |
|
host_type => mongoose_credentials:host_type(Creds), |
94 |
|
lserver => FromJID#jid.lserver, |
95 |
|
from_jid => FromJID, |
96 |
|
to_jid => ToJID, |
97 |
|
element => XMLMsg0 }), |
98 |
2 |
Acc1 = mongoose_hooks:rest_user_send_packet(Acc0, FromJID, ToJID, XMLMsg0), |
99 |
2 |
XMLMsg1 = mongoose_acc:element(Acc1), |
100 |
2 |
ejabberd_router:route(FromJID, ToJID, Acc1, XMLMsg1), |
101 |
2 |
Resp = #{<<"id">> => UUID}, |
102 |
2 |
Req3 = cowboy_req:set_resp_body(jiffy:encode(Resp), Req2), |
103 |
2 |
{true, Req3, State}; |
104 |
|
_ -> |
105 |
:-( |
mongoose_client_api:bad_request(Req2, State) |
106 |
|
end. |
107 |
|
|
108 |
|
build_message(From, To, Id, Body) -> |
109 |
2 |
Attrs = [{<<"from">>, From}, |
110 |
|
{<<"to">>, To}, |
111 |
|
{<<"id">>, Id}, |
112 |
|
{<<"type">>, <<"chat">>}], |
113 |
2 |
#xmlel{name = <<"message">>, |
114 |
|
attrs = Attrs, |
115 |
|
children = [#xmlel{name = <<"body">>, |
116 |
|
children = [#xmlcdata{content = Body}]}]}. |
117 |
|
|
118 |
|
make_json_msg(Msg, MAMId) -> |
119 |
:-( |
{Microsec, _} = mod_mam_utils:decode_compact_uuid(MAMId), |
120 |
:-( |
encode(Msg, Microsec div 1000). |
121 |
|
|
122 |
|
-spec encode(exml:item(), integer()) -> map(). |
123 |
|
encode(Msg, Timestamp) -> |
124 |
|
|
125 |
|
%Smack library specific query for properties. |
126 |
1 |
RawMsgProps = exml_query:subelement_with_name_and_ns( |
127 |
|
Msg, |
128 |
|
<<"properties">>, |
129 |
|
<<"http://www.jivesoftware.com/xmlns/xmpp/properties">>), |
130 |
|
|
131 |
1 |
BodyTag = exml_query:path(Msg, [{element, <<"body">>}]), |
132 |
1 |
ExtensionList = |
133 |
|
case RawMsgProps of |
134 |
|
#xmlel{children = Children} -> |
135 |
:-( |
Props = [convert_prop_child(Child) || Child <- Children], |
136 |
:-( |
[{<<"properties">>, maps:from_list(Props)}]; |
137 |
|
_ -> |
138 |
1 |
[] |
139 |
|
end, |
140 |
1 |
Thread = exml_query:path(Msg, [{element, <<"thread">>}, cdata]), |
141 |
1 |
ThreadParent = exml_query:path(Msg, [{element, <<"thread">>}, {attr, <<"parent">>}]), |
142 |
1 |
ThreadAndThreadParentList = case {Thread, ThreadParent} of |
143 |
|
{undefined, undefined} -> |
144 |
1 |
[]; |
145 |
|
{Thread, undefined} -> |
146 |
:-( |
[{<<"thread">>, Thread}]; |
147 |
|
{Thread, ThreadParent} -> |
148 |
:-( |
[{<<"thread">>, Thread}, {<<"parent">>, ThreadParent}] |
149 |
|
end, |
150 |
1 |
L = [{<<"from">>, exml_query:attr(Msg, <<"from">>)}, |
151 |
|
{<<"to">>, exml_query:attr(Msg, <<"to">>)}, |
152 |
|
{<<"id">>, exml_query:attr(Msg, <<"id">>)}, |
153 |
|
{<<"body">>, exml_query:cdata(BodyTag)}, |
154 |
|
{<<"timestamp">>, Timestamp} | ExtensionList] ++ ThreadAndThreadParentList, |
155 |
1 |
maps:from_list(L). |
156 |
|
|
157 |
|
convert_prop_child(Child)-> |
158 |
:-( |
Name = exml_query:path(Child, [{element, <<"name">>}, cdata]), |
159 |
:-( |
Value = exml_query:path(Child, [{element, <<"value">>}, cdata]), |
160 |
:-( |
{Name, Value}. |
161 |
|
|
162 |
|
maybe_jid(undefined) -> |
163 |
:-( |
undefined; |
164 |
|
maybe_jid(JID) -> |
165 |
:-( |
jid:from_binary(JID). |
166 |
|
|
167 |
|
maybe_integer(undefined) -> |
168 |
:-( |
undefined; |
169 |
|
maybe_integer(Val) -> |
170 |
:-( |
binary_to_integer(Val). |
171 |
|
|
172 |
|
maybe_before_to_us(undefined, Now) -> |
173 |
:-( |
Now; |
174 |
|
maybe_before_to_us(Timestamp, _) -> |
175 |
:-( |
Timestamp * 1000. |