aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_c2s.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/ejabberd_c2s.erl')
-rw-r--r--src/ejabberd_c2s.erl3102
1 files changed, 1542 insertions, 1560 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index ed26400f0..f1cde0e0f 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -25,7 +25,9 @@
%%%----------------------------------------------------------------------
-module(ejabberd_c2s).
+
-author('alexey@process-one.net').
+
-update_info({update, 0}).
-define(GEN_FSM, p1_fsm).
@@ -61,11 +63,13 @@
code_change/4,
handle_info/3,
terminate/3,
- print_state/1
+ print_state/1
]).
-include("ejabberd.hrl").
+
-include("jlib.hrl").
+
-include("mod_privacy.hrl").
-define(SETS, gb_sets).
@@ -88,7 +92,7 @@
tls_options = [],
authenticated = false,
jid,
- user = "", server = ?MYNAME, resource = "",
+ user = "", server = ?MYNAME, resource = <<"">>,
sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
@@ -107,9 +111,13 @@
%-define(DBGFSM, true).
-ifdef(DBGFSM).
+
-define(FSMOPTS, [{debug, [trace]}]).
+
-else.
+
-define(FSMOPTS, []).
+
-endif.
%% Module start with or without supervisor:
@@ -124,22 +132,26 @@
%% This is the timeout to apply between event when starting a new
%% session:
-define(C2S_OPEN_TIMEOUT, 60000).
+
-define(C2S_HIBERNATE_TIMEOUT, 90000).
-define(STREAM_HEADER,
- "<?xml version='1.0'?>"
- "<stream:stream xmlns='jabber:client' "
- "xmlns:stream='http://etherx.jabber.org/streams' "
- "id='~s' from='~s'~s~s>"
- ).
+ <<"<?xml version='1.0'?><stream:stream "
+ "xmlns='jabber:client' xmlns:stream='http://et"
+ "herx.jabber.org/streams' id='~s' from='~s'~s~"
+ "s>">>).
--define(STREAM_TRAILER, "</stream:stream>").
+-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(INVALID_NS_ERR, ?SERR_INVALID_NAMESPACE).
+
-define(INVALID_XML_ERR, ?SERR_XML_NOT_WELL_FORMED).
+
-define(HOST_UNKNOWN_ERR, ?SERR_HOST_UNKNOWN).
+
-define(POLICY_VIOLATION_ERR(Lang, Text),
?SERRT_POLICY_VIOLATION(Lang, Text)).
+
-define(INVALID_FROM, ?SERR_INVALID_FROM).
@@ -153,24 +165,23 @@ start_link(SockData, Opts) ->
?GEN_FSM:start_link(ejabberd_c2s, [SockData, Opts],
fsm_limit_opts(Opts) ++ ?FSMOPTS).
-socket_type() ->
- xml_stream.
+socket_type() -> xml_stream.
%% Return Username, Resource and presence information
get_presence(FsmRef) ->
- ?GEN_FSM:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
+ (?GEN_FSM):sync_send_all_state_event(FsmRef,
+ {get_presence}, 1000).
get_aux_field(Key, #state{aux_fields = Opts}) ->
case lists:keysearch(Key, 1, Opts) of
- {value, {_, Val}} ->
- {ok, Val};
- _ ->
- error
+ {value, {_, Val}} -> {ok, Val};
+ _ -> error
end.
-set_aux_field(Key, Val, #state{aux_fields = Opts} = State) ->
+set_aux_field(Key, Val,
+ #state{aux_fields = Opts} = State) ->
Opts1 = lists:keydelete(Key, 1, Opts),
- State#state{aux_fields = [{Key, Val}|Opts1]}.
+ State#state{aux_fields = [{Key, Val} | Opts1]}.
del_aux_field(Key, #state{aux_fields = Opts} = State) ->
Opts1 = lists:keydelete(Key, 1, Opts),
@@ -179,11 +190,13 @@ del_aux_field(Key, #state{aux_fields = Opts} = State) ->
get_subscription(From = #jid{}, StateData) ->
get_subscription(jlib:jid_tolower(From), StateData);
get_subscription(LFrom, StateData) ->
- LBFrom = setelement(3, LFrom, ""),
- F = ?SETS:is_element(LFrom, StateData#state.pres_f) orelse
- ?SETS:is_element(LBFrom, StateData#state.pres_f),
- T = ?SETS:is_element(LFrom, StateData#state.pres_t) orelse
- ?SETS:is_element(LBFrom, StateData#state.pres_t),
+ LBFrom = setelement(3, LFrom, <<"">>),
+ F = (?SETS):is_element(LFrom, StateData#state.pres_f)
+ orelse
+ (?SETS):is_element(LBFrom, StateData#state.pres_f),
+ T = (?SETS):is_element(LFrom, StateData#state.pres_t)
+ orelse
+ (?SETS):is_element(LBFrom, StateData#state.pres_t),
if F and T -> both;
F -> from;
T -> to;
@@ -193,8 +206,7 @@ get_subscription(LFrom, StateData) ->
broadcast(FsmRef, Type, From, Packet) ->
FsmRef ! {broadcast, Type, From, Packet}.
-stop(FsmRef) ->
- ?GEN_FSM:send_event(FsmRef, closed).
+stop(FsmRef) -> (?GEN_FSM):send_event(FsmRef, closed).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
@@ -209,63 +221,58 @@ stop(FsmRef) ->
%%----------------------------------------------------------------------
init([{SockMod, Socket}, Opts]) ->
Access = case lists:keysearch(access, 1, Opts) of
- {value, {_, A}} -> A;
- _ -> all
+ {value, {_, A}} -> A;
+ _ -> all
end,
Shaper = case lists:keysearch(shaper, 1, Opts) of
- {value, {_, S}} -> S;
- _ -> none
+ {value, {_, S}} -> S;
+ _ -> none
end,
- XMLSocket =
- case lists:keysearch(xml_socket, 1, Opts) of
- {value, {_, XS}} -> XS;
- _ -> false
- end,
+ XMLSocket = case lists:keysearch(xml_socket, 1, Opts) of
+ {value, {_, XS}} -> XS;
+ _ -> false
+ end,
Zlib = lists:member(zlib, Opts),
StartTLS = lists:member(starttls, Opts),
- StartTLSRequired = lists:member(starttls_required, Opts),
+ StartTLSRequired = lists:member(starttls_required,
+ Opts),
TLSEnabled = lists:member(tls, Opts),
- TLS = StartTLS orelse StartTLSRequired orelse TLSEnabled,
- TLSOpts1 =
- lists:filter(fun({certfile, _}) -> true;
- (_) -> false
- end, Opts),
+ TLS = StartTLS orelse
+ StartTLSRequired orelse TLSEnabled,
+ TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
+ (_) -> false
+ end,
+ Opts),
TLSOpts = [verify_none | TLSOpts1],
IP = peerip(SockMod, Socket),
%% Check if IP is blacklisted:
case is_ip_blacklisted(IP) of
- true ->
- ?INFO_MSG("Connection attempt from blacklisted IP: ~s (~w)",
- [jlib:ip_to_list(IP), IP]),
- {stop, normal};
- false ->
- Socket1 =
- if
- TLSEnabled ->
- SockMod:starttls(Socket, TLSOpts);
- true ->
- Socket
- end,
- SocketMonitor = SockMod:monitor(Socket1),
- {ok, wait_for_stream, #state{socket = Socket1,
- sockmod = SockMod,
- socket_monitor = SocketMonitor,
- xml_socket = XMLSocket,
- zlib = Zlib,
- tls = TLS,
- tls_required = StartTLSRequired,
- tls_enabled = TLSEnabled,
- tls_options = TLSOpts,
- streamid = new_id(),
- access = Access,
- shaper = Shaper,
- ip = IP},
- ?C2S_OPEN_TIMEOUT}
+ true ->
+ ?INFO_MSG("Connection attempt from blacklisted "
+ "IP: ~s (~w)",
+ [jlib:ip_to_list(IP), IP]),
+ {stop, normal};
+ false ->
+ Socket1 = if TLSEnabled andalso
+ SockMod /= ejabberd_frontend_socket ->
+ SockMod:starttls(Socket, TLSOpts);
+ true -> Socket
+ end,
+ SocketMonitor = SockMod:monitor(Socket1),
+ StateData = #state{socket = Socket1, sockmod = SockMod,
+ socket_monitor = SocketMonitor,
+ xml_socket = XMLSocket, zlib = Zlib, tls = TLS,
+ tls_required = StartTLSRequired,
+ tls_enabled = TLSEnabled, tls_options = TLSOpts,
+ streamid = new_id(), access = Access,
+ shaper = Shaper, ip = IP},
+ {ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end.
%% Return list of all available resources of contacts,
get_subscribed(FsmRef) ->
- ?GEN_FSM:sync_send_all_state_event(FsmRef, get_subscribed, 1000).
+ (?GEN_FSM):sync_send_all_state_event(FsmRef,
+ get_subscribed, 1000).
%%----------------------------------------------------------------------
%% Func: StateName/2
@@ -276,17 +283,15 @@ get_subscribed(FsmRef) ->
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
DefaultLang = case ?MYLANG of
- undefined ->
- "en";
- DL ->
- DL
- end,
- case xml:get_attr_s("xmlns:stream", Attrs) of
+ undefined -> <<"en">>;
+ DL -> DL
+ end,
+ case xml:get_attr_s(<<"xmlns:stream">>, Attrs) of
?NS_STREAM ->
- Server = jlib:nameprep(xml:get_attr_s("to", Attrs)),
+ Server = jlib:nameprep(xml:get_attr_s(<<"to">>, Attrs)),
case lists:member(Server, ?MYHOSTS) of
true ->
- Lang = case xml:get_attr_s("xml:lang", Attrs) of
+ Lang = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
Lang1 when length(Lang1) =< 35 ->
%% As stated in BCP47, 4.4.1:
%% Protocols or specifications that
@@ -297,17 +302,17 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
_ ->
%% Do not store long language tag to
%% avoid possible DoS/flood attacks
- ""
+ <<"">>
end,
- change_shaper(StateData, jlib:make_jid("", Server, "")),
- case xml:get_attr_s("version", Attrs) of
- "1.0" ->
- send_header(StateData, Server, "1.0", DefaultLang),
+ change_shaper(StateData, jlib:make_jid(<<"">>, Server, <<"">>)),
+ case xml:get_attr_s(<<"version">>, Attrs) of
+ <<"1.0">> ->
+ send_header(StateData, Server, <<"1.0">>, DefaultLang),
case StateData#state.authenticated of
false ->
SASLState =
cyrsasl:server_new(
- "jabber", Server, "", [],
+ <<"jabber">>, Server, <<"">>, [],
fun(U) ->
ejabberd_auth:get_password_with_authmodule(
U, Server)
@@ -320,11 +325,12 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
ejabberd_auth:check_password_with_authmodule(
U, Server, P, D, DG)
end),
- Mechs = lists:map(
- fun(S) ->
- {xmlelement, "mechanism", [],
- [{xmlcdata, S}]}
- end, cyrsasl:listmech(Server)),
+ Mechs = lists:map(fun (S) ->
+ #xmlel{name = <<"mechanism">>,
+ attrs = [],
+ children = [{xmlcdata, S}]}
+ end,
+ cyrsasl:listmech(Server)),
SockMod =
(StateData#state.sockmod):get_sockmod(
StateData#state.socket),
@@ -334,10 +340,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
((SockMod == gen_tcp) orelse
(SockMod == tls)) of
true ->
- [{xmlelement, "compression",
- [{"xmlns", ?NS_FEATURE_COMPRESS}],
- [{xmlelement, "method",
- [], [{xmlcdata, "zlib"}]}]}];
+ [#xmlel{name = <<"compression">>,
+ attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
+ children = [#xmlel{name = <<"method">>,
+ attrs = [],
+ children = [{xmlcdata, <<"zlib">>}]}]}];
_ ->
[]
end,
@@ -351,27 +358,30 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
true ->
case TLSRequired of
true ->
- [{xmlelement, "starttls",
- [{"xmlns", ?NS_TLS}],
- [{xmlelement, "required",
- [], []}]}];
+ [#xmlel{name = <<"starttls">>,
+ attrs = [{<<"xmlns">>, ?NS_TLS}],
+ children = [#xmlel{name = <<"required">>,
+ attrs = [],
+ children = []}]}];
_ ->
- [{xmlelement, "starttls",
- [{"xmlns", ?NS_TLS}], []}]
+ [#xmlel{name = <<"starttls">>,
+ attrs = [{<<"xmlns">>, ?NS_TLS}],
+ children = []}]
end;
false ->
[]
end,
send_element(StateData,
- {xmlelement, "stream:features", [],
- TLSFeature ++ CompressFeature ++
- [{xmlelement, "mechanisms",
- [{"xmlns", ?NS_SASL}],
- Mechs}] ++
- ejabberd_hooks:run_fold(
- c2s_stream_features,
- Server,
- [], [Server])}),
+ #xmlel{name = <<"stream:features">>,
+ attrs = [],
+ children =
+ TLSFeature ++ CompressFeature ++
+ [#xmlel{name = <<"mechanisms">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children = Mechs}]
+ ++
+ ejabberd_hooks:run_fold(c2s_stream_features,
+ Server, [], [Server])}),
fsm_next_state(wait_for_feature_request,
StateData#state{
server = Server,
@@ -379,556 +389,578 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
lang = Lang});
_ ->
case StateData#state.resource of
- "" ->
- RosterVersioningFeature =
- ejabberd_hooks:run_fold(
- roster_get_versioning_feature,
- Server, [], [Server]),
- StreamFeatures =
- [{xmlelement, "bind",
- [{"xmlns", ?NS_BIND}], []},
- {xmlelement, "session",
- [{"xmlns", ?NS_SESSION}], []}]
- ++ RosterVersioningFeature
- ++ ejabberd_hooks:run_fold(
- c2s_stream_features,
- Server,
- [], [Server]),
- send_element(
- StateData,
- {xmlelement, "stream:features", [],
- StreamFeatures}),
- fsm_next_state(wait_for_bind,
- StateData#state{
- server = Server,
- lang = Lang});
- _ ->
- send_element(
- StateData,
- {xmlelement, "stream:features", [], []}),
- fsm_next_state(wait_for_session,
- StateData#state{
- server = Server,
- lang = Lang})
+ <<"">> ->
+ RosterVersioningFeature =
+ ejabberd_hooks:run_fold(roster_get_versioning_feature,
+ Server, [],
+ [Server]),
+ StreamFeatures = [#xmlel{name = <<"bind">>,
+ attrs = [{<<"xmlns">>, ?NS_BIND}],
+ children = []},
+ #xmlel{name = <<"session">>,
+ attrs = [{<<"xmlns">>, ?NS_SESSION}],
+ children = []}]
+ ++
+ RosterVersioningFeature ++
+ ejabberd_hooks:run_fold(c2s_stream_features,
+ Server, [], [Server]),
+ send_element(StateData,
+ #xmlel{name = <<"stream:features">>,
+ attrs = [],
+ children = StreamFeatures}),
+ fsm_next_state(wait_for_bind,
+ StateData#state{server = Server, lang = Lang});
+ _ ->
+ send_element(StateData,
+ #xmlel{name = <<"stream:features">>,
+ attrs = [],
+ children = []}),
+ fsm_next_state(wait_for_session,
+ StateData#state{server = Server, lang = Lang})
end
end;
- _ ->
- send_header(StateData, Server, "", DefaultLang),
- if
- (not StateData#state.tls_enabled) and
- StateData#state.tls_required ->
- send_element(
- StateData,
- ?POLICY_VIOLATION_ERR(
- Lang,
- "Use of STARTTLS required")),
- send_trailer(StateData),
- {stop, normal, StateData};
- true ->
- fsm_next_state(wait_for_auth,
- StateData#state{
- server = Server,
- lang = Lang})
- end
- end;
_ ->
- send_header(StateData, ?MYNAME, "", DefaultLang),
- send_element(StateData, ?HOST_UNKNOWN_ERR),
- send_trailer(StateData),
- {stop, normal, StateData}
+ send_header(StateData, Server, <<"">>, DefaultLang),
+ if not StateData#state.tls_enabled and
+ StateData#state.tls_required ->
+ send_element(StateData,
+ ?POLICY_VIOLATION_ERR(Lang,
+ <<"Use of STARTTLS required">>)),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ true ->
+ fsm_next_state(wait_for_auth,
+ StateData#state{server = Server,
+ lang = Lang})
+ end
end;
_ ->
- send_header(StateData, ?MYNAME, "", DefaultLang),
- send_element(StateData, ?INVALID_NS_ERR),
+ send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
+ send_element(StateData, ?HOST_UNKNOWN_ERR),
send_trailer(StateData),
{stop, normal, StateData}
+ end;
+ _ ->
+ send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
+ send_element(StateData, ?INVALID_NS_ERR),
+ send_trailer(StateData),
+ {stop, normal, StateData}
end;
-
wait_for_stream(timeout, StateData) ->
{stop, normal, StateData};
-
wait_for_stream({xmlstreamelement, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_stream({xmlstreamend, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_stream({xmlstreamerror, _}, StateData) ->
- send_header(StateData, ?MYNAME, "1.0", ""),
+ send_header(StateData, ?MYNAME, <<"1.0">>, <<"">>),
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_stream(closed, StateData) ->
{stop, normal, StateData}.
-
wait_for_auth({xmlstreamelement, El}, StateData) ->
case is_auth_packet(El) of
- {auth, _ID, get, {U, _, _, _}} ->
- {xmlelement, Name, Attrs, _Els} = jlib:make_result_iq_reply(El),
- case U of
- "" ->
- UCdata = [];
- _ ->
- UCdata = [{xmlcdata, U}]
- end,
- Res = case ejabberd_auth:plain_password_required(
- StateData#state.server) of
- false ->
- {xmlelement, Name, Attrs,
- [{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
- [{xmlelement, "username", [], UCdata},
- {xmlelement, "password", [], []},
- {xmlelement, "digest", [], []},
- {xmlelement, "resource", [], []}
- ]}]};
- true ->
- {xmlelement, Name, Attrs,
- [{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
- [{xmlelement, "username", [], UCdata},
- {xmlelement, "password", [], []},
- {xmlelement, "resource", [], []}
- ]}]}
- end,
- send_element(StateData, Res),
- fsm_next_state(wait_for_auth, StateData);
- {auth, _ID, set, {_U, _P, _D, ""}} ->
- Err = jlib:make_error_reply(
- El,
- ?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
- send_element(StateData, Err),
- fsm_next_state(wait_for_auth, StateData);
- {auth, _ID, set, {U, P, D, R}} ->
- JID = jlib:make_jid(U, StateData#state.server, R),
- case (JID /= error) andalso
- (acl:match_rule(StateData#state.server,
- StateData#state.access, JID) == allow) of
- true ->
- DGen = fun(PW) ->
- sha:sha(StateData#state.streamid ++ PW) end,
- case ejabberd_auth:check_password_with_authmodule(
- U, StateData#state.server, P, D, DGen) of
- {true, AuthModule} ->
- ?INFO_MSG(
- "(~w) Accepted legacy authentication for ~s by ~p",
- [StateData#state.socket,
- jlib:jid_to_string(JID), AuthModule]),
- SID = {now(), self()},
- Conn = get_conn_type(StateData),
- Info = [{ip, StateData#state.ip}, {conn, Conn},
+ {auth, _ID, get, {U, _, _, _}} ->
+ #xmlel{name = Name, attrs = Attrs} =
+ jlib:make_result_iq_reply(El),
+ case U of
+ <<"">> -> UCdata = [];
+ _ -> UCdata = [{xmlcdata, U}]
+ end,
+ Res = case
+ ejabberd_auth:plain_password_required(StateData#state.server)
+ of
+ false ->
+ #xmlel{name = Name, attrs = Attrs,
+ children =
+ [#xmlel{name = <<"query">>,
+ attrs = [{<<"xmlns">>, ?NS_AUTH}],
+ children =
+ [#xmlel{name = <<"username">>,
+ attrs = [],
+ children = UCdata},
+ #xmlel{name = <<"password">>,
+ attrs = [], children = []},
+ #xmlel{name = <<"digest">>,
+ attrs = [], children = []},
+ #xmlel{name = <<"resource">>,
+ attrs = [],
+ children = []}]}]};
+ true ->
+ #xmlel{name = Name, attrs = Attrs,
+ children =
+ [#xmlel{name = <<"query">>,
+ attrs = [{<<"xmlns">>, ?NS_AUTH}],
+ children =
+ [#xmlel{name = <<"username">>,
+ attrs = [],
+ children = UCdata},
+ #xmlel{name = <<"password">>,
+ attrs = [], children = []},
+ #xmlel{name = <<"resource">>,
+ attrs = [],
+ children = []}]}]}
+ end,
+ send_element(StateData, Res),
+ fsm_next_state(wait_for_auth, StateData);
+ {auth, _ID, set, {_U, _P, _D, <<"">>}} ->
+ Err = jlib:make_error_reply(El,
+ ?ERR_AUTH_NO_RESOURCE_PROVIDED((StateData#state.lang))),
+ send_element(StateData, Err),
+ fsm_next_state(wait_for_auth, StateData);
+ {auth, _ID, set, {U, P, D, R}} ->
+ JID = jlib:make_jid(U, StateData#state.server, R),
+ case JID /= error andalso
+ acl:match_rule(StateData#state.server,
+ StateData#state.access, JID)
+ == allow
+ of
+ true ->
+ DGen = fun (PW) ->
+ sha:sha(<<(StateData#state.streamid)/binary, PW/binary>>)
+ end,
+ case ejabberd_auth:check_password_with_authmodule(U,
+ StateData#state.server,
+ P, D, DGen)
+ of
+ {true, AuthModule} ->
+ ?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p",
+ [StateData#state.socket,
+ jlib:jid_to_string(JID), AuthModule]),
+ SID = {now(), self()},
+ Conn = (StateData#state.sockmod):get_conn_type(
+ StateData#state.socket),
+ Info = [{ip, StateData#state.ip}, {conn, Conn},
{auth_module, AuthModule}],
- Res1 = jlib:make_result_iq_reply(El),
- Res = setelement(4, Res1, []),
- send_element(StateData, Res),
- ejabberd_sm:open_session(
- SID, U, StateData#state.server, R, Info),
- change_shaper(StateData, JID),
- {Fs, Ts} = ejabberd_hooks:run_fold(
- roster_get_subscription_lists,
- StateData#state.server,
- {[], []},
- [U, StateData#state.server]),
- LJID = jlib:jid_tolower(
- jlib:jid_remove_resource(JID)),
- Fs1 = [LJID | Fs],
- Ts1 = [LJID | Ts],
- PrivList =
- ejabberd_hooks:run_fold(
- privacy_get_user_list, StateData#state.server,
- #userlist{},
- [U, StateData#state.server]),
- NewStateData =
- StateData#state{
- user = U,
- resource = R,
- jid = JID,
- sid = SID,
- conn = Conn,
- auth_module = AuthModule,
- pres_f = ?SETS:from_list(Fs1),
- pres_t = ?SETS:from_list(Ts1),
- privacy_list = PrivList},
- fsm_next_state_pack(session_established,
- NewStateData);
- _ ->
- IP = peerip(StateData#state.sockmod, StateData#state.socket),
- ?INFO_MSG(
- "(~w) Failed legacy authentication for ~s from IP ~s (~w)",
- [StateData#state.socket,
- jlib:jid_to_string(JID), jlib:ip_to_list(IP), IP]),
- Err = jlib:make_error_reply(
- El, ?ERR_NOT_AUTHORIZED),
- send_element(StateData, Err),
- fsm_next_state(wait_for_auth, StateData)
- end;
- _ ->
- if
- JID == error ->
- ?INFO_MSG(
- "(~w) Forbidden legacy authentication for "
- "username '~s' with resource '~s'",
- [StateData#state.socket, U, R]),
- Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
- send_element(StateData, Err),
- fsm_next_state(wait_for_auth, StateData);
- true ->
- ?INFO_MSG(
- "(~w) Forbidden legacy authentication for ~s",
- [StateData#state.socket,
- jlib:jid_to_string(JID)]),
- Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
- send_element(StateData, Err),
- fsm_next_state(wait_for_auth, StateData)
- end
- end;
- _ ->
- process_unauthenticated_stanza(StateData, El),
- fsm_next_state(wait_for_auth, StateData)
+ Res1 = jlib:make_result_iq_reply(El),
+ Res = Res1#xmlel{children = []},
+ send_element(StateData, Res),
+ ejabberd_sm:open_session(SID, U, StateData#state.server, R, Info),
+ change_shaper(StateData, JID),
+ {Fs, Ts} =
+ ejabberd_hooks:run_fold(roster_get_subscription_lists,
+ StateData#state.server,
+ {[], []},
+ [U,
+ StateData#state.server]),
+ LJID =
+ jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+ Fs1 = [LJID | Fs],
+ Ts1 = [LJID | Ts],
+ PrivList = ejabberd_hooks:run_fold(privacy_get_user_list,
+ StateData#state.server,
+ #userlist{},
+ [U, StateData#state.server]),
+ NewStateData = StateData#state{user = U,
+ resource = R,
+ jid = JID, sid = SID,
+ conn = Conn,
+ auth_module = AuthModule,
+ pres_f = (?SETS):from_list(Fs1),
+ pres_t = (?SETS):from_list(Ts1),
+ privacy_list = PrivList},
+ fsm_next_state(session_established, NewStateData);
+ _ ->
+ IP = peerip(StateData#state.sockmod,
+ StateData#state.socket),
+ ?INFO_MSG("(~w) Failed legacy authentication for "
+ "~s from IP ~s",
+ [StateData#state.socket,
+ jlib:jid_to_string(JID), jlib:ip_to_list(IP)]),
+ Err = jlib:make_error_reply(El, ?ERR_NOT_AUTHORIZED),
+ send_element(StateData, Err),
+ fsm_next_state(wait_for_auth, StateData)
+ end;
+ _ ->
+ if JID == error ->
+ ?INFO_MSG("(~w) Forbidden legacy authentication "
+ "for username '~s' with resource '~s'",
+ [StateData#state.socket, U, R]),
+ Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
+ send_element(StateData, Err),
+ fsm_next_state(wait_for_auth, StateData);
+ true ->
+ ?INFO_MSG("(~w) Forbidden legacy authentication "
+ "for ~s",
+ [StateData#state.socket,
+ jlib:jid_to_string(JID)]),
+ Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
+ send_element(StateData, Err),
+ fsm_next_state(wait_for_auth, StateData)
+ end
+ end;
+ _ ->
+ process_unauthenticated_stanza(StateData, El),
+ fsm_next_state(wait_for_auth, StateData)
end;
-
wait_for_auth(timeout, StateData) ->
{stop, normal, StateData};
-
wait_for_auth({xmlstreamend, _Name}, StateData) ->
- send_trailer(StateData),
- {stop, normal, StateData};
-
+ send_trailer(StateData), {stop, normal, StateData};
wait_for_auth({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_auth(closed, StateData) ->
{stop, normal, StateData}.
-
-wait_for_feature_request({xmlstreamelement, El}, StateData) ->
- {xmlelement, Name, Attrs, Els} = El,
+wait_for_feature_request({xmlstreamelement, El},
+ StateData) ->
+ #xmlel{name = Name, attrs = Attrs, children = Els} = El,
Zlib = StateData#state.zlib,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
TLSRequired = StateData#state.tls_required,
- SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
- case {xml:get_attr_s("xmlns", Attrs), Name} of
- {?NS_SASL, "auth"} when not ((SockMod == gen_tcp) and TLSRequired) ->
- Mech = xml:get_attr_s("mechanism", Attrs),
- ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
- case cyrsasl:server_start(StateData#state.sasl_state,
- Mech,
- ClientIn) of
- {ok, Props} ->
- (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}], []}),
- U = xml:get_attr_s(username, Props),
- AuthModule = xml:get_attr_s(auth_module, Props),
- ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
- [StateData#state.socket, U, AuthModule]),
- fsm_next_state(wait_for_stream,
- StateData#state{
- streamid = new_id(),
- authenticated = true,
- auth_module = AuthModule,
- user = U });
- {continue, ServerOut, NewSASLState} ->
- send_element(StateData,
- {xmlelement, "challenge",
- [{"xmlns", ?NS_SASL}],
- [{xmlcdata,
- jlib:encode_base64(ServerOut)}]}),
- fsm_next_state(wait_for_sasl_response,
- StateData#state{
- sasl_state = NewSASLState});
- {error, Error, Username} ->
- IP = peerip(StateData#state.sockmod, StateData#state.socket),
- ?INFO_MSG(
- "(~w) Failed authentication for ~s@~s from IP ~s (~w)",
+ SockMod =
+ (StateData#state.sockmod):get_sockmod(StateData#state.socket),
+ case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ {?NS_SASL, <<"auth">>}
+ when not ((SockMod == gen_tcp) and TLSRequired) ->
+ Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
+ ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
+ case cyrsasl:server_start(StateData#state.sasl_state,
+ Mech, ClientIn)
+ of
+ {ok, Props} ->
+ (StateData#state.sockmod):reset_stream(StateData#state.socket),
+ %U = xml:get_attr_s(username, Props),
+ U = proplists:get_value(username, Props, <<>>),
+ %AuthModule = xml:get_attr_s(auth_module, Props),
+ AuthModule = proplists:get_value(auth_module, Props, undefined),
+ ?INFO_MSG("(~w) Accepted authentication for ~s "
+ "by ~p",
+ [StateData#state.socket, U, AuthModule]),
+ send_element(StateData,
+ #xmlel{name = <<"success">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children = []}),
+ fsm_next_state(wait_for_stream,
+ StateData#state{streamid = new_id(),
+ authenticated = true,
+ auth_module = AuthModule,
+ user = U});
+ {continue, ServerOut, NewSASLState} ->
+ send_element(StateData,
+ #xmlel{name = <<"challenge">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [{xmlcdata,
+ jlib:encode_base64(ServerOut)}]}),
+ fsm_next_state(wait_for_sasl_response,
+ StateData#state{sasl_state = NewSASLState});
+ {error, Error, Username} ->
+ IP = peerip(StateData#state.sockmod, StateData#state.socket),
+ ?INFO_MSG("(~w) Failed authentication for ~s@~s from IP ~s",
[StateData#state.socket,
- Username, StateData#state.server, jlib:ip_to_list(IP), IP]),
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_SASL}],
- [{xmlelement, Error, [], []}]}),
- {next_state, wait_for_feature_request, StateData,
- ?C2S_OPEN_TIMEOUT};
- {error, Error} ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_SASL}],
- [{xmlelement, Error, [], []}]}),
- fsm_next_state(wait_for_feature_request, StateData)
- end;
- {?NS_TLS, "starttls"} when TLS == true,
- TLSEnabled == false,
- SockMod == gen_tcp ->
- TLSOpts = case ejabberd_config:get_local_option(
- {domain_certfile, StateData#state.server}) of
- undefined ->
- StateData#state.tls_options;
- CertFile ->
- [{certfile, CertFile} |
- lists:keydelete(
- certfile, 1, StateData#state.tls_options)]
- end,
- Socket = StateData#state.socket,
- TLSSocket = (StateData#state.sockmod):starttls(
- Socket, TLSOpts,
- xml:element_to_binary(
- {xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
- fsm_next_state(wait_for_stream,
- StateData#state{socket = TLSSocket,
- streamid = new_id(),
- tls_enabled = true
- });
- {?NS_COMPRESS, "compress"} when Zlib == true,
- ((SockMod == gen_tcp) or
- (SockMod == tls)) ->
- case xml:get_subtag(El, "method") of
- false ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_COMPRESS}],
- [{xmlelement, "setup-failed", [], []}]}),
- fsm_next_state(wait_for_feature_request, StateData);
- Method ->
- case xml:get_tag_cdata(Method) of
- "zlib" ->
- Socket = StateData#state.socket,
- ZlibSocket = (StateData#state.sockmod):compress(
- Socket,
- xml:element_to_binary(
- {xmlelement, "compressed",
- [{"xmlns", ?NS_COMPRESS}], []})),
- fsm_next_state(wait_for_stream,
- StateData#state{socket = ZlibSocket,
- streamid = new_id()
- });
- _ ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_COMPRESS}],
- [{xmlelement, "unsupported-method",
- [], []}]}),
- fsm_next_state(wait_for_feature_request,
- StateData)
- end
- end;
- _ ->
- if
- (SockMod == gen_tcp) and TLSRequired ->
- Lang = StateData#state.lang,
- send_element(StateData, ?POLICY_VIOLATION_ERR(
- Lang,
- "Use of STARTTLS required")),
- send_trailer(StateData),
- {stop, normal, StateData};
- true ->
- process_unauthenticated_stanza(StateData, El),
- fsm_next_state(wait_for_feature_request, StateData)
- end
+ Username, StateData#state.server, jlib:ip_to_list(IP)]),
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = Error, attrs = [],
+ children = []}]}),
+ {next_state, wait_for_feature_request, StateData,
+ ?C2S_OPEN_TIMEOUT};
+ {error, Error} ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = Error, attrs = [],
+ children = []}]}),
+ fsm_next_state(wait_for_feature_request, StateData)
+ end;
+ {?NS_TLS, <<"starttls">>}
+ when TLS == true, TLSEnabled == false,
+ SockMod == gen_tcp ->
+ TLSOpts = case
+ ejabberd_config:get_local_option(
+ {domain_certfile, StateData#state.server},
+ fun iolist_to_binary/1)
+ of
+ undefined -> StateData#state.tls_options;
+ CertFile ->
+ [{certfile, CertFile} | lists:keydelete(certfile, 1,
+ StateData#state.tls_options)]
+ end,
+ Socket = StateData#state.socket,
+ TLSSocket = (StateData#state.sockmod):starttls(Socket,
+ TLSOpts,
+ xml:element_to_binary(#xmlel{name
+ =
+ <<"proceed">>,
+ attrs
+ =
+ [{<<"xmlns">>,
+ ?NS_TLS}],
+ children
+ =
+ []})),
+ fsm_next_state(wait_for_stream,
+ StateData#state{socket = TLSSocket,
+ streamid = new_id(),
+ tls_enabled = true});
+ {?NS_COMPRESS, <<"compress">>}
+ when Zlib == true,
+ (SockMod == gen_tcp) or (SockMod == tls) ->
+ case xml:get_subtag(El, <<"method">>) of
+ false ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
+ children =
+ [#xmlel{name = <<"setup-failed">>,
+ attrs = [], children = []}]}),
+ fsm_next_state(wait_for_feature_request, StateData);
+ Method ->
+ case xml:get_tag_cdata(Method) of
+ <<"zlib">> ->
+ Socket = StateData#state.socket,
+ ZlibSocket = (StateData#state.sockmod):compress(Socket,
+ xml:element_to_binary(#xmlel{name
+ =
+ <<"compressed">>,
+ attrs
+ =
+ [{<<"xmlns">>,
+ ?NS_COMPRESS}],
+ children
+ =
+ []})),
+ fsm_next_state(wait_for_stream,
+ StateData#state{socket = ZlibSocket,
+ streamid = new_id()});
+ _ ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_COMPRESS}],
+ children =
+ [#xmlel{name =
+ <<"unsupported-method">>,
+ attrs = [],
+ children = []}]}),
+ fsm_next_state(wait_for_feature_request, StateData)
+ end
+ end;
+ _ ->
+ if (SockMod == gen_tcp) and TLSRequired ->
+ Lang = StateData#state.lang,
+ send_element(StateData,
+ ?POLICY_VIOLATION_ERR(Lang,
+ <<"Use of STARTTLS required">>)),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ true ->
+ process_unauthenticated_stanza(StateData, El),
+ fsm_next_state(wait_for_feature_request, StateData)
+ end
end;
-
wait_for_feature_request(timeout, StateData) ->
{stop, normal, StateData};
-
-wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
- send_trailer(StateData),
- {stop, normal, StateData};
-
-wait_for_feature_request({xmlstreamerror, _}, StateData) ->
+wait_for_feature_request({xmlstreamend, _Name},
+ StateData) ->
+ send_trailer(StateData), {stop, normal, StateData};
+wait_for_feature_request({xmlstreamerror, _},
+ StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_feature_request(closed, StateData) ->
{stop, normal, StateData}.
-
-wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
- {xmlelement, Name, Attrs, Els} = El,
- case {xml:get_attr_s("xmlns", Attrs), Name} of
- {?NS_SASL, "response"} ->
- ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
- case cyrsasl:server_step(StateData#state.sasl_state,
- ClientIn) of
- {ok, Props} ->
- (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}], []}),
- U = xml:get_attr_s(username, Props),
- AuthModule = xml:get_attr_s(auth_module, Props),
- ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
- [StateData#state.socket, U, AuthModule]),
- fsm_next_state(wait_for_stream,
- StateData#state{
- streamid = new_id(),
- authenticated = true,
- auth_module = AuthModule,
- user = U});
- {ok, Props, ServerOut} ->
- (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}],
- [{xmlcdata,
- jlib:encode_base64(ServerOut)}]}),
- U = xml:get_attr_s(username, Props),
- AuthModule = xml:get_attr_s(auth_module, Props),
- ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
- [StateData#state.socket, U, AuthModule]),
- fsm_next_state(wait_for_stream,
- StateData#state{
- streamid = new_id(),
- authenticated = true,
- auth_module = AuthModule,
- user = U});
- {continue, ServerOut, NewSASLState} ->
- send_element(StateData,
- {xmlelement, "challenge",
- [{"xmlns", ?NS_SASL}],
- [{xmlcdata,
- jlib:encode_base64(ServerOut)}]}),
- fsm_next_state(wait_for_sasl_response,
- StateData#state{sasl_state = NewSASLState});
- {error, Error, Username} ->
- IP = peerip(StateData#state.sockmod, StateData#state.socket),
- ?INFO_MSG(
- "(~w) Failed authentication for ~s@~s from IP ~s (~w)",
+wait_for_sasl_response({xmlstreamelement, El},
+ StateData) ->
+ #xmlel{name = Name, attrs = Attrs, children = Els} = El,
+ case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ {?NS_SASL, <<"response">>} ->
+ ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
+ case cyrsasl:server_step(StateData#state.sasl_state,
+ ClientIn)
+ of
+ {ok, Props} ->
+ catch
+ (StateData#state.sockmod):reset_stream(StateData#state.socket),
+% U = xml:get_attr_s(username, Props),
+ U = proplists:get_value(username, Props, <<>>),
+% AuthModule = xml:get_attr_s(auth_module, Props),
+ AuthModule = proplists:get_value(auth_module, Props, <<>>),
+ ?INFO_MSG("(~w) Accepted authentication for ~s "
+ "by ~p",
+ [StateData#state.socket, U, AuthModule]),
+ send_element(StateData,
+ #xmlel{name = <<"success">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children = []}),
+ fsm_next_state(wait_for_stream,
+ StateData#state{streamid = new_id(),
+ authenticated = true,
+ auth_module = AuthModule,
+ user = U});
+ {ok, Props, ServerOut} ->
+ (StateData#state.sockmod):reset_stream(StateData#state.socket),
+% U = xml:get_attr_s(username, Props),
+ U = proplists:get_value(username, Props, <<>>),
+% AuthModule = xml:get_attr_s(auth_module, Props),
+ AuthModule = proplists:get_value(auth_module, Props, undefined),
+ ?INFO_MSG("(~w) Accepted authentication for ~s "
+ "by ~p",
+ [StateData#state.socket, U, AuthModule]),
+ send_element(StateData,
+ #xmlel{name = <<"success">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [{xmlcdata,
+ jlib:encode_base64(ServerOut)}]}),
+ fsm_next_state(wait_for_stream,
+ StateData#state{streamid = new_id(),
+ authenticated = true,
+ auth_module = AuthModule,
+ user = U});
+ {continue, ServerOut, NewSASLState} ->
+ send_element(StateData,
+ #xmlel{name = <<"challenge">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [{xmlcdata,
+ jlib:encode_base64(ServerOut)}]}),
+ fsm_next_state(wait_for_sasl_response,
+ StateData#state{sasl_state = NewSASLState});
+ {error, Error, Username} ->
+ IP = peerip(StateData#state.sockmod, StateData#state.socket),
+ ?INFO_MSG("(~w) Failed authentication for ~s@~s from IP ~s",
[StateData#state.socket,
- Username, StateData#state.server, jlib:ip_to_list(IP), IP]),
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_SASL}],
- [{xmlelement, Error, [], []}]}),
- fsm_next_state(wait_for_feature_request, StateData);
- {error, Error} ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_SASL}],
- [{xmlelement, Error, [], []}]}),
- fsm_next_state(wait_for_feature_request, StateData)
- end;
- _ ->
- process_unauthenticated_stanza(StateData, El),
- fsm_next_state(wait_for_feature_request, StateData)
+ Username, StateData#state.server, jlib:ip_to_list(IP)]),
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = Error, attrs = [],
+ children = []}]}),
+ fsm_next_state(wait_for_feature_request, StateData);
+ {error, Error} ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = Error, attrs = [],
+ children = []}]}),
+ fsm_next_state(wait_for_feature_request, StateData)
+ end;
+ _ ->
+ process_unauthenticated_stanza(StateData, El),
+ fsm_next_state(wait_for_feature_request, StateData)
end;
-
wait_for_sasl_response(timeout, StateData) ->
{stop, normal, StateData};
-
-wait_for_sasl_response({xmlstreamend, _Name}, StateData) ->
- send_trailer(StateData),
- {stop, normal, StateData};
-
-wait_for_sasl_response({xmlstreamerror, _}, StateData) ->
+wait_for_sasl_response({xmlstreamend, _Name},
+ StateData) ->
+ send_trailer(StateData), {stop, normal, StateData};
+wait_for_sasl_response({xmlstreamerror, _},
+ StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_sasl_response(closed, StateData) ->
{stop, normal, StateData}.
-
resource_conflict_action(U, S, R) ->
- OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of
- true ->
- ejabberd_config:get_local_option({resource_conflict,S});
- false ->
- acceptnew
+ OptionRaw = case ejabberd_sm:is_existing_resource(U, S,
+ R)
+ of
+ true ->
+ ejabberd_config:get_local_option(
+ {resource_conflict, S},
+ fun(setresource) -> setresource;
+ (closeold) -> closeold;
+ (closenew) -> closenew;
+ (acceptnew) -> acceptnew
+ end);
+ false ->
+ acceptnew
end,
Option = case OptionRaw of
- setresource -> setresource;
- closeold -> acceptnew; %% ejabberd_sm will close old session
- closenew -> closenew;
- acceptnew -> acceptnew;
- _ -> acceptnew %% default ejabberd behavior
+ setresource -> setresource;
+ closeold ->
+ acceptnew; %% ejabberd_sm will close old session
+ closenew -> closenew;
+ acceptnew -> acceptnew;
+ _ -> acceptnew %% default ejabberd behavior
end,
case Option of
- acceptnew ->
- {accept_resource, R};
- closenew ->
- closenew;
- setresource ->
- Rnew = lists:concat([randoms:get_string() | tuple_to_list(now())]),
- {accept_resource, Rnew}
+ acceptnew -> {accept_resource, R};
+ closenew -> closenew;
+ setresource ->
+ Rnew = iolist_to_binary([randoms:get_string()
+ | tuple_to_list(now())]),
+ {accept_resource, Rnew}
end.
wait_for_bind({xmlstreamelement, El}, StateData) ->
case jlib:iq_query_info(El) of
- #iq{type = set, xmlns = ?NS_BIND, sub_el = SubEl} = IQ ->
- U = StateData#state.user,
- R1 = xml:get_path_s(SubEl, [{elem, "resource"}, cdata]),
- R = case jlib:resourceprep(R1) of
- error -> error;
- "" ->
- lists:concat(
- [randoms:get_string() | tuple_to_list(now())]);
- Resource -> Resource
- end,
- case R of
- error ->
- Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
- send_element(StateData, Err),
- fsm_next_state(wait_for_bind, StateData);
- _ ->
- %%Server = StateData#state.server,
- %%RosterVersioningFeature =
- %% ejabberd_hooks:run_fold(
- %% roster_get_versioning_feature, Server, [], [Server]),
- %%StreamFeatures = [{xmlelement, "session",
- %% [{"xmlns", ?NS_SESSION}], []} |
- %% RosterVersioningFeature],
- %%send_element(StateData, {xmlelement, "stream:features",
- %% [], StreamFeatures}),
- case resource_conflict_action(U, StateData#state.server, R) of
- closenew ->
- Err = jlib:make_error_reply(El, ?STANZA_ERROR("409", "modify", "conflict")),
- send_element(StateData, Err),
- fsm_next_state(wait_for_bind, StateData);
- {accept_resource, R2} ->
- JID = jlib:make_jid(U, StateData#state.server, R2),
- Res = IQ#iq{type = result,
- sub_el = [{xmlelement, "bind",
- [{"xmlns", ?NS_BIND}],
- [{xmlelement, "jid", [],
- [{xmlcdata,
- jlib:jid_to_string(JID)}]}]}]},
- send_element(StateData, jlib:iq_to_xml(Res)),
- fsm_next_state(wait_for_session,
- StateData#state{resource = R2, jid = JID})
- end
- end;
- _ ->
- fsm_next_state(wait_for_bind, StateData)
+ #iq{type = set, xmlns = ?NS_BIND, sub_el = SubEl} =
+ IQ ->
+ U = StateData#state.user,
+ R1 = xml:get_path_s(SubEl,
+ [{elem, <<"resource">>}, cdata]),
+ R = case jlib:resourceprep(R1) of
+ error -> error;
+ <<"">> ->
+ iolist_to_binary([randoms:get_string()
+ | tuple_to_list(now())]);
+ Resource -> Resource
+ end,
+ case R of
+ error ->
+ Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
+ send_element(StateData, Err),
+ fsm_next_state(wait_for_bind, StateData);
+ _ ->
+ case resource_conflict_action(U, StateData#state.server,
+ R)
+ of
+ closenew ->
+ Err = jlib:make_error_reply(El,
+ ?STANZA_ERROR(<<"409">>,
+ <<"modify">>,
+ <<"conflict">>)),
+ send_element(StateData, Err),
+ fsm_next_state(wait_for_bind, StateData);
+ {accept_resource, R2} ->
+ JID = jlib:make_jid(U, StateData#state.server, R2),
+ Res = IQ#iq{type = result,
+ sub_el =
+ [#xmlel{name = <<"bind">>,
+ attrs = [{<<"xmlns">>, ?NS_BIND}],
+ children =
+ [#xmlel{name = <<"jid">>,
+ attrs = [],
+ children =
+ [{xmlcdata,
+ jlib:jid_to_string(JID)}]}]}]},
+ send_element(StateData, jlib:iq_to_xml(Res)),
+ fsm_next_state(wait_for_session,
+ StateData#state{resource = R2, jid = JID})
+ end
+ end;
+ _ -> fsm_next_state(wait_for_bind, StateData)
end;
-
wait_for_bind(timeout, StateData) ->
{stop, normal, StateData};
-
wait_for_bind({xmlstreamend, _Name}, StateData) ->
- send_trailer(StateData),
- {stop, normal, StateData};
-
+ send_trailer(StateData), {stop, normal, StateData};
wait_for_bind({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_bind(closed, StateData) ->
{stop, normal, StateData}.
-
-
wait_for_session({xmlstreamelement, El}, StateData) ->
case jlib:iq_query_info(El) of
#iq{type = set, xmlns = ?NS_SESSION} ->
@@ -988,23 +1020,18 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
wait_for_session(timeout, StateData) ->
{stop, normal, StateData};
-
wait_for_session({xmlstreamend, _Name}, StateData) ->
- send_trailer(StateData),
- {stop, normal, StateData};
-
+ send_trailer(StateData), {stop, normal, StateData};
wait_for_session({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
wait_for_session(closed, StateData) ->
{stop, normal, StateData}.
-
-session_established({xmlstreamelement, El}, StateData) ->
+session_established({xmlstreamelement, El},
+ StateData) ->
FromJID = StateData#state.jid,
- % Check 'from' attribute in stanza RFC 3920 Section 9.1.2
case check_from(El, FromJID) of
'invalid-from' ->
send_element(StateData, ?INVALID_FROM),
@@ -1013,124 +1040,109 @@ session_established({xmlstreamelement, El}, StateData) ->
_NewEl ->
session_established2(El, StateData)
end;
-
%% We hibernate the process to reduce memory consumption after a
%% configurable activity timeout
session_established(timeout, StateData) ->
- %% TODO: Options must be stored in state:
Options = [],
proc_lib:hibernate(?GEN_FSM, enter_loop,
[?MODULE, Options, session_established, StateData]),
fsm_next_state(session_established, StateData);
-
session_established({xmlstreamend, _Name}, StateData) ->
+ send_trailer(StateData), {stop, normal, StateData};
+session_established({xmlstreamerror,
+ <<"XML stanza is too big">> = E},
+ StateData) ->
+ send_element(StateData,
+ ?POLICY_VIOLATION_ERR((StateData#state.lang), E)),
send_trailer(StateData),
{stop, normal, StateData};
-
-session_established({xmlstreamerror, "XML stanza is too big" = E}, StateData) ->
- send_element(StateData, ?POLICY_VIOLATION_ERR(StateData#state.lang, E)),
- send_trailer(StateData),
- {stop, normal, StateData};
-
session_established({xmlstreamerror, _}, StateData) ->
send_element(StateData, ?INVALID_XML_ERR),
send_trailer(StateData),
{stop, normal, StateData};
-
session_established(closed, StateData) ->
{stop, normal, StateData}.
%% Process packets sent by user (coming from user on c2s XMPP
%% connection)
session_established2(El, StateData) ->
- {xmlelement, Name, Attrs, _Els} = El,
+ #xmlel{name = Name, attrs = Attrs} = El,
User = StateData#state.user,
Server = StateData#state.server,
FromJID = StateData#state.jid,
- To = xml:get_attr_s("to", Attrs),
+ To = xml:get_attr_s(<<"to">>, Attrs),
ToJID = case To of
- "" ->
- jlib:make_jid(User, Server, "");
- _ ->
- jlib:string_to_jid(To)
+ <<"">> -> jlib:make_jid(User, Server, <<"">>);
+ _ -> jlib:string_to_jid(To)
end,
- NewEl1 = jlib:remove_attr("xmlns", El),
- NewEl = case xml:get_attr_s("xml:lang", Attrs) of
- "" ->
- case StateData#state.lang of
- "" -> NewEl1;
- Lang ->
- xml:replace_tag_attr("xml:lang", Lang, NewEl1)
- end;
- _ ->
- NewEl1
+ NewEl1 = jlib:remove_attr(<<"xmlns">>, El),
+ NewEl = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
+ <<"">> ->
+ case StateData#state.lang of
+ <<"">> -> NewEl1;
+ Lang ->
+ xml:replace_tag_attr(<<"xml:lang">>, Lang, NewEl1)
+ end;
+ _ -> NewEl1
end,
- NewState =
- case ToJID of
- error ->
- case xml:get_attr_s("type", Attrs) of
- "error" -> StateData;
- "result" -> StateData;
- _ ->
- Err = jlib:make_error_reply(NewEl, ?ERR_JID_MALFORMED),
- send_element(StateData, Err),
- StateData
- end;
- _ ->
- case Name of
- "presence" ->
- PresenceEl = ejabberd_hooks:run_fold(
- c2s_update_presence,
- Server,
- NewEl,
- [User, Server]),
- ejabberd_hooks:run(
- user_send_packet,
- Server,
- [FromJID, ToJID, PresenceEl]),
- case ToJID of
- #jid{user = User,
- server = Server,
- resource = ""} ->
- ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)",
- [FromJID, PresenceEl, StateData]),
- presence_update(FromJID, PresenceEl,
- StateData);
- _ ->
- presence_track(FromJID, ToJID, PresenceEl,
- StateData)
- end;
- "iq" ->
- case jlib:iq_query_info(NewEl) of
- #iq{xmlns = Xmlns} = IQ
- when Xmlns == ?NS_PRIVACY;
- Xmlns == ?NS_BLOCKING ->
- process_privacy_iq(
- FromJID, ToJID, IQ, StateData);
- _ ->
- ejabberd_hooks:run(
- user_send_packet,
- Server,
- [FromJID, ToJID, NewEl]),
- check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),
- StateData
- end;
- "message" ->
- ejabberd_hooks:run(user_send_packet,
- Server,
- [FromJID, ToJID, NewEl]),
- check_privacy_route(FromJID, StateData, FromJID,
- ToJID, NewEl),
- StateData;
- _ ->
- StateData
- end
- end,
- ejabberd_hooks:run(c2s_loop_debug, [{xmlstreamelement, El}]),
+ NewState = case ToJID of
+ error ->
+ case xml:get_attr_s(<<"type">>, Attrs) of
+ <<"error">> -> StateData;
+ <<"result">> -> StateData;
+ _ ->
+ Err = jlib:make_error_reply(NewEl,
+ ?ERR_JID_MALFORMED),
+ send_element(StateData, Err),
+ StateData
+ end;
+ _ ->
+ case Name of
+ <<"presence">> ->
+ PresenceEl =
+ ejabberd_hooks:run_fold(c2s_update_presence,
+ Server, NewEl,
+ [User, Server]),
+ ejabberd_hooks:run(user_send_packet, Server,
+ [FromJID, ToJID, PresenceEl]),
+ case ToJID of
+ #jid{user = User, server = Server,
+ resource = <<"">>} ->
+ ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)",
+ [FromJID, PresenceEl, StateData]),
+ presence_update(FromJID, PresenceEl,
+ StateData);
+ _ ->
+ presence_track(FromJID, ToJID, PresenceEl,
+ StateData)
+ end;
+ <<"iq">> ->
+ case jlib:iq_query_info(NewEl) of
+ #iq{xmlns = Xmlns} = IQ
+ when Xmlns == (?NS_PRIVACY);
+ Xmlns == (?NS_BLOCKING) ->
+ process_privacy_iq(FromJID, ToJID, IQ,
+ StateData);
+ _ ->
+ ejabberd_hooks:run(user_send_packet, Server,
+ [FromJID, ToJID, NewEl]),
+ check_privacy_route(FromJID, StateData,
+ FromJID, ToJID, NewEl),
+ StateData
+ end;
+ <<"message">> ->
+ ejabberd_hooks:run(user_send_packet, Server,
+ [FromJID, ToJID, NewEl]),
+ check_privacy_route(FromJID, StateData, FromJID,
+ ToJID, NewEl),
+ StateData;
+ _ -> StateData
+ end
+ end,
+ ejabberd_hooks:run(c2s_loop_debug,
+ [{xmlstreamelement, El}]),
fsm_next_state(session_established, NewState).
-
-
%%----------------------------------------------------------------------
%% Func: StateName/3
%% Returns: {next_state, NextStateName, NextStateData} |
@@ -1162,24 +1174,22 @@ handle_event(_Event, StateName, StateData) ->
%% {stop, Reason, NewStateData} |
%% {stop, Reason, Reply, NewStateData}
%%----------------------------------------------------------------------
-handle_sync_event({get_presence}, _From, StateName, StateData) ->
+handle_sync_event({get_presence}, _From, StateName,
+ StateData) ->
User = StateData#state.user,
PresLast = StateData#state.pres_last,
-
Show = get_showtag(PresLast),
Status = get_statustag(PresLast),
Resource = StateData#state.resource,
-
Reply = {User, Resource, Show, Status},
fsm_reply(Reply, StateName, StateData);
-
-handle_sync_event(get_subscribed, _From, StateName, StateData) ->
- Subscribed = ?SETS:to_list(StateData#state.pres_f),
+handle_sync_event(get_subscribed, _From, StateName,
+ StateData) ->
+ Subscribed = (?SETS):to_list(StateData#state.pres_f),
{reply, Subscribed, StateName, StateData};
-
-handle_sync_event(_Event, _From, StateName, StateData) ->
- Reply = ok,
- fsm_reply(Reply, StateName, StateData).
+handle_sync_event(_Event, _From, StateName,
+ StateData) ->
+ Reply = ok, fsm_reply(Reply, StateName, StateData).
code_change(_OldVsn, StateName, StateData, _Extra) ->
{ok, StateName, StateData}.
@@ -1197,209 +1207,301 @@ handle_info({send_text, Text}, StateName, StateData) ->
handle_info(replaced, _StateName, StateData) ->
Lang = StateData#state.lang,
send_element(StateData,
- ?SERRT_CONFLICT(Lang, "Replaced by new connection")),
+ ?SERRT_CONFLICT(Lang,
+ <<"Replaced by new connection">>)),
send_trailer(StateData),
- {stop, normal, StateData#state{authenticated = replaced}};
+ {stop, normal,
+ StateData#state{authenticated = replaced}};
+handle_info({route, _From, _To, {broadcast, Data}},
+ StateName, StateData) ->
+ ?DEBUG("broadcast~n~p~n", [Data]),
+ case Data of
+ {item, IJID, ISubscription} ->
+ fsm_next_state(StateName,
+ roster_change(IJID, ISubscription, StateData));
+ {exit, Reason} ->
+ Lang = StateData#state.lang,
+ send_element(StateData, ?SERRT_CONFLICT(Lang, Reason)),
+ catch send_trailer(StateData),
+ {stop, normal, StateData};
+ {privacy_list, PrivList, PrivListName} ->
+ case ejabberd_hooks:run_fold(privacy_updated_list,
+ StateData#state.server,
+ false,
+ [StateData#state.privacy_list,
+ PrivList]) of
+ false ->
+ fsm_next_state(StateName, StateData);
+ NewPL ->
+ PrivPushIQ = #iq{type = set,
+ xmlns = ?NS_PRIVACY,
+ id = <<"push",
+ (randoms:get_string())/binary>>,
+ sub_el =
+ [#xmlel{name = <<"query">>,
+ attrs = [{<<"xmlns">>,
+ ?NS_PRIVACY}],
+ children =
+ [#xmlel{name = <<"list">>,
+ attrs = [{<<"name">>,
+ PrivListName}],
+ children = []}]}]},
+ PrivPushEl = jlib:replace_from_to(
+ jlib:jid_remove_resource(StateData#state.jid),
+ StateData#state.jid,
+ jlib:iq_to_xml(PrivPushIQ)),
+ send_element(StateData, PrivPushEl),
+ fsm_next_state(StateName,
+ StateData#state{privacy_list = NewPL})
+ end;
+ {blocking, What} ->
+ route_blocking(What, StateData),
+ fsm_next_state(StateName, StateData);
+ _ ->
+ fsm_next_state(StateName, StateData)
+ end;
%% Process Packets that are to be send to the user
-handle_info({route, From, To, Packet}, StateName, StateData) ->
- {xmlelement, Name, Attrs, Els} = Packet,
- {Pass, NewAttrs, NewState} =
- case Name of
- "presence" ->
- State = ejabberd_hooks:run_fold(
- c2s_presence_in, StateData#state.server,
- StateData,
- [{From, To, Packet}]),
- case xml:get_attr_s("type", Attrs) of
- "probe" ->
- LFrom = jlib:jid_tolower(From),
- LBFrom = jlib:jid_remove_resource(LFrom),
- NewStateData =
- case ?SETS:is_element(
- LFrom, State#state.pres_a) orelse
- ?SETS:is_element(
- LBFrom, State#state.pres_a) of
- true ->
- State;
- false ->
- case ?SETS:is_element(
- LFrom, State#state.pres_f) of
- true ->
- A = ?SETS:add_element(
- LFrom,
- State#state.pres_a),
- State#state{pres_a = A};
- false ->
- case ?SETS:is_element(
- LBFrom, State#state.pres_f) of
- true ->
- A = ?SETS:add_element(
- LBFrom,
- State#state.pres_a),
- State#state{pres_a = A};
- false ->
- State
- end
- end
- end,
- process_presence_probe(From, To, NewStateData),
- {false, Attrs, NewStateData};
- "error" ->
- NewA = remove_element(jlib:jid_tolower(From),
- State#state.pres_a),
- {true, Attrs, State#state{pres_a = NewA}};
- "invisible" ->
- Attrs1 = lists:keydelete("type", 1, Attrs),
- {true, [{"type", "unavailable"} | Attrs1], State};
- "subscribe" ->
- SRes = is_privacy_allow(State, From, To, Packet, in),
- {SRes, Attrs, State};
- "subscribed" ->
- SRes = is_privacy_allow(State, From, To, Packet, in),
- {SRes, Attrs, State};
- "unsubscribe" ->
- SRes = is_privacy_allow(State, From, To, Packet, in),
- {SRes, Attrs, State};
- "unsubscribed" ->
- SRes = is_privacy_allow(State, From, To, Packet, in),
- {SRes, Attrs, State};
- _ ->
- case privacy_check_packet(State, From, To, Packet, in) of
- allow ->
- LFrom = jlib:jid_tolower(From),
- LBFrom = jlib:jid_remove_resource(LFrom),
- case ?SETS:is_element(
- LFrom, State#state.pres_a) orelse
- ?SETS:is_element(
- LBFrom, State#state.pres_a) of
- true ->
- {true, Attrs, State};
- false ->
- case ?SETS:is_element(
- LFrom, State#state.pres_f) of
- true ->
- A = ?SETS:add_element(
- LFrom,
- State#state.pres_a),
- {true, Attrs,
- State#state{pres_a = A}};
- false ->
- case ?SETS:is_element(
- LBFrom, State#state.pres_f) of
- true ->
- A = ?SETS:add_element(
- LBFrom,
- State#state.pres_a),
- {true, Attrs,
- State#state{pres_a = A}};
- false ->
- {true, Attrs, State}
- end
- end
- end;
- deny ->
- {false, Attrs, State}
- end
- end;
- "broadcast" ->
- ?DEBUG("broadcast~n~p~n", [Els]),
- case Els of
- [{item, IJID, ISubscription}] ->
- {false, Attrs,
- roster_change(IJID, ISubscription,
- StateData)};
- [{exit, Reason}] ->
- {exit, Attrs, Reason};
- [{privacy_list, PrivList, PrivListName}] ->
- case ejabberd_hooks:run_fold(
- privacy_updated_list, StateData#state.server,
- false,
- [StateData#state.privacy_list,
- PrivList]) of
- false ->
- {false, Attrs, StateData};
- NewPL ->
- PrivPushIQ =
- #iq{type = set, xmlns = ?NS_PRIVACY,
- id = "push" ++ randoms:get_string(),
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_PRIVACY}],
- [{xmlelement, "list",
- [{"name", PrivListName}],
- []}]}]},
- PrivPushEl =
- jlib:replace_from_to(
- jlib:jid_remove_resource(
- StateData#state.jid),
- StateData#state.jid,
- jlib:iq_to_xml(PrivPushIQ)),
- send_element(StateData, PrivPushEl),
- {false, Attrs, StateData#state{privacy_list = NewPL}}
- end;
- [{blocking, What}] ->
- route_blocking(What, StateData),
- {false, Attrs, StateData};
- _ ->
- {false, Attrs, StateData}
- end;
- "iq" ->
- IQ = jlib:iq_query_info(Packet),
- case IQ of
- #iq{xmlns = ?NS_LAST} ->
- LFrom = jlib:jid_tolower(From),
- LBFrom = jlib:jid_remove_resource(LFrom),
- HasFromSub = (?SETS:is_element(LFrom, StateData#state.pres_f) orelse ?SETS:is_element(LBFrom, StateData#state.pres_f))
- andalso is_privacy_allow(StateData, To, From, {xmlelement, "presence", [], []}, out),
- case HasFromSub of
- true ->
- case privacy_check_packet(StateData, From, To, Packet, in) of
- allow ->
- {true, Attrs, StateData};
- deny ->
- {false, Attrs, StateData}
- end;
- _ ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err),
- {false, Attrs, StateData}
- end;
- IQ when (is_record(IQ, iq)) or (IQ == reply) ->
- case privacy_check_packet(StateData, From, To, Packet, in) of
- allow ->
- {true, Attrs, StateData};
- deny when is_record(IQ, iq) ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_SERVICE_UNAVAILABLE),
- ejabberd_router:route(To, From, Err),
- {false, Attrs, StateData};
- deny when IQ == reply ->
- {false, Attrs, StateData}
- end;
- IQ when (IQ == invalid) or (IQ == not_iq) ->
- {false, Attrs, StateData}
- end;
- "message" ->
- case privacy_check_packet(StateData, From, To, Packet, in) of
- allow ->
- {true, Attrs, StateData};
- deny ->
- {false, Attrs, StateData}
- end;
- _ ->
- {true, Attrs, StateData}
- end,
- if
- Pass == exit ->
+handle_info({route, From, To,
+ #xmlel{name = Name, attrs = Attrs, children = Els} = Packet},
+ StateName, StateData) ->
+ {Pass, NewAttrs, NewState} = case Name of
+ <<"presence">> ->
+ State =
+ ejabberd_hooks:run_fold(c2s_presence_in,
+ StateData#state.server,
+ StateData,
+ [{From, To,
+ Packet}]),
+ case xml:get_attr_s(<<"type">>, Attrs) of
+ <<"probe">> ->
+ LFrom = jlib:jid_tolower(From),
+ LBFrom =
+ jlib:jid_remove_resource(LFrom),
+ NewStateData = case
+ (?SETS):is_element(LFrom,
+ State#state.pres_a)
+ orelse
+ (?SETS):is_element(LBFrom,
+ State#state.pres_a)
+ of
+ true -> State;
+ false ->
+ case
+ (?SETS):is_element(LFrom,
+ State#state.pres_f)
+ of
+ true ->
+ A =
+ (?SETS):add_element(LFrom,
+ State#state.pres_a),
+ State#state{pres_a
+ =
+ A};
+ false ->
+ case
+ (?SETS):is_element(LBFrom,
+ State#state.pres_f)
+ of
+ true ->
+ A =
+ (?SETS):add_element(LBFrom,
+ State#state.pres_a),
+ State#state{pres_a
+ =
+ A};
+ false ->
+ State
+ end
+ end
+ end,
+ process_presence_probe(From, To,
+ NewStateData),
+ {false, Attrs, NewStateData};
+ <<"error">> ->
+ NewA =
+ remove_element(jlib:jid_tolower(From),
+ State#state.pres_a),
+ {true, Attrs,
+ State#state{pres_a = NewA}};
+ <<"subscribe">> ->
+ SRes = is_privacy_allow(State,
+ From, To,
+ Packet,
+ in),
+ {SRes, Attrs, State};
+ <<"subscribed">> ->
+ SRes = is_privacy_allow(State,
+ From, To,
+ Packet,
+ in),
+ {SRes, Attrs, State};
+ <<"unsubscribe">> ->
+ SRes = is_privacy_allow(State,
+ From, To,
+ Packet,
+ in),
+ {SRes, Attrs, State};
+ <<"unsubscribed">> ->
+ SRes = is_privacy_allow(State,
+ From, To,
+ Packet,
+ in),
+ {SRes, Attrs, State};
+ _ ->
+ case privacy_check_packet(State,
+ From, To,
+ Packet,
+ in)
+ of
+ allow ->
+ LFrom =
+ jlib:jid_tolower(From),
+ LBFrom =
+ jlib:jid_remove_resource(LFrom),
+ case
+ (?SETS):is_element(LFrom,
+ State#state.pres_a)
+ orelse
+ (?SETS):is_element(LBFrom,
+ State#state.pres_a)
+ of
+ true ->
+ {true, Attrs, State};
+ false ->
+ case
+ (?SETS):is_element(LFrom,
+ State#state.pres_f)
+ of
+ true ->
+ A =
+ (?SETS):add_element(LFrom,
+ State#state.pres_a),
+ {true, Attrs,
+ State#state{pres_a
+ =
+ A}};
+ false ->
+ case
+ (?SETS):is_element(LBFrom,
+ State#state.pres_f)
+ of
+ true ->
+ A =
+ (?SETS):add_element(LBFrom,
+ State#state.pres_a),
+ {true,
+ Attrs,
+ State#state{pres_a
+ =
+ A}};
+ false ->
+ {true,
+ Attrs,
+ State}
+ end
+ end
+ end;
+ deny -> {false, Attrs, State}
+ end
+ end;
+ <<"iq">> ->
+ IQ = jlib:iq_query_info(Packet),
+ case IQ of
+ #iq{xmlns = ?NS_LAST} ->
+ LFrom = jlib:jid_tolower(From),
+ LBFrom =
+ jlib:jid_remove_resource(LFrom),
+ HasFromSub =
+ ((?SETS):is_element(LFrom,
+ StateData#state.pres_f)
+ orelse
+ (?SETS):is_element(LBFrom,
+ StateData#state.pres_f))
+ andalso
+ is_privacy_allow(StateData,
+ To, From,
+ #xmlel{name
+ =
+ <<"presence">>,
+ attrs
+ =
+ [],
+ children
+ =
+ []},
+ out),
+ case HasFromSub of
+ true ->
+ case
+ privacy_check_packet(StateData,
+ From,
+ To,
+ Packet,
+ in)
+ of
+ allow ->
+ {true, Attrs,
+ StateData};
+ deny ->
+ {false, Attrs,
+ StateData}
+ end;
+ _ ->
+ Err =
+ jlib:make_error_reply(Packet,
+ ?ERR_FORBIDDEN),
+ ejabberd_router:route(To,
+ From,
+ Err),
+ {false, Attrs, StateData}
+ end;
+ IQ
+ when is_record(IQ, iq) or
+ (IQ == reply) ->
+ case
+ privacy_check_packet(StateData,
+ From, To,
+ Packet, in)
+ of
+ allow ->
+ {true, Attrs, StateData};
+ deny when is_record(IQ, iq) ->
+ Err =
+ jlib:make_error_reply(Packet,
+ ?ERR_SERVICE_UNAVAILABLE),
+ ejabberd_router:route(To,
+ From,
+ Err),
+ {false, Attrs, StateData};
+ deny when IQ == reply ->
+ {false, Attrs, StateData}
+ end;
+ IQ
+ when (IQ == invalid) or
+ (IQ == not_iq) ->
+ {false, Attrs, StateData}
+ end;
+ <<"message">> ->
+ case privacy_check_packet(StateData,
+ From, To,
+ Packet, in)
+ of
+ allow -> {true, Attrs, StateData};
+ deny -> {false, Attrs, StateData}
+ end;
+ _ -> {true, Attrs, StateData}
+ end,
+ if Pass == exit ->
%% When Pass==exit, NewState contains a string instead of a #state{}
Lang = StateData#state.lang,
send_element(StateData, ?SERRT_CONFLICT(Lang, NewState)),
send_trailer(StateData),
{stop, normal, StateData};
Pass ->
- Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
- jlib:jid_to_string(To),
- NewAttrs),
- FixedPacket = {xmlelement, Name, Attrs2, Els},
+ Attrs2 =
+ jlib:replace_from_to_attrs(jlib:jid_to_string(From),
+ jlib:jid_to_string(To), NewAttrs),
+ FixedPacket = #xmlel{name = Name, attrs = Attrs2, children = Els},
send_element(StateData, FixedPacket),
ejabberd_hooks:run(user_receive_packet,
StateData#state.server,
@@ -1410,40 +1512,38 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
fsm_next_state(StateName, NewState)
end;
-handle_info({'DOWN', Monitor, _Type, _Object, _Info}, _StateName, StateData)
- when Monitor == StateData#state.socket_monitor ->
+handle_info({'DOWN', Monitor, _Type, _Object, _Info},
+ _StateName, StateData)
+ when Monitor == StateData#state.socket_monitor ->
{stop, normal, StateData};
handle_info(system_shutdown, StateName, StateData) ->
case StateName of
- wait_for_stream ->
- send_header(StateData, ?MYNAME, "1.0", "en"),
- send_element(StateData, ?SERR_SYSTEM_SHUTDOWN),
- send_trailer(StateData),
- ok;
- _ ->
- send_element(StateData, ?SERR_SYSTEM_SHUTDOWN),
- send_trailer(StateData),
- ok
+ wait_for_stream ->
+ send_header(StateData, ?MYNAME, <<"1.0">>, <<"en">>),
+ send_element(StateData, ?SERR_SYSTEM_SHUTDOWN),
+ send_trailer(StateData),
+ ok;
+ _ ->
+ send_element(StateData, ?SERR_SYSTEM_SHUTDOWN),
+ send_trailer(StateData),
+ ok
end,
{stop, normal, StateData};
handle_info({force_update_presence, LUser}, StateName,
- #state{user = LUser, server = LServer} = StateData) ->
- NewStateData =
- case StateData#state.pres_last of
- {xmlelement, "presence", _Attrs, _Els} ->
- PresenceEl = ejabberd_hooks:run_fold(
- c2s_update_presence,
- LServer,
- StateData#state.pres_last,
- [LUser, LServer]),
- StateData2 = StateData#state{pres_last = PresenceEl},
- presence_update(StateData2#state.jid,
- PresenceEl,
- StateData2),
- StateData2;
- _ ->
- StateData
- end,
+ #state{user = LUser, server = LServer} = StateData) ->
+ NewStateData = case StateData#state.pres_last of
+ #xmlel{name = <<"presence">>} ->
+ PresenceEl =
+ ejabberd_hooks:run_fold(c2s_update_presence,
+ LServer,
+ StateData#state.pres_last,
+ [LUser, LServer]),
+ StateData2 = StateData#state{pres_last = PresenceEl},
+ presence_update(StateData2#state.jid, PresenceEl,
+ StateData2),
+ StateData2;
+ _ -> StateData
+ end,
{next_state, StateName, NewStateData};
handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
Recipients = ejabberd_hooks:run_fold(
@@ -1480,61 +1580,59 @@ print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
%%----------------------------------------------------------------------
terminate(_Reason, StateName, StateData) ->
case StateName of
- session_established ->
- case StateData#state.authenticated of
- replaced ->
- ?INFO_MSG("(~w) Replaced session for ~s",
- [StateData#state.socket,
- jlib:jid_to_string(StateData#state.jid)]),
- From = StateData#state.jid,
- Packet = {xmlelement, "presence",
- [{"type", "unavailable"}],
- [{xmlelement, "status", [],
- [{xmlcdata, "Replaced by new connection"}]}]},
- ejabberd_sm:close_session_unset_presence(
- StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- "Replaced by new connection"),
- presence_broadcast(
- StateData, From, StateData#state.pres_a, Packet),
- presence_broadcast(
- StateData, From, StateData#state.pres_i, Packet);
- _ ->
- ?INFO_MSG("(~w) Close session for ~s",
- [StateData#state.socket,
- jlib:jid_to_string(StateData#state.jid)]),
-
- EmptySet = ?SETS:new(),
- case StateData of
- #state{pres_last = undefined,
- pres_a = EmptySet,
- pres_i = EmptySet,
- pres_invis = false} ->
- ejabberd_sm:close_session(StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource);
- _ ->
- From = StateData#state.jid,
- Packet = {xmlelement, "presence",
- [{"type", "unavailable"}], []},
- ejabberd_sm:close_session_unset_presence(
- StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- ""),
- presence_broadcast(
- StateData, From, StateData#state.pres_a, Packet),
- presence_broadcast(
- StateData, From, StateData#state.pres_i, Packet)
- end
- end,
- bounce_messages();
- _ ->
- ok
+ session_established ->
+ case StateData#state.authenticated of
+ replaced ->
+ ?INFO_MSG("(~w) Replaced session for ~s",
+ [StateData#state.socket,
+ jlib:jid_to_string(StateData#state.jid)]),
+ From = StateData#state.jid,
+ Packet = #xmlel{name = <<"presence">>,
+ attrs = [{<<"type">>, <<"unavailable">>}],
+ children =
+ [#xmlel{name = <<"status">>, attrs = [],
+ children =
+ [{xmlcdata,
+ <<"Replaced by new connection">>}]}]},
+ ejabberd_sm:close_session_unset_presence(StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource,
+ <<"Replaced by new connection">>),
+ presence_broadcast(StateData, From,
+ StateData#state.pres_a, Packet),
+ presence_broadcast(StateData, From,
+ StateData#state.pres_i, Packet);
+ _ ->
+ ?INFO_MSG("(~w) Close session for ~s",
+ [StateData#state.socket,
+ jlib:jid_to_string(StateData#state.jid)]),
+ EmptySet = (?SETS):new(),
+ case StateData of
+ #state{pres_last = undefined, pres_a = EmptySet, pres_i = EmptySet, pres_invis = false} ->
+ ejabberd_sm:close_session(StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource);
+ _ ->
+ From = StateData#state.jid,
+ Packet = #xmlel{name = <<"presence">>,
+ attrs = [{<<"type">>, <<"unavailable">>}],
+ children = []},
+ ejabberd_sm:close_session_unset_presence(StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource,
+ <<"">>),
+ presence_broadcast(StateData, From,
+ StateData#state.pres_a, Packet),
+ presence_broadcast(StateData, From,
+ StateData#state.pres_i, Packet)
+ end
+ end,
+ bounce_messages();
+ _ ->
+ ok
end,
(StateData#state.sockmod):close(StateData#state.socket),
ok.
@@ -1546,10 +1644,11 @@ terminate(_Reason, StateName, StateData) ->
change_shaper(StateData, JID) ->
Shaper = acl:match_rule(StateData#state.server,
StateData#state.shaper, JID),
- (StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
+ (StateData#state.sockmod):change_shaper(StateData#state.socket,
+ Shaper).
send_text(StateData, Text) when StateData#state.xml_socket ->
- ?DEBUG("Send Text on stream = ~p", [lists:flatten(Text)]),
+ ?DEBUG("Send Text on stream = ~p", [Text]),
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamraw, Text});
send_text(StateData, Text) ->
@@ -1563,82 +1662,67 @@ send_element(StateData, El) ->
send_text(StateData, xml:element_to_binary(El)).
send_header(StateData, Server, Version, Lang)
- when StateData#state.xml_socket ->
- VersionAttr =
- case Version of
- "" -> [];
- _ -> [{"version", Version}]
- end,
- LangAttr =
- case Lang of
- "" -> [];
- _ -> [{"xml:lang", Lang}]
- end,
- Header =
- {xmlstreamstart,
- "stream:stream",
- VersionAttr ++
- LangAttr ++
- [{"xmlns", "jabber:client"},
- {"xmlns:stream", "http://etherx.jabber.org/streams"},
- {"id", StateData#state.streamid},
- {"from", Server}]},
- (StateData#state.sockmod):send_xml(
- StateData#state.socket, Header);
+ when StateData#state.xml_socket ->
+ VersionAttr = case Version of
+ <<"">> -> [];
+ _ -> [{<<"version">>, Version}]
+ end,
+ LangAttr = case Lang of
+ <<"">> -> [];
+ _ -> [{<<"xml:lang">>, Lang}]
+ end,
+ Header = {xmlstreamstart, <<"stream:stream">>,
+ VersionAttr ++
+ LangAttr ++
+ [{<<"xmlns">>, <<"jabber:client">>},
+ {<<"xmlns:stream">>,
+ <<"http://etherx.jabber.org/streams">>},
+ {<<"id">>, StateData#state.streamid},
+ {<<"from">>, Server}]},
+ (StateData#state.sockmod):send_xml(StateData#state.socket,
+ Header);
send_header(StateData, Server, Version, Lang) ->
- VersionStr =
- case Version of
- "" -> "";
- _ -> [" version='", Version, "'"]
- end,
- LangStr =
- case Lang of
- "" -> "";
- _ -> [" xml:lang='", Lang, "'"]
- end,
+ VersionStr = case Version of
+ <<"">> -> <<"">>;
+ _ -> [<<" version='">>, Version, <<"'">>]
+ end,
+ LangStr = case Lang of
+ <<"">> -> <<"">>;
+ _ -> [<<" xml:lang='">>, Lang, <<"'">>]
+ end,
Header = io_lib:format(?STREAM_HEADER,
- [StateData#state.streamid,
- Server,
- VersionStr,
+ [StateData#state.streamid, Server, VersionStr,
LangStr]),
- send_text(StateData, Header).
+ send_text(StateData, iolist_to_binary(Header)).
-send_trailer(StateData) when StateData#state.xml_socket ->
- (StateData#state.sockmod):send_xml(
- StateData#state.socket,
- {xmlstreamend, "stream:stream"});
+send_trailer(StateData)
+ when StateData#state.xml_socket ->
+ (StateData#state.sockmod):send_xml(StateData#state.socket,
+ {xmlstreamend, <<"stream:stream">>});
send_trailer(StateData) ->
send_text(StateData, ?STREAM_TRAILER).
-
-new_id() ->
- randoms:get_string().
-
+new_id() -> randoms:get_string().
is_auth_packet(El) ->
case jlib:iq_query_info(El) of
- #iq{id = ID, type = Type, xmlns = ?NS_AUTH, sub_el = SubEl} ->
- {xmlelement, _, _, Els} = SubEl,
- {auth, ID, Type,
- get_auth_tags(Els, "", "", "", "")};
- _ ->
- false
+ #iq{id = ID, type = Type, xmlns = ?NS_AUTH,
+ sub_el = SubEl} ->
+ #xmlel{children = Els} = SubEl,
+ {auth, ID, Type,
+ get_auth_tags(Els, <<"">>, <<"">>, <<"">>, <<"">>)};
+ _ -> false
end.
-
-get_auth_tags([{xmlelement, Name, _Attrs, Els}| L], U, P, D, R) ->
+get_auth_tags([#xmlel{name = Name, children = Els} | L],
+ U, P, D, R) ->
CData = xml:get_cdata(Els),
case Name of
- "username" ->
- get_auth_tags(L, CData, P, D, R);
- "password" ->
- get_auth_tags(L, U, CData, D, R);
- "digest" ->
- get_auth_tags(L, U, P, CData, R);
- "resource" ->
- get_auth_tags(L, U, P, D, CData);
- _ ->
- get_auth_tags(L, U, P, D, R)
+ <<"username">> -> get_auth_tags(L, CData, P, D, R);
+ <<"password">> -> get_auth_tags(L, U, CData, D, R);
+ <<"digest">> -> get_auth_tags(L, U, P, CData, R);
+ <<"resource">> -> get_auth_tags(L, U, P, D, CData);
+ _ -> get_auth_tags(L, U, P, D, R)
end;
get_auth_tags([_ | L], U, P, D, R) ->
get_auth_tags(L, U, P, D, R);
@@ -1664,7 +1748,7 @@ get_conn_type(StateData) ->
process_presence_probe(From, To, StateData) ->
LFrom = jlib:jid_tolower(From),
- LBFrom = setelement(3, LFrom, ""),
+ LBFrom = setelement(3, LFrom, <<"">>),
case StateData#state.pres_last of
undefined ->
ok;
@@ -1688,7 +1772,7 @@ process_presence_probe(From, To, StateData) ->
Packet = xml:append_subtags(
StateData#state.pres_last,
%% To is the one sending the presence (the target of the probe)
- [jlib:timestamp_to_xml(Timestamp, utc, To, ""),
+ [jlib:timestamp_to_xml(Timestamp, utc, To, <<"">>),
%% TODO: Delete the next line once XEP-0091 is Obsolete
jlib:timestamp_to_xml(Timestamp)]),
case privacy_check_packet(StateData, To, From, Packet, out) of
@@ -1707,9 +1791,9 @@ process_presence_probe(From, To, StateData) ->
end;
Cond2 ->
ejabberd_router:route(To, From,
- {xmlelement, "presence",
- [],
- []});
+ #xmlel{name = <<"presence">>,
+ attrs = [],
+ children = []});
true ->
ok
end
@@ -1717,500 +1801,414 @@ process_presence_probe(From, To, StateData) ->
%% User updates his presence (non-directed presence packet)
presence_update(From, Packet, StateData) ->
- {xmlelement, _Name, Attrs, _Els} = Packet,
- case xml:get_attr_s("type", Attrs) of
- "unavailable" ->
- Status = case xml:get_subtag(Packet, "status") of
- false ->
- "";
- StatusTag ->
- xml:get_tag_cdata(StatusTag)
- end,
- Info = [{ip, StateData#state.ip}, {conn, StateData#state.conn},
- {auth_module, StateData#state.auth_module}],
- ejabberd_sm:unset_presence(StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- Status,
- Info),
- presence_broadcast(StateData, From, StateData#state.pres_a, Packet),
- presence_broadcast(StateData, From, StateData#state.pres_i, Packet),
- StateData#state{pres_last = undefined,
- pres_timestamp = undefined,
- pres_a = ?SETS:new(),
- pres_i = ?SETS:new(),
- pres_invis = false};
- "invisible" ->
- NewPriority = get_priority_from_presence(Packet),
- update_priority(NewPriority, Packet, StateData),
- NewState =
- if
- not StateData#state.pres_invis ->
- presence_broadcast(StateData, From,
- StateData#state.pres_a,
- Packet),
- presence_broadcast(StateData, From,
- StateData#state.pres_i,
- Packet),
- S1 = StateData#state{pres_last = undefined,
- pres_timestamp = undefined,
- pres_a = ?SETS:new(),
- pres_i = ?SETS:new(),
- pres_invis = true},
- presence_broadcast_first(From, S1, Packet);
- true ->
- StateData
- end,
- NewState;
- "error" ->
- StateData;
- "probe" ->
- StateData;
- "subscribe" ->
- StateData;
- "subscribed" ->
- StateData;
- "unsubscribe" ->
- StateData;
- "unsubscribed" ->
- StateData;
- _ ->
- OldPriority = case StateData#state.pres_last of
- undefined ->
- 0;
- OldPresence ->
- get_priority_from_presence(OldPresence)
- end,
- NewPriority = get_priority_from_presence(Packet),
- Timestamp = calendar:now_to_universal_time(now()),
- update_priority(NewPriority, Packet, StateData),
- FromUnavail = (StateData#state.pres_last == undefined) or
- StateData#state.pres_invis,
- ?DEBUG("from unavail = ~p~n", [FromUnavail]),
- NewStateData = StateData#state{pres_last = Packet,
- pres_invis = false,
- pres_timestamp = Timestamp},
- NewState =
- if
- FromUnavail ->
- ejabberd_hooks:run(user_available_hook,
- NewStateData#state.server,
- [NewStateData#state.jid]),
- if NewPriority >= 0 ->
- resend_offline_messages(NewStateData),
- resend_subscription_requests(NewStateData);
- true ->
- ok
- end,
- presence_broadcast_first(From, NewStateData, Packet);
- true ->
- presence_broadcast_to_trusted(NewStateData,
- From,
- NewStateData#state.pres_f,
- NewStateData#state.pres_a,
- Packet),
- if OldPriority < 0, NewPriority >= 0 ->
- resend_offline_messages(NewStateData);
- true ->
- ok
+ #xmlel{attrs = Attrs} = Packet,
+ case xml:get_attr_s(<<"type">>, Attrs) of
+ <<"unavailable">> ->
+ Status = case xml:get_subtag(Packet, <<"status">>) of
+ false -> <<"">>;
+ StatusTag -> xml:get_tag_cdata(StatusTag)
+ end,
+ Info = [{ip, StateData#state.ip},
+ {conn, StateData#state.conn},
+ {auth_module, StateData#state.auth_module}],
+ ejabberd_sm:unset_presence(StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource, Status, Info),
+ presence_broadcast(StateData, From,
+ StateData#state.pres_a, Packet),
+ StateData#state{pres_last = undefined,
+ pres_timestamp = undefined, pres_a = (?SETS):new()};
+ <<"error">> -> StateData;
+ <<"probe">> -> StateData;
+ <<"subscribe">> -> StateData;
+ <<"subscribed">> -> StateData;
+ <<"unsubscribe">> -> StateData;
+ <<"unsubscribed">> -> StateData;
+ _ ->
+ OldPriority = case StateData#state.pres_last of
+ undefined -> 0;
+ OldPresence -> get_priority_from_presence(OldPresence)
end,
- NewStateData
- end,
- NewState
+ NewPriority = get_priority_from_presence(Packet),
+ Timestamp = calendar:now_to_universal_time(now()),
+ update_priority(NewPriority, Packet, StateData),
+ FromUnavail = (StateData#state.pres_last == undefined),
+ ?DEBUG("from unavail = ~p~n", [FromUnavail]),
+ NewStateData = StateData#state{pres_last = Packet,
+ pres_timestamp = Timestamp},
+ NewState = if FromUnavail ->
+ ejabberd_hooks:run(user_available_hook,
+ NewStateData#state.server,
+ [NewStateData#state.jid]),
+ if NewPriority >= 0 ->
+ resend_offline_messages(NewStateData),
+ resend_subscription_requests(NewStateData);
+ true -> ok
+ end,
+ presence_broadcast_first(From, NewStateData,
+ Packet);
+ true ->
+ presence_broadcast_to_trusted(NewStateData, From,
+ NewStateData#state.pres_f,
+ NewStateData#state.pres_a,
+ Packet),
+ if OldPriority < 0, NewPriority >= 0 ->
+ resend_offline_messages(NewStateData);
+ true -> ok
+ end,
+ NewStateData
+ end,
+ NewState
end.
%% User sends a directed presence packet
presence_track(From, To, Packet, StateData) ->
- {xmlelement, _Name, Attrs, _Els} = Packet,
+ #xmlel{attrs = Attrs} = Packet,
LTo = jlib:jid_tolower(To),
User = StateData#state.user,
Server = StateData#state.server,
- case xml:get_attr_s("type", Attrs) of
- "unavailable" ->
- check_privacy_route(From, StateData, From, To, Packet),
- I = remove_element(LTo, StateData#state.pres_i),
- A = remove_element(LTo, StateData#state.pres_a),
- StateData#state{pres_i = I,
- pres_a = A};
- "invisible" ->
- check_privacy_route(From, StateData, From, To, Packet),
- I = ?SETS:add_element(LTo, StateData#state.pres_i),
- A = remove_element(LTo, StateData#state.pres_a),
- StateData#state{pres_i = I,
- pres_a = A};
- "subscribe" ->
- ejabberd_hooks:run(roster_out_subscription,
- Server,
- [User, Server, To, subscribe]),
- check_privacy_route(From, StateData, jlib:jid_remove_resource(From),
- To, Packet),
- StateData;
- "subscribed" ->
- ejabberd_hooks:run(roster_out_subscription,
- Server,
- [User, Server, To, subscribed]),
- check_privacy_route(From, StateData, jlib:jid_remove_resource(From),
- To, Packet),
- StateData;
- "unsubscribe" ->
- ejabberd_hooks:run(roster_out_subscription,
- Server,
- [User, Server, To, unsubscribe]),
- check_privacy_route(From, StateData, jlib:jid_remove_resource(From),
- To, Packet),
- StateData;
- "unsubscribed" ->
- ejabberd_hooks:run(roster_out_subscription,
- Server,
- [User, Server, To, unsubscribed]),
- check_privacy_route(From, StateData, jlib:jid_remove_resource(From),
- To, Packet),
- StateData;
- "error" ->
- check_privacy_route(From, StateData, From, To, Packet),
- StateData;
- "probe" ->
- check_privacy_route(From, StateData, From, To, Packet),
- StateData;
- _ ->
- check_privacy_route(From, StateData, From, To, Packet),
- I = remove_element(LTo, StateData#state.pres_i),
- A = ?SETS:add_element(LTo, StateData#state.pres_a),
- StateData#state{pres_i = I,
- pres_a = A}
+ case xml:get_attr_s(<<"type">>, Attrs) of
+ <<"unavailable">> ->
+ check_privacy_route(From, StateData, From, To, Packet),
+ A = remove_element(LTo, StateData#state.pres_a),
+ StateData#state{pres_a = A};
+ <<"subscribe">> ->
+ ejabberd_hooks:run(roster_out_subscription, Server,
+ [User, Server, To, subscribe]),
+ check_privacy_route(From, StateData,
+ jlib:jid_remove_resource(From), To, Packet),
+ StateData;
+ <<"subscribed">> ->
+ ejabberd_hooks:run(roster_out_subscription, Server,
+ [User, Server, To, subscribed]),
+ check_privacy_route(From, StateData,
+ jlib:jid_remove_resource(From), To, Packet),
+ StateData;
+ <<"unsubscribe">> ->
+ ejabberd_hooks:run(roster_out_subscription, Server,
+ [User, Server, To, unsubscribe]),
+ check_privacy_route(From, StateData,
+ jlib:jid_remove_resource(From), To, Packet),
+ StateData;
+ <<"unsubscribed">> ->
+ ejabberd_hooks:run(roster_out_subscription, Server,
+ [User, Server, To, unsubscribed]),
+ check_privacy_route(From, StateData,
+ jlib:jid_remove_resource(From), To, Packet),
+ StateData;
+ <<"error">> ->
+ check_privacy_route(From, StateData, From, To, Packet),
+ StateData;
+ <<"probe">> ->
+ check_privacy_route(From, StateData, From, To, Packet),
+ StateData;
+ _ ->
+ check_privacy_route(From, StateData, From, To, Packet),
+ A = (?SETS):add_element(LTo, StateData#state.pres_a),
+ StateData#state{pres_a = A}
end.
-check_privacy_route(From, StateData, FromRoute, To, Packet) ->
- case privacy_check_packet(StateData, From, To, Packet, out) of
- deny ->
- Lang = StateData#state.lang,
- ErrText = "Your active privacy list has denied the routing of this stanza.",
- Err = jlib:make_error_reply(Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
- ejabberd_router:route(To, From, Err),
- ok;
- allow ->
- ejabberd_router:route(FromRoute, To, Packet)
+check_privacy_route(From, StateData, FromRoute, To,
+ Packet) ->
+ case privacy_check_packet(StateData, From, To, Packet,
+ out)
+ of
+ deny ->
+ Lang = StateData#state.lang,
+ ErrText = <<"Your active privacy list has denied "
+ "the routing of this stanza.">>,
+ Err = jlib:make_error_reply(Packet,
+ ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
+ ejabberd_router:route(To, From, Err),
+ ok;
+ allow -> ejabberd_router:route(FromRoute, To, Packet)
end.
-privacy_check_packet(StateData, From, To, Packet, Dir) ->
- ejabberd_hooks:run_fold(
- privacy_check_packet, StateData#state.server,
- allow,
- [StateData#state.user,
- StateData#state.server,
- StateData#state.privacy_list,
- {From, To, Packet},
- Dir]).
-
%% Check if privacy rules allow this delivery
+privacy_check_packet(StateData, From, To, Packet,
+ Dir) ->
+ ejabberd_hooks:run_fold(privacy_check_packet,
+ StateData#state.server, allow,
+ [StateData#state.user, StateData#state.server,
+ StateData#state.privacy_list, {From, To, Packet},
+ Dir]).
+
is_privacy_allow(StateData, From, To, Packet, Dir) ->
- allow == privacy_check_packet(StateData, From, To, Packet, Dir).
+ allow ==
+ privacy_check_packet(StateData, From, To, Packet, Dir).
+%% Send presence when disconnecting
presence_broadcast(StateData, From, JIDSet, Packet) ->
- lists:foreach(fun(JID) ->
- FJID = jlib:make_jid(JID),
- case privacy_check_packet(StateData, From, FJID, Packet, out) of
- deny ->
- ok;
- allow ->
- ejabberd_router:route(From, FJID, Packet)
- end
- end, ?SETS:to_list(JIDSet)).
-
-presence_broadcast_to_trusted(StateData, From, T, A, Packet) ->
- lists:foreach(
- fun(JID) ->
- case ?SETS:is_element(JID, T) of
- true ->
- FJID = jlib:make_jid(JID),
- case privacy_check_packet(StateData, From, FJID, Packet, out) of
- deny ->
- ok;
- allow ->
- ejabberd_router:route(From, FJID, Packet)
- end;
- _ ->
- ok
- end
- end, ?SETS:to_list(A)).
+ JIDs = ?SETS:to_list(JIDSet),
+ JIDs2 = format_and_check_privacy(From, StateData, Packet, JIDs, out),
+ Server = StateData#state.server,
+ send_multiple(From, Server, JIDs2, Packet).
+%% Send presence when updating presence
+presence_broadcast_to_trusted(StateData, From, Trusted, JIDSet, Packet) ->
+ JIDs = ?SETS:to_list(JIDSet),
+ JIDs_trusted = [JID || JID <- JIDs, ?SETS:is_element(JID, Trusted)],
+ JIDs2 = format_and_check_privacy(From, StateData, Packet, JIDs_trusted, out),
+ Server = StateData#state.server,
+ send_multiple(From, Server, JIDs2, Packet).
+%% Send presence when connecting
presence_broadcast_first(From, StateData, Packet) ->
- ?SETS:fold(fun(JID, X) ->
- ejabberd_router:route(
- From,
- jlib:make_jid(JID),
- {xmlelement, "presence",
- [{"type", "probe"}],
- []}),
- X
- end,
- [],
- StateData#state.pres_t),
- if
- StateData#state.pres_invis ->
- StateData;
- true ->
- As = ?SETS:fold(
- fun(JID, A) ->
- FJID = jlib:make_jid(JID),
- case privacy_check_packet(StateData, From, FJID, Packet, out) of
- deny ->
- ok;
- allow ->
- ejabberd_router:route(From, FJID, Packet)
- end,
- ?SETS:add_element(JID, A)
- end,
- StateData#state.pres_a,
- StateData#state.pres_f),
- StateData#state{pres_a = As}
- end.
+ JIDsProbe =
+ ?SETS:fold(
+ fun(JID, L) -> [JID | L] end,
+ [],
+ StateData#state.pres_t),
+ PacketProbe = #xmlel{name = <<"presence">>, attrs = [{<<"type">>,<<"probe">>}], children = []},
+ JIDs2Probe = format_and_check_privacy(From, StateData, Packet, JIDsProbe, out),
+ Server = StateData#state.server,
+ send_multiple(From, Server, JIDs2Probe, PacketProbe),
+ {As, JIDs} =
+ ?SETS:fold(
+ fun(JID, {A, JID_list}) ->
+ {?SETS:add_element(JID, A), JID_list++[JID]}
+ end,
+ {StateData#state.pres_a, []},
+ StateData#state.pres_f),
+ JIDs2 = format_and_check_privacy(From, StateData, Packet, JIDs, out),
+ Server = StateData#state.server,
+ send_multiple(From, Server, JIDs2, Packet),
+ StateData#state{pres_a = As}.
+
+format_and_check_privacy(From, StateData, Packet, JIDs, Dir) ->
+ FJIDs = [jlib:make_jid(JID) || JID <- JIDs],
+ lists:filter(
+ fun(FJID) ->
+ case ejabberd_hooks:run_fold(
+ privacy_check_packet, StateData#state.server,
+ allow,
+ [StateData#state.user,
+ StateData#state.server,
+ StateData#state.privacy_list,
+ {From, FJID, Packet},
+ Dir]) of
+ deny -> false;
+ allow -> true
+ end
+ end,
+ FJIDs).
+
+send_multiple(From, Server, JIDs, Packet) ->
+ ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet).
remove_element(E, Set) ->
- case ?SETS:is_element(E, Set) of
- true ->
- ?SETS:del_element(E, Set);
- _ ->
- Set
+ case (?SETS):is_element(E, Set) of
+ true -> (?SETS):del_element(E, Set);
+ _ -> Set
end.
-
roster_change(IJID, ISubscription, StateData) ->
LIJID = jlib:jid_tolower(IJID),
- IsFrom = (ISubscription == both) or (ISubscription == from),
- IsTo = (ISubscription == both) or (ISubscription == to),
- OldIsFrom = ?SETS:is_element(LIJID, StateData#state.pres_f),
- FSet = if
- IsFrom ->
- ?SETS:add_element(LIJID, StateData#state.pres_f);
- true ->
- remove_element(LIJID, StateData#state.pres_f)
+ IsFrom = (ISubscription == both) or
+ (ISubscription == from),
+ IsTo = (ISubscription == both) or (ISubscription == to),
+ OldIsFrom = (?SETS):is_element(LIJID,
+ StateData#state.pres_f),
+ FSet = if IsFrom ->
+ (?SETS):add_element(LIJID, StateData#state.pres_f);
+ true -> remove_element(LIJID, StateData#state.pres_f)
end,
- TSet = if
- IsTo ->
- ?SETS:add_element(LIJID, StateData#state.pres_t);
- true ->
- remove_element(LIJID, StateData#state.pres_t)
+ TSet = if IsTo ->
+ (?SETS):add_element(LIJID, StateData#state.pres_t);
+ true -> remove_element(LIJID, StateData#state.pres_t)
end,
case StateData#state.pres_last of
- undefined ->
- StateData#state{pres_f = FSet, pres_t = TSet};
- P ->
- ?DEBUG("roster changed for ~p~n", [StateData#state.user]),
- From = StateData#state.jid,
- To = jlib:make_jid(IJID),
- Cond1 = (not StateData#state.pres_invis) and IsFrom
- and (not OldIsFrom),
- Cond2 = (not IsFrom) and OldIsFrom
- and (?SETS:is_element(LIJID, StateData#state.pres_a) or
- ?SETS:is_element(LIJID, StateData#state.pres_i)),
- if
- Cond1 ->
- ?DEBUG("C1: ~p~n", [LIJID]),
- case privacy_check_packet(StateData, From, To, P, out) of
- deny ->
- ok;
- allow ->
- ejabberd_router:route(From, To, P)
- end,
- A = ?SETS:add_element(LIJID,
- StateData#state.pres_a),
- StateData#state{pres_a = A,
- pres_f = FSet,
- pres_t = TSet};
- Cond2 ->
- ?DEBUG("C2: ~p~n", [LIJID]),
- PU = {xmlelement, "presence",
- [{"type", "unavailable"}], []},
- case privacy_check_packet(StateData, From, To, PU, out) of
- deny ->
- ok;
- allow ->
- ejabberd_router:route(From, To, PU)
- end,
- I = remove_element(LIJID,
- StateData#state.pres_i),
- A = remove_element(LIJID,
- StateData#state.pres_a),
- StateData#state{pres_i = I,
- pres_a = A,
- pres_f = FSet,
- pres_t = TSet};
- true ->
- StateData#state{pres_f = FSet, pres_t = TSet}
- end
+ undefined ->
+ StateData#state{pres_f = FSet, pres_t = TSet};
+ P ->
+ ?DEBUG("roster changed for ~p~n",
+ [StateData#state.user]),
+ From = StateData#state.jid,
+ To = jlib:make_jid(IJID),
+ Cond1 = IsFrom andalso not OldIsFrom,
+ Cond2 = not IsFrom andalso OldIsFrom andalso
+ ((?SETS):is_element(LIJID, StateData#state.pres_a)),
+ if Cond1 ->
+ ?DEBUG("C1: ~p~n", [LIJID]),
+ case privacy_check_packet(StateData, From, To, P, out)
+ of
+ deny -> ok;
+ allow -> ejabberd_router:route(From, To, P)
+ end,
+ A = (?SETS):add_element(LIJID, StateData#state.pres_a),
+ StateData#state{pres_a = A, pres_f = FSet,
+ pres_t = TSet};
+ Cond2 ->
+ ?DEBUG("C2: ~p~n", [LIJID]),
+ PU = #xmlel{name = <<"presence">>,
+ attrs = [{<<"type">>, <<"unavailable">>}],
+ children = []},
+ case privacy_check_packet(StateData, From, To, PU, out)
+ of
+ deny -> ok;
+ allow -> ejabberd_router:route(From, To, PU)
+ end,
+ A = remove_element(LIJID, StateData#state.pres_a),
+ StateData#state{pres_a = A, pres_f = FSet,
+ pres_t = TSet};
+ true -> StateData#state{pres_f = FSet, pres_t = TSet}
+ end
end.
-
update_priority(Priority, Packet, StateData) ->
Info = [{ip, StateData#state.ip}, {conn, StateData#state.conn},
{auth_module, StateData#state.auth_module}],
ejabberd_sm:set_presence(StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- Priority,
- Packet,
- Info).
+ StateData#state.user, StateData#state.server,
+ StateData#state.resource, Priority, Packet, Info).
get_priority_from_presence(PresencePacket) ->
- case xml:get_subtag(PresencePacket, "priority") of
- false ->
- 0;
- SubEl ->
- case catch list_to_integer(xml:get_tag_cdata(SubEl)) of
- P when is_integer(P) ->
- P;
- _ ->
- 0
- end
+ case xml:get_subtag(PresencePacket, <<"priority">>) of
+ false -> 0;
+ SubEl ->
+ case catch
+ jlib:binary_to_integer(xml:get_tag_cdata(SubEl))
+ of
+ P when is_integer(P) -> P;
+ _ -> 0
+ end
end.
process_privacy_iq(From, To,
- #iq{type = Type, sub_el = SubEl} = IQ,
- StateData) ->
- {Res, NewStateData} =
- case Type of
- get ->
- R = ejabberd_hooks:run_fold(
- privacy_iq_get, StateData#state.server,
- {error, ?ERR_FEATURE_NOT_IMPLEMENTED},
- [From, To, IQ, StateData#state.privacy_list]),
- {R, StateData};
- set ->
- case ejabberd_hooks:run_fold(
- privacy_iq_set, StateData#state.server,
- {error, ?ERR_FEATURE_NOT_IMPLEMENTED},
- [From, To, IQ]) of
- {result, R, NewPrivList} ->
- {{result, R},
- StateData#state{privacy_list = NewPrivList}};
- R -> {R, StateData}
- end
- end,
- IQRes =
- case Res of
- {result, Result} ->
- IQ#iq{type = result, sub_el = Result};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]}
- end,
- ejabberd_router:route(
- To, From, jlib:iq_to_xml(IQRes)),
+ #iq{type = Type, sub_el = SubEl} = IQ, StateData) ->
+ {Res, NewStateData} = case Type of
+ get ->
+ R = ejabberd_hooks:run_fold(privacy_iq_get,
+ StateData#state.server,
+ {error,
+ ?ERR_FEATURE_NOT_IMPLEMENTED},
+ [From, To, IQ,
+ StateData#state.privacy_list]),
+ {R, StateData};
+ set ->
+ case ejabberd_hooks:run_fold(privacy_iq_set,
+ StateData#state.server,
+ {error,
+ ?ERR_FEATURE_NOT_IMPLEMENTED},
+ [From, To, IQ])
+ of
+ {result, R, NewPrivList} ->
+ {{result, R},
+ StateData#state{privacy_list =
+ NewPrivList}};
+ R -> {R, StateData}
+ end
+ end,
+ IQRes = case Res of
+ {result, Result} ->
+ IQ#iq{type = result, sub_el = Result};
+ {error, Error} ->
+ IQ#iq{type = error, sub_el = [SubEl, Error]}
+ end,
+ ejabberd_router:route(To, From, jlib:iq_to_xml(IQRes)),
NewStateData.
-
resend_offline_messages(StateData) ->
- case ejabberd_hooks:run_fold(
- resend_offline_messages_hook, StateData#state.server,
- [],
- [StateData#state.user, StateData#state.server]) of
- Rs when is_list(Rs) ->
- lists:foreach(
- fun({route,
- From, To, {xmlelement, _Name, _Attrs, _Els} = Packet}) ->
- Pass = case privacy_check_packet(StateData, From, To, Packet, in) of
- allow ->
- true;
- deny ->
- false
- end,
- if
- Pass ->
- %% Attrs2 = jlib:replace_from_to_attrs(
- %% jlib:jid_to_string(From),
- %% jlib:jid_to_string(To),
- %% Attrs),
- %% FixedPacket = {xmlelement, Name, Attrs2, Els},
- %% Use route instead of send_element to go through standard workflow
- ejabberd_router:route(From, To, Packet);
- %% send_element(StateData, FixedPacket),
- %% ejabberd_hooks:run(user_receive_packet,
- %% StateData#state.server,
- %% [StateData#state.jid,
- %% From, To, FixedPacket]);
- true ->
- ok
- end
- end, Rs)
+ case
+ ejabberd_hooks:run_fold(resend_offline_messages_hook,
+ StateData#state.server, [],
+ [StateData#state.user, StateData#state.server])
+ of
+ Rs -> %%when is_list(Rs) ->
+ lists:foreach(fun ({route, From, To,
+ #xmlel{} = Packet}) ->
+ Pass = case privacy_check_packet(StateData,
+ From, To,
+ Packet, in)
+ of
+ allow -> true;
+ deny -> false
+ end,
+ if Pass ->
+ ejabberd_router:route(From, To, Packet);
+ %% send_element(StateData, FixedPacket),
+ %% ejabberd_hooks:run(user_receive_packet,
+ %% StateData#state.server,
+ %% [StateData#state.jid,
+ %% From, To, FixedPacket]);
+ true -> ok
+ end
+ end,
+ Rs)
end.
resend_subscription_requests(#state{user = User,
- server = Server} = StateData) ->
- PendingSubscriptions = ejabberd_hooks:run_fold(
- resend_subscription_requests_hook,
- Server,
- [],
- [User, Server]),
- lists:foreach(fun(XMLPacket) ->
- send_element(StateData,
- XMLPacket)
+ server = Server} =
+ StateData) ->
+ PendingSubscriptions =
+ ejabberd_hooks:run_fold(resend_subscription_requests_hook,
+ Server, [], [User, Server]),
+ lists:foreach(fun (XMLPacket) ->
+ send_element(StateData, XMLPacket)
end,
PendingSubscriptions).
-get_showtag(undefined) ->
- "unavailable";
+get_showtag(undefined) -> <<"unavailable">>;
get_showtag(Presence) ->
- case xml:get_path_s(Presence, [{elem, "show"}, cdata]) of
- "" -> "available";
- ShowTag -> ShowTag
+ case xml:get_path_s(Presence,
+ [{elem, <<"show">>}, cdata])
+ of
+ <<"">> -> <<"available">>;
+ ShowTag -> ShowTag
end.
-get_statustag(undefined) ->
- "";
+get_statustag(undefined) -> <<"">>;
get_statustag(Presence) ->
- case xml:get_path_s(Presence, [{elem, "status"}, cdata]) of
- ShowTag -> ShowTag
+ case xml:get_path_s(Presence,
+ [{elem, <<"status">>}, cdata])
+ of
+ ShowTag -> ShowTag
end.
process_unauthenticated_stanza(StateData, El) ->
- NewEl = case xml:get_tag_attr_s("xml:lang", El) of
- "" ->
- case StateData#state.lang of
- "" -> El;
- Lang ->
- xml:replace_tag_attr("xml:lang", Lang, El)
- end;
- _ ->
- El
+ NewEl = case xml:get_tag_attr_s(<<"xml:lang">>, El) of
+ <<"">> ->
+ case StateData#state.lang of
+ <<"">> -> El;
+ Lang -> xml:replace_tag_attr(<<"xml:lang">>, Lang, El)
+ end;
+ _ -> El
end,
case jlib:iq_query_info(NewEl) of
- #iq{} = IQ ->
- Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq,
- StateData#state.server,
- empty,
- [StateData#state.server, IQ,
- StateData#state.ip]),
- case Res of
- empty ->
- % The only reasonable IQ's here are auth and register IQ's
- % They contain secrets, so don't include subelements to response
- ResIQ = IQ#iq{type = error,
- sub_el = [?ERR_SERVICE_UNAVAILABLE]},
- Res1 = jlib:replace_from_to(
- jlib:make_jid("", StateData#state.server, ""),
- jlib:make_jid("", "", ""),
- jlib:iq_to_xml(ResIQ)),
- send_element(StateData, jlib:remove_attr("to", Res1));
- _ ->
- send_element(StateData, Res)
- end;
- _ ->
- % Drop any stanza, which isn't IQ stanza
- ok
+ #iq{} = IQ ->
+ Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq,
+ StateData#state.server, empty,
+ [StateData#state.server, IQ,
+ StateData#state.ip]),
+ case Res of
+ empty ->
+ ResIQ = IQ#iq{type = error,
+ sub_el = [?ERR_SERVICE_UNAVAILABLE]},
+ Res1 = jlib:replace_from_to(jlib:make_jid(<<"">>,
+ StateData#state.server,
+ <<"">>),
+ jlib:make_jid(<<"">>, <<"">>,
+ <<"">>),
+ jlib:iq_to_xml(ResIQ)),
+ send_element(StateData,
+ jlib:remove_attr(<<"to">>, Res1));
+ _ -> send_element(StateData, Res)
+ end;
+ _ ->
+ % Drop any stanza, which isn't IQ stanza
+ ok
end.
peerip(SockMod, Socket) ->
IP = case SockMod of
- gen_tcp -> inet:peername(Socket);
- _ -> SockMod:peername(Socket)
+ gen_tcp -> inet:peername(Socket);
+ _ -> SockMod:peername(Socket)
end,
case IP of
- {ok, IPOK} -> IPOK;
- _ -> undefined
+ {ok, IPOK} -> IPOK;
+ _ -> undefined
end.
%% fsm_next_state_pack: Pack the StateData structure to improve
@@ -2227,70 +2225,64 @@ fsm_next_state_gc(StateName, PackedStateData) ->
%% fsm_next_state: Generate the next_state FSM tuple with different
%% timeout, depending on the future state
fsm_next_state(session_established, StateData) ->
- {next_state, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
+ {next_state, session_established, StateData,
+ ?C2S_HIBERNATE_TIMEOUT};
fsm_next_state(StateName, StateData) ->
{next_state, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
%% fsm_reply: Generate the reply FSM tuple with different timeout,
%% depending on the future state
fsm_reply(Reply, session_established, StateData) ->
- {reply, Reply, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
+ {reply, Reply, session_established, StateData,
+ ?C2S_HIBERNATE_TIMEOUT};
fsm_reply(Reply, StateName, StateData) ->
{reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
%% Used by c2s blacklist plugins
-is_ip_blacklisted(undefined) ->
- false;
-is_ip_blacklisted({IP,_Port}) ->
+is_ip_blacklisted(undefined) -> false;
+is_ip_blacklisted({IP, _Port}) ->
ejabberd_hooks:run_fold(check_bl_c2s, false, [IP]).
%% Check from attributes
%% returns invalid-from|NewElement
check_from(El, FromJID) ->
- case xml:get_tag_attr("from", El) of
- false ->
- El;
- {value, SJID} ->
- JID = jlib:string_to_jid(SJID),
- case JID of
- error ->
- 'invalid-from';
- #jid{} ->
- if
- (JID#jid.luser == FromJID#jid.luser) and
- (JID#jid.lserver == FromJID#jid.lserver) and
- (JID#jid.lresource == FromJID#jid.lresource) ->
- El;
- (JID#jid.luser == FromJID#jid.luser) and
- (JID#jid.lserver == FromJID#jid.lserver) and
- (JID#jid.lresource == "") ->
- El;
- true ->
- 'invalid-from'
- end
- end
+ case xml:get_tag_attr(<<"from">>, El) of
+ false -> El;
+ {value, SJID} ->
+ JID = jlib:string_to_jid(SJID),
+ case JID of
+ error -> 'invalid-from';
+ #jid{} ->
+ if (JID#jid.luser == FromJID#jid.luser) and
+ (JID#jid.lserver == FromJID#jid.lserver)
+ and (JID#jid.lresource == FromJID#jid.lresource) ->
+ El;
+ (JID#jid.luser == FromJID#jid.luser) and
+ (JID#jid.lserver == FromJID#jid.lserver)
+ and (JID#jid.lresource == <<"">>) ->
+ El;
+ true -> 'invalid-from'
+ end
+ end
end.
fsm_limit_opts(Opts) ->
case lists:keysearch(max_fsm_queue, 1, Opts) of
- {value, {_, N}} when is_integer(N) ->
- [{max_queue, N}];
- _ ->
- case ejabberd_config:get_local_option(max_fsm_queue) of
- N when is_integer(N) ->
- [{max_queue, N}];
- _ ->
- []
- end
+ {value, {_, N}} when is_integer(N) -> [{max_queue, N}];
+ _ ->
+ case ejabberd_config:get_local_option(
+ max_fsm_queue,
+ fun(I) when is_integer(I), I > 0 -> I end) of
+ undefined -> [];
+ N -> [{max_queue, N}]
+ end
end.
bounce_messages() ->
receive
- {route, From, To, El} ->
- ejabberd_router:route(From, To, El),
- bounce_messages()
- after 0 ->
- ok
+ {route, From, To, El} ->
+ ejabberd_router:route(From, To, El), bounce_messages()
+ after 0 -> ok
end.
%%%----------------------------------------------------------------------
@@ -2298,40 +2290,40 @@ bounce_messages() ->
%%%----------------------------------------------------------------------
route_blocking(What, StateData) ->
- SubEl =
- case What of
- {block, JIDs} ->
- {xmlelement, "block",
- [{"xmlns", ?NS_BLOCKING}],
- lists:map(
- fun(JID) ->
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string(JID)}],
- []}
- end, JIDs)};
- {unblock, JIDs} ->
- {xmlelement, "unblock",
- [{"xmlns", ?NS_BLOCKING}],
- lists:map(
- fun(JID) ->
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string(JID)}],
- []}
- end, JIDs)};
- unblock_all ->
- {xmlelement, "unblock",
- [{"xmlns", ?NS_BLOCKING}], []}
- end,
- PrivPushIQ =
- #iq{type = set, xmlns = ?NS_BLOCKING,
- id = "push",
- sub_el = [SubEl]},
+ SubEl = case What of
+ {block, JIDs} ->
+ #xmlel{name = <<"block">>,
+ attrs = [{<<"xmlns">>, ?NS_BLOCKING}],
+ children =
+ lists:map(fun (JID) ->
+ #xmlel{name = <<"item">>,
+ attrs =
+ [{<<"jid">>,
+ jlib:jid_to_string(JID)}],
+ children = []}
+ end,
+ JIDs)};
+ {unblock, JIDs} ->
+ #xmlel{name = <<"unblock">>,
+ attrs = [{<<"xmlns">>, ?NS_BLOCKING}],
+ children =
+ lists:map(fun (JID) ->
+ #xmlel{name = <<"item">>,
+ attrs =
+ [{<<"jid">>,
+ jlib:jid_to_string(JID)}],
+ children = []}
+ end,
+ JIDs)};
+ unblock_all ->
+ #xmlel{name = <<"unblock">>,
+ attrs = [{<<"xmlns">>, ?NS_BLOCKING}], children = []}
+ end,
+ PrivPushIQ = #iq{type = set, xmlns = ?NS_BLOCKING,
+ id = <<"push">>, sub_el = [SubEl]},
PrivPushEl =
- jlib:replace_from_to(
- jlib:jid_remove_resource(
- StateData#state.jid),
- StateData#state.jid,
- jlib:iq_to_xml(PrivPushIQ)),
+ jlib:replace_from_to(jlib:jid_remove_resource(StateData#state.jid),
+ StateData#state.jid, jlib:iq_to_xml(PrivPushIQ)),
send_element(StateData, PrivPushEl),
%% No need to replace active privacy list here,
%% blocking pushes are always accompanied by
@@ -2344,45 +2336,35 @@ route_blocking(What, StateData) ->
%% Try to reduce the heap footprint of the four presence sets
%% by ensuring that we re-use strings and Jids wherever possible.
-pack(S = #state{pres_a=A,
- pres_i=I,
- pres_f=F,
- pres_t=T}) ->
- {NewA, Pack1} = pack_jid_set(A, gb_trees:empty()),
- {NewI, Pack2} = pack_jid_set(I, Pack1),
+pack(S = #state{pres_a = A, pres_f = F,
+ pres_t = T}) ->
+ {NewA, Pack2} = pack_jid_set(A, gb_trees:empty()),
{NewF, Pack3} = pack_jid_set(F, Pack2),
{NewT, _Pack4} = pack_jid_set(T, Pack3),
- %% Throw away Pack4 so that if we delete references to
- %% Strings or Jids in any of the sets there will be
- %% no live references for the GC to find.
- S#state{pres_a=NewA,
- pres_i=NewI,
- pres_f=NewF,
- pres_t=NewT}.
+ S#state{pres_a = NewA, pres_f = NewF,
+ pres_t = NewT}.
pack_jid_set(Set, Pack) ->
- Jids = ?SETS:to_list(Set),
+ Jids = (?SETS):to_list(Set),
{PackedJids, NewPack} = pack_jids(Jids, Pack, []),
- {?SETS:from_list(PackedJids), NewPack}.
+ {(?SETS):from_list(PackedJids), NewPack}.
pack_jids([], Pack, Acc) -> {Acc, Pack};
-pack_jids([{U,S,R}=Jid | Jids], Pack, Acc) ->
+pack_jids([{U, S, R} = Jid | Jids], Pack, Acc) ->
case gb_trees:lookup(Jid, Pack) of
- {value, PackedJid} ->
- pack_jids(Jids, Pack, [PackedJid | Acc]);
- none ->
- {NewU, Pack1} = pack_string(U, Pack),
- {NewS, Pack2} = pack_string(S, Pack1),
- {NewR, Pack3} = pack_string(R, Pack2),
- NewJid = {NewU, NewS, NewR},
- NewPack = gb_trees:insert(NewJid, NewJid, Pack3),
- pack_jids(Jids, NewPack, [NewJid | Acc])
+ {value, PackedJid} ->
+ pack_jids(Jids, Pack, [PackedJid | Acc]);
+ none ->
+ {NewU, Pack1} = pack_string(U, Pack),
+ {NewS, Pack2} = pack_string(S, Pack1),
+ {NewR, Pack3} = pack_string(R, Pack2),
+ NewJid = {NewU, NewS, NewR},
+ NewPack = gb_trees:insert(NewJid, NewJid, Pack3),
+ pack_jids(Jids, NewPack, [NewJid | Acc])
end.
pack_string(String, Pack) ->
case gb_trees:lookup(String, Pack) of
- {value, PackedString} ->
- {PackedString, Pack};
- none ->
- {String, gb_trees:insert(String, String, Pack)}
+ {value, PackedString} -> {PackedString, Pack};
+ none -> {String, gb_trees:insert(String, String, Pack)}
end.