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.erl5798
1 files changed, 2818 insertions, 2980 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index f92898d99..9853bb3ac 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).
@@ -33,107 +35,106 @@
-behaviour(?GEN_FSM).
%% External exports
--export([start/2,
- stop/1,
- start_link/3,
- send_text/2,
- send_element/2,
- socket_type/0,
- get_presence/1,
- get_aux_field/2,
- set_aux_field/3,
- del_aux_field/2,
- get_subscription/2,
- broadcast/4,
- get_subscribed/1]).
+-export([start/2, stop/1, start_link/3, send_text/2,
+ send_element/2, socket_type/0, get_presence/1,
+ get_aux_field/2, set_aux_field/3, del_aux_field/2,
+ get_subscription/2, broadcast/4, get_subscribed/1]).
%% API:
-export([add_rosteritem/3, del_rosteritem/2]).
%% gen_fsm callbacks
--export([init/1,
- wait_for_stream/2,
- wait_for_auth/2,
- wait_for_feature_request/2,
- wait_for_bind/2,
- wait_for_session/2,
- wait_for_sasl_response/2,
- session_established/2,
- handle_event/3,
- handle_sync_event/4,
- code_change/4,
- handle_info/3,
- terminate/3,
- print_state/1,
- migrate/3,
- migrate_shutdown/3
- ]).
+-export([init/1, wait_for_stream/2, wait_for_auth/2,
+ wait_for_feature_request/2, wait_for_bind/2,
+ wait_for_session/2, wait_for_sasl_response/2,
+ session_established/2, handle_event/3,
+ handle_sync_event/4, code_change/4, handle_info/3,
+ terminate/3, print_state/1, migrate/3,
+ migrate_shutdown/3]).
-include("ejabberd.hrl").
+
-include("jlib.hrl").
+
-include("mod_privacy.hrl").
+
-include("ejabberd_c2s.hrl").
%-define(DBGFSM, true).
-ifdef(DBGFSM).
+
-define(FSMOPTS, [{debug, [trace]}]).
+
-else.
+
-define(FSMOPTS, []).
+
-endif.
%% Module start with or without supervisor:
-ifdef(NO_TRANSIENT_SUPERVISORS).
--define(SUPERVISOR_START, ?GEN_FSM:start(ejabberd_c2s,
- [SockData, Opts, FSMLimitOpts],
- FSMLimitOpts ++ ?FSMOPTS)).
+
+-define(SUPERVISOR_START,
+ (?GEN_FSM):start(ejabberd_c2s,
+ [SockData, Opts, FSMLimitOpts],
+ FSMLimitOpts ++ (?FSMOPTS))).
+
-else.
--define(SUPERVISOR_START, supervisor:start_child(ejabberd_c2s_sup,
- [SockData, Opts, FSMLimitOpts])).
+
+-define(SUPERVISOR_START,
+ supervisor:start_child(ejabberd_c2s_sup,
+ [SockData, Opts, FSMLimitOpts])).
+
-endif.
-%% 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(FLASH_STREAM_HEADER,
- "<?xml version='1.0'?>"
- "<flash:stream xmlns='jabber:client' "
- "xmlns:stream='http://etherx.jabber.org/streams' "
- "id='~s' from='~s'~s~s>"
- ).
+ <<"<?xml version='1.0'?><flash:stream xmlns='jab"
+ "ber:client' xmlns:stream='http://etherx.jabbe"
+ "r.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).
--define(NS_P1_REBIND, "p1:rebind").
--define(NS_P1_PUSH, "p1:push").
--define(NS_P1_ACK, "p1:ack").
--define(NS_P1_PUSHED, "p1:pushed").
--define(NS_P1_ATTACHMENT, "http://process-one.net/attachement").
+-define(NS_P1_REBIND, <<"p1:rebind">>).
+
+-define(NS_P1_PUSH, <<"p1:push">>).
+
+-define(NS_P1_ACK, <<"p1:ack">>).
+
+-define(NS_P1_PUSHED, <<"p1:pushed">>).
+
+-define(NS_P1_ATTACHMENT,
+ <<"http://process-one.net/attachement">>).
-define(C2S_P1_ACK_TIMEOUT, 10000).
--define(MAX_OOR_TIMEOUT, 1440). %% Max allowed session duration 24h (24*60)
+
+-define(MAX_OOR_TIMEOUT, 1440).
+
-define(MAX_OOR_MESSAGES, 1000).
-%%%----------------------------------------------------------------------
-%%% API
-%%%----------------------------------------------------------------------
-start(StateName, #state{fsm_limit_opts = Opts} = State) ->
+start(StateName,
+ #state{fsm_limit_opts = Opts} = State) ->
start(StateName, State, Opts);
start(SockData, Opts) ->
start(SockData, Opts, fsm_limit_opts(Opts)).
@@ -142,33 +143,34 @@ start(SockData, Opts, FSMLimitOpts) ->
?SUPERVISOR_START.
start_link(SockData, Opts, FSMLimitOpts) ->
- ?GEN_FSM:start_link(ejabberd_c2s, [SockData, Opts, FSMLimitOpts],
- FSMLimitOpts ++ ?FSMOPTS).
+ (?GEN_FSM):start_link(ejabberd_c2s,
+ [SockData, Opts, FSMLimitOpts],
+ FSMLimitOpts ++ (?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).
add_rosteritem(FsmRef, IJID, ISubscription) ->
- ?GEN_FSM:send_all_state_event(FsmRef, {add_rosteritem, IJID, ISubscription}).
+ (?GEN_FSM):send_all_state_event(FsmRef,
+ {add_rosteritem, IJID, ISubscription}).
del_rosteritem(FsmRef, IJID) ->
- ?GEN_FSM:send_all_state_event(FsmRef, {del_rosteritem, IJID}).
+ (?GEN_FSM):send_all_state_event(FsmRef,
+ {del_rosteritem, IJID}).
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),
@@ -177,11 +179,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;
@@ -191,8 +195,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).
migrate(FsmRef, Node, After) ->
erlang:send_after(After, FsmRef, {migrate, Node}).
@@ -204,127 +207,105 @@ migrate_shutdown(FsmRef, Node, After) ->
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
-%%----------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, StateName, StateData} |
-%% {ok, StateName, StateData, Timeout} |
-%% ignore |
-%% {stop, StopReason}
-%%----------------------------------------------------------------------
init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
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],
Redirect = case lists:keysearch(redirect, 1, Opts) of
- {value, {_, true}} ->
- true;
- _ ->
- false
- end,
+ {value, {_, true}} -> true;
+ _ -> false
+ end,
IP = case lists:keysearch(frontend_ip, 1, Opts) of
- {value, {_, IP1}} ->
- IP1;
- _ ->
- peerip(SockMod, Socket)
+ {value, {_, IP1}} -> IP1;
+ _ -> peerip(SockMod, Socket)
end,
- %% Check if IP is blacklisted:
+ FlashHack = ejabberd_config:get_local_option(
+ flash_hack, fun(V) -> V end, false),
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 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,
- redirect = Redirect,
- fsm_limit_opts = FSMLimitOpts},
- erlang:send_after(?C2S_OPEN_TIMEOUT, self(), open_timeout),
- case get_jid_from_opts(Opts) of
- {ok, #jid{user = U, server = Server, resource = R} = JID} ->
- ?GEN_FSM:send_event(self(), open_session),
- {ok, wait_for_session, StateData#state{
- user = U,
- server = Server,
- resource = R,
- jid = JID,
- lang = ""}};
- _ ->
- {ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
- end
+ 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, redirect = Redirect,
+ flash_hack = FlashHack,
+ fsm_limit_opts = FSMLimitOpts},
+ erlang:send_after(?C2S_OPEN_TIMEOUT, self(),
+ open_timeout),
+ case get_jid_from_opts(Opts) of
+ {ok,
+ #jid{user = U, server = Server, resource = R} = JID} ->
+ (?GEN_FSM):send_event(self(), open_session),
+ {ok, wait_for_session,
+ StateData#state{user = U, server = Server, resource = R,
+ jid = JID, lang = <<"">>}};
+ _ -> {ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
+ end
end;
init([StateName, StateData, _FSMLimitOpts]) ->
- MRef = (StateData#state.sockmod):monitor(StateData#state.socket),
+ MRef =
+ (StateData#state.sockmod):monitor(StateData#state.socket),
if StateName == session_established ->
- Conn = get_conn_type(StateData),
- Info = [{ip, StateData#state.ip}, {conn, Conn},
- {auth_module, StateData#state.auth_module}],
- {Time, _} = StateData#state.sid,
- SID = {Time, self()},
- Priority = case StateData#state.pres_last of
- undefined ->
- undefined;
- El ->
- get_priority_from_presence(El)
- end,
- ejabberd_sm:drop_session(StateData#state.sid),
- ejabberd_sm:open_session(
- SID,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- Priority,
- Info),
- %%ejabberd_sm:drop_session(StateData#state.sid),
- NewStateData = StateData#state{sid = SID, socket_monitor = MRef},
- StateData2 = change_reception(NewStateData, true),
- StateData3 = start_keepalive_timer(StateData2),
- {ok, StateName, StateData3};
+ Conn = (StateData#state.sockmod):get_conn_type(
+ StateData#state.socket),
+ Info = [{ip, StateData#state.ip}, {conn, Conn},
+ {auth_module, StateData#state.auth_module}],
+ {Time, _} = StateData#state.sid,
+ SID = {Time, self()},
+ Priority = case StateData#state.pres_last of
+ undefined -> undefined;
+ El -> get_priority_from_presence(El)
+ end,
+ ejabberd_sm:drop_session(StateData#state.sid),
+ ejabberd_sm:open_session(SID, StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource, Priority, Info),
+ NewStateData = StateData#state{sid = SID,
+ socket_monitor = MRef},
+ StateData2 = change_reception(NewStateData, true),
+ StateData3 = start_keepalive_timer(StateData2),
+ {ok, StateName, StateData3};
true ->
- {ok, StateName, StateData#state{socket_monitor = MRef}}
+ {ok, StateName, StateData#state{socket_monitor = MRef}}
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
@@ -333,1003 +314,1052 @@ get_subscribed(FsmRef) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
-wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
- DefaultLang = case ?MYLANG of
- undefined ->
- "en";
- DL ->
- DL
- end,
-
- case {xml:get_attr_s("xmlns:stream", Attrs),
- xml:get_attr_s("xmlns:flash", Attrs),
- ?FLASH_HACK,
- StateData#state.flash_connection} of
- {_, ?NS_FLASH_STREAM, true, false} ->
- %% Flash client connecting - attention!
- %% Some of them don't provide an xmlns:stream attribute -
- %% compensate for that.
- wait_for_stream({xmlstreamstart, Name, [{"xmlns:stream", ?NS_STREAM}|Attrs]},
- StateData#state{flash_connection = true});
- {?NS_STREAM, _, _, _} ->
- 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
- Lang1 when length(Lang1) =< 35 ->
- %% As stated in BCP47, 4.4.1:
- %% Protocols or specifications that
- %% specify limited buffer sizes for
- %% language tags MUST allow for
- %% language tags of at least 35 characters.
- Lang1;
- _ ->
- %% 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),
- case StateData#state.authenticated of
- false ->
- SASLState =
- cyrsasl:server_new(
- "jabber", Server, "", [],
- fun(U) ->
- ejabberd_auth:get_password_with_authmodule(
- U, Server)
- end,
- fun(U, P) ->
- ejabberd_auth:check_password_with_authmodule(
- U, Server, P)
- end,
- fun(U, P, D, DG) ->
- 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)),
- SockMod =
- (StateData#state.sockmod):get_sockmod(
- StateData#state.socket),
- Zlib = StateData#state.zlib,
- CompressFeature =
- case Zlib andalso
- ((SockMod == gen_tcp) orelse
- (SockMod == tls)) of
- true ->
- [{xmlelement, "compression",
- [{"xmlns", ?NS_FEATURE_COMPRESS}],
- [{xmlelement, "method",
- [], [{xmlcdata, "zlib"}]}]}];
- _ ->
- []
- end,
- TLS = StateData#state.tls,
- TLSEnabled = StateData#state.tls_enabled,
- TLSRequired = StateData#state.tls_required,
- TLSFeature =
- case (TLS == true) andalso
- (TLSEnabled == false) andalso
- (SockMod == gen_tcp) of
- true ->
- case TLSRequired of
- true ->
- [{xmlelement, "starttls",
- [{"xmlns", ?NS_TLS}],
- [{xmlelement, "required",
- [], []}]}];
- _ ->
- [{xmlelement, "starttls",
- [{"xmlns", ?NS_TLS}], []}]
- end;
- false ->
- []
- end,
- P1PushFeature =
- [{xmlelement, "push",
- [{"xmlns", ?NS_P1_PUSH}], []}],
- P1RebindFeature =
- [{xmlelement, "rebind",
- [{"xmlns", ?NS_P1_REBIND}], []}],
- P1AckFeature =
- [{xmlelement, "ack",
- [{"xmlns", ?NS_P1_ACK}], []}],
- send_element(StateData,
- {xmlelement, "stream:features", [],
- TLSFeature ++
- CompressFeature ++
- P1PushFeature ++
- P1RebindFeature ++
- P1AckFeature ++
- [{xmlelement, "mechanisms",
- [{"xmlns", ?NS_SASL}],
- Mechs}] ++
- ejabberd_hooks:run_fold(
- c2s_stream_features,
- Server,
- [], [Server])}),
- fsm_next_state(wait_for_feature_request,
- StateData#state{
- server = Server,
- sasl_state = SASLState,
- lang = Lang});
- _ ->
- case StateData#state.resource of
- "" ->
- RosterVersioningFeature =
- ejabberd_hooks:run_fold(
- roster_get_versioning_feature,
- Server, [], [Server]),
- StreamFeatures =
- [{xmlelement, "push",
- [{"xmlns", ?NS_P1_PUSH}], []},
- {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})
- end
- end;
+wait_for_stream({xmlstreamstart, Name, Attrs},
+ StateData) ->
+ DefaultLang = ?MYLANG,
+ case {xml:get_attr_s(<<"xmlns:stream">>, Attrs),
+ xml:get_attr_s(<<"xmlns:flash">>, Attrs),
+ StateData#state.flash_hack,
+ StateData#state.flash_connection}
+ of
+ {_, ?NS_FLASH_STREAM, true, false} ->
+ wait_for_stream({xmlstreamstart, Name,
+ [{<<"xmlns:stream">>, ?NS_STREAM} | Attrs]},
+ StateData#state{flash_connection = true});
+ {?NS_STREAM, _, _, _} ->
+ 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
+ Lang1 when byte_size(Lang1) =< 35 ->
+ %% As stated in BCP47, 4.4.1:
+ %% Protocols or specifications that
+ %% specify limited buffer sizes for
+ %% language tags MUST allow for
+ %% language tags of at least 35 characters.
+ Lang1;
+ _ ->
+ %% 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),
+ case StateData#state.authenticated of
+ false ->
+ SASLState = cyrsasl:server_new(<<"jabber">>, Server,
+ <<"">>, [],
+ fun (U) ->
+ ejabberd_auth:get_password_with_authmodule(U,
+ Server)
+ end,
+ fun (U, P) ->
+ ejabberd_auth:check_password_with_authmodule(U,
+ Server,
+ P)
+ end,
+ fun (U, P, D, DG) ->
+ ejabberd_auth:check_password_with_authmodule(U,
+ Server,
+ P,
+ D,
+ DG)
+ end),
+ 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),
+ Zlib = StateData#state.zlib,
+ CompressFeature = case Zlib andalso
+ (SockMod == gen_tcp orelse
+ SockMod == tls)
+ of
+ true ->
+ [#xmlel{name =
+ <<"compression">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_FEATURE_COMPRESS}],
+ children =
+ [#xmlel{name =
+ <<"method">>,
+ attrs =
+ [],
+ children
+ =
+ [{xmlcdata,
+ <<"zlib">>}]}]}];
+ _ -> []
+ end,
+ TLS = StateData#state.tls,
+ TLSEnabled = StateData#state.tls_enabled,
+ TLSRequired = StateData#state.tls_required,
+ TLSFeature = case TLS == true andalso
+ TLSEnabled == false andalso
+ SockMod == gen_tcp
+ of
+ true ->
+ case TLSRequired of
+ true ->
+ [#xmlel{name =
+ <<"starttls">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_TLS}],
+ children =
+ [#xmlel{name =
+ <<"required">>,
+ attrs =
+ [],
+ children
+ =
+ []}]}];
+ _ ->
+ [#xmlel{name =
+ <<"starttls">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_TLS}],
+ children = []}]
+ end;
+ false -> []
+ end,
+ P1PushFeature = [#xmlel{name = <<"push">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_P1_PUSH}],
+ children = []}],
+ P1RebindFeature = [#xmlel{name = <<"rebind">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_P1_REBIND}],
+ children = []}],
+ P1AckFeature = [#xmlel{name = <<"ack">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_P1_ACK}],
+ children = []}],
+ send_element(StateData,
+ #xmlel{name = <<"stream:features">>,
+ attrs = [],
+ children =
+ TLSFeature ++
+ CompressFeature ++
+ P1PushFeature ++
+ P1RebindFeature ++
+ P1AckFeature ++
+ [#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,
+ sasl_state =
+ SASLState,
+ lang = Lang});
_ ->
- 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})
+ case StateData#state.resource of
+ <<"">> ->
+ RosterVersioningFeature =
+ ejabberd_hooks:run_fold(roster_get_versioning_feature,
+ Server, [],
+ [Server]),
+ StreamFeatures = [#xmlel{name = <<"push">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_P1_PUSH}],
+ children = []},
+ #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, ?MYNAME, "", DefaultLang),
- send_element(StateData, ?HOST_UNKNOWN_ERR),
- send_trailer(StateData),
- {stop, normal, StateData}
- end;
- _ ->
- case Name of
- "policy-file-request" ->
- send_text(StateData, flash_policy_string()),
- {stop, normal, StateData};
- _ ->
- send_header(StateData, ?MYNAME, "", DefaultLang),
- send_element(StateData, ?INVALID_NS_ERR),
- send_trailer(StateData),
- {stop, normal, StateData}
- 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}
+ end;
+ _ ->
+ case Name of
+ <<"policy-file-request">> ->
+ send_text(StateData, flash_policy_string()),
+ {stop, normal, StateData};
+ _ ->
+ send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
+ send_element(StateData, ?INVALID_NS_ERR),
+ send_trailer(StateData),
+ {stop, normal, StateData}
+ end
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]),
- case need_redirect(StateData#state{user = U}) of
- {true, Host} ->
- ?INFO_MSG("(~w) Redirecting ~s to ~s",
- [StateData#state.socket,
- jlib:jid_to_string(JID), Host]),
- send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
- send_trailer(StateData),
- {stop, normal, StateData};
- false ->
- SID = {now(), self()},
- Conn = get_conn_type(StateData),
- Res1 = jlib:make_result_iq_reply(El),
- Res = setelement(4, Res1, []),
- send_element(StateData, Res),
- 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},
- DebugFlag = ejabberd_hooks:run_fold(
- c2s_debug_start_hook,
- NewStateData#state.server,
- false,
- [self(), NewStateData]),
- maybe_migrate(session_established,
- NewStateData#state{debug=DebugFlag})
- end;
- _ ->
- ?INFO_MSG(
- "(~w) Failed legacy authentication for ~s",
- [StateData#state.socket,
- jlib:jid_to_string(JID)]),
- 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;
- _ ->
- {xmlelement, Name, Attrs, _Els} = El,
- case {xml:get_attr_s("xmlns", Attrs), Name} of
- {?NS_P1_REBIND, "rebind"} ->
- SJID = xml:get_path_s(El, [{elem, "jid"}, cdata]),
- SID = xml:get_path_s(El, [{elem, "sid"}, cdata]),
- case jlib:string_to_jid(SJID) of
- error ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_P1_REBIND}],
- [{xmlcdata, "Invalid JID"}]}),
- fsm_next_state(wait_for_auth,
- StateData);
- JID ->
- case rebind(StateData, JID, SID) of
- {next_state, wait_for_feature_request,
- NewStateData, Timeout} ->
- {next_state, wait_for_auth,
- NewStateData, Timeout};
- Res ->
- Res
- end
- end;
- _ ->
- process_unauthenticated_stanza(StateData, El),
- fsm_next_state(wait_for_auth, StateData)
- end
+ {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]),
+ case need_redirect(StateData#state{user = U}) of
+ {true, Host} ->
+ ?INFO_MSG("(~w) Redirecting ~s to ~s",
+ [StateData#state.socket,
+ jlib:jid_to_string(JID), Host]),
+ send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ false ->
+ SID = {now(), self()},
+ Conn = (StateData#state.sockmod):get_conn_type(
+ StateData#state.socket),
+ Res1 = jlib:make_result_iq_reply(El),
+ Res = Res1#xmlel{children = []},
+ send_element(StateData, Res),
+ 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},
+ DebugFlag =
+ ejabberd_hooks:run_fold(c2s_debug_start_hook,
+ NewStateData#state.server,
+ false,
+ [self(), NewStateData]),
+ maybe_migrate(session_established,
+ NewStateData#state{debug = DebugFlag})
+ end;
+ _ ->
+ ?INFO_MSG("(~w) Failed legacy authentication for ~s",
+ [StateData#state.socket,
+ jlib:jid_to_string(JID)]),
+ 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;
+ _ ->
+ #xmlel{name = Name, attrs = Attrs} = El,
+ case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ {?NS_P1_REBIND, <<"rebind">>} ->
+ SJID = xml:get_path_s(El, [{elem, <<"jid">>}, cdata]),
+ SID = xml:get_path_s(El, [{elem, <<"sid">>}, cdata]),
+ case jlib:string_to_jid(SJID) of
+ error ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs =
+ [{<<"xmlns">>, ?NS_P1_REBIND}],
+ children =
+ [{xmlcdata, <<"Invalid JID">>}]}),
+ fsm_next_state(wait_for_auth, StateData);
+ JID ->
+ case rebind(StateData, JID, SID) of
+ {next_state, wait_for_feature_request, NewStateData,
+ Timeout} ->
+ {next_state, wait_for_auth, NewStateData, Timeout};
+ Res -> Res
+ end
+ end;
+ _ ->
+ process_unauthenticated_stanza(StateData, El),
+ fsm_next_state(wait_for_auth, StateData)
+ end
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} ->
- catch (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- 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]),
- case need_redirect(StateData#state{user = U}) of
- {true, Host} ->
- ?INFO_MSG("(~w) Redirecting ~s to ~s",
- [StateData#state.socket, U, Host]),
- send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
- send_trailer(StateData),
- {stop, normal, StateData};
- false ->
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}], []}),
- fsm_next_state(wait_for_stream,
- StateData#state{
- streamid = new_id(),
- authenticated = true,
- auth_module = AuthModule,
- user = U })
- end;
- {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} ->
- ?INFO_MSG(
- "(~w) Failed authentication for ~s@~s",
- [StateData#state.socket,
- Username, StateData#state.server]),
- 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;
- {?NS_P1_REBIND, "rebind"} ->
- SJID = xml:get_path_s(El, [{elem, "jid"}, cdata]),
- SID = xml:get_path_s(El, [{elem, "sid"}, cdata]),
- case jlib:string_to_jid(SJID) of
- error ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_P1_REBIND}],
- [{xmlcdata, "Invalid JID"}]}),
- fsm_next_state(wait_for_feature_request,
- StateData);
- JID ->
- rebind(StateData, JID, SID)
- end;
- {?NS_P1_ACK, "ack"} ->
- fsm_next_state(wait_for_feature_request,
- StateData#state{ack_enabled = true});
- _ ->
- 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
+ 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} ->
+ 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, undefined),
+ ?INFO_MSG("(~w) Accepted authentication for ~s "
+ "by ~p",
+ [StateData#state.socket, U, AuthModule]),
+ case need_redirect(StateData#state{user = U}) of
+ {true, Host} ->
+ ?INFO_MSG("(~w) Redirecting ~s to ~s",
+ [StateData#state.socket, U, Host]),
+ send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ false ->
+ 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})
+ end;
+ {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} ->
+ ?INFO_MSG("(~w) Failed authentication for ~s@~s",
+ [StateData#state.socket, Username,
+ StateData#state.server]),
+ 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;
+ {?NS_P1_REBIND, <<"rebind">>} ->
+ SJID = xml:get_path_s(El, [{elem, <<"jid">>}, cdata]),
+ SID = xml:get_path_s(El, [{elem, <<"sid">>}, cdata]),
+ case jlib:string_to_jid(SJID) of
+ error ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_P1_REBIND}],
+ children =
+ [{xmlcdata, <<"Invalid JID">>}]}),
+ fsm_next_state(wait_for_feature_request, StateData);
+ JID -> rebind(StateData, JID, SID)
+ end;
+ {?NS_P1_ACK, <<"ack">>} ->
+ fsm_next_state(wait_for_feature_request,
+ StateData#state{ack_enabled = true});
+ _ ->
+ 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} ->
- catch (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- 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]),
- case need_redirect(StateData#state{user = U}) of
- {true, Host} ->
- ?INFO_MSG("(~w) Redirecting ~s to ~s",
- [StateData#state.socket, U, Host]),
- send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
- send_trailer(StateData),
- {stop, normal, StateData};
- false ->
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}], []}),
- fsm_next_state(wait_for_stream,
- StateData#state{
- streamid = new_id(),
- authenticated = true,
- auth_module = AuthModule,
- user = U})
- end;
- {ok, Props, ServerOut} ->
- (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- 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]),
- case need_redirect(StateData#state{user = U}) of
- {true, Host} ->
- ?INFO_MSG("(~w) Redirecting ~s to ~s",
- [StateData#state.socket, U, Host]),
- send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
- send_trailer(StateData),
- {stop, normal, StateData};
- false ->
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}],
- [{xmlcdata,
- jlib:encode_base64(ServerOut)}]}),
- fsm_next_state(wait_for_stream,
- StateData#state{
- streamid = new_id(),
- authenticated = true,
- auth_module = AuthModule,
- user = U})
- end;
- {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} ->
- ?INFO_MSG(
- "(~w) Failed authentication for ~s@~s",
- [StateData#state.socket,
- Username, StateData#state.server]),
- 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)
+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]),
+ case need_redirect(StateData#state{user = U}) of
+ {true, Host} ->
+ ?INFO_MSG("(~w) Redirecting ~s to ~s",
+ [StateData#state.socket, U, Host]),
+ send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ false ->
+ 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})
+ end;
+ {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]),
+ case need_redirect(StateData#state{user = U}) of
+ {true, Host} ->
+ ?INFO_MSG("(~w) Redirecting ~s to ~s",
+ [StateData#state.socket, U, Host]),
+ send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ false ->
+ 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})
+ end;
+ {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} ->
+ ?INFO_MSG("(~w) Failed authentication for ~s@~s",
+ [StateData#state.socket, Username,
+ StateData#state.server]),
+ 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} ->
- U = StateData#state.user,
- %%R = StateData#state.resource,
- JID = StateData#state.jid,
- case acl:match_rule(StateData#state.server,
- StateData#state.access, JID) of
- allow ->
- ?INFO_MSG("(~w) Opened session for ~s",
- [StateData#state.socket,
- jlib:jid_to_string(JID)]),
- Res = jlib:make_result_iq_reply(El),
- send_element(StateData, Res),
- 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]),
- SID = {now(), self()},
- Conn = get_conn_type(StateData),
- %% Info = [{ip, StateData#state.ip}, {conn, Conn},
- %% {auth_module, StateData#state.auth_module}],
- %% ejabberd_sm:open_session(
- %% SID, U, StateData#state.server, R, Info),
- NewStateData =
- StateData#state{
- sid = SID,
- conn = Conn,
- pres_f = ?SETS:from_list(Fs1),
- pres_t = ?SETS:from_list(Ts1),
- privacy_list = PrivList},
- DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook,
- NewStateData#state.server,
- false,
- [self(), NewStateData]),
- maybe_migrate(session_established, NewStateData#state{debug=DebugFlag});
- _ ->
- ejabberd_hooks:run(forbidden_session_hook,
- StateData#state.server, [JID]),
- ?INFO_MSG("(~w) Forbidden session 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_session, StateData)
- end;
- _ ->
- fsm_next_state(wait_for_session, StateData)
+ #iq{type = set, xmlns = ?NS_SESSION} ->
+ U = StateData#state.user,
+ JID = StateData#state.jid,
+ case acl:match_rule(StateData#state.server,
+ StateData#state.access, JID)
+ of
+ allow ->
+ ?INFO_MSG("(~w) Opened session for ~s",
+ [StateData#state.socket, jlib:jid_to_string(JID)]),
+ Res = jlib:make_result_iq_reply(El),
+ send_element(StateData, Res),
+ 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]),
+ SID = {now(), self()},
+ Conn = (StateData#state.sockmod):get_conn_type(
+ StateData#state.socket),
+ NewStateData = StateData#state{sid = SID, conn = Conn,
+ pres_f = (?SETS):from_list(Fs1),
+ pres_t = (?SETS):from_list(Ts1),
+ privacy_list = PrivList},
+ DebugFlag =
+ ejabberd_hooks:run_fold(c2s_debug_start_hook,
+ NewStateData#state.server, false,
+ [self(), NewStateData]),
+ maybe_migrate(session_established,
+ NewStateData#state{debug = DebugFlag});
+ _ ->
+ ejabberd_hooks:run(forbidden_session_hook,
+ StateData#state.server, [JID]),
+ ?INFO_MSG("(~w) Forbidden session 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_session, StateData)
+ end;
+ _ -> fsm_next_state(wait_for_session, StateData)
end;
-
wait_for_session(open_session, StateData) ->
- El = {xmlelement, "iq", [{"type", "set"}, {"id", "session"}],
- [{xmlelement, "session", [{"xmlns", ?NS_SESSION}], []}]},
+ El = #xmlel{name = <<"iq">>,
+ attrs =
+ [{<<"type">>, <<"set">>}, {<<"id">>, <<"session">>}],
+ children =
+ [#xmlel{name = <<"session">>,
+ attrs = [{<<"xmlns">>, ?NS_SESSION}],
+ children = []}]},
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),
- send_trailer(StateData),
- {stop, normal, StateData};
- _NewEl ->
- NSD1 = change_reception(StateData, true),
- NSD2 = start_keepalive_timer(NSD1),
- session_established2(El, NSD2)
+ 'invalid-from' ->
+ send_element(StateData, ?INVALID_FROM),
+ send_trailer(StateData),
+ {stop, normal, StateData};
+ _NewEl ->
+ NSD1 = change_reception(StateData, true),
+ NSD2 = start_keepalive_timer(NSD1),
+ session_established2(El, NSD2)
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) ->
- if
- not StateData#state.reception ->
- fsm_next_state(session_established, StateData);
- (StateData#state.keepalive_timer /= undefined) ->
- NewState1 = change_reception(StateData, false),
- NewState = start_keepalive_timer(NewState1),
- fsm_next_state(session_established, NewState);
- true ->
- {stop, normal, StateData}
+ if not StateData#state.reception ->
+ fsm_next_state(session_established, StateData);
+ StateData#state.keepalive_timer /= undefined ->
+ NewState1 = change_reception(StateData, false),
+ NewState = start_keepalive_timer(NewState1),
+ fsm_next_state(session_established, NewState);
+ true -> {stop, normal, StateData}
end.
-%% 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,
- [StateData#state.debug, 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 ->
- ejabberd_hooks:run(
- user_send_packet,
- Server,
- [StateData#state.debug, FromJID, ToJID, NewEl]),
- process_privacy_iq(
- FromJID, ToJID, IQ, StateData);
- #iq{xmlns = ?NS_P1_PUSH} = IQ ->
- process_push_iq(FromJID, ToJID, IQ, StateData);
- _ ->
- ejabberd_hooks:run(
- user_send_packet,
- Server,
- [StateData#state.debug, FromJID, ToJID, NewEl]),
- check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),
- StateData
- end;
- "message" ->
- ejabberd_hooks:run(user_send_packet,
- Server,
- [StateData#state.debug, FromJID, ToJID, NewEl]),
- check_privacy_route(FromJID, StateData, FromJID,
- ToJID, NewEl),
- StateData;
- "standby" ->
- StandBy = xml:get_tag_cdata(NewEl) == "true",
- change_standby(StateData, StandBy);
- "a" ->
- SCounter = xml:get_tag_attr_s("h", NewEl),
- receive_ack(StateData, SCounter);
- _ ->
- 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,
+ [StateData#state.debug, 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) ->
+ ejabberd_hooks:run(user_send_packet, Server,
+ [StateData#state.debug,
+ FromJID, ToJID, NewEl]),
+ process_privacy_iq(FromJID, ToJID, IQ,
+ StateData);
+ #iq{xmlns = ?NS_P1_PUSH} = IQ ->
+ process_push_iq(FromJID, ToJID, IQ, StateData);
+ _ ->
+ ejabberd_hooks:run(user_send_packet, Server,
+ [StateData#state.debug,
+ FromJID, ToJID, NewEl]),
+ check_privacy_route(FromJID, StateData,
+ FromJID, ToJID, NewEl),
+ StateData
+ end;
+ <<"message">> ->
+ ejabberd_hooks:run(user_send_packet, Server,
+ [StateData#state.debug, FromJID,
+ ToJID, NewEl]),
+ check_privacy_route(FromJID, StateData, FromJID,
+ ToJID, NewEl),
+ StateData;
+ <<"standby">> ->
+ StandBy = xml:get_tag_cdata(NewEl) == <<"true">>,
+ change_standby(StateData, StandBy);
+ <<"a">> ->
+ SCounter = xml:get_tag_attr_s(<<"h">>, NewEl),
+ receive_ack(StateData, SCounter);
+ _ -> 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} |
@@ -1343,66 +1373,44 @@ session_established2(El, StateData) ->
% Reply = ok,
% {reply, Reply, state_name, StateData}.
-%%----------------------------------------------------------------------
-%% Func: handle_event/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-handle_event({add_rosteritem, IJID, ISubscription}, StateName, StateData) ->
- NewStateData = roster_change(IJID, ISubscription, StateData),
+handle_event({add_rosteritem, IJID, ISubscription},
+ StateName, StateData) ->
+ NewStateData = roster_change(IJID, ISubscription,
+ StateData),
fsm_next_state(StateName, NewStateData);
-
-handle_event({del_rosteritem, IJID}, StateName, StateData) ->
+handle_event({del_rosteritem, IJID}, StateName,
+ StateData) ->
NewStateData = roster_change(IJID, none, StateData),
fsm_next_state(StateName, NewStateData);
-
-handle_event({xmlstreamcdata, _}, session_established = StateName, StateData) ->
+handle_event({xmlstreamcdata, _},
+ session_established = StateName, StateData) ->
?DEBUG("cdata ping", []),
NSD1 = change_reception(StateData, true),
NSD2 = start_keepalive_timer(NSD1),
fsm_next_state(StateName, NSD2);
-
handle_event(_Event, StateName, StateData) ->
fsm_next_state(StateName, StateData).
-%%----------------------------------------------------------------------
-%% Func: handle_sync_event/4
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {reply, Reply, NextStateName, NextStateData} |
-%% {reply, Reply, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData} |
-%% {stop, Reason, Reply, NewStateData}
-%%----------------------------------------------------------------------
-handle_sync_event({get_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}.
-%%----------------------------------------------------------------------
-%% Func: handle_info/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text),
ejabberd_hooks:run(c2s_loop_debug, [Text]),
@@ -1410,514 +1418,580 @@ 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);
+ {rebind, Pid2, StreamID2} ->
+ if StreamID2 == StateData#state.streamid ->
+ Pid2 ! {rebind, prepare_acks_for_rebind(StateData)},
+ receive after 1000 -> ok end,
+ catch send_trailer(StateData),
+ {stop, normal, StateData#state{authenticated = rebinded}};
+ true ->
+ Pid2 ! {rebind, false},
+ fsm_next_state(StateName, StateData)
+ end;
+ _ ->
+ 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;
- rebind ->
- {Pid2, StreamID2} = Els,
- if
- StreamID2 == StateData#state.streamid ->
- Pid2 ! {rebind, prepare_acks_for_rebind(StateData)},
- receive after 1000 -> ok end,
- {exit, Attrs, rebind};
- true ->
- Pid2 ! {rebind, false},
- {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 ->
- if StateData#state.reception ->
- case ejabberd_hooks:run_fold(
- feature_check_packet, StateData#state.server,
- allow,
- [StateData#state.jid,
- StateData#state.server,
- StateData#state.pres_last,
- {From, To, Packet},
- in]) of
- allow ->
- {true, Attrs, StateData};
- deny ->
- {false, Attrs, StateData}
- end;
- true ->
- {true, Attrs, StateData}
- end;
- deny ->
- {false, Attrs, StateData}
- end;
- _ ->
- {true, Attrs, StateData}
- end,
- if
- Pass == exit ->
- catch send_trailer(StateData),
- case NewState of
- rebind ->
- {stop, normal, StateData#state{authenticated = rebinded}};
- _ ->
- {stop, normal, StateData}
- end;
- Pass ->
- Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
- jlib:jid_to_string(To),
- NewAttrs),
- FixedPacket = {xmlelement, Name, Attrs2, Els},
- NewState2 =
- if
- NewState#state.reception and
- not (NewState#state.standby and (Name /= "message")) ->
- send_element(NewState, FixedPacket),
- ack(NewState, From, To, FixedPacket);
- true ->
- NewState1 = send_out_of_reception_message(
- NewState, From, To, Packet),
- enqueue(NewState1, From, To, FixedPacket)
- end,
- ejabberd_hooks:run(user_receive_packet,
- StateData#state.server,
- [StateData#state.debug, StateData#state.jid, From, To, FixedPacket]),
- ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
- fsm_next_state(StateName, NewState2);
- true ->
- ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
- fsm_next_state(StateName, NewState)
+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 ->
+ if StateData#state.reception ->
+ case
+ ejabberd_hooks:run_fold(feature_check_packet,
+ StateData#state.server,
+ allow,
+ [StateData#state.jid,
+ StateData#state.server,
+ StateData#state.pres_last,
+ {From,
+ To,
+ Packet},
+ in])
+ of
+ allow ->
+ {true, Attrs,
+ StateData};
+ deny ->
+ {false, Attrs,
+ StateData}
+ end;
+ true -> {true, Attrs, StateData}
+ end;
+ deny -> {false, Attrs, StateData}
+ end;
+ _ -> {true, Attrs, StateData}
+ end,
+ if Pass ->
+ 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},
+ NewState2 = if NewState#state.reception and
+ not
+ (NewState#state.standby and
+ (Name /= "message")) ->
+ send_element(NewState, FixedPacket),
+ ack(NewState, From, To, FixedPacket);
+ true ->
+ NewState1 =
+ send_out_of_reception_message(NewState, From,
+ To, Packet),
+ enqueue(NewState1, From, To, FixedPacket)
+ end,
+ ejabberd_hooks:run(user_receive_packet,
+ StateData#state.server,
+ [StateData#state.debug, StateData#state.jid, From,
+ To, FixedPacket]),
+ ejabberd_hooks:run(c2s_loop_debug,
+ [{route, From, To, Packet}]),
+ fsm_next_state(StateName, NewState2);
+ true ->
+ ejabberd_hooks:run(c2s_loop_debug,
+ [{route, From, To, Packet}]),
+ fsm_next_state(StateName, NewState)
end;
handle_info({timeout, Timer, _}, StateName,
- #state{keepalive_timer = Timer, reception = true} = StateData) ->
+ #state{keepalive_timer = Timer, reception = true} =
+ StateData) ->
NewState1 = change_reception(StateData, false),
NewState = start_keepalive_timer(NewState1),
fsm_next_state(StateName, NewState);
handle_info({timeout, Timer, _}, _StateName,
- #state{keepalive_timer = Timer, reception = false} = StateData) ->
+ #state{keepalive_timer = Timer, reception = false} =
+ StateData) ->
{stop, normal, StateData};
handle_info({timeout, Timer, PrevCounter}, StateName,
#state{ack_timer = Timer} = StateData) ->
AckCounter = StateData#state.ack_counter,
- NewState =
- if
- PrevCounter >= AckCounter ->
- StateData#state{ack_timer = undefined};
- true ->
- send_ack_request(StateData#state{ack_timer = undefined})
- end,
+ NewState = if PrevCounter >= AckCounter ->
+ StateData#state{ack_timer = undefined};
+ true ->
+ send_ack_request(StateData#state{ack_timer = undefined})
+ end,
fsm_next_state(StateName, NewState);
-handle_info({ack_timeout, Counter}, StateName, StateData) ->
+handle_info({ack_timeout, Counter}, StateName,
+ StateData) ->
AckQueue = StateData#state.ack_queue,
case queue:is_empty(AckQueue) of
- true ->
- fsm_next_state(StateName, StateData);
- false ->
- C = element(1, queue:head(AckQueue)),
- if
- C =< Counter ->
- {stop, normal, StateData};
- true ->
- fsm_next_state(StateName, StateData)
- end
+ true -> fsm_next_state(StateName, StateData);
+ false ->
+ C = element(1, queue:head(AckQueue)),
+ if C =< Counter -> {stop, normal, StateData};
+ true -> fsm_next_state(StateName, StateData)
+ end
end;
handle_info(open_timeout, StateName, StateData) ->
case StateName of
- session_established ->
- fsm_next_state(StateName, StateData);
- _ ->
- {stop, normal, StateData}
+ session_established ->
+ fsm_next_state(StateName, StateData);
+ _ -> {stop, normal, StateData}
end;
-handle_info({'DOWN', Monitor, _Type, _Object, _Info}, StateName, StateData)
- when Monitor == StateData#state.socket_monitor ->
- if
- (StateName == session_established) and
- (not StateData#state.reception) ->
- fsm_next_state(StateName, StateData);
- (StateName == session_established) and
- (StateData#state.keepalive_timer /= undefined) ->
- NewState1 = change_reception(StateData, false),
- NewState = start_keepalive_timer(NewState1),
- fsm_next_state(StateName, NewState);
- true ->
- {stop, normal, StateData}
+handle_info({'DOWN', Monitor, _Type, _Object, _Info},
+ StateName, StateData)
+ when Monitor == StateData#state.socket_monitor ->
+ if (StateName == session_established) and
+ not StateData#state.reception ->
+ fsm_next_state(StateName, StateData);
+ (StateName == session_established) and
+ (StateData#state.keepalive_timer /= undefined) ->
+ NewState1 = change_reception(StateData, false),
+ NewState = start_keepalive_timer(NewState1),
+ fsm_next_state(StateName, NewState);
+ true -> {stop, normal, StateData}
end;
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({migrate, Node}, StateName, StateData) ->
if Node /= node() ->
- fsm_migrate(StateName, StateData, Node, 0);
- true ->
- fsm_next_state(StateName, StateData)
+ fsm_migrate(StateName, StateData, Node, 0);
+ true -> fsm_next_state(StateName, StateData)
end;
-handle_info({migrate_shutdown, Node, After}, StateName, StateData) ->
- case StateData#state.sockmod == ejabberd_frontend_socket orelse
- StateData#state.xml_socket == true orelse
- is_remote_receiver(StateData#state.socket) of
- true ->
- migrate(self(), Node, After);
- false ->
- self() ! system_shutdown
+handle_info({migrate_shutdown, Node, After}, StateName,
+ StateData) ->
+ case StateData#state.sockmod == ejabberd_frontend_socket
+ orelse
+ StateData#state.xml_socket == true orelse
+ (StateData#state.sockmod):is_remote_receiver(
+ StateData#state.socket)
+ of
+ true -> migrate(self(), Node, After);
+ false -> self() ! system_shutdown
end,
fsm_next_state(StateName, StateData);
-handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
- Recipients = ejabberd_hooks:run_fold(
- c2s_broadcast_recipients, StateData#state.server,
- [],
- [StateData, Type, From, Packet]),
- lists:foreach(
- fun(USR) ->
- ejabberd_router:route(
- From, jlib:make_jid(USR), Packet)
- end, lists:usort(Recipients)),
+handle_info({broadcast, Type, From, Packet}, StateName,
+ StateData) ->
+ Recipients =
+ ejabberd_hooks:run_fold(c2s_broadcast_recipients,
+ StateData#state.server, [],
+ [StateData#state.server, StateData,
+ Type, From, Packet]),
+ lists:foreach(fun (USR) ->
+ ejabberd_router:route(From, jlib:make_jid(USR),
+ Packet)
+ end,
+ lists:usort(Recipients)),
fsm_next_state(StateName, StateData);
-handle_info({change_socket, Socket}, StateName, StateData) ->
+handle_info({change_socket, Socket}, StateName,
+ StateData) ->
erlang:demonitor(StateData#state.socket_monitor),
- NewSocket = (StateData#state.sockmod):change_socket(
- StateData#state.socket, Socket),
+ NewSocket =
+ (StateData#state.sockmod):change_socket(StateData#state.socket,
+ Socket),
MRef = (StateData#state.sockmod):monitor(NewSocket),
fsm_next_state(StateName,
- StateData#state{socket = NewSocket,
- socket_monitor = MRef});
+ StateData#state{socket = NewSocket,
+ socket_monitor = MRef});
handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
fsm_next_state(StateName, StateData).
+print_state(State = #state{pres_t = T, pres_f = F,
+ pres_a = A}) ->
+ State#state{pres_t = {pres_t, (?SETS):size(T)},
+ pres_f = {pres_f, (?SETS):size(F)},
+ pres_a = {pres_a, (?SETS):size(A)}}.
-%%----------------------------------------------------------------------
-%% Func: print_state/1
-%% Purpose: Prepare the state to be printed on error log
-%% Returns: State to print
-%%----------------------------------------------------------------------
-print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
- State#state{pres_t = {pres_t, ?SETS:size(T)},
- pres_f = {pres_f, ?SETS:size(F)},
- pres_a = {pres_a, ?SETS:size(A)},
- pres_i = {pres_i, ?SETS:size(I)}
- }.
-
-%%----------------------------------------------------------------------
-%% Func: terminate/3
-%% Purpose: Shutdown the fsm
-%% Returns: any
-%%----------------------------------------------------------------------
terminate({migrated, ClonePid}, StateName, StateData) ->
ejabberd_hooks:run(c2s_debug_stop_hook,
- StateData#state.server,
- [self(), StateData]),
+ StateData#state.server, [self(), StateData]),
if StateName == session_established ->
- ?INFO_MSG("(~w) Migrating ~s to ~p on node ~p",
- [StateData#state.socket,
- jlib:jid_to_string(StateData#state.jid),
- ClonePid, node(ClonePid)]),
- ejabberd_sm:close_migrated_session(StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource);
- true ->
- ok
+ ?INFO_MSG("(~w) Migrating ~s to ~p on node ~p",
+ [StateData#state.socket,
+ jlib:jid_to_string(StateData#state.jid), ClonePid,
+ node(ClonePid)]),
+ ejabberd_sm:close_migrated_session(StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource);
+ true -> ok
end,
- (StateData#state.sockmod):change_controller(
- StateData#state.socket, ClonePid),
+ (StateData#state.sockmod):change_controller(StateData#state.socket,
+ ClonePid),
ok;
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);
- rebinded ->
- ejabberd_sm:close_migrated_session(
- StateData#state.sid,
- StateData#state.user,
- StateData#state.server,
- StateData#state.resource),
- ok;
- _ ->
- ?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,
- case StateData#state.authenticated of
- rebinded ->
- ok;
- _ ->
- if
- not StateData#state.reception, not StateData#state.oor_offline ->
- SFrom = jlib:jid_to_string(StateData#state.jid),
- ejabberd_hooks:run(
- p1_push_notification,
- StateData#state.server,
- [StateData#state.server,
- StateData#state.jid,
- StateData#state.oor_notification,
- "Instant messaging session expired",
- 0,
- false,
- StateData#state.oor_appid,
- SFrom]);
- true ->
- ok
- end,
- lists:foreach(
- fun({_Counter, From, To, FixedPacket}) ->
- ejabberd_router:route(From, To, FixedPacket)
- end, queue:to_list(StateData#state.ack_queue)),
- lists:foreach(
- fun({From, To, FixedPacket}) ->
- ejabberd_router:route(From, To, FixedPacket)
- end, queue:to_list(StateData#state.queue))
- 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);
+ rebinded ->
+ ejabberd_sm:close_migrated_session(StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource),
+ ok;
+ _ ->
+ ?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} ->
+ 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)
+ end
+ end,
+ case StateData#state.authenticated of
+ rebinded -> ok;
+ _ ->
+ if not StateData#state.reception,
+ not StateData#state.oor_offline ->
+ SFrom = jlib:jid_to_string(StateData#state.jid),
+ ejabberd_hooks:run(p1_push_notification,
+ StateData#state.server,
+ [StateData#state.server,
+ StateData#state.jid,
+ StateData#state.oor_notification,
+ <<"Instant messaging session expired">>,
+ 0, false, StateData#state.oor_appid,
+ SFrom]);
+ true -> ok
+ end,
+ lists:foreach(fun ({_Counter, From, To, FixedPacket}) ->
+ ejabberd_router:route(From, To,
+ FixedPacket)
+ end,
+ queue:to_list(StateData#state.ack_queue)),
+ lists:foreach(fun ({From, To, FixedPacket}) ->
+ ejabberd_router:route(From, To,
+ FixedPacket)
+ end,
+ queue:to_list(StateData#state.queue))
+ end,
+ bounce_messages();
+ _ -> ok
end,
(StateData#state.sockmod):close(StateData#state.socket),
ok.
@@ -1929,1059 +2003,854 @@ 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).
+
+-spec send_text(c2s_state(), binary()) -> any().
-send_text(StateData, Text) when StateData#state.xml_socket ->
- ?DEBUG("Send Text on stream = ~p", [lists:flatten(Text)]),
+send_text(StateData, Text)
+ when StateData#state.xml_socket ->
+ ?DEBUG("Send Text on stream = ~p", [Text]),
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamraw, Text});
send_text(StateData, Text) ->
?DEBUG("Send XML on stream = ~p", [Text]),
- Text1 =
- if ?FLASH_HACK and StateData#state.flash_connection ->
- %% send a null byte after each stanza to Flash clients
- [Text, 0];
- true ->
- Text
- end,
- (StateData#state.sockmod):send(StateData#state.socket, Text1).
-
-send_element(StateData, El) when StateData#state.xml_socket ->
+ Text1 = if StateData#state.flash_hack and
+ StateData#state.flash_connection ->
+ <<Text/binary, 0>>;
+ true -> Text
+ end,
+ (StateData#state.sockmod):send(StateData#state.socket,
+ Text1).
+
+-spec send_element(c2s_state(), xmlel()) -> any().
+
+send_element(StateData, El)
+ when StateData#state.xml_socket ->
ejabberd_hooks:run(feature_inspect_packet,
- StateData#state.server,
- [StateData#state.jid,
- StateData#state.server,
- StateData#state.pres_last, El]),
+ StateData#state.server,
+ [StateData#state.jid, StateData#state.server,
+ StateData#state.pres_last, El]),
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamelement, El});
send_element(StateData, El) ->
ejabberd_hooks:run(feature_inspect_packet,
- StateData#state.server,
- [StateData#state.jid,
- StateData#state.server,
- StateData#state.pres_last, El]),
+ StateData#state.server,
+ [StateData#state.jid, StateData#state.server,
+ StateData#state.pres_last, El]),
send_text(StateData, xml:element_to_binary(El)).
-send_header(StateData,Server, Version, Lang)
- when StateData#state.flash_connection ->
+send_header(StateData, Server, Version, Lang)
+ when StateData#state.flash_connection ->
Header = io_lib:format(?FLASH_STREAM_HEADER,
- [StateData#state.streamid,
- Server,
- Version,
- Lang]),
- send_text(StateData, Header);
-
+ [StateData#state.streamid, Server, Version, Lang]),
+ send_text(StateData, iolist_to_binary(Header));
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);
-get_auth_tags([], U, P, D, R) ->
- {U, P, D, R}.
-
-%% Copied from ejabberd_socket.erl
--record(socket_state, {sockmod, socket, receiver}).
-
-get_conn_type(StateData) ->
- case (StateData#state.sockmod):get_sockmod(StateData#state.socket) of
- gen_tcp -> c2s;
- tls -> c2s_tls;
- ejabberd_zlib ->
- if is_pid(StateData#state.socket) ->
- unknown;
- true ->
- case ejabberd_zlib:get_sockmod(
- (StateData#state.socket)#socket_state.socket) of
- gen_tcp -> c2s_compressed;
- tls -> c2s_compressed_tls
- end
- end;
- ejabberd_http_poll -> http_poll;
- ejabberd_http_ws -> http_ws;
- ejabberd_http_bind -> http_bind;
- _ -> unknown
- end.
+get_auth_tags([], U, P, D, R) -> {U, P, D, R}.
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;
- _ ->
- Cond1 = (not StateData#state.pres_invis)
- andalso (?SETS:is_element(LFrom, StateData#state.pres_f)
- orelse
- ((LFrom /= LBFrom) andalso
- ?SETS:is_element(LBFrom, StateData#state.pres_f)))
- andalso (not
- (?SETS:is_element(LFrom, StateData#state.pres_i)
- orelse
- ((LFrom /= LBFrom) andalso
- ?SETS:is_element(LBFrom, StateData#state.pres_i)))),
- Cond2 = StateData#state.pres_invis
- andalso ?SETS:is_element(LFrom, StateData#state.pres_f)
- andalso ?SETS:is_element(LFrom, StateData#state.pres_a),
- if
- Cond1 ->
- Packet =
- case StateData#state.reception of
- true ->
- StateData#state.pres_last;
+ undefined -> ok;
+ _ ->
+ Cond1 =
+ ((?SETS):is_element(LFrom, StateData#state.pres_f)
+ orelse
+ LFrom /= LBFrom andalso
+ (?SETS):is_element(LBFrom, StateData#state.pres_f)),
+ if Cond1 ->
+ Packet = case StateData#state.reception of
+ true -> StateData#state.pres_last;
false ->
case StateData#state.oor_show of
- "" ->
- StateData#state.pres_last;
- _ ->
- {xmlelement, _, PresAttrs, PresEls} =
- StateData#state.pres_last,
- PresEls1 =
- lists:flatmap(
- fun({xmlelement, Name, _, _})
- when Name == "show";
- Name == "status" ->
- [];
- (E) ->
- [E]
- end, PresEls),
- make_oor_presence(
- StateData, PresAttrs, PresEls1)
+ <<"">> -> StateData#state.pres_last;
+ _ ->
+ #xmlel{attrs = PresAttrs,
+ children = PresEls} =
+ StateData#state.pres_last,
+ PresEls1 = lists:flatmap(fun (#xmlel{name
+ =
+ Name})
+ when Name
+ ==
+ <<"show">>;
+ Name
+ ==
+ <<"status">> ->
+ [];
+ (E) -> [E]
+ end,
+ PresEls),
+ make_oor_presence(StateData, PresAttrs,
+ PresEls1)
end
- end,
- Timestamp = StateData#state.pres_timestamp,
- Packet1 = maybe_add_delay(Packet, utc, To, "", Timestamp),
- case ejabberd_hooks:run_fold(
- privacy_check_packet, StateData#state.server,
- allow,
- [StateData#state.user,
- StateData#state.server,
- StateData#state.privacy_list,
- {To, From, Packet1},
- out]) of
- deny ->
- ok;
- allow ->
- Pid=element(2, StateData#state.sid),
- ejabberd_hooks:run(presence_probe_hook, StateData#state.server, [From, To, Pid]),
- %% Don't route a presence probe to oneself
- case From == To of
- false ->
- ejabberd_router:route(To, From, Packet1);
- true ->
- ok
- end
- end;
- Cond2 ->
- ejabberd_router:route(To, From,
- {xmlelement, "presence",
- [],
- []});
- true ->
- ok
- end
+ end,
+ Timestamp = StateData#state.pres_timestamp,
+ Packet1 = maybe_add_delay(Packet, utc, To, <<"">>,
+ Timestamp),
+ case ejabberd_hooks:run_fold(privacy_check_packet,
+ StateData#state.server, allow,
+ [StateData#state.user,
+ StateData#state.server,
+ StateData#state.privacy_list,
+ {To, From, Packet1}, out])
+ of
+ deny -> ok;
+ allow ->
+ Pid = element(2, StateData#state.sid),
+ ejabberd_hooks:run(presence_probe_hook,
+ StateData#state.server,
+ [From, To, Pid]),
+ case From == To of
+ false -> ejabberd_router:route(To, From, Packet1);
+ true -> ok
+ end
+ end;
+ true -> ok
+ end
end.
-%% 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
+ #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,
- 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
+ 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),
-% To = 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) ->
- Info1 = [{ip, StateData#state.ip}, {conn, StateData#state.conn},
+ Info1 = [{ip, StateData#state.ip},
+ {conn, StateData#state.conn},
{auth_module, StateData#state.auth_module}],
- Info =
- case StateData#state.reception of
- false ->
- [{oor, true} | Info1];
- _ ->
- Info1
- end,
+ Info = case StateData#state.reception of
+ false -> [{oor, true} | Info1];
+ _ -> Info1
+ end,
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.
maybe_migrate(StateName, StateData) ->
PackedStateData = pack(StateData),
- #state{user = U, server = S, resource = R, sid = SID} = StateData,
- case ejabberd_cluster:get_node({jlib:nodeprep(U), jlib:nameprep(S)}) of
- Node when Node == node() ->
- Conn = get_conn_type(StateData),
- Info = [{ip, StateData#state.ip}, {conn, Conn},
- {auth_module, StateData#state.auth_module}],
- Presence = StateData#state.pres_last,
- Priority =
- case Presence of
- undefined ->
- undefined;
- _ ->
- get_priority_from_presence(Presence)
- end,
- ejabberd_sm:open_session(SID, U, S, R, Priority, Info),
- StateData2 = change_reception(PackedStateData, true),
- StateData3 = start_keepalive_timer(StateData2),
- erlang:garbage_collect(),
- fsm_next_state(StateName, StateData3);
- Node ->
- fsm_migrate(StateName, PackedStateData, Node, 0)
+ #state{user = U, server = S, resource = R, sid = SID} =
+ StateData,
+ case ejabberd_cluster:get_node({jlib:nodeprep(U),
+ jlib:nameprep(S)})
+ of
+ Node when Node == node() ->
+ Conn = ejabberd_socket:get_conn_type(StateData#state.socket),
+ Info = [{ip, StateData#state.ip}, {conn, Conn},
+ {auth_module, StateData#state.auth_module}],
+ Presence = StateData#state.pres_last,
+ Priority = case Presence of
+ undefined -> undefined;
+ _ -> get_priority_from_presence(Presence)
+ end,
+ ejabberd_sm:open_session(SID, U, S, R, Priority, Info),
+ StateData2 = change_reception(PackedStateData, true),
+ StateData3 = start_keepalive_timer(StateData2),
+ erlang:garbage_collect(),
+ fsm_next_state(StateName, StateData3);
+ Node -> fsm_migrate(StateName, PackedStateData, Node, 0)
end.
-%% 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_migrate(StateName, StateData, Node, Timeout) ->
{migrate, StateData,
- {Node, ?MODULE, start, [StateName, StateData]}, Timeout}.
+ {Node, ?MODULE, start, [StateName, StateData]},
+ 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.
start_keepalive_timer(StateData) ->
- if
- is_reference(StateData#state.keepalive_timer) ->
- cancel_timer(StateData#state.keepalive_timer);
- true ->
- ok
+ if is_reference(StateData#state.keepalive_timer) ->
+ cancel_timer(StateData#state.keepalive_timer);
+ true -> ok
end,
- Timeout =
- if
- StateData#state.reception -> StateData#state.keepalive_timeout;
- true -> StateData#state.oor_timeout
- end,
- Timer =
- if
- is_integer(Timeout) ->
- erlang:start_timer(Timeout * 1000, self(), []);
- true ->
- undefined
- end,
+ Timeout = if StateData#state.reception ->
+ StateData#state.keepalive_timeout;
+ true -> StateData#state.oor_timeout
+ end,
+ Timer = if is_integer(Timeout) ->
+ erlang:start_timer(Timeout * 1000, self(), []);
+ true -> undefined
+ end,
StateData#state{keepalive_timer = Timer}.
-change_reception(#state{reception = Reception} = StateData, Reception) ->
+change_reception(#state{reception = Reception} =
+ StateData,
+ Reception) ->
StateData;
-change_reception(#state{reception = true} = StateData, false) ->
+change_reception(#state{reception = true} = StateData,
+ false) ->
?DEBUG("reception -> false", []),
case StateData#state.oor_show of
- "" ->
- ok;
- _ ->
- Packet = make_oor_presence(StateData),
- update_priority(0, Packet, StateData#state{reception = false}),
- presence_broadcast_to_trusted(
- StateData,
- StateData#state.jid,
- StateData#state.pres_f,
- StateData#state.pres_a,
- Packet)
+ <<"">> -> ok;
+ _ ->
+ Packet = make_oor_presence(StateData),
+ update_priority(0, Packet,
+ StateData#state{reception = false}),
+ presence_broadcast_to_trusted(StateData,
+ StateData#state.jid,
+ StateData#state.pres_f,
+ StateData#state.pres_a, Packet)
end,
StateData#state{reception = false};
-change_reception(#state{reception = false, standby = true} = StateData, true) ->
+change_reception(#state{reception = false,
+ standby = true} =
+ StateData,
+ true) ->
?DEBUG("reception -> standby", []),
- NewQueue =
- lists:foldl(
- fun({_From, _To, {xmlelement, "message", _, _} = FixedPacket}, Q) ->
- send_element(StateData, FixedPacket),
- Q;
- (Item, Q) ->
- queue:in(Item, Q)
- end, queue:new(), queue:to_list(StateData#state.queue)),
+ NewQueue = lists:foldl(fun ({_From, _To,
+ #xmlel{name = <<"message">>} = FixedPacket},
+ Q) ->
+ send_element(StateData, FixedPacket), Q;
+ (Item, Q) -> queue:in(Item, Q)
+ end,
+ queue:new(), queue:to_list(StateData#state.queue)),
StateData#state{queue = NewQueue,
- queue_len = queue:len(NewQueue),
- reception = true,
- oor_unread = 0,
- oor_unread_users = ?SETS:new()};
-change_reception(#state{reception = false} = StateData, true) ->
+ queue_len = queue:len(NewQueue), reception = true,
+ oor_unread = 0, oor_unread_users = (?SETS):new()};
+change_reception(#state{reception = false} = StateData,
+ true) ->
?DEBUG("reception -> true", []),
case StateData#state.oor_show of
- "" ->
- ok;
- _ ->
- Packet = StateData#state.pres_last,
- NewPriority = get_priority_from_presence(Packet),
- update_priority(NewPriority, Packet,
- StateData#state{reception = true}),
- presence_broadcast_to_trusted(
- StateData,
- StateData#state.jid,
- StateData#state.pres_f,
- StateData#state.pres_a,
- Packet)
+ <<"">> -> ok;
+ _ ->
+ Packet = StateData#state.pres_last,
+ NewPriority = get_priority_from_presence(Packet),
+ update_priority(NewPriority, Packet,
+ StateData#state{reception = true}),
+ presence_broadcast_to_trusted(StateData,
+ StateData#state.jid,
+ StateData#state.pres_f,
+ StateData#state.pres_a, Packet)
end,
- lists:foreach(
- fun({_From, _To, FixedPacket}) ->
- send_element(StateData, FixedPacket)
- end, queue:to_list(StateData#state.queue)),
- lists:foreach(
- fun(FixedPacket) ->
- send_element(StateData, FixedPacket)
- end, gb_trees:values(StateData#state.pres_queue)),
- StateData#state{queue = queue:new(),
- queue_len = 0,
- pres_queue = gb_trees:empty(),
- reception = true,
- oor_unread = 0,
- oor_unread_users = ?SETS:new()}.
-
-change_standby(#state{standby = StandBy} = StateData, StandBy) ->
+ lists:foreach(fun ({_From, _To, FixedPacket}) ->
+ send_element(StateData, FixedPacket)
+ end,
+ queue:to_list(StateData#state.queue)),
+ lists:foreach(fun (FixedPacket) ->
+ send_element(StateData, FixedPacket)
+ end,
+ gb_trees:values(StateData#state.pres_queue)),
+ StateData#state{queue = queue:new(), queue_len = 0,
+ pres_queue = gb_trees:empty(), reception = true,
+ oor_unread = 0, oor_unread_users = (?SETS):new()}.
+
+change_standby(#state{standby = StandBy} = StateData,
+ StandBy) ->
StateData;
-change_standby(#state{standby = false} = StateData, true) ->
+change_standby(#state{standby = false} = StateData,
+ true) ->
?DEBUG("standby -> true", []),
StateData#state{standby = true};
-change_standby(#state{standby = true} = StateData, false) ->
+change_standby(#state{standby = true} = StateData,
+ false) ->
?DEBUG("standby -> false", []),
- lists:foreach(
- fun({_From, _To, FixedPacket}) ->
- send_element(StateData, FixedPacket)
- end, queue:to_list(StateData#state.queue)),
- lists:foreach(
- fun(FixedPacket) ->
- send_element(StateData, FixedPacket)
- end, gb_trees:values(StateData#state.pres_queue)),
- StateData#state{queue = queue:new(),
- queue_len = 0,
- pres_queue = gb_trees:empty(),
- standby = false}.
+ lists:foreach(fun ({_From, _To, FixedPacket}) ->
+ send_element(StateData, FixedPacket)
+ end,
+ queue:to_list(StateData#state.queue)),
+ lists:foreach(fun (FixedPacket) ->
+ send_element(StateData, FixedPacket)
+ end,
+ gb_trees:values(StateData#state.pres_queue)),
+ StateData#state{queue = queue:new(), queue_len = 0,
+ pres_queue = gb_trees:empty(), standby = false}.
send_out_of_reception_message(StateData, From, To,
- {xmlelement, "message", _, _} = Packet) ->
- Type = xml:get_tag_attr_s("type", Packet),
- if
- (Type == "normal") or
- (Type == "") or
- (Type == "chat") or
- (StateData#state.oor_send_groupchat and (Type == "groupchat"))->
- %Lang = case xml:get_tag_attr_s("xml:lang", Packet) of
- % "" ->
- % StateData#state.lang;
- % L ->
- % L
- % end,
- %Text = translate:translate(
- % Lang, "User is temporarily out of reception"),
- %MsgType = "error",
- %Message = {xmlelement, "message",
- % [{"type", MsgType}],
- % [{xmlelement, "body", [],
- % [{xmlcdata, Text}]}]},
- %ejabberd_router:route(To, From, Message),
- Body1 = xml:get_path_s(Packet, [{elem, "body"}, cdata]),
- Body =
- case check_x_attachment(Packet) of
+ #xmlel{name = <<"message">>} = Packet) ->
+ Type = xml:get_tag_attr_s(<<"type">>, Packet),
+ if (Type == <<"normal">>) or (Type == <<"">>) or
+ (Type == <<"chat">>)
+ or
+ StateData#state.oor_send_groupchat and
+ (Type == <<"groupchat">>) ->
+ Body1 = xml:get_path_s(Packet,
+ [{elem, <<"body">>}, cdata]),
+ Body = case check_x_attachment(Packet) of
true ->
case Body1 of
- "" -> [238, 128, 136];
- _ ->
- [238, 128, 136, 32 | Body1]
+ <<"">> -> <<238, 128, 136>>;
+ _ -> <<238, 128, 136, 32, Body1/binary>>
end;
- false ->
- Body1
- end,
- Pushed = check_x_pushed(Packet),
- if
- Body == "";
- Pushed ->
- StateData;
- true ->
- BFrom = jlib:jid_remove_resource(From),
- LBFrom = jlib:jid_tolower(BFrom),
- UnreadUsers = ?SETS:add_element(
- LBFrom,
- StateData#state.oor_unread_users),
- IncludeBody =
- case StateData#state.oor_send_body of
- all ->
- true;
- first_per_user ->
- not ?SETS:is_element(
- LBFrom,
- StateData#state.oor_unread_users);
- first ->
- StateData#state.oor_unread == 0;
- none ->
- false
- end,
- Unread = StateData#state.oor_unread + 1,
- SFrom = jlib:jid_to_string(BFrom),
- Msg =
- if
- IncludeBody ->
- CBody = utf8_cut(Body, 100),
- case StateData#state.oor_send_from of
- jid -> SFrom ++ ": " ++ CBody;
- username ->
- UnescapedFrom =
- unescape(BFrom#jid.user),
- UnescapedFrom ++ ": " ++ CBody;
- name ->
- Name = get_roster_name(
- StateData, BFrom),
- Name ++ ": " ++ CBody;
- _ -> CBody
- end;
- true ->
- ""
+ false -> Body1
+ end,
+ Pushed = check_x_pushed(Packet),
+ if Body == <<"">>; Pushed -> StateData;
+ true ->
+ BFrom = jlib:jid_remove_resource(From),
+ LBFrom = jlib:jid_tolower(BFrom),
+ UnreadUsers = (?SETS):add_element(LBFrom,
+ StateData#state.oor_unread_users),
+ IncludeBody = case StateData#state.oor_send_body of
+ all -> true;
+ first_per_user ->
+ not
+ (?SETS):is_element(LBFrom,
+ StateData#state.oor_unread_users);
+ first -> StateData#state.oor_unread == 0;
+ none -> false
+ end,
+ Unread = StateData#state.oor_unread + 1,
+ SFrom = jlib:jid_to_string(BFrom),
+ Msg = if IncludeBody ->
+ CBody = utf8_cut(Body, 100),
+ case StateData#state.oor_send_from of
+ jid -> <<SFrom/binary, ": ", (iolist_to_binary(CBody))/binary>>;
+ username ->
+ UnescapedFrom = unescape(BFrom#jid.user),
+ <<UnescapedFrom/binary, ": ",
+ (iolist_to_binary(CBody))/binary>>;
+ name ->
+ Name = get_roster_name(StateData, BFrom),
+ <<Name/binary, ": ", (iolist_to_binary(CBody))/binary>>;
+ _ -> <<(iolist_to_binary(CBody))/binary>>
+ end;
+ true -> <<"">>
end,
- Sound = IncludeBody,
- AppID = StateData#state.oor_appid,
- ejabberd_hooks:run(
- p1_push_notification,
- StateData#state.server,
- [StateData#state.server,
- StateData#state.jid,
- StateData#state.oor_notification,
- Msg,
- Unread + StateData#state.oor_unread_client,
- Sound,
- AppID,
- SFrom]),
- %% This hook is intended to give other module a
- %% chance to notify the sender that the message is
- %% not directly delivered to the client (almost
- %% equivalent to offline).
- ejabberd_hooks:run(delayed_message_hook,
- StateData#state.server,
- [From, To, Packet]),
- StateData#state{oor_unread = Unread,
- oor_unread_users = UnreadUsers}
- end;
- true ->
- StateData
+ Sound = IncludeBody,
+ AppID = StateData#state.oor_appid,
+ ejabberd_hooks:run(p1_push_notification,
+ StateData#state.server,
+ [StateData#state.server,
+ StateData#state.jid,
+ StateData#state.oor_notification, Msg,
+ Unread +
+ StateData#state.oor_unread_client,
+ Sound, AppID, SFrom]),
+ ejabberd_hooks:run(delayed_message_hook,
+ StateData#state.server,
+ [From, To, Packet]),
+ StateData#state{oor_unread = Unread,
+ oor_unread_users = UnreadUsers}
+ end;
+ true -> StateData
end;
-send_out_of_reception_message(StateData, _From, _To, _Packet) ->
+send_out_of_reception_message(StateData, _From, _To,
+ _Packet) ->
StateData.
make_oor_presence(StateData) ->
make_oor_presence(StateData, [], []).
-make_oor_presence(StateData, PresenceAttrs, PresenceEls) ->
- ShowEl =
- case StateData#state.oor_show of
- "available" -> [];
- _ ->
- [{xmlelement, "show", [],
- [{xmlcdata, StateData#state.oor_show}]}]
- end,
- {xmlelement, "presence", PresenceAttrs,
- ShowEl ++
- [{xmlelement, "status", [],
- [{xmlcdata, StateData#state.oor_status}]}]
- ++ PresenceEls}.
-
-utf8_cut(S, Bytes) ->
- utf8_cut(S, [], [], Bytes + 1).
-
-utf8_cut(_S, _Cur, Prev, 0) ->
- lists:reverse(Prev);
-utf8_cut([], Cur, _Prev, _Bytes) ->
- lists:reverse(Cur);
-utf8_cut([C | S], Cur, Prev, Bytes) ->
- if
- C bsr 6 == 2 ->
- utf8_cut(S, [C | Cur], Prev, Bytes - 1);
- true ->
- utf8_cut(S, [C | Cur], Cur, Bytes - 1)
+make_oor_presence(StateData, PresenceAttrs,
+ PresenceEls) ->
+ ShowEl = case StateData#state.oor_show of
+ <<"available">> -> [];
+ _ ->
+ [#xmlel{name = <<"show">>, attrs = [],
+ children = [{xmlcdata, StateData#state.oor_show}]}]
+ end,
+ #xmlel{name = <<"presence">>, attrs = PresenceAttrs,
+ children =
+ ShowEl ++
+ [#xmlel{name = <<"status">>, attrs = [],
+ children = [{xmlcdata, StateData#state.oor_status}]}]
+ ++ PresenceEls}.
+
+utf8_cut(S, Bytes) -> utf8_cut(S, <<>>, <<>>, Bytes + 1).
+
+utf8_cut(_S, _Cur, Prev, 0) -> Prev;
+utf8_cut(<<>>, Cur, _Prev, _Bytes) -> Cur;
+utf8_cut(<<C, S/binary>>, Cur, Prev, Bytes) ->
+ if C bsr 6 == 2 ->
+ utf8_cut(S, <<Cur/binary, C>>, Prev, Bytes - 1);
+ true -> utf8_cut(S, <<Cur/binary, C>>, Cur, Bytes - 1)
end.
-include("mod_roster.hrl").
@@ -2989,479 +2858,478 @@ utf8_cut([C | S], Cur, Prev, Bytes) ->
get_roster_name(StateData, JID) ->
User = StateData#state.user,
Server = StateData#state.server,
- RosterItems = ejabberd_hooks:run_fold(
- roster_get, Server, [], [{User, Server}]),
+ RosterItems = ejabberd_hooks:run_fold(roster_get,
+ Server, [], [{User, Server}]),
JUser = JID#jid.luser,
JServer = JID#jid.lserver,
- Item =
- lists:foldl(
- fun(_, Res = #roster{}) ->
- Res;
- (I, false) ->
- case I#roster.jid of
- {JUser, JServer, _} ->
- I;
- _ ->
- false
- end
- end, false, RosterItems),
+ Item = lists:foldl(fun (_, Res = #roster{}) -> Res;
+ (I, false) ->
+ case I#roster.jid of
+ {JUser, JServer, _} -> I;
+ _ -> false
+ end
+ end,
+ false, RosterItems),
case Item of
- false ->
- unescape(JID#jid.user);
- #roster{} ->
- Item#roster.name
+ false -> unescape(JID#jid.user);
+ #roster{} -> Item#roster.name
end.
-unescape("") -> "";
-unescape("\\20" ++ S) -> [$\s | unescape(S)];
-unescape("\\22" ++ S) -> [$" | unescape(S)];
-unescape("\\26" ++ S) -> [$& | unescape(S)];
-unescape("\\27" ++ S) -> [$' | unescape(S)];
-unescape("\\2f" ++ S) -> [$/ | unescape(S)];
-unescape("\\3a" ++ S) -> [$: | unescape(S)];
-unescape("\\3c" ++ S) -> [$< | unescape(S)];
-unescape("\\3e" ++ S) -> [$> | unescape(S)];
-unescape("\\40" ++ S) -> [$@ | unescape(S)];
-unescape("\\5c" ++ S) -> [$\\ | unescape(S)];
-unescape([C | S]) -> [C | unescape(S)].
-
+unescape(<<"">>) -> <<"">>;
+unescape(<<"\\20", S/binary>>) ->
+ <<"\s", (unescape(S))/binary>>;
+unescape(<<"\\22", S/binary>>) ->
+ <<"\"", (unescape(S))/binary>>;
+unescape(<<"\\26", S/binary>>) ->
+ <<"&", (unescape(S))/binary>>;
+unescape(<<"\\27", S/binary>>) ->
+ <<"'", (unescape(S))/binary>>;
+unescape(<<"\\2f", S/binary>>) ->
+ <<"/", (unescape(S))/binary>>;
+unescape(<<"\\3a", S/binary>>) ->
+ <<":", (unescape(S))/binary>>;
+unescape(<<"\\3c", S/binary>>) ->
+ <<"<", (unescape(S))/binary>>;
+unescape(<<"\\3e", S/binary>>) ->
+ <<">", (unescape(S))/binary>>;
+unescape(<<"\\40", S/binary>>) ->
+ <<"@", (unescape(S))/binary>>;
+unescape(<<"\\5c", S/binary>>) ->
+ <<"\\", (unescape(S))/binary>>;
+unescape(<<C, S/binary>>) -> <<C, (unescape(S))/binary>>.
cancel_timer(Timer) ->
erlang:cancel_timer(Timer),
- receive
- {timeout, Timer, _} ->
- ok
- after 0 ->
- ok
- end.
+ receive {timeout, Timer, _} -> ok after 0 -> ok end.
enqueue(StateData, From, To, Packet) ->
- IsPresence =
- case Packet of
- {xmlelement, "presence", _, _} ->
- case xml:get_tag_attr_s("type", Packet) of
- "subscribe" ->
- false;
- "subscribed" ->
- false;
- "unsubscribe" ->
- false;
- "unsubscribed" ->
- false;
- _ ->
- true
- end;
- _ ->
- false
- end,
- Messages =
- StateData#state.queue_len + gb_trees:size(StateData#state.pres_queue),
- if
- Messages >= ?MAX_OOR_MESSAGES ->
- self() ! {timeout, StateData#state.keepalive_timer, []};
- true ->
- ok
+ IsPresence = case Packet of
+ #xmlel{name = <<"presence">>} ->
+ case xml:get_tag_attr_s(<<"type">>, Packet) of
+ <<"subscribe">> -> false;
+ <<"subscribed">> -> false;
+ <<"unsubscribe">> -> false;
+ <<"unsubscribed">> -> false;
+ _ -> true
+ end;
+ _ -> false
+ end,
+ Messages = StateData#state.queue_len +
+ gb_trees:size(StateData#state.pres_queue),
+ if Messages >= (?MAX_OOR_MESSAGES) ->
+ self() ! {timeout, StateData#state.keepalive_timer, []};
+ true -> ok
end,
- if
- IsPresence ->
- LFrom = jlib:jid_tolower(From),
- case is_own_presence(StateData#state.jid, LFrom) of
- true -> StateData;
- false ->
- NewQueue = gb_trees:enter(LFrom, Packet,
- StateData#state.pres_queue),
- StateData#state{pres_queue = NewQueue}
- end;
- true ->
- CleanPacket = xml:remove_subtags(Packet, "x", {"xmlns", ?NS_P1_PUSHED}),
- Packet2 =
- case CleanPacket of
- {xmlelement, "message", _, _} ->
- xml:append_subtags(
- maybe_add_delay(CleanPacket, utc, To, ""),
- [{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]);
- _ ->
- Packet
- end,
- NewQueue = queue:in({From, To, Packet2},
- StateData#state.queue),
- NewQueueLen = StateData#state.queue_len + 1,
- StateData#state{queue = NewQueue,
- queue_len = NewQueueLen}
+ if IsPresence ->
+ LFrom = jlib:jid_tolower(From),
+ case jlib:jid_tolower(StateData#state.jid) == LFrom of
+ true -> StateData;
+ false ->
+ NewQueue = gb_trees:enter(LFrom, Packet,
+ StateData#state.pres_queue),
+ StateData#state{pres_queue = NewQueue}
+ end;
+ true ->
+ CleanPacket = xml:remove_subtags(Packet, <<"x">>,
+ {<<"xmlns">>, ?NS_P1_PUSHED}),
+ Packet2 = case CleanPacket of
+ #xmlel{name = <<"message">>} ->
+ xml:append_subtags(maybe_add_delay(CleanPacket, utc,
+ To, <<"">>),
+ [#xmlel{name = <<"x">>,
+ attrs =
+ [{<<"xmlns">>,
+ ?NS_P1_PUSHED}],
+ children = []}]);
+ _ -> Packet
+ end,
+ NewQueue = queue:in({From, To, Packet2},
+ StateData#state.queue),
+ NewQueueLen = StateData#state.queue_len + 1,
+ StateData#state{queue = NewQueue,
+ queue_len = NewQueueLen}
end.
-%% Is my own presence packet ?
-is_own_presence(MyFullJID, MyFullJID) ->
- true;
-is_own_presence(_MyFullJID, _LFrom) ->
- false.
-
ack(StateData, From, To, Packet) ->
- if
- StateData#state.ack_enabled ->
- NeedsAck =
- case Packet of
- {xmlelement, "presence", _, _} ->
- case xml:get_tag_attr_s("type", Packet) of
- "subscribe" ->
- true;
- "subscribed" ->
- true;
- "unsubscribe" ->
- true;
- "unsubscribed" ->
- true;
- _ ->
- false
- end;
- {xmlelement, "message", _, _} ->
- true;
- _ ->
- false
- end,
- if
- NeedsAck ->
- Counter = StateData#state.ack_counter + 1,
- NewAckQueue = queue:in({Counter, From, To, Packet},
- StateData#state.ack_queue),
- send_ack_request(StateData#state{ack_queue = NewAckQueue,
- ack_counter = Counter});
- true ->
- StateData
- end;
- true ->
- StateData
+ if StateData#state.ack_enabled ->
+ NeedsAck = case Packet of
+ #xmlel{name = <<"presence">>} ->
+ case xml:get_tag_attr_s(<<"type">>, Packet) of
+ <<"subscribe">> -> true;
+ <<"subscribed">> -> true;
+ <<"unsubscribe">> -> true;
+ <<"unsubscribed">> -> true;
+ _ -> false
+ end;
+ #xmlel{name = <<"message">>} -> true;
+ _ -> false
+ end,
+ if NeedsAck ->
+ Counter = StateData#state.ack_counter + 1,
+ NewAckQueue = queue:in({Counter, From, To, Packet},
+ StateData#state.ack_queue),
+ send_ack_request(StateData#state{ack_queue =
+ NewAckQueue,
+ ack_counter = Counter});
+ true -> StateData
+ end;
+ true -> StateData
end.
send_ack_request(StateData) ->
case StateData#state.ack_timer of
- undefined ->
- AckCounter = StateData#state.ack_counter,
- AckTimer =
- erlang:start_timer(?C2S_P1_ACK_TIMEOUT, self(), AckCounter),
- AckTimeout = StateData#state.keepalive_timeout +
- StateData#state.oor_timeout,
- erlang:send_after(AckTimeout * 1000, self(),
- {ack_timeout, AckTimeout}),
- send_element(
- StateData,
- {xmlelement, "r",
- [{"h", integer_to_list(AckCounter)}], []}),
- StateData#state{ack_timer = AckTimer};
- _ ->
- StateData
+ undefined ->
+ AckCounter = StateData#state.ack_counter,
+ AckTimer = erlang:start_timer(?C2S_P1_ACK_TIMEOUT,
+ self(), AckCounter),
+ AckTimeout = StateData#state.keepalive_timeout +
+ StateData#state.oor_timeout,
+ erlang:send_after(AckTimeout * 1000, self(),
+ {ack_timeout, AckTimeout}),
+ send_element(StateData,
+ #xmlel{name = <<"r">>,
+ attrs =
+ [{<<"h">>,
+ iolist_to_binary(integer_to_list(AckCounter))}],
+ children = []}),
+ StateData#state{ack_timer = AckTimer};
+ _ -> StateData
end.
receive_ack(StateData, SCounter) ->
- case catch list_to_integer(SCounter) of
- Counter when is_integer(Counter) ->
- NewQueue = clean_queue(StateData#state.ack_queue, Counter),
- StateData#state{ack_queue = NewQueue};
- _ ->
- StateData
+ case catch jlib:binary_to_integer(SCounter) of
+ Counter when is_integer(Counter) ->
+ NewQueue = clean_queue(StateData#state.ack_queue,
+ Counter),
+ StateData#state{ack_queue = NewQueue};
+ _ -> StateData
end.
clean_queue(Queue, Counter) ->
case queue:is_empty(Queue) of
- true ->
- Queue;
- false ->
- C = element(1, queue:head(Queue)),
- if
- C =< Counter ->
- clean_queue(queue:tail(Queue), Counter);
- true ->
- Queue
- end
+ true -> Queue;
+ false ->
+ C = element(1, queue:head(Queue)),
+ if C =< Counter ->
+ clean_queue(queue:tail(Queue), Counter);
+ true -> Queue
+ end
end.
prepare_acks_for_rebind(StateData) ->
AckQueue = StateData#state.ack_queue,
case queue:is_empty(AckQueue) of
- true ->
- StateData;
- false ->
- Unsent =
- lists:map(
- fun({_Counter, From, To, FixedPacket}) ->
- {From, To, FixedPacket}
- end, queue:to_list(AckQueue)),
- NewQueue = queue:join(queue:from_list(Unsent),
- StateData#state.queue),
- StateData#state{queue = NewQueue,
- queue_len = queue:len(NewQueue),
- ack_queue = queue:new(),
- reception = false}
+ true -> StateData;
+ false ->
+ Unsent = lists:map(fun ({_Counter, From, To,
+ FixedPacket}) ->
+ {From, To, FixedPacket}
+ end,
+ queue:to_list(AckQueue)),
+ NewQueue = queue:join(queue:from_list(Unsent),
+ StateData#state.queue),
+ StateData#state{queue = NewQueue,
+ queue_len = queue:len(NewQueue),
+ ack_queue = queue:new(), reception = false}
end.
-
rebind(StateData, JID, StreamID) ->
case JID#jid.lresource of
- "" ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_P1_REBIND}],
- [{xmlcdata, "Invalid JID"}]}),
- fsm_next_state(wait_for_feature_request,
- StateData);
- _ ->
- ejabberd_sm:route(
- ?MODULE, JID,
- {xmlelement, rebind, [], {self(), StreamID}}),
- receive
- {rebind, false} ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_P1_REBIND}],
- [{xmlcdata, "Session not found"}]}),
- fsm_next_state(wait_for_feature_request,
- StateData);
- {rebind, NewStateData} ->
- ?INFO_MSG("(~w) Reopened session for ~s",
- [StateData#state.socket,
- jlib:jid_to_string(JID)]),
- SID = {now(), self()},
- StateData2 =
- NewStateData#state{
- socket = StateData#state.socket,
- sockmod = StateData#state.sockmod,
- socket_monitor = StateData#state.socket_monitor,
- sid = SID,
- ip = StateData#state.ip,
- keepalive_timer = StateData#state.keepalive_timer,
- ack_timer = undefined
- },
- send_element(StateData2,
- {xmlelement, "rebind",
- [{"xmlns", ?NS_P1_REBIND}],
- []}),
- maybe_migrate(session_established, StateData2)
+ <<"">> ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_P1_REBIND}],
+ children = [{xmlcdata, <<"Invalid JID">>}]}),
+ fsm_next_state(wait_for_feature_request, StateData);
+ _ ->
+ ejabberd_sm:route(jlib:make_jid(<<"">>, StateData#state.server, <<"">>),
+ JID, {broadcast, {rebind, self(), StreamID}}),
+ receive
+ {rebind, false} ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_P1_REBIND}],
+ children =
+ [{xmlcdata, <<"Session not found">>}]}),
+ fsm_next_state(wait_for_feature_request, StateData);
+ {rebind, NewStateData} ->
+ ?INFO_MSG("(~w) Reopened session for ~s",
+ [StateData#state.socket, jlib:jid_to_string(JID)]),
+ SID = {now(), self()},
+ StateData2 = NewStateData#state{socket =
+ StateData#state.socket,
+ sockmod =
+ StateData#state.sockmod,
+ socket_monitor =
+ StateData#state.socket_monitor,
+ sid = SID,
+ ip = StateData#state.ip,
+ keepalive_timer =
+ StateData#state.keepalive_timer,
+ ack_timer = undefined},
+ send_element(StateData2,
+ #xmlel{name = <<"rebind">>,
+ attrs = [{<<"xmlns">>, ?NS_P1_REBIND}],
+ children = []}),
+ maybe_migrate(session_established, StateData2)
after 1000 ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_P1_REBIND}],
- [{xmlcdata, "Session not found"}]}),
- fsm_next_state(wait_for_feature_request,
- StateData)
- end
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs =
+ [{<<"xmlns">>, ?NS_P1_REBIND}],
+ children =
+ [{xmlcdata,
+ <<"Session not found">>}]}),
+ fsm_next_state(wait_for_feature_request, StateData)
+ end
end.
process_push_iq(From, To,
- #iq{type = _Type, sub_el = El} = IQ,
- StateData) ->
- {Res, NewStateData} =
- case El of
- {xmlelement, "push", _, _} ->
- SKeepAlive =
- xml:get_path_s(El, [{elem, "keepalive"}, {attr, "max"}]),
- SOORTimeout =
- xml:get_path_s(El, [{elem, "session"}, {attr, "duration"}]),
- Status = xml:get_path_s(El, [{elem, "status"}, cdata]),
- Show = xml:get_path_s(El, [{elem, "status"}, {attr, "type"}]),
- SSendBody = xml:get_path_s(El, [{elem, "body"}, {attr, "send"}]),
- SendBody =
- case SSendBody of
- "all" -> all;
- "first-per-user" -> first_per_user;
- "first" -> first;
- "none" -> none;
- _ -> none
- end,
- SendGroupchat =
- xml:get_path_s(El, [{elem, "body"},
- {attr, "groupchat"}]) == "true",
- SendFrom = send_from(El),
- AppID = xml:get_path_s(El, [{elem, "appid"}, cdata]),
- {Offline, Keep} =
- case xml:get_path_s(El, [{elem, "offline"}, cdata]) of
- "true" -> {true, false};
- "keep" -> {false, true};
- _ -> {false, false}
- end,
- Notification1 = xml:get_path_s(El, [{elem, "notification"}]),
- Notification =
- case Notification1 of
- {xmlelement, _, _, _} ->
- Notification1;
- _ ->
- {xmlelement, "notification", [],
- [{xmlelement, "type", [],
- [{xmlcdata, "none"}]}]}
- end,
- case catch {list_to_integer(SKeepAlive),
- list_to_integer(SOORTimeout)} of
- {KeepAlive, OORTimeout}
- when OORTimeout =< ?MAX_OOR_TIMEOUT ->
- if
- Offline ->
- ejabberd_hooks:run(
- p1_push_enable_offline,
- StateData#state.server,
- [StateData#state.jid,
- Notification, SendBody, SendFrom, AppID]);
- Keep ->
- ok;
- true ->
- ejabberd_hooks:run(
- p1_push_disable,
- StateData#state.server,
- [StateData#state.jid,
- Notification,
- AppID])
- end,
- NSD1 =
- StateData#state{keepalive_timeout = KeepAlive,
- oor_timeout = OORTimeout * 60,
- oor_status = Status,
- oor_show = Show,
- oor_notification = Notification,
- oor_send_body = SendBody,
- oor_send_groupchat = SendGroupchat,
- oor_send_from = SendFrom,
- oor_appid = AppID,
- oor_offline = Offline},
- NSD2 = start_keepalive_timer(NSD1),
- {{result, []}, NSD2};
- _ ->
- {{error, ?ERR_BAD_REQUEST}, StateData}
- end;
- {xmlelement, "disable", _, _} ->
- ejabberd_hooks:run(
- p1_push_disable,
- StateData#state.server,
- [StateData#state.jid,
- StateData#state.oor_notification,
- StateData#state.oor_appid]),
- NSD1 =
- StateData#state{keepalive_timeout = undefined,
- oor_timeout = undefined,
- oor_status = "",
- oor_show = "",
- oor_notification = undefined,
- oor_send_body = all},
- NSD2 = start_keepalive_timer(NSD1),
- {{result, []}, NSD2};
- {xmlelement, "badge", _, _} ->
- SBadge = xml:get_path_s(El, [{attr, "unread"}]),
- Badge =
- case catch list_to_integer(SBadge) of
- B when is_integer(B) ->
- B;
- _ ->
- 0
- end,
- NSD1 =
- StateData#state{oor_unread_client = Badge},
- {{result, []}, NSD1};
- _ ->
- {{error, ?ERR_BAD_REQUEST}, StateData}
- end,
- IQRes =
- case Res of
- {result, Result} ->
- IQ#iq{type = result, sub_el = Result};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [El, Error]}
- end,
- ejabberd_router:route(
- To, From, jlib:iq_to_xml(IQRes)),
+ #iq{type = _Type, sub_el = El} = IQ, StateData) ->
+ {Res, NewStateData} = case El of
+ #xmlel{name = <<"push">>} ->
+ SKeepAlive = xml:get_path_s(El,
+ [{elem,
+ <<"keepalive">>},
+ {attr,
+ <<"max">>}]),
+ SOORTimeout = xml:get_path_s(El,
+ [{elem,
+ <<"session">>},
+ {attr,
+ <<"duration">>}]),
+ Status = xml:get_path_s(El,
+ [{elem, <<"status">>},
+ cdata]),
+ Show = xml:get_path_s(El,
+ [{elem, <<"status">>},
+ {attr, <<"type">>}]),
+ SSendBody = xml:get_path_s(El,
+ [{elem, <<"body">>},
+ {attr,
+ <<"send">>}]),
+ SendBody = case SSendBody of
+ <<"all">> -> all;
+ <<"first-per-user">> ->
+ first_per_user;
+ <<"first">> -> first;
+ <<"none">> -> none;
+ _ -> none
+ end,
+ SendGroupchat = xml:get_path_s(El,
+ [{elem,
+ <<"body">>},
+ {attr,
+ <<"groupchat">>}])
+ == <<"true">>,
+ SendFrom = send_from(El),
+ AppID = xml:get_path_s(El,
+ [{elem, <<"appid">>},
+ cdata]),
+ {Offline, Keep} = case xml:get_path_s(El,
+ [{elem,
+ <<"offline">>},
+ cdata])
+ of
+ <<"true">> -> {true, false};
+ <<"keep">> -> {false, true};
+ _ -> {false, false}
+ end,
+ Notification1 = xml:get_path_s(El,
+ [{elem,
+ <<"notification">>}]),
+ Notification = case Notification1 of
+ #xmlel{} -> Notification1;
+ _ ->
+ #xmlel{name =
+ <<"notification">>,
+ attrs = [],
+ children =
+ [#xmlel{name =
+ <<"type">>,
+ attrs =
+ [],
+ children
+ =
+ [{xmlcdata,
+ <<"none">>}]}]}
+ end,
+ case catch
+ {jlib:binary_to_integer(SKeepAlive),
+ jlib:binary_to_integer(SOORTimeout)}
+ of
+ {KeepAlive, OORTimeout}
+ when OORTimeout =< (?MAX_OOR_TIMEOUT) ->
+ if Offline ->
+ ejabberd_hooks:run(p1_push_enable_offline,
+ StateData#state.server,
+ [StateData#state.jid,
+ Notification,
+ SendBody,
+ SendFrom,
+ AppID]);
+ Keep -> ok;
+ true ->
+ ejabberd_hooks:run(p1_push_disable,
+ StateData#state.server,
+ [StateData#state.jid,
+ Notification,
+ AppID])
+ end,
+ NSD1 = StateData#state{keepalive_timeout =
+ KeepAlive,
+ oor_timeout =
+ OORTimeout *
+ 60,
+ oor_status =
+ Status,
+ oor_show = Show,
+ oor_notification =
+ Notification,
+ oor_send_body =
+ SendBody,
+ oor_send_groupchat
+ =
+ SendGroupchat,
+ oor_send_from =
+ SendFrom,
+ oor_appid = AppID,
+ oor_offline =
+ Offline},
+ NSD2 = start_keepalive_timer(NSD1),
+ {{result, []}, NSD2};
+ _ -> {{error, ?ERR_BAD_REQUEST}, StateData}
+ end;
+ #xmlel{name = <<"disable">>} ->
+ ejabberd_hooks:run(p1_push_disable,
+ StateData#state.server,
+ [StateData#state.jid,
+ StateData#state.oor_notification,
+ StateData#state.oor_appid]),
+ NSD1 = StateData#state{keepalive_timeout =
+ undefined,
+ oor_timeout = undefined,
+ oor_status = <<"">>,
+ oor_show = <<"">>,
+ oor_notification =
+ undefined,
+ oor_send_body = all},
+ NSD2 = start_keepalive_timer(NSD1),
+ {{result, []}, NSD2};
+ #xmlel{name = <<"badge">>} ->
+ SBadge = xml:get_path_s(El,
+ [{attr, <<"unread">>}]),
+ Badge = case catch
+ jlib:binary_to_integer(SBadge)
+ of
+ B when is_integer(B) -> B;
+ _ -> 0
+ end,
+ NSD1 = StateData#state{oor_unread_client =
+ Badge},
+ {{result, []}, NSD1};
+ _ -> {{error, ?ERR_BAD_REQUEST}, StateData}
+ end,
+ IQRes = case Res of
+ {result, Result} ->
+ IQ#iq{type = result, sub_el = Result};
+ {error, Error} ->
+ IQ#iq{type = error, sub_el = [El, Error]}
+ end,
+ ejabberd_router:route(To, From, jlib:iq_to_xml(IQRes)),
NewStateData.
-check_x_pushed({xmlelement, _Name, _Attrs, Els}) ->
+check_x_pushed(#xmlel{children = Els}) ->
check_x_pushed1(Els).
-check_x_pushed1([]) ->
- false;
+check_x_pushed1([]) -> false;
check_x_pushed1([{xmlcdata, _} | Els]) ->
check_x_pushed1(Els);
check_x_pushed1([El | Els]) ->
- case xml:get_tag_attr_s("xmlns", El) of
- ?NS_P1_PUSHED ->
- true;
- _ ->
- check_x_pushed1(Els)
+ case xml:get_tag_attr_s(<<"xmlns">>, El) of
+ ?NS_P1_PUSHED -> true;
+ _ -> check_x_pushed1(Els)
end.
-check_x_attachment({xmlelement, _Name, _Attrs, Els}) ->
+check_x_attachment(#xmlel{children = Els}) ->
check_x_attachment1(Els).
-check_x_attachment1([]) ->
- false;
+check_x_attachment1([]) -> false;
check_x_attachment1([{xmlcdata, _} | Els]) ->
check_x_attachment1(Els);
check_x_attachment1([El | Els]) ->
- case xml:get_tag_attr_s("xmlns", El) of
- ?NS_P1_ATTACHMENT ->
- true;
- _ ->
- check_x_attachment1(Els)
+ case xml:get_tag_attr_s(<<"xmlns">>, El) of
+ ?NS_P1_ATTACHMENT -> true;
+ _ -> check_x_attachment1(Els)
end.
-%% TODO: Delete XEP-0091 stuff once it is Obsolete
maybe_add_delay(El, TZ, From, Desc) ->
- maybe_add_delay(El, TZ, From, Desc, calendar:now_to_universal_time(now())).
-maybe_add_delay({xmlelement, _, _, Els} = El, TZ, From, Desc, TimeStamp) ->
- HasOldTS = lists:any(
- fun({xmlelement, "x", Attrs, _}) ->
- xml:get_attr_s("xmlns", Attrs) == ?NS_DELAY91;
- (_) ->
- false
- end, Els),
- HasNewTS = lists:any(
- fun({xmlelement, "delay", Attrs, _}) ->
- xml:get_attr_s("xmlns", Attrs) == ?NS_DELAY;
- (_) ->
- false
- end, Els),
+ maybe_add_delay(El, TZ, From, Desc,
+ calendar:now_to_universal_time(now())).
+
+maybe_add_delay(#xmlel{children = Els} = El, TZ, From,
+ Desc, TimeStamp) ->
+ HasOldTS = lists:any(fun (#xmlel{name = <<"x">>,
+ attrs = Attrs}) ->
+ xml:get_attr_s(<<"xmlns">>, Attrs) ==
+ (?NS_DELAY91);
+ (_) -> false
+ end,
+ Els),
+ HasNewTS = lists:any(fun (#xmlel{name = <<"delay">>,
+ attrs = Attrs}) ->
+ xml:get_attr_s(<<"xmlns">>, Attrs) ==
+ (?NS_DELAY);
+ (_) -> false
+ end,
+ Els),
El1 = if not HasOldTS ->
- xml:append_subtags(El, [jlib:timestamp_to_xml(TimeStamp)]);
- true ->
- El
- end,
+ xml:append_subtags(El,
+ [jlib:timestamp_to_xml(TimeStamp)]);
+ true -> El
+ end,
if not HasNewTS ->
- xml:append_subtags(
- El1, [jlib:timestamp_to_xml(TimeStamp, TZ, From, Desc)]);
- true ->
- El1
+ xml:append_subtags(El1,
+ [jlib:timestamp_to_xml(TimeStamp, TZ, From,
+ Desc)]);
+ true -> El1
end.
send_from(El) ->
- %% First test previous version attribute:
- case xml:get_path_s(El, [{elem, "body"}, {attr, "jid"}]) of
- "false" ->
- none;
- "true" ->
- jid;
- "" ->
- case xml:get_path_s(El, [{elem, "body"}, {attr, "from"}]) of
- "jid" -> jid;
- "username" -> username;
- "name" -> name;
- "none" -> none;
- _ -> jid
- end
+ case xml:get_path_s(El,
+ [{elem, <<"body">>}, {attr, <<"jid">>}])
+ of
+ <<"false">> -> none;
+ <<"true">> -> jid;
+ <<"">> ->
+ case xml:get_path_s(El,
+ [{elem, <<"body">>}, {attr, <<"from">>}])
+ of
+ <<"jid">> -> jid;
+ <<"username">> -> username;
+ <<"name">> -> name;
+ <<"none">> -> none;
+ _ -> jid
+ 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.
%%%----------------------------------------------------------------------
@@ -3469,40 +3337,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
@@ -3513,113 +3381,83 @@ route_blocking(What, StateData) ->
%%% JID Set memory footprint reduction code
%%%----------------------------------------------------------------------
-%% 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.
-
-%% @spec () -> string()
-%% @doc Build the content of a Flash policy file.
-%% It specifies as domain "*".
-%% It specifies as to-ports the ports that serve ejabberd_c2s.
flash_policy_string() ->
- Listen = ejabberd_config:get_local_option(listen),
- ClientPortsDeep = ["," ++ integer_to_list(Port)
- || {{Port,_,_}, ejabberd_c2s, _Opts} <- Listen],
- %% NOTE: The function string:join/2 was introduced in Erlang/OTP R12B-0
- %% so it can't be used yet in ejabberd.
- ToPortsString = case lists:flatten(ClientPortsDeep) of
- [$, | Tail] -> Tail;
- _ -> []
+ Listen = ejabberd_config:get_local_option(listen, fun(V) -> V end),
+ ClientPortsDeep = [<<",",
+ (iolist_to_binary(integer_to_list(Port)))/binary>>
+ || {{Port, _, _}, ejabberd_c2s, _Opts} <- Listen],
+ ToPortsString = case iolist_to_binary(lists:flatten(ClientPortsDeep)) of
+ <<",", Tail/binary>> -> Tail;
+ _ -> <<>>
end,
-
- "<?xml version=\"1.0\"?>\n"
- "<!DOCTYPE cross-domain-policy SYSTEM "
- "\"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
- "<cross-domain-policy>\n"
- " <allow-access-from domain=\"*\" to-ports=\""
- ++ ToPortsString ++
- "\"/>\n"
- "</cross-domain-policy>\n\0".
-
-need_redirect(#state{redirect = true, user = User, server = Server}) ->
+ <<"<?xml version=\"1.0\"?>\n<!DOCTYPE cross-doma"
+ "in-policy SYSTEM \"http://www.macromedia.com/"
+ "xml/dtds/cross-domain-policy.dtd\">\n<cross-d"
+ "omain-policy>\n <allow-access-from "
+ "domain=\"*\" to-ports=\"",
+ ToPortsString/binary,
+ "\"/>\n</cross-domain-policy>\n\000">>.
+
+need_redirect(#state{redirect = true, user = User,
+ server = Server}) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
case ejabberd_cluster:get_node({LUser, LServer}) of
- Node when node() == Node ->
- false;
- Node ->
- case rpc:call(Node, ejabberd_config,
- get_local_option, [hostname], 5000) of
- Host when is_list(Host) ->
- {true, Host};
- _ ->
- false
- end
+ Node when node() == Node -> false;
+ Node ->
+ case rpc:call(Node, ejabberd_config, get_local_option,
+ [hostname], 5000)
+ of
+ Host when is_binary(Host) -> {true, Host};
+ _ -> false
+ end
end;
-need_redirect(_) ->
- false.
+need_redirect(_) -> false.
get_jid_from_opts(Opts) ->
case lists:keysearch(jid, 1, Opts) of
- {value, {_, JIDValue}} ->
- JID = case JIDValue of
- {_U, _S, _R} ->
- jlib:make_jid(JIDValue);
- _ when is_binary(JIDValue) ->
- jlib:string_to_jid(binary_to_list(JIDValue));
- _ when is_list(JIDValue) ->
- jlib:string_to_jid(JIDValue);
- _ ->
- JIDValue
- end,
- {ok, JID};
- _ ->
- error
+ {value, {_, JIDValue}} ->
+ JID = case JIDValue of
+ {_U, _S, _R} -> jlib:make_jid(JIDValue);
+ _ when is_list(JIDValue) ->
+ jlib:string_to_jid(list_to_binary(JIDValue));
+ _ when is_binary(JIDValue) ->
+ jlib:string_to_jid(JIDValue);
+ _ -> JIDValue
+ end,
+ {ok, JID};
+ _ -> error
end.
-
-is_remote_receiver(#socket_state{receiver = Pid}) when is_pid(Pid) ->
- node(Pid) /= node();
-is_remote_receiver(_) ->
- false.