* 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
This commit is contained in:
Alexey Shchepin 2004-01-18 20:42:09 +00:00
parent 7a002470e6
commit 273886701b
6 changed files with 181 additions and 29 deletions

View File

@ -1,3 +1,18 @@
2004-01-18 Alexey Shchepin <alexey@sevcom.net>
* 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 <alexey@sevcom.net>
* src/mod_muc/mod_muc_room.erl: Bugfix, updated error codes

View File

@ -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.

View File

@ -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"

View File

@ -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,

View File

@ -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),

View File

@ -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}.