./ct_report/coverage/mod_jingle_sip.COVER.html

1 %% @doc Enables Jingle to SIP and SIP to Jingle translator.
2 %% @author Michal Piotrowski <michal.piotrowski@erlang-solutions.com>
3 %%
4 %%==============================================================================
5 %% Copyright 2018 Erlang Solutions Ltd.
6 %%
7 %% Licensed under the Apache License, Version 2.0 (the "License");
8 %% you may not use this file except in compliance with the License.
9 %% You may obtain a copy of the License at
10 %%
11 %% http://www.apache.org/licenses/LICENSE-2.0
12 %%
13 %% Unless required by applicable law or agreed to in writing, software
14 %% distributed under the License is distributed on an "AS IS" BASIS,
15 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 %% See the License for the specific language governing permissions and
17 %% limitations under the License.
18 %%==============================================================================
19 -module(mod_jingle_sip).
20
21 -behaviour(gen_mod).
22 -behaviour(mongoose_module_metrics).
23
24 -include("jlib.hrl").
25 -include("mongoose.hrl").
26 -include("mongoose_config_spec.hrl").
27 -include_lib("nklib/include/nklib.hrl").
28 -include_lib("nksip/include/nksip.hrl").
29 -include_lib("nksip/include/nksip_call.hrl").
30
31 -define(SERVICE, "mim_sip").
32
33 %% gen_mod callbacks
34 -export([start/2, stop/1, config_spec/0]).
35
36 -export([intercept_jingle_stanza/2]).
37
38 -export([content_to_nksip_media/1]).
39
40 -ignore_xref([content_to_nksip_media/1, intercept_jingle_stanza/2]).
41
42 %% this is because nksip has wrong type specs
43 -dialyzer({nowarn_function, [translate_to_sip/3,
44 get_proxy_uri/1,
45 prepare_initial_sdp/2,
46 jingle_content_to_media/1,
47 content_to_nksip_media/1]}).
48
49 %%--------------------------------------------------------------------
50 %% gen_mod callbacks
51 %%--------------------------------------------------------------------
52 %%
53
54 -spec start(jid:server(), list()) -> ok.
55 start(Host, Opts) ->
56
:-(
start_nksip_service_or_error(Opts),
57
:-(
mod_jingle_sip_backend:init(Host, Opts),
58
:-(
ejabberd_hooks:add(hooks(Host)),
59
:-(
ok.
60
61 start_nksip_service_or_error(Opts) ->
62
:-(
{ok, _} = application:ensure_all_started(nksip),
63
:-(
ListenPort = gen_mod:get_opt(listen_port, Opts, 5600),
64
:-(
NkSipBasicOpts = #{sip_listen => "sip:all:" ++ integer_to_list(ListenPort),
65 callback => jingle_sip_callbacks,
66 plugins => [nksip_outbound, nksip_100rel]},
67
:-(
NkSipOpts = maybe_add_udp_max_size(NkSipBasicOpts, Opts),
68
:-(
case nksip:start(?SERVICE, NkSipOpts) of
69 {ok, _SrvID} ->
70
:-(
ok;
71 {error, already_started} ->
72
:-(
ok;
73 Other ->
74
:-(
erlang:error(Other)
75 end.
76
77 maybe_add_udp_max_size(NkSipOpts, Opts) ->
78
:-(
case gen_mod:get_opt(udp_max_size, Opts, undefined) of
79 undefined ->
80
:-(
NkSipOpts;
81 Size ->
82
:-(
NkSipOpts#{sip_udp_max_size => Size}
83 end.
84
85 -spec stop(jid:server()) -> ok.
86 stop(Host) ->
87
:-(
ejabberd_hooks:delete(hooks(Host)),
88
:-(
ok.
89
90 -spec config_spec() -> mongoose_config_spec:config_section().
91 config_spec() ->
92 146 #section{
93 items = #{<<"proxy_host">> => #option{type = string,
94 validate = network_address},
95 <<"proxy_port">> => #option{type = integer,
96 validate = port},
97 <<"listen_port">> => #option{type = integer,
98 validate = port},
99 <<"local_host">> => #option{type = string,
100 validate = network_address},
101 <<"sdp_origin">> => #option{type = string,
102 validate = ip_address}
103 }
104 }.
105 hooks(Host) ->
106
:-(
[{c2s_preprocessing_hook, Host, ?MODULE, intercept_jingle_stanza, 75}].
107
108 intercept_jingle_stanza(Acc, _C2SState) ->
109
:-(
case mongoose_acc:get(hook, result, undefined, Acc) of
110 drop ->
111
:-(
Acc;
112 _ ->
113
:-(
maybe_iq_stanza(Acc)
114 end.
115
116 maybe_iq_stanza(Acc) ->
117
:-(
case mongoose_acc:stanza_name(Acc) of
118 <<"iq">> ->
119
:-(
maybe_iq_to_other_user(Acc);
120 _ ->
121
:-(
Acc
122 end.
123
124 maybe_iq_to_other_user(Acc) ->
125
:-(
#jid{luser = StanzaTo} = mongoose_acc:to_jid(Acc),
126
:-(
#jid{luser = LUser} = mongoose_acc:get(c2s, origin_jid, Acc),
127
:-(
case LUser of
128 StanzaTo ->
129
:-(
QueryInfo = jlib:iq_query_info(mongoose_acc:element(Acc)),
130
:-(
maybe_jingle_get_stanza_to_self(QueryInfo, Acc);
131 _ ->
132
:-(
QueryInfo = jlib:iq_query_info(mongoose_acc:element(Acc)),
133
:-(
maybe_jingle_stanza(QueryInfo, Acc)
134 end.
135
136 maybe_jingle_stanza(#iq{xmlns = ?JINGLE_NS, sub_el = Jingle, type = set} = IQ, Acc) ->
137
:-(
JingleAction = exml_query:attr(Jingle, <<"action">>),
138
:-(
From = mongoose_acc:from_jid(Acc),
139
:-(
To = mongoose_acc:to_jid(Acc),
140
:-(
maybe_translate_to_sip(JingleAction, From, To, IQ, Acc);
141 maybe_jingle_stanza(_, Acc) ->
142
:-(
Acc.
143
144 maybe_jingle_get_stanza_to_self(#iq{xmlns = ?JINGLE_NS, sub_el = Jingle, type = get} = IQ, Acc) ->
145
:-(
JingleAction = exml_query:attr(Jingle, <<"action">>),
146
:-(
case JingleAction of
147 <<"existing-session-initiate">> ->
148
:-(
resend_session_initiate(IQ, Acc),
149
:-(
mongoose_acc:set(hook, result, drop, Acc);
150 _ ->
151
:-(
Acc
152 end;
153 maybe_jingle_get_stanza_to_self(_, Acc) ->
154
:-(
Acc.
155
156 maybe_translate_to_sip(JingleAction, From, To, IQ, Acc)
157 when JingleAction =:= <<"session-initiate">>;
158 JingleAction =:= <<"session-accept">>;
159 JingleAction =:= <<"session-terminate">>;
160 JingleAction =:= <<"source-remove">>;
161 JingleAction =:= <<"source-add">>;
162 JingleAction =:= <<"source-update">>;
163 JingleAction =:= <<"transport-info">> ->
164
:-(
#iq{sub_el = Jingle} = IQ,
165
:-(
try
166
:-(
Result = translate_to_sip(JingleAction, Jingle, Acc),
167
:-(
route_result(Result, From, To, IQ)
168 catch Class:Error:StackTrace ->
169
:-(
ejabberd_router:route_error_reply(To, From, Acc, mongoose_xmpp_errors:internal_server_error()),
170
:-(
?LOG_ERROR(#{what => sip_translate_failed, acc => Acc,
171
:-(
class => Class, reason => Error, stacktrace => StackTrace})
172 end,
173
:-(
mongoose_acc:set(hook, result, drop, Acc);
174 maybe_translate_to_sip(JingleAction, _, _, _, Acc) ->
175
:-(
?LOG_WARNING(#{what => sip_unknown_action,
176 text => <<"Forwarding unknown action to SIP">>,
177
:-(
jingle_action => JingleAction, acc => Acc}),
178
:-(
Acc.
179
180 route_result(ok, From, To, IQ) ->
181
:-(
route_ok_result(From, To, IQ);
182 route_result({ok, _}, From, To, IQ) ->
183
:-(
route_ok_result(From, To, IQ);
184 route_result({ok, _, _}, From, To, IQ) ->
185
:-(
route_ok_result(From, To, IQ);
186 route_result({error, item_not_found}, From, To, IQ) ->
187
:-(
Error = mongoose_xmpp_errors:item_not_found(),
188
:-(
route_error_reply(From, To, IQ, Error);
189 route_result(Other, From, To, IQ) ->
190
:-(
?LOG_WARNING(#{what => sip_unknown_result, reason => Other, iq => IQ}),
191
:-(
Error = mongoose_xmpp_errors:internal_server_error(),
192
:-(
route_error_reply(From, To, IQ, Error).
193
194 route_error_reply(From, To, IQ, Error) ->
195
:-(
IQResult = IQ#iq{type = error, sub_el = [Error]},
196
:-(
Packet = jlib:replace_from_to(From, To, jlib:iq_to_xml(IQResult)),
197
:-(
ejabberd_router:route(To, From, Packet).
198
199 route_ok_result(From, To, IQ) ->
200
:-(
IQResult = IQ#iq{type = result, sub_el = []},
201
:-(
Packet = jlib:replace_from_to(From, To, jlib:iq_to_xml(IQResult)),
202
:-(
ejabberd_router:route(To, From, Packet).
203
204 resend_session_initiate(#iq{sub_el = Jingle} = IQ, Acc) ->
205
:-(
From = mongoose_acc:from_jid(Acc),
206
:-(
To = mongoose_acc:to_jid(Acc),
207
:-(
SID = exml_query:attr(Jingle, <<"sid">>),
208
:-(
case mod_jingle_sip_backend:get_session_info(SID, From) of
209 {ok, Session} ->
210
:-(
maybe_resend_session_initiate(From, To, IQ, Acc, Session);
211 _ ->
212
:-(
ejabberd_router:route_error_reply(To, From, Acc, mongoose_xmpp_errors:item_not_found())
213 end.
214
215 translate_to_sip(<<"session-initiate">>, Jingle, Acc) ->
216
:-(
SID = exml_query:attr(Jingle, <<"sid">>),
217
:-(
#jid{luser = ToUser} = ToJID = jingle_sip_helper:maybe_rewrite_to_phone(Acc),
218
:-(
#jid{luser = FromUser} = FromJID = mongoose_acc:from_jid(Acc),
219
:-(
From = jid:to_binary(jid:to_lus(FromJID)),
220
:-(
To = jid:to_binary(jid:to_lus(ToJID)),
221
:-(
LServer = mongoose_acc:lserver(Acc),
222
:-(
SDP = prepare_initial_sdp(LServer, Jingle),
223
:-(
ProxyURI = get_proxy_uri(LServer),
224
:-(
RequestURI = list_to_binary(["sip:", ToUser, "@", ProxyURI]),
225
:-(
ToHeader = <<ToUser/binary, " <sip:",To/binary, ">">>,
226
:-(
LocalHost = gen_mod:get_module_opt(LServer, ?MODULE, local_host, "localhost"),
227
228
:-(
{async, Handle} = nksip_uac:invite(?SERVICE, RequestURI,
229 [%% Request options
230 {to, ToHeader},
231 {from, <<FromUser/binary, " <sip:", From/binary, ">">>},
232 {call_id, SID},
233 {body, SDP},
234 {local_host, LocalHost},
235 auto_2xx_ack,
236 %% Internal options
237 async,
238 {callback, fun jingle_sip_callbacks:invite_resp_callback/1}]),
239
:-(
Result = mod_jingle_sip_backend:set_outgoing_request(SID, Handle, FromJID, ToJID),
240
:-(
{_, SrvId, DialogId, _CallId} = nksip_sipmsg:parse_handle(Handle),
241
:-(
?LOG_INFO(#{what => sip_session_start,
242 text => <<"Start SIP session with set_outgoing_request call">>,
243 jingle_action => 'session-initiate', result => Result,
244 from_jid => From, to_jid => To,
245
:-(
call_id => SID, server_id => SrvId, dialog_id => DialogId}),
246
:-(
{ok, Handle};
247 translate_to_sip(<<"session-accept">>, Jingle, Acc) ->
248
:-(
LServer = mongoose_acc:lserver(Acc),
249
:-(
SID = exml_query:attr(Jingle, <<"sid">>),
250
:-(
case mod_jingle_sip_backend:get_incoming_request(SID, mongoose_acc:get(c2s, origin_jid, Acc)) of
251 {ok, ReqID} ->
252
:-(
try_to_accept_session(ReqID, Jingle, Acc, LServer, SID);
253 _ ->
254
:-(
{error, item_not_found}
255 end;
256 translate_to_sip(<<"source-remove">> = Name, Jingle, Acc) ->
257
:-(
translate_source_change_to_sip(Name, Jingle, Acc);
258 translate_to_sip(<<"source-add">> = Name, Jingle, Acc) ->
259
:-(
translate_source_change_to_sip(Name, Jingle, Acc);
260 translate_to_sip(<<"source-update">> = Name, Jingle, Acc) ->
261
:-(
translate_source_change_to_sip(Name, Jingle, Acc);
262 translate_to_sip(<<"transport-info">>, Jingle, Acc) ->
263
:-(
SID = exml_query:attr(Jingle, <<"sid">>),
264
:-(
SDP = make_sdp_for_ice_candidate(Jingle),
265
:-(
case mod_jingle_sip_backend:get_outgoing_handle(SID, mongoose_acc:get(c2s, origin_jid, Acc)) of
266 {ok, undefined} ->
267
:-(
?LOG_ERROR(#{what => sip_missing_dialog, sid => SID, acc => Acc}),
268
:-(
{error, item_not_found};
269 {ok, Handle} ->
270
:-(
nksip_uac:info(Handle, [{content_type, <<"application/sdp">>},
271 {body, SDP}]);
272 _ ->
273
:-(
?LOG_ERROR(#{what => missing_sip_session, sid => SID, acc => Acc}),
274
:-(
{error, item_not_found}
275 end;
276 translate_to_sip(<<"session-terminate">>, Jingle, Acc) ->
277
:-(
SID = exml_query:attr(Jingle, <<"sid">>),
278
:-(
ToJID = jingle_sip_helper:maybe_rewrite_to_phone(Acc),
279
:-(
From = mongoose_acc:get(c2s, origin_jid, Acc),
280
:-(
FromLUS = jid:to_lus(From),
281
:-(
ToLUS = jid:to_lus(ToJID),
282
:-(
case mod_jingle_sip_backend:get_session_info(SID, From) of
283 {ok, Session} ->
284
:-(
try_to_terminate_the_session(FromLUS, ToLUS, Session);
285 _ ->
286
:-(
{error, item_not_found}
287 end.
288
289 translate_source_change_to_sip(ActionName, Jingle, Acc) ->
290
:-(
SID = exml_query:attr(Jingle, <<"sid">>),
291
:-(
LServer = mongoose_acc:lserver(Acc),
292
:-(
#sdp{attributes = SDPAttrs} = RawSDP = prepare_initial_sdp(LServer, Jingle),
293
294
:-(
SDPAttrsWithActionName = [{<<"jingle-action">>, [ActionName]}
295 | SDPAttrs],
296
:-(
SDP = RawSDP#sdp{attributes = SDPAttrsWithActionName},
297
298
:-(
case mod_jingle_sip_backend:get_outgoing_handle(SID, mongoose_acc:get(c2s, origin_jid, Acc)) of
299 {ok, undefined} ->
300
:-(
?LOG_ERROR(#{what => sip_missing_dialod, sid => SID, acc => Acc}),
301
:-(
{error, item_not_found};
302 {ok, Handle} ->
303
:-(
nksip_uac:invite(Handle, [auto_2xx_ack, {body, SDP}]);
304 _ ->
305
:-(
?LOG_ERROR(#{what => sip_missing_session, sid => SID, acc => Acc}),
306
:-(
{error, item_not_found}
307 end.
308
309 try_to_terminate_the_session(FromLUS, ToLUS, Session) ->
310
:-(
case maps:get(state, Session) of
311 accepted ->
312
:-(
DialogHandle = maps:get(dialog, Session),
313
:-(
Node = maps:get(node, Session),
314
:-(
nksip_uac_bye(Node, DialogHandle, [{to, make_user_header(ToLUS)},
315 {from, make_user_header(FromLUS)}]);
316 _ ->
317
:-(
RequestHandle = maps:get(request, Session),
318
:-(
case maps:get(direction, Session) of
319 out ->
320
:-(
nksip_uac:cancel(RequestHandle, [{to, make_user_header(ToLUS)},
321 {from, make_user_header(FromLUS)}]);
322 in ->
323 %% When reject incoming invite we need to reply with an error code
324
:-(
Node = maps:get(node, Session),
325
:-(
nksip_request_reply(busy, {Node, RequestHandle})
326 end
327 end.
328
329 try_to_accept_session(ReqID, Jingle, Acc, Server, SID) ->
330
:-(
SDP = prepare_initial_sdp(Server, Jingle),
331
:-(
LocalHost = gen_mod:get_module_opt(Server, ?MODULE, local_host, "localhost"),
332
:-(
case nksip_request_reply({ok, [{body, SDP}, {local_host, LocalHost}]}, ReqID) of
333 ok ->
334
:-(
ok = mod_jingle_sip_backend:set_incoming_accepted(SID),
335
:-(
terminate_session_on_other_devices(SID, Acc),
336
:-(
ok;
337 Other ->
338
:-(
Other
339 end.
340
341 terminate_session_on_other_devices(SID, Acc) ->
342
:-(
#jid{lresource = Res} = From = mongoose_acc:from_jid(Acc),
343
:-(
FromBin = jid:to_binary(From),
344
:-(
ReasonEl = #xmlel{name = <<"reason">>,
345 children = [#xmlel{name = <<"cancel">>}]},
346
:-(
JingleEl = jingle_sip_helper:jingle_element(SID, <<"session-terminate">>, [ReasonEl]),
347
:-(
PResources = ejabberd_sm:get_user_present_resources(From),
348
:-(
lists:foreach(
349 fun({_, R}) when R /= Res ->
350
:-(
ToJID = jid:replace_resource(From, R),
351
:-(
IQEl = jingle_sip_helper:jingle_iq(jid:to_binary(ToJID), FromBin, JingleEl),
352
:-(
ejabberd_router:route(From, ToJID, Acc, IQEl);
353 (_) ->
354
:-(
skip
355 end, PResources).
356
357 make_user_header({User, _} = US) ->
358
:-(
JIDBin = jid:to_binary(US),
359
:-(
<<User/binary, " <sip:", JIDBin/binary, ">">>.
360
361
362 get_proxy_uri(Server) ->
363
:-(
ProxyHost = gen_mod:get_module_opt(Server, ?MODULE, proxy_host, "localhost"),
364
:-(
ProxyPort = gen_mod:get_module_opt(Server, ?MODULE, proxy_port, 5060),
365
:-(
Transport = gen_mod:get_module_opt(Server, ?MODULE, transport, "udp"),
366
:-(
PortStr = integer_to_list(ProxyPort),
367
:-(
[ProxyHost, ":", PortStr, ";transport=", Transport].
368
369 make_sdp_for_ice_candidate(#xmlel{children = Children}) ->
370
:-(
Content = parse_content(Children, []),
371
:-(
list_to_binary(Content).
372
373 parse_content([], Acc) ->
374
:-(
Acc;
375 parse_content([#xmlel{name = <<"content">>} = Content | Rest], Acc) ->
376
:-(
SDPCandidate = parse_content(Content),
377
:-(
NewAcc = [SDPCandidate | Acc],
378
:-(
parse_content(Rest, NewAcc).
379
380 parse_content(Content) ->
381
:-(
Name = exml_query:attr(Content, <<"name">>),
382
:-(
MediaLine = [<<"m=">>, Name, <<"9 RTP/AVP 0">>, <<"\r\n">>,
383 <<"a=mid:1\r\n">>],
384
:-(
Transport = jingle_to_sdp:parse_transport_element(exml_query:subelement(Content, <<"transport">>)),
385
:-(
IceParamsLine = [<<"a=ice-pwd:">>, maps:get(pwd, Transport), <<"\r\n">>,
386 <<"a=ice-ufrag:">>, maps:get(ufrag, Transport), <<"\r\n">>],
387
:-(
Candidates = [candidate_to_sdp_line(Candidate) || Candidate <- maps:get(candidates, Transport)],
388
:-(
[IceParamsLine, MediaLine, Candidates].
389
390 candidate_to_sdp_line(Candidate) ->
391
:-(
OptionalArgs = [generation, tcptype, raddr, rport],
392
:-(
ExtraArgs = [map_field_to_candidate_arg(Field, Candidate) || Field <- OptionalArgs],
393
:-(
[<<"a=candidate: ">>,
394 maps:get(foundation, Candidate), " ",
395 maps:get(component, Candidate), " ",
396 maps:get(protocol, Candidate), " ",
397 maps:get(priority, Candidate), " ",
398 maps:get(ip, Candidate), " ",
399 maps:get(port, Candidate), " ",
400 "typ ", maps:get(type, Candidate),
401 ExtraArgs,
402 <<"\r\n">>].
403
404 map_field_to_candidate_arg(Field, Map) ->
405
:-(
case maps:find(Field, Map) of
406 {ok, Value} ->
407
:-(
[" ", atom_to_binary(Field, utf8), " ", Value];
408 _ ->
409
:-(
[]
410 end.
411
412 prepare_initial_sdp(Server, Jingle) ->
413
:-(
Medias = [jingle_content_to_media(Content) ||
414
:-(
Content <- exml_query:subelements(Jingle, <<"content">>)],
415
416
:-(
GroupingAttrs = add_group_attr_from_jingle(Jingle, []),
417
418
:-(
OriginAddress = gen_mod:get_module_opt(Server, ?MODULE, sdp_origin, "127.0.0.1"),
419
:-(
SDP = nksip_sdp:new(OriginAddress, []),
420
:-(
SDP#sdp{medias = Medias,
421 attributes = GroupingAttrs}.
422
423 jingle_content_to_media(ContentEl) ->
424
:-(
Content = jingle_to_sdp:from_media(ContentEl),
425
:-(
content_to_nksip_media(Content).
426
427 content_to_nksip_media(Content) ->
428
:-(
{FMTS, DescAttrs} = description_to_nksip_attrs(maps:get(description, Content)),
429
:-(
TransportAttrs = transport_to_nksip_attrs(maps:get(transport, Content), DescAttrs),
430
:-(
AttrsWithMediaName = add_media_name_attr(maps:get(name, Content), TransportAttrs),
431
:-(
AttrsWithSender = add_sender_attr(maps:get(senders, Content), AttrsWithMediaName),
432
433
:-(
#sdp_m{media = maps:get(media, Content),
434 port = 1024 + rand:uniform(1024),
435 proto = maps:get(protocol, Content),
436 fmt = FMTS,
437 attributes = AttrsWithSender}.
438
439 description_to_nksip_attrs(Desc) ->
440
:-(
AttrsWithSSRC = sources_to_nksip_attrs(maps:get(sources, Desc)),
441
:-(
{FMTS, CodecAttrs} = codecs_to_nksip_attrs(maps:get(codecs, Desc), AttrsWithSSRC),
442
:-(
AttrsWithRTPHeaderExt = add_rtp_header_ext(maps:get(rtphdr_ext, Desc), CodecAttrs),
443
:-(
AttrsWithRTCPMUX = add_rtcp_mux(Desc, AttrsWithRTPHeaderExt),
444
445
:-(
{FMTS, AttrsWithRTCPMUX}.
446
447 sources_to_nksip_attrs(Sources) ->
448
:-(
lists:flatmap(fun source_to_nksip_attr/1, Sources).
449
450 source_to_nksip_attr({ID, KeyValues}) ->
451
:-(
[{<<"ssrc">>, [ID, source_to_nksip_value(Name, Value)]} || {Name, Value} <- KeyValues].
452
453 source_to_nksip_value(Name, Value) ->
454
:-(
<<Name/binary, $:, Value/binary>>.
455
456 codecs_to_nksip_attrs(Codecs, Attrs) ->
457
:-(
codecs_to_nksip_attrs2(Codecs, {[], Attrs}).
458
459 codecs_to_nksip_attrs2([], Acc) ->
460
:-(
Acc;
461 codecs_to_nksip_attrs2([Codec | Rest], {FMTS, Attrs}) ->
462
:-(
ID = maps:get(id, Codec),
463
:-(
NewFMTS = [ID | FMTS],
464
:-(
RTCPFBAttrs = [rtcp_fb_param_to_attr(ID, Param) || Param <- maps:get(rtcp_fb_params, Codec)],
465
:-(
AttrsWithFMTPParams = maybe_add_fmtp_attr(ID, maps:get(params, Codec), RTCPFBAttrs),
466
:-(
RTPMapValue = prepare_rtp_map(maps:get(name, Codec), maps:get(clock_rate, Codec), maps:get(channels, Codec)),
467
:-(
RTPAttr = {<<"rtpmap">>, [ID, RTPMapValue]},
468
:-(
CodecAttrs = [RTPAttr | AttrsWithFMTPParams],
469
470
:-(
codecs_to_nksip_attrs2(Rest, {NewFMTS, lists:append(CodecAttrs, Attrs)}).
471
472 rtcp_fb_param_to_attr(ID, RTCPParam) ->
473
:-(
{<<"rtcp-fb">>, [ID | RTCPParam]}.
474
475 maybe_add_fmtp_attr(_ID, [], RTCPParams) ->
476
:-(
RTCPParams;
477 maybe_add_fmtp_attr(ID, Params, RTCPParams) ->
478
:-(
KV = [basic_parameter_to_fmtp_value(Param) || Param <- Params],
479
:-(
Joined = lists:join($;, KV),
480
:-(
FMTPValue = list_to_binary(Joined),
481
:-(
[{<<"fmtp">>, [ID, FMTPValue]} | RTCPParams].
482
483 basic_parameter_to_fmtp_value({undefined, Value}) ->
484
:-(
Value;
485 basic_parameter_to_fmtp_value({Name, Value}) ->
486
:-(
<<Name/binary, $=, Value/binary>>.
487
488 prepare_rtp_map(Name, ClockRate, <<"1">>) ->
489
:-(
<<Name/binary, $/, ClockRate/binary>>;
490 prepare_rtp_map(Name, ClockRate, Channels) ->
491
:-(
<<Name/binary, $/, ClockRate/binary, $/, Channels/binary>>.
492
493 add_rtp_header_ext(RTPHdrExts, Attrs) ->
494
:-(
RTPHdrextAttrs = [rtp_hdrext_to_sdp_attr(RTPHdrExt) || RTPHdrExt <- RTPHdrExts],
495
:-(
lists:append(RTPHdrextAttrs, Attrs).
496
497 rtp_hdrext_to_sdp_attr({ID, URI, Senders}) ->
498
:-(
{<<"extmap">>, rtp_hdrext_value(ID, URI, Senders)}.
499
500 rtp_hdrext_value(ID, URI, <<"sendrecv">>) ->
501
:-(
[ID, URI];
502 rtp_hdrext_value(ID, URI, Sender) ->
503
:-(
[<<ID/binary, $/, Sender/binary>>, URI].
504
505 add_rtcp_mux(#{rtcp_mux := true}, Attrs) ->
506
:-(
[{<<"rtcp-mux">>, []} | Attrs];
507 add_rtcp_mux(_, Attrs) ->
508
:-(
Attrs.
509
510 transport_to_nksip_attrs(Transport, Attrs) ->
511
:-(
AttrsWithUfrag = maybe_add_ice_ufrag_attr(maps:get(ufrag, Transport), Attrs),
512
:-(
AttrsWithPwd = maybe_add_ice_pwd_attr(maps:get(pwd, Transport), AttrsWithUfrag),
513
:-(
maybe_add_fingerprint(maps:get(fingerprint, Transport), AttrsWithPwd).
514
515 maybe_add_fingerprint({Hash, Setup, Fingerprint}, Attrs) ->
516
:-(
AttrsWithFingerprint = [{<<"fingerprint">>, [Hash, Fingerprint]} | Attrs],
517
:-(
case Setup of
518 undefined ->
519
:-(
AttrsWithFingerprint;
520 _ ->
521
:-(
[{<<"setup">>, [Setup]} | AttrsWithFingerprint]
522 end;
523 maybe_add_fingerprint(_, Attrs) ->
524
:-(
Attrs.
525
526 maybe_add_ice_ufrag_attr(undefined, Attrs) ->
527
:-(
Attrs;
528 maybe_add_ice_ufrag_attr(Ufrag, Attrs) ->
529
:-(
[{<<"ice-ufrag">>, [Ufrag]} | Attrs].
530
531 maybe_add_ice_pwd_attr(undefined, Attrs) ->
532
:-(
Attrs;
533 maybe_add_ice_pwd_attr(Pwd, Attrs) ->
534
:-(
[{<<"ice-pwd">>, [Pwd]} | Attrs].
535
536 add_media_name_attr(Name, Attrs) ->
537
:-(
[{<<"mid">>, [Name]} | Attrs].
538
539 add_sender_attr(Sender, Attrs) ->
540
:-(
[{Sender, []} | Attrs].
541
542 add_group_attr_from_jingle(Jingle, Attrs) ->
543
:-(
case exml_query:subelement(Jingle, <<"group">>) of
544 #xmlel{} = El ->
545
:-(
Attr = make_group_attr(El),
546
:-(
[Attr | Attrs];
547 _ ->
548
:-(
Attrs
549 end.
550
551 make_group_attr(#xmlel{children = Children} = Group) ->
552
:-(
Semantic = exml_query:attr(Group, <<"semantics">>),
553
:-(
Contents = [exml_query:attr(Content, <<"name">>) || Content <- Children],
554
555
:-(
{<<"group">>, [Semantic | Contents]}.
556
557 maybe_resend_session_initiate(From, To, IQ, Acc,
558 #{meta := Meta, from := OriginFrom}) ->
559
:-(
case maps:get(init_stanza, Meta, undefined) of
560 undefined ->
561
:-(
Error = mongoose_xmpp_errors:item_not_found(<<"en">>,
562 <<"no session-initiate for this SID">>),
563
:-(
ejabberd_router:route_error_reply(To, From, Acc, Error);
564 Stanza ->
565
:-(
IQResult = IQ#iq{type = result, sub_el = []},
566
:-(
Packet = jlib:replace_from_to(From, To, jlib:iq_to_xml(IQResult)),
567
:-(
ejabberd_router:route(To, From, Acc, Packet),
568
:-(
OriginFromBin = jid:to_binary(OriginFrom),
569
:-(
IQSet = jingle_sip_helper:jingle_iq(jid:to_binary(From), OriginFromBin,
570 Stanza),
571
:-(
{U, S} = OriginFrom,
572
:-(
OriginJID = jid:make_noprep(U, S, <<>>),
573
:-(
ejabberd_router:route(OriginJID, From, Acc, IQSet)
574 end.
575
576 nksip_request_reply(Reply, {Node, ReqID}) ->
577
:-(
case node() of
578 Node ->
579
:-(
nksip_request:reply(Reply, ReqID);
580 _ ->
581
:-(
rpc:call(Node, nksip_request, reply, [Reply, ReqID], timer:seconds(5))
582 end.
583
584 nksip_uac_bye(Node, DialogHandle, Args) ->
585
:-(
case node() of
586 Node ->
587
:-(
nksip_uac:bye(DialogHandle, Args);
588 _ ->
589
:-(
rpc:call(Node, nksip_uac, bye, [DialogHandle, Args], timer:seconds(5))
590 end.
591
Line Hits Source