25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-30 16:36:29 +01:00

Convert to exmpp.

SVN Revision: 1383
This commit is contained in:
Jean-Sébastien Pédron 2008-06-26 15:48:19 +00:00
parent 1a311a30b5
commit 22e79490ff
3 changed files with 319 additions and 351 deletions

View File

@ -13,6 +13,8 @@
(send_element): For stanzas under the NS_JABBER_SERVER namespace, lie (send_element): For stanzas under the NS_JABBER_SERVER namespace, lie
to exmpp_xml by telling it that this namespace is the default one. to exmpp_xml by telling it that this namespace is the default one.
* src/ejabberd_s2s_in.erl, src/ejabberd_s2s_out.erl: Convert to exmpp.
2008-06-25 Jean-Sébastien Pédron <js.pedron@meetic-corp.com> 2008-06-25 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/ejabberd_c2s.erl: Finish ejabberd_c2s conversion with the * src/ejabberd_c2s.erl: Finish ejabberd_c2s conversion with the

View File

@ -46,8 +46,9 @@
handle_info/3, handle_info/3,
terminate/3]). terminate/3]).
-include("exmpp.hrl").
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.hrl").
-ifdef(SSL39). -ifdef(SSL39).
-include_lib("ssl/include/ssl_pkix.hrl"). -include_lib("ssl/include/ssl_pkix.hrl").
-define(PKIXEXPLICIT, 'OTP-PKIX'). -define(PKIXEXPLICIT, 'OTP-PKIX').
@ -92,28 +93,10 @@
[SockData, Opts])). [SockData, Opts])).
-endif. -endif.
-define(STREAM_HEADER(Version), % These are the namespace already declared by the stream opening. This is
("<?xml version='1.0'?>" % used at serialization time.
"<stream:stream " -define(DEFAULT_NS, [?NS_JABBER_SERVER]).
"xmlns:stream='http://etherx.jabber.org/streams' " -define(PREFIXED_NS, [{?NS_XMPP, "stream"}, {?NS_JABBER_DIALBACK, "db"}]).
"xmlns='jabber:server' "
"xmlns:db='jabber:server:dialback' "
"id='" ++ StateData#state.streamid ++ "'" ++ Version ++ ">")
).
-define(STREAM_TRAILER, "</stream:stream>").
-define(INVALID_NAMESPACE_ERR,
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
-define(HOST_UNKNOWN_ERR,
xml:element_to_string(?SERR_HOST_UNKNOWN)).
-define(INVALID_FROM_ERR,
xml:element_to_string(?SERR_INVALID_FROM)).
-define(INVALID_XML_ERR,
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% API %%% API
@ -174,13 +157,16 @@ init([{SockMod, Socket}, Opts]) ->
%% {stop, Reason, NewStateData} %% {stop, Reason, NewStateData}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> wait_for_stream({xmlstreamstart, Opening}, StateData) ->
case {xml:get_attr_s("xmlns", Attrs), case {exmpp_stream:get_default_ns(Opening),
xml:get_attr_s("xmlns:db", Attrs), exmpp_xml:is_ns_declared_here(Opening, ?NS_JABBER_DIALBACK),
xml:get_attr_s("version", Attrs) == "1.0"} of exmpp_stream:get_version(Opening) == {1, 0}} of
{"jabber:server", _, true} when {?NS_JABBER_SERVER, _, true} when
StateData#state.tls and (not StateData#state.authenticated) -> StateData#state.tls and (not StateData#state.authenticated) ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")), Opening_Reply = exmpp_stream:opening_reply(Opening,
StateData#state.streamid),
send_element(StateData,
exmpp_stream:set_dialback_support(Opening_Reply)),
SASL = SASL =
if if
StateData#state.tls_enabled -> StateData#state.tls_enabled ->
@ -190,10 +176,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
case (StateData#state.sockmod):get_verify_result( case (StateData#state.sockmod):get_verify_result(
StateData#state.socket) of StateData#state.socket) of
0 -> 0 ->
[{xmlelement, "mechanisms", [exmpp_server_sasl:feature(
[{"xmlns", ?NS_SASL}], ["EXTERNAL"])];
[{xmlelement, "mechanism", [],
[{xmlcdata, "EXTERNAL"}]}]}];
_ -> _ ->
[] []
end; end;
@ -207,30 +191,35 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
StateData#state.tls_enabled -> StateData#state.tls_enabled ->
[]; [];
true -> true ->
[{xmlelement, "starttls", [exmpp_server_tls:feature()]
[{"xmlns", ?NS_TLS}], []}]
end, end,
send_element(StateData, send_element(StateData, exmpp_stream:features(SASL ++ StartTLS)),
{xmlelement, "stream:features", [],
SASL ++ StartTLS}),
{next_state, wait_for_feature_request, StateData}; {next_state, wait_for_feature_request, StateData};
{"jabber:server", _, true} when {?NS_JABBER_SERVER, _, true} when
StateData#state.authenticated -> StateData#state.authenticated ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")), Opening_Reply = exmpp_stream:opening_reply(Opening,
StateData#state.streamid),
send_element(StateData, send_element(StateData,
{xmlelement, "stream:features", [], []}), exmpp_stream:set_dialback_support(Opening_Reply)),
send_element(StateData, exmpp_stream:features([])),
{next_state, stream_established, StateData}; {next_state, stream_established, StateData};
{"jabber:server", "jabber:server:dialback", _} -> {?NS_JABBER_SERVER, true, _} ->
send_text(StateData, ?STREAM_HEADER("")), Opening_Reply = exmpp_stream:opening_reply(Opening,
StateData#state.streamid),
send_element(StateData,
exmpp_stream:set_dialback_support(Opening_Reply)),
{next_state, stream_established, StateData}; {next_state, stream_established, StateData};
_ -> _ ->
send_text(StateData, ?INVALID_NAMESPACE_ERR), send_element(StateData, exmpp_stream:error('invalid-namespace')),
{stop, normal, StateData} {stop, normal, StateData}
end; end;
wait_for_stream({xmlstreamerror, _}, StateData) -> wait_for_stream({xmlstreamerror, _}, StateData) ->
send_text(StateData, Opening_Reply = exmpp_stream:opening_reply(undefined, ?NS_JABBER_SERVER,
?STREAM_HEADER("") ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER), "", StateData#state.streamid),
send_element(StateData, Opening_Reply),
send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
send_element(StateData, exmpp_stream:closing()),
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_stream(timeout, StateData) -> wait_for_stream(timeout, StateData) ->
@ -241,31 +230,29 @@ wait_for_stream(closed, StateData) ->
wait_for_feature_request({xmlstreamelement, El}, StateData) -> wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
TLS = StateData#state.tls, TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled, TLSEnabled = StateData#state.tls_enabled,
SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket), SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
case {xml:get_attr_s("xmlns", Attrs), Name} of case El of
{?NS_TLS, "starttls"} when TLS == true, #xmlel{ns = ?NS_TLS, name = 'starttls'} when TLS == true,
TLSEnabled == false, TLSEnabled == false,
SockMod == gen_tcp -> SockMod == gen_tcp ->
?DEBUG("starttls", []), ?DEBUG("starttls", []),
Socket = StateData#state.socket, Socket = StateData#state.socket,
Proceed = exmpp_xml:document_fragment_to_list(
exmpp_server_tls:proceed(), ?DEFAULT_NS, ?PREFIXED_NS),
TLSOpts = StateData#state.tls_options, TLSOpts = StateData#state.tls_options,
TLSSocket = (StateData#state.sockmod):starttls( TLSSocket = (StateData#state.sockmod):starttls(
Socket, TLSOpts, Socket, TLSOpts,
xml:element_to_string( Proceed),
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
{next_state, wait_for_stream, {next_state, wait_for_stream,
StateData#state{socket = TLSSocket, StateData#state{socket = TLSSocket,
streamid = new_id(), streamid = new_id(),
tls_enabled = true tls_enabled = true
}}; }};
{?NS_SASL, "auth"} when TLSEnabled -> #xmlel{ns = ?NS_SASL, name = 'auth'} when TLSEnabled ->
Mech = xml:get_attr_s("mechanism", Attrs), case exmpp_server_sasl:next_step(El) of
case Mech of {auth, "EXTERNAL", Auth} ->
"EXTERNAL" ->
Auth = jlib:decode_base64(xml:get_cdata(Els)),
AuthDomain = jlib:nameprep(Auth), AuthDomain = jlib:nameprep(Auth),
AuthRes = AuthRes =
case (StateData#state.sockmod):get_peer_certificate( case (StateData#state.sockmod):get_peer_certificate(
@ -300,8 +287,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
(StateData#state.sockmod):reset_stream( (StateData#state.sockmod):reset_stream(
StateData#state.socket), StateData#state.socket),
send_element(StateData, send_element(StateData,
{xmlelement, "success", exmpp_server_sasl:success()),
[{"xmlns", ?NS_SASL}], []}),
?DEBUG("(~w) Accepted s2s authentication for ~s", ?DEBUG("(~w) Accepted s2s authentication for ~s",
[StateData#state.socket, AuthDomain]), [StateData#state.socket, AuthDomain]),
{next_state, wait_for_stream, {next_state, wait_for_stream,
@ -311,16 +297,14 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
}}; }};
true -> true ->
send_element(StateData, send_element(StateData,
{xmlelement, "failure", exmpp_server_sasl:failure()),
[{"xmlns", ?NS_SASL}], []}), send_element(StateData,
send_text(StateData, ?STREAM_TRAILER), exmpp_stream:closing()),
{stop, normal, StateData} {stop, normal, StateData}
end; end;
_ -> _ ->
send_element(StateData, send_element(StateData,
{xmlelement, "failure", exmpp_server_sasl:failure('invalid-mechanism')),
[{"xmlns", ?NS_SASL}],
[{xmlelement, "invalid-mechanism", [], []}]}),
{stop, normal, StateData} {stop, normal, StateData}
end; end;
_ -> _ ->
@ -328,11 +312,12 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
end; end;
wait_for_feature_request({xmlstreamend, _Name}, StateData) -> wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
send_text(StateData, ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_feature_request({xmlstreamerror, _}, StateData) -> wait_for_feature_request({xmlstreamerror, _}, StateData) ->
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
send_element(StateData, exmpp_stream:closing()),
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_feature_request(closed, StateData) -> wait_for_feature_request(closed, StateData) ->
@ -345,8 +330,8 @@ stream_established({xmlstreamelement, El}, StateData) ->
case is_key_packet(El) of case is_key_packet(El) of
{key, To, From, Id, Key} -> {key, To, From, Id, Key} ->
?DEBUG("GET KEY: ~p", [{To, From, Id, Key}]), ?DEBUG("GET KEY: ~p", [{To, From, Id, Key}]),
LTo = jlib:nameprep(To), LTo = exmpp_stringprep:nameprep(To),
LFrom = jlib:nameprep(From), LFrom = exmpp_stringprep:nameprep(From),
%% Checks if the from domain is allowed and if the to %% Checks if the from domain is allowed and if the to
%% domain is handled by this server: %% domain is handled by this server:
case {ejabberd_s2s:allow_host(To, From), case {ejabberd_s2s:allow_host(To, From),
@ -358,49 +343,56 @@ stream_established({xmlstreamelement, El}, StateData) ->
Key, StateData#state.streamid}), Key, StateData#state.streamid}),
Conns = ?DICT:store({LFrom, LTo}, wait_for_verification, Conns = ?DICT:store({LFrom, LTo}, wait_for_verification,
StateData#state.connections), StateData#state.connections),
change_shaper(StateData, LTo, jlib:make_jid("", LFrom, "")), change_shaper(StateData, LTo,
exmpp_jid:make_bare_jid(undefined, LFrom)),
{next_state, {next_state,
stream_established, stream_established,
StateData#state{connections = Conns, StateData#state{connections = Conns,
timer = Timer}}; timer = Timer}};
{_, false} -> {_, false} ->
send_text(StateData, ?HOST_UNKNOWN_ERR), send_element(StateData, exmpp_stream:error('host-unknown')),
{stop, normal, StateData}; {stop, normal, StateData};
{false, _} -> {false, _} ->
send_text(StateData, ?INVALID_FROM_ERR), send_element(StateData, exmpp_stream:error('invalid-from')),
{stop, normal, StateData} {stop, normal, StateData}
end; end;
{verify, To, From, Id, Key} -> {verify, To, From, Id, Key} ->
?DEBUG("VERIFY KEY: ~p", [{To, From, Id, Key}]), ?DEBUG("VERIFY KEY: ~p", [{To, From, Id, Key}]),
LTo = jlib:nameprep(To), LTo = exmpp_stringprep:nameprep(To),
LFrom = jlib:nameprep(From), LFrom = exmpp_stringprep:nameprep(From),
Type = case ejabberd_s2s:has_key({LTo, LFrom}, Key) of send_element(StateData, exmpp_dialback:verify_response(
true -> "valid"; El, ejabberd_s2s:has_key({LTo, LFrom}, Key))),
_ -> "invalid"
end,
%Type = if Key == Key1 -> "valid";
% true -> "invalid"
% end,
send_element(StateData,
{xmlelement,
"db:verify",
[{"from", To},
{"to", From},
{"id", Id},
{"type", Type}],
[]}),
{next_state, stream_established, StateData#state{timer = Timer}}; {next_state, stream_established, StateData#state{timer = Timer}};
_ -> _ ->
NewEl = jlib:remove_attr("xmlns", El), From = case exmpp_stanza:get_sender(El) of
{xmlelement, Name, Attrs, _Els} = NewEl, undefined ->
From_s = xml:get_attr_s("from", Attrs), error;
From = jlib:string_to_jid(From_s), F ->
To_s = xml:get_attr_s("to", Attrs), try
To = jlib:string_to_jid(To_s), exmpp_jid:string_to_jid(F)
catch
_Exception1 -> error
end
end,
To = case exmpp_stanza:get_recipient(El) of
undefined ->
error;
T ->
try
exmpp_jid:string_to_jid(T)
catch
_Exception2 -> error
end
end,
% XXX OLD FORMAT: El.
% XXX No namespace conversion (:server <-> :client) is done.
% This is handled by C2S and S2S send_element functions.
ElOld = exmpp_xml:xmlel_to_xmlelement(El,
?DEFAULT_NS, ?PREFIXED_NS),
if if
(To /= error) and (From /= error) -> (To /= error) and (From /= error) ->
LFrom = From#jid.lserver, LFrom = From#jid.ldomain,
LTo = To#jid.lserver, LTo = To#jid.ldomain,
if if
StateData#state.authenticated -> StateData#state.authenticated ->
case (LFrom == StateData#state.auth_domain) case (LFrom == StateData#state.auth_domain)
@ -409,15 +401,19 @@ stream_established({xmlstreamelement, El}, StateData) ->
LTo, LTo,
ejabberd_router:dirty_get_all_domains()) of ejabberd_router:dirty_get_all_domains()) of
true -> true ->
if ((Name == "iq") or Name = El#xmlel.name,
(Name == "message") or if ((Name == 'iq') or
(Name == "presence")) -> (Name == 'message') or
(Name == 'presence')) ->
% XXX OLD FORMAT: From, To.
FromOld = exmpp_jid:to_ejabberd_jid(From),
ToOld = exmpp_jid:to_ejabberd_jid(To),
ejabberd_hooks:run( ejabberd_hooks:run(
s2s_receive_packet, s2s_receive_packet,
LFrom, LFrom,
[From, To, NewEl]), [FromOld, ToOld, ElOld]),
ejabberd_router:route( ejabberd_router:route(
From, To, NewEl); FromOld, ToOld, ElOld);
true -> true ->
error error
end; end;
@ -428,15 +424,19 @@ stream_established({xmlstreamelement, El}, StateData) ->
case ?DICT:find({LFrom, LTo}, case ?DICT:find({LFrom, LTo},
StateData#state.connections) of StateData#state.connections) of
{ok, established} -> {ok, established} ->
if ((Name == "iq") or Name = El#xmlel.name,
(Name == "message") or if ((Name == 'iq') or
(Name == "presence")) -> (Name == 'message') or
(Name == 'presence')) ->
% XXX OLD FORMAT: From, To.
FromOld = exmpp_jid:to_ejabberd_jid(From),
ToOld = exmpp_jid:to_ejabberd_jid(To),
ejabberd_hooks:run( ejabberd_hooks:run(
s2s_receive_packet, s2s_receive_packet,
LFrom, LFrom,
[From, To, NewEl]), [FromOld, ToOld, ElOld]),
ejabberd_router:route( ejabberd_router:route(
From, To, NewEl); FromOld, ToOld, ElOld);
true -> true ->
error error
end; end;
@ -447,35 +447,24 @@ stream_established({xmlstreamelement, El}, StateData) ->
true -> true ->
error error
end, end,
ejabberd_hooks:run(s2s_loop_debug, [{xmlstreamelement, El}]), ejabberd_hooks:run(s2s_loop_debug, [{xmlstreamelement, ElOld}]),
{next_state, stream_established, StateData#state{timer = Timer}} {next_state, stream_established, StateData#state{timer = Timer}}
end; end;
stream_established({valid, From, To}, StateData) -> stream_established({valid, From, To}, StateData) ->
send_element(StateData, send_element(StateData, exmpp_dialback:validate(From, To)),
{xmlelement, LFrom = exmpp_stringprep:nameprep(From),
"db:result", LTo = exmpp_stringprep:nameprep(To),
[{"from", To},
{"to", From},
{"type", "valid"}],
[]}),
LFrom = jlib:nameprep(From),
LTo = jlib:nameprep(To),
NSD = StateData#state{ NSD = StateData#state{
connections = ?DICT:store({LFrom, LTo}, established, connections = ?DICT:store({LFrom, LTo}, established,
StateData#state.connections)}, StateData#state.connections)},
{next_state, stream_established, NSD}; {next_state, stream_established, NSD};
stream_established({invalid, From, To}, StateData) -> stream_established({invalid, From, To}, StateData) ->
send_element(StateData, Valid = exmpp_dialback:validate(From, To),
{xmlelement, send_element(StateData, exmpp_stanza:set_type(Valid, "invalid")),
"db:result", LFrom = exmpp_stringprep:nameprep(From),
[{"from", To}, LTo = exmpp_stringprep:nameprep(To),
{"to", From},
{"type", "invalid"}],
[]}),
LFrom = jlib:nameprep(From),
LTo = jlib:nameprep(To),
NSD = StateData#state{ NSD = StateData#state{
connections = ?DICT:erase({LFrom, LTo}, connections = ?DICT:erase({LFrom, LTo},
StateData#state.connections)}, StateData#state.connections)},
@ -485,8 +474,8 @@ stream_established({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
stream_established({xmlstreamerror, _}, StateData) -> stream_established({xmlstreamerror, _}, StateData) ->
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
{stop, normal, StateData}; {stop, normal, StateData};
stream_established(timeout, StateData) -> stream_established(timeout, StateData) ->
@ -570,12 +559,21 @@ terminate(Reason, _StateName, StateData) ->
send_text(StateData, Text) -> send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text). (StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, #xmlel{ns = ?NS_XMPP, name = 'stream'} = El) ->
send_text(StateData, exmpp_xml:document_to_list(El));
send_element(StateData, #xmlel{ns = ?NS_JABBER_CLIENT} = El) ->
send_text(StateData, exmpp_xml:document_fragment_to_list(El,
[?NS_JABBER_CLIENT], ?PREFIXED_NS));
send_element(StateData, El) -> send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)). send_text(StateData, exmpp_xml:document_fragment_to_list(El,
?DEFAULT_NS, ?PREFIXED_NS)).
change_shaper(StateData, Host, JID) -> change_shaper(StateData, Host, JID) ->
Shaper = acl:match_rule(Host, StateData#state.shaper, JID), % XXX OLD FORMAT: JIDOld is an old #jid.
JIDOld = exmpp_jid:to_ejabberd_jid(JID),
Shaper = acl:match_rule(Host, StateData#state.shaper, JIDOld),
(StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper). (StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
@ -592,18 +590,20 @@ cancel_timer(Timer) ->
end. end.
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" -> is_key_packet(#xmlel{ns = ?NS_JABBER_DIALBACK, name = 'result',
attrs = Attrs} = El) ->
{key, {key,
xml:get_attr_s("to", Attrs), exmpp_stanza:get_recipient_from_attrs(Attrs),
xml:get_attr_s("from", Attrs), exmpp_stanza:get_sender_from_attrs(Attrs),
xml:get_attr_s("id", Attrs), exmpp_stanza:get_id_from_attrs(Attrs),
xml:get_cdata(Els)}; exmpp_xml:get_cdata_as_list(El)};
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:verify" -> is_key_packet(#xmlel{ns = ?NS_JABBER_DIALBACK, name = 'verify',
attrs = Attrs} = El) ->
{verify, {verify,
xml:get_attr_s("to", Attrs), exmpp_stanza:get_recipient_from_attrs(Attrs),
xml:get_attr_s("from", Attrs), exmpp_stanza:get_sender_from_attrs(Attrs),
xml:get_attr_s("id", Attrs), exmpp_stanza:get_id_from_attrs(Attrs),
xml:get_cdata(Els)}; exmpp_xml:get_cdata_as_list(El)};
is_key_packet(_) -> is_key_packet(_) ->
false. false.
@ -625,10 +625,10 @@ get_cert_domains(Cert) ->
end, end,
if if
D /= error -> D /= error ->
case jlib:string_to_jid(D) of case exmpp_jid:string_to_jid(D) of
#jid{luser = "", #jid{lnode = undefined,
lserver = LD, ldomain = LD,
lresource = ""} -> lresource = undefined} ->
[LD]; [LD];
_ -> _ ->
[] []
@ -662,8 +662,8 @@ get_cert_domains(Cert) ->
{ok, D} when is_binary(D) -> {ok, D} when is_binary(D) ->
case jlib:string_to_jid( case jlib:string_to_jid(
binary_to_list(D)) of binary_to_list(D)) of
#jid{luser = "", #jid{lnode = "",
lserver = LD, ldomain = LD,
lresource = ""} -> lresource = ""} ->
case idna:domain_utf8_to_ascii(LD) of case idna:domain_utf8_to_ascii(LD) of
false -> false ->
@ -678,10 +678,10 @@ get_cert_domains(Cert) ->
[] []
end; end;
({dNSName, D}) when is_list(D) -> ({dNSName, D}) when is_list(D) ->
case jlib:string_to_jid(D) of case exmpp_jid:string_to_jid(D) of
#jid{luser = "", #jid{lnode = undefined,
lserver = LD, ldomain = LD,
lresource = ""} -> lresource = undefined} ->
[LD]; [LD];
_ -> _ ->
[] []

View File

@ -54,8 +54,9 @@
code_change/4, code_change/4,
test_get_addr_port/1]). test_get_addr_port/1]).
-include("exmpp.hrl").
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {socket, -record(state, {socket,
streamid, streamid,
@ -72,7 +73,7 @@
new = false, verify = false, new = false, verify = false,
timer}). timer}).
%%-define(DBGFSM, true). %-define(DBGFSM, true).
-ifdef(DBGFSM). -ifdef(DBGFSM).
-define(FSMOPTS, [{debug, [trace]}]). -define(FSMOPTS, [{debug, [trace]}]).
@ -98,25 +99,10 @@
%% Specified in miliseconds. Default value is 5 minutes. %% Specified in miliseconds. Default value is 5 minutes.
-define(MAX_RETRY_DELAY, 300000). -define(MAX_RETRY_DELAY, 300000).
-define(STREAM_HEADER, % These are the namespace already declared by the stream opening. This is
"<?xml version='1.0'?>" % used at serialization time.
"<stream:stream " -define(DEFAULT_NS, [?NS_JABBER_SERVER]).
"xmlns:stream='http://etherx.jabber.org/streams' " -define(PREFIXED_NS, [{?NS_XMPP, "stream"}, {?NS_JABBER_DIALBACK, "db"}]).
"xmlns='jabber:server' "
"xmlns:db='jabber:server:dialback' "
"to='~s'~s>"
).
-define(STREAM_TRAILER, "</stream:stream>").
-define(INVALID_NAMESPACE_ERR,
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
-define(HOST_UNKNOWN_ERR,
xml:element_to_string(?SERR_HOST_UNKNOWN)).
-define(INVALID_XML_ERR,
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% API %%% API
@ -209,16 +195,19 @@ open_socket(init, StateData) ->
{ok, Socket} -> {ok, Socket} ->
Version = if Version = if
StateData#state.use_v10 -> StateData#state.use_v10 ->
" version='1.0'"; "1.0";
true -> true ->
"" ""
end, end,
NewStateData = StateData#state{socket = Socket, NewStateData = StateData#state{socket = Socket,
tls_enabled = false, tls_enabled = false,
streamid = new_id()}, streamid = new_id()},
send_text(NewStateData, io_lib:format(?STREAM_HEADER, Opening = exmpp_stream:opening(
[StateData#state.server, StateData#state.server,
Version])), ?NS_JABBER_SERVER,
Version),
send_element(NewStateData,
exmpp_stream:set_dialback_support(Opening)),
{next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT}; {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT};
{error, _Reason} -> {error, _Reason} ->
?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)", ?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)",
@ -272,27 +261,27 @@ open_socket1(Addr, Port) ->
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> wait_for_stream({xmlstreamstart, Opening}, StateData) ->
case {xml:get_attr_s("xmlns", Attrs), case {exmpp_stream:get_default_ns(Opening),
xml:get_attr_s("xmlns:db", Attrs), exmpp_xml:is_ns_declared_here(Opening, ?NS_JABBER_DIALBACK),
xml:get_attr_s("version", Attrs) == "1.0"} of exmpp_stream:get_version(Opening) == {1, 0}} of
{"jabber:server", "jabber:server:dialback", false} -> {?NS_JABBER_SERVER, true, false} ->
send_db_request(StateData); send_db_request(StateData);
{"jabber:server", "jabber:server:dialback", true} when {?NS_JABBER_SERVER, true, true} when
StateData#state.use_v10 -> StateData#state.use_v10 ->
{next_state, wait_for_features, StateData, ?FSMTIMEOUT}; {next_state, wait_for_features, StateData, ?FSMTIMEOUT};
{"jabber:server", "", true} when StateData#state.use_v10 -> {?NS_JABBER_SERVER, false, true} when StateData#state.use_v10 ->
{next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT}; {next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT};
_ -> _ ->
send_text(StateData, ?INVALID_NAMESPACE_ERR), send_element(StateData, exmpp_stream:error('invalid-namespace')),
?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid namespace)", ?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid namespace)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
{stop, normal, StateData} {stop, normal, StateData}
end; end;
wait_for_stream({xmlstreamerror, _}, StateData) -> wait_for_stream({xmlstreamerror, _}, StateData) ->
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid xml)", ?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid xml)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
{stop, normal, StateData}; {stop, normal, StateData};
@ -376,8 +365,8 @@ wait_for_validation({xmlstreamend, _Name}, StateData) ->
wait_for_validation({xmlstreamerror, _}, StateData) -> wait_for_validation({xmlstreamerror, _}, StateData) ->
?INFO_MSG("wait for validation: ~s -> ~s (xmlstreamerror)", ?INFO_MSG("wait for validation: ~s -> ~s (xmlstreamerror)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_validation(timeout, #state{verify = {VPid, VKey, SID}} = StateData) wait_for_validation(timeout, #state{verify = {VPid, VKey, SID}} = StateData)
@ -401,41 +390,37 @@ wait_for_validation(closed, StateData) ->
wait_for_features({xmlstreamelement, El}, StateData) -> wait_for_features({xmlstreamelement, El}, StateData) ->
case El of case El of
{xmlelement, "stream:features", _Attrs, Els} -> #xmlel{ns = ?NS_XMPP, name = 'features'} = Features ->
{SASLEXT, StartTLS, StartTLSRequired} = {SASLEXT, StartTLS, StartTLSRequired} =
lists:foldl( lists:foldl(
fun({xmlelement, "mechanisms", Attrs1, Els1} = _El1, fun(#xmlel{ns = ?NS_SASL, name = 'mechanisms'},
{_SEXT, STLS, STLSReq} = Acc) -> {_SEXT, STLS, STLSReq} = Acc) ->
case xml:get_attr_s("xmlns", Attrs1) of try
?NS_SASL -> Mechs = exmpp_client_sasl:announced_mechanisms(
NewSEXT = El),
lists:any( NewSEXT = lists:member("EXTERNAL", Mechs),
fun({xmlelement, "mechanism", _, Els2}) -> {NewSEXT, STLS, STLSReq}
case xml:get_cdata(Els2) of catch
"EXTERNAL" -> true; _Exception ->
_ -> false
end;
(_) -> false
end, Els1),
{NewSEXT, STLS, STLSReq};
_ ->
Acc Acc
end; end;
({xmlelement, "starttls", Attrs1, _Els1} = El1, (#xmlel{ns = ?NS_TLS, name ='starttls'},
{SEXT, _STLS, _STLSReq} = Acc) -> {SEXT, _STLS, _STLSReq} = Acc) ->
case xml:get_attr_s("xmlns", Attrs1) of try
?NS_TLS -> Support = exmpp_client_tls:announced_support(
Req = case xml:get_subtag(El1, "required") of El),
{xmlelement, _, _, _} -> true; case Support of
false -> false none -> Acc;
end, optional -> {SEXT, true, false};
{SEXT, true, Req}; required -> {SEXT, true, true}
_ -> end
catch
_Exception ->
Acc Acc
end; end;
(_, Acc) -> (_, Acc) ->
Acc Acc
end, {false, false, false}, Els), end, {false, false, false}, Features#xmlel.children),
if if
(not SASLEXT) and (not StartTLS) and (not SASLEXT) and (not StartTLS) and
StateData#state.authenticated -> StateData#state.authenticated ->
@ -450,19 +435,14 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
SASLEXT and StateData#state.try_auth and SASLEXT and StateData#state.try_auth and
(StateData#state.new /= false) -> (StateData#state.new /= false) ->
send_element(StateData, send_element(StateData,
{xmlelement, "auth", exmpp_client_sasl:selected_mechanism("EXTERNAL",
[{"xmlns", ?NS_SASL}, StateData#state.myname)),
{"mechanism", "EXTERNAL"}],
[{xmlcdata,
jlib:encode_base64(
StateData#state.myname)}]}),
{next_state, wait_for_auth_result, {next_state, wait_for_auth_result,
StateData#state{try_auth = false}, ?FSMTIMEOUT}; StateData#state{try_auth = false}, ?FSMTIMEOUT};
StartTLS and StateData#state.tls and StartTLS and StateData#state.tls and
(not StateData#state.tls_enabled) -> (not StateData#state.tls_enabled) ->
send_element(StateData, send_element(StateData,
{xmlelement, "starttls", exmpp_client_tls:starttls()),
[{"xmlns", ?NS_TLS}], []}),
{next_state, wait_for_starttls_proceed, StateData, {next_state, wait_for_starttls_proceed, StateData,
?FSMTIMEOUT}; ?FSMTIMEOUT};
StartTLSRequired and (not StateData#state.tls) -> StartTLSRequired and (not StateData#state.tls) ->
@ -483,9 +463,8 @@ wait_for_features({xmlstreamelement, El}, StateData) ->
use_v10 = false}, ?FSMTIMEOUT} use_v10 = false}, ?FSMTIMEOUT}
end; end;
_ -> _ ->
send_text(StateData, send_element(StateData, exmpp_stream:error('bad-format')),
xml:element_to_string(?SERR_BAD_FORMAT) ++ send_element(StateData, exmpp_stream:closing()),
?STREAM_TRAILER),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)", ?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
{stop, normal, StateData} {stop, normal, StateData}
@ -496,8 +475,8 @@ wait_for_features({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_features({xmlstreamerror, _}, StateData) -> wait_for_features({xmlstreamerror, _}, StateData) ->
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
?INFO_MSG("wait for features: xmlstreamerror", []), ?INFO_MSG("wait for features: xmlstreamerror", []),
{stop, normal, StateData}; {stop, normal, StateData};
@ -512,48 +491,29 @@ wait_for_features(closed, StateData) ->
wait_for_auth_result({xmlstreamelement, El}, StateData) -> wait_for_auth_result({xmlstreamelement, El}, StateData) ->
case El of case El of
{xmlelement, "success", Attrs, _Els} -> #xmlel{ns = ?NS_SASL, name = 'success'} ->
case xml:get_attr_s("xmlns", Attrs) of ?DEBUG("auth: ~p", [{StateData#state.myname,
?NS_SASL -> StateData#state.server}]),
?DEBUG("auth: ~p", [{StateData#state.myname, ejabberd_socket:reset_stream(StateData#state.socket),
StateData#state.server}]), Opening = exmpp_stream:opening(
ejabberd_socket:reset_stream(StateData#state.socket), StateData#state.server,
send_text(StateData, ?NS_JABBER_SERVER,
io_lib:format(?STREAM_HEADER, "1.0"),
[StateData#state.server, send_element(StateData,
" version='1.0'"])), exmpp_stream:set_dialback_support(Opening)),
{next_state, wait_for_stream, {next_state, wait_for_stream,
StateData#state{streamid = new_id(), StateData#state{streamid = new_id(),
authenticated = true authenticated = true
}, ?FSMTIMEOUT}; }, ?FSMTIMEOUT};
_ -> #xmlel{ns = ?NS_SASL, name = 'failure'} ->
send_text(StateData, ?DEBUG("restarted: ~p", [{StateData#state.myname,
xml:element_to_string(?SERR_BAD_FORMAT) ++ StateData#state.server}]),
?STREAM_TRAILER), ejabberd_socket:close(StateData#state.socket),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)", {next_state, reopen_socket,
[StateData#state.myname, StateData#state.server]), StateData#state{socket = undefined}, ?FSMTIMEOUT};
{stop, normal, StateData}
end;
{xmlelement, "failure", Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_SASL ->
?DEBUG("restarted: ~p", [{StateData#state.myname,
StateData#state.server}]),
ejabberd_socket:close(StateData#state.socket),
{next_state, reopen_socket,
StateData#state{socket = undefined}, ?FSMTIMEOUT};
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)",
[StateData#state.myname, StateData#state.server]),
{stop, normal, StateData}
end;
_ -> _ ->
send_text(StateData, send_element(StateData, exmpp_stream:error('bad-format')),
xml:element_to_string(?SERR_BAD_FORMAT) ++ send_element(StateData, exmpp_stream:closing()),
?STREAM_TRAILER),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)", ?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
{stop, normal, StateData} {stop, normal, StateData}
@ -564,8 +524,8 @@ wait_for_auth_result({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_auth_result({xmlstreamerror, _}, StateData) -> wait_for_auth_result({xmlstreamerror, _}, StateData) ->
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
?INFO_MSG("wait for auth result: xmlstreamerror", []), ?INFO_MSG("wait for auth result: xmlstreamerror", []),
{stop, normal, StateData}; {stop, normal, StateData};
@ -580,42 +540,36 @@ wait_for_auth_result(closed, StateData) ->
wait_for_starttls_proceed({xmlstreamelement, El}, StateData) -> wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
case El of case El of
{xmlelement, "proceed", Attrs, _Els} -> #xmlel{ns = ?NS_TLS, name = 'proceed'} ->
case xml:get_attr_s("xmlns", Attrs) of ?DEBUG("starttls: ~p", [{StateData#state.myname,
?NS_TLS -> StateData#state.server}]),
?DEBUG("starttls: ~p", [{StateData#state.myname, Socket = StateData#state.socket,
StateData#state.server}]), TLSOpts = case ejabberd_config:get_local_option(
Socket = StateData#state.socket, {domain_certfile,
TLSOpts = case ejabberd_config:get_local_option( StateData#state.server}) of
{domain_certfile, undefined ->
StateData#state.server}) of StateData#state.tls_options;
undefined -> CertFile ->
StateData#state.tls_options; [{certfile, CertFile} |
CertFile -> lists:keydelete(
[{certfile, CertFile} | certfile, 1,
lists:keydelete( StateData#state.tls_options)]
certfile, 1, end,
StateData#state.tls_options)] TLSSocket = ejabberd_socket:starttls(Socket, TLSOpts),
end, NewStateData = StateData#state{socket = TLSSocket,
TLSSocket = ejabberd_socket:starttls(Socket, TLSOpts), streamid = new_id(),
NewStateData = StateData#state{socket = TLSSocket, tls_enabled = true
streamid = new_id(), },
tls_enabled = true Opening = exmpp_stream:opening(
}, StateData#state.server,
send_text(NewStateData, ?NS_JABBER_SERVER,
io_lib:format(?STREAM_HEADER, "1.0"),
[StateData#state.server, send_element(NewStateData,
" version='1.0'"])), exmpp_stream:set_dialback_support(Opening)),
{next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT}; {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT};
_ ->
send_text(StateData,
xml:element_to_string(?SERR_BAD_FORMAT) ++
?STREAM_TRAILER),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)",
[StateData#state.myname, StateData#state.server]),
{stop, normal, StateData}
end;
_ -> _ ->
send_element(StateData, exmpp_stream:error('bad-format')),
send_element(StateData, exmpp_stream:closing()),
?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)", ?INFO_MSG("Closing s2s connection: ~s -> ~s (bad format)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
{stop, normal, StateData} {stop, normal, StateData}
@ -626,8 +580,8 @@ wait_for_starttls_proceed({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
wait_for_starttls_proceed({xmlstreamerror, _}, StateData) -> wait_for_starttls_proceed({xmlstreamerror, _}, StateData) ->
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
?INFO_MSG("wait for starttls proceed: xmlstreamerror", []), ?INFO_MSG("wait for starttls proceed: xmlstreamerror", []),
{stop, normal, StateData}; {stop, normal, StateData};
@ -690,8 +644,8 @@ stream_established({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
stream_established({xmlstreamerror, _}, StateData) -> stream_established({xmlstreamerror, _}, StateData) ->
send_text(StateData, send_element(StateData, exmpp_stream:error('xml-not-well-formed')),
?INVALID_XML_ERR ++ ?STREAM_TRAILER), send_element(StateData, exmpp_stream:closing()),
?INFO_MSG("stream established: ~s -> ~s (xmlstreamerror)", ?INFO_MSG("stream established: ~s -> ~s (xmlstreamerror)",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server]),
{stop, normal, StateData}; {stop, normal, StateData};
@ -759,7 +713,10 @@ handle_info({send_text, Text}, StateName, StateData) ->
{next_state, StateName, StateData#state{timer = Timer}, {next_state, StateName, StateData#state{timer = Timer},
get_timeout_interval(StateName)}; get_timeout_interval(StateName)};
handle_info({send_element, El}, StateName, StateData) -> handle_info({send_element, ElOld}, StateName, StateData) ->
% XXX OLD FORMAT: El.
El = exmpp_xml:xmlelement_to_xmlel(ElOld,
[?NS_JABBER_CLIENT], ?PREFIXED_NS),
case StateName of case StateName of
stream_established -> stream_established ->
cancel_timer(StateData#state.timer), cancel_timer(StateData#state.timer),
@ -769,7 +726,7 @@ handle_info({send_element, El}, StateName, StateData) ->
%% In this state we bounce all message: We are waiting before %% In this state we bounce all message: We are waiting before
%% trying to reconnect %% trying to reconnect
wait_before_retry -> wait_before_retry ->
bounce_element(El, ?ERR_REMOTE_SERVER_NOT_FOUND), bounce_element(El, 'remote-server-not-found'),
{next_state, StateName, StateData}; {next_state, StateName, StateData};
_ -> _ ->
Q = queue:in(El, StateData#state.queue), Q = queue:in(El, StateData#state.queue),
@ -811,8 +768,8 @@ terminate(Reason, StateName, StateData) ->
{StateData#state.myname, StateData#state.server}, self(), Key) {StateData#state.myname, StateData#state.server}, self(), Key)
end, end,
%% bounce queue manage by process and Erlang message queue %% bounce queue manage by process and Erlang message queue
bounce_queue(StateData#state.queue, ?ERR_REMOTE_SERVER_NOT_FOUND), bounce_queue(StateData#state.queue, 'remote-server-not-found'),
bounce_messages(?ERR_REMOTE_SERVER_NOT_FOUND), bounce_messages('remote-server-not-found'),
case StateData#state.socket of case StateData#state.socket of
undefined -> undefined ->
ok; ok;
@ -828,8 +785,14 @@ terminate(Reason, StateName, StateData) ->
send_text(StateData, Text) -> send_text(StateData, Text) ->
ejabberd_socket:send(StateData#state.socket, Text). ejabberd_socket:send(StateData#state.socket, Text).
send_element(StateData, #xmlel{ns = ?NS_XMPP, name = 'stream'} = El) ->
send_text(StateData, exmpp_xml:document_to_list(El));
send_element(StateData, #xmlel{ns = ?NS_JABBER_CLIENT} = El) ->
send_text(StateData, exmpp_xml:document_fragment_to_list(El,
[?NS_JABBER_CLIENT], ?PREFIXED_NS));
send_element(StateData, El) -> send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)). send_text(StateData, exmpp_xml:document_fragment_to_list(El,
?DEFAULT_NS, ?PREFIXED_NS)).
send_queue(StateData, Q) -> send_queue(StateData, Q) ->
case queue:out(Q) of case queue:out(Q) of
@ -840,24 +803,31 @@ send_queue(StateData, Q) ->
ok ok
end. end.
%% Bounce a single message (xmlelement) %% Bounce a single message (xmlel)
bounce_element(El, Error) -> bounce_element(El, Condition) ->
{xmlelement, _Name, Attrs, _SubTags} = El, case exmpp_stanza:get_type(El) of
case xml:get_attr_s("type", Attrs) of
"error" -> ok; "error" -> ok;
"result" -> ok; "result" -> ok;
_ -> _ ->
Err = jlib:make_error_reply(El, Error), Error = exmpp_stanza:error(El#xmlel.ns, Condition),
From = jlib:string_to_jid(xml:get_tag_attr_s("from", El)), Err = exmpp_stanza:reply_with_error(El, Error),
To = jlib:string_to_jid(xml:get_tag_attr_s("to", El)), From = exmpp_jid:string_to_jid(exmpp_stanza:get_sender(El)),
ejabberd_router:route(To, From, Err) To = exmpp_jid:string_to_jid(exmpp_stanza:get_recipient(El)),
% XXX OLD FORMAT: From, To, Err.
% XXX No namespace conversion (:server <-> :client) is done.
% This is handled by C2S and S2S send_element functions.
ErrOld = exmpp_xml:xmlel_to_xmlelement(Err,
[?NS_JABBER_CLIENT], ?PREFIXED_NS),
FromOld = exmpp_jid:to_ejabberd_jid(From),
ToOld = exmpp_jid:to_ejabberd_jid(To),
ejabberd_router:route(ToOld, FromOld, ErrOld)
end. end.
bounce_queue(Q, Error) -> bounce_queue(Q, Condition) ->
case queue:out(Q) of case queue:out(Q) of
{{value, El}, Q1} -> {{value, El}, Q1} ->
bounce_element(El, Error), bounce_element(El, Condition),
bounce_queue(Q1, Error); bounce_queue(Q1, Condition);
{empty, _} -> {empty, _} ->
ok ok
end. end.
@ -874,11 +844,14 @@ cancel_timer(Timer) ->
ok ok
end. end.
bounce_messages(Error) -> bounce_messages(Condition) ->
receive receive
{send_element, El} -> {send_element, ElOld} ->
bounce_element(El, Error), % XXX OLD FORMAT: El.
bounce_messages(Error) El = exmpp_xml:xmlelement_to_xmlel(ElOld,
[?NS_JABBER_CLIENT], ?PREFIXED_NS),
bounce_element(El, Condition),
bounce_messages(Condition)
after 0 -> after 0 ->
ok ok
end. end.
@ -902,40 +875,33 @@ send_db_request(StateData) ->
false -> false ->
ok; ok;
Key1 -> Key1 ->
send_element(StateData, send_element(StateData, exmpp_dialback:key(
{xmlelement, StateData#state.myname, Server, Key1))
"db:result",
[{"from", StateData#state.myname},
{"to", Server}],
[{xmlcdata, Key1}]})
end, end,
case StateData#state.verify of case StateData#state.verify of
false -> false ->
ok; ok;
{_Pid, Key2, SID} -> {_Pid, Key2, SID} ->
send_element(StateData, send_element(StateData, exmpp_dialback:verify_request(
{xmlelement, StateData#state.myname, StateData#state.server, SID, Key2))
"db:verify",
[{"from", StateData#state.myname},
{"to", StateData#state.server},
{"id", SID}],
[{xmlcdata, Key2}]})
end, end,
{next_state, wait_for_validation, StateData#state{new = New}, ?FSMTIMEOUT*6}. {next_state, wait_for_validation, StateData#state{new = New}, ?FSMTIMEOUT*6}.
is_verify_res({xmlelement, Name, Attrs, _Els}) when Name == "db:result" -> is_verify_res(#xmlel{ns = ?NS_JABBER_DIALBACK, name = 'result',
attrs = Attrs}) ->
{result, {result,
xml:get_attr_s("to", Attrs), exmpp_stanza:get_recipient_from_attrs(Attrs),
xml:get_attr_s("from", Attrs), exmpp_stanza:get_sender_from_attrs(Attrs),
xml:get_attr_s("id", Attrs), exmpp_stanza:get_id_from_attrs(Attrs),
xml:get_attr_s("type", Attrs)}; exmpp_stanza:get_type_from_attrs(Attrs)};
is_verify_res({xmlelement, Name, Attrs, _Els}) when Name == "db:verify" -> is_verify_res(#xmlel{ns = ?NS_JABBER_DIALBACK, name = 'verify',
attrs = Attrs}) ->
{verify, {verify,
xml:get_attr_s("to", Attrs), exmpp_stanza:get_recipient_from_attrs(Attrs),
xml:get_attr_s("from", Attrs), exmpp_stanza:get_sender_from_attrs(Attrs),
xml:get_attr_s("id", Attrs), exmpp_stanza:get_id_from_attrs(Attrs),
xml:get_attr_s("type", Attrs)}; exmpp_stanza:get_type_from_attrs(Attrs)};
is_verify_res(_) -> is_verify_res(_) ->
false. false.
@ -1032,8 +998,8 @@ get_timeout_interval(StateName) ->
%% function that want to wait for a reconnect delay before stopping. %% function that want to wait for a reconnect delay before stopping.
wait_before_reconnect(StateData) -> wait_before_reconnect(StateData) ->
%% bounce queue manage by process and Erlang message queue %% bounce queue manage by process and Erlang message queue
bounce_queue(StateData#state.queue, ?ERR_REMOTE_SERVER_NOT_FOUND), bounce_queue(StateData#state.queue, 'remote-server-not-found'),
bounce_messages(?ERR_REMOTE_SERVER_NOT_FOUND), bounce_messages('remote-server-not-found'),
cancel_timer(StateData#state.timer), cancel_timer(StateData#state.timer),
Delay = case StateData#state.delay_to_retry of Delay = case StateData#state.delay_to_retry of
undefined_delay -> undefined_delay ->