From 9899935e42a0f12e9a5cbd52140680e20e4db45f Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 7 Dec 2014 15:55:18 +0100 Subject: [PATCH] Improve interface for adding timestamps Provide a simpler interface for adding and timestamps to stanzas. This also makes sure that only one tag and one tag is added to a given stanza. --- src/ejabberd_c2s.erl | 22 +++----- src/jlib.erl | 108 ++++++++++++++++++++++++++++------------ src/mod_muc_room.erl | 9 ++-- src/mod_offline.erl | 78 ++++++++--------------------- src/mod_pubsub.erl | 16 ++---- src/mod_pubsub_odbc.erl | 16 ++---- test/ejabberd_SUITE.erl | 5 +- 7 files changed, 120 insertions(+), 134 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 3794ac350..e52378c6d 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -2049,13 +2049,9 @@ process_presence_probe(From, To, StateData) -> ?SETS:is_element(LBFrom, StateData#state.pres_f)), if Cond -> - Timestamp = StateData#state.pres_timestamp, - Packet = xml:append_subtags( - StateData#state.pres_last, - %% To is the one sending the presence (the target of the probe) - [jlib:timestamp_to_xml(Timestamp, utc, To, <<"">>), - %% TODO: Delete the next line once XEP-0091 is Obsolete - jlib:timestamp_to_xml(Timestamp)]), + %% To is the one sending the presence (the probe target) + Packet = jlib:add_delay_info(StateData#state.pres_last, To, + StateData#state.pres_timestamp), case privacy_check_packet(StateData, To, From, Packet, out) of deny -> ok; @@ -2107,12 +2103,11 @@ presence_update(From, Packet, StateData) -> OldPresence -> get_priority_from_presence(OldPresence) end, NewPriority = get_priority_from_presence(Packet), - Timestamp = calendar:now_to_universal_time(now()), update_priority(NewPriority, Packet, StateData), FromUnavail = (StateData#state.pres_last == undefined), ?DEBUG("from unavail = ~p~n", [FromUnavail]), NewStateData = StateData#state{pres_last = Packet, - pres_timestamp = Timestamp}, + pres_timestamp = now()}, NewState = if FromUnavail -> ejabberd_hooks:run(user_available_hook, NewStateData#state.server, @@ -3054,14 +3049,9 @@ csi_filter_stanza(#state{csi_state = CsiState, jid = JID} = StateData, StateData2#state{csi_state = CsiState} end. -csi_queue_add(#state{csi_queue = Queue, server = Host} = StateData, - #xmlel{children = Els} = Stanza) -> +csi_queue_add(#state{csi_queue = Queue, server = Host} = StateData, Stanza) -> From = xml:get_tag_attr_s(<<"from">>, Stanza), - Time = calendar:now_to_universal_time(os:timestamp()), - DelayTag = [jlib:timestamp_to_xml(Time, utc, - jlib:make_jid(<<"">>, Host, <<"">>), - <<"Client Inactive">>)], - NewStanza = Stanza#xmlel{children = Els ++ DelayTag}, + NewStanza = jlib:add_delay_info(Stanza, Host, now(), <<"Client Inactive">>), case length(StateData#state.csi_queue) >= csi_max_queue(StateData) of true -> csi_queue_add(csi_queue_flush(StateData), NewStanza); false -> diff --git a/src/jlib.erl b/src/jlib.erl index be1da3fd0..74d8503e4 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -41,10 +41,11 @@ jid_remove_resource/1, jid_replace_resource/2, get_iq_namespace/1, iq_query_info/1, iq_query_or_response_info/1, is_iq_request_type/1, - iq_to_xml/1, parse_xdata_submit/1, timestamp_to_iso/1, - timestamp_to_iso/2, timestamp_to_xml/4, - timestamp_to_xml/1, now_to_utc_string/1, - now_to_local_string/1, datetime_string_to_timestamp/1, + iq_to_xml/1, parse_xdata_submit/1, + add_delay_info/3, add_delay_info/4, + timestamp_to_iso/1, timestamp_to_iso/2, + now_to_utc_string/1, now_to_local_string/1, + datetime_string_to_timestamp/1, term_to_base64/1, base64_to_term/1, decode_base64/1, encode_base64/1, ip_to_list/1, rsm_encode/1, rsm_encode/2, rsm_decode/1, @@ -600,6 +601,78 @@ rsm_encode_count(Count, Arr) -> children = [{xmlcdata, i2l(Count)}]} | Arr]. +-spec add_delay_info(xmlel(), erlang:timestamp(), binary()) -> xmlel(). + +add_delay_info(El, From, Time) -> + add_delay_info(El, From, Time, <<"">>). + +-spec add_delay_info(xmlel(), erlang:timestamp(), binary(), + binary()) -> xmlel(). + +add_delay_info(El, From, Time, Desc) -> + %% TODO: Remove support for , XEP-0091 is obsolete. + El1 = add_delay_info(El, From, Time, Desc, <<"delay">>, ?NS_DELAY), + El2 = add_delay_info(El1, From, Time, Desc, <<"x">>, ?NS_DELAY91), + El2. + +-spec add_delay_info(xmlel(), erlang:timestamp(), binary(), binary(), binary(), + binary()) -> xmlel(). + +add_delay_info(El, From, Time, Desc, Name, XMLNS) -> + case xml:get_subtag_with_xmlns(El, Name, XMLNS) of + false -> + %% Add new tag + DelayTag = create_delay_tag(calendar:now_to_universal_time(Time), + From, Desc, XMLNS), + xml:append_subtags(El, [DelayTag]); + DelayTag -> + %% Update existing tag + NewDelayTag = + case {xml:get_tag_cdata(DelayTag), Desc} of + {<<"">>, <<"">>} -> + DelayTag; + {OldDesc, <<"">>} -> + DelayTag#xmlel{children = [{xmlcdata, OldDesc}]}; + {<<"">>, NewDesc} -> + DelayTag#xmlel{children = [{xmlcdata, NewDesc}]}; + {OldDesc, NewDesc} -> + case binary:match(OldDesc, NewDesc) of + nomatch -> + FinalDesc = <>, + DelayTag#xmlel{children = [{xmlcdata, FinalDesc}]}; + _ -> + DelayTag#xmlel{children = [{xmlcdata, OldDesc}]} + end + end, + NewEl = xml:remove_subtags(El, Name, {<<"xmlns">>, XMLNS}), + xml:append_subtags(NewEl, [NewDelayTag]) + end. + +-spec create_delay_tag(calendar:datetime(), jid() | binary(), + binary(), binary()) -> xmlel() | error. + +create_delay_tag(DateTime, FromJID, Desc, XMLNS) when is_tuple(FromJID) -> + From = jlib:jid_to_string(FromJID), + {Name, Stamp} = case XMLNS of + ?NS_DELAY -> + {T, Tz} = timestamp_to_iso(DateTime, utc), + {<<"delay">>, <>}; + ?NS_DELAY91 -> + {<<"x">>, timestamp_to_iso(DateTime)} + end, + Children = case Desc of + <<"">> -> []; + _ -> [{xmlcdata, Desc}] + end, + #xmlel{name = Name, + attrs = + [{<<"xmlns">>, XMLNS}, {<<"from">>, From}, + {<<"stamp">>, Stamp}], + children = Children}; +create_delay_tag(DateTime, Host, Desc, XMLNS) when is_binary(Host) -> + FromJID = jlib:make_jid(<<"">>, Host, <<"">>), + create_delay_tag(DateTime, FromJID, Desc, XMLNS). + -type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc. %% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes} @@ -634,33 +707,6 @@ timestamp_to_iso({{Year, Month, Day}, iolist_to_binary(io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", [Year, Month, Day, Hour, Minute, Second])). --spec timestamp_to_xml(calendar:datetime(), tz(), jid(), binary()) -> xmlel(). - -timestamp_to_xml(DateTime, Timezone, FromJID, Desc) -> - {T_string, Tz_string} = timestamp_to_iso(DateTime, - Timezone), - Text = [{xmlcdata, Desc}], - From = jlib:jid_to_string(FromJID), -%% TODO: Remove this function once XEP-0091 is Obsolete - #xmlel{name = <<"delay">>, - attrs = - [{<<"xmlns">>, ?NS_DELAY}, {<<"from">>, From}, - {<<"stamp">>, <>}], - children = Text}. - --spec timestamp_to_xml(calendar:datetime()) -> xmlel(). - -timestamp_to_xml({{Year, Month, Day}, - {Hour, Minute, Second}}) -> - #xmlel{name = <<"x">>, - attrs = - [{<<"xmlns">>, ?NS_DELAY91}, - {<<"stamp">>, - iolist_to_binary(io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, - Second]))}], - children = []}. - -spec now_to_utc_string(erlang:timestamp()) -> binary(). now_to_utc_string({MegaSecs, Secs, MicroSecs}) -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 0974950b7..95d437bfc 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -2438,24 +2438,21 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) -> false -> false; _ -> true end, - TimeStamp = calendar:now_to_universal_time(now()), + TimeStamp = now(), SenderJid = case (StateData#state.config)#config.anonymous of true -> StateData#state.jid; false -> FromJID end, - TSPacket = xml:append_subtags(Packet, - [jlib:timestamp_to_xml(TimeStamp, utc, - SenderJid, <<"">>), - jlib:timestamp_to_xml(TimeStamp)]), + TSPacket = jlib:add_delay_info(Packet, SenderJid, TimeStamp), SPacket = jlib:replace_from_to(jlib:jid_replace_resource(StateData#state.jid, FromNick), StateData#state.jid, TSPacket), Size = element_size(SPacket), Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, - TimeStamp, Size}, + calendar:now_to_universal_time(TimeStamp), Size}, StateData#state.history), add_to_log(text, {FromNick, Packet}, StateData), StateData#state{history = Q1}. diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 91d31a75d..4ac3f18f8 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -215,26 +215,15 @@ store_offline_msg(Host, {User, _Server}, Msgs, Len, MaxOfflineMsgs, odbc) -> ejabberd_odbc:escape((M#offline_msg.to)#jid.luser), From = M#offline_msg.from, To = M#offline_msg.to, - #xmlel{name = Name, attrs = Attrs, - children = Els} = - M#offline_msg.packet, - Attrs2 = - jlib:replace_from_to_attrs(jlib:jid_to_string(From), - jlib:jid_to_string(To), - Attrs), - Packet = #xmlel{name = Name, - attrs = Attrs2, - children = - Els ++ - [jlib:timestamp_to_xml(calendar:now_to_universal_time(M#offline_msg.timestamp), - utc, - jlib:make_jid(<<"">>, - Host, - <<"">>), - <<"Offline Storage">>), - jlib:timestamp_to_xml(calendar:now_to_universal_time(M#offline_msg.timestamp))]}, + Packet = + jlib:replace_from_to(From, To, + M#offline_msg.packet), + NewPacket = + jlib:add_delay_info(Packet, Host, + M#offline_msg.timestamp, + <<"Offline Storage">>), XML = - ejabberd_odbc:escape(xml:element_to_binary(Packet)), + ejabberd_odbc:escape(xml:element_to_binary(NewPacket)), odbc_queries:add_spool_sql(Username, XML) end, Msgs), @@ -432,15 +421,12 @@ resend_offline_messages(User, Server) -> case mnesia:transaction(F) of {atomic, Rs} -> lists:foreach(fun (R) -> - #xmlel{name = Name, attrs = Attrs, - children = Els} = - R#offline_msg.packet, ejabberd_sm ! {route, R#offline_msg.from, R#offline_msg.to, - #xmlel{name = Name, attrs = Attrs, - children = - Els ++ - [jlib:timestamp_to_xml(calendar:now_to_universal_time(R#offline_msg.timestamp))]}} + jlib:add_delay_info(R#offline_msg.packet, + LServer, + R#offline_msg.timestamp, + <<"Offline Storage">>)} end, lists:keysort(#offline_msg.timestamp, Rs)); _ -> ok @@ -686,19 +672,9 @@ get_offline_els(LUser, LServer, odbc) -> end. offline_msg_to_route(LServer, #offline_msg{} = R) -> - El = #xmlel{children = Els} = R#offline_msg.packet, {route, R#offline_msg.from, R#offline_msg.to, - El#xmlel{children = - Els ++ - [jlib:timestamp_to_xml( - calendar:now_to_universal_time( - R#offline_msg.timestamp), - utc, - jlib:make_jid(<<"">>, LServer, <<"">>), - <<"Offline Storage">>), - jlib:timestamp_to_xml( - calendar:now_to_universal_time( - R#offline_msg.timestamp))]}}; + jlib:add_delay_info(R#offline_msg.packet, LServer, R#offline_msg.timestamp, + <<"Offline Storage">>)}; offline_msg_to_route(_LServer, #xmlel{} = El) -> To = jlib:string_to_jid(xml:get_tag_attr_s(<<"to">>, El)), From = jlib:string_to_jid(xml:get_tag_attr_s(<<"from">>, El)), @@ -1109,26 +1085,14 @@ export(_Server) -> packet = Packet}) when LServer == Host -> Username = ejabberd_odbc:escape(LUser), - #xmlel{name = Name, attrs = Attrs, children = Els} = - Packet, - Attrs2 = - jlib:replace_from_to_attrs(jlib:jid_to_string(From), - jlib:jid_to_string(To), - Attrs), - NewPacket = #xmlel{name = Name, attrs = Attrs2, - children = - Els ++ - [jlib:timestamp_to_xml( - calendar:now_to_universal_time(TimeStamp), - utc, - jlib:make_jid(<<"">>, - LServer, - <<"">>), - <<"Offline Storage">>), - jlib:timestamp_to_xml( - calendar:now_to_universal_time(TimeStamp))]}, + Packet1 = + jlib:replace_from_to(jlib:jid_to_string(From), + jlib:jid_to_string(To), Packet), + Packet2 = + jlib:add_delay_info(Packet1, LServer, TimeStamp, + <<"Offline Storage">>), XML = - ejabberd_odbc:escape(xml:element_to_binary(NewPacket)), + ejabberd_odbc:escape(xml:element_to_binary(Packet2)), [[<<"delete from spool where username='">>, Username, <<"';">>], [<<"insert into spool(username, xml) values ('">>, Username, <<"', '">>, XML, <<"');">>]]; diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 5f16fed7e..f7d30558e 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4275,21 +4275,15 @@ payload_xmlelements([_ | Tail], Count) -> %% @spec (Els) -> stanza() %% Els = [xmlelement()] %% @doc

Build pubsub event stanza

-event_stanza(Els) -> event_stanza_withmoreels(Els, []). - -event_stanza_with_delay(Els, ModifNow, ModifUSR) -> - DateTime = calendar:now_to_datetime(ModifNow), - MoreEls = [jlib:timestamp_to_xml(DateTime, utc, - ModifUSR, <<"">>)], - event_stanza_withmoreels(Els, MoreEls). - -event_stanza_withmoreels(Els, MoreEls) -> +event_stanza(Els) -> #xmlel{name = <<"message">>, attrs = [], children = [#xmlel{name = <<"event">>, attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}], - children = Els} - | MoreEls]}. + children = Els}]}. + +event_stanza_with_delay(Els, ModifNow, ModifUSR) -> + jlib:add_delay_info(event_stanza(Els), ModifUSR, ModifNow). %%%%%% broadcast functions diff --git a/src/mod_pubsub_odbc.erl b/src/mod_pubsub_odbc.erl index c953e26a9..b3f2db7c0 100644 --- a/src/mod_pubsub_odbc.erl +++ b/src/mod_pubsub_odbc.erl @@ -3925,21 +3925,15 @@ payload_xmlelements([_ | Tail], Count) -> %% @spec (Els) -> stanza() %% Els = [xmlelement()] %% @doc

Build pubsub event stanza

-event_stanza(Els) -> event_stanza_withmoreels(Els, []). - -event_stanza_with_delay(Els, ModifNow, ModifUSR) -> - DateTime = calendar:now_to_datetime(ModifNow), - MoreEls = [jlib:timestamp_to_xml(DateTime, utc, - ModifUSR, <<"">>)], - event_stanza_withmoreels(Els, MoreEls). - -event_stanza_withmoreels(Els, MoreEls) -> +event_stanza(Els) -> #xmlel{name = <<"message">>, attrs = [], children = [#xmlel{name = <<"event">>, attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}], - children = Els} - | MoreEls]}. + children = Els}]}. + +event_stanza_with_delay(Els, ModifNow, ModifUSR) -> + jlib:add_delay_info(event_stanza(Els), ModifUSR, ModifNow). %%%%%% broadcast functions diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 50fa70bd9..380ed606f 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -801,7 +801,7 @@ pubsub(Config) -> node = Node, jid = my_jid(Config)}}]}), ?recv2( - #message{sub_els = [#pubsub_event{}, #delay{}]}, + #message{sub_els = [#pubsub_event{}, #delay{}, #legacy_delay{}]}, #iq{type = result, id = I1}), %% Get subscriptions true = lists:member(?PUBSUB("retrieve-subscriptions"), Features), @@ -1588,7 +1588,8 @@ client_state_slave(Config) -> body = [#text{data = <<"body">>}]} = recv(), wait_for_master(Config), send(Config, #csi{type = active}), - ?recv2(#presence{from = Peer, type = unavailable, sub_els = [#delay{}]}, + ?recv2(#presence{from = Peer, type = unavailable, + sub_els = [#delay{}, #legacy_delay{}]}, #message{from = Peer, thread = <<"1">>, sub_els = [#chatstate{type = active}]}), disconnect(Config).