audio: moved media_call from sipe_account_data to sipe_core_private
[siplcs.git] / src / core / sipe-media.c
blob4aa8b05563e656e8c94cb11f77a5e54617a56bca
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 gint result = g_strcmp0(name1, name2);
127 g_free(name1);
128 g_free(name2);
130 return result;
133 static GList *
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;
154 static GList *
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",
161 SIPE_COMPONENT_RTP,
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",
168 SIPE_COMPONENT_RTCP,
169 SIPE_CANDIDATE_TYPE_HOST,
170 SIPE_NETWORK_PROTOCOL_UDP,
171 remote_ip, remote_port + 1);
172 candidates = g_list_append(candidates, candidate);
174 return candidates;
177 static GList *
178 sipe_media_parse_remote_candidates(GSList *sdp_attrs)
180 sipe_candidate *candidate;
181 GList *candidates = NULL;
182 const gchar *attr;
183 int i = 0;
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++))) {
189 gchar **tokens;
190 gchar *foundation;
191 SipeComponentType component;
192 SipeNetworkProtocol protocol;
193 guint32 priority;
194 gchar* ip;
195 guint16 port;
196 SipeCandidateType type;
198 tokens = g_strsplit_set(attr, " ", 0);
200 foundation = tokens[0];
202 switch (atoi(tokens[1])) {
203 case 1:
204 component = SIPE_COMPONENT_RTP;
205 break;
206 case 2:
207 component = SIPE_COMPONENT_RTCP;
208 break;
209 default:
210 component = SIPE_COMPONENT_NONE;
213 if (sipe_strequal(tokens[2], "UDP"))
214 protocol = SIPE_NETWORK_PROTOCOL_UDP;
215 else {
216 // Ignore TCP candidates, at least for now...
217 g_strfreev(tokens);
218 continue;
221 priority = atoi(tokens[3]);
222 ip = tokens[4];
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;
231 else {
232 g_strfreev(tokens);
233 continue;
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);
241 g_strfreev(tokens);
244 if (username) {
245 GList *it = candidates;
246 while (it) {
247 sipe_backend_candidate_set_username_and_pwd(it->data, username, password);
248 it = it->next;
252 return candidates;
255 static gchar *
256 sipe_media_sdp_codec_ids_format(GList *codecs)
258 GString *result = g_string_new(NULL);
260 while (codecs) {
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);
265 g_free(tmp);
267 codecs = codecs->next;
270 return g_string_free(result, FALSE);
273 static gchar *
274 sipe_media_sdp_codecs_format(GList *codecs)
276 GString *result = g_string_new(NULL);
278 while (codecs) {
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),
285 name,
286 sipe_backend_codec_get_clock_rate(c));
288 g_free(name);
289 g_string_append(result, tmp);
290 g_free(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);
295 g_free(tmp);
297 while (params) {
298 struct sipnameval* par = params->data;
299 tmp = g_strdup_printf(" %s=%s", par->name, par->value);
300 g_string_append(result, tmp);
301 g_free(tmp);
302 params = params->next;
304 g_string_append(result, "\r\n");
307 codecs = codecs->next;
310 return g_string_free(result, FALSE);
313 static gchar *
314 sipe_media_sdp_candidates_format(GList *candidates, sipe_media_call* call)
316 GString *result = g_string_new("");
317 gchar *tmp;
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);
327 g_free(tmp);
329 while (candidates) {
330 sipe_candidate *c = candidates->data;
332 guint16 port;
333 guint16 component;
334 gchar *protocol;
335 gchar *type;
337 port = sipe_backend_candidate_get_port(c);
339 switch (sipe_backend_candidate_get_component_type(c)) {
340 case SIPE_COMPONENT_RTP:
341 component = 1;
342 break;
343 case SIPE_COMPONENT_RTCP:
344 component = 2;
345 if (rtcp_port == 0)
346 rtcp_port = port;
347 break;
348 case SIPE_COMPONENT_NONE:
349 component = 0;
352 switch (sipe_backend_candidate_get_protocol(c)) {
353 case SIPE_NETWORK_PROTOCOL_TCP:
354 protocol = "TCP";
355 break;
356 case SIPE_NETWORK_PROTOCOL_UDP:
357 protocol = "UDP";
358 break;
361 switch (sipe_backend_candidate_get_type(c)) {
362 case SIPE_CANDIDATE_TYPE_HOST:
363 type = "host";
364 break;
365 case SIPE_CANDIDATE_TYPE_RELAY:
366 type = "relay";
367 break;
368 case SIPE_CANDIDATE_TYPE_SRFLX:
369 type = "srflx";
370 break;
371 default:
372 // TODO: error unknown/unsupported type
373 break;
376 tmp = g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
377 sipe_backend_candidate_get_foundation(c),
378 component,
379 protocol,
380 sipe_backend_candidate_get_priority(c),
381 sipe_backend_candidate_get_ip(c),
382 port,
383 type);
385 g_string_append(result, tmp);
386 g_free(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);
400 g_free(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);
407 g_free(tmp);
410 return g_string_free(result, FALSE);
413 static gchar*
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(
428 "v=0\r\n"
429 "o=- 0 0 IN IP4 %s\r\n"
430 "s=session\r\n"
431 "c=IN IP4 %s\r\n"
432 "b=CT:99980\r\n"
433 "t=0 0\r\n"
434 "m=audio %d RTP/AVP%s\r\n"
435 "%s"
436 "%s"
437 "%s"
438 "a=encryption:rejected\r\n"
439 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
441 g_free(sdp_codecs);
442 g_free(sdp_codec_ids);
443 g_free(sdp_candidates);
444 sipe_media_codec_list_free(usable_codecs);
446 return body;
449 static void
450 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
452 gchar *hdr;
453 gchar *contact;
454 gchar *body;
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"
466 "Contact: %s%s\r\n"
467 "Content-Type: application/sdp\r\n",
468 contact,
469 (call->local_on_hold || call->remote_on_hold) ? ";+sip.rendering=\"no\"" : "");
470 g_free(contact);
472 body = sipe_media_create_sdp(call);
474 send_sip_request(SIP_TO_CORE_PRIVATE, "INVITE", dialog->with, dialog->with, hdr, body,
475 dialog, tc);
477 g_free(body);
478 g_free(hdr);
481 static gboolean
482 sipe_media_parse_remote_codecs(sipe_media_call *call);
484 static gboolean
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;
491 gchar **ptr;
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);
497 if(!parts[0]) {
498 g_strfreev(parts);
499 sipe_utils_nameval_free(sdp_attrs);
500 sdp_attrs = NULL;
501 no_error = FALSE;
502 break;
504 sdp_attrs = sipe_utils_nameval_add(sdp_attrs, parts[0], parts[1]);
505 g_strfreev(parts);
507 } else if (g_str_has_prefix(*ptr, "o=")) {
508 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
509 remote_ip = g_strdup(parts[5]);
510 g_strfreev(parts);
511 } else if (g_str_has_prefix(*ptr, "m=")) {
512 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
513 remote_port = atoi(parts[1]);
514 g_strfreev(parts);
518 g_strfreev(lines);
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;
528 if (no_error) {
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;
536 } else {
537 sipe_utils_nameval_free(sdp_attrs);
538 sipe_media_candidate_list_free(remote_candidates);
541 return no_error;
544 static gboolean
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);
555 if (remote_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
562 return FALSE;
565 return TRUE;
566 } else {
567 sipe_media_codec_list_free(remote_codecs);
568 printf("ERROR NO CANDIDATES OR CODECS");
570 return FALSE;
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;
579 gchar *newHeader;
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);
586 g_free(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);
593 return dialog;
596 static void
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);
602 g_free(body);
605 static gboolean
606 sipe_media_process_invite_response(struct sipe_core_private *sipe_private,
607 struct sipmsg *msg,
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)) {
616 g_free(call);
617 return;
620 send_response_with_session_description(call, 183, "Session Progress");
624 static void media_connected_cb(sipe_media_call *call)
626 call = call;
629 static void call_accept_cb(sipe_media_call *call, gboolean local)
631 if (local) {
632 send_response_with_session_description(call, 200, "OK");
636 static void call_reject_cb(sipe_media_call *call, gboolean local)
638 if (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);
645 static gboolean
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)
662 if (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;
691 return call;
694 void sipe_media_hangup(struct sipe_core_private *sipe_private)
696 sipe_media_call *call = sipe_private->media_call;
697 if (call)
698 sipe_backend_media_hangup(call->media, FALSE);
701 void
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)
708 return;
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);
725 void
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;
735 if (call) {
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)) {
749 g_free(call);
750 return;
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);
759 } else {
760 send_response_with_session_description(call, 200, "OK");
762 } else {
763 send_sip_response(sipe_private, msg, 486, "Busy Here", NULL);
765 return;
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)) {
780 // TODO error
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
791 static gboolean
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;
798 int trans_cseq;
799 int tmp_cseq;
801 if (!call || !call->dialog)
802 return FALSE;
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;
812 return TRUE;
815 static gboolean
816 sipe_media_process_invite_response(struct sipe_core_private *sipe_private,
817 struct sipmsg *msg,
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))
824 return FALSE;
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)) {
837 g_free(call);
838 return FALSE;
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);
846 g_free(rack);
847 } else if (msg->response == 603) {
848 sipe_backend_media_reject(call->media, FALSE);
849 sipe_media_send_ack(sipe_private, msg, trans);
850 } else {
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);
857 return TRUE;
861 Local Variables:
862 mode: c
863 c-file-style: "bsd"
864 indent-tabs-mode: t
865 tab-width: 8
866 End: