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
;
59 struct sipmsg
*invitation
;
60 GList
*remote_candidates
;
64 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
65 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
68 sipe_media_get_callid(struct sipe_media_call_private
*call
)
70 return call
->dialog
->callid
;
73 static void sipe_media_codec_list_free(GList
*codecs
)
75 for (; codecs
; codecs
= g_list_delete_link(codecs
, codecs
))
76 sipe_backend_codec_free(codecs
->data
);
79 static void sipe_media_candidate_list_free(GList
*candidates
)
81 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
))
82 sipe_backend_candidate_free(candidates
->data
);
86 sipe_media_call_free(struct sipe_media_call_private
*call_private
)
89 sipe_utils_nameval_free(call_private
->sdp_attrs
);
90 if (call_private
->invitation
)
91 sipmsg_free(call_private
->invitation
);
92 sipe_media_codec_list_free(call_private
->public.remote_codecs
);
93 sipe_media_candidate_list_free(call_private
->remote_candidates
);
99 sipe_media_parse_codecs(GSList
*sdp_attrs
)
103 GList
*codecs
= NULL
;
105 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "rtpmap", i
++))) {
106 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
108 int id
= atoi(tokens
[0]);
109 gchar
*name
= tokens
[1];
110 int clock_rate
= atoi(tokens
[2]);
111 SipeMediaType type
= SIPE_MEDIA_AUDIO
;
113 struct sipe_backend_codec
*codec
= sipe_backend_codec_new(id
, name
, clock_rate
, type
);
115 // TODO: more secure and effective implementation
118 while((params
= sipe_utils_nameval_find_instance(sdp_attrs
, "fmtp", j
++))) {
119 gchar
**tokens
= g_strsplit_set(params
, " ", 0);
120 gchar
**next
= tokens
+ 1;
122 if (atoi(tokens
[0]) == id
) {
127 if (sscanf(*next
, "%[a-zA-Z0-9]=%s", name
, value
) == 2)
128 sipe_backend_codec_add_optional_parameter(codec
, name
, value
);
137 codecs
= g_list_append(codecs
, codec
);
145 codec_name_compare(struct sipe_backend_codec
*codec1
, struct sipe_backend_codec
*codec2
)
147 gchar
*name1
= sipe_backend_codec_get_name(codec1
);
148 gchar
*name2
= sipe_backend_codec_get_name(codec2
);
150 gint result
= g_strcmp0(name1
, name2
);
159 sipe_media_prune_remote_codecs(GList
*local_codecs
, GList
*remote_codecs
)
161 GList
*remote_codecs_head
= remote_codecs
;
162 GList
*pruned_codecs
= NULL
;
164 while (remote_codecs
) {
165 struct sipe_backend_codec
*c
= remote_codecs
->data
;
167 if (g_list_find_custom(local_codecs
, c
, (GCompareFunc
)codec_name_compare
)) {
168 pruned_codecs
= g_list_append(pruned_codecs
, c
);
169 remote_codecs
->data
= NULL
;
171 remote_codecs
= remote_codecs
->next
;
174 sipe_media_codec_list_free(remote_codecs_head
);
176 return pruned_codecs
;
180 sipe_media_parse_remote_candidates_legacy(gchar
*remote_ip
, guint16 remote_port
)
182 struct sipe_backend_candidate
*candidate
;
183 GList
*candidates
= NULL
;
185 candidate
= sipe_backend_candidate_new("foundation",
187 SIPE_CANDIDATE_TYPE_HOST
,
188 SIPE_NETWORK_PROTOCOL_UDP
,
189 remote_ip
, remote_port
);
190 candidates
= g_list_append(candidates
, candidate
);
192 candidate
= sipe_backend_candidate_new("foundation",
194 SIPE_CANDIDATE_TYPE_HOST
,
195 SIPE_NETWORK_PROTOCOL_UDP
,
196 remote_ip
, remote_port
+ 1);
197 candidates
= g_list_append(candidates
, candidate
);
203 sipe_media_parse_remote_candidates(GSList
*sdp_attrs
)
205 struct sipe_backend_candidate
*candidate
;
206 GList
*candidates
= NULL
;
210 const gchar
* username
= sipe_utils_nameval_find(sdp_attrs
, "ice-ufrag");
211 const gchar
* password
= sipe_utils_nameval_find(sdp_attrs
, "ice-pwd");
213 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "candidate", i
++))) {
216 SipeComponentType component
;
217 SipeNetworkProtocol protocol
;
221 SipeCandidateType type
;
223 tokens
= g_strsplit_set(attr
, " ", 0);
225 foundation
= tokens
[0];
227 switch (atoi(tokens
[1])) {
229 component
= SIPE_COMPONENT_RTP
;
232 component
= SIPE_COMPONENT_RTCP
;
235 component
= SIPE_COMPONENT_NONE
;
238 if (sipe_strequal(tokens
[2], "UDP"))
239 protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
241 // Ignore TCP candidates, at least for now...
242 // Also, if this is ICEv6 candidate list, candidates are dropped here
247 priority
= atoi(tokens
[3]);
249 port
= atoi(tokens
[5]);
251 if (sipe_strequal(tokens
[7], "host"))
252 type
= SIPE_CANDIDATE_TYPE_HOST
;
253 else if (sipe_strequal(tokens
[7], "relay"))
254 type
= SIPE_CANDIDATE_TYPE_RELAY
;
255 else if (sipe_strequal(tokens
[7], "srflx"))
256 type
= SIPE_CANDIDATE_TYPE_SRFLX
;
257 else if (sipe_strequal(tokens
[7], "prflx"))
258 type
= SIPE_CANDIDATE_TYPE_PRFLX
;
264 candidate
= sipe_backend_candidate_new(foundation
, component
,
265 type
, protocol
, ip
, port
);
266 sipe_backend_candidate_set_priority(candidate
, priority
);
267 candidates
= g_list_append(candidates
, candidate
);
273 GList
*it
= candidates
;
275 sipe_backend_candidate_set_username_and_pwd(it
->data
, username
, password
);
284 sipe_media_sdp_codec_ids_format(GList
*codecs
)
286 GString
*result
= g_string_new(NULL
);
289 struct sipe_backend_codec
*c
= codecs
->data
;
291 g_string_append_printf(result
,
293 sipe_backend_codec_get_id(c
));
295 codecs
= codecs
->next
;
298 return g_string_free(result
, FALSE
);
302 sipe_media_sdp_codecs_format(GList
*codecs
)
304 GString
*result
= g_string_new(NULL
);
307 struct sipe_backend_codec
*c
= codecs
->data
;
308 GList
*params
= NULL
;
309 gchar
*name
= sipe_backend_codec_get_name(c
);
311 g_string_append_printf(result
,
312 "a=rtpmap:%d %s/%d\r\n",
313 sipe_backend_codec_get_id(c
),
315 sipe_backend_codec_get_clock_rate(c
));
318 if ((params
= sipe_backend_codec_get_optional_parameters(c
))) {
319 g_string_append_printf(result
,
321 sipe_backend_codec_get_id(c
));
324 struct sipnameval
* par
= params
->data
;
325 g_string_append_printf(result
,
327 par
->name
, par
->value
);
328 params
= params
->next
;
330 g_string_append(result
, "\r\n");
333 codecs
= codecs
->next
;
336 return g_string_free(result
, FALSE
);
340 candidate_compare_by_component_id(struct sipe_backend_candidate
*c1
, struct sipe_backend_candidate
*c2
)
342 return sipe_backend_candidate_get_component_type(c1
)
343 - sipe_backend_candidate_get_component_type(c2
);
347 sipe_media_sdp_candidates_format(struct sipe_media_call_private
*call_private
, guint16
*local_port
)
349 struct sipe_backend_media
*backend_media
= call_private
->public.backend_private
;
350 struct sipe_backend_stream
*voice_stream
= call_private
->voice_stream
;
356 GString
*result
= g_string_new("");
357 guint16 rtcp_port
= 0;
359 // If we have established candidate pairs, send them in SDP response.
360 // Otherwise send all available local candidates.
361 l_candidates
= sipe_backend_media_get_active_local_candidates(backend_media
, voice_stream
);
363 l_candidates
= sipe_backend_get_local_candidates(backend_media
, voice_stream
);
365 // If in legacy mode, just fill local_port variable with local host's RTP
366 // component port and return empty string.
367 if (call_private
->legacy_mode
) {
368 for (cand
= l_candidates
; cand
; cand
= cand
->next
) {
369 struct sipe_backend_candidate
*c
= cand
->data
;
370 SipeCandidateType type
= sipe_backend_candidate_get_type(c
);
371 SipeComponentType component
= sipe_backend_candidate_get_component_type(c
);
373 if (type
== SIPE_CANDIDATE_TYPE_HOST
&& component
== SIPE_COMPONENT_RTP
) {
374 *local_port
= sipe_backend_candidate_get_port(c
);
379 sipe_media_candidate_list_free(l_candidates
);
381 return g_string_free(result
, FALSE
);
384 username
= sipe_backend_candidate_get_username(l_candidates
->data
);
385 password
= sipe_backend_candidate_get_password(l_candidates
->data
);
387 g_string_append_printf(result
,
388 "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
394 for (cand
= l_candidates
; cand
; cand
= cand
->next
) {
395 struct sipe_backend_candidate
*c
= cand
->data
;
398 SipeComponentType component
;
399 const gchar
*protocol
;
401 gchar
*related
= NULL
;
402 gchar
*tmp_foundation
;
405 component
= sipe_backend_candidate_get_component_type(c
);
406 port
= sipe_backend_candidate_get_port(c
);
408 switch (sipe_backend_candidate_get_protocol(c
)) {
409 case SIPE_NETWORK_PROTOCOL_TCP
:
412 case SIPE_NETWORK_PROTOCOL_UDP
:
417 switch (sipe_backend_candidate_get_type(c
)) {
418 case SIPE_CANDIDATE_TYPE_HOST
:
420 if (component
== SIPE_COMPONENT_RTP
)
422 else if (component
== SIPE_COMPONENT_RTCP
)
425 case SIPE_CANDIDATE_TYPE_RELAY
:
428 case SIPE_CANDIDATE_TYPE_SRFLX
:
433 related
= g_strdup_printf("raddr %s rport %d",
434 tmp
= sipe_backend_candidate_get_base_ip(c
),
435 sipe_backend_candidate_get_base_port(c
));
439 case SIPE_CANDIDATE_TYPE_PRFLX
:
443 // TODO: error unknown/unsupported type
447 g_string_append_printf(result
,
448 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
449 tmp_foundation
= sipe_backend_candidate_get_foundation(c
),
452 sipe_backend_candidate_get_priority(c
),
453 tmp_ip
= sipe_backend_candidate_get_ip(c
),
456 related
? related
: "");
458 g_free(tmp_foundation
);
462 r_candidates
= sipe_backend_media_get_active_remote_candidates(backend_media
, voice_stream
);
463 r_candidates
= g_list_sort(r_candidates
, (GCompareFunc
)candidate_compare_by_component_id
);
466 g_string_append(result
, "a=remote-candidates:");
467 for (cand
= r_candidates
; cand
; cand
= cand
->next
) {
468 struct sipe_backend_candidate
*candidate
= cand
->data
;
470 g_string_append_printf(result
,
472 sipe_backend_candidate_get_component_type(candidate
),
473 tmp
= sipe_backend_candidate_get_ip(candidate
),
474 sipe_backend_candidate_get_port(candidate
));
477 g_string_append(result
, "\r\n");
480 sipe_media_candidate_list_free(l_candidates
);
481 sipe_media_candidate_list_free(r_candidates
);
483 if (rtcp_port
!= 0) {
484 g_string_append_printf(result
,
485 "a=maxptime:200\r\na=rtcp:%u\r\n",
489 return g_string_free(result
, FALSE
);
493 sipe_media_create_sdp(struct sipe_media_call_private
*call_private
) {
494 GList
*usable_codecs
= sipe_backend_get_local_codecs(SIPE_MEDIA_CALL
,
495 call_private
->voice_stream
);
498 const char *ip
= sipe_utils_get_suitable_local_ip(-1);
499 guint16 local_port
= 0;
501 gchar
*sdp_codecs
= sipe_media_sdp_codecs_format(usable_codecs
);
502 gchar
*sdp_codec_ids
= sipe_media_sdp_codec_ids_format(usable_codecs
);
503 gchar
*sdp_candidates
= sipe_media_sdp_candidates_format(call_private
, &local_port
);
504 gchar
*inactive
= (call_private
->public.local_on_hold
||
505 call_private
->public.remote_on_hold
) ? "a=inactive\r\n" : "";
507 body
= g_strdup_printf(
509 "o=- 0 0 IN IP4 %s\r\n"
514 "m=audio %d RTP/AVP%s\r\n"
518 "a=encryption:rejected\r\n"
519 ,ip
, ip
, local_port
, sdp_codec_ids
, sdp_candidates
, inactive
, sdp_codecs
);
522 g_free(sdp_codec_ids
);
523 g_free(sdp_candidates
);
525 sipe_media_codec_list_free(usable_codecs
);
531 sipe_invite_call(struct sipe_core_private
*sipe_private
, TransCallback tc
)
536 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
537 struct sip_dialog
*dialog
= call_private
->dialog
;
539 contact
= get_contact(sipe_private
);
540 hdr
= g_strdup_printf(
541 "Supported: ms-early-media\r\n"
542 "Supported: 100rel\r\n"
543 "ms-keep-alive: UAC;hop-hop=yes\r\n"
545 "Content-Type: application/sdp\r\n",
547 (call_private
->public.local_on_hold
|| call_private
->public.remote_on_hold
) ? ";+sip.rendering=\"no\"" : "");
550 body
= sipe_media_create_sdp(call_private
);
552 dialog
->outgoing_invite
= sip_transport_invite(sipe_private
,
563 sipe_media_parse_remote_codecs(struct sipe_media_call_private
*call_private
);
566 sipe_media_parse_sdp_attributes_and_candidates(struct sipe_media_call_private
*call_private
, gchar
*frame
) {
567 gchar
**lines
= g_strsplit(frame
, "\r\n", 0);
568 GSList
*sdp_attrs
= NULL
;
569 gchar
*remote_ip
= NULL
;
570 guint16 remote_port
= 0;
571 GList
*remote_candidates
;
573 gboolean no_error
= TRUE
;
575 for (ptr
= lines
; *ptr
!= NULL
; ++ptr
) {
576 if (g_str_has_prefix(*ptr
, "a=")) {
577 gchar
**parts
= g_strsplit(*ptr
+ 2, ":", 2);
580 sipe_utils_nameval_free(sdp_attrs
);
585 sdp_attrs
= sipe_utils_nameval_add(sdp_attrs
, parts
[0], parts
[1]);
588 } else if (g_str_has_prefix(*ptr
, "o=")) {
589 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 6);
590 remote_ip
= g_strdup(parts
[5]);
592 } else if (g_str_has_prefix(*ptr
, "m=")) {
593 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 3);
594 remote_port
= atoi(parts
[1]);
601 remote_candidates
= sipe_media_parse_remote_candidates(sdp_attrs
);
602 if (!remote_candidates
) {
603 // No a=candidate in SDP message, revert to OC2005 behaviour
604 remote_candidates
= sipe_media_parse_remote_candidates_legacy(remote_ip
, remote_port
);
605 // This seems to be pre-OC2007 R2 UAC
606 call_private
->legacy_mode
= TRUE
;
610 sipe_utils_nameval_free(call_private
->sdp_attrs
);
611 sipe_media_candidate_list_free(call_private
->remote_candidates
);
613 call_private
->sdp_attrs
= sdp_attrs
;
614 call_private
->remote_ip
= remote_ip
;
615 call_private
->remote_port
= remote_port
;
616 call_private
->remote_candidates
= remote_candidates
;
618 sipe_utils_nameval_free(sdp_attrs
);
619 sipe_media_candidate_list_free(remote_candidates
);
626 sipe_media_parse_remote_codecs(struct sipe_media_call_private
*call_private
)
628 GList
*local_codecs
= sipe_backend_get_local_codecs(SIPE_MEDIA_CALL
,
629 call_private
->voice_stream
);
630 GList
*remote_codecs
;
632 remote_codecs
= sipe_media_parse_codecs(call_private
->sdp_attrs
);
633 remote_codecs
= sipe_media_prune_remote_codecs(local_codecs
, remote_codecs
);
635 sipe_media_codec_list_free(local_codecs
);
638 sipe_media_codec_list_free(call_private
->public.remote_codecs
);
640 call_private
->public.remote_codecs
= remote_codecs
;
642 if (!sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL
,
643 call_private
->voice_stream
)) {
644 SIPE_DEBUG_ERROR_NOFORMAT("ERROR SET REMOTE CODECS"); // TODO
650 sipe_media_codec_list_free(remote_codecs
);
651 SIPE_DEBUG_ERROR_NOFORMAT("ERROR NO CANDIDATES OR CODECS");
657 static struct sip_dialog
*
658 sipe_media_dialog_init(struct sip_session
* session
, struct sipmsg
*msg
)
660 gchar
*newTag
= gentag();
661 const gchar
*oldHeader
;
663 struct sip_dialog
*dialog
;
665 oldHeader
= sipmsg_find_header(msg
, "To");
666 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
667 sipmsg_remove_header_now(msg
, "To");
668 sipmsg_add_header_now(msg
, "To", newHeader
);
671 dialog
= sipe_dialog_add(session
);
672 dialog
->callid
= g_strdup(session
->callid
);
673 dialog
->with
= parse_from(sipmsg_find_header(msg
, "From"));
674 sipe_dialog_parse(dialog
, msg
, FALSE
);
680 send_response_with_session_description(struct sipe_media_call_private
*call_private
, int code
, gchar
*text
)
682 gchar
*body
= sipe_media_create_sdp(call_private
);
683 sipmsg_add_header(call_private
->invitation
, "Content-Type", "application/sdp");
684 sip_transport_response(call_private
->sipe_private
, call_private
->invitation
, code
, text
, body
);
689 encryption_levels_compatible(struct sipe_media_call_private
*call_private
)
691 const gchar
*encrypion_level
;
693 encrypion_level
= sipe_utils_nameval_find(call_private
->sdp_attrs
,
696 // Decline call if peer requires encryption as we don't support it yet.
697 return !sipe_strequal(encrypion_level
, "required");
701 handle_incompatible_encryption_level(struct sipe_media_call_private
*call_private
)
703 sipmsg_add_header(call_private
->invitation
, "Warning",
704 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
705 sip_transport_response(call_private
->sipe_private
,
706 call_private
->invitation
,
707 488, "Encryption Levels not compatible",
709 sipe_backend_media_reject(call_private
->public.backend_private
, FALSE
);
710 sipe_backend_notify_error(_("Unable to establish a call"),
711 _("Encryption settings of peer are incompatible with ours."));
715 process_invite_call_response(struct sipe_core_private
*sipe_private
,
717 struct transaction
*trans
);
719 static void candidates_prepared_cb(struct sipe_media_call
*call
)
721 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
723 if (sipe_backend_media_is_initiator(call_private
->public.backend_private
,
724 call_private
->voice_stream
)) {
725 sipe_invite_call(call_private
->sipe_private
,
726 process_invite_call_response
);
730 if (!sipe_media_parse_remote_codecs(call_private
)) {
731 g_free(call_private
);
735 if ( !call_private
->legacy_mode
736 && encryption_levels_compatible(SIPE_MEDIA_CALL_PRIVATE
))
737 send_response_with_session_description(call_private
,
738 183, "Session Progress");
741 static void media_connected_cb(SIPE_UNUSED_PARAMETER
struct sipe_media_call_private
*call_private
)
745 static void call_accept_cb(struct sipe_media_call
*call
, gboolean local
)
748 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
750 if (!encryption_levels_compatible(call_private
)) {
751 handle_incompatible_encryption_level(call_private
);
755 send_response_with_session_description(call_private
, 200, "OK");
759 static void call_reject_cb(struct sipe_media_call
*call
, gboolean local
)
761 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
764 sip_transport_response(call_private
->sipe_private
, call_private
->invitation
, 603, "Decline", NULL
);
766 call_private
->sipe_private
->media_call
= NULL
;
767 sipe_media_call_free(call_private
);
771 sipe_media_send_ack(struct sipe_core_private
*sipe_private
, struct sipmsg
*msg
,
772 struct transaction
*trans
);
774 static void call_hold_cb(struct sipe_media_call
*call
, gboolean local
, gboolean state
)
776 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
778 if (local
&& (call_private
->public.local_on_hold
!= state
)) {
779 call_private
->public.local_on_hold
= state
;
780 sipe_invite_call(call_private
->sipe_private
, sipe_media_send_ack
);
781 } else if (call_private
->public.remote_on_hold
!= state
) {
782 call_private
->public.remote_on_hold
= state
;
783 send_response_with_session_description(call_private
, 200, "OK");
787 static void call_hangup_cb(struct sipe_media_call
*call
, gboolean local
)
789 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
792 sip_transport_bye(call_private
->sipe_private
, call_private
->dialog
);
794 call_private
->sipe_private
->media_call
= NULL
;
795 sipe_media_call_free(call_private
);
798 static struct sipe_media_call_private
*
799 sipe_media_call_init(struct sipe_core_private
*sipe_private
, const gchar
* participant
, gboolean initiator
)
801 struct sipe_media_call_private
*call_private
= g_new0(struct sipe_media_call_private
, 1);
803 call_private
->sipe_private
= sipe_private
;
804 call_private
->public.backend_private
= sipe_backend_media_new(SIPE_CORE_PUBLIC
,
809 call_private
->legacy_mode
= FALSE
;
810 call_private
->using_nice
= TRUE
;
812 call_private
->public.candidates_prepared_cb
= candidates_prepared_cb
;
813 call_private
->public.media_connected_cb
= media_connected_cb
;
814 call_private
->public.call_accept_cb
= call_accept_cb
;
815 call_private
->public.call_reject_cb
= call_reject_cb
;
816 call_private
->public.call_hold_cb
= call_hold_cb
;
817 call_private
->public.call_hangup_cb
= call_hangup_cb
;
819 call_private
->public.local_on_hold
= FALSE
;
820 call_private
->public.remote_on_hold
= FALSE
;
825 void sipe_media_hangup(struct sipe_core_private
*sipe_private
)
827 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
829 sipe_backend_media_hangup(call_private
->public.backend_private
,
834 sipe_core_media_initiate_call(struct sipe_core_public
*sipe_public
,
835 const char *participant
)
837 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
838 struct sipe_media_call_private
*call_private
;
840 if (sipe_private
->media_call
)
843 call_private
= sipe_media_call_init(sipe_private
, participant
, TRUE
);
845 sipe_private
->media_call
= call_private
;
847 call_private
->session
= sipe_session_add_chat(sipe_private
);
848 call_private
->dialog
= sipe_dialog_add(call_private
->session
);
849 call_private
->dialog
->callid
= gencallid();
850 call_private
->dialog
->with
= g_strdup(participant
);
851 call_private
->dialog
->ourtag
= gentag();
853 call_private
->voice_stream
=
854 sipe_backend_media_add_stream(call_private
->public.backend_private
,
857 call_private
->using_nice
, TRUE
);
862 sipe_media_incoming_invite(struct sipe_core_private
*sipe_private
,
865 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
867 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
868 struct sip_session
*session
;
869 struct sip_dialog
*dialog
;
872 if (sipe_strequal(call_private
->dialog
->callid
, callid
)) {
873 if (call_private
->invitation
)
874 sipmsg_free(call_private
->invitation
);
875 call_private
->invitation
= sipmsg_copy(msg
);
877 sipe_utils_nameval_free(call_private
->sdp_attrs
);
878 call_private
->sdp_attrs
= NULL
;
879 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
,
880 call_private
->invitation
->body
)) {
881 // TODO: handle error
884 if (!encryption_levels_compatible(call_private
)) {
885 handle_incompatible_encryption_level(call_private
);
889 if (!sipe_media_parse_remote_codecs(call_private
)) {
890 g_free(call_private
);
894 if (call_private
->legacy_mode
&& !call_private
->public.remote_on_hold
) {
895 sipe_backend_media_hold(call_private
->public.backend_private
,
897 } else if (sipe_utils_nameval_find(call_private
->sdp_attrs
, "inactive")) {
898 sipe_backend_media_hold(call_private
->public.backend_private
, FALSE
);
899 } else if (call_private
->public.remote_on_hold
) {
900 sipe_backend_media_unhold(call_private
->public.backend_private
, FALSE
);
902 send_response_with_session_description(call_private
,
906 sip_transport_response(sipe_private
, msg
, 486, "Busy Here", NULL
);
911 session
= sipe_session_find_or_add_chat_by_callid(sipe_private
, callid
);
912 dialog
= sipe_media_dialog_init(session
, msg
);
914 call_private
= sipe_media_call_init(sipe_private
, dialog
->with
, FALSE
);
915 call_private
->invitation
= sipmsg_copy(msg
);
916 call_private
->session
= session
;
917 call_private
->dialog
= dialog
;
919 sipe_private
->media_call
= call_private
;
921 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
, msg
->body
)) {
925 call_private
->voice_stream
=
926 sipe_backend_media_add_stream(call_private
->public.backend_private
,
929 !call_private
->legacy_mode
, FALSE
);
930 sipe_backend_media_add_remote_candidates(call_private
->public.backend_private
,
931 call_private
->voice_stream
,
932 call_private
->remote_candidates
);
934 sip_transport_response(sipe_private
, call_private
->invitation
, 180, "Ringing", NULL
);
936 // Processing continues in candidates_prepared_cb
940 sipe_media_send_ack(struct sipe_core_private
*sipe_private
,
941 SIPE_UNUSED_PARAMETER
struct sipmsg
*msg
,
942 struct transaction
*trans
)
944 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
945 struct sip_dialog
*dialog
;
949 if (!call_private
|| !call_private
->dialog
)
952 dialog
= call_private
->dialog
;
953 tmp_cseq
= dialog
->cseq
;
955 sscanf(trans
->key
, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq
);
956 dialog
->cseq
= trans_cseq
- 1;
957 sip_transport_ack(sipe_private
, dialog
);
958 dialog
->cseq
= tmp_cseq
;
960 dialog
->outgoing_invite
= NULL
;
966 process_invite_call_response(struct sipe_core_private
*sipe_private
,
968 struct transaction
*trans
)
970 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
972 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
973 struct sipe_backend_media
*backend_private
;
976 !sipe_strequal(sipe_media_get_callid(call_private
), callid
))
979 backend_private
= call_private
->public.backend_private
;
980 with
= call_private
->dialog
->with
;
982 call_private
->dialog
->outgoing_invite
= NULL
;
984 if (msg
->response
>= 400) {
985 // Call rejected by remote peer or an error occurred
987 GString
*desc
= g_string_new("");
989 sipe_backend_media_reject(backend_private
, FALSE
);
990 sipe_media_send_ack(sipe_private
, msg
, trans
);
992 switch (msg
->response
) {
994 title
= _("User unavailable");
995 g_string_append_printf(desc
, _("User %s is not available"), with
);
998 title
= _("Call rejected");
999 g_string_append_printf(desc
, _("User %s rejected call"), with
);
1002 title
= _("Error occured");
1003 g_string_append(desc
, _("Unable to establish a call"));
1007 g_string_append_printf(desc
, "\n%d %s", msg
->response
, msg
->responsestr
);
1009 sipe_backend_notify_error(title
, desc
->str
);
1010 g_string_free(desc
, TRUE
);
1015 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
, msg
->body
)) {
1019 if (!sipe_media_parse_remote_codecs(call_private
)) {
1020 g_free(call_private
);
1024 sipe_backend_media_add_remote_candidates(backend_private
,
1025 call_private
->voice_stream
,
1026 call_private
->remote_candidates
);
1028 sipe_dialog_parse(call_private
->dialog
, msg
, TRUE
);
1030 if (msg
->response
== 183) {
1031 // Session in progress
1032 const gchar
*rseq
= sipmsg_find_header(msg
, "RSeq");
1033 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
1034 gchar
*rack
= g_strdup_printf("RAck: %s %s\r\n", rseq
, cseq
);
1035 sip_transport_request(sipe_private
,
1041 call_private
->dialog
,
1045 sipe_media_send_ack(sipe_private
, msg
, trans
);
1047 if (call_private
->legacy_mode
&& call_private
->using_nice
) {
1048 // We created non-legacy stream as we don't know which version of
1049 // client is on the other side until first SDP response is received.
1050 // This client requires legacy mode, so we must remove current session
1051 // (using ICE) and create new using raw UDP transport.
1052 struct sipe_backend_stream
*new_stream
;
1054 call_private
->using_nice
= FALSE
;
1056 new_stream
= sipe_backend_media_add_stream(backend_private
,
1062 sipe_backend_media_remove_stream(backend_private
,
1063 call_private
->voice_stream
);
1064 call_private
->voice_stream
= new_stream
;
1066 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private
, msg
->body
)) {
1070 if (!sipe_media_parse_remote_codecs(call_private
)) {
1071 g_free(call_private
);
1075 sipe_backend_media_add_remote_candidates(backend_private
,
1076 call_private
->voice_stream
,
1077 call_private
->remote_candidates
);
1079 // New INVITE will be sent in candidates_prepared_cb
1081 sipe_invite_call(sipe_private
, sipe_media_send_ack
);