From 273886701bd8d033330037ff7997b7bfe6e3f501 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sun, 18 Jan 2004 20:42:09 +0000 Subject: [PATCH] * src/ejabberd_ctl.erl: Added commands for backup processing * src/ejabberd_c2s.erl: Added processing of xml:lang according to latest XMPP-IM draft * src/xml.erl: Added replace_tag_attr/3 function * src/mod_roster.erl: Added auto-reply on incoming subscription request according to latest XMPP-IM draft * src/mod_offline.erl: Added pop_offline_messages/1 function * src/ejabberd_c2s.erl: Updated sending of offline messages SVN Revision: 200 --- ChangeLog | 15 +++++++++++ src/ejabberd_c2s.erl | 64 +++++++++++++++++++++++++++++++------------- src/ejabberd_ctl.erl | 44 ++++++++++++++++++++++++++++-- src/mod_offline.erl | 29 +++++++++++++++++++- src/mod_roster.erl | 48 ++++++++++++++++++++++++++++----- src/xml.erl | 10 ++++++- 6 files changed, 181 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index be9539f4d..29c078b74 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2004-01-18 Alexey Shchepin + + * src/ejabberd_ctl.erl: Added commands for backup processing + + * src/ejabberd_c2s.erl: Added processing of xml:lang according to + latest XMPP-IM draft + + * src/xml.erl: Added replace_tag_attr/3 function + + * src/mod_roster.erl: Added auto-reply on incoming subscription + request according to latest XMPP-IM draft + + * src/mod_offline.erl: Added pop_offline_messages/1 function + * src/ejabberd_c2s.erl: Updated sending of offline messages + 2004-01-17 Alexey Shchepin * src/mod_muc/mod_muc_room.erl: Bugfix, updated error codes diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 09ab2a7b7..44da28c0e 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -54,7 +54,8 @@ pres_last, pres_pri, pres_timestamp, pres_invis = false, - privacy_list = none}). + privacy_list = none, + lang}). %-define(DBGFSM, true). @@ -127,6 +128,7 @@ init([{SockMod, Socket}, Opts]) -> wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> case xml:get_attr_s("xmlns:stream", Attrs) of ?NS_STREAM -> + Lang = xml:get_attr_s("xml:lang", Attrs), case xml:get_attr_s("version", Attrs) of "1.0" -> Header = io_lib:format(?STREAM_HEADER, @@ -149,7 +151,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> [{"xmlns", ?NS_SASL}], Mechs}]}), {next_state, wait_for_sasl_auth, - StateData#state{sasl_state = SASLState}}; + StateData#state{sasl_state = SASLState, + lang = Lang}}; _ -> case StateData#state.resource of "" -> @@ -158,12 +161,14 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> {xmlelement, "stream:features", [], [{xmlelement, "bind", [{"xmlns", ?NS_BIND}], []}]}), - {next_state, wait_for_bind, StateData}; + {next_state, wait_for_bind, + StateData#state{lang = Lang}}; _ -> send_element( StateData, {xmlelement, "stream:features", [], []}), - {next_state, wait_for_session, StateData} + {next_state, wait_for_session, + StateData#state{lang = Lang}} end end; _ -> @@ -171,7 +176,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> ?STREAM_HEADER, [StateData#state.streamid, ?MYNAME, ""]), send_text(StateData, Header), - {next_state, wait_for_auth, StateData} + {next_state, wait_for_auth, StateData#state{lang = Lang}} end; _ -> Header = io_lib:format( @@ -541,9 +546,7 @@ session_established({xmlstreamelement, El}, StateData) -> {xmlelement, Name, Attrs, _Els} = El, User = StateData#state.user, Server = StateData#state.server, - %FromJID = {User, - % Server, - % StateData#state.resource}, + % TODO: check 'from' attribute in stanza FromJID = StateData#state.jid, To = xml:get_attr_s("to", Attrs), ToJID = case To of @@ -552,6 +555,16 @@ session_established({xmlstreamelement, El}, StateData) -> _ -> jlib:string_to_jid(To) end, + NewEl = case xml:get_attr_s("xml:lang", Attrs) of + "" -> + case StateData#state.lang of + "" -> El; + Lang -> + xml:replace_tag_attr("xml:lang", Lang, El) + end; + _ -> + El + end, NewState = case ToJID of error -> @@ -559,7 +572,7 @@ session_established({xmlstreamelement, El}, StateData) -> "error" -> StateData; "result" -> StateData; _ -> - Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED), + Err = jlib:make_error_reply(NewEl, ?ERR_JID_MALFORMED), send_element(StateData, Err), StateData end; @@ -571,29 +584,29 @@ session_established({xmlstreamelement, El}, StateData) -> server = Server, resource = ""} -> ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)", - [FromJID, El, StateData]), - presence_update(FromJID, El, StateData); + [FromJID, NewEl, StateData]), + presence_update(FromJID, NewEl, StateData); _ -> - presence_track(FromJID, ToJID, El, StateData) + presence_track(FromJID, ToJID, NewEl, StateData) end; "iq" -> case StateData#state.privacy_list of none -> - ejabberd_router:route(FromJID, ToJID, El), + ejabberd_router:route(FromJID, ToJID, NewEl), StateData; _PrivList -> - case jlib:iq_query_info(El) of + case jlib:iq_query_info(NewEl) of #iq{xmlns = ?NS_PRIVACY} = IQ -> process_privacy_iq( FromJID, ToJID, IQ, StateData); _ -> ejabberd_router:route( - FromJID, ToJID, El), + FromJID, ToJID, NewEl), StateData end end; "message" -> - ejabberd_router:route(FromJID, ToJID, El), + ejabberd_router:route(FromJID, ToJID, NewEl), StateData; _ -> StateData @@ -987,8 +1000,7 @@ presence_update(From, Packet, StateData) -> FromUnavail -> % TODO: watching ourself - catch mod_offline:resend_offline_messages( - StateData#state.user), + resend_offline_messages(StateData), presence_broadcast_first( From, StateData#state{pres_last = Packet, pres_invis = false @@ -1271,3 +1283,19 @@ process_privacy_iq(From, To, NewStateData. +resend_offline_messages(StateData) -> + case catch mod_offline:pop_offline_messages(StateData#state.user) of + {'EXIT', _Reason} -> + ok; + Rs when list(Rs) -> + lists:foreach( + fun({route, From, To, {xmlelement, Name, Attrs, Els}}) -> + Attrs2 = jlib:replace_from_to_attrs( + jlib:jid_to_string(From), + jlib:jid_to_string(To), + Attrs), + send_element(StateData, {xmlelement, Name, Attrs2, Els}) + end, Rs) + end. + + diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 39141f3a6..ea71f7575 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -67,6 +67,43 @@ process(Node, ["unregister", User]) -> [User, Node, Reason]) end; +process(Node, ["backup", Path]) -> + case rpc:call(Node, mnesia, backup, [Path]) of + {atomic, ok} -> + ok; + {error, Reason} -> + io:format("Can't store backup in ~p on node ~p: ~p~n", + [Path, Node, Reason]); + {badrpc, Reason} -> + io:format("Can't store backup in ~p on node ~p: ~p~n", + [Path, Node, Reason]) + end; + +process(Node, ["restore", Path]) -> + case rpc:call(Node, + mnesia, restore, [Path, [{default_op, keep_tables}]]) of + {atomic, ok} -> + ok; + {error, Reason} -> + io:format("Can't restore backup from ~p on node ~p: ~p~n", + [Path, Node, Reason]); + {badrpc, Reason} -> + io:format("Can't restore backup from ~p on node ~p: ~p~n", + [Path, Node, Reason]) + end; + +process(Node, ["install-fallback", Path]) -> + case rpc:call(Node, mnesia, install_fallback, [Path]) of + {atomic, ok} -> + ok; + {error, Reason} -> + io:format("Can't install fallback from ~p on node ~p: ~p~n", + [Path, Node, Reason]); + {badrpc, Reason} -> + io:format("Can't install fallback from ~p on node ~p: ~p~n", + [Path, Node, Reason]) + end; + process(_Node, _Args) -> print_usage(). @@ -78,8 +115,11 @@ print_usage() -> "Available commands:~n" " stop\t\t\t\tstop ejabberd~n" " restart\t\t\trestart ejabberd~n" - " register user password\tregister user~n" - " unregister user\t\tunregister user~n" + " register user password\tregister a user~n" + " unregister user\t\tunregister a user~n" + " backup file\t\t\tstore a backup in file~n" + " restore file\t\t\trestore a backup from file~n" + " install-fallback file\t\tinstall a fallback from file~n" "~n" "Example:~n" " ejabberdctl ejabberd@host restart~n" diff --git a/src/mod_offline.erl b/src/mod_offline.erl index fbd8dfbff..7af72442b 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -16,6 +16,7 @@ stop/0, store_packet/3, resend_offline_messages/1, + pop_offline_messages/1, remove_old_messages/1, remove_user/1]). @@ -129,7 +130,7 @@ find_x_event([El | Els]) -> resend_offline_messages(User) -> LUser = jlib:nodeprep(User), F = fun() -> - Rs = mnesia:read({offline_msg, LUser}), + Rs = mnesia:wread({offline_msg, LUser}), mnesia:delete({offline_msg, LUser}), Rs end, @@ -153,6 +154,32 @@ resend_offline_messages(User) -> ok end. +pop_offline_messages(User) -> + LUser = jlib:nodeprep(User), + F = fun() -> + Rs = mnesia:wread({offline_msg, LUser}), + mnesia:delete({offline_msg, LUser}), + Rs + end, + case mnesia:transaction(F) of + {atomic, Rs} -> + lists:map( + fun(R) -> + {xmlelement, Name, Attrs, Els} = R#offline_msg.packet, + {route, + R#offline_msg.from, + R#offline_msg.to, + {xmlelement, Name, Attrs, + Els ++ + [jlib:timestamp_to_xml( + calendar:now_to_universal_time( + R#offline_msg.timestamp))]}} + end, + lists:keysort(#offline_msg.timestamp, Rs)); + _ -> + [] + end. + remove_old_messages(Days) -> {MegaSecs, Secs, _MicroSecs} = now(), S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days, diff --git a/src/mod_roster.erl b/src/mod_roster.erl index cbb6085ab..836dbdba1 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -375,22 +375,45 @@ process_subscription(Direction, User, JID1, Type) -> Item#roster.ask, Type) end, + AutoReply = case Direction of + out -> + none; + in -> + in_auto_reply(Item#roster.subscription, + Item#roster.ask, + Type) + end, case NewState of none -> - none; + {none, AutoReply}; {Subscription, Pending} -> NewItem = Item#roster{subscription = Subscription, ask = Pending}, mnesia:write(NewItem), - {push, NewItem} + {{push, NewItem}, AutoReply} end end, case mnesia:transaction(F) of - {atomic, ok} -> - false; - {atomic, {push, Item}} -> - push_item(User, {"", ?MYNAME, ""}, Item), - true; + {atomic, {Push, AutoReply}} -> + case AutoReply of + none -> + ok; + _ -> + T = case AutoReply of + subscribed -> "subscribed"; + unsubscribed -> "unsubscribed" + end, + ejabberd_router:route( + {User, ?MYNAME, ""}, JID1, + {xmlelement, "presence", [{"type", T}], []}) + end, + case Push of + {push, Item} -> + push_item(User, {"", ?MYNAME, ""}, Item), + true; + none -> + false + end; _ -> false end. @@ -474,6 +497,17 @@ out_state_change(both, none, subscribed) -> none; out_state_change(both, none, unsubscribe) -> {from, none}; out_state_change(both, none, unsubscribed) -> {to, none}. +in_auto_reply(from, none, subscribe) -> subscribed; +in_auto_reply(from, out, subscribe) -> subscribed; +in_auto_reply(both, none, subscribe) -> subscribed; +in_auto_reply(none, in, unsubscribe) -> unsubscribed; +in_auto_reply(none, both, unsubscribe) -> unsubscribed; +in_auto_reply(to, in, unsubscribe) -> unsubscribed; +in_auto_reply(from, none, unsubscribe) -> unsubscribed; +in_auto_reply(from, out, unsubscribe) -> unsubscribed; +in_auto_reply(both, none, unsubscribe) -> unsubscribed; +in_auto_reply(_, _, _) -> none. + remove_user(User) -> LUser = jlib:nodeprep(User), diff --git a/src/xml.erl b/src/xml.erl index 9f047c825..e14f54c15 100644 --- a/src/xml.erl +++ b/src/xml.erl @@ -17,7 +17,8 @@ get_attr/2, get_attr_s/2, get_tag_attr/2, get_tag_attr_s/2, get_subtag/2, - get_path_s/2]). + get_path_s/2, + replace_tag_attr/3]). element_to_string(El) -> case El of @@ -190,3 +191,10 @@ get_path_s(El, [{attr, Name}]) -> get_path_s(El, [cdata]) -> get_tag_cdata(El). + +replace_tag_attr(Attr, Value, {xmlelement, Name, Attrs, Els}) -> + Attrs1 = lists:keydelete(Attr, 1, Attrs), + Attrs2 = [{Attr, Value} | Attrs1], + {xmlelement, Name, Attrs2, Els}. + +