aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_listener.erl2
-rw-r--r--src/ejabberd_xmlrpc.erl4
-rw-r--r--src/mod_irc.erl54
-rw-r--r--src/mod_roster.erl3
-rw-r--r--src/mod_sip.erl20
-rw-r--r--src/mod_sip_registrar.erl365
6 files changed, 321 insertions, 127 deletions
diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl
index 02a2f3fbd..d2dc0fb73 100644
--- a/src/ejabberd_listener.erl
+++ b/src/ejabberd_listener.erl
@@ -595,7 +595,7 @@ transform_option({{Port, IP, Transport}, Mod, Opts}) ->
try
Mod:transform_listen_option(Opt, Acc)
catch error:undef ->
- Acc
+ [Opt|Acc]
end
end, [], Opts1),
TransportOpt = if Transport == tcp -> [];
diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl
index ff89d2858..a289196a3 100644
--- a/src/ejabberd_xmlrpc.erl
+++ b/src/ejabberd_xmlrpc.erl
@@ -450,6 +450,10 @@ format_result(String, {Name, string}) when is_list(String) ->
{struct, [{Name, lists:flatten(String)}]};
format_result(Binary, {Name, string}) when is_binary(Binary) ->
{struct, [{Name, binary_to_list(Binary)}]};
+format_result(String, {Name, binary}) when is_list(String) ->
+ {struct, [{Name, lists:flatten(String)}]};
+format_result(Binary, {Name, binary}) when is_binary(Binary) ->
+ {struct, [{Name, binary_to_list(Binary)}]};
format_result(Code, {Name, rescode}) ->
{struct, [{Name, make_status(Code)}]};
format_result({Code, Text}, {Name, restuple}) ->
diff --git a/src/mod_irc.erl b/src/mod_irc.erl
index 88e0f1dce..c7dda8303 100644
--- a/src/mod_irc.erl
+++ b/src/mod_irc.erl
@@ -56,7 +56,8 @@
-type conn_param() :: {binary(), binary(), inet:port_number(), binary()} |
{binary(), binary(), inet:port_number()} |
- {binary(), binary()}.
+ {binary(), binary()} |
+ {binary()}.
-record(irc_connection,
{jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()},
@@ -600,7 +601,7 @@ get_data(LServer, Host, From, odbc) ->
<<"';">>])
of
{selected, [<<"data">>], [[SData]]} ->
- data_to_binary(ejabberd_odbc:decode_term(SData));
+ data_to_binary(From, ejabberd_odbc:decode_term(SData));
{'EXIT', _} -> error;
{selected, _, _} -> empty
end.
@@ -711,7 +712,7 @@ get_form(_ServerHost, _Host, _, _, _Lang) ->
set_data(ServerHost, Host, From, Data) ->
LServer = jlib:nameprep(ServerHost),
- set_data(LServer, Host, From, data_to_binary(Data),
+ set_data(LServer, Host, From, data_to_binary(From, Data),
gen_mod:db_type(LServer, ?MODULE)).
set_data(_LServer, Host, From, Data, mnesia) ->
@@ -1217,28 +1218,48 @@ get_username_and_connection_params(Data) ->
end,
{Username, ConnParams}.
-data_to_binary(Data) ->
+data_to_binary(JID, Data) ->
lists:map(
fun({username, U}) ->
{username, iolist_to_binary(U)};
({connections_params, Params}) ->
- {connections_params,
- lists:map(
- fun({S, E}) ->
- {iolist_to_binary(S), iolist_to_binary(E)};
- ({S, E, Port}) ->
- {iolist_to_binary(S), iolist_to_binary(E), Port};
- ({S, E, Port, P}) ->
- {iolist_to_binary(S), iolist_to_binary(E),
- Port, iolist_to_binary(P)}
- end, Params)};
+ {connections_params,
+ lists:flatmap(
+ fun(Param) ->
+ try
+ [conn_param_to_binary(Param)]
+ catch _:_ ->
+ if JID /= error ->
+ ?ERROR_MSG("failed to convert "
+ "parameter ~p for user ~s",
+ [Param,
+ jlib:jid_to_string(JID)]);
+ true ->
+ ?ERROR_MSG("failed to convert "
+ "parameter ~p",
+ [Param])
+ end,
+ []
+ end
+ end, Params)};
(Opt) ->
Opt
end, Data).
+conn_param_to_binary({S}) ->
+ {iolist_to_binary(S)};
+conn_param_to_binary({S, E}) ->
+ {iolist_to_binary(S), iolist_to_binary(E)};
+conn_param_to_binary({S, E, Port}) when is_integer(Port) ->
+ {iolist_to_binary(S), iolist_to_binary(E), Port};
+conn_param_to_binary({S, E, Port, P}) when is_integer(Port) ->
+ {iolist_to_binary(S), iolist_to_binary(E), Port, iolist_to_binary(P)}.
+
conn_params_to_list(Params) ->
lists:map(
- fun({S, E}) ->
+ fun({S}) ->
+ {binary_to_list(S)};
+ ({S, E}) ->
{binary_to_list(S), binary_to_list(E)};
({S, E, Port}) ->
{binary_to_list(S), binary_to_list(E), Port};
@@ -1256,10 +1277,11 @@ update_table() ->
fun(#irc_custom{us_host = {_, H}}) -> H end,
fun(#irc_custom{us_host = {{U, S}, H},
data = Data} = R) ->
+ JID = jlib:make_jid(U, S, <<"">>),
R#irc_custom{us_host = {{iolist_to_binary(U),
iolist_to_binary(S)},
iolist_to_binary(H)},
- data = data_to_binary(Data)}
+ data = data_to_binary(JID, Data)}
end);
_ ->
?INFO_MSG("Recreating irc_custom table", []),
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 4ab8239b5..01646229f 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -1319,7 +1319,8 @@ update_roster_table() ->
iolist_to_binary(R2)},
name = iolist_to_binary(Name),
groups = [iolist_to_binary(G) || G <- Gs],
- askmessage = iolist_to_binary(Ask),
+ askmessage = try iolist_to_binary(Ask)
+ catch _:_ -> <<"">> end,
xs = [xml:to_xmlel(X) || X <- Xs]}
end);
_ ->
diff --git a/src/mod_sip.erl b/src/mod_sip.erl
index 8f7dba9cb..fd36fb5ac 100644
--- a/src/mod_sip.erl
+++ b/src/mod_sip.erl
@@ -68,8 +68,8 @@ message_in(#sip{type = request, method = M} = Req, SIPSock)
Action ->
request(Req, SIPSock, undefined, Action)
end;
-message_in(ping, _SIPSock) ->
- pong;
+message_in(ping, SIPSock) ->
+ mod_sip_registrar:ping(SIPSock);
message_in(_, _) ->
ok.
@@ -162,8 +162,9 @@ action(#sip{method = <<"REGISTER">>, type = request, hdrs = Hdrs,
uri = #uri{user = <<"">>} = URI} = Req, SIPSock) ->
case at_my_host(URI) of
true ->
- case esip:get_hdrs('require', Hdrs) of
- [_|_] = Require ->
+ Require = esip:get_hdrs('require', Hdrs) -- supported(),
+ case Require of
+ [_|_] ->
{unsupported, Require};
_ ->
{_, ToURI, _} = esip:get_hdr('to', Hdrs),
@@ -189,8 +190,9 @@ action(#sip{method = Method, hdrs = Hdrs, type = request} = Req, SIPSock) ->
0 ->
loop;
_ ->
- case esip:get_hdrs('proxy-require', Hdrs) of
- [_|_] = Require ->
+ Require = esip:get_hdrs('proxy-require', Hdrs) -- supported(),
+ case Require of
+ [_|_] ->
{unsupported, Require};
_ ->
{_, ToURI, _} = esip:get_hdr('to', Hdrs),
@@ -253,9 +255,13 @@ check_auth(#sip{method = Method, hdrs = Hdrs, body = Body}, AuthHdr, _SIPSock) -
allow() ->
[<<"OPTIONS">>, <<"REGISTER">>].
+supported() ->
+ [<<"path">>, <<"outbound">>].
+
process(#sip{method = <<"OPTIONS">>} = Req, _) ->
make_response(Req, #sip{type = response, status = 200,
- hdrs = [{'allow', allow()}]});
+ hdrs = [{'allow', allow()},
+ {'supported', supported()}]});
process(#sip{method = <<"REGISTER">>} = Req, _) ->
make_response(Req, #sip{type = response, status = 400});
process(Req, _) ->
diff --git a/src/mod_sip_registrar.erl b/src/mod_sip_registrar.erl
index 2102af851..7233d053b 100644
--- a/src/mod_sip_registrar.erl
+++ b/src/mod_sip_registrar.erl
@@ -12,7 +12,7 @@
-behaviour(?GEN_SERVER).
%% API
--export([start_link/0, request/2, find_sockets/2]).
+-export([start_link/0, request/2, find_sockets/2, ping/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -24,17 +24,19 @@
-define(CALL_TIMEOUT, timer:seconds(30)).
-define(DEFAULT_EXPIRES, 3600).
-
--record(binding, {socket = #sip_socket{},
- call_id = <<"">> :: binary(),
- cseq = 0 :: non_neg_integer(),
- timestamp = now() :: erlang:timestamp(),
- contact :: {binary(), #uri{}, [{binary(), binary()}]},
- tref = make_ref() :: reference(),
- expires = 0 :: non_neg_integer()}).
+-define(FLOW_TIMEOUT_DATAGRAM, 29).
+-define(FLOW_TIMEOUT_STREAM, 180).
-record(sip_session, {us = {<<"">>, <<"">>} :: {binary(), binary()},
- bindings = [] :: [#binding{}]}).
+ socket = #sip_socket{} :: #sip_socket{},
+ call_id = <<"">> :: binary(),
+ cseq = 0 :: non_neg_integer(),
+ timestamp = now() :: erlang:timestamp(),
+ contact :: {binary(), #uri{}, [{binary(), binary()}]},
+ flow_tref :: reference(),
+ reg_tref = make_ref() :: reference(),
+ conn_mref = make_ref() :: reference(),
+ expires = 0 :: non_neg_integer()}).
-record(state, {}).
@@ -53,6 +55,8 @@ request(#sip{hdrs = Hdrs} = Req, SIPSock) ->
CallID = esip:get_hdr('call-id', Hdrs),
CSeq = esip:get_hdr('cseq', Hdrs),
Expires = esip:get_hdr('expires', Hdrs, ?DEFAULT_EXPIRES),
+ Supported = esip:get_hdrs('supported', Hdrs),
+ IsOutboundSupported = lists:member(<<"outbound">>, Supported),
case esip:get_hdrs('contact', Hdrs) of
[<<"*">>] when Expires == 0 ->
case unregister_session(US, CallID, CSeq) of
@@ -74,6 +78,7 @@ request(#sip{hdrs = Hdrs} = Req, SIPSock) ->
end;
[{_, _URI, _Params}|_] = Contacts ->
ContactsWithExpires = make_contacts_with_expires(Contacts, Expires),
+ ContactsHaveManyRegID = contacts_have_many_reg_id(Contacts),
Expires1 = lists:max([E || {_, E} <- ContactsWithExpires]),
MinExpires = min_expires(),
if Expires1 > 0, Expires1 < MinExpires ->
@@ -81,19 +86,31 @@ request(#sip{hdrs = Hdrs} = Req, SIPSock) ->
Req, #sip{type = response,
status = 423,
hdrs = [{'min-expires', MinExpires}]});
+ ContactsHaveManyRegID ->
+ mod_sip:make_response(
+ Req, #sip{type = response, status = 400,
+ reason = <<"Multiple 'reg-id' parameter">>});
true ->
case register_session(US, SIPSock, CallID, CSeq,
+ IsOutboundSupported,
ContactsWithExpires) of
{ok, Res} ->
?INFO_MSG("~s SIP session for user ~s@~s from ~s",
[Res, LUser, LServer,
inet_parse:ntoa(PeerIP)]),
Cs = prepare_contacts_to_send(ContactsWithExpires),
+ Require = case need_ob_hdrs(
+ Contacts, IsOutboundSupported) of
+ true -> [{'require', [<<"outbound">>]},
+ {'flow-timer',
+ get_flow_timeout(LServer, SIPSock)}];
+ false -> []
+ end,
mod_sip:make_response(
Req,
#sip{type = response,
status = 200,
- hdrs = [{'contact', Cs}]});
+ hdrs = [{'contact', Cs}|Require]});
{error, Why} ->
{Status, Reason} = make_status(Why),
mod_sip:make_response(
@@ -104,12 +121,12 @@ request(#sip{hdrs = Hdrs} = Req, SIPSock) ->
end;
[] ->
case mnesia:dirty_read(sip_session, US) of
- [#sip_session{bindings = Bindings}] ->
+ [_|_] = Sessions ->
ContactsWithExpires =
lists:map(
- fun(#binding{contact = Contact, expires = Es}) ->
+ fun(#sip_session{contact = Contact, expires = Es}) ->
{Contact, Es}
- end, Bindings),
+ end, Sessions),
Cs = prepare_contacts_to_send(ContactsWithExpires),
mod_sip:make_response(
Req, #sip{type = response, status = 200,
@@ -127,32 +144,42 @@ request(#sip{hdrs = Hdrs} = Req, SIPSock) ->
find_sockets(U, S) ->
case mnesia:dirty_read(sip_session, {U, S}) of
- [#sip_session{bindings = Bindings}] ->
+ [_|_] = Sessions ->
lists:map(
- fun(#binding{contact = {_, URI, _},
+ fun(#sip_session{contact = {_, URI, _},
socket = Socket}) ->
{Socket, URI}
- end, Bindings);
+ end, Sessions);
[] ->
[]
end.
+ping(SIPSocket) ->
+ call({ping, SIPSocket}).
+
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
+ update_table(),
mnesia:create_table(sip_session,
[{ram_copies, [node()]},
+ {type, bag},
{attributes, record_info(fields, sip_session)}]),
+ mnesia:add_table_index(sip_session, conn_mref),
+ mnesia:add_table_index(sip_session, socket),
mnesia:add_table_copy(sip_session, node(), ram_copies),
{ok, #state{}}.
-handle_call({write, Session}, _From, State) ->
- Res = write_session(Session),
+handle_call({write, Sessions, Supported}, _From, State) ->
+ Res = write_session(Sessions, Supported),
{reply, Res, State};
handle_call({delete, US, CallID, CSeq}, _From, State) ->
Res = delete_session(US, CallID, CSeq),
{reply, Res, State};
+handle_call({ping, SIPSocket}, _From, State) ->
+ Res = process_ping(SIPSocket),
+ {reply, Res, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
@@ -160,8 +187,8 @@ handle_call(_Request, _From, State) ->
handle_cast(_Msg, State) ->
{noreply, State}.
-handle_info({write, Session}, State) ->
- write_session(Session),
+handle_info({write, Sessions, Supported}, State) ->
+ write_session(Sessions, Supported),
{noreply, State};
handle_info({delete, US, CallID, CSeq}, State) ->
delete_session(US, CallID, CSeq),
@@ -169,6 +196,14 @@ handle_info({delete, US, CallID, CSeq}, State) ->
handle_info({timeout, TRef, US}, State) ->
delete_expired_session(US, TRef),
{noreply, State};
+handle_info({'DOWN', MRef, process, _Pid, _Reason}, State) ->
+ case mnesia:dirty_index_read(sip_session, MRef, #sip_session.conn_mref) of
+ [Session] ->
+ mnesia:dirty_delete_object(Session);
+ _ ->
+ ok
+ end,
+ {noreply, State};
handle_info(_Info, State) ->
?ERROR_MSG("got unexpected info: ~p", [_Info]),
{noreply, State}.
@@ -182,108 +217,95 @@ code_change(_OldVsn, State, _Extra) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
-register_session(US, SIPSocket, CallID, CSeq, ContactsWithExpires) ->
- Bindings = lists:map(
+register_session(US, SIPSocket, CallID, CSeq, IsOutboundSupported,
+ ContactsWithExpires) ->
+ Sessions = lists:map(
fun({Contact, Expires}) ->
- #binding{socket = SIPSocket,
- call_id = CallID,
- cseq = CSeq,
- timestamp = now(),
- contact = Contact,
- expires = Expires}
+ #sip_session{us = US,
+ socket = SIPSocket,
+ call_id = CallID,
+ cseq = CSeq,
+ timestamp = now(),
+ contact = Contact,
+ expires = Expires}
end, ContactsWithExpires),
- Session = #sip_session{us = US, bindings = Bindings},
- call({write, Session}).
+ Msg = {write, Sessions, IsOutboundSupported},
+ call(Msg).
unregister_session(US, CallID, CSeq) ->
Msg = {delete, US, CallID, CSeq},
call(Msg).
-write_session(#sip_session{us = {U, S} = US, bindings = NewBindings}) ->
- PrevBindings = case mnesia:dirty_read(sip_session, US) of
- [#sip_session{bindings = PrevBindings1}] ->
- PrevBindings1;
- [] ->
- []
- end,
+write_session([#sip_session{us = {U, S} = US}|_] = NewSessions,
+ IsOutboundSupported) ->
+ PrevSessions = mnesia:dirty_read(sip_session, US),
Res = lists:foldl(
fun(_, {error, _} = Err) ->
Err;
- (#binding{call_id = CallID,
- expires = Expires,
- cseq = CSeq} = Binding, {Add, Keep, Del}) ->
- case find_binding(Binding, PrevBindings) of
- {ok, #binding{call_id = CallID, cseq = PrevCSeq}}
+ (#sip_session{call_id = CallID,
+ expires = Expires,
+ cseq = CSeq} = Session, {Add, Del}) ->
+ case find_session(Session, PrevSessions,
+ IsOutboundSupported) of
+ {ok, normal, #sip_session{call_id = CallID,
+ cseq = PrevCSeq}}
when PrevCSeq > CSeq ->
{error, cseq_out_of_order};
- {ok, PrevBinding} when Expires == 0 ->
- {Add, Keep -- [PrevBinding], [PrevBinding|Del]};
- {ok, PrevBinding} ->
- {[Binding|Add], Keep -- [PrevBinding], Del};
+ {ok, _Type, PrevSession} when Expires == 0 ->
+ {Add, [PrevSession|Del]};
+ {ok, _Type, PrevSession} ->
+ {[Session|Add], [PrevSession|Del]};
{error, notfound} when Expires == 0 ->
{error, notfound};
{error, notfound} ->
- {[Binding|Add], Keep, Del}
+ {[Session|Add], Del}
end
- end, {[], PrevBindings, []}, NewBindings),
+ end, {[], []}, NewSessions),
MaxSessions = ejabberd_sm:get_max_user_sessions(U, S),
case Res of
{error, Why} ->
{error, Why};
- {AddBindings, KeepBindings, DelBindings} ->
+ {AddSessions, DelSessions} ->
MaxSessions = ejabberd_sm:get_max_user_sessions(U, S),
- AllBindings = AddBindings ++ KeepBindings,
- if length(AllBindings) > MaxSessions ->
+ AllSessions = AddSessions ++ PrevSessions -- DelSessions,
+ if length(AllSessions) > MaxSessions ->
{error, too_many_sessions};
true ->
+ lists:foreach(fun delete_session/1, DelSessions),
lists:foreach(
- fun(#binding{tref = TRef}) ->
- erlang:cancel_timer(TRef)
- end, DelBindings),
- AddBindings1 = lists:map(
- fun(#binding{tref = TRef,
- expires = Expires} = Binding) ->
- erlang:cancel_timer(TRef),
- NewTRef = erlang:start_timer(
- Expires * 1000, self(), US),
- Binding#binding{tref = NewTRef}
- end, AddBindings),
- AllBindings1 = AddBindings1 ++ KeepBindings,
- case AllBindings1 of
- [] ->
- mnesia:dirty_delete(sip_session, US),
+ fun(Session) ->
+ NewSession = set_monitor_and_timer(
+ Session, IsOutboundSupported),
+ mnesia:dirty_write(NewSession)
+ end, AddSessions),
+ case {AllSessions, AddSessions} of
+ {[], _} ->
+ {ok, unregister};
+ {_, []} ->
{ok, unregister};
_ ->
- mnesia:dirty_write(
- #sip_session{us = US, bindings = AllBindings1}),
- if length(DelBindings) == length(NewBindings) ->
- {ok, unregister};
- true ->
- {ok, register}
- end
+ {ok, register}
end
end
end.
delete_session(US, CallID, CSeq) ->
case mnesia:dirty_read(sip_session, US) of
- [#sip_session{bindings = Bindings}] ->
+ [_|_] = Sessions ->
case lists:all(
- fun(B) when B#binding.call_id == CallID,
- B#binding.cseq > CSeq ->
+ fun(S) when S#sip_session.call_id == CallID,
+ S#sip_session.cseq > CSeq ->
false;
(_) ->
true
- end, Bindings) of
+ end, Sessions) of
true ->
ContactsWithExpires =
lists:map(
- fun(#binding{contact = Contact,
- tref = TRef}) ->
- erlang:cancel_timer(TRef),
+ fun(#sip_session{contact = Contact} = Session) ->
+ delete_session(Session),
{Contact, 0}
- end, Bindings),
- mnesia:dirty_delete(sip_session, US),
+ end, Sessions),
{ok, ContactsWithExpires};
false ->
{error, cseq_out_of_order}
@@ -294,20 +316,15 @@ delete_session(US, CallID, CSeq) ->
delete_expired_session(US, TRef) ->
case mnesia:dirty_read(sip_session, US) of
- [#sip_session{bindings = Bindings}] ->
- case lists:filter(
- fun(#binding{tref = TRef1}) when TRef1 == TRef ->
- false;
- (_) ->
- true
- end, Bindings) of
- [] ->
- mnesia:dirty_delete(sip_session, US);
- NewBindings ->
- mnesia:dirty_write(sip_session,
- #sip_session{us = US,
- bindings = NewBindings})
- end;
+ [_|_] = Sessions ->
+ lists:foreach(
+ fun(#sip_session{reg_tref = T1,
+ flow_tref = T2} = Session)
+ when T1 == TRef; T2 == TRef ->
+ delete_session(Session);
+ (_) ->
+ ok
+ end, Sessions);
[] ->
ok
end.
@@ -355,15 +372,55 @@ prepare_contacts_to_send(ContactsWithExpires) ->
{Name, URI, Params1}
end, ContactsWithExpires).
-find_binding(#binding{contact = {_, URI1, _}} = OrigBinding,
- [#binding{contact = {_, URI2, _}} = Binding|Bindings]) ->
+contacts_have_many_reg_id(Contacts) ->
+ Sum = lists:foldl(
+ fun({_Name, _URI, Params}, Acc) ->
+ case get_ob_params(Params) of
+ error ->
+ Acc;
+ {_, _} ->
+ Acc + 1
+ end
+ end, 0, Contacts),
+ if Sum > 1 ->
+ true;
+ true ->
+ false
+ end.
+
+find_session(#sip_session{contact = {_, URI, Params}}, Sessions,
+ IsOutboundSupported) ->
+ if IsOutboundSupported ->
+ case get_ob_params(Params) of
+ {InstanceID, RegID} ->
+ find_session_by_ob({InstanceID, RegID}, Sessions);
+ error ->
+ find_session_by_uri(URI, Sessions)
+ end;
+ true ->
+ find_session_by_uri(URI, Sessions)
+ end.
+
+find_session_by_ob({InstanceID, RegID},
+ [#sip_session{contact = {_, _, Params}} = Session|Sessions]) ->
+ case get_ob_params(Params) of
+ {InstanceID, RegID} ->
+ {ok, flow, Session};
+ _ ->
+ find_session_by_ob({InstanceID, RegID}, Sessions)
+ end;
+find_session_by_ob(_, []) ->
+ {error, notfound}.
+
+find_session_by_uri(URI1,
+ [#sip_session{contact = {_, URI2, _}} = Session|Sessions]) ->
case cmp_uri(URI1, URI2) of
true ->
- {ok, Binding};
+ {ok, normal, Session};
false ->
- find_binding(OrigBinding, Bindings)
+ find_session_by_uri(URI1, Sessions)
end;
-find_binding(_, []) ->
+find_session_by_uri(_, []) ->
{error, notfound}.
%% TODO: this is *totally* wrong.
@@ -384,3 +441,107 @@ make_status(too_many_sessions) ->
{503, <<"Too Many Registered Sessions">>};
make_status(_) ->
{500, esip:reason(500)}.
+
+get_ob_params(Params) ->
+ case esip:get_param(<<"+sip.instance">>, Params) of
+ <<>> ->
+ error;
+ InstanceID ->
+ case to_integer(esip:get_param(<<"reg-id">>, Params),
+ 0, (1 bsl 32)-1) of
+ {ok, RegID} ->
+ {InstanceID, RegID};
+ error ->
+ error
+ end
+ end.
+
+need_ob_hdrs(_Contacts, _IsOutboundSupported = false) ->
+ false;
+need_ob_hdrs(Contacts, _IsOutboundSupported = true) ->
+ lists:any(
+ fun({_Name, _URI, Params}) ->
+ case get_ob_params(Params) of
+ error -> false;
+ {_, _} -> true
+ end
+ end, Contacts).
+
+get_flow_timeout(LServer, #sip_socket{type = Type}) ->
+ {Option, Default} =
+ case Type of
+ udp -> {flow_timeout_datagram, ?FLOW_TIMEOUT_DATAGRAM};
+ _ -> {flow_timeout_stream, ?FLOW_TIMEOUT_STREAM}
+ end,
+ gen_mod:get_module_opt(
+ LServer, mod_sip, Option,
+ fun(I) when is_integer(I), I>0 -> I end,
+ Default).
+
+update_table() ->
+ Fields = record_info(fields, sip_session),
+ case catch mnesia:table_info(sip_session, attributes) of
+ Fields ->
+ ok;
+ [_|_] ->
+ mnesia:delete_table(sip_session);
+ {'EXIT', _} ->
+ ok
+ end.
+
+set_monitor_and_timer(#sip_session{socket = #sip_socket{type = Type,
+ pid = Pid} = SIPSock,
+ conn_mref = MRef,
+ expires = Expires,
+ us = {_, LServer},
+ contact = {_, _, Params}} = Session,
+ IsOutboundSupported) ->
+ RegTRef = set_timer(Session, Expires),
+ Session1 = Session#sip_session{reg_tref = RegTRef},
+ if IsOutboundSupported ->
+ case get_ob_params(Params) of
+ error ->
+ Session1;
+ {_, _} ->
+ FlowTimeout = get_flow_timeout(LServer, SIPSock),
+ FlowTRef = set_timer(Session1, FlowTimeout),
+ NewMRef = if Type == udp -> MRef;
+ true -> erlang:monitor(process, Pid)
+ end,
+ Session1#sip_session{conn_mref = NewMRef,
+ flow_tref = FlowTRef}
+ end;
+ true ->
+ Session1
+ end.
+
+set_timer(#sip_session{us = US}, Timeout) ->
+ erlang:start_timer(Timeout * 1000, self(), US).
+
+delete_session(#sip_session{reg_tref = RegTRef,
+ flow_tref = FlowTRef,
+ conn_mref = MRef} = Session) ->
+ erlang:cancel_timer(RegTRef),
+ catch erlang:cancel_timer(FlowTRef),
+ catch erlang:demonitor(MRef, [flush]),
+ mnesia:dirty_delete_object(Session).
+
+process_ping(SIPSocket) ->
+ ErrResponse = if SIPSocket#sip_socket.type == udp -> error;
+ true -> drop
+ end,
+ Sessions = mnesia:dirty_index_read(
+ sip_session, SIPSocket, #sip_session.socket),
+ lists:foldl(
+ fun(#sip_session{flow_tref = TRef,
+ us = {_, LServer}} = Session, _)
+ when TRef /= undefined ->
+ erlang:cancel_timer(TRef),
+ mnesia:dirty_delete_object(Session),
+ Timeout = get_flow_timeout(LServer, SIPSocket),
+ NewTRef = set_timer(Session, Timeout),
+ mnesia:dirty_write(
+ Session#sip_session{flow_tref = NewTRef});
+ (_, Acc) ->
+ Acc
+ end, ErrResponse, Sessions).