Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/processone/ejabberd.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mod_mam.erl91
-rw-r--r--test/ejabberd_SUITE.erl19
-rw-r--r--tools/xmpp_codec.erl81
-rw-r--r--tools/xmpp_codec.hrl4
-rw-r--r--tools/xmpp_codec.spec10
5 files changed, 144 insertions, 61 deletions
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index 4e58f9ffb..c3c8a2b92 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -518,10 +518,10 @@ select_and_send(#jid{lserver = LServer} = From,
DBType).
select_and_send(From, To, Start, End, With, RSM, IQ, DBType) ->
- {Msgs, Count} = select_and_start(From, To, Start, End, With,
- RSM, DBType),
+ {Msgs, IsComplete, Count} = select_and_start(From, To, Start, End, With,
+ RSM, DBType),
SortedMsgs = lists:keysort(2, Msgs),
- send(From, To, SortedMsgs, RSM, Count, IQ).
+ send(From, To, SortedMsgs, RSM, Count, IsComplete, IQ).
select_and_start(From, _To, StartUser, End, With, RSM, DB) ->
{JidRequestor, Start, With2} = case With of
@@ -538,17 +538,17 @@ select(#jid{luser = LUser, lserver = LServer} = JidRequestor,
Start, End, With, RSM, mnesia) ->
MS = make_matchspec(LUser, LServer, Start, End, With),
Msgs = mnesia:dirty_select(archive_msg, MS),
- FilteredMsgs = filter_by_rsm(Msgs, RSM),
+ {FilteredMsgs, IsComplete} = filter_by_rsm(Msgs, RSM),
Count = length(Msgs),
{lists:map(
fun(Msg) ->
{Msg#archive_msg.id,
jlib:binary_to_integer(Msg#archive_msg.id),
msg_to_el(Msg, JidRequestor)}
- end, FilteredMsgs), Count};
+ end, FilteredMsgs), IsComplete, Count};
select(#jid{luser = LUser, lserver = LServer} = JidRequestor,
Start, End, With, RSM, {odbc, Host}) ->
- {Query, CountQuery} = make_sql_query(LUser, LServer,
+ {Query, CountQuery} = make_sql_query(LUser, LServer,
Start, End, With, RSM),
% XXX TODO from XEP-0313:
% To conserve resources, a server MAY place a reasonable limit on
@@ -559,6 +559,20 @@ select(#jid{luser = LUser, lserver = LServer} = JidRequestor,
case {ejabberd_odbc:sql_query(Host, Query),
ejabberd_odbc:sql_query(Host, CountQuery)} of
{{selected, _, Res}, {selected, _, [[Count]]}} ->
+ {Max, Direction} = case RSM of
+ #rsm_in{max = M, direction = D} -> {M, D};
+ _ -> {undefined, undefined}
+ end,
+ {Res1, IsComplete} =
+ if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
+ if Direction == before ->
+ {lists:nthtail(1, Res), false};
+ true ->
+ {lists:sublist(Res, Max), false}
+ end;
+ true ->
+ {Res, true}
+ end,
{lists:map(
fun([TS, XML, PeerBin]) ->
#xmlel{} = El = xml_stream:parse_element(XML),
@@ -569,9 +583,9 @@ select(#jid{luser = LUser, lserver = LServer} = JidRequestor,
packet = El,
peer = PeerJid},
JidRequestor)}
- end, Res), jlib:binary_to_integer(Count)};
- _ ->
- {[], 0}
+ end, Res1), IsComplete, jlib:binary_to_integer(Count)};
+ _ ->
+ {[], false, 0}
end.
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, peer = Peer},
@@ -599,14 +613,19 @@ maybe_update_from_to(Pkt, JidRequestor, Peer) ->
_ -> Pkt
end.
-send(From, To, Msgs, RSM, Count, #iq{sub_el = SubEl} = IQ) ->
+send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
QID = xml:get_tag_attr_s(<<"queryid">>, SubEl),
NS = xml:get_tag_attr_s(<<"xmlns">>, SubEl),
QIDAttr = if QID /= <<>> ->
- [{<<"queryid">>, QID}];
- true ->
- []
- end,
+ [{<<"queryid">>, QID}];
+ true ->
+ []
+ end,
+ CompleteAttr = if NS == ?NS_MAM_TMP ->
+ [];
+ NS == ?NS_MAM_0 ->
+ [{<<"complete">>, jlib:atom_to_binary(IsComplete)}]
+ end,
Els = lists:map(
fun({ID, _IDInt, El}) ->
#xmlel{name = <<"message">>,
@@ -615,7 +634,7 @@ send(From, To, Msgs, RSM, Count, #iq{sub_el = SubEl} = IQ) ->
{<<"id">>, ID}|QIDAttr],
children = [El]}]}
end, Msgs),
- RSMOut = make_rsm_out(Msgs, RSM, Count, QIDAttr, NS),
+ RSMOut = make_rsm_out(Msgs, RSM, Count, QIDAttr ++ CompleteAttr, NS),
case NS of
?NS_MAM_TMP ->
lists:foreach(
@@ -637,30 +656,30 @@ send(From, To, Msgs, RSM, Count, #iq{sub_el = SubEl} = IQ) ->
end.
-make_rsm_out(_Msgs, none, _Count, _QIDAttr, ?NS_MAM_TMP) ->
+make_rsm_out(_Msgs, none, _Count, _Attrs, ?NS_MAM_TMP) ->
[];
-make_rsm_out(_Msgs, none, _Count, QIDAttr, ?NS_MAM_0) ->
- [#xmlel{name = <<"fin">>, attrs = [{<<"xmlns">>, ?NS_MAM_0}|QIDAttr]}];
-make_rsm_out([], #rsm_in{}, Count, QIDAttr, NS) ->
+make_rsm_out(_Msgs, none, _Count, Attrs, ?NS_MAM_0) ->
+ [#xmlel{name = <<"fin">>, attrs = [{<<"xmlns">>, ?NS_MAM_0}|Attrs]}];
+make_rsm_out([], #rsm_in{}, Count, Attrs, NS) ->
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
true -> <<"fin">>
end,
- [#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|QIDAttr],
- children = jlib:rsm_encode(#rsm_out{count = Count})}];
-make_rsm_out([{FirstID, _, _}|_] = Msgs, #rsm_in{}, Count, QIDAttr, NS) ->
+ [#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|Attrs],
+ children = jlib:rsm_encode(#rsm_out{count = Count})}];
+make_rsm_out([{FirstID, _, _}|_] = Msgs, #rsm_in{}, Count, Attrs, NS) ->
{LastID, _, _} = lists:last(Msgs),
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
true -> <<"fin">>
end,
- [#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|QIDAttr],
- children = jlib:rsm_encode(
- #rsm_out{first = FirstID, count = Count,
- last = LastID})}].
+ [#xmlel{name = Tag, attrs = [{<<"xmlns">>, NS}|Attrs],
+ children = jlib:rsm_encode(
+ #rsm_out{first = FirstID, count = Count,
+ last = LastID})}].
filter_by_rsm(Msgs, none) ->
- Msgs;
-filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max =< 0 ->
- [];
+ {Msgs, true};
+filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max < 0 ->
+ {[], true};
filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) ->
NewMsgs = case Direction of
aft when ID /= <<"">> ->
@@ -683,11 +702,11 @@ filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) ->
filter_by_max(NewMsgs, Max).
filter_by_max(Msgs, undefined) ->
- Msgs;
+ {Msgs, true};
filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 ->
- lists:sublist(Msgs, Len);
+ {lists:sublist(Msgs, Len), length(Msgs) =< Len};
filter_by_max(_Msgs, _Junk) ->
- [].
+ {[], true}.
make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) ->
ets:fun2ms(
@@ -729,10 +748,10 @@ make_sql_query(LUser, _LServer, Start, End, With, RSM) ->
{none, none, <<>>}
end,
LimitClause = if is_integer(Max), Max >= 0 ->
- [<<" limit ">>, jlib:integer_to_binary(Max)];
- true ->
- []
- end,
+ [<<" limit ">>, jlib:integer_to_binary(Max+1)];
+ true ->
+ []
+ end,
WithClause = case With of
{text, <<>>} ->
[];
diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl
index 8cd21d5e9..075a53fe2 100644
--- a/test/ejabberd_SUITE.erl
+++ b/test/ejabberd_SUITE.erl
@@ -1690,7 +1690,7 @@ mam_query_all(Config, NS) ->
if NS == ?NS_MAM_TMP ->
?recv1(#iq{type = result, id = I, sub_els = []});
true ->
- ?recv1(#message{sub_els = [#mam_fin{id = QID}]})
+ ?recv1(#message{sub_els = [#mam_fin{complete = true, id = QID}]})
end.
mam_query_with(Config, JID, NS) ->
@@ -1726,7 +1726,7 @@ mam_query_with(Config, JID, NS) ->
if NS == ?NS_MAM_TMP ->
?recv1(#iq{type = result, id = I, sub_els = []});
true ->
- ?recv1(#message{sub_els = [#mam_fin{}]})
+ ?recv1(#message{sub_els = [#mam_fin{complete = true}]})
end.
maybe_recv_iq_result(?NS_MAM_0, I1) ->
@@ -1767,6 +1767,7 @@ mam_query_rsm(Config, NS) ->
rsm = #rsm_set{last = Last, count = 5}}]});
true ->
?recv1(#message{sub_els = [#mam_fin{
+ complete = false,
rsm = #rsm_set{last = Last, count = 10}}]})
end,
%% Get the next items starting from the `Last`.
@@ -1802,15 +1803,16 @@ mam_query_rsm(Config, NS) ->
true ->
?recv1(#message{
sub_els = [#mam_fin{
+ complete = false,
rsm = #rsm_set{
count = 10,
first = #rsm_first{data = First}}}]})
end,
- %% Paging back. Should receive 2 elements: 2, 3.
+ %% Paging back. Should receive 3 elements: 1, 2, 3.
I3 = send(Config,
#iq{type = Type,
sub_els = [#mam_query{xmlns = NS,
- rsm = #rsm_set{max = 2,
+ rsm = #rsm_set{max = 3,
before = First}}]}),
maybe_recv_iq_result(NS, I3),
lists:foreach(
@@ -1827,13 +1829,14 @@ mam_query_rsm(Config, NS) ->
[#message{
from = MyJID, to = Peer,
body = [Text]}]}]}]})
- end, lists:seq(2, 3)),
+ end, lists:seq(1, 3)),
if NS == ?NS_MAM_TMP ->
?recv1(#iq{type = result, id = I3,
sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]});
true ->
?recv1(#message{
- sub_els = [#mam_fin{rsm = #rsm_set{count = 10}}]})
+ sub_els = [#mam_fin{complete = true,
+ rsm = #rsm_set{count = 10}}]})
end,
%% Getting the item count. Should be 5 (or 10).
I4 = send(Config,
@@ -1851,6 +1854,7 @@ mam_query_rsm(Config, NS) ->
true ->
?recv1(#message{
sub_els = [#mam_fin{
+ complete = false,
rsm = #rsm_set{count = 10,
first = undefined,
last = undefined}}]})
@@ -1882,7 +1886,8 @@ mam_query_rsm(Config, NS) ->
sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]});
true ->
?recv1(#message{
- sub_els = [#mam_fin{rsm = #rsm_set{count = 10}}]})
+ sub_els = [#mam_fin{complete = false,
+ rsm = #rsm_set{count = 10}}]})
end.
client_state_master(Config) ->
diff --git a/tools/xmpp_codec.erl b/tools/xmpp_codec.erl
index 08a2e84ca..987eb7c41 100644
--- a/tools/xmpp_codec.erl
+++ b/tools/xmpp_codec.erl
@@ -2082,7 +2082,7 @@ encode({mam_result, _, _, _, _} = Result) ->
encode_mam_result(Result, []);
encode({mam_prefs, _, _, _, _} = Prefs) ->
encode_mam_prefs(Prefs, []);
-encode({mam_fin, _, _} = Fin) ->
+encode({mam_fin, _, _, _, _} = Fin) ->
encode_mam_fin(Fin,
[{<<"xmlns">>, <<"urn:xmpp:mam:0">>}]);
encode({forwarded, _, _} = Forwarded) ->
@@ -2308,7 +2308,7 @@ get_ns({rsm_first, _, _}) ->
get_ns({rsm_set, _, _, _, _, _, _, _}) ->
<<"http://jabber.org/protocol/rsm">>;
get_ns({mam_archived, _, _}) -> <<"urn:xmpp:mam:tmp">>;
-get_ns({mam_fin, _, _}) -> <<"urn:xmpp:mam:0">>;
+get_ns({mam_fin, _, _, _, _}) -> <<"urn:xmpp:mam:0">>;
get_ns({forwarded, _, _}) -> <<"urn:xmpp:forward:0">>;
get_ns({carbons_disable}) -> <<"urn:xmpp:carbons:2">>;
get_ns({carbons_enable}) -> <<"urn:xmpp:carbons:2">>;
@@ -2505,7 +2505,7 @@ pp(mam_query, 7) ->
pp(mam_archived, 2) -> [by, id];
pp(mam_result, 4) -> [xmlns, queryid, id, sub_els];
pp(mam_prefs, 4) -> [xmlns, default, always, never];
-pp(mam_fin, 2) -> [id, rsm];
+pp(mam_fin, 4) -> [id, rsm, stable, complete];
pp(forwarded, 2) -> [delay, sub_els];
pp(carbons_disable, 0) -> [];
pp(carbons_enable, 0) -> [];
@@ -3709,9 +3709,10 @@ decode_mam_fin(__TopXMLNS, __IgnoreEls,
{xmlel, <<"fin">>, _attrs, _els}) ->
Rsm = decode_mam_fin_els(__TopXMLNS, __IgnoreEls, _els,
undefined),
- Id = decode_mam_fin_attrs(__TopXMLNS, _attrs,
- undefined),
- {mam_fin, Id, Rsm}.
+ {Id, Stable, Complete} =
+ decode_mam_fin_attrs(__TopXMLNS, _attrs, undefined,
+ undefined, undefined),
+ {mam_fin, Id, Rsm, Stable, Complete}.
decode_mam_fin_els(__TopXMLNS, __IgnoreEls, [], Rsm) ->
Rsm;
@@ -3729,16 +3730,37 @@ decode_mam_fin_els(__TopXMLNS, __IgnoreEls, [_ | _els],
decode_mam_fin_els(__TopXMLNS, __IgnoreEls, _els, Rsm).
decode_mam_fin_attrs(__TopXMLNS,
- [{<<"queryid">>, _val} | _attrs], _Id) ->
- decode_mam_fin_attrs(__TopXMLNS, _attrs, _val);
-decode_mam_fin_attrs(__TopXMLNS, [_ | _attrs], Id) ->
- decode_mam_fin_attrs(__TopXMLNS, _attrs, Id);
-decode_mam_fin_attrs(__TopXMLNS, [], Id) ->
- decode_mam_fin_attr_queryid(__TopXMLNS, Id).
-
-encode_mam_fin({mam_fin, Id, Rsm}, _xmlns_attrs) ->
+ [{<<"queryid">>, _val} | _attrs], _Id, Stable,
+ Complete) ->
+ decode_mam_fin_attrs(__TopXMLNS, _attrs, _val, Stable,
+ Complete);
+decode_mam_fin_attrs(__TopXMLNS,
+ [{<<"stable">>, _val} | _attrs], Id, _Stable,
+ Complete) ->
+ decode_mam_fin_attrs(__TopXMLNS, _attrs, Id, _val,
+ Complete);
+decode_mam_fin_attrs(__TopXMLNS,
+ [{<<"complete">>, _val} | _attrs], Id, Stable,
+ _Complete) ->
+ decode_mam_fin_attrs(__TopXMLNS, _attrs, Id, Stable,
+ _val);
+decode_mam_fin_attrs(__TopXMLNS, [_ | _attrs], Id,
+ Stable, Complete) ->
+ decode_mam_fin_attrs(__TopXMLNS, _attrs, Id, Stable,
+ Complete);
+decode_mam_fin_attrs(__TopXMLNS, [], Id, Stable,
+ Complete) ->
+ {decode_mam_fin_attr_queryid(__TopXMLNS, Id),
+ decode_mam_fin_attr_stable(__TopXMLNS, Stable),
+ decode_mam_fin_attr_complete(__TopXMLNS, Complete)}.
+
+encode_mam_fin({mam_fin, Id, Rsm, Stable, Complete},
+ _xmlns_attrs) ->
_els = lists:reverse('encode_mam_fin_$rsm'(Rsm, [])),
- _attrs = encode_mam_fin_attr_queryid(Id, _xmlns_attrs),
+ _attrs = encode_mam_fin_attr_complete(Complete,
+ encode_mam_fin_attr_stable(Stable,
+ encode_mam_fin_attr_queryid(Id,
+ _xmlns_attrs))),
{xmlel, <<"fin">>, _attrs, _els}.
'encode_mam_fin_$rsm'(undefined, _acc) -> _acc;
@@ -3755,6 +3777,35 @@ encode_mam_fin_attr_queryid(undefined, _acc) -> _acc;
encode_mam_fin_attr_queryid(_val, _acc) ->
[{<<"queryid">>, _val} | _acc].
+decode_mam_fin_attr_stable(__TopXMLNS, undefined) ->
+ undefined;
+decode_mam_fin_attr_stable(__TopXMLNS, _val) ->
+ case catch dec_bool(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"stable">>, <<"fin">>, __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_mam_fin_attr_stable(undefined, _acc) -> _acc;
+encode_mam_fin_attr_stable(_val, _acc) ->
+ [{<<"stable">>, enc_bool(_val)} | _acc].
+
+decode_mam_fin_attr_complete(__TopXMLNS, undefined) ->
+ undefined;
+decode_mam_fin_attr_complete(__TopXMLNS, _val) ->
+ case catch dec_bool(_val) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"complete">>, <<"fin">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_mam_fin_attr_complete(undefined, _acc) -> _acc;
+encode_mam_fin_attr_complete(_val, _acc) ->
+ [{<<"complete">>, enc_bool(_val)} | _acc].
+
decode_mam_prefs(__TopXMLNS, __IgnoreEls,
{xmlel, <<"prefs">>, _attrs, _els}) ->
{Never, Always} = decode_mam_prefs_els(__TopXMLNS,
diff --git a/tools/xmpp_codec.hrl b/tools/xmpp_codec.hrl
index fa8e5e74d..7996f6a11 100644
--- a/tools/xmpp_codec.hrl
+++ b/tools/xmpp_codec.hrl
@@ -288,7 +288,9 @@
max :: non_neg_integer()}).
-record(mam_fin, {id :: binary(),
- rsm :: #rsm_set{}}).
+ rsm :: #rsm_set{},
+ stable :: any(),
+ complete :: any()}).
-record(vcard_tel, {home = false :: boolean(),
work = false :: boolean(),
diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec
index 326d1de36..38508ce6c 100644
--- a/tools/xmpp_codec.spec
+++ b/tools/xmpp_codec.spec
@@ -2210,8 +2210,14 @@
-xml(mam_fin,
#elem{name = <<"fin">>,
xmlns = <<"urn:xmpp:mam:0">>,
- result = {mam_fin, '$id', '$rsm'},
- attrs = [#attr{name = <<"queryid">>, label = '$id'}],
+ result = {mam_fin, '$id', '$rsm', '$stable', '$complete'},
+ attrs = [#attr{name = <<"queryid">>, label = '$id'},
+ #attr{name = <<"stable">>, label = '$stable',
+ dec = {dec_bool, []},
+ enc = {enc_bool, []}},
+ #attr{name = <<"complete">>, label = '$complete',
+ dec = {dec_bool, []},
+ enc = {enc_bool, []}}],
refs = [#ref{name = rsm_set, min = 0, max = 1, label = '$rsm'}]}).
-xml(forwarded,