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
31 #include "sipe-core.h"
32 #include "sipe-core-private.h"
35 #include "sipe-session.h"
36 #include "sipe-media.h"
37 #include "sipe-dialog.h"
38 #include "sipe-utils.h"
39 #include "sipe-common.h"
40 #include "sip-transport.h"
43 sipe_media_get_callid(sipe_media_call
*call
)
45 return call
->dialog
->callid
;
48 void sipe_media_codec_list_free(GList
*codecs
)
50 for (; codecs
; codecs
= g_list_delete_link(codecs
, codecs
))
51 sipe_backend_codec_free(codecs
->data
);
54 void sipe_media_candidate_list_free(GList
*candidates
)
56 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
))
57 sipe_backend_candidate_free(candidates
->data
);
61 sipe_media_call_free(sipe_media_call
*call
)
64 sipe_utils_nameval_free(call
->sdp_attrs
);
66 sipmsg_free(call
->invitation
);
67 sipe_media_codec_list_free(call
->remote_codecs
);
68 sipe_media_candidate_list_free(call
->remote_candidates
);
74 sipe_media_parse_codecs(GSList
*sdp_attrs
)
80 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "rtpmap", i
++))) {
81 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
83 int id
= atoi(tokens
[0]);
84 gchar
*name
= tokens
[1];
85 int clock_rate
= atoi(tokens
[2]);
86 SipeMediaType type
= SIPE_MEDIA_AUDIO
;
88 sipe_codec
*codec
= sipe_backend_codec_new(id
, name
, clock_rate
, type
);
90 // TODO: more secure and effective implementation
93 while((params
= sipe_utils_nameval_find_instance(sdp_attrs
, "fmtp", j
++))) {
94 gchar
**tokens
= g_strsplit_set(params
, " ", 0);
95 gchar
**next
= tokens
+ 1;
97 if (atoi(tokens
[0]) == id
) {
102 if (sscanf(*next
, "%[a-zA-Z0-9]=%s", name
, value
) == 2)
103 sipe_backend_codec_add_optional_parameter(codec
, name
, value
);
112 codecs
= g_list_append(codecs
, codec
);
120 codec_name_compare(sipe_codec
* codec1
, sipe_codec
* codec2
)
122 gchar
*name1
= sipe_backend_codec_get_name(codec1
);
123 gchar
*name2
= sipe_backend_codec_get_name(codec2
);
125 gint result
= g_strcmp0(name1
, name2
);
134 sipe_media_prune_remote_codecs(GList
*local_codecs
, GList
*remote_codecs
)
136 GList
*remote_codecs_head
= remote_codecs
;
137 GList
*pruned_codecs
= NULL
;
139 while (remote_codecs
) {
140 sipe_codec
*c
= remote_codecs
->data
;
142 if (g_list_find_custom(local_codecs
, c
, (GCompareFunc
)codec_name_compare
)) {
143 pruned_codecs
= g_list_append(pruned_codecs
, c
);
144 remote_codecs
->data
= NULL
;
146 remote_codecs
= remote_codecs
->next
;
149 sipe_media_codec_list_free(remote_codecs_head
);
151 return pruned_codecs
;
155 sipe_media_parse_remote_candidates_legacy(gchar
*remote_ip
, guint16 remote_port
)
157 sipe_candidate
*candidate
;
158 GList
*candidates
= NULL
;
160 candidate
= sipe_backend_candidate_new("foundation",
162 SIPE_CANDIDATE_TYPE_HOST
,
163 SIPE_NETWORK_PROTOCOL_UDP
,
164 remote_ip
, remote_port
);
165 candidates
= g_list_append(candidates
, candidate
);
167 candidate
= sipe_backend_candidate_new("foundation",
169 SIPE_CANDIDATE_TYPE_HOST
,
170 SIPE_NETWORK_PROTOCOL_UDP
,
171 remote_ip
, remote_port
+ 1);
172 candidates
= g_list_append(candidates
, candidate
);
178 sipe_media_parse_remote_candidates(GSList
*sdp_attrs
)
180 sipe_candidate
*candidate
;
181 GList
*candidates
= NULL
;
185 const gchar
* username
= sipe_utils_nameval_find(sdp_attrs
, "ice-ufrag");
186 const gchar
* password
= sipe_utils_nameval_find(sdp_attrs
, "ice-pwd");
188 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "candidate", i
++))) {
191 SipeComponentType component
;
192 SipeNetworkProtocol protocol
;
196 SipeCandidateType type
;
198 tokens
= g_strsplit_set(attr
, " ", 0);
200 foundation
= tokens
[0];
202 switch (atoi(tokens
[1])) {
204 component
= SIPE_COMPONENT_RTP
;
207 component
= SIPE_COMPONENT_RTCP
;
210 component
= SIPE_COMPONENT_NONE
;
213 if (sipe_strequal(tokens
[2], "UDP"))
214 protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
216 // Ignore TCP candidates, at least for now...
221 priority
= atoi(tokens
[3]);
223 port
= atoi(tokens
[5]);
225 if (sipe_strequal(tokens
[7], "host"))
226 type
= SIPE_CANDIDATE_TYPE_HOST
;
227 else if (sipe_strequal(tokens
[7], "relay"))
228 type
= SIPE_CANDIDATE_TYPE_RELAY
;
229 else if (sipe_strequal(tokens
[7], "srflx"))
230 type
= SIPE_CANDIDATE_TYPE_SRFLX
;
236 candidate
= sipe_backend_candidate_new(foundation
, component
,
237 type
, protocol
, ip
, port
);
238 sipe_backend_candidate_set_priority(candidate
, priority
);
239 candidates
= g_list_append(candidates
, candidate
);
245 GList
*it
= candidates
;
247 sipe_backend_candidate_set_username_and_pwd(it
->data
, username
, password
);
256 sipe_media_sdp_codec_ids_format(GList
*codecs
)
258 GString
*result
= g_string_new(NULL
);
261 sipe_codec
*c
= codecs
->data
;
263 gchar
*tmp
= g_strdup_printf(" %d", sipe_backend_codec_get_id(c
));
264 g_string_append(result
,tmp
);
267 codecs
= codecs
->next
;
270 return g_string_free(result
, FALSE
);
274 sipe_media_sdp_codecs_format(GList
*codecs
)
276 GString
*result
= g_string_new(NULL
);
279 sipe_codec
*c
= codecs
->data
;
280 GList
*params
= NULL
;
281 gchar
*name
= sipe_backend_codec_get_name(c
);
283 gchar
*tmp
= g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
284 sipe_backend_codec_get_id(c
),
286 sipe_backend_codec_get_clock_rate(c
));
289 g_string_append(result
, tmp
);
292 if ((params
= sipe_backend_codec_get_optional_parameters(c
))) {
293 tmp
= g_strdup_printf("a=fmtp:%d",sipe_backend_codec_get_id(c
));
294 g_string_append(result
, tmp
);
298 struct sipnameval
* par
= params
->data
;
299 tmp
= g_strdup_printf(" %s=%s", par
->name
, par
->value
);
300 g_string_append(result
, tmp
);
302 params
= params
->next
;
304 g_string_append(result
, "\r\n");
307 codecs
= codecs
->next
;
310 return g_string_free(result
, FALSE
);
314 sipe_media_sdp_candidates_format(GList
*candidates
, sipe_media_call
* call
)
316 GString
*result
= g_string_new("");
318 gchar
*username
= sipe_backend_candidate_get_username(candidates
->data
);
319 gchar
*password
= sipe_backend_candidate_get_password(candidates
->data
);
320 guint16 rtcp_port
= 0;
322 if (call
->legacy_mode
)
323 return g_string_free(result
, FALSE
);
325 tmp
= g_strdup_printf("a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",username
, password
);
326 g_string_append(result
, tmp
);
330 sipe_candidate
*c
= candidates
->data
;
337 port
= sipe_backend_candidate_get_port(c
);
339 switch (sipe_backend_candidate_get_component_type(c
)) {
340 case SIPE_COMPONENT_RTP
:
343 case SIPE_COMPONENT_RTCP
:
348 case SIPE_COMPONENT_NONE
:
352 switch (sipe_backend_candidate_get_protocol(c
)) {
353 case SIPE_NETWORK_PROTOCOL_TCP
:
356 case SIPE_NETWORK_PROTOCOL_UDP
:
361 switch (sipe_backend_candidate_get_type(c
)) {
362 case SIPE_CANDIDATE_TYPE_HOST
:
365 case SIPE_CANDIDATE_TYPE_RELAY
:
368 case SIPE_CANDIDATE_TYPE_SRFLX
:
372 // TODO: error unknown/unsupported type
376 tmp
= g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
377 sipe_backend_candidate_get_foundation(c
),
380 sipe_backend_candidate_get_priority(c
),
381 sipe_backend_candidate_get_ip(c
),
385 g_string_append(result
, tmp
);
388 candidates
= candidates
->next
;
391 // No exchange of remote candidates in the first round of negotiation
392 if ((call
->invite_cnt
> 1) && call
->remote_candidates
) {
393 sipe_candidate
*first
= call
->remote_candidates
->data
;
394 sipe_candidate
*second
= call
->remote_candidates
->next
->data
;
395 tmp
= g_strdup_printf("a=remote-candidates:1 %s %u 2 %s %u\r\n",
396 sipe_backend_candidate_get_ip(first
), sipe_backend_candidate_get_port(first
),
397 sipe_backend_candidate_get_ip(second
), sipe_backend_candidate_get_port(second
));
399 g_string_append(result
, tmp
);
404 if (rtcp_port
!= 0) {
405 tmp
= g_strdup_printf("a=maxptime:200\r\na=rtcp:%u\r\n", rtcp_port
);
406 g_string_append(result
, tmp
);
410 return g_string_free(result
, FALSE
);
414 sipe_media_create_sdp(sipe_media_call
*call
) {
415 GList
*usable_codecs
= sipe_backend_get_local_codecs(call
);
416 GList
*local_candidates
= sipe_backend_get_local_candidates(call
, call
->dialog
->with
);
418 // TODO: more sophisticated
419 guint16 local_port
= sipe_backend_candidate_get_port(local_candidates
->data
);
420 const char *ip
= sipe_utils_get_suitable_local_ip(-1);
422 gchar
*sdp_codecs
= sipe_media_sdp_codecs_format(usable_codecs
);
423 gchar
*sdp_codec_ids
= sipe_media_sdp_codec_ids_format(usable_codecs
);
424 gchar
*sdp_candidates
= sipe_media_sdp_candidates_format(local_candidates
, call
);
425 gchar
*inactive
= (call
->local_on_hold
|| call
->remote_on_hold
) ? "a=inactive\r\n" : "";
427 gchar
*body
= g_strdup_printf(
429 "o=- 0 0 IN IP4 %s\r\n"
434 "m=audio %d RTP/AVP%s\r\n"
438 "a=encryption:rejected\r\n"
439 ,ip
, ip
, local_port
, sdp_codec_ids
, sdp_candidates
, inactive
, sdp_codecs
);
442 g_free(sdp_codec_ids
);
443 g_free(sdp_candidates
);
444 sipe_media_codec_list_free(usable_codecs
);
450 sipe_invite_call(struct sipe_core_private
*sipe_private
, TransCallback tc
)
455 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
456 sipe_media_call
*call
= sipe_private
->media_call
;
457 struct sip_dialog
*dialog
= call
->dialog
;
459 ++(call
->invite_cnt
);
461 contact
= get_contact(sip
);
462 hdr
= g_strdup_printf(
463 "Supported: ms-early-media\r\n"
464 "Supported: 100rel\r\n"
465 "ms-keep-alive: UAC;hop-hop=yes\r\n"
467 "Content-Type: application/sdp\r\n",
469 (call
->local_on_hold
|| call
->remote_on_hold
) ? ";+sip.rendering=\"no\"" : "");
472 body
= sipe_media_create_sdp(call
);
474 send_sip_request(SIP_TO_CORE_PRIVATE
, "INVITE", dialog
->with
, dialog
->with
, hdr
, body
,
482 sipe_media_parse_remote_codecs(sipe_media_call
*call
);
485 sipe_media_parse_sdp_attributes_and_candidates(sipe_media_call
* call
, gchar
*frame
) {
486 gchar
**lines
= g_strsplit(frame
, "\r\n", 0);
487 GSList
*sdp_attrs
= NULL
;
488 gchar
*remote_ip
= NULL
;
489 guint16 remote_port
= 0;
490 GList
*remote_candidates
;
492 gboolean no_error
= TRUE
;
494 for (ptr
= lines
; *ptr
!= NULL
; ++ptr
) {
495 if (g_str_has_prefix(*ptr
, "a=")) {
496 gchar
**parts
= g_strsplit(*ptr
+ 2, ":", 2);
499 sipe_utils_nameval_free(sdp_attrs
);
504 sdp_attrs
= sipe_utils_nameval_add(sdp_attrs
, parts
[0], parts
[1]);
507 } else if (g_str_has_prefix(*ptr
, "o=")) {
508 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 6);
509 remote_ip
= g_strdup(parts
[5]);
511 } else if (g_str_has_prefix(*ptr
, "m=")) {
512 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 3);
513 remote_port
= atoi(parts
[1]);
520 remote_candidates
= sipe_media_parse_remote_candidates(sdp_attrs
);
521 if (!remote_candidates
) {
522 // No a=candidate in SDP message, revert to OC2005 behaviour
523 sipe_media_parse_remote_candidates_legacy(remote_ip
, remote_port
);
524 // This seems to be pre-OC2007 R2 UAC
525 call
->legacy_mode
= TRUE
;
529 sipe_utils_nameval_free(call
->sdp_attrs
);
530 sipe_media_candidate_list_free(call
->remote_candidates
);
532 call
->sdp_attrs
= sdp_attrs
;
533 call
->remote_ip
= remote_ip
;
534 call
->remote_port
= remote_port
;
535 call
->remote_candidates
= remote_candidates
;
537 sipe_utils_nameval_free(sdp_attrs
);
538 sipe_media_candidate_list_free(remote_candidates
);
545 sipe_media_parse_remote_codecs(sipe_media_call
*call
)
547 GList
*local_codecs
= sipe_backend_get_local_codecs(call
);
548 GList
*remote_codecs
;
550 remote_codecs
= sipe_media_parse_codecs(call
->sdp_attrs
);
551 remote_codecs
= sipe_media_prune_remote_codecs(local_codecs
, remote_codecs
);
553 sipe_media_codec_list_free(local_codecs
);
556 sipe_media_codec_list_free(call
->remote_codecs
);
558 call
->remote_codecs
= remote_codecs
;
560 if (!sipe_backend_set_remote_codecs(call
, call
->dialog
->with
)) {
561 printf("ERROR SET REMOTE CODECS"); // TODO
567 sipe_media_codec_list_free(remote_codecs
);
568 printf("ERROR NO CANDIDATES OR CODECS");
574 static struct sip_dialog
*
575 sipe_media_dialog_init(struct sip_session
* session
, struct sipmsg
*msg
)
577 gchar
*newTag
= gentag();
578 const gchar
*oldHeader
;
580 struct sip_dialog
*dialog
;
582 oldHeader
= sipmsg_find_header(msg
, "To");
583 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
584 sipmsg_remove_header_now(msg
, "To");
585 sipmsg_add_header_now(msg
, "To", newHeader
);
588 dialog
= sipe_dialog_add(session
);
589 dialog
->callid
= g_strdup(session
->callid
);
590 dialog
->with
= parse_from(sipmsg_find_header(msg
, "From"));
591 sipe_dialog_parse(dialog
, msg
, FALSE
);
597 send_response_with_session_description(sipe_media_call
*call
, int code
, gchar
*text
)
599 gchar
*body
= sipe_media_create_sdp(call
);
600 sipmsg_add_header(call
->invitation
, "Content-Type", "application/sdp");
601 send_sip_response(call
->sipe_private
, call
->invitation
, code
, text
, body
);
606 sipe_media_process_invite_response(struct sipe_core_private
*sipe_private
,
608 struct transaction
*trans
);
610 static void candidates_prepared_cb(sipe_media_call
*call
)
612 if (sipe_backend_media_is_initiator(call
->media
, call
->dialog
->with
)) {
613 sipe_invite_call(call
->sipe_private
, sipe_media_process_invite_response
);
614 } else if (!call
->legacy_mode
) {
615 if (!sipe_media_parse_remote_codecs(call
)) {
620 send_response_with_session_description(call
, 183, "Session Progress");
624 static void media_connected_cb(sipe_media_call
*call
)
629 static void call_accept_cb(sipe_media_call
*call
, gboolean local
)
632 send_response_with_session_description(call
, 200, "OK");
636 static void call_reject_cb(sipe_media_call
*call
, gboolean local
)
639 send_sip_response(call
->sipe_private
, call
->invitation
, 603, "Decline", NULL
);
641 call
->sipe_private
->media_call
= NULL
;
642 sipe_media_call_free(call
);
646 sipe_media_send_ack(struct sipe_core_private
*sipe_private
, struct sipmsg
*msg
,
647 struct transaction
*trans
);
649 static void call_hold_cb(sipe_media_call
*call
, gboolean local
, gboolean state
)
651 if (local
&& (call
->local_on_hold
!= state
)) {
652 call
->local_on_hold
= state
;
653 sipe_invite_call(call
->sipe_private
, sipe_media_send_ack
);
654 } else if (call
->remote_on_hold
!= state
) {
655 call
->remote_on_hold
= state
;
656 send_response_with_session_description(call
, 200, "OK");
660 static void call_hangup_cb(sipe_media_call
*call
, gboolean local
)
663 send_sip_request(call
->sipe_private
, "BYE", call
->dialog
->with
, call
->dialog
->with
,
664 NULL
, NULL
, call
->dialog
, NULL
);
666 call
->sipe_private
->media_call
= NULL
;
667 sipe_media_call_free(call
);
670 static sipe_media_call
*
671 sipe_media_call_init(struct sipe_core_private
*sipe_private
, const gchar
* participant
, gboolean initiator
)
673 sipe_media_call
*call
= g_new0(sipe_media_call
, 1);
676 call
->sipe_private
= sipe_private
;
677 call
->media
= sipe_backend_media_new(call
, participant
, initiator
);
679 call
->legacy_mode
= FALSE
;
681 call
->candidates_prepared_cb
= candidates_prepared_cb
;
682 call
->media_connected_cb
= media_connected_cb
;
683 call
->call_accept_cb
= call_accept_cb
;
684 call
->call_reject_cb
= call_reject_cb
;
685 call
->call_hold_cb
= call_hold_cb
;
686 call
->call_hangup_cb
= call_hangup_cb
;
688 call
->local_on_hold
= FALSE
;
689 call
->remote_on_hold
= FALSE
;
694 void sipe_media_hangup(struct sipe_core_private
*sipe_private
)
696 sipe_media_call
*call
= sipe_private
->media_call
;
698 sipe_backend_media_hangup(call
->media
, FALSE
);
702 sipe_media_initiate_call(struct sipe_core_private
*sipe_private
, const char *participant
)
704 sipe_media_call
*call
;
705 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
707 if (sipe_private
->media_call
)
710 call
= sipe_media_call_init(sipe_private
, participant
, TRUE
);
712 sipe_private
->media_call
= call
;
714 call
->session
= sipe_session_add_chat(sip
);
715 call
->dialog
= sipe_dialog_add(call
->session
);
716 call
->dialog
->callid
= gencallid();
717 call
->dialog
->with
= g_strdup(participant
);
718 call
->dialog
->ourtag
= gentag();
720 sipe_backend_media_add_stream(call
->media
, participant
, SIPE_MEDIA_AUDIO
,
721 !call
->legacy_mode
, TRUE
);
726 sipe_media_incoming_invite(struct sipe_core_private
*sipe_private
, struct sipmsg
*msg
)
728 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
730 sipe_media_call
*call
= sipe_private
->media_call
;
731 struct sip_session
*session
;
732 struct sip_dialog
*dialog
;
733 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
736 if (sipe_strequal(call
->dialog
->callid
, callid
)) {
737 ++(call
->invite_cnt
);
739 if (call
->invitation
)
740 sipmsg_free(call
->invitation
);
741 call
->invitation
= sipmsg_copy(msg
);
743 sipe_utils_nameval_free(call
->sdp_attrs
);
744 call
->sdp_attrs
= NULL
;
745 if (!sipe_media_parse_sdp_attributes_and_candidates(call
, call
->invitation
->body
)) {
746 // TODO: handle error
748 if (!sipe_media_parse_remote_codecs(call
)) {
753 if (call
->legacy_mode
&& !call
->remote_on_hold
) {
754 sipe_backend_media_hold(call
->media
, FALSE
);
755 } else if (sipe_utils_nameval_find(call
->sdp_attrs
, "inactive")) {
756 sipe_backend_media_hold(call
->media
, FALSE
);
757 } else if (call
->remote_on_hold
) {
758 sipe_backend_media_unhold(call
->media
, FALSE
);
760 send_response_with_session_description(call
, 200, "OK");
763 send_sip_response(sipe_private
, msg
, 486, "Busy Here", NULL
);
768 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
769 dialog
= sipe_media_dialog_init(session
, msg
);
771 call
= sipe_media_call_init(sipe_private
, dialog
->with
, FALSE
);
772 call
->invitation
= sipmsg_copy(msg
);
773 call
->session
= session
;
774 call
->dialog
= dialog
;
775 call
->invite_cnt
= 1;
777 sipe_private
->media_call
= call
;
779 if (!sipe_media_parse_sdp_attributes_and_candidates(call
, msg
->body
)) {
783 sipe_backend_media_add_stream(call
->media
, dialog
->with
, SIPE_MEDIA_AUDIO
, !call
->legacy_mode
, FALSE
);
784 sipe_backend_media_add_remote_candidates(call
->media
, dialog
->with
, call
->remote_candidates
);
786 send_sip_response(sipe_private
, call
->invitation
, 180, "Ringing", NULL
);
788 // Processing continues in candidates_prepared_cb
792 sipe_media_send_ack(struct sipe_core_private
*sipe_private
,
793 SIPE_UNUSED_PARAMETER
struct sipmsg
*msg
,
794 struct transaction
*trans
)
796 sipe_media_call
*call
= sipe_private
->media_call
;
797 struct sip_dialog
*dialog
;
801 if (!call
|| !call
->dialog
)
804 dialog
= call
->dialog
;
805 tmp_cseq
= dialog
->cseq
;
807 sscanf(trans
->key
, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq
);
808 dialog
->cseq
= trans_cseq
- 1;
809 send_sip_request(sipe_private
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
810 dialog
->cseq
= tmp_cseq
;
816 sipe_media_process_invite_response(struct sipe_core_private
*sipe_private
,
818 struct transaction
*trans
)
820 const gchar
* callid
= sipmsg_find_header(msg
, "Call-ID");
821 sipe_media_call
*call
= sipe_private
->media_call
;
823 if (!call
|| !sipe_strequal(sipe_media_get_callid(call
), callid
))
826 if (msg
->response
== 183) {
827 // Session in progress
828 const gchar
*rseq
= sipmsg_find_header(msg
, "RSeq");
829 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
830 gchar
*rack
= g_strdup_printf("RAck: %s %s\r\n", rseq
, cseq
);
832 if (!sipe_media_parse_sdp_attributes_and_candidates(call
, msg
->body
)) {
833 // TODO: handle error
836 if (!sipe_media_parse_remote_codecs(call
)) {
841 sipe_backend_media_add_remote_candidates(call
->media
, call
->dialog
->with
, call
->remote_candidates
);
843 sipe_dialog_parse(call
->dialog
, msg
, TRUE
);
845 send_sip_request(sipe_private
, "PRACK", call
->dialog
->with
, call
->dialog
->with
, rack
, NULL
, call
->dialog
, NULL
);
847 } else if (msg
->response
== 603) {
848 sipe_backend_media_reject(call
->media
, FALSE
);
849 sipe_media_send_ack(sipe_private
, msg
, trans
);
851 //PurpleMedia* m = (PurpleMedia*) call->media;
852 //purple_media_stream_info(m, PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, FALSE);
853 sipe_media_send_ack(sipe_private
, msg
, trans
);
854 sipe_invite_call(sipe_private
, sipe_media_send_ack
);