aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_service.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/ejabberd_service.erl')
-rw-r--r--src/ejabberd_service.erl530
1 files changed, 184 insertions, 346 deletions
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index 26374c1f1..94cd68ecf 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -36,7 +36,7 @@
-behaviour(?GEN_FSM).
%% External exports
--export([start/0, start/2, start_link/2, send_text/2,
+-export([start/2, start_link/2, send_text/2,
send_element/2, socket_type/0, transform_listen_option/2]).
-export([init/1, wait_for_stream/2,
@@ -44,61 +44,35 @@
handle_event/3, handle_sync_event/4, code_change/4,
handle_info/3, terminate/3, print_state/1, opt_type/1]).
--include("ejabberd_service.hrl").
--include("mod_privacy.hrl").
-
--export([get_delegated_ns/1]).
+-include("ejabberd.hrl").
+-include("logger.hrl").
+-include("xmpp.hrl").
+
+-record(state,
+ {socket :: ejabberd_socket:socket_state(),
+ sockmod = ejabberd_socket :: ejabberd_socket | ejabberd_frontend_socket,
+ streamid = <<"">> :: binary(),
+ host_opts = dict:new() :: ?TDICT,
+ host = <<"">> :: binary(),
+ access :: atom(),
+ check_from = true :: boolean()}).
+
+-type state_name() :: wait_for_stream | wait_for_handshake | stream_established.
+-type state() :: #state{}.
+-type fsm_next() :: {next_state, state_name(), state()}.
+-type fsm_stop() :: {stop, normal, state()}.
+-type fsm_transition() :: fsm_stop() | fsm_next().
%-define(DBGFSM, true).
-
-ifdef(DBGFSM).
-
-define(FSMOPTS, [{debug, [trace]}]).
-
-else.
-
-define(FSMOPTS, []).
-
-endif.
--define(STREAM_HEADER,
- <<"<?xml version='1.0'?><stream:stream "
- "xmlns:stream='http://etherx.jabber.org/stream"
- "s' xmlns='jabber:component:accept' id='~s' "
- "from='~s'>">>).
-
--define(STREAM_TRAILER, <<"</stream:stream>">>).
-
--define(INVALID_HEADER_ERR,
- <<"<stream:stream xmlns:stream='http://etherx.ja"
- "bber.org/streams'><stream:error>Invalid "
- "Stream Header</stream:error></stream:stream>">>).
-
--define(INVALID_HANDSHAKE_ERR,
- <<"<stream:error><not-authorized xmlns='urn:ietf"
- ":params:xml:ns:xmpp-streams'/><text "
- "xmlns='urn:ietf:params:xml:ns:xmpp-streams' "
- "xml:lang='en'>Invalid Handshake</text></strea"
- "m:error></stream:stream>">>).
-
--define(INVALID_XML_ERR,
- fxml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
-
--define(INVALID_NS_ERR,
- fxml:element_to_binary(?SERR_INVALID_NAMESPACE)).
-
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
-
-%% for xep-0355
-%% table contans records like {namespace, fitering attributes, pid(),
-%% host, disco info for general case, bare jid disco info }
-
-start() ->
- ets:new(delegated_namespaces, [named_table, public]),
- ets:new(hooks_tmp, [named_table, public]).
-
start(SockData, Opts) ->
supervisor:start_child(ejabberd_service_sup,
[SockData, Opts]).
@@ -109,20 +83,9 @@ start_link(SockData, Opts) ->
socket_type() -> xml_stream.
-get_delegated_ns(FsmRef) ->
- (?GEN_FSM):sync_send_all_state_event(FsmRef, {get_delegated_ns}).
-
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
-
-%%----------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, StateName, StateData} |
-%% {ok, StateName, StateData, Timeout} |
-%% ignore |
-%% {stop, StopReason}
-%%----------------------------------------------------------------------
init([{SockMod, Socket}, Opts]) ->
?INFO_MSG("(~w) External service connected", [Socket]),
Access = case lists:keysearch(access, 1, Opts) of
@@ -144,21 +107,6 @@ init([{SockMod, Socket}, Opts]) ->
p1_sha:sha(randoms:bytes(20))),
dict:from_list([{global, Pass}])
end,
- %% privilege access to entities data
- PrivAccess = case lists:keysearch(privilege_access, 1, Opts) of
- {value, {_, PrivAcc}} -> PrivAcc;
- _ -> []
- end,
- Delegations = case lists:keyfind(delegations, 1, Opts) of
- {delegations, Del} ->
- lists:foldl(
- fun({Ns, FiltAttr}, D) when Ns /= ?NS_DELEGATION ->
- Attr = proplists:get_value(filtering, FiltAttr, []),
- D ++ [{Ns, Attr}];
- (_Deleg, D) -> D
- end, [], Del);
- false -> []
- end,
Shaper = case lists:keysearch(shaper_rule, 1, Opts) of
{value, {_, S}} -> S;
_ -> none
@@ -172,223 +120,136 @@ init([{SockMod, Socket}, Opts]) ->
SockMod:change_shaper(Socket, Shaper),
{ok, wait_for_stream,
#state{socket = Socket, sockmod = SockMod,
- streamid = new_id(), host_opts = HostOpts, access = Access,
- check_from = CheckFrom, privilege_access = PrivAccess,
- delegations = Delegations}}.
-
-%%----------------------------------------------------------------------
-%% Func: StateName/2
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-
-wait_for_stream({xmlstreamstart, _Name, Attrs},
- StateData) ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- <<"jabber:component:accept">> ->
- To = fxml:get_attr_s(<<"to">>, Attrs),
- Host = jid:nameprep(To),
- if Host == error ->
- Header = io_lib:format(?STREAM_HEADER,
- [<<"none">>, ?MYNAME]),
- send_text(StateData,
- <<(list_to_binary(Header))/binary,
- (?INVALID_XML_ERR)/binary,
- (?STREAM_TRAILER)/binary>>),
- {stop, normal, StateData};
- true ->
- Header = io_lib:format(?STREAM_HEADER,
- [StateData#state.streamid, fxml:crypt(To)]),
- send_text(StateData, Header),
- HostOpts = case dict:is_key(Host, StateData#state.host_opts) of
- true ->
- StateData#state.host_opts;
- false ->
- case dict:find(global, StateData#state.host_opts) of
- {ok, GlobalPass} ->
- dict:from_list([{Host, GlobalPass}]);
- error ->
- StateData#state.host_opts
- end
- end,
- {next_state, wait_for_handshake,
- StateData#state{host = Host, host_opts = HostOpts}}
- end;
- _ ->
- send_text(StateData, ?INVALID_HEADER_ERR),
- {stop, normal, StateData}
+ streamid = new_id(), host_opts = HostOpts,
+ access = Access, check_from = CheckFrom}}.
+
+wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
+ try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of
+ #stream_start{xmlns = NS_COMPONENT, stream_xmlns = NS_STREAM}
+ when NS_COMPONENT /= ?NS_COMPONENT; NS_STREAM /= ?NS_STREAM ->
+ send_header(StateData, ?MYNAME),
+ send_element(StateData, xmpp:serr_invalid_namespace()),
+ {stop, normal, StateData};
+ #stream_start{to = To} when is_record(To, jid) ->
+ Host = To#jid.lserver,
+ send_header(StateData, Host),
+ HostOpts = case dict:is_key(Host, StateData#state.host_opts) of
+ true ->
+ StateData#state.host_opts;
+ false ->
+ case dict:find(global, StateData#state.host_opts) of
+ {ok, GlobalPass} ->
+ dict:from_list([{Host, GlobalPass}]);
+ error ->
+ StateData#state.host_opts
+ end
+ end,
+ {next_state, wait_for_handshake,
+ StateData#state{host = Host, host_opts = HostOpts}};
+ #stream_start{} ->
+ send_header(StateData, ?MYNAME),
+ send_element(StateData, xmpp:serr_improper_addressing()),
+ {stop, normal, StateData};
+ _ ->
+ send_header(StateData, ?MYNAME),
+ send_element(StateData, xmpp:serr_invalid_xml()),
+ {stop, normal, StateData}
+ catch _:{xmpp_codec, Why} ->
+ Txt = xmpp:format_error(Why),
+ send_header(StateData, ?MYNAME),
+ send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)),
+ {stop, normal, StateData}
end;
wait_for_stream({xmlstreamerror, _}, StateData) ->
- Header = io_lib:format(?STREAM_HEADER,
- [<<"none">>, ?MYNAME]),
- send_text(StateData,
- <<(list_to_binary(Header))/binary, (?INVALID_XML_ERR)/binary,
- (?STREAM_TRAILER)/binary>>),
+ send_header(StateData, ?MYNAME),
+ send_element(StateData, xmpp:serr_not_well_formed()),
{stop, normal, StateData};
wait_for_stream(closed, StateData) ->
{stop, normal, StateData}.
wait_for_handshake({xmlstreamelement, El}, StateData) ->
- #xmlel{name = Name, children = Els} = El,
- case {Name, fxml:get_cdata(Els)} of
- {<<"handshake">>, Digest} ->
- case dict:find(StateData#state.host, StateData#state.host_opts) of
- {ok, Password} ->
- case p1_sha:sha(<<(StateData#state.streamid)/binary,
- Password/binary>>) of
- Digest ->
- send_text(StateData, <<"<handshake/>">>),
- lists:foreach(
- fun (H) ->
- ejabberd_router:register_route(H, ?MYNAME),
- ?INFO_MSG("Route registered for service ~p~n",
- [H]),
- ejabberd_hooks:run(component_connected,
- [H])
- end, dict:fetch_keys(StateData#state.host_opts)),
-
- mod_privilege:advertise_permissions(StateData),
- DelegatedNs = mod_delegation:advertise_delegations(StateData),
-
- RosterAccess = proplists:get_value(roster,
- StateData#state.privilege_access),
-
- case proplists:get_value(presence,
- StateData#state.privilege_access) of
- <<"managed_entity">> ->
- mod_privilege:initial_presences(StateData),
- Fun = mod_privilege:process_presence(self()),
- add_hooks(user_send_packet, Fun);
- <<"roster">> when (RosterAccess == <<"both">>) or
- (RosterAccess == <<"get">>) ->
- mod_privilege:initial_presences(StateData),
- Fun = mod_privilege:process_presence(self()),
- add_hooks(user_send_packet, Fun),
- Fun2 = mod_privilege:process_roster_presence(self()),
- add_hooks(s2s_receive_packet, Fun2);
- _ -> ok
- end,
- {next_state, stream_established,
- StateData#state{delegations = DelegatedNs}};
- _ ->
- send_text(StateData, ?INVALID_HANDSHAKE_ERR),
- {stop, normal, StateData}
- end;
- _ ->
- send_text(StateData, ?INVALID_HANDSHAKE_ERR),
- {stop, normal, StateData}
- end;
- _ -> {next_state, wait_for_handshake, StateData}
- end;
+ decode_element(El, wait_for_handshake, StateData);
+wait_for_handshake(#handshake{data = Digest}, StateData) ->
+ send_element(StateData, #handshake{}),
+ lists:foreach(
+ fun (H) ->
+ ejabberd_router:register_route(H, ?MYNAME),
+ ?INFO_MSG("Route registered for service ~p~n",
+ [H]),
+ ejabberd_hooks:run(component_connected, [H])
+ end, dict:fetch_keys(StateData#state.host_opts)),
+ {next_state, stream_established, StateData};
+ %% case dict:find(StateData#state.host, StateData#state.host_opts) of
+ %% {ok, Password} ->
+ %% case p1_sha:sha(<<(StateData#state.streamid)/binary,
+ %% Password/binary>>) of
+ %% Digest ->
+ %% send_element(StateData, #handshake{}),
+ %% lists:foreach(
+ %% fun (H) ->
+ %% ejabberd_router:register_route(H, ?MYNAME),
+ %% ?INFO_MSG("Route registered for service ~p~n",
+ %% [H]),
+ %% ejabberd_hooks:run(component_connected, [H])
+ %% end, dict:fetch_keys(StateData#state.host_opts)),
+ %% {next_state, stream_established, StateData};
+ %% _ ->
+ %% send_element(StateData, xmpp:serr_not_authorized()),
+ %% {stop, normal, StateData}
+ %% end;
+ %% _ ->
+ %% send_element(StateData, xmpp:serr_not_authorized()),
+ %% {stop, normal, StateData}
+ %% end;
wait_for_handshake({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData};
wait_for_handshake({xmlstreamerror, _}, StateData) ->
- send_text(StateData,
- <<(?INVALID_XML_ERR)/binary,
- (?STREAM_TRAILER)/binary>>),
+ send_element(StateData, xmpp:serr_not_well_formed()),
{stop, normal, StateData};
wait_for_handshake(closed, StateData) ->
- {stop, normal, StateData}.
+ {stop, normal, StateData};
+wait_for_handshake(_Pkt, StateData) ->
+ {next_state, wait_for_handshake, StateData}.
stream_established({xmlstreamelement, El}, StateData) ->
- NewEl = jlib:remove_attr(<<"xmlns">>, El),
- #xmlel{name = Name, attrs = Attrs} = NewEl,
- From = fxml:get_attr_s(<<"from">>, Attrs),
- FromJID = case StateData#state.check_from of
- %% If the admin does not want to check the from field
- %% when accept packets from any address.
- %% In this case, the component can send packet of
- %% behalf of the server users.
- false -> jid:from_string(From);
- %% The default is the standard behaviour in XEP-0114
- _ ->
- FromJID1 = jid:from_string(From),
- case FromJID1 of
- #jid{lserver = Server} ->
- case dict:is_key(Server, StateData#state.host_opts) of
- true -> FromJID1;
- false -> error
- end;
- _ -> error
- end
- end,
- To = fxml:get_attr_s(<<"to">>, Attrs),
- ToJID = case To of
- <<"">> -> error;
- _ -> jid:from_string(To)
- end,
- if (Name == <<"iq">>) and (ToJID /= error) and (FromJID /= error) ->
- mod_privilege:process_iq(StateData, FromJID, ToJID, NewEl);
- (Name == <<"presence">>) and (ToJID /= error) and (FromJID /= error) ->
- ejabberd_router:route(FromJID, ToJID, NewEl);
- (Name == <<"message">>) and (ToJID /= error) and (FromJID /= error) ->
- mod_privilege:process_message(StateData, FromJID, ToJID, NewEl);
+ decode_element(El, stream_established, StateData);
+stream_established(El, StateData) when ?is_stanza(El) ->
+ From = xmpp:get_from(El),
+ To = xmpp:get_to(El),
+ Lang = xmpp:get_lang(El),
+ if From == undefined orelse To == undefined ->
+ Txt = <<"Missing 'from' or 'to' attribute">>,
+ send_error(StateData, El, xmpp:err_jid_malformed(Txt, Lang));
true ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, El),
- Txt = <<"Incorrect stanza name or from/to JID">>,
- Err = jlib:make_error_reply(NewEl, ?ERRT_BAD_REQUEST(Lang, Txt)),
- send_element(StateData, Err),
- error
+ case check_from(From, StateData) of
+ true ->
+ ejabberd_router:route(From, To, El);
+ false ->
+ Txt = <<"Improper domain part of 'from' attribute">>,
+ send_error(StateData, El, xmpp:err_not_allowed(Txt, Lang))
+ end
end,
{next_state, stream_established, StateData};
stream_established({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData};
stream_established({xmlstreamerror, _}, StateData) ->
- send_text(StateData,
- <<(?INVALID_XML_ERR)/binary,
- (?STREAM_TRAILER)/binary>>),
+ send_element(StateData, xmpp:serr_not_well_formed()),
{stop, normal, StateData};
stream_established(closed, StateData) ->
- {stop, normal, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: StateName/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {reply, Reply, NextStateName, NextStateData} |
-%% {reply, Reply, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData} |
-%% {stop, Reason, Reply, NewStateData}
-%%----------------------------------------------------------------------
-%state_name(Event, From, StateData) ->
-% Reply = ok,
-% {reply, Reply, state_name, StateData}.
+ {stop, normal, StateData};
+stream_established(_Event, StateData) ->
+ {next_state, stream_established, StateData}.
-%%----------------------------------------------------------------------
-%% Func: handle_event/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
-%%----------------------------------------------------------------------
-%% Func: handle_sync_event/4
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {reply, Reply, NextStateName, NextStateData} |
-%% {reply, Reply, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData} |
-%% {stop, Reason, Reply, NewStateData}
-%%----------------------------------------------------------------------
-handle_sync_event({get_delegated_ns}, _From, StateName, StateData) ->
- Reply = {StateData#state.host, StateData#state.delegations},
- {reply, Reply, StateName, StateData};
-
-handle_sync_event(_Event, _From, StateName, StateData) ->
+handle_sync_event(_Event, _From, StateName,
+ StateData) ->
Reply = ok, {reply, Reply, StateName, StateData}.
code_change(_OldVsn, StateName, StateData, _Extra) ->
{ok, StateName, StateData}.
-%%----------------------------------------------------------------------
-%% Func: handle_info/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text),
{next_state, StateName, StateData};
@@ -397,64 +258,20 @@ handle_info({send_element, El}, StateName, StateData) ->
{next_state, StateName, StateData};
handle_info({route, From, To, Packet}, StateName,
StateData) ->
- case acl:match_rule(global, StateData#state.access,
- From)
- of
+ case acl:match_rule(global, StateData#state.access, From) of
allow ->
- #xmlel{name = Name, attrs = Attrs, children = Els} =
- Packet,
- Attrs2 =
- jlib:replace_from_to_attrs(jid:to_string(From),
- jid:to_string(To), Attrs),
- Text = fxml:element_to_binary(#xmlel{name = Name,
- attrs = Attrs2, children = Els}),
- send_text(StateData, Text);
- deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
- Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_NOT_ALLOWED(Lang, Txt)),
- ejabberd_router:route_error(To, From, Err, Packet)
+ Pkt = xmpp:set_from_to(Packet, From, To),
+ send_element(StateData, Pkt);
+ deny ->
+ Lang = xmpp:get_lang(Packet),
+ Err = xmpp:err_not_allowed(<<"Denied by ACL">>, Lang),
+ ejabberd_router:route_error(To, From, Packet, Err)
end,
{next_state, StateName, StateData};
-
-handle_info({user_presence, Packet, From},
- stream_established, StateData) ->
- To = jid:from_string(StateData#state.host),
- PacketNew = jlib:replace_from_to(From, To, Packet),
- send_element(StateData, PacketNew),
- {next_state, stream_established, StateData};
-
-handle_info({roster_presence, Packet, From},
- stream_established, StateData) ->
- %% check that current presence stanza is equivalent to last
- PresenceNew = jlib:remove_attr(<<"to">>, Packet),
- Dict = StateData#state.last_pres,
- LastPresence =
- try dict:fetch(From, Dict)
- catch _:_ ->
- undefined
- end,
- case mod_privilege:compare_presences(LastPresence, PresenceNew) of
- false ->
- #xmlel{attrs = Attrs} = PresenceNew,
- Presence = PresenceNew#xmlel{attrs = [{<<"to">>, StateData#state.host} | Attrs]},
- send_element(StateData, Presence),
- DictNew = dict:store(From, PresenceNew, Dict),
- StateDataNew = StateData#state{last_pres = DictNew},
- {next_state, stream_established, StateDataNew};
- _ ->
- {next_state, stream_established, StateData}
- end;
-
handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
{next_state, StateName, StateData}.
-%%----------------------------------------------------------------------
-%% Func: terminate/3
-%% Purpose: Shutdown the fsm
-%% Returns: any
-%%----------------------------------------------------------------------
terminate(Reason, StateName, StateData) ->
?INFO_MSG("terminated: ~p", [Reason]),
case StateName of
@@ -462,30 +279,12 @@ terminate(Reason, StateName, StateData) ->
lists:foreach(fun (H) ->
ejabberd_router:unregister_route(H),
ejabberd_hooks:run(component_disconnected,
- [StateData#state.host, Reason])
+ [H, Reason])
end,
- dict:fetch_keys(StateData#state.host_opts)),
-
- lists:foreach(fun({Ns, _FilterAttr}) ->
- ets:delete(delegated_namespaces, Ns),
- remove_iq_handlers(Ns)
- end, StateData#state.delegations),
-
- RosterAccess = proplists:get_value(roster, StateData#state.privilege_access),
- case proplists:get_value(presence, StateData#state.privilege_access) of
- <<"managed_entity">> ->
- Fun = mod_privilege:process_presence(self()),
- remove_hooks(user_send_packet, Fun);
- <<"roster">> when (RosterAccess == <<"both">>) or
- (RosterAccess == <<"get">>) ->
- Fun = mod_privilege:process_presence(self()),
- remove_hooks(user_send_packet, Fun),
- Fun2 = mod_privilege:process_roster_presence(self()),
- remove_hooks(s2s_receive_packet, Fun2);
- _ -> ok
- end;
+ dict:fetch_keys(StateData#state.host_opts));
_ -> ok
end,
+ catch send_trailer(StateData),
(StateData#state.sockmod):close(StateData#state.socket),
ok.
@@ -500,13 +299,68 @@ print_state(State) -> State.
%%% Internal functions
%%%----------------------------------------------------------------------
+-spec send_text(state(), iodata()) -> ok.
send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket,
Text).
+-spec send_element(state(), xmpp_element()) -> ok.
send_element(StateData, El) ->
- send_text(StateData, fxml:element_to_binary(El)).
+ El1 = xmpp:encode(El, ?NS_COMPONENT),
+ send_text(StateData, fxml:element_to_binary(El1)).
+
+-spec send_error(state(), xmlel() | stanza(), stanza_error()) -> ok.
+send_error(StateData, Stanza, Error) ->
+ Type = xmpp:get_type(Stanza),
+ if Type == error; Type == result;
+ Type == <<"error">>; Type == <<"result">> ->
+ ok;
+ true ->
+ send_element(StateData, xmpp:make_error(Stanza, Error))
+ end.
+-spec send_header(state(), binary()) -> ok.
+send_header(StateData, Host) ->
+ Header = xmpp:encode(
+ #stream_start{xmlns = ?NS_COMPONENT,
+ stream_xmlns = ?NS_STREAM,
+ from = jid:make(Host),
+ id = StateData#state.streamid}),
+ send_text(StateData, fxml:element_to_header(Header)).
+
+-spec send_trailer(state()) -> ok.
+send_trailer(StateData) ->
+ send_text(StateData, <<"</stream:stream>">>).
+
+-spec decode_element(xmlel(), state_name(), state()) -> fsm_transition().
+decode_element(#xmlel{} = El, StateName, StateData) ->
+ try xmpp:decode(El, ?NS_COMPONENT, [ignore_els]) of
+ Pkt -> ?MODULE:StateName(Pkt, StateData)
+ catch error:{xmpp_codec, Why} ->
+ case xmpp:is_stanza(El) of
+ true ->
+ Lang = xmpp:get_lang(El),
+ Txt = xmpp:format_error(Why),
+ send_error(StateData, El, xmpp:err_bad_request(Txt, Lang));
+ false ->
+ ok
+ end,
+ {next_state, StateName, StateData}
+ end.
+
+-spec check_from(jid(), state()) -> boolean().
+check_from(_From, #state{check_from = false}) ->
+ %% If the admin does not want to check the from field
+ %% when accept packets from any address.
+ %% In this case, the component can send packet of
+ %% behalf of the server users.
+ true;
+check_from(From, StateData) ->
+ %% The default is the standard behaviour in XEP-0114
+ Server = From#jid.lserver,
+ dict:is_key(Server, StateData#state.host_opts).
+
+-spec new_id() -> binary().
new_id() -> randoms:get_string().
transform_listen_option({hosts, Hosts, O}, Opts) ->
@@ -543,19 +397,3 @@ fsm_limit_opts(Opts) ->
opt_type(max_fsm_queue) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(_) -> [max_fsm_queue].
-
-remove_iq_handlers(Ns) ->
- lists:foreach(fun(Host) ->
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, Ns),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, Ns)
- end, ?MYHOSTS).
-
-add_hooks(Hook, Fun) ->
- lists:foreach(fun(Host) ->
- ejabberd_hooks:add(Hook, Host,Fun, 100)
- end, ?MYHOSTS).
-
-remove_hooks(Hook, Fun) ->
- lists:foreach(fun(Host) ->
- ejabberd_hooks:delete(Hook, Host, Fun, 100)
- end, ?MYHOSTS).