6 * Copyright (C) 2010 Jakub Adam <jakub.adam@tieto.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "sipe-common.h"
34 #include "sip-transport.h"
35 #include "sipe-backend.h"
36 #include "sipe-core.h"
37 #include "sipe-core-private.h"
38 #include "sipe-dialog.h"
39 #include "sipe-media.h"
40 #include "sipe-session.h"
41 #include "sipe-utils.h"
45 struct sipe_media_call_private
{
46 struct sipe_media_call
public;
48 /* private part starts here */
49 struct sipe_core_private
*sipe_private
;
50 struct sip_session
*session
;
51 struct sip_dialog
*dialog
;
53 struct sipe_backend_stream
*voice_stream
;
58 struct sipmsg
*invitation
;
59 GList
*remote_candidates
;
63 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
64 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
67 sipe_media_get_callid(struct sipe_media_call_private
*call
)
69 return call
->dialog
->callid
;
72 static void sipe_media_codec_list_free(GList
*codecs
)
74 for (; codecs
; codecs
= g_list_delete_link(codecs
, codecs
))
75 sipe_backend_codec_free(codecs
->data
);
78 static void sipe_media_candidate_list_free(GList
*candidates
)
80 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
))
81 sipe_backend_candidate_free(candidates
->data
);
85 sipe_media_call_free(struct sipe_media_call_private
*call_private
)
88 sipe_backend_media_free(call_private
->public.backend_private
);
89 sipe_media_codec_list_free(call_private
->public.remote_codecs
);
91 if (call_private
->session
)
92 sipe_session_remove(call_private
->sipe_private
,
93 call_private
->session
);
95 sipe_utils_nameval_free(call_private
->sdp_attrs
);
97 if (call_private
->invitation
)
98 sipmsg_free(call_private
->invitation
);
100 sipe_media_candidate_list_free(call_private
->remote_candidates
);
101 g_free(call_private
);
106 sipe_media_parse_codecs(GSList
*sdp_attrs
)
110 GList
*codecs
= NULL
;
112 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "rtpmap", i
++))) {
113 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
115 int id
= atoi(tokens
[0]);
116 gchar
*name
= tokens
[1];
117 int clock_rate
= atoi(tokens
[2]);
118 SipeMediaType type
= SIPE_MEDIA_AUDIO
;
120 struct sipe_backend_codec
*codec
= sipe_backend_codec_new(id
, name
, clock_rate
, type
);
122 // TODO: more secure and effective implementation
125 while((params
= sipe_utils_nameval_find_instance(sdp_attrs
, "fmtp", j
++))) {
126 gchar
**tokens
= g_strsplit_set(params
, " ", 0);
127 gchar
**next
= tokens
+ 1;
129 if (atoi(tokens
[0]) == id
) {
134 if (sscanf(*next
, "%[a-zA-Z0-9]=%s", name
, value
) == 2)
135 sipe_backend_codec_add_optional_parameter(codec
, name
, value
);
144 codecs
= g_list_append(codecs
, codec
);
152 codec_name_compare(struct sipe_backend_codec
*codec1
, struct sipe_backend_codec
*codec2
)
154 gchar
*name1
= sipe_backend_codec_get_name(codec1
);
155 gchar
*name2
= sipe_backend_codec_get_name(codec2
);
157 gint result
= g_strcmp0(name1
, name2
);
166 sipe_media_prune_remote_codecs(GList
*local_codecs
, GList
*remote_codecs
)
168 GList
*remote_codecs_head
= remote_codecs
;
169 GList
*pruned_codecs
= NULL
;
171 while (remote_codecs
) {
172 struct sipe_backend_codec
*c
= remote_codecs
->data
;
174 if (g_list_find_custom(local_codecs
, c
, (GCompareFunc
)codec_name_compare
)) {
175 pruned_codecs
= g_list_append(pruned_codecs
, c
);
176 remote_codecs
->data
= NULL
;
178 remote_codecs
= remote_codecs
->next
;
181 sipe_media_codec_list_free(remote_codecs_head
);
183 return pruned_codecs
;
187 sipe_media_parse_remote_candidates_legacy(gchar
*remote_ip
, guint16 remote_port
)
189 struct sipe_backend_candidate
*candidate
;
190 GList
*candidates
= NULL
;
192 candidate
= sipe_backend_candidate_new("foundation",
194 SIPE_CANDIDATE_TYPE_HOST
,
195 SIPE_NETWORK_PROTOCOL_UDP
,
196 remote_ip
, remote_port
);
197 candidates
= g_list_append(candidates
, candidate
);
199 candidate
= sipe_backend_candidate_new("foundation",
201 SIPE_CANDIDATE_TYPE_HOST
,
202 SIPE_NETWORK_PROTOCOL_UDP
,
203 remote_ip
, remote_port
+ 1);
204 candidates
= g_list_append(candidates
, candidate
);
210 sipe_media_parse_remote_candidates(GSList
*sdp_attrs
)
212 struct sipe_backend_candidate
*candidate
;
213 GList
*candidates
= NULL
;
217 const gchar
* username
= sipe_utils_nameval_find(sdp_attrs
, "ice-ufrag");
218 const gchar
* password
= sipe_utils_nameval_find(sdp_attrs
, "ice-pwd");
220 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "candidate", i
++))) {
223 SipeComponentType component
;
224 SipeNetworkProtocol protocol
;
228 SipeCandidateType type
;
230 tokens
= g_strsplit_set(attr
, " ", 0);
232 foundation
= tokens
[0];
234 switch (atoi(tokens
[1])) {
236 component
= SIPE_COMPONENT_RTP
;
239 component
= SIPE_COMPONENT_RTCP
;
242 component
= SIPE_COMPONENT_NONE
;
245 if (sipe_strequal(tokens
[2], "UDP"))
246 protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
248 // Ignore TCP candidates, at least for now...
249 // Also, if this is ICEv6 candidate list, candidates are dropped here
254 priority
= atoi(tokens
[3]);
256 port
= atoi(tokens
[5]);
258 if (sipe_strequal(tokens
[7], "host"))
259 type
= SIPE_CANDIDATE_TYPE_HOST
;
260 else if (sipe_strequal(tokens
[7], "relay"))
261 type
= SIPE_CANDIDATE_TYPE_RELAY
;
262 else if (sipe_strequal(tokens
[7], "srflx"))
263 type
= SIPE_CANDIDATE_TYPE_SRFLX
;
264 else if (sipe_strequal(tokens
[7], "prflx"))
265 type
= SIPE_CANDIDATE_TYPE_PRFLX
;
271 candidate
= sipe_backend_candidate_new(foundation
, component
,
272 type
, protocol
, ip
, port
);
273 sipe_backend_candidate_set_priority(candidate
, priority
);
274 candidates
= g_list_append(candidates
, candidate
);
280 GList
*it
= candidates
;
282 sipe_backend_candidate_set_username_and_pwd(it
->data
, username
, password
);
291 sipe_media_sdp_codec_ids_format(GList
*codecs
)
293 GString
*result
= g_string_new(NULL
);
296 struct sipe_backend_codec
*c
= codecs
->data
;
298 g_string_append_printf(result
,
300 sipe_backend_codec_get_id(c
));
302 codecs
= codecs
->next
;
305 return g_string_free(result
, FALSE
);
309 sipe_media_sdp_codecs_format(GList
*codecs
)
311 GString
*result
= g_string_new(NULL
);
314 struct sipe_backend_codec
*c
= codecs
->data
;
315 GList
*params
= NULL
;
316 gchar
*name
= sipe_backend_codec_get_name(c
);
318 g_string_append_printf(result
,
319 "a=rtpmap:%d %s/%d\r\n",
320 sipe_backend_codec_get_id(c
),
322 sipe_backend_codec_get_clock_rate(c
));
325 if ((params
= sipe_backend_codec_get_optional_parameters(c
))) {
326 g_string_append_printf(result
,
328 sipe_backend_codec_get_id(c
));
331 struct sipnameval
* par
= params
->data
;
332 g_string_append_printf(result
,
334 par
->name
, par
->value
);
335 params
= params
->next
;
337 g_string_append(result
, "\r\n");
340 codecs
= codecs
->next
;
343 return g_string_free(result
, FALSE
);
347 candidate_compare_by_component_id(struct sipe_backend_candidate
*c1
, struct sipe_backend_candidate
*c2
)
349 return sipe_backend_candidate_get_component_type(c1
)
350 - sipe_backend_candidate_get_component_type(c2
);
354 sipe_media_sdp_candidates_format(struct sipe_media_call_private
*call_private
, guint16
*local_port
)
356 struct sipe_backend_media
*backend_media
= call_private
->public.backend_private
;
357 struct sipe_backend_stream
*voice_stream
= call_private
->voice_stream
;
363 GString
*result
= g_string_new("");
364 guint16 rtcp_port
= 0;
366 // If we have established candidate pairs, send them in SDP response.
367 // Otherwise send all available local candidates.
368 l_candidates
= sipe_backend_media_get_active_local_candidates(backend_media
, voice_stream
);
370 l_candidates
= sipe_backend_get_local_candidates(backend_media
, voice_stream
);
372 // If in legacy mode, just fill local_port variable with local host's RTP
373 // component port and return empty string.
374 if (call_private
->legacy_mode
) {
375 for (cand
= l_candidates
; cand
; cand
= cand
->next
) {
376 struct sipe_backend_candidate
*c
= cand
->data
;
377 SipeCandidateType type
= sipe_backend_candidate_get_type(c
);
378 SipeComponentType component
= sipe_backend_candidate_get_component_type(c
);
380 if (type
== SIPE_CANDIDATE_TYPE_HOST
&& component
== SIPE_COMPONENT_RTP
) {
381 *local_port
= sipe_backend_candidate_get_port(c
);
386 sipe_media_candidate_list_free(l_candidates
);
388 return g_string_free(result
, FALSE
);
391 username
= sipe_backend_candidate_get_username(l_candidates
->data
);
392 password
= sipe_backend_candidate_get_password(l_candidates
->data
);
394 g_string_append_printf(result
,
395 "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
401 for (cand
= l_candidates
; cand
; cand
= cand
->next
) {
402 struct sipe_backend_candidate
*c
= cand
->data
;
405 SipeComponentType component
;
406 const gchar
*protocol
;
408 gchar
*related
= NULL
;
409 gchar
*tmp_foundation
;
412 component
= sipe_backend_candidate_get_component_type(c
);
413 port
= sipe_backend_candidate_get_port(c
);
415 switch (sipe_backend_candidate_get_protocol(c
)) {
416 case SIPE_NETWORK_PROTOCOL_TCP
:
419 case SIPE_NETWORK_PROTOCOL_UDP
:
424 switch (sipe_backend_candidate_get_type(c
)) {
425 case SIPE_CANDIDATE_TYPE_HOST
:
427 if (component
== SIPE_COMPONENT_RTP
)
429 else if (component
== SIPE_COMPONENT_RTCP
)
432 case SIPE_CANDIDATE_TYPE_RELAY
:
435 case SIPE_CANDIDATE_TYPE_SRFLX
:
440 related
= g_strdup_printf("raddr %s rport %d",
441 tmp
= sipe_backend_candidate_get_base_ip(c
),
442 sipe_backend_candidate_get_base_port(c
));
446 case SIPE_CANDIDATE_TYPE_PRFLX
:
450 // TODO: error unknown/unsupported type
454 g_string_append_printf(result
,
455 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
456 tmp_foundation
= sipe_backend_candidate_get_foundation(c
),
459 sipe_backend_candidate_get_priority(c
),
460 tmp_ip
= sipe_backend_candidate_get_ip(c
),
463 related
? related
: "");
465 g_free(tmp_foundation
);
469 r_candidates
= sipe_backend_media_get_active_remote_candidates(backend_media
, voice_stream
);
470 r_candidates
= g_list_sort(r_candidates
, (GCompareFunc
)candidate_compare_by_component_id
);
473 g_string_append(result
, "a=remote-candidates:");
474 for (cand
= r_candidates
; cand
; cand
= cand
->next
) {
475 struct sipe_backend_candidate
*candidate
= cand
->data
;
477 g_string_append_printf(result
,
479 sipe_backend_candidate_get_component_type(candidate
),
480 tmp
= sipe_backend_candidate_get_ip(candidate
),
481 sipe_backend_candidate_get_port(candidate
));
484 g_string_append(result
, "\r\n");
487 sipe_media_candidate_list_free(l_candidates
);
488 sipe_media_candidate_list_free(r_candidates
);
490 if (rtcp_port
!= 0) {
491 g_string_append_printf(result
,
492 "a=maxptime:200\r\na=rtcp:%u\r\n",
496 return g_string_free(result
, FALSE
);
500 sipe_media_create_sdp(struct sipe_media_call_private
*call_private
) {
501 GList
*usable_codecs
= sipe_backend_get_local_codecs(SIPE_MEDIA_CALL
,
502 call_private
->voice_stream
);
505 const char *ip
= sipe_utils_get_suitable_local_ip(-1);
506 guint16 local_port
= 0;
508 gchar
*sdp_codecs
= sipe_media_sdp_codecs_format(usable_codecs
);
509 gchar
*sdp_codec_ids
= sipe_media_sdp_codec_ids_format(usable_codecs
);
510 gchar
*sdp_candidates
= sipe_media_sdp_candidates_format(call_private
, &local_port
);
511 gchar
*inactive
= (call_private
->public.local_on_hold
||
512 call_private
->public.remote_on_hold
) ? "a=inactive\r\n" : "";
514 body
= g_strdup_printf(
516 "o=- 0 0 IN IP4 %s\r\n"
521 "m=audio %d RTP/AVP%s\r\n"
525 "a=encryption:rejected\r\n"
526 ,ip
, ip
, local_port
, sdp_codec_ids
, sdp_candidates
, inactive
, sdp_codecs
);
529 g_free(sdp_codec_ids
);
530 g_free(sdp_candidates
);
532 sipe_media_codec_list_free(usable_codecs
);
538 sipe_invite_call(struct sipe_core_private
*sipe_private
, TransCallback tc
)
543 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
544 struct sip_dialog
*dialog
= call_private
->dialog
;
546 contact
= get_contact(sipe_private
);
547 hdr
= g_strdup_printf(
548 "Supported: ms-early-media\r\n"
549 "Supported: 100rel\r\n"
550 "ms-keep-alive: UAC;hop-hop=yes\r\n"
552 "Content-Type: application/sdp\r\n",
554 (call_private
->public.local_on_hold
|| call_private
->public.remote_on_hold
) ? ";+sip.rendering=\"no\"" : "");
557 body
= sipe_media_create_sdp(call_private
);
559 dialog
->outgoing_invite
= sip_transport_invite(sipe_private
,
570 sipe_media_parse_remote_codecs(struct sipe_media_call_private
*call_private
);
573 sipe_media_parse_sdp_attributes_and_candidates(struct sipe_media_call_private
*call_private
, gchar
*frame
) {
574 gchar
**lines
= g_strsplit(frame
, "\r\n", 0);
575 GSList
*sdp_attrs
= NULL
;
576 gchar
*remote_ip
= NULL
;
577 guint16 remote_port
= 0;
578 GList
*remote_candidates
;
580 gboolean no_error
= TRUE
;
582 for (ptr
= lines
; *ptr
!= NULL
; ++ptr
) {
583 if (g_str_has_prefix(*ptr
, "a=")) {
584 gchar
**parts
= g_strsplit(*ptr
+ 2, ":", 2);
587 sipe_utils_nameval_free(sdp_attrs
);
592 sdp_attrs
= sipe_utils_nameval_add(sdp_attrs
, parts
[0], parts
[1]);
595 } else if (g_str_has_prefix(*ptr
, "o=")) {
596 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 6);
597 remote_ip
= g_strdup(parts
[5]);
599 } else if (g_str_has_prefix(*ptr
, "m=")) {
600 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 3);
601 remote_port
= atoi(parts
[1]);
608 remote_candidates
= sipe_media_parse_remote_candidates(sdp_attrs
);
609 if (!remote_candidates
) {
610 // No a=candidate in SDP message, revert to OC2005 behaviour
611 remote_candidates
= sipe_media_parse_remote_candidates_legacy(remote_ip
, remote_port
);
612 // This seems to be pre-OC2007 R2 UAC
613 call_private
->legacy_mode
= TRUE
;
617 sipe_utils_nameval_free(call_private
->sdp_attrs
);
618 sipe_media_candidate_list_free(call_private
->remote_candidates
);
620 call_private
->sdp_attrs
= sdp_attrs
;
621 call_private
->remote_port
= remote_port
;
622 call_private
->remote_candidates
= remote_candidates
;
624 sipe_utils_nameval_free(sdp_attrs
);
625 sipe_media_candidate_list_free(remote_candidates
);
632 sipe_media_parse_remote_codecs(struct sipe_media_call_private
*call_private
)
634 GList
*local_codecs
= sipe_backend_get_local_codecs(SIPE_MEDIA_CALL
,
635 call_private
->voice_stream
);
636 GList
*remote_codecs
;
638 remote_codecs
= sipe_media_parse_codecs(call_private
->sdp_attrs
);
639 remote_codecs
= sipe_media_prune_remote_codecs(local_codecs
, remote_codecs
);
641 sipe_media_codec_list_free(local_codecs
);
644 sipe_media_codec_list_free(call_private
->public.remote_codecs
);
646 call_private
->public.remote_codecs
= remote_codecs
;
648 if (!sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL
,
649 call_private
->voice_stream
)) {
650 SIPE_DEBUG_ERROR_NOFORMAT("ERROR SET REMOTE CODECS"); // TODO
656 sipe_media_codec_list_free(remote_codecs
);
657 SIPE_DEBUG_ERROR_NOFORMAT("ERROR NO CANDIDATES OR CODECS");
663 static struct sip_dialog
*
664 sipe_media_dialog_init(struct sip_session
* session
, struct sipmsg
*msg
)
666 gchar
*newTag
= gentag();
667 const gchar
*oldHeader
;
669 struct sip_dialog
*dialog
;
671 oldHeader
= sipmsg_find_header(msg
, "To");
672 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
673 sipmsg_remove_header_now(msg
, "To");
674 sipmsg_add_header_now(msg
, "To", newHeader
);
677 dialog
= sipe_dialog_add(session
);
678 dialog
->callid
= g_strdup(session
->callid
);
679 dialog
->with
= parse_from(sipmsg_find_header(msg
, "From"));
680 sipe_dialog_parse(dialog
, msg
, FALSE
);
686 send_response_with_session_description(struct sipe_media_call_private
*call_private
, int code
, gchar
*text
)
688 gchar
*body
= sipe_media_create_sdp(call_private
);
689 sipmsg_add_header(call_private
->invitation
, "Content-Type", "application/sdp");
690 sip_transport_response(call_private
->sipe_private
, call_private
->invitation
, code
, text
, body
);
695 encryption_levels_compatible(struct sipe_media_call_private
*call_private
)
697 const gchar
*encrypion_level
;
699 encrypion_level
= sipe_utils_nameval_find(call_private
->sdp_attrs
,
702 // Decline call if peer requires encryption as we don't support it yet.
703 return !sipe_strequal(encrypion_level
, "required");
707 handle_incompatible_encryption_level(struct sipe_media_call_private
*call_private
)
709 sipmsg_add_header(call_private
->invitation
, "Warning",
710 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
711 sip_transport_response(call_private
->sipe_private
,
712 call_private
->invitation
,
713 488, "Encryption Levels not compatible",
715 sipe_backend_media_reject(call_private
->public.backend_private
, FALSE
);
716 sipe_backend_notify_error(_("Unable to establish a call"),
717 _("Encryption settings of peer are incompatible with ours."));
721 process_invite_call_response(struct sipe_core_private
*sipe_private
,
723 struct transaction
*trans
);
725 static void candidates_prepared_cb(struct sipe_media_call
*call
)
727 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
729 if (sipe_backend_media_is_initiator(call_private
->public.backend_private
,
730 call_private
->voice_stream
)) {
731 sipe_invite_call(call_private
->sipe_private
,
732 process_invite_call_response
);
736 if (!sipe_media_parse_remote_codecs(call_private
)) {
737 g_free(call_private
);
741 if ( !call_private
->legacy_mode
742 && encryption_levels_compatible(SIPE_MEDIA_CALL_PRIVATE
))
743 send_response_with_session_description(call_private
,
744 183, "Session Progress");
747 static void media_connected_cb(SIPE_UNUSED_PARAMETER
struct sipe_media_call_private
*call_private
)
751 static void call_accept_cb(struct sipe_media_call
*call
, gboolean local
)
754 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
756 if (!encryption_levels_compatible(call_private
)) {
757 handle_incompatible_encryption_level(call_private
);
761 send_response_with_session_description(call_private
, 200, "OK");
765 static void call_reject_cb(struct sipe_media_call
*call
, gboolean local
)
767 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
770 sip_transport_response(call_private
->sipe_private
, call_private
->invitation
, 603, "Decline", NULL
);
772 call_private
->sipe_private
->media_call
= NULL
;
773 sipe_media_call_free(call_private
);
777 sipe_media_send_ack(struct sipe_core_private
*sipe_private
, struct sipmsg
*msg
,
778 struct transaction
*trans
);
780 static void call_hold_cb(struct sipe_media_call
*call
, gboolean local
, gboolean state
)
782 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
784 if (local
&& (call_private
->public.local_on_hold
!= state
)) {
785 call_private
->public.local_on_hold
= state
;
786 sipe_invite_call(call_private
->sipe_private
, sipe_media_send_ack
);
787 } else if (call_private
->public.remote_on_hold
!= state
) {
788 call_private
->public.remote_on_hold
= state
;
789 send_response_with_session_description(call_private
, 200, "OK");
793 static void call_hangup_cb(struct sipe_media_call
*call
, gboolean local
)
795 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
798 sip_transport_bye(call_private
->sipe_private
, call_private
->dialog
);
800 call_private
->sipe_private
->media_call
= NULL
;
801 sipe_media_call_free(call_private
);
804 static struct sipe_media_call_private
*
805 sipe_media_call_new(struct sipe_core_private
*sipe_private
,
806 const gchar
* with
, gboolean initiator
)
808 struct sipe_media_call_private
*call_private
= g_new0(struct sipe_media_call_private
, 1);
810 call_private
->sipe_private
= sipe_private
;
811 call_private
->public.backend_private
= sipe_backend_media_new(SIPE_CORE_PUBLIC
,
816 call_private
->legacy_mode
= FALSE
;
817 call_private
->using_nice
= TRUE
;
819 call_private
->public.candidates_prepared_cb
= candidates_prepared_cb
;
820 call_private
->public.media_connected_cb
= media_connected_cb
;
821 call_private
->public.call_accept_cb
= call_accept_cb
;
822 call_private
->public.call_reject_cb
= call_reject_cb
;
823 call_private
->public.call_hold_cb
= call_hold_cb
;
824 call_private
->public.call_hangup_cb
= call_hangup_cb
;
826 call_private
->public.local_on_hold
= FALSE
;
827 call_private
->public.remote_on_hold
= FALSE
;
832 void sipe_media_hangup(struct sipe_core_private
*sipe_private
)
834 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
836 sipe_backend_media_hangup(call_private
->public.backend_private
,
841 sipe_core_media_initiate_call(struct sipe_core_public
*sipe_public
,
844 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
845 struct sipe_media_call_private
*call_private
;
847 if (sipe_private
->media_call
)
850 call_private
= sipe_media_call_new(sipe_private
, with
, TRUE
);
852 call_private
->session
= sipe_session_add_call(sipe_private
, with
);
853 call_private
->dialog
= sipe_dialog_add(call_private
->session
);
854 call_private
->dialog
->callid
= gencallid();
855 call_private
->dialog
->with
= g_strdup(call_private
->session
->with
);
856 call_private
->dialog
->ourtag
= gentag();
858 call_private
->voice_stream
= sipe_backend_media_add_stream(
859 call_private
->public.backend_private
,
862 call_private
->using_nice
,
865 if (!call_private
->voice_stream
) {
866 sipe_backend_notify_error("Error occured",
867 "Error creating media stream");
868 sipe_media_call_free(call_private
);
872 sipe_private
->media_call
= call_private
;
874 // Processing continues in candidates_prepared_cb
879 sipe_media_incoming_invite(struct sipe_core_private
*sipe_private
,
882 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
884 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
885 struct sip_session
*session
;
886 struct sip_dialog
*dialog
;
889 if (sipe_strequal(call_private
->dialog
->callid
, callid
)) {
890 if (call_private
->invitation
)
891 sipmsg_free(call_private
->invitation
);
892 call_private
->invitation
= sipmsg_copy(msg
);
894 sipe_utils_nameval_free(call_private
->sdp_attrs
);
895 call_private
->sdp_attrs
= NULL
;
896 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
,
897 call_private
->invitation
->body
)) {
898 // TODO: handle error
901 if (!encryption_levels_compatible(call_private
)) {
902 handle_incompatible_encryption_level(call_private
);
906 if (!sipe_media_parse_remote_codecs(call_private
)) {
907 g_free(call_private
);
911 if (call_private
->legacy_mode
&& !call_private
->public.remote_on_hold
) {
912 sipe_backend_media_hold(call_private
->public.backend_private
,
914 } else if (sipe_utils_nameval_find(call_private
->sdp_attrs
, "inactive")) {
915 sipe_backend_media_hold(call_private
->public.backend_private
, FALSE
);
916 } else if (call_private
->public.remote_on_hold
) {
917 sipe_backend_media_unhold(call_private
->public.backend_private
, FALSE
);
919 send_response_with_session_description(call_private
,
923 sip_transport_response(sipe_private
, msg
, 486, "Busy Here", NULL
);
928 session
= sipe_session_find_or_add_chat_by_callid(sipe_private
, callid
);
929 dialog
= sipe_media_dialog_init(session
, msg
);
931 call_private
= sipe_media_call_new(sipe_private
, dialog
->with
, FALSE
);
932 call_private
->invitation
= sipmsg_copy(msg
);
933 call_private
->session
= session
;
934 call_private
->dialog
= dialog
;
936 sipe_private
->media_call
= call_private
;
938 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
, msg
->body
)) {
942 call_private
->voice_stream
=
943 sipe_backend_media_add_stream(call_private
->public.backend_private
,
946 !call_private
->legacy_mode
, FALSE
);
947 sipe_backend_media_add_remote_candidates(call_private
->public.backend_private
,
948 call_private
->voice_stream
,
949 call_private
->remote_candidates
);
951 sip_transport_response(sipe_private
, call_private
->invitation
, 180, "Ringing", NULL
);
953 // Processing continues in candidates_prepared_cb
957 sipe_media_send_ack(struct sipe_core_private
*sipe_private
,
958 SIPE_UNUSED_PARAMETER
struct sipmsg
*msg
,
959 struct transaction
*trans
)
961 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
962 struct sip_dialog
*dialog
;
966 if (!call_private
|| !call_private
->dialog
)
969 dialog
= call_private
->dialog
;
970 tmp_cseq
= dialog
->cseq
;
972 sscanf(trans
->key
, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq
);
973 dialog
->cseq
= trans_cseq
- 1;
974 sip_transport_ack(sipe_private
, dialog
);
975 dialog
->cseq
= tmp_cseq
;
977 dialog
->outgoing_invite
= NULL
;
983 process_invite_call_response(struct sipe_core_private
*sipe_private
,
985 struct transaction
*trans
)
987 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
989 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
990 struct sipe_backend_media
*backend_private
;
993 !sipe_strequal(sipe_media_get_callid(call_private
), callid
))
996 backend_private
= call_private
->public.backend_private
;
997 with
= call_private
->dialog
->with
;
999 call_private
->dialog
->outgoing_invite
= NULL
;
1001 if (msg
->response
>= 400) {
1002 // Call rejected by remote peer or an error occurred
1004 GString
*desc
= g_string_new("");
1006 sipe_backend_media_reject(backend_private
, FALSE
);
1007 sipe_media_send_ack(sipe_private
, msg
, trans
);
1009 switch (msg
->response
) {
1011 title
= _("User unavailable");
1012 g_string_append_printf(desc
, _("User %s is not available"), with
);
1015 title
= _("Call rejected");
1016 g_string_append_printf(desc
, _("User %s rejected call"), with
);
1019 title
= _("Error occured");
1020 g_string_append(desc
, _("Unable to establish a call"));
1024 g_string_append_printf(desc
, "\n%d %s", msg
->response
, msg
->responsestr
);
1026 sipe_backend_notify_error(title
, desc
->str
);
1027 g_string_free(desc
, TRUE
);
1032 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
, msg
->body
)) {
1036 if (!sipe_media_parse_remote_codecs(call_private
)) {
1037 g_free(call_private
);
1041 sipe_backend_media_add_remote_candidates(backend_private
,
1042 call_private
->voice_stream
,
1043 call_private
->remote_candidates
);
1045 sipe_dialog_parse(call_private
->dialog
, msg
, TRUE
);
1047 if (msg
->response
== 183) {
1048 // Session in progress
1049 const gchar
*rseq
= sipmsg_find_header(msg
, "RSeq");
1050 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
1051 gchar
*rack
= g_strdup_printf("RAck: %s %s\r\n", rseq
, cseq
);
1052 sip_transport_request(sipe_private
,
1058 call_private
->dialog
,
1062 sipe_media_send_ack(sipe_private
, msg
, trans
);
1064 if (call_private
->legacy_mode
&& call_private
->using_nice
) {
1065 // We created non-legacy stream as we don't know which version of
1066 // client is on the other side until first SDP response is received.
1067 // This client requires legacy mode, so we must remove current session
1068 // (using ICE) and create new using raw UDP transport.
1069 struct sipe_backend_stream
*new_stream
;
1071 call_private
->using_nice
= FALSE
;
1073 new_stream
= sipe_backend_media_add_stream(backend_private
,
1079 sipe_backend_media_remove_stream(backend_private
,
1080 call_private
->voice_stream
);
1081 call_private
->voice_stream
= new_stream
;
1083 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
, msg
->body
)) {
1087 if (!sipe_media_parse_remote_codecs(call_private
)) {
1088 g_free(call_private
);
1092 sipe_backend_media_add_remote_candidates(backend_private
,
1093 call_private
->voice_stream
,
1094 call_private
->remote_candidates
);
1096 // New INVITE will be sent in candidates_prepared_cb
1098 sipe_invite_call(sipe_private
, sipe_media_send_ack
);