./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 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 82 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.
Line Hits Source