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
29 #include "sipe-core.h"
32 #include "sipe-session.h"
33 #include "sipe-media.h"
34 #include "sipe-dialog.h"
35 #include "sipe-utils.h"
36 #include "sipe-common.h"
39 sipe_media_get_callid(sipe_media_call
*call
)
41 return call
->dialog
->callid
;
44 void sipe_media_codec_list_free(GList
*codecs
)
46 for (; codecs
; codecs
= g_list_delete_link(codecs
, codecs
)) {
47 sipe_backend_codec_free(codecs
->data
);
51 void sipe_media_candidate_list_free(GList
*candidates
)
53 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
)) {
54 sipe_backend_candidate_free(candidates
->data
);
59 sipe_media_call_free(sipe_media_call
*call
)
62 sipe_utils_nameval_free(call
->sdp_attrs
);
64 sipmsg_free(call
->invitation
);
65 sipe_media_codec_list_free(call
->remote_codecs
);
66 sipe_media_candidate_list_free(call
->remote_candidates
);
72 sipe_media_parse_remote_codecs(const sipe_media_call
*call
)
78 while ((attr
= sipe_utils_nameval_find_instance(call
->sdp_attrs
, "rtpmap", i
++))) {
79 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
81 int id
= atoi(tokens
[0]);
82 gchar
*name
= tokens
[1];
83 int clock_rate
= atoi(tokens
[2]);
84 SipeMediaType type
= SIPE_MEDIA_AUDIO
;
86 sipe_codec
*codec
= sipe_backend_codec_new(id
, name
, clock_rate
, type
);
88 codecs
= g_list_append(codecs
, codec
);
96 codec_name_compare(sipe_codec
* codec1
, sipe_codec
* codec2
)
98 gchar
*name1
= sipe_backend_codec_get_name(codec1
);
99 gchar
*name2
= sipe_backend_codec_get_name(codec2
);
101 return g_strcmp0(name1
, name2
);
105 sipe_media_prune_remote_codecs(sipe_media_call
*call
, GList
*codecs
)
107 GList
*remote_codecs
= codecs
;
108 GList
*local_codecs
= sipe_backend_get_local_codecs(call
);
109 GList
*pruned_codecs
= NULL
;
111 while (remote_codecs
) {
112 sipe_codec
*c
= remote_codecs
->data
;
114 if (g_list_find_custom(local_codecs
, c
, (GCompareFunc
)codec_name_compare
)) {
115 pruned_codecs
= g_list_append(pruned_codecs
, c
);
116 remote_codecs
->data
= NULL
;
118 remote_codecs
= remote_codecs
->next
;
121 sipe_media_codec_list_free(codecs
);
123 return pruned_codecs
;
127 sipe_media_parse_remote_candidates(sipe_media_call
*call
)
129 GSList
*sdp_attrs
= call
->sdp_attrs
;
130 sipe_candidate
*candidate
;
131 GList
*candidates
= NULL
;
135 const gchar
* username
= sipe_utils_nameval_find(sdp_attrs
, "ice-ufrag");
136 const gchar
* password
= sipe_utils_nameval_find(sdp_attrs
, "ice-pwd");
138 while ((attr
= sipe_utils_nameval_find_instance(sdp_attrs
, "candidate", i
++))) {
141 SipeComponentType component
;
142 SipeNetworkProtocol protocol
;
146 SipeCandidateType type
;
148 tokens
= g_strsplit_set(attr
, " ", 0);
150 foundation
= tokens
[0];
152 switch (atoi(tokens
[1])) {
154 component
= SIPE_COMPONENT_RTP
;
157 component
= SIPE_COMPONENT_RTCP
;
160 component
= SIPE_COMPONENT_NONE
;
163 if (sipe_strequal(tokens
[2], "UDP"))
164 protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
166 // Ignore TCP candidates, at least for now...
171 priority
= atoi(tokens
[3]);
173 port
= atoi(tokens
[5]);
175 if (sipe_strequal(tokens
[7], "host"))
176 type
= SIPE_CANDIDATE_TYPE_HOST
;
177 else if (sipe_strequal(tokens
[7], "relay"))
178 type
= SIPE_CANDIDATE_TYPE_RELAY
;
179 else if (sipe_strequal(tokens
[7], "srflx"))
180 type
= SIPE_CANDIDATE_TYPE_SRFLX
;
186 candidate
= sipe_backend_candidate_new(foundation
, component
,
187 type
, protocol
, ip
, port
);
188 sipe_backend_candidate_set_priority(candidate
, priority
);
189 candidates
= g_list_append(candidates
, candidate
);
195 // No a=candidate in SDP message, revert to OC2005 behaviour
196 candidate
= sipe_backend_candidate_new("foundation",
198 SIPE_CANDIDATE_TYPE_HOST
,
199 SIPE_NETWORK_PROTOCOL_UDP
,
200 call
->remote_ip
, call
->remote_port
);
201 candidates
= g_list_append(candidates
, candidate
);
203 candidate
= sipe_backend_candidate_new("foundation",
205 SIPE_CANDIDATE_TYPE_HOST
,
206 SIPE_NETWORK_PROTOCOL_UDP
,
207 call
->remote_ip
, call
->remote_port
+ 1);
208 candidates
= g_list_append(candidates
, candidate
);
210 // This seems to be pre-OC2007 R2 UAC
211 call
->legacy_mode
= TRUE
;
215 GList
*it
= candidates
;
217 sipe_backend_candidate_set_username_and_pwd(it
->data
, username
, password
);
226 sipe_media_sdp_codec_ids_format(GList
*codecs
)
228 GString
*result
= g_string_new(NULL
);
231 sipe_codec
*c
= codecs
->data
;
233 gchar
*tmp
= g_strdup_printf(" %d", sipe_backend_codec_get_id(c
));
234 g_string_append(result
,tmp
);
237 codecs
= codecs
->next
;
240 return g_string_free(result
, FALSE
);
244 sipe_media_sdp_codecs_format(GList
*codecs
)
246 GString
*result
= g_string_new(NULL
);
249 sipe_codec
*c
= codecs
->data
;
250 GList
*params
= NULL
;
252 gchar
*tmp
= g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
253 sipe_backend_codec_get_id(c
),
254 sipe_backend_codec_get_name(c
),
255 sipe_backend_codec_get_clock_rate(c
));
257 g_string_append(result
, tmp
);
260 if ((params
= sipe_backend_codec_get_optional_parameters(c
))) {
261 tmp
= g_strdup_printf("a=fmtp:%d",sipe_backend_codec_get_id(c
));
262 g_string_append(result
, tmp
);
266 struct sipnameval
* par
= params
->data
;
267 tmp
= g_strdup_printf(" %s=%s", par
->name
, par
->value
);
268 g_string_append(result
, tmp
);
270 params
= params
->next
;
272 g_string_append(result
, "\r\n");
275 codecs
= codecs
->next
;
278 return g_string_free(result
, FALSE
);
282 sipe_media_sdp_candidates_format(GList
*candidates
, sipe_media_call
* call
, gboolean remote_candidate
)
284 GString
*result
= g_string_new("");
286 gchar
*username
= sipe_backend_candidate_get_username(candidates
->data
);
287 gchar
*password
= sipe_backend_candidate_get_password(candidates
->data
);
288 guint16 rtcp_port
= 0;
290 if (call
->legacy_mode
)
291 return g_string_free(result
, FALSE
);
293 tmp
= g_strdup_printf("a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",username
, password
);
294 g_string_append(result
, tmp
);
298 sipe_candidate
*c
= candidates
->data
;
305 port
= sipe_backend_candidate_get_port(c
);
307 switch (sipe_backend_candidate_get_component_type(c
)) {
308 case SIPE_COMPONENT_RTP
:
311 case SIPE_COMPONENT_RTCP
:
316 case SIPE_COMPONENT_NONE
:
320 switch (sipe_backend_candidate_get_protocol(c
)) {
321 case SIPE_NETWORK_PROTOCOL_TCP
:
324 case SIPE_NETWORK_PROTOCOL_UDP
:
329 switch (sipe_backend_candidate_get_type(c
)) {
330 case SIPE_CANDIDATE_TYPE_HOST
:
333 case SIPE_CANDIDATE_TYPE_RELAY
:
336 case SIPE_CANDIDATE_TYPE_SRFLX
:
340 // TODO: error unknown/unsupported type
344 tmp
= g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
345 sipe_backend_candidate_get_foundation(c
),
348 sipe_backend_candidate_get_priority(c
),
349 sipe_backend_candidate_get_ip(c
),
353 g_string_append(result
, tmp
);
356 candidates
= candidates
->next
;
359 if (remote_candidate
&& call
->remote_candidates
) {
360 sipe_candidate
*first
= call
->remote_candidates
->data
;
361 sipe_candidate
*second
= call
->remote_candidates
->next
->data
;
362 tmp
= g_strdup_printf("a=remote-candidates:1 %s %u 2 %s %u\r\n",
363 sipe_backend_candidate_get_ip(first
), sipe_backend_candidate_get_port(first
),
364 sipe_backend_candidate_get_ip(second
), sipe_backend_candidate_get_port(second
));
366 g_string_append(result
, tmp
);
371 if (rtcp_port
!= 0) {
372 tmp
= g_strdup_printf("a=maxptime:200\r\na=rtcp:%u\r\n", rtcp_port
);
373 g_string_append(result
, tmp
);
377 return g_string_free(result
, FALSE
);
381 sipe_media_create_sdp(sipe_media_call
*call
, gboolean remote_candidate
) {
382 GList
*local_codecs
= sipe_backend_get_local_codecs(call
);
383 GList
*local_candidates
= sipe_backend_get_local_candidates(call
, call
->dialog
->with
);
385 // TODO: more sophisticated
386 guint16 local_port
= sipe_backend_candidate_get_port(local_candidates
->data
);
387 const char *ip
= sipe_utils_get_suitable_local_ip(-1);
389 gchar
*sdp_codecs
= sipe_media_sdp_codecs_format(local_codecs
);
390 gchar
*sdp_codec_ids
= sipe_media_sdp_codec_ids_format(local_codecs
);
391 gchar
*sdp_candidates
= sipe_media_sdp_candidates_format(local_candidates
, call
, remote_candidate
);
392 gchar
*inactive
= call
->state
== SIPE_CALL_HELD
? "a=inactive\r\n" : "";
394 gchar
*body
= g_strdup_printf(
396 "o=- 0 0 IN IP4 %s\r\n"
401 "m=audio %d RTP/AVP%s\r\n"
405 "a=encryption:rejected\r\n"
406 ,ip
, ip
, local_port
, sdp_codec_ids
, sdp_candidates
, inactive
, sdp_codecs
);
409 g_free(sdp_codec_ids
);
410 g_free(sdp_candidates
);
416 sipe_invite_call(struct sipe_account_data
*sip
, TransCallback tc
)
421 sipe_media_call
*call
= sip
->media_call
;
422 struct sip_dialog
*dialog
= call
->dialog
;
424 contact
= get_contact(sip
);
425 hdr
= g_strdup_printf(
426 "Supported: ms-early-media\r\n"
427 "Supported: 100rel\r\n"
428 "ms-keep-alive: UAC;hop-hop=yes\r\n"
430 "Content-Type: application/sdp\r\n",
432 call
->state
== SIPE_CALL_HELD
? ";+sip.rendering=\"no\"" : "");
435 body
= sipe_media_create_sdp(call
, TRUE
);
437 send_sip_request(sip
->gc
, "INVITE", dialog
->with
, dialog
->with
, hdr
, body
,
445 notify_state_change(struct sipe_account_data
*sip
, gboolean local
) {
447 sipe_invite_call(sip
, NULL
);
449 gchar
* body
= sipe_media_create_sdp(sip
->media_call
, TRUE
);
450 send_sip_response(sip
->gc
, sip
->media_call
->invitation
, 200, "OK", body
);
456 sipe_media_parse_sdp_frame(sipe_media_call
* call
, gchar
*frame
) {
457 gchar
**lines
= g_strsplit(frame
, "\r\n", 0);
458 GSList
*sdp_attrs
= NULL
;
459 gchar
*remote_ip
= NULL
;
460 guint16 remote_port
= 0;
462 gboolean no_error
= TRUE
;
464 for (ptr
= lines
; *ptr
!= NULL
; ++ptr
) {
465 if (g_str_has_prefix(*ptr
, "a=")) {
466 gchar
**parts
= g_strsplit(*ptr
+ 2, ":", 2);
469 sipe_utils_nameval_free(sdp_attrs
);
474 sdp_attrs
= sipe_utils_nameval_add(sdp_attrs
, parts
[0], parts
[1]);
477 } else if (g_str_has_prefix(*ptr
, "o=")) {
478 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 6);
479 remote_ip
= g_strdup(parts
[5]);
481 } else if (g_str_has_prefix(*ptr
, "m=")) {
482 gchar
**parts
= g_strsplit(*ptr
+ 2, " ", 3);
483 remote_port
= atoi(parts
[1]);
491 sipe_utils_nameval_free(call
->sdp_attrs
);
492 call
->sdp_attrs
= sdp_attrs
;
493 call
->remote_ip
= remote_ip
;
494 call
->remote_port
= remote_port
;
500 static struct sip_dialog
*
501 sipe_media_dialog_init(struct sip_session
* session
, struct sipmsg
*msg
)
503 gchar
*newTag
= gentag();
504 const gchar
*oldHeader
;
506 struct sip_dialog
*dialog
;
508 oldHeader
= sipmsg_find_header(msg
, "To");
509 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
510 sipmsg_remove_header_now(msg
, "To");
511 sipmsg_add_header_now(msg
, "To", newHeader
);
514 dialog
= sipe_dialog_add(session
);
515 dialog
->callid
= g_strdup(session
->callid
);
516 dialog
->with
= parse_from(sipmsg_find_header(msg
, "From"));
517 sipe_dialog_parse(dialog
, msg
, FALSE
);
523 sipe_media_process_invite_response(struct sipe_account_data
*sip
,
525 struct transaction
*trans
);
527 static void candidates_prepared_cb(sipe_media_call
*call
)
529 if (sipe_backend_media_is_initiator(call
->media
, call
->dialog
->with
)) {
530 sipe_invite_call(call
->sip
, sipe_media_process_invite_response
);
531 } else if (!call
->legacy_mode
) {
532 PurpleAccount
* account
= purple_media_get_account(call
->media
);
534 if (!call
->sdp_response
)
535 call
->sdp_response
= sipe_media_create_sdp(call
, FALSE
);
537 send_sip_response(account
->gc
, call
->invitation
, 183, "Session Progress", call
->sdp_response
);
541 static void media_connected_cb(sipe_media_call
*call
)
546 static void call_accept_cb(sipe_media_call
*call
, gboolean local
)
549 PurpleAccount
* account
= call
->sip
->account
;
551 if (!call
->sdp_response
)
552 call
->sdp_response
= sipe_media_create_sdp(call
, FALSE
);
554 send_sip_response(account
->gc
, call
->invitation
, 200, "OK", call
->sdp_response
);
555 call
->state
= SIPE_CALL_RUNNING
;
559 static void call_reject_cb(sipe_media_call
*call
, gboolean local
)
562 PurpleAccount
*account
= call
->sip
->account
;
563 send_sip_response(account
->gc
, call
->invitation
, 603, "Decline", NULL
);
564 call
->sip
->media_call
= NULL
;
565 sipe_media_call_free(call
);
569 static void call_hold_cb(sipe_media_call
*call
, gboolean local
)
571 if (call
->state
== SIPE_CALL_HELD
)
574 call
->state
= SIPE_CALL_HELD
;
575 notify_state_change(call
->sip
, local
);
576 sipe_backend_media_hold(call
->media
, TRUE
);
579 static void call_unhold_cb(sipe_media_call
*call
, gboolean local
)
581 if (call
->state
== SIPE_CALL_RUNNING
)
584 call
->state
= SIPE_CALL_RUNNING
;
585 notify_state_change(call
->sip
, local
);
586 sipe_backend_media_unhold(call
->media
, TRUE
);
589 static void call_hangup_cb(sipe_media_call
*call
, gboolean local
)
591 call
->state
= SIPE_CALL_FINISHED
;
593 send_sip_request(call
->sip
->gc
, "BYE", call
->dialog
->with
, call
->dialog
->with
,
594 NULL
, NULL
, call
->dialog
, NULL
);
595 call
->sip
->media_call
= NULL
;
596 sipe_media_call_free(call
);
599 static sipe_media_call
*
600 sipe_media_call_init()
602 sipe_media_call
*call
= g_new0(sipe_media_call
, 1);
604 call
->legacy_mode
= FALSE
;
605 call
->state
= SIPE_CALL_CONNECTING
;
607 call
->candidates_prepared_cb
= candidates_prepared_cb
;
608 call
->media_connected_cb
= media_connected_cb
;
609 call
->call_accept_cb
= call_accept_cb
;
610 call
->call_reject_cb
= call_reject_cb
;
611 call
->call_hold_cb
= call_hold_cb
;
612 call
->call_unhold_cb
= call_unhold_cb
;
613 call
->call_hangup_cb
= call_hangup_cb
;
618 void sipe_media_hold(struct sipe_account_data
*sip
)
621 sipe_backend_media_hold(sip
->media_call
->media
, FALSE
);
624 void sipe_media_unhold(struct sipe_account_data
*sip
)
627 sipe_backend_media_unhold(sip
->media_call
->media
, FALSE
);
630 void sipe_media_hangup(struct sipe_account_data
*sip
)
633 sipe_backend_media_hangup(sip
->media_call
->media
, FALSE
);
637 sipe_media_initiate_call(struct sipe_account_data
*sip
, const char *participant
)
639 sipe_media_call
*call
;
644 call
= sipe_media_call_init();
645 sip
->media_call
= call
;
648 call
->session
= sipe_session_add_chat(sip
);
649 call
->dialog
= sipe_dialog_add(call
->session
);
650 call
->dialog
->callid
= gencallid();
651 call
->dialog
->with
= g_strdup(participant
);
652 call
->dialog
->ourtag
= gentag();
654 call
->media
= sipe_backend_media_new(call
, participant
, TRUE
);
656 sipe_backend_media_add_stream(call
->media
, participant
, SIPE_MEDIA_AUDIO
,
657 !call
->legacy_mode
, TRUE
);
662 sipe_media_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
664 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
667 sipe_media_call
*call
;
668 struct sip_session
*session
;
669 struct sip_dialog
*dialog
;
671 if (sip
->media_call
) {
672 if (sipe_strequal(sip
->media_call
->dialog
->callid
, callid
)) {
675 call
= sip
->media_call
;
677 sipmsg_free(call
->invitation
);
678 call
->invitation
= sipmsg_copy(msg
);
680 sipmsg_add_header(call
->invitation
, "Supported", "Replaces");
682 sipe_utils_nameval_free(call
->sdp_attrs
);
683 call
->sdp_attrs
= NULL
;
684 if (!sipe_media_parse_sdp_frame(call
, call
->invitation
->body
)) {
685 // TODO: handle error
688 if (call
->legacy_mode
&& call
->state
== SIPE_CALL_RUNNING
) {
689 sipe_media_hold(sip
);
693 if (sipe_utils_nameval_find(call
->sdp_attrs
, "inactive")) {
694 sipe_media_hold(sip
);
698 if (call
->state
== SIPE_CALL_HELD
) {
699 sipe_media_unhold(sip
);
703 call
->remote_codecs
= sipe_media_parse_remote_codecs(call
);
704 call
->remote_codecs
= sipe_media_prune_remote_codecs(call
, call
->remote_codecs
);
705 if (!call
->remote_codecs
) {
706 // TODO: error no remote codecs
708 if (sipe_backend_set_remote_codecs(call
, call
->dialog
->with
) == FALSE
)
709 printf("ERROR SET REMOTE CODECS"); // TODO
711 rsp
= sipe_media_create_sdp(sip
->media_call
, TRUE
);
712 send_sip_response(sip
->gc
, call
->invitation
, 200, "OK", rsp
);
715 send_sip_response(sip
->gc
, msg
, 486, "Busy Here", NULL
);
720 call
= sipe_media_call_init();
721 call
->invitation
= sipmsg_copy(msg
);
723 if (sipe_media_parse_sdp_frame(call
, msg
->body
) == FALSE
) {
728 call
->remote_candidates
= sipe_media_parse_remote_candidates(call
);
730 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
731 dialog
= sipe_media_dialog_init(session
, call
->invitation
);
734 call
->session
= session
;
735 call
->dialog
= dialog
;
737 media
= sipe_backend_media_new(call
, dialog
->with
, FALSE
);
740 sipe_backend_media_add_stream(media
, dialog
->with
, SIPE_MEDIA_AUDIO
, !call
->legacy_mode
, FALSE
);
742 sipe_backend_media_add_remote_candidates(media
, dialog
->with
, call
->remote_candidates
);
744 call
->remote_codecs
= sipe_media_parse_remote_codecs(call
);
745 call
->remote_codecs
= sipe_media_prune_remote_codecs(call
, call
->remote_codecs
);
746 if (!call
->remote_candidates
|| !call
->remote_codecs
) {
747 sipe_media_call_free(call
);
748 sip
->media_call
= NULL
;
749 printf("ERROR NO CANDIDATES OR CODECS");
752 if (sipe_backend_set_remote_codecs(call
, dialog
->with
) == FALSE
)
753 printf("ERROR SET REMOTE CODECS"); // TODO
755 sip
->media_call
= call
;
757 send_sip_response(sip
->gc
, call
->invitation
, 180, "Ringing", NULL
);
761 sipe_media_process_invite_response(struct sipe_account_data
*sip
,
763 struct transaction
*trans
)
765 const gchar
* callid
= sipmsg_find_header(msg
, "Call-ID");
766 sipe_media_call
*call
= sip
->media_call
;
768 if (!call
|| !sipe_strequal(sipe_media_get_callid(call
), callid
))
771 if (msg
->response
== 183) {
772 // Session in progress
773 const gchar
*rseq
= sipmsg_find_header(msg
, "RSeq");
774 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
775 gchar
*rack
= g_strdup_printf("RAck: %s %s\r\n", rseq
, cseq
);
777 sipe_utils_nameval_free(call
->sdp_attrs
);
778 call
->sdp_attrs
= NULL
;
779 if (!sipe_media_parse_sdp_frame(call
, msg
->body
)) {
780 // TODO: handle error
783 call
->remote_candidates
= sipe_media_parse_remote_candidates(call
);
784 call
->remote_codecs
= sipe_media_parse_remote_codecs(call
);
785 call
->remote_codecs
= sipe_media_prune_remote_codecs(call
, call
->remote_codecs
);
786 if (!call
->remote_codecs
) {
787 // TODO: error no remote codecs
789 if (sipe_backend_set_remote_codecs(call
, call
->dialog
->with
) == FALSE
)
790 printf("ERROR SET REMOTE CODECS"); // TODO
792 sipe_backend_media_add_remote_candidates(call
->media
, call
->dialog
->with
, call
->remote_candidates
);
794 sipe_dialog_parse(call
->dialog
, msg
, TRUE
);
796 send_sip_request(sip
->gc
, "PRACK", call
->dialog
->with
, call
->dialog
->with
, rack
, NULL
, call
->dialog
, NULL
);
800 int tmp_cseq
= call
->dialog
->cseq
;
801 //PurpleMedia* m = (PurpleMedia*) call->media;
802 //purple_media_stream_info(m, PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, FALSE);
804 sscanf(trans
->key
, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq
);
805 call
->dialog
->cseq
= trans_cseq
- 1;
806 send_sip_request(sip
->gc
, "ACK", call
->dialog
->with
, call
->dialog
->with
, NULL
, NULL
, call
->dialog
, NULL
);
807 call
->dialog
->cseq
= tmp_cseq
;
808 sipe_invite_call(sip
, NULL
);