./ct_report/coverage/mongoose_client_api_messages.COVER.html

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