audio: fixes for call hangup/resume on both local and remote sides
[siplcs.git] / src / core / sipe-media.c
blob20e2851ee5fef7e5807117694b5a173f004148ad
1 /**
2 * @file sipe-media.c
4 * pidgin-sipe
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
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <glib.h>
28 #include <stdio.h>
29 #include <stdlib.h>
31 #include "sipe-core.h"
32 #include "sipe-core-private.h"
33 #include "sipe.h"
34 #include "sipmsg.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"
42 gchar *
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);
60 static void
61 sipe_media_call_free(sipe_media_call *call)
63 if (call) {
64 sipe_utils_nameval_free(call->sdp_attrs);
65 if (call->invitation)
66 sipmsg_free(call->invitation);
67 sipe_media_codec_list_free(call->remote_codecs);
68 sipe_media_candidate_list_free(call->remote_candidates);
69 g_free(call);
73 static GList *
74 sipe_media_parse_codecs(GSList *sdp_attrs)
76 int i = 0;
77 const gchar *attr;
78 GList *codecs = NULL;
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
91 int j = 0;
92 const gchar* params;
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) {
98 while (*next) {
99 gchar name[50];
100 gchar value[50];
102 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
103 sipe_backend_codec_add_optional_parameter(codec, name, value);
105 ++next;
109 g_strfreev(tokens);
112 codecs = g_list_append(codecs, codec);
113 g_strfreev(tokens);
116 return codecs;
119 static gint
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 return g_strcmp0(name1, name2);
128 static GList *
129 sipe_media_prune_remote_codecs(GList *local_codecs, GList *remote_codecs)
131 GList *remote_codecs_head = remote_codecs;
132 GList *pruned_codecs = NULL;
134 while (remote_codecs) {
135 sipe_codec *c = remote_codecs->data;
137 if (g_list_find_custom(local_codecs, c, (GCompareFunc)codec_name_compare)) {
138 pruned_codecs = g_list_append(pruned_codecs, c);
139 remote_codecs->data = NULL;
141 remote_codecs = remote_codecs->next;
144 sipe_media_codec_list_free(remote_codecs_head);
146 return pruned_codecs;
149 static GList *
150 sipe_media_parse_remote_candidates_legacy(gchar *remote_ip, guint16 remote_port)
152 sipe_candidate *candidate;
153 GList *candidates = NULL;
155 candidate = sipe_backend_candidate_new("foundation",
156 SIPE_COMPONENT_RTP,
157 SIPE_CANDIDATE_TYPE_HOST,
158 SIPE_NETWORK_PROTOCOL_UDP,
159 remote_ip, remote_port);
160 candidates = g_list_append(candidates, candidate);
162 candidate = sipe_backend_candidate_new("foundation",
163 SIPE_COMPONENT_RTCP,
164 SIPE_CANDIDATE_TYPE_HOST,
165 SIPE_NETWORK_PROTOCOL_UDP,
166 remote_ip, remote_port + 1);
167 candidates = g_list_append(candidates, candidate);
169 return candidates;
172 static GList *
173 sipe_media_parse_remote_candidates(GSList *sdp_attrs)
175 sipe_candidate *candidate;
176 GList *candidates = NULL;
177 const gchar *attr;
178 int i = 0;
180 const gchar* username = sipe_utils_nameval_find(sdp_attrs, "ice-ufrag");
181 const gchar* password = sipe_utils_nameval_find(sdp_attrs, "ice-pwd");
183 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "candidate", i++))) {
184 gchar **tokens;
185 gchar *foundation;
186 SipeComponentType component;
187 SipeNetworkProtocol protocol;
188 guint32 priority;
189 gchar* ip;
190 guint16 port;
191 SipeCandidateType type;
193 tokens = g_strsplit_set(attr, " ", 0);
195 foundation = tokens[0];
197 switch (atoi(tokens[1])) {
198 case 1:
199 component = SIPE_COMPONENT_RTP;
200 break;
201 case 2:
202 component = SIPE_COMPONENT_RTCP;
203 break;
204 default:
205 component = SIPE_COMPONENT_NONE;
208 if (sipe_strequal(tokens[2], "UDP"))
209 protocol = SIPE_NETWORK_PROTOCOL_UDP;
210 else {
211 // Ignore TCP candidates, at least for now...
212 g_strfreev(tokens);
213 continue;
216 priority = atoi(tokens[3]);
217 ip = tokens[4];
218 port = atoi(tokens[5]);
220 if (sipe_strequal(tokens[7], "host"))
221 type = SIPE_CANDIDATE_TYPE_HOST;
222 else if (sipe_strequal(tokens[7], "relay"))
223 type = SIPE_CANDIDATE_TYPE_RELAY;
224 else if (sipe_strequal(tokens[7], "srflx"))
225 type = SIPE_CANDIDATE_TYPE_SRFLX;
226 else {
227 g_strfreev(tokens);
228 continue;
231 candidate = sipe_backend_candidate_new(foundation, component,
232 type, protocol, ip, port);
233 sipe_backend_candidate_set_priority(candidate, priority);
234 candidates = g_list_append(candidates, candidate);
236 g_strfreev(tokens);
239 if (username) {
240 GList *it = candidates;
241 while (it) {
242 sipe_backend_candidate_set_username_and_pwd(it->data, username, password);
243 it = it->next;
247 return candidates;
250 static gchar *
251 sipe_media_sdp_codec_ids_format(GList *codecs)
253 GString *result = g_string_new(NULL);
255 while (codecs) {
256 sipe_codec *c = codecs->data;
258 gchar *tmp = g_strdup_printf(" %d", sipe_backend_codec_get_id(c));
259 g_string_append(result,tmp);
260 g_free(tmp);
262 codecs = codecs->next;
265 return g_string_free(result, FALSE);
268 static gchar *
269 sipe_media_sdp_codecs_format(GList *codecs)
271 GString *result = g_string_new(NULL);
273 while (codecs) {
274 sipe_codec *c = codecs->data;
275 GList *params = NULL;
277 gchar *tmp = g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
278 sipe_backend_codec_get_id(c),
279 sipe_backend_codec_get_name(c),
280 sipe_backend_codec_get_clock_rate(c));
282 g_string_append(result, tmp);
283 g_free(tmp);
285 if ((params = sipe_backend_codec_get_optional_parameters(c))) {
286 tmp = g_strdup_printf("a=fmtp:%d",sipe_backend_codec_get_id(c));
287 g_string_append(result, tmp);
288 g_free(tmp);
290 while (params) {
291 struct sipnameval* par = params->data;
292 tmp = g_strdup_printf(" %s=%s", par->name, par->value);
293 g_string_append(result, tmp);
294 g_free(tmp);
295 params = params->next;
297 g_string_append(result, "\r\n");
300 codecs = codecs->next;
303 return g_string_free(result, FALSE);
306 static gchar *
307 sipe_media_sdp_candidates_format(GList *candidates, sipe_media_call* call, gboolean remote_candidate)
309 GString *result = g_string_new("");
310 gchar *tmp;
311 gchar *username = sipe_backend_candidate_get_username(candidates->data);
312 gchar *password = sipe_backend_candidate_get_password(candidates->data);
313 guint16 rtcp_port = 0;
315 if (call->legacy_mode)
316 return g_string_free(result, FALSE);
318 tmp = g_strdup_printf("a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",username, password);
319 g_string_append(result, tmp);
320 g_free(tmp);
322 while (candidates) {
323 sipe_candidate *c = candidates->data;
325 guint16 port;
326 guint16 component;
327 gchar *protocol;
328 gchar *type;
330 port = sipe_backend_candidate_get_port(c);
332 switch (sipe_backend_candidate_get_component_type(c)) {
333 case SIPE_COMPONENT_RTP:
334 component = 1;
335 break;
336 case SIPE_COMPONENT_RTCP:
337 component = 2;
338 if (rtcp_port == 0)
339 rtcp_port = port;
340 break;
341 case SIPE_COMPONENT_NONE:
342 component = 0;
345 switch (sipe_backend_candidate_get_protocol(c)) {
346 case SIPE_NETWORK_PROTOCOL_TCP:
347 protocol = "TCP";
348 break;
349 case SIPE_NETWORK_PROTOCOL_UDP:
350 protocol = "UDP";
351 break;
354 switch (sipe_backend_candidate_get_type(c)) {
355 case SIPE_CANDIDATE_TYPE_HOST:
356 type = "host";
357 break;
358 case SIPE_CANDIDATE_TYPE_RELAY:
359 type = "relay";
360 break;
361 case SIPE_CANDIDATE_TYPE_SRFLX:
362 type = "srflx";
363 break;
364 default:
365 // TODO: error unknown/unsupported type
366 break;
369 tmp = g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
370 sipe_backend_candidate_get_foundation(c),
371 component,
372 protocol,
373 sipe_backend_candidate_get_priority(c),
374 sipe_backend_candidate_get_ip(c),
375 port,
376 type);
378 g_string_append(result, tmp);
379 g_free(tmp);
381 candidates = candidates->next;
384 if (remote_candidate && call->remote_candidates) {
385 sipe_candidate *first = call->remote_candidates->data;
386 sipe_candidate *second = call->remote_candidates->next->data;
387 tmp = g_strdup_printf("a=remote-candidates:1 %s %u 2 %s %u\r\n",
388 sipe_backend_candidate_get_ip(first), sipe_backend_candidate_get_port(first),
389 sipe_backend_candidate_get_ip(second), sipe_backend_candidate_get_port(second));
391 g_string_append(result, tmp);
392 g_free(tmp);
396 if (rtcp_port != 0) {
397 tmp = g_strdup_printf("a=maxptime:200\r\na=rtcp:%u\r\n", rtcp_port);
398 g_string_append(result, tmp);
399 g_free(tmp);
402 return g_string_free(result, FALSE);
405 static gchar*
406 sipe_media_create_sdp(sipe_media_call *call, gboolean remote_candidate) {
407 GList *usable_codecs = sipe_backend_get_local_codecs(call);
408 GList *local_candidates = sipe_backend_get_local_candidates(call, call->dialog->with);
410 // TODO: more sophisticated
411 guint16 local_port = sipe_backend_candidate_get_port(local_candidates->data);
412 const char *ip = sipe_utils_get_suitable_local_ip(-1);
414 gchar *sdp_codecs = sipe_media_sdp_codecs_format(usable_codecs);
415 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(usable_codecs);
416 gchar *sdp_candidates = sipe_media_sdp_candidates_format(local_candidates, call, remote_candidate);
417 gchar *inactive = (call->local_on_hold || call->remote_on_hold) ? "a=inactive\r\n" : "";
419 gchar *body = g_strdup_printf(
420 "v=0\r\n"
421 "o=- 0 0 IN IP4 %s\r\n"
422 "s=session\r\n"
423 "c=IN IP4 %s\r\n"
424 "b=CT:99980\r\n"
425 "t=0 0\r\n"
426 "m=audio %d RTP/AVP%s\r\n"
427 "%s"
428 "%s"
429 "%s"
430 "a=encryption:rejected\r\n"
431 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
433 g_free(sdp_codecs);
434 g_free(sdp_codec_ids);
435 g_free(sdp_candidates);
437 return body;
440 static void
441 sipe_invite_call(struct sipe_account_data *sip, TransCallback tc)
443 gchar *hdr;
444 gchar *contact;
445 gchar *body;
446 sipe_media_call *call = sip->media_call;
447 struct sip_dialog *dialog = call->dialog;
449 contact = get_contact(sip);
450 hdr = g_strdup_printf(
451 "Supported: ms-early-media\r\n"
452 "Supported: 100rel\r\n"
453 "ms-keep-alive: UAC;hop-hop=yes\r\n"
454 "Contact: %s%s\r\n"
455 "Content-Type: application/sdp\r\n",
456 contact,
457 (call->local_on_hold || call->remote_on_hold) ? ";+sip.rendering=\"no\"" : "");
458 g_free(contact);
460 body = sipe_media_create_sdp(call, TRUE);
462 send_sip_request(SIP_TO_CORE_PRIVATE, "INVITE", dialog->with, dialog->with, hdr, body,
463 dialog, tc);
465 g_free(body);
466 g_free(hdr);
469 static gboolean
470 sipe_media_parse_remote_codecs(sipe_media_call *call);
472 static gboolean
473 sipe_media_parse_sdp_attributes_and_candidates(sipe_media_call* call, gchar *frame) {
474 gchar **lines = g_strsplit(frame, "\r\n", 0);
475 GSList *sdp_attrs = NULL;
476 gchar *remote_ip = NULL;
477 guint16 remote_port = 0;
478 GList *remote_candidates;
479 gchar **ptr;
480 gboolean no_error = TRUE;
482 for (ptr = lines; *ptr != NULL; ++ptr) {
483 if (g_str_has_prefix(*ptr, "a=")) {
484 gchar **parts = g_strsplit(*ptr + 2, ":", 2);
485 if(!parts[0]) {
486 g_strfreev(parts);
487 sipe_utils_nameval_free(sdp_attrs);
488 sdp_attrs = NULL;
489 no_error = FALSE;
490 break;
492 sdp_attrs = sipe_utils_nameval_add(sdp_attrs, parts[0], parts[1]);
493 g_strfreev(parts);
495 } else if (g_str_has_prefix(*ptr, "o=")) {
496 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
497 remote_ip = g_strdup(parts[5]);
498 g_strfreev(parts);
499 } else if (g_str_has_prefix(*ptr, "m=")) {
500 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
501 remote_port = atoi(parts[1]);
502 g_strfreev(parts);
506 g_strfreev(lines);
508 remote_candidates = sipe_media_parse_remote_candidates(sdp_attrs);
509 if (!remote_candidates) {
510 // No a=candidate in SDP message, revert to OC2005 behaviour
511 sipe_media_parse_remote_candidates_legacy(remote_ip, remote_port);
512 // This seems to be pre-OC2007 R2 UAC
513 call->legacy_mode = TRUE;
516 if (no_error) {
517 sipe_utils_nameval_free(call->sdp_attrs);
518 sipe_media_candidate_list_free(call->remote_candidates);
520 call->sdp_attrs = sdp_attrs;
521 call->remote_ip = remote_ip;
522 call->remote_port = remote_port;
523 call->remote_candidates = remote_candidates;
524 } else {
525 sipe_utils_nameval_free(sdp_attrs);
526 sipe_media_candidate_list_free(remote_candidates);
529 return no_error;
532 static gboolean
533 sipe_media_parse_remote_codecs(sipe_media_call *call)
535 GList *local_codecs = sipe_backend_get_local_codecs(call);
536 GList *remote_codecs;
538 remote_codecs = sipe_media_parse_codecs(call->sdp_attrs);
539 remote_codecs = sipe_media_prune_remote_codecs(local_codecs, remote_codecs);
541 if (remote_codecs) {
542 sipe_media_codec_list_free(call->remote_codecs);
544 call->remote_codecs = remote_codecs;
546 if (!sipe_backend_set_remote_codecs(call, call->dialog->with)) {
547 printf("ERROR SET REMOTE CODECS"); // TODO
548 return FALSE;
551 return TRUE;
552 } else {
553 sipe_media_codec_list_free(remote_codecs);
554 printf("ERROR NO CANDIDATES OR CODECS");
556 return FALSE;
560 static struct sip_dialog *
561 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
563 gchar *newTag = gentag();
564 const gchar *oldHeader;
565 gchar *newHeader;
566 struct sip_dialog *dialog;
568 oldHeader = sipmsg_find_header(msg, "To");
569 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
570 sipmsg_remove_header_now(msg, "To");
571 sipmsg_add_header_now(msg, "To", newHeader);
572 g_free(newHeader);
574 dialog = sipe_dialog_add(session);
575 dialog->callid = g_strdup(session->callid);
576 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
577 sipe_dialog_parse(dialog, msg, FALSE);
579 return dialog;
582 static gboolean
583 sipe_media_process_invite_response(struct sipe_account_data *sip,
584 struct sipmsg *msg,
585 struct transaction *trans);
587 static void candidates_prepared_cb(sipe_media_call *call)
589 if (sipe_backend_media_is_initiator(call->media, call->dialog->with)) {
590 sipe_invite_call(call->sip, sipe_media_process_invite_response);
591 } else if (!call->legacy_mode) {
592 struct sipe_account_data *sip = call->sip;
593 gchar *body;
595 if (!sipe_media_parse_remote_codecs(call)) {
596 g_free(call);
597 return;
600 body = sipe_media_create_sdp(call, FALSE);
602 sipmsg_add_header(call->invitation, "Content-Type", "application/sdp");
603 send_sip_response(SIP_TO_CORE_PRIVATE, call->invitation, 183, "Session Progress", body);
605 g_free(body);
609 static void media_connected_cb(sipe_media_call *call)
611 call = call;
614 static void call_accept_cb(sipe_media_call *call, gboolean local)
616 if (local) {
617 struct sipe_account_data *sip = call->sip;
618 gchar *body = sipe_media_create_sdp(call, FALSE);
620 sipmsg_add_header(call->invitation, "Content-Type", "application/sdp");
621 send_sip_response(SIP_TO_CORE_PRIVATE, call->invitation, 200, "OK", body);
623 g_free(body);
627 static void call_reject_cb(sipe_media_call *call, gboolean local)
629 if (local) {
630 struct sipe_account_data *sip = call->sip;
631 send_sip_response(SIP_TO_CORE_PRIVATE, call->invitation, 603, "Decline", NULL);
632 call->sip->media_call = NULL;
633 sipe_media_call_free(call);
637 static gboolean
638 sipe_media_send_ack(struct sipe_account_data *sip, struct sipmsg *msg,
639 struct transaction *trans);
641 static void call_hold_cb(sipe_media_call *call, gboolean local, gboolean state)
643 if (local && (call->local_on_hold != state)) {
644 call->local_on_hold = state;
645 sipe_invite_call(call->sip, sipe_media_send_ack);
646 } else if (call->remote_on_hold != state) {
647 struct sipe_account_data *sip = call->sip;
648 gchar* rsp;
650 call->remote_on_hold = state;
652 sipmsg_add_header(call->invitation, "Content-Type", "application/sdp");
653 rsp = sipe_media_create_sdp(sip->media_call, TRUE);
654 send_sip_response(SIP_TO_CORE_PRIVATE, call->invitation, 200, "OK", rsp);
655 g_free(rsp);
659 static void call_hangup_cb(sipe_media_call *call, gboolean local)
661 if (local) {
662 struct sipe_account_data *sip = call->sip;
663 send_sip_request(SIP_TO_CORE_PRIVATE, "BYE", call->dialog->with, call->dialog->with,
664 NULL, NULL, call->dialog, NULL);
666 call->sip->media_call = NULL;
667 sipe_media_call_free(call);
670 static sipe_media_call *
671 sipe_media_call_init(struct sipe_account_data *sip, const gchar* participant, gboolean initiator)
673 sipe_media_call *call = g_new0(sipe_media_call, 1);
675 call->sip = sip;
676 call->media = sipe_backend_media_new(call, participant, initiator);
678 call->legacy_mode = FALSE;
680 call->candidates_prepared_cb = candidates_prepared_cb;
681 call->media_connected_cb = media_connected_cb;
682 call->call_accept_cb = call_accept_cb;
683 call->call_reject_cb = call_reject_cb;
684 call->call_hold_cb = call_hold_cb;
685 call->call_hangup_cb = call_hangup_cb;
687 call->local_on_hold = FALSE;
688 call->remote_on_hold = FALSE;
690 return call;
693 void sipe_media_hangup(struct sipe_account_data *sip)
695 if (sip->media_call)
696 sipe_backend_media_hangup(sip->media_call->media, FALSE);
699 void
700 sipe_media_initiate_call(struct sipe_account_data *sip, const char *participant)
702 sipe_media_call *call;
704 if (sip->media_call)
705 return;
707 call = sipe_media_call_init(sip, participant, TRUE);
709 sip->media_call = call;
711 call->session = sipe_session_add_chat(sip);
712 call->dialog = sipe_dialog_add(call->session);
713 call->dialog->callid = gencallid();
714 call->dialog->with = g_strdup(participant);
715 call->dialog->ourtag = gentag();
717 sipe_backend_media_add_stream(call->media, participant, SIPE_MEDIA_AUDIO,
718 !call->legacy_mode, TRUE);
722 void
723 sipe_media_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
725 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
727 sipe_media_call *call;
728 struct sip_session *session;
729 struct sip_dialog *dialog;
731 if (sip->media_call) {
732 if (sipe_strequal(sip->media_call->dialog->callid, callid)) {
733 call = sip->media_call;
735 if (call->invitation)
736 sipmsg_free(call->invitation);
737 call->invitation = sipmsg_copy(msg);
739 sipe_utils_nameval_free(call->sdp_attrs);
740 call->sdp_attrs = NULL;
741 if (!sipe_media_parse_sdp_attributes_and_candidates(call, call->invitation->body)) {
742 // TODO: handle error
744 if (!sipe_media_parse_remote_codecs(call)) {
745 g_free(call);
746 return;
749 if (call->legacy_mode && !call->remote_on_hold) {
750 sipe_backend_media_hold(call->media, FALSE);
751 } else if (sipe_utils_nameval_find(call->sdp_attrs, "inactive")) {
752 sipe_backend_media_hold(call->media, FALSE);
753 } else if (call->remote_on_hold) {
754 sipe_backend_media_unhold(call->media, FALSE);
755 } else {
756 gchar *rsp = sipe_media_create_sdp(call, FALSE);
757 sipmsg_add_header(call->invitation, "Content-Type", "application/sdp");
758 send_sip_response(SIP_TO_CORE_PRIVATE, call->invitation, 200, "OK", rsp);
759 g_free(rsp);
761 } else {
762 send_sip_response(SIP_TO_CORE_PRIVATE, msg, 486, "Busy Here", NULL);
764 return;
767 session = sipe_session_find_or_add_chat_by_callid(sip, callid);
768 dialog = sipe_media_dialog_init(session, msg);
770 call = sipe_media_call_init(sip, dialog->with, FALSE);
771 call->invitation = sipmsg_copy(msg);
772 call->session = session;
773 call->dialog = dialog;
775 sip->media_call = call;
777 if (!sipe_media_parse_sdp_attributes_and_candidates(call, msg->body)) {
778 // TODO error
781 sipe_backend_media_add_stream(call->media, dialog->with, SIPE_MEDIA_AUDIO, !call->legacy_mode, FALSE);
782 sipe_backend_media_add_remote_candidates(call->media, dialog->with, call->remote_candidates);
784 send_sip_response(SIP_TO_CORE_PRIVATE, call->invitation, 180, "Ringing", NULL);
786 // Processing continues in candidates_prepared_cb
789 static gboolean
790 sipe_media_send_ack(struct sipe_account_data *sip,
791 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
792 struct transaction *trans)
794 struct sip_dialog *dialog;
795 int trans_cseq;
796 int tmp_cseq;
798 if (!sip->media_call || !sip->media_call->dialog)
799 return FALSE;
801 dialog = sip->media_call->dialog;
802 tmp_cseq = dialog->cseq;
804 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq);
805 dialog->cseq = trans_cseq - 1;
806 send_sip_request(SIP_TO_CORE_PRIVATE, "ACK", dialog->with, dialog->with, NULL, NULL, dialog, NULL);
807 dialog->cseq = tmp_cseq;
809 return TRUE;
812 static gboolean
813 sipe_media_process_invite_response(struct sipe_account_data *sip,
814 struct sipmsg *msg,
815 struct transaction *trans)
817 const gchar* callid = sipmsg_find_header(msg, "Call-ID");
818 sipe_media_call *call = sip->media_call;
820 if (!call || !sipe_strequal(sipe_media_get_callid(call), callid))
821 return FALSE;
823 if (msg->response == 183) {
824 // Session in progress
825 const gchar *rseq = sipmsg_find_header(msg, "RSeq");
826 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
827 gchar *rack = g_strdup_printf("RAck: %s %s\r\n", rseq, cseq);
829 if (!sipe_media_parse_sdp_attributes_and_candidates(call, msg->body)) {
830 // TODO: handle error
833 if (!sipe_media_parse_remote_codecs(call)) {
834 g_free(call);
835 return FALSE;
838 sipe_backend_media_add_remote_candidates(call->media, call->dialog->with, call->remote_candidates);
840 sipe_dialog_parse(call->dialog, msg, TRUE);
842 send_sip_request(SIP_TO_CORE_PRIVATE, "PRACK", call->dialog->with, call->dialog->with, rack, NULL, call->dialog, NULL);
843 g_free(rack);
844 } else {
845 //PurpleMedia* m = (PurpleMedia*) call->media;
846 //purple_media_stream_info(m, PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, FALSE);
847 sipe_media_send_ack(sip, msg, trans);
848 sipe_invite_call(sip, sipe_media_send_ack);
851 return TRUE;
855 Local Variables:
856 mode: c
857 c-file-style: "bsd"
858 indent-tabs-mode: t
859 tab-width: 8
860 End: