./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 76 mongoose_client_api_messages_doc:trails().
30
31 init(Req, Opts) ->
32 16 mongoose_client_api:init(Req, Opts).
33
34 is_authorized(Req, State) ->
35 16 mongoose_client_api:is_authorized(Req, State).
36
37 content_types_provided(Req, State) ->
38 16 {[
39 {{<<"application">>, <<"json">>, '*'}, to_json}
40 ], Req, State}.
41
42 content_types_accepted(Req, State) ->
43 8 {[
44 {{<<"application">>, <<"json">>, '*'}, send_message}
45 ], Req, State}.
46
47 allowed_methods(Req, State) ->
48 16 {[<<"OPTIONS">>, <<"GET">>, <<"POST">>], Req, State}.
49
50 to_json(Req, #{jid := JID} = State) ->
51 8 With = cowboy_req:binding(with, Req),
52 8 WithJID = maybe_jid(With),
53 8 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 8 HostType = mongoose_credentials:host_type(Creds),
60 8 Now = os:system_time(microsecond),
61 8 ArchiveID = mod_mam:archive_id_int(HostType, JID),
62 8 QS = cowboy_req:parse_qs(Req),
63 8 PageSize = maybe_integer(proplists:get_value(<<"limit">>, QS, <<"50">>)),
64 8 Before = maybe_integer(proplists:get_value(<<"before">>, QS)),
65 8 End = maybe_before_to_us(Before, Now),
66 8 RSM = #rsm_in{direction = before, id = undefined},
67 8 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 8 {ok, {_, _, Msgs}} = R,
82 8 Resp = [make_json_msg(Msg, MAMId) || #{id := MAMId, packet := Msg} <- Msgs],
83 8 {jiffy:encode(Resp), Req, State}.
84
85 send_message(Req, #{user := RawUser, jid := FromJID, creds := Creds} = State) ->
86 8 {ok, Body, Req2} = cowboy_req:read_body(Req),
87 8 case mongoose_client_api:json_to_map(Body) of
88 {ok, #{<<"to">> := To, <<"body">> := MsgBody}} when is_binary(To), is_binary(MsgBody) ->
89 8 ToJID = jid:from_binary(To),
90 8 UUID = uuid:uuid_to_string(uuid:get_v4(), binary_standard),
91 8 XMLMsg0 = build_message(RawUser, To, UUID, MsgBody),
92 8 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 8 Acc1 = mongoose_hooks:rest_user_send_packet(Acc0, FromJID, ToJID, XMLMsg0),
99 8 XMLMsg1 = mongoose_acc:element(Acc1),
100 8 ejabberd_router:route(FromJID, ToJID, Acc1, XMLMsg1),
101 8 Resp = #{<<"id">> => UUID},
102 8 Req3 = cowboy_req:set_resp_body(jiffy:encode(Resp), Req2),
103 8 {true, Req3, State};
104 _ ->
105
:-(
mongoose_client_api:bad_request(Req2, State)
106 end.
107
108 build_message(From, To, Id, Body) ->
109 8 Attrs = [{<<"from">>, From},
110 {<<"to">>, To},
111 {<<"id">>, Id},
112 {<<"type">>, <<"chat">>}],
113 8 #xmlel{name = <<"message">>,
114 attrs = Attrs,
115 children = [#xmlel{name = <<"body">>,
116 children = [#xmlcdata{content = Body}]}]}.
117
118 make_json_msg(Msg, MAMId) ->
119 21 {Microsec, _} = mod_mam_utils:decode_compact_uuid(MAMId),
120 21 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 22 RawMsgProps = exml_query:subelement_with_name_and_ns(
127 Msg,
128 <<"properties">>,
129 <<"http://www.jivesoftware.com/xmlns/xmpp/properties">>),
130
131 22 BodyTag = exml_query:path(Msg, [{element, <<"body">>}]),
132 22 ExtensionList =
133 case RawMsgProps of
134 #xmlel{children = Children} ->
135 1 Props = [convert_prop_child(Child) || Child <- Children],
136 1 [{<<"properties">>, maps:from_list(Props)}];
137 _ ->
138 21 []
139 end,
140 22 Thread = exml_query:path(Msg, [{element, <<"thread">>}, cdata]),
141 22 ThreadParent = exml_query:path(Msg, [{element, <<"thread">>}, {attr, <<"parent">>}]),
142 22 ThreadAndThreadParentList = case {Thread, ThreadParent} of
143 {undefined, undefined} ->
144 22 [];
145 {Thread, undefined} ->
146
:-(
[{<<"thread">>, Thread}];
147 {Thread, ThreadParent} ->
148
:-(
[{<<"thread">>, Thread}, {<<"parent">>, ThreadParent}]
149 end,
150 22 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 22 maps:from_list(L).
156
157 convert_prop_child(Child)->
158 2 Name = exml_query:path(Child, [{element, <<"name">>}, cdata]),
159 2 Value = exml_query:path(Child, [{element, <<"value">>}, cdata]),
160 2 {Name, Value}.
161
162 maybe_jid(undefined) ->
163 1 undefined;
164 maybe_jid(JID) ->
165 7 jid:from_binary(JID).
166
167 maybe_integer(undefined) ->
168 6 undefined;
169 maybe_integer(Val) ->
170 18 binary_to_integer(Val).
171
172 maybe_before_to_us(undefined, Now) ->
173 6 Now;
174 maybe_before_to_us(Timestamp, _) ->
175 2 Timestamp * 1000.
Line Hits Source