diff --git a/doc/guide.html b/doc/guide.html index 3b4302871..50daeb690 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -1805,7 +1805,7 @@ all entries end with a comma: mod_shared_rosterShared roster managementmod_roster or   mod_roster_odbc mod_statsStatistics Gathering (XEP-0039)  -mod_timeEntity Time (XEP-0090)  +mod_timeEntity Time (XEP-0202mod_vcardvcard-temp (XEP-0054mod_vcard_ldapvcard-temp (XEP-0054)LDAP server mod_vcard_odbcvcard-temp (XEP-0054)supported DB (*) @@ -2914,7 +2914,7 @@ by sending: </iq>

3.3.22  mod_time

-

This module features support for Entity Time (XEP-0090). By using this XEP, +

This module features support for Entity Time (XEP-0202). By using this XEP, you are able to discover the time at another entity’s location.

Options:

iqdisc
This specifies diff --git a/doc/guide.tex b/doc/guide.tex index ca8eeec5e..96861581d 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -2398,7 +2398,7 @@ The following table lists all modules included in \ejabberd{}. \hline \ahrefloc{modsharedroster}{\modsharedroster{}} & Shared roster management & \modroster{} or \\ & & \modrosterodbc\\ \hline \ahrefloc{modstats}{\modstats{}} & Statistics Gathering (\xepref{0039}) & \\ - \hline \ahrefloc{modtime}{\modtime{}} & Entity Time (\xepref{0090}) & \\ + \hline \ahrefloc{modtime}{\modtime{}} & Entity Time (\xepref{0202}) & \\ \hline \ahrefloc{modvcard}{\modvcard{}} & vcard-temp (\xepref{0054}) & \\ \hline \ahrefloc{modvcardldap}{\modvcardldap{}} & vcard-temp (\xepref{0054}) & LDAP server \\ \hline \ahrefloc{modvcard}{\modvcardodbc{}} & vcard-temp (\xepref{0054}) & supported DB (*) \\ @@ -3751,9 +3751,9 @@ in order to get the statistics. Here they are: \end{itemize} \makesubsection{modtime}{\modtime{}} -\ind{modules!\modtime{}}\ind{protocols!XEP-0090: Entity Time} +\ind{modules!\modtime{}}\ind{protocols!XEP-0202: Entity Time} -This module features support for Entity Time (\xepref{0090}). By using this XEP, +This module features support for Entity Time (\xepref{0202}). By using this XEP, you are able to discover the time at another entity's location. Options: diff --git a/src/ejd2odbc.erl b/src/ejd2odbc.erl index 12fc607e3..7b5c8ec43 100644 --- a/src/ejd2odbc.erl +++ b/src/ejd2odbc.erl @@ -131,7 +131,14 @@ export_offline(Server, Output) -> NewPacket = {xmlelement, Name, Attrs2, Els ++ [jlib:timestamp_to_xml( - calendar:now_to_universal_time(TimeStamp))]}, + calendar:now_to_universal_time(TimeStamp), + utc, + jlib:make_jid("", Server, ""), + "Offline Storage"), + %% TODO: Delete the next three lines once XEP-0091 is Obsolete + jlib:timestamp_to_xml( + calendar:now_to_universal_time( + TimeStamp))]}, XML = ejabberd_odbc:escape( lists:flatten( diff --git a/src/jlib.erl b/src/jlib.erl index 03bf99805..de75ca0a9 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -55,8 +55,10 @@ is_iq_request_type/1, iq_to_xml/1, parse_xdata_submit/1, - timestamp_to_iso/1, - timestamp_to_xml/1, + timestamp_to_iso/1, % TODO: Remove once XEP-0091 is Obsolete + timestamp_to_iso/2, + timestamp_to_xml/4, + timestamp_to_xml/1, % TODO: Remove once XEP-0091 is Obsolete now_to_utc_string/1, now_to_local_string/1, datetime_string_to_timestamp/1, @@ -553,14 +555,45 @@ rsm_encode_count(Count, Arr)-> i2l(I) when is_integer(I) -> integer_to_list(I); i2l(L) when is_list(L) -> L. +%% Timezone = utc | {Hours, Minutes} +%% Hours = integer() +%% Minutes = integer() +timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}, Timezone) -> + Timestamp_string = + lists:flatten( + io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second])), + Timezone_string = + case Timezone of + utc -> "Z"; + {TZh, TZm} -> + Sign = case TZh >= 0 of + true -> "+"; + false -> "-" + end, + io_lib:format("~s~2..0w:~2..0w", [Sign, abs(TZh),TZm]) + end, + {Timestamp_string, Timezone_string}. + timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) -> lists:flatten( io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", [Year, Month, Day, Hour, Minute, Second])). +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), + {xmlelement, "delay", + [{"xmlns", ?NS_DELAY}, + {"from", From}, + {"stamp", T_string ++ Tz_string}], + Text}. + +%% TODO: Remove this function once XEP-0091 is Obsolete timestamp_to_xml({{Year, Month, Day}, {Hour, Minute, Second}}) -> {xmlelement, "x", - [{"xmlns", ?NS_DELAY}, + [{"xmlns", ?NS_DELAY91}, {"stamp", lists:flatten( io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", [Year, Month, Day, Hour, Minute, Second]))}], diff --git a/src/jlib.hrl b/src/jlib.hrl index fda3e2ae1..0a01a7b75 100644 --- a/src/jlib.hrl +++ b/src/jlib.hrl @@ -30,11 +30,13 @@ -define(NS_PRIVACY, "jabber:iq:privacy"). -define(NS_PRIVATE, "jabber:iq:private"). -define(NS_VERSION, "jabber:iq:version"). --define(NS_TIME, "jabber:iq:time"). +-define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete +-define(NS_TIME, "urn:xmpp:time"). -define(NS_LAST, "jabber:iq:last"). -define(NS_XDATA, "jabber:x:data"). -define(NS_IQDATA, "jabber:iq:data"). --define(NS_DELAY, "jabber:x:delay"). +-define(NS_DELAY91, "jabber:x:delay"). % TODO: Remove once XEP-0091 is Obsolete +-define(NS_DELAY, "urn:xmpp:delay"). -define(NS_EXPIRE, "jabber:x:expire"). -define(NS_EVENT, "jabber:x:event"). -define(NS_XCONFERENCE, "jabber:x:conference"). @@ -71,6 +73,7 @@ -define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth"). -define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register"). -define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress"). +-define(NS_FEATURE_MSGOFFLINE, "msgoffline"). -define(NS_COMPRESS, "http://jabber.org/protocol/compress"). diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index d110ad2d1..a6ee81d3e 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -546,6 +546,7 @@ handle_event({service_message, Msg}, _StateName, StateData) -> end, ?DICT:to_list(StateData#state.users)), NSD = add_message_to_history("", + StateData#state.jid, MessagePkt, StateData), {next_state, normal_state, NSD}; @@ -787,6 +788,7 @@ process_groupchat_message(From, {xmlelement, "message", Attrs, _Els} = Packet, ?DICT:to_list(StateData#state.users)), NewStateData2 = add_message_to_history(FromNick, + From, Packet, NewStateData1), {next_state, normal_state, NewStateData2}; @@ -1986,7 +1988,7 @@ lqueue_to_list(#lqueue{queue = Q1}) -> queue:to_list(Q1). -add_message_to_history(FromNick, Packet, StateData) -> +add_message_to_history(FromNick, FromJID, Packet, StateData) -> HaveSubject = case xml:get_subtag(Packet, "subject") of false -> false; @@ -1994,8 +1996,18 @@ add_message_to_history(FromNick, Packet, StateData) -> true end, TimeStamp = calendar:now_to_universal_time(now()), + %% Chatroom history is stored as XMPP packets, so + %% the decision to include the original sender's JID or not is based on the + %% chatroom configuration when the message was originally sent. + %% Also, if the chatroom is anonymous, even moderators will not get the real JID + SenderJid = case ((StateData#state.config)#config.anonymous) of + true -> StateData#state.jid; + false -> FromJID + end, TSPacket = append_subtags(Packet, - [jlib:timestamp_to_xml(TimeStamp)]), + [jlib:timestamp_to_xml(TimeStamp, utc, SenderJid, ""), + %% TODO: Delete the next line once XEP-0091 is Obsolete + jlib:timestamp_to_xml(TimeStamp)]), SPacket = jlib:replace_from_to( jlib:jid_replace_resource(StateData#state.jid, FromNick), StateData#state.jid, diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 0a79d6a28..9c375997a 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -293,6 +293,13 @@ resend_offline_messages(User, Server) -> {xmlelement, Name, Attrs, Els ++ [jlib:timestamp_to_xml( + calendar:now_to_universal_time( + R#offline_msg.timestamp), + utc, + jlib:make_jid("", Server, ""), + "Offline Storage"), + %% TODO: Delete the next three lines once XEP-0091 is Obsolete + jlib:timestamp_to_xml( calendar:now_to_universal_time( R#offline_msg.timestamp))]}} end, @@ -322,7 +329,14 @@ pop_offline_messages(Ls, User, Server) -> {xmlelement, Name, Attrs, Els ++ [jlib:timestamp_to_xml( - calendar:now_to_universal_time( + calendar:now_to_universal_time( + R#offline_msg.timestamp), + utc, + jlib:make_jid("", Server, ""), + "Offline Storage"), + %% TODO: Delete the next three lines once XEP-0091 is Obsolete + jlib:timestamp_to_xml( + calendar:now_to_universal_time( R#offline_msg.timestamp))]}} end, lists:filter( diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl index 9add941df..891ec6419 100644 --- a/src/mod_offline_odbc.erl +++ b/src/mod_offline_odbc.erl @@ -112,6 +112,13 @@ loop(Host, AccessMaxOfflineMsgs) -> Els ++ [jlib:timestamp_to_xml( calendar:now_to_universal_time( + M#offline_msg.timestamp), + utc, + jlib:make_jid("", Host, ""), + "Offline Storage"), + %% TODO: Delete the next three lines once XEP-0091 is Obsolete + jlib:timestamp_to_xml( + calendar:now_to_universal_time( M#offline_msg.timestamp))]}, XML = ejabberd_odbc:escape( diff --git a/src/mod_time.erl b/src/mod_time.erl index 1311b6957..0b4133692 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -31,6 +31,7 @@ -export([start/2, stop/1, + process_local_iq90/3, % TODO: Remove once XEP-0090 is Obsolete process_local_iq/3]). -include("ejabberd.hrl"). @@ -39,13 +40,19 @@ start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), + %% TODO: Remove the next two lines once XEP-0090 is Obsolete + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_TIME90, + ?MODULE, process_local_iq90, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_TIME, ?MODULE, process_local_iq, IQDisc). stop(Host) -> + %% TODO: Remove the next line once XEP-0090 is Obsolete + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_TIME90), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_TIME). -process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> +%% TODO: Remove this function once XEP-0090 is Obsolete +process_local_iq90(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> case Type of set -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; @@ -53,9 +60,31 @@ process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> UTC = jlib:timestamp_to_iso(calendar:universal_time()), IQ#iq{type = result, sub_el = [{xmlelement, "query", - [{"xmlns", ?NS_TIME}], + [{"xmlns", ?NS_TIME90}], [{xmlelement, "utc", [], [{xmlcdata, UTC}]}]}]} end. +process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> + case Type of + set -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; + get -> + Now = now(), + Now_universal = calendar:now_to_universal_time(Now), + Now_local = calendar:now_to_local_time(Now), + {UTC, UTC_diff} = jlib:timestamp_to_iso(Now_universal, utc), + Seconds_diff = calendar:datetime_to_gregorian_seconds(Now_local) + - calendar:datetime_to_gregorian_seconds(Now_universal), + {Hd, Md, _} = calendar:seconds_to_time(Seconds_diff), + {_, TZO_diff} = jlib:timestamp_to_iso({{0, 0, 0}, {0, 0, 0}}, {Hd, Md}), + IQ#iq{type = result, + sub_el = [{xmlelement, "time", + [{"xmlns", ?NS_TIME}], + [{xmlelement, "tzo", [], + [{xmlcdata, TZO_diff}]}, + {xmlelement, "utc", [], + [{xmlcdata, UTC ++ UTC_diff}]}]}]} + end. +