1 |
|
-module(mongoose_stanza_helper). |
2 |
|
-export([build_message/3]). |
3 |
|
-export([build_message_with_headline/3]). |
4 |
|
-export([parse_from_to/2]). |
5 |
|
-export([get_last_messages/4]). |
6 |
|
-export([route/4]). |
7 |
|
|
8 |
|
-include("jlib.hrl"). |
9 |
|
-include("mongoose_logger.hrl"). |
10 |
|
|
11 |
|
-spec build_message(From :: binary(), To :: binary(), Body :: binary()) -> exml:element(). |
12 |
|
build_message(From, To, Body) -> |
13 |
17 |
#xmlel{name = <<"message">>, |
14 |
|
attrs = [{<<"type">>, <<"chat">>}, |
15 |
|
{<<"id">>, mongoose_bin:gen_from_crypto()}, |
16 |
|
{<<"from">>, From}, |
17 |
|
{<<"to">>, To}], |
18 |
|
children = [#xmlel{name = <<"body">>, |
19 |
|
children = [#xmlcdata{content = Body}]}] |
20 |
|
}. |
21 |
|
|
22 |
|
build_message_with_headline(FromBin, ToBin, |
23 |
|
#{<<"body">> := Body, <<"subject">> := Subject}) -> |
24 |
2 |
Children = maybe_cdata_elem(<<"subject">>, Subject) ++ |
25 |
|
maybe_cdata_elem(<<"body">>, Body), |
26 |
2 |
Attrs = [{<<"type">>, <<"headline">>}, |
27 |
|
{<<"id">>, mongoose_bin:gen_from_crypto()}, |
28 |
|
{<<"from">>, FromBin}, |
29 |
|
{<<"to">>, ToBin}], |
30 |
2 |
#xmlel{name = <<"message">>, attrs = Attrs, children = Children}. |
31 |
|
|
32 |
:-( |
maybe_cdata_elem(_, null) -> []; |
33 |
:-( |
maybe_cdata_elem(_, <<>>) -> []; |
34 |
|
maybe_cdata_elem(Name, Text) when is_binary(Text) -> |
35 |
4 |
[cdata_elem(Name, Text)]. |
36 |
|
|
37 |
|
cdata_elem(Name, Text) when is_binary(Name), is_binary(Text) -> |
38 |
4 |
#xmlel{name = Name, children = [#xmlcdata{content = Text}]}. |
39 |
|
|
40 |
|
-spec parse_from_to(jid:jid() | binary() | undefined, jid:jid() | binary() | undefined) -> |
41 |
|
{ok, jid:jid(), jid:jid()} | {error, missing} | {error, type_error, string()}. |
42 |
|
parse_from_to(F, T) when F == undefined; T == undefined -> |
43 |
1 |
{error, missing}; |
44 |
|
parse_from_to(F, T) -> |
45 |
53 |
case parse_jid_list([F, T]) of |
46 |
41 |
{ok, [Fjid, Tjid]} -> {ok, Fjid, Tjid}; |
47 |
12 |
E -> E |
48 |
|
end. |
49 |
|
|
50 |
|
-spec parse_jid_list(BinJids :: [binary()]) -> {ok, [jid:jid()]} | {error, type_error, string()}. |
51 |
|
parse_jid_list([_ | _] = BinJids) -> |
52 |
53 |
Jids = lists:map(fun parse_jid/1, BinJids), |
53 |
53 |
case [Msg || {error, Msg} <- Jids] of |
54 |
41 |
[] -> {ok, Jids}; |
55 |
12 |
Errors -> {error, type_error, lists:join("; ", Errors)} |
56 |
|
end. |
57 |
|
|
58 |
|
-spec parse_jid(binary() | jid:jid()) -> jid:jid() | {error, string()}. |
59 |
|
parse_jid(#jid{} = Jid) -> |
60 |
:-( |
Jid; |
61 |
|
parse_jid(Jid) when is_binary(Jid) -> |
62 |
106 |
case jid:from_binary(Jid) of |
63 |
12 |
error -> {error, io_lib:format("Invalid jid: ~p", [Jid])}; |
64 |
94 |
B -> B |
65 |
|
end. |
66 |
|
|
67 |
|
-spec get_last_messages(Caller :: jid:jid(), |
68 |
|
Limit :: non_neg_integer(), |
69 |
|
With :: null | jid:jid(), |
70 |
|
Before :: null | mod_mam:unix_timestamp()) -> {ok, map()}. |
71 |
|
get_last_messages(Caller, Limit, With, Before) -> |
72 |
9 |
With2 = null_as_undefined(With), |
73 |
9 |
Before2 = null_as_undefined(Before), %% Before is in microseconds |
74 |
9 |
Limit2 = min(500, Limit), |
75 |
9 |
Rows = mongoose_stanza_api:lookup_recent_messages(Caller, With2, Before2, Limit2), |
76 |
9 |
Maps = lists:map(fun row_to_map/1, Rows), |
77 |
9 |
{ok, #{<<"stanzas">> => Maps, <<"limit">> => Limit2}}. |
78 |
|
|
79 |
16 |
null_as_undefined(null) -> undefined; |
80 |
2 |
null_as_undefined(Value) -> Value. |
81 |
|
|
82 |
|
-spec row_to_map(mod_mam:message_row()) -> {ok, map()}. |
83 |
|
row_to_map(#{id := Id, jid := From, packet := Msg}) -> |
84 |
12 |
{Microseconds, _} = mod_mam_utils:decode_compact_uuid(Id), |
85 |
12 |
StanzaID = mod_mam_utils:mess_id_to_external_binary(Id), |
86 |
12 |
Map = #{<<"sender">> => From, <<"timestamp">> => Microseconds, |
87 |
|
<<"stanza_id">> => StanzaID, <<"stanza">> => Msg}, |
88 |
12 |
{ok, Map}. |
89 |
|
|
90 |
|
-spec route(From :: jid:jid(), To :: jid:jid(), |
91 |
|
Packet :: exml:element(), SkipAuth :: boolean()) -> |
92 |
|
{ok, map()} | {error, term()}. |
93 |
|
route(From = #jid{lserver = LServer}, To, Packet, SkipAuth) -> |
94 |
19 |
case mongoose_graphql_helper:check_user(From, SkipAuth) of |
95 |
|
{ok, HostType} -> |
96 |
17 |
do_route(HostType, LServer, From, To, Packet); |
97 |
|
Error -> |
98 |
2 |
Error |
99 |
|
end. |
100 |
|
|
101 |
|
do_route(HostType, LServer, From, To, Packet) -> |
102 |
|
%% Based on mod_commands:do_send_packet/3 |
103 |
17 |
Acc = mongoose_acc:new(#{location => ?LOCATION, |
104 |
|
host_type => HostType, |
105 |
|
lserver => LServer, |
106 |
|
element => Packet}), |
107 |
17 |
Acc1 = mongoose_hooks:user_send_packet(Acc, From, To, Packet), |
108 |
17 |
Acc2 = ejabberd_router:route(From, To, Acc1), |
109 |
17 |
MessID = mod_mam_utils:get_mam_id_ext(Acc2), |
110 |
17 |
{ok, #{ <<"id">> => MessID }}. |