From b8dcc911a3b1e3cc05074d9ac4bd8da80b431388 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 18 Nov 2016 13:38:08 +0300 Subject: Make common tests working again --- src/ejabberd_local.erl | 12 +- src/flex_offline.erl | 2 +- src/mam_query.erl | 220 +++++++++++++++++++++++++++++++++ src/mod_carboncopy.erl | 4 +- src/mod_mam.erl | 197 ++++++++++++++--------------- src/mod_mam_mnesia.erl | 8 +- src/mod_mam_sql.erl | 18 +-- src/mod_offline.erl | 3 +- src/mod_offline_riak.erl | 2 +- src/mod_offline_sql.erl | 3 +- src/mod_roster.erl | 2 +- src/muc_register.erl | 2 +- src/muc_request.erl | 2 +- src/muc_roomconfig.erl | 2 +- src/muc_roominfo.erl | 2 +- src/pubsub_get_pending.erl | 2 +- src/pubsub_node_config.erl | 2 +- src/pubsub_publish_options.erl | 2 +- src/pubsub_subscribe_authorization.erl | 2 +- src/pubsub_subscribe_options.erl | 2 +- src/xmpp_util.erl | 9 +- 21 files changed, 360 insertions(+), 138 deletions(-) create mode 100644 src/mam_query.erl (limited to 'src') diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index d7849396b..74d86945d 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -177,15 +177,19 @@ unregister_iq_handler(Host, XMLNS) -> refresh_iq_handlers() -> ejabberd_local ! refresh_iq_handlers. --spec bounce_resource_packet(jid(), jid(), stanza()) -> ok. -bounce_resource_packet(_From, _To, #presence{}) -> +-spec bounce_resource_packet(jid(), jid(), stanza()) -> stop. +bounce_resource_packet(_From, #jid{lresource = <<"">>}, #presence{}) -> + ok; +bounce_resource_packet(_From, #jid{lresource = <<"">>}, + #message{type = headline}) -> ok; bounce_resource_packet(From, To, Packet) -> Lang = xmpp:get_lang(Packet), Txt = <<"No available resource found">>, Err = xmpp:make_error(Packet, xmpp:err_item_not_found(Txt, Lang)), - ejabberd_router:route(To, From, Err). + ejabberd_router:route(To, From, Err), + stop. %%==================================================================== %% gen_server callbacks @@ -283,7 +287,7 @@ do_route(From, To, Packet) -> ejabberd_sm:route(From, To, Packet); is_record(Packet, iq), To#jid.lresource == <<"">> -> process_iq(From, To, Packet); - Type == result; Type == error; Type == headline -> + Type == result; Type == error -> ok; true -> ejabberd_hooks:run(local_send_to_resource_hook, diff --git a/src/flex_offline.erl b/src/flex_offline.erl index 090ab3ddf..acc57342e 100644 --- a/src/flex_offline.erl +++ b/src/flex_offline.erl @@ -12,7 +12,7 @@ -include("flex_offline.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_int(Val, Min, Max) -> case list_to_integer(binary_to_list(Val)) of diff --git a/src/mam_query.erl b/src/mam_query.erl new file mode 100644 index 000000000..cb5bfe13a --- /dev/null +++ b/src/mam_query.erl @@ -0,0 +1,220 @@ +%% Created automatically by xdata generator (xdata_codec.erl) +%% Source: mam_query.xdata +%% Form type: urn:xmpp:mam:1 +%% Document: XEP-0313 + +-module(mam_query). + +-export([decode/1, decode/2, encode/1, encode/2, + format_error/1]). + +-include("xmpp_codec.hrl"). + +-include("mam_query.hrl"). + +-export_type([property/0, result/0, form/0]). + +enc_jid(J) -> jid:to_string(J). + +dec_jid(Val) -> + case jid:from_string(Val) of + error -> erlang:error(badarg); + J -> J + end. + +format_error({form_type_mismatch, Type}) -> + <<"FORM_TYPE doesn't match '", Type/binary, "'">>; +format_error({bad_var_value, Var, Type}) -> + <<"Bad value of field '", Var/binary, "' of type '", + Type/binary, "'">>; +format_error({missing_value, Var, Type}) -> + <<"Missing value of field '", Var/binary, "' of type '", + Type/binary, "'">>; +format_error({too_many_values, Var, Type}) -> + <<"Too many values for field '", Var/binary, + "' of type '", Type/binary, "'">>; +format_error({unknown_var, Var, Type}) -> + <<"Unknown field '", Var/binary, "' of type '", + Type/binary, "'">>; +format_error({missing_required_var, Var, Type}) -> + <<"Missing required field '", Var/binary, "' of type '", + Type/binary, "'">>. + +decode(Fs) -> decode(Fs, []). + +decode(Fs, Acc) -> + case lists:keyfind(<<"FORM_TYPE">>, #xdata_field.var, + Fs) + of + false -> decode(Fs, Acc, []); + #xdata_field{values = [<<"urn:xmpp:mam:1">>]} -> + decode(Fs, Acc, []); + _ -> + erlang:error({?MODULE, + {form_type_mismatch, <<"urn:xmpp:mam:1">>}}) + end. + +encode(Cfg) -> encode(Cfg, fun (Text) -> Text end). + +encode(List, Translate) when is_list(List) -> + Fs = [case Opt of + {with, Val} -> [encode_with(Val, Translate)]; + {with, _, _} -> erlang:error({badarg, Opt}); + {start, Val} -> [encode_start(Val, Translate)]; + {start, _, _} -> erlang:error({badarg, Opt}); + {'end', Val} -> [encode_end(Val, Translate)]; + {'end', _, _} -> erlang:error({badarg, Opt}); + {withtext, Val} -> [encode_withtext(Val, Translate)]; + {withtext, _, _} -> erlang:error({badarg, Opt}); + #xdata_field{} -> [Opt]; + _ -> [] + end + || Opt <- List], + FormType = #xdata_field{var = <<"FORM_TYPE">>, + type = hidden, values = [<<"urn:xmpp:mam:1">>]}, + [FormType | lists:flatten(Fs)]. + +decode([#xdata_field{var = <<"with">>, values = [Value]} + | Fs], + Acc, Required) -> + try dec_jid(Value) of + Result -> + decode(Fs, [{with, Result} | Acc], + lists:delete(<<"with">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"with">>, <<"urn:xmpp:mam:1">>}}) + end; +decode([#xdata_field{var = <<"with">>, values = []} = F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"with">>, values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"with">>} | _], _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"with">>, <<"urn:xmpp:mam:1">>}}); +decode([#xdata_field{var = <<"start">>, + values = [Value]} + | Fs], + Acc, Required) -> + try xmpp_util:decode_timestamp(Value) of + Result -> + decode(Fs, [{start, Result} | Acc], + lists:delete(<<"start">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"start">>, <<"urn:xmpp:mam:1">>}}) + end; +decode([#xdata_field{var = <<"start">>, values = []} = F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"start">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"start">>} | _], _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"start">>, <<"urn:xmpp:mam:1">>}}); +decode([#xdata_field{var = <<"end">>, values = [Value]} + | Fs], + Acc, Required) -> + try xmpp_util:decode_timestamp(Value) of + Result -> + decode(Fs, [{'end', Result} | Acc], + lists:delete(<<"end">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"end">>, <<"urn:xmpp:mam:1">>}}) + end; +decode([#xdata_field{var = <<"end">>, values = []} = F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"end">>, values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"end">>} | _], _, _) -> + erlang:error({?MODULE, + {too_many_values, <<"end">>, <<"urn:xmpp:mam:1">>}}); +decode([#xdata_field{var = <<"withtext">>, + values = [Value]} + | Fs], + Acc, Required) -> + try Value of + Result -> + decode(Fs, [{withtext, Result} | Acc], + lists:delete(<<"withtext">>, Required)) + catch + _:_ -> + erlang:error({?MODULE, + {bad_var_value, <<"withtext">>, <<"urn:xmpp:mam:1">>}}) + end; +decode([#xdata_field{var = <<"withtext">>, + values = []} = + F + | Fs], + Acc, Required) -> + decode([F#xdata_field{var = <<"withtext">>, + values = [<<>>]} + | Fs], + Acc, Required); +decode([#xdata_field{var = <<"withtext">>} | _], _, + _) -> + erlang:error({?MODULE, + {too_many_values, <<"withtext">>, + <<"urn:xmpp:mam:1">>}}); +decode([#xdata_field{var = Var} | Fs], Acc, Required) -> + if Var /= <<"FORM_TYPE">> -> + erlang:error({?MODULE, + {unknown_var, Var, <<"urn:xmpp:mam:1">>}}); + true -> decode(Fs, Acc, Required) + end; +decode([], _, [Var | _]) -> + erlang:error({?MODULE, + {missing_required_var, Var, <<"urn:xmpp:mam:1">>}}); +decode([], Acc, []) -> Acc. + +encode_with(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [enc_jid(Value)] + end, + Opts = [], + #xdata_field{var = <<"with">>, values = Values, + required = false, type = 'jid-single', options = Opts, + desc = <<>>, label = Translate(<<"User JID">>)}. + +encode_start(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"start">>, values = Values, + required = false, type = 'text-single', options = Opts, + desc = <<>>, + label = Translate(<<"Search from the date">>)}. + +encode_end(Value, Translate) -> + Values = case Value of + undefined -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"end">>, values = Values, + required = false, type = 'text-single', options = Opts, + desc = <<>>, + label = Translate(<<"Search until the date">>)}. + +encode_withtext(Value, Translate) -> + Values = case Value of + <<>> -> []; + Value -> [Value] + end, + Opts = [], + #xdata_field{var = <<"withtext">>, values = Values, + required = false, type = 'text-single', options = Opts, + desc = <<>>, label = Translate(<<"Search the text">>)}. diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 7d8ca5332..1c8ca1fdc 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -228,8 +228,8 @@ complete_packet(_From, Msg, _Direction) -> -spec is_chat_message(stanza()) -> boolean(). is_chat_message(#message{type = chat}) -> true; -is_chat_message(#message{type = normal, body = Body}) -> - xmpp:get_text(Body) /= <<"">>; +is_chat_message(#message{type = normal, body = [_|_]}) -> + true; is_chat_message(_) -> false. diff --git a/src/mod_mam.erl b/src/mod_mam.erl index f9ef104bd..61754ae59 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -54,12 +54,13 @@ -callback delete_old_messages(binary() | global, erlang:timestamp(), all | chat | groupchat) -> any(). --callback extended_fields() -> [xdata_field()]. +-callback extended_fields() -> [mam_query:property() | #xdata_field{}]. -callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat, jid(), binary(), recv | send) -> {ok, binary()} | any(). -callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any(). -callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error. --callback select(binary(), jid(), jid(), mam_query(), chat | groupchat) -> +-callback select(binary(), jid(), jid(), mam_query:result(), + #rsm_set{} | undefined, chat | groupchat) -> {[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}. %%%=================================================================== @@ -259,8 +260,9 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState, end. set_stanza_id(Pkt, JID, ID) -> - Archived = #mam_archived{by = JID, id = ID}, - StanzaID = #stanza_id{by = JID, id = ID}, + BareJID = jid:remove_resource(JID), + Archived = #mam_archived{by = BareJID, id = ID}, + StanzaID = #stanza_id{by = BareJID, id = ID}, NewEls = [Archived, StanzaID|xmpp:get_els(Pkt)], xmpp:set_els(Pkt, NewEls). @@ -308,43 +310,24 @@ muc_process_iq(#iq{type = get, muc_process_iq(IQ, _MUCState) -> IQ. -parse_query(#mam_query{xdata = #xdata{fields = Fs}} = Query, Lang) -> - try - lists:foldl( - fun(#xdata_field{var = <<"start">>, values = [Data|_]}, Q) -> - try xmpp_util:decode_timestamp(Data) of - {_, _, _} = TS -> Q#mam_query{start = TS} - catch _:{bad_timestamp, _} -> throw({error, <<"start">>}) - end; - (#xdata_field{var = <<"end">>, values = [Data|_]}, Q) -> - try xmpp_util:decode_timestamp(Data) of - {_, _, _} = TS -> Q#mam_query{start = TS} - catch _:{bad_timestamp, _} -> throw({error, <<"end">>}) - end; - (#xdata_field{var = <<"with">>, values = [Data|_]}, Q) -> - case jid:from_string(Data) of - error -> throw({error, <<"with">>}); - J -> Q#mam_query{with = J} - end; - (#xdata_field{var = <<"withtext">>, values = [Data|_]}, Q) -> - case Data of - <<"">> -> throw({error, <<"withtext">>}); - _ -> Q#mam_query{withtext = Data} - end; - (#xdata_field{var = <<"FORM_TYPE">>, values = [NS|_]}, Q) -> - case Query#mam_query.xmlns of - NS -> Q; - _ -> throw({error, <<"FORM_TYPE">>}) - end; - (#xdata_field{}, Acc) -> - Acc - end, Query, Fs) - catch throw:{error, Var} -> - Txt = io_lib:format("Incorrect value of field '~s'", [Var]), - {error, xmpp:err_bad_request(iolist_to_binary(Txt), Lang)} +parse_query(#mam_query{xmlns = ?NS_MAM_TMP, + start = Start, 'end' = End, + with = With, withtext = Text}, _Lang) -> + {ok, [{start, Start}, {'end', End}, + {with, With}, {withtext, Text}]}; +parse_query(#mam_query{xdata = #xdata{}} = Query, Lang) -> + X = xmpp_util:set_xdata_field( + #xdata_field{var = <<"FORM_TYPE">>, + type = hidden, values = [?NS_MAM_1]}, + Query#mam_query.xdata), + try mam_query:decode(X#xdata.fields) of + Form -> {ok, Form} + catch _:{mam_query, Why} -> + Txt = mam_query:format_error(Why), + {error, xmpp:err_bad_request(Txt, Lang)} end; -parse_query(Query, _Lang) -> - Query. +parse_query(#mam_query{}, _Lang) -> + {ok, []}. disco_sm_features(empty, From, To, Node, Lang) -> disco_sm_features({result, []}, From, To, Node, Lang); @@ -402,17 +385,16 @@ delete_old_messages(_TypeBin, _Days) -> %%%=================================================================== process_iq(LServer, #iq{sub_els = [#mam_query{xmlns = NS}]} = IQ) -> - CommonFields = [#xdata_field{type = hidden, - var = <<"FORM_TYPE">>, - values = [NS]}, - #xdata_field{type = 'jid-single', var = <<"with">>}, - #xdata_field{type = 'text-single', var = <<"start">>}, - #xdata_field{type = 'text-single', var = <<"end">>}], Mod = gen_mod:db_mod(LServer, ?MODULE), + CommonFields = [{with, undefined}, + {start, undefined}, + {'end', undefined}], ExtendedFields = Mod:extended_fields(), - Fields = CommonFields ++ ExtendedFields, - Form = #xdata{type = form, fields = Fields}, - xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = Form}). + Fields = mam_query:encode(CommonFields ++ ExtendedFields), + X = xmpp_util:set_xdata_field( + #xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [NS]}, + #xdata{type = form, fields = Fields}), + xmpp:make_iq_result(IQ, #mam_query{xmlns = NS, xdata = X}). % Preference setting (both v0.2 & v0.3) process_iq(#iq{type = set, lang = Lang, @@ -457,15 +439,17 @@ process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang, {groupchat, _Role, _MUCState} -> ok end, - case parse_query(SubEl, Lang) of + case SubEl of #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) -> xmpp:make_error(IQ, xmpp:err_feature_not_implemented()); - #mam_query{rsm = RSM, xmlns = NS} = Query -> - NewRSM = limit_max(RSM, NS), - NewQuery = Query#mam_query{rsm = NewRSM}, - select_and_send(LServer, NewQuery, IQ, MsgType); - {error, Err} -> - xmpp:make_error(IQ, Err) + #mam_query{rsm = RSM, xmlns = NS} -> + case parse_query(SubEl, Lang) of + {ok, Query} -> + NewRSM = limit_max(RSM, NS), + select_and_send(LServer, Query, NewRSM, IQ, MsgType); + {error, Err} -> + xmpp:make_error(IQ, Err) + end end. should_archive(#message{type = error}, _LServer) -> @@ -493,57 +477,57 @@ should_archive(_, _LServer) -> -spec strip_my_archived_tag(stanza(), binary()) -> stanza(). strip_my_archived_tag(Pkt, LServer) -> - NewPkt = xmpp:decode_els( - Pkt, ?NS_CLIENT, + Els = xmpp:get_els(Pkt), + NewEls = lists:filter( fun(El) -> - case xmpp:get_name(El) of - <<"archived">> -> - xmpp:get_ns(El) == ?NS_MAM_TMP; - <<"stanza-id">> -> - xmpp:get_ns(El) == ?NS_SID_0; - _ -> - false + Name = xmpp:get_name(El), + NS = xmpp:get_ns(El), + if (Name == <<"archived">> andalso NS == ?NS_MAM_TMP); + (Name == <<"stanza-id">> andalso NS == ?NS_SID_0) -> + try xmpp:decode(El) of + #mam_archived{by = By} -> + By#jid.lserver /= LServer; + #stanza_id{by = By} -> + By#jid.lserver /= LServer + catch _:{xmpp_codec, _} -> + false + end; + true -> + true end - end), - NewEls = lists:filter( - fun(#mam_archived{by = By}) -> - By#jid.lserver /= LServer; - (#stanza_id{by = By}) -> - By#jid.lserver /= LServer; - (_) -> - true - end, xmpp:get_els(NewPkt)), - xmpp:set_els(NewPkt, NewEls). + end, Els), + xmpp:set_els(Pkt, NewEls). +-spec strip_x_jid_tags(stanza()) -> stanza(). strip_x_jid_tags(Pkt) -> - NewPkt = xmpp:decode_els( - Pkt, ?NS_CLIENT, + Els = xmpp:get_els(Pkt), + NewEls = lists:filter( fun(El) -> case xmpp:get_name(El) of <<"x">> -> - case xmpp:get_ns(El) of - ?NS_MUC_USER -> true; - ?NS_MUC_ADMIN -> true; - ?NS_MUC_OWNER -> true; - _ -> false - end; + NS = xmpp:get_ns(El), + Items = if NS == ?NS_MUC_USER; + NS == ?NS_MUC_ADMIN; + NS == ?NS_MUC_OWNER -> + try xmpp:decode(El) of + #muc_user{items = Is} -> Is; + #muc_admin{items = Is} -> Is; + #muc_owner{items = Is} -> Is + catch _:{xmpp_codec, _} -> + [] + end; + true -> + [] + end, + not lists:any( + fun(#muc_item{jid = JID}) -> + JID /= undefined + end, Items); _ -> - false + true end - end), - NewEls = lists:filter( - fun(El) -> - Items = case El of - #muc_user{items = Is} -> Is; - #muc_admin{items = Is} -> Is; - #muc_owner{items = Is} -> Is; - _ -> [] - end, - not lists:any(fun(#muc_item{jid = JID}) -> - JID /= undefined - end, Items) - end, xmpp:get_els(NewPkt)), - xmpp:set_els(NewPkt, NewEls). + end, Els), + xmpp:set_els(Pkt, NewEls). should_archive_peer(C2SState, #archive_prefs{default = Default, @@ -625,7 +609,7 @@ has_no_store_hint(Message) -> -spec is_resent(message(), binary()) -> boolean(). is_resent(Pkt, LServer) -> case xmpp:get_subtag(Pkt, #stanza_id{}) of - #stanza_id{by = #jid{luser = <<>>, lserver = LServer}} -> + #stanza_id{by = #jid{lserver = LServer}} -> true; _ -> false @@ -741,21 +725,22 @@ maybe_activate_mam(LUser, LServer) -> ok end. -select_and_send(LServer, Query, #iq{from = From, to = To} = IQ, MsgType) -> +select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) -> {Msgs, IsComplete, Count} = case MsgType of chat -> - select(LServer, From, From, Query, MsgType); + select(LServer, From, From, Query, RSM, MsgType); {groupchat, _Role, _MUCState} -> - select(LServer, From, To, Query, MsgType) + select(LServer, From, To, Query, RSM, MsgType) end, SortedMsgs = lists:keysort(2, Msgs), send(SortedMsgs, Count, IsComplete, IQ). -select(_LServer, JidRequestor, JidArchive, - #mam_query{start = Start, 'end' = End, rsm = RSM}, +select(_LServer, JidRequestor, JidArchive, Query, RSM, {groupchat, _Role, #state{config = #config{mam = false}, history = History}} = MsgType) -> + Start = proplists:get_value(start, Query), + End = proplists:get_value('end', Query), #lqueue{len = L, queue = Q} = History, Msgs = lists:flatmap( @@ -786,9 +771,9 @@ select(_LServer, JidRequestor, JidArchive, _ -> {Msgs, true, L} end; -select(LServer, JidRequestor, JidArchive, Query, MsgType) -> +select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) -> Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:select(LServer, JidRequestor, JidArchive, Query, MsgType). + Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType). msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer}, MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) -> diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index e913d5a45..8b9c6676c 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -12,7 +12,7 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]). + extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/6]). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). @@ -132,8 +132,10 @@ get_prefs(LUser, LServer) -> select(_LServer, JidRequestor, #jid{luser = LUser, lserver = LServer} = JidArchive, - #mam_query{start = Start, 'end' = End, - with = With, rsm = RSM}, MsgType) -> + Query, RSM, MsgType) -> + Start = proplists:get_value(start, Query), + End = proplists:get_value('end', Query), + With = proplists:get_value(with, Query), LWith = if With /= undefined -> jid:tolower(With); true -> undefined end, diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 1491f70f2..c500745a3 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -14,7 +14,7 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/5]). + extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/6]). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). @@ -51,7 +51,7 @@ delete_old_messages(ServerHost, TimeStamp, Type) -> ok. extended_fields() -> - [#xdata_field{type = 'text-single', var = <<"withtext">>}]. + [{withtext, <<"">>}]. store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir) -> TSinteger = p1_time_compat:system_time(micro_seconds), @@ -124,12 +124,12 @@ get_prefs(LUser, LServer) -> end. select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive, - MAMQuery, MsgType) -> + MAMQuery, RSM, MsgType) -> User = case MsgType of chat -> LUser; {groupchat, _Role, _MUCState} -> jid:to_string(JidArchive) end, - {Query, CountQuery} = make_sql_query(User, LServer, MAMQuery), + {Query, CountQuery} = make_sql_query(User, LServer, MAMQuery, RSM), % TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a % reasonable limit on how many stanzas may be pushed to a client in one % request. If a query returns a number of stanzas greater than this limit @@ -139,7 +139,7 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive, case {ejabberd_sql:sql_query(LServer, Query), ejabberd_sql:sql_query(LServer, CountQuery)} of {{selected, _, Res}, {selected, _, [[Count]]}} -> - {Max, Direction, _} = get_max_direction_id(MAMQuery#mam_query.rsm), + {Max, Direction, _} = get_max_direction_id(RSM), {Res1, IsComplete} = if Max >= 0 andalso Max /= undefined andalso length(Res) > Max -> if Direction == before -> @@ -194,9 +194,11 @@ usec_to_now(Int) -> Sec = Secs rem 1000000, {MSec, Sec, USec}. -make_sql_query(User, LServer, - #mam_query{start = Start, 'end' = End, with = With, - withtext = WithText, rsm = RSM}) -> +make_sql_query(User, LServer, MAMQuery, RSM) -> + Start = proplists:get_value(start, MAMQuery), + End = proplists:get_value('end', MAMQuery), + With = proplists:get_value(with, MAMQuery), + WithText = proplists:get_value(withtext, MAMQuery), {Max, Direction, ID} = get_max_direction_id(RSM), ODBCType = ejabberd_config:get_option( {sql_type, LServer}, diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 2f6d52c36..d007bf3c6 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -99,7 +99,8 @@ -callback remove_expired_messages(binary()) -> {atomic, any()}. -callback remove_old_messages(non_neg_integer(), binary()) -> {atomic, any()}. -callback remove_user(binary(), binary()) -> {atomic, any()}. --callback read_message_headers(binary(), binary()) -> any(). +-callback read_message_headers(binary(), binary()) -> + [{non_neg_integer(), jid(), jid(), undefined | erlang:timestamp(), xmlel()}]. -callback read_message(binary(), binary(), non_neg_integer()) -> {ok, #offline_msg{}} | error. -callback remove_message(binary(), binary(), non_neg_integer()) -> ok | {error, any()}. diff --git a/src/mod_offline_riak.erl b/src/mod_offline_riak.erl index 241a8d650..24d565383 100644 --- a/src/mod_offline_riak.erl +++ b/src/mod_offline_riak.erl @@ -88,7 +88,7 @@ read_message_headers(LUser, LServer) -> fun(#offline_msg{from = From, to = To, packet = Pkt, timestamp = TS}) -> Seq = now_to_integer(TS), - {Seq, From, To, Pkt} + {Seq, From, To, TS, Pkt} end, Rs), lists:keysort(1, Hdrs); _Err -> diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index 2b7a40bff..025aa56f5 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -103,8 +103,9 @@ read_message_headers(LUser, LServer) -> case xml_to_offline_msg(XML) of {ok, #offline_msg{from = From, to = To, + timestamp = TS, packet = El}} -> - [{Seq, From, To, El}]; + [{Seq, From, To, TS, El}]; _ -> [] end diff --git a/src/mod_roster.erl b/src/mod_roster.erl index c344213f3..2da09d317 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -330,7 +330,7 @@ set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) -> transaction( LServer, fun() -> - roster_subscribe_t(LUser, LServer, LJID, Item) + update_roster_t(LUser, LServer, LJID, Item) end). del_roster(LUser, LServer, LJID) -> diff --git a/src/muc_register.erl b/src/muc_register.erl index cddce2b98..c2b951dfc 100644 --- a/src/muc_register.erl +++ b/src/muc_register.erl @@ -12,7 +12,7 @@ -include("muc_register.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_bool(<<"1">>) -> true; dec_bool(<<"0">>) -> false; diff --git a/src/muc_request.erl b/src/muc_request.erl index 4c7becd2e..2d79ba0a5 100644 --- a/src/muc_request.erl +++ b/src/muc_request.erl @@ -12,7 +12,7 @@ -include("muc_request.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_enum(Val, Enums) -> AtomVal = erlang:binary_to_existing_atom(Val, utf8), diff --git a/src/muc_roomconfig.erl b/src/muc_roomconfig.erl index 73ceb649e..7d18bab66 100644 --- a/src/muc_roomconfig.erl +++ b/src/muc_roomconfig.erl @@ -12,7 +12,7 @@ -include("muc_roomconfig.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_int(Val, Min, Max) -> case list_to_integer(binary_to_list(Val)) of diff --git a/src/muc_roominfo.erl b/src/muc_roominfo.erl index 809dcef5b..bd5cb011b 100644 --- a/src/muc_roominfo.erl +++ b/src/muc_roominfo.erl @@ -12,7 +12,7 @@ -include("muc_roominfo.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_int(Val, Min, Max) -> case list_to_integer(binary_to_list(Val)) of diff --git a/src/pubsub_get_pending.erl b/src/pubsub_get_pending.erl index 1a7de6a2d..c1f2ba3ad 100644 --- a/src/pubsub_get_pending.erl +++ b/src/pubsub_get_pending.erl @@ -12,7 +12,7 @@ -include("pubsub_get_pending.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). format_error({form_type_mismatch, Type}) -> <<"FORM_TYPE doesn't match '", Type/binary, "'">>; diff --git a/src/pubsub_node_config.erl b/src/pubsub_node_config.erl index 47ed10b49..e831d6a83 100644 --- a/src/pubsub_node_config.erl +++ b/src/pubsub_node_config.erl @@ -12,7 +12,7 @@ -include("pubsub_node_config.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_int(Val, Min, Max) -> case list_to_integer(binary_to_list(Val)) of diff --git a/src/pubsub_publish_options.erl b/src/pubsub_publish_options.erl index 8d0229071..6e96946fd 100644 --- a/src/pubsub_publish_options.erl +++ b/src/pubsub_publish_options.erl @@ -12,7 +12,7 @@ -include("pubsub_publish_options.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_enum(Val, Enums) -> AtomVal = erlang:binary_to_existing_atom(Val, utf8), diff --git a/src/pubsub_subscribe_authorization.erl b/src/pubsub_subscribe_authorization.erl index e019ed6b9..46538da8d 100644 --- a/src/pubsub_subscribe_authorization.erl +++ b/src/pubsub_subscribe_authorization.erl @@ -12,7 +12,7 @@ -include("pubsub_subscribe_authorization.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_bool(<<"1">>) -> true; dec_bool(<<"0">>) -> false; diff --git a/src/pubsub_subscribe_options.erl b/src/pubsub_subscribe_options.erl index 446a84a00..02c046995 100644 --- a/src/pubsub_subscribe_options.erl +++ b/src/pubsub_subscribe_options.erl @@ -12,7 +12,7 @@ -include("pubsub_subscribe_options.hrl"). --export_type([{property, 0}, {result, 0}, {form, 0}]). +-export_type([property/0, result/0, form/0]). dec_enum(Val, Enums) -> AtomVal = erlang:binary_to_existing_atom(Val, utf8), diff --git a/src/xmpp_util.erl b/src/xmpp_util.erl index 57440b50e..22b8ea597 100644 --- a/src/xmpp_util.erl +++ b/src/xmpp_util.erl @@ -11,7 +11,8 @@ %% API -export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1, is_standalone_chat_state/1, get_xdata_values/2, - has_xdata_var/2, make_adhoc_response/1, make_adhoc_response/2, + set_xdata_field/2, has_xdata_var/2, + make_adhoc_response/1, make_adhoc_response/2, decode_timestamp/1, encode_timestamp/1]). -include("xmpp.hrl"). @@ -78,6 +79,12 @@ get_xdata_values(Var, #xdata{fields = Fields}) -> false -> [] end. +-spec set_xdata_field(xdata_field(), xdata()) -> xdata(). +set_xdata_field(Field, #xdata{fields = Fields} = X) -> + NewFields = lists:keystore(Field#xdata_field.var, #xdata_field.var, + Fields, Field), + X#xdata{fields = NewFields}. + -spec has_xdata_var(binary(), xdata()) -> boolean(). has_xdata_var(Var, #xdata{fields = Fields}) -> lists:keymember(Var, #xdata_field.var, Fields). -- cgit v1.2.3