audio: cleanup resources on end of call
[siplcs.git] / src / core / sipe-media.c
blobf843c511b1799a0cd95b537daa7001742fce3462
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 <stdio.h>
28 #include <stdlib.h>
30 #include <glib.h>
32 #include "sipe-common.h"
33 #include "sipmsg.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"
42 #include "sipe-nls.h"
43 #include "sipe.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;
55 guint16 remote_port;
57 GSList *sdp_attrs;
58 struct sipmsg *invitation;
59 GList *remote_candidates;
60 gboolean legacy_mode;
61 gboolean using_nice;
63 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
64 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
66 gchar *
67 sipe_media_get_callid(struct sipe_media_call_private *call)
69 return call->dialog->callid;
72 static void sipe_media_codec_list_free(GList *codecs)
74 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
75 sipe_backend_codec_free(codecs->data);
78 static void sipe_media_candidate_list_free(GList *candidates)
80 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
81 sipe_backend_candidate_free(candidates->data);
84 static void
85 sipe_media_call_free(struct sipe_media_call_private *call_private)
87 if (call_private) {
88 sipe_backend_media_free(call_private->public.backend_private);
89 sipe_media_codec_list_free(call_private->public.remote_codecs);
91 if (call_private->session)
92 sipe_session_remove(call_private->sipe_private,
93 call_private->session);
95 sipe_utils_nameval_free(call_private->sdp_attrs);
97 if (call_private->invitation)
98 sipmsg_free(call_private->invitation);
100 sipe_media_candidate_list_free(call_private->remote_candidates);
101 g_free(call_private);
105 static GList *
106 sipe_media_parse_codecs(GSList *sdp_attrs)
108 int i = 0;
109 const gchar *attr;
110 GList *codecs = NULL;
112 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "rtpmap", i++))) {
113 gchar **tokens = g_strsplit_set(attr, " /", 3);
115 int id = atoi(tokens[0]);
116 gchar *name = tokens[1];
117 int clock_rate = atoi(tokens[2]);
118 SipeMediaType type = SIPE_MEDIA_AUDIO;
120 struct sipe_backend_codec *codec = sipe_backend_codec_new(id, name, clock_rate, type);
122 // TODO: more secure and effective implementation
123 int j = 0;
124 const gchar* params;
125 while((params = sipe_utils_nameval_find_instance(sdp_attrs, "fmtp", j++))) {
126 gchar **tokens = g_strsplit_set(params, " ", 0);
127 gchar **next = tokens + 1;
129 if (atoi(tokens[0]) == id) {
130 while (*next) {
131 gchar name[50];
132 gchar value[50];
134 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
135 sipe_backend_codec_add_optional_parameter(codec, name, value);
137 ++next;
141 g_strfreev(tokens);
144 codecs = g_list_append(codecs, codec);
145 g_strfreev(tokens);
148 return codecs;
151 static gint
152 codec_name_compare(struct sipe_backend_codec *codec1, struct sipe_backend_codec *codec2)
154 gchar *name1 = sipe_backend_codec_get_name(codec1);
155 gchar *name2 = sipe_backend_codec_get_name(codec2);
157 gint result = g_strcmp0(name1, name2);
159 g_free(name1);
160 g_free(name2);
162 return result;
165 static GList *
166 sipe_media_prune_remote_codecs(GList *local_codecs, GList *remote_codecs)
168 GList *remote_codecs_head = remote_codecs;
169 GList *pruned_codecs = NULL;
171 while (remote_codecs) {
172 struct sipe_backend_codec *c = remote_codecs->data;
174 if (g_list_find_custom(local_codecs, c, (GCompareFunc)codec_name_compare)) {
175 pruned_codecs = g_list_append(pruned_codecs, c);
176 remote_codecs->data = NULL;
178 remote_codecs = remote_codecs->next;
181 sipe_media_codec_list_free(remote_codecs_head);
183 return pruned_codecs;
186 static GList *
187 sipe_media_parse_remote_candidates_legacy(gchar *remote_ip, guint16 remote_port)
189 struct sipe_backend_candidate *candidate;
190 GList *candidates = NULL;
192 candidate = sipe_backend_candidate_new("foundation",
193 SIPE_COMPONENT_RTP,
194 SIPE_CANDIDATE_TYPE_HOST,
195 SIPE_NETWORK_PROTOCOL_UDP,
196 remote_ip, remote_port);
197 candidates = g_list_append(candidates, candidate);
199 candidate = sipe_backend_candidate_new("foundation",
200 SIPE_COMPONENT_RTCP,
201 SIPE_CANDIDATE_TYPE_HOST,
202 SIPE_NETWORK_PROTOCOL_UDP,
203 remote_ip, remote_port + 1);
204 candidates = g_list_append(candidates, candidate);
206 return candidates;
209 static GList *
210 sipe_media_parse_remote_candidates(GSList *sdp_attrs)
212 struct sipe_backend_candidate *candidate;
213 GList *candidates = NULL;
214 const gchar *attr;
215 int i = 0;
217 const gchar* username = sipe_utils_nameval_find(sdp_attrs, "ice-ufrag");
218 const gchar* password = sipe_utils_nameval_find(sdp_attrs, "ice-pwd");
220 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "candidate", i++))) {
221 gchar **tokens;
222 gchar *foundation;
223 SipeComponentType component;
224 SipeNetworkProtocol protocol;
225 guint32 priority;
226 gchar* ip;
227 guint16 port;
228 SipeCandidateType type;
230 tokens = g_strsplit_set(attr, " ", 0);
232 foundation = tokens[0];
234 switch (atoi(tokens[1])) {
235 case 1:
236 component = SIPE_COMPONENT_RTP;
237 break;
238 case 2:
239 component = SIPE_COMPONENT_RTCP;
240 break;
241 default:
242 component = SIPE_COMPONENT_NONE;
245 if (sipe_strequal(tokens[2], "UDP"))
246 protocol = SIPE_NETWORK_PROTOCOL_UDP;
247 else {
248 // Ignore TCP candidates, at least for now...
249 // Also, if this is ICEv6 candidate list, candidates are dropped here
250 g_strfreev(tokens);
251 continue;
254 priority = atoi(tokens[3]);
255 ip = tokens[4];
256 port = atoi(tokens[5]);
258 if (sipe_strequal(tokens[7], "host"))
259 type = SIPE_CANDIDATE_TYPE_HOST;
260 else if (sipe_strequal(tokens[7], "relay"))
261 type = SIPE_CANDIDATE_TYPE_RELAY;
262 else if (sipe_strequal(tokens[7], "srflx"))
263 type = SIPE_CANDIDATE_TYPE_SRFLX;
264 else if (sipe_strequal(tokens[7], "prflx"))
265 type = SIPE_CANDIDATE_TYPE_PRFLX;
266 else {
267 g_strfreev(tokens);
268 continue;
271 candidate = sipe_backend_candidate_new(foundation, component,
272 type, protocol, ip, port);
273 sipe_backend_candidate_set_priority(candidate, priority);
274 candidates = g_list_append(candidates, candidate);
276 g_strfreev(tokens);
279 if (username) {
280 GList *it = candidates;
281 while (it) {
282 sipe_backend_candidate_set_username_and_pwd(it->data, username, password);
283 it = it->next;
287 return candidates;
290 static gchar *
291 sipe_media_sdp_codec_ids_format(GList *codecs)
293 GString *result = g_string_new(NULL);
295 while (codecs) {
296 struct sipe_backend_codec *c = codecs->data;
298 g_string_append_printf(result,
299 " %d",
300 sipe_backend_codec_get_id(c));
302 codecs = codecs->next;
305 return g_string_free(result, FALSE);
308 static gchar *
309 sipe_media_sdp_codecs_format(GList *codecs)
311 GString *result = g_string_new(NULL);
313 while (codecs) {
314 struct sipe_backend_codec *c = codecs->data;
315 GList *params = NULL;
316 gchar *name = sipe_backend_codec_get_name(c);
318 g_string_append_printf(result,
319 "a=rtpmap:%d %s/%d\r\n",
320 sipe_backend_codec_get_id(c),
321 name,
322 sipe_backend_codec_get_clock_rate(c));
323 g_free(name);
325 if ((params = sipe_backend_codec_get_optional_parameters(c))) {
326 g_string_append_printf(result,
327 "a=fmtp:%d",
328 sipe_backend_codec_get_id(c));
330 while (params) {
331 struct sipnameval* par = params->data;
332 g_string_append_printf(result,
333 " %s=%s",
334 par->name, par->value);
335 params = params->next;
337 g_string_append(result, "\r\n");
340 codecs = codecs->next;
343 return g_string_free(result, FALSE);
346 static gint
347 candidate_compare_by_component_id(struct sipe_backend_candidate *c1, struct sipe_backend_candidate *c2)
349 return sipe_backend_candidate_get_component_type(c1)
350 - sipe_backend_candidate_get_component_type(c2);
353 static gchar *
354 sipe_media_sdp_candidates_format(struct sipe_media_call_private *call_private, guint16 *local_port)
356 struct sipe_backend_media *backend_media = call_private->public.backend_private;
357 struct sipe_backend_stream *voice_stream = call_private->voice_stream;
358 GList *l_candidates;
359 GList *r_candidates;
360 GList *cand;
361 gchar *username;
362 gchar *password;
363 GString *result = g_string_new("");
364 guint16 rtcp_port = 0;
366 // If we have established candidate pairs, send them in SDP response.
367 // Otherwise send all available local candidates.
368 l_candidates = sipe_backend_media_get_active_local_candidates(backend_media, voice_stream);
369 if (!l_candidates)
370 l_candidates = sipe_backend_get_local_candidates(backend_media, voice_stream);
372 // If in legacy mode, just fill local_port variable with local host's RTP
373 // component port and return empty string.
374 if (call_private->legacy_mode) {
375 for (cand = l_candidates; cand; cand = cand->next) {
376 struct sipe_backend_candidate *c = cand->data;
377 SipeCandidateType type = sipe_backend_candidate_get_type(c);
378 SipeComponentType component = sipe_backend_candidate_get_component_type(c);
380 if (type == SIPE_CANDIDATE_TYPE_HOST && component == SIPE_COMPONENT_RTP) {
381 *local_port = sipe_backend_candidate_get_port(c);
382 break;
386 sipe_media_candidate_list_free(l_candidates);
388 return g_string_free(result, FALSE);
391 username = sipe_backend_candidate_get_username(l_candidates->data);
392 password = sipe_backend_candidate_get_password(l_candidates->data);
394 g_string_append_printf(result,
395 "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
396 username, password);
398 g_free(username);
399 g_free(password);
401 for (cand = l_candidates; cand; cand = cand->next) {
402 struct sipe_backend_candidate *c = cand->data;
404 guint16 port;
405 SipeComponentType component;
406 const gchar *protocol;
407 const gchar *type;
408 gchar *related = NULL;
409 gchar *tmp_foundation;
410 gchar *tmp_ip;
412 component = sipe_backend_candidate_get_component_type(c);
413 port = sipe_backend_candidate_get_port(c);
415 switch (sipe_backend_candidate_get_protocol(c)) {
416 case SIPE_NETWORK_PROTOCOL_TCP:
417 protocol = "TCP";
418 break;
419 case SIPE_NETWORK_PROTOCOL_UDP:
420 protocol = "UDP";
421 break;
424 switch (sipe_backend_candidate_get_type(c)) {
425 case SIPE_CANDIDATE_TYPE_HOST:
426 type = "host";
427 if (component == SIPE_COMPONENT_RTP)
428 *local_port = port;
429 else if (component == SIPE_COMPONENT_RTCP)
430 rtcp_port = port;
431 break;
432 case SIPE_CANDIDATE_TYPE_RELAY:
433 type = "relay";
434 break;
435 case SIPE_CANDIDATE_TYPE_SRFLX:
437 gchar *tmp;
439 type = "srflx";
440 related = g_strdup_printf("raddr %s rport %d",
441 tmp = sipe_backend_candidate_get_base_ip(c),
442 sipe_backend_candidate_get_base_port(c));
443 g_free(tmp);
445 break;
446 case SIPE_CANDIDATE_TYPE_PRFLX:
447 type = "prflx";
448 break;
449 default:
450 // TODO: error unknown/unsupported type
451 break;
454 g_string_append_printf(result,
455 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
456 tmp_foundation = sipe_backend_candidate_get_foundation(c),
457 component,
458 protocol,
459 sipe_backend_candidate_get_priority(c),
460 tmp_ip = sipe_backend_candidate_get_ip(c),
461 port,
462 type,
463 related ? related : "");
464 g_free(tmp_ip);
465 g_free(tmp_foundation);
466 g_free(related);
469 r_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, voice_stream);
470 r_candidates = g_list_sort(r_candidates, (GCompareFunc)candidate_compare_by_component_id);
472 if (r_candidates) {
473 g_string_append(result, "a=remote-candidates:");
474 for (cand = r_candidates; cand; cand = cand->next) {
475 struct sipe_backend_candidate *candidate = cand->data;
476 gchar *tmp;
477 g_string_append_printf(result,
478 "%u %s %u ",
479 sipe_backend_candidate_get_component_type(candidate),
480 tmp = sipe_backend_candidate_get_ip(candidate),
481 sipe_backend_candidate_get_port(candidate));
482 g_free(tmp);
484 g_string_append(result, "\r\n");
487 sipe_media_candidate_list_free(l_candidates);
488 sipe_media_candidate_list_free(r_candidates);
490 if (rtcp_port != 0) {
491 g_string_append_printf(result,
492 "a=maxptime:200\r\na=rtcp:%u\r\n",
493 rtcp_port);
496 return g_string_free(result, FALSE);
499 static gchar*
500 sipe_media_create_sdp(struct sipe_media_call_private *call_private) {
501 GList *usable_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
502 call_private->voice_stream);
503 gchar *body = NULL;
505 const char *ip = sipe_utils_get_suitable_local_ip(-1);
506 guint16 local_port = 0;
508 gchar *sdp_codecs = sipe_media_sdp_codecs_format(usable_codecs);
509 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(usable_codecs);
510 gchar *sdp_candidates = sipe_media_sdp_candidates_format(call_private, &local_port);
511 gchar *inactive = (call_private->public.local_on_hold ||
512 call_private->public.remote_on_hold) ? "a=inactive\r\n" : "";
514 body = g_strdup_printf(
515 "v=0\r\n"
516 "o=- 0 0 IN IP4 %s\r\n"
517 "s=session\r\n"
518 "c=IN IP4 %s\r\n"
519 "b=CT:99980\r\n"
520 "t=0 0\r\n"
521 "m=audio %d RTP/AVP%s\r\n"
522 "%s"
523 "%s"
524 "%s"
525 "a=encryption:rejected\r\n"
526 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
528 g_free(sdp_codecs);
529 g_free(sdp_codec_ids);
530 g_free(sdp_candidates);
532 sipe_media_codec_list_free(usable_codecs);
534 return body;
537 static void
538 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
540 gchar *hdr;
541 gchar *contact;
542 gchar *body;
543 struct sipe_media_call_private *call_private = sipe_private->media_call;
544 struct sip_dialog *dialog = call_private->dialog;
546 contact = get_contact(sipe_private);
547 hdr = g_strdup_printf(
548 "Supported: ms-early-media\r\n"
549 "Supported: 100rel\r\n"
550 "ms-keep-alive: UAC;hop-hop=yes\r\n"
551 "Contact: %s%s\r\n"
552 "Content-Type: application/sdp\r\n",
553 contact,
554 (call_private->public.local_on_hold || call_private->public.remote_on_hold) ? ";+sip.rendering=\"no\"" : "");
555 g_free(contact);
557 body = sipe_media_create_sdp(call_private);
559 dialog->outgoing_invite = sip_transport_invite(sipe_private,
560 hdr,
561 body,
562 dialog,
563 tc);
565 g_free(body);
566 g_free(hdr);
569 static gboolean
570 sipe_media_parse_remote_codecs(struct sipe_media_call_private *call_private);
572 static gboolean
573 sipe_media_parse_sdp_attributes_and_candidates(struct sipe_media_call_private *call_private, gchar *frame) {
574 gchar **lines = g_strsplit(frame, "\r\n", 0);
575 GSList *sdp_attrs = NULL;
576 gchar *remote_ip = NULL;
577 guint16 remote_port = 0;
578 GList *remote_candidates;
579 gchar **ptr;
580 gboolean no_error = TRUE;
582 for (ptr = lines; *ptr != NULL; ++ptr) {
583 if (g_str_has_prefix(*ptr, "a=")) {
584 gchar **parts = g_strsplit(*ptr + 2, ":", 2);
585 if(!parts[0]) {
586 g_strfreev(parts);
587 sipe_utils_nameval_free(sdp_attrs);
588 sdp_attrs = NULL;
589 no_error = FALSE;
590 break;
592 sdp_attrs = sipe_utils_nameval_add(sdp_attrs, parts[0], parts[1]);
593 g_strfreev(parts);
595 } else if (g_str_has_prefix(*ptr, "o=")) {
596 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
597 remote_ip = g_strdup(parts[5]);
598 g_strfreev(parts);
599 } else if (g_str_has_prefix(*ptr, "m=")) {
600 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
601 remote_port = atoi(parts[1]);
602 g_strfreev(parts);
606 g_strfreev(lines);
608 remote_candidates = sipe_media_parse_remote_candidates(sdp_attrs);
609 if (!remote_candidates) {
610 // No a=candidate in SDP message, revert to OC2005 behaviour
611 remote_candidates = sipe_media_parse_remote_candidates_legacy(remote_ip, remote_port);
612 // This seems to be pre-OC2007 R2 UAC
613 call_private->legacy_mode = TRUE;
616 if (no_error) {
617 sipe_utils_nameval_free(call_private->sdp_attrs);
618 sipe_media_candidate_list_free(call_private->remote_candidates);
620 call_private->sdp_attrs = sdp_attrs;
621 call_private->remote_port = remote_port;
622 call_private->remote_candidates = remote_candidates;
623 } else {
624 sipe_utils_nameval_free(sdp_attrs);
625 sipe_media_candidate_list_free(remote_candidates);
628 return no_error;
631 static gboolean
632 sipe_media_parse_remote_codecs(struct sipe_media_call_private *call_private)
634 GList *local_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
635 call_private->voice_stream);
636 GList *remote_codecs;
638 remote_codecs = sipe_media_parse_codecs(call_private->sdp_attrs);
639 remote_codecs = sipe_media_prune_remote_codecs(local_codecs, remote_codecs);
641 sipe_media_codec_list_free(local_codecs);
643 if (remote_codecs) {
644 sipe_media_codec_list_free(call_private->public.remote_codecs);
646 call_private->public.remote_codecs = remote_codecs;
648 if (!sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL,
649 call_private->voice_stream)) {
650 SIPE_DEBUG_ERROR_NOFORMAT("ERROR SET REMOTE CODECS"); // TODO
651 return FALSE;
654 return TRUE;
655 } else {
656 sipe_media_codec_list_free(remote_codecs);
657 SIPE_DEBUG_ERROR_NOFORMAT("ERROR NO CANDIDATES OR CODECS");
659 return FALSE;
663 static struct sip_dialog *
664 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
666 gchar *newTag = gentag();
667 const gchar *oldHeader;
668 gchar *newHeader;
669 struct sip_dialog *dialog;
671 oldHeader = sipmsg_find_header(msg, "To");
672 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
673 sipmsg_remove_header_now(msg, "To");
674 sipmsg_add_header_now(msg, "To", newHeader);
675 g_free(newHeader);
677 dialog = sipe_dialog_add(session);
678 dialog->callid = g_strdup(session->callid);
679 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
680 sipe_dialog_parse(dialog, msg, FALSE);
682 return dialog;
685 static void
686 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
688 gchar *body = sipe_media_create_sdp(call_private);
689 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
690 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
691 g_free(body);
694 static gboolean
695 encryption_levels_compatible(struct sipe_media_call_private *call_private)
697 const gchar *encrypion_level;
699 encrypion_level = sipe_utils_nameval_find(call_private->sdp_attrs,
700 "encryption");
702 // Decline call if peer requires encryption as we don't support it yet.
703 return !sipe_strequal(encrypion_level, "required");
706 static void
707 handle_incompatible_encryption_level(struct sipe_media_call_private *call_private)
709 sipmsg_add_header(call_private->invitation, "Warning",
710 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
711 sip_transport_response(call_private->sipe_private,
712 call_private->invitation,
713 488, "Encryption Levels not compatible",
714 NULL);
715 sipe_backend_media_reject(call_private->public.backend_private, FALSE);
716 sipe_backend_notify_error(_("Unable to establish a call"),
717 _("Encryption settings of peer are incompatible with ours."));
720 static gboolean
721 process_invite_call_response(struct sipe_core_private *sipe_private,
722 struct sipmsg *msg,
723 struct transaction *trans);
725 static void candidates_prepared_cb(struct sipe_media_call *call)
727 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
729 if (sipe_backend_media_is_initiator(call_private->public.backend_private,
730 call_private->voice_stream)) {
731 sipe_invite_call(call_private->sipe_private,
732 process_invite_call_response);
733 return;
736 if (!sipe_media_parse_remote_codecs(call_private)) {
737 g_free(call_private);
738 return;
741 if ( !call_private->legacy_mode
742 && encryption_levels_compatible(SIPE_MEDIA_CALL_PRIVATE))
743 send_response_with_session_description(call_private,
744 183, "Session Progress");
747 static void media_connected_cb(SIPE_UNUSED_PARAMETER struct sipe_media_call_private *call_private)
751 static void call_accept_cb(struct sipe_media_call *call, gboolean local)
753 if (local) {
754 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
756 if (!encryption_levels_compatible(call_private)) {
757 handle_incompatible_encryption_level(call_private);
758 return;
761 send_response_with_session_description(call_private, 200, "OK");
765 static void call_reject_cb(struct sipe_media_call *call, gboolean local)
767 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
769 if (local) {
770 sip_transport_response(call_private->sipe_private, call_private->invitation, 603, "Decline", NULL);
772 call_private->sipe_private->media_call = NULL;
773 sipe_media_call_free(call_private);
776 static gboolean
777 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
778 struct transaction *trans);
780 static void call_hold_cb(struct sipe_media_call *call, gboolean local, gboolean state)
782 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
784 if (local && (call_private->public.local_on_hold != state)) {
785 call_private->public.local_on_hold = state;
786 sipe_invite_call(call_private->sipe_private, sipe_media_send_ack);
787 } else if (call_private->public.remote_on_hold != state) {
788 call_private->public.remote_on_hold = state;
789 send_response_with_session_description(call_private, 200, "OK");
793 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
795 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
797 if (local) {
798 sip_transport_bye(call_private->sipe_private, call_private->dialog);
800 call_private->sipe_private->media_call = NULL;
801 sipe_media_call_free(call_private);
804 static struct sipe_media_call_private *
805 sipe_media_call_new(struct sipe_core_private *sipe_private,
806 const gchar* with, gboolean initiator)
808 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
810 call_private->sipe_private = sipe_private;
811 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
812 SIPE_MEDIA_CALL,
813 with,
814 initiator);
816 call_private->legacy_mode = FALSE;
817 call_private->using_nice = TRUE;
819 call_private->public.candidates_prepared_cb = candidates_prepared_cb;
820 call_private->public.media_connected_cb = media_connected_cb;
821 call_private->public.call_accept_cb = call_accept_cb;
822 call_private->public.call_reject_cb = call_reject_cb;
823 call_private->public.call_hold_cb = call_hold_cb;
824 call_private->public.call_hangup_cb = call_hangup_cb;
826 call_private->public.local_on_hold = FALSE;
827 call_private->public.remote_on_hold = FALSE;
829 return call_private;
832 void sipe_media_hangup(struct sipe_core_private *sipe_private)
834 struct sipe_media_call_private *call_private = sipe_private->media_call;
835 if (call_private)
836 sipe_backend_media_hangup(call_private->public.backend_private,
837 FALSE);
840 void
841 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
842 const char *with)
844 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
845 struct sipe_media_call_private *call_private;
847 if (sipe_private->media_call)
848 return;
850 call_private = sipe_media_call_new(sipe_private, with, TRUE);
852 call_private->session = sipe_session_add_call(sipe_private, with);
853 call_private->dialog = sipe_dialog_add(call_private->session);
854 call_private->dialog->callid = gencallid();
855 call_private->dialog->with = g_strdup(call_private->session->with);
856 call_private->dialog->ourtag = gentag();
858 call_private->voice_stream = sipe_backend_media_add_stream(
859 call_private->public.backend_private,
860 with,
861 SIPE_MEDIA_AUDIO,
862 call_private->using_nice,
863 TRUE);
865 if (!call_private->voice_stream) {
866 sipe_backend_notify_error("Error occured",
867 "Error creating media stream");
868 sipe_media_call_free(call_private);
869 return;
872 sipe_private->media_call = call_private;
874 // Processing continues in candidates_prepared_cb
878 void
879 sipe_media_incoming_invite(struct sipe_core_private *sipe_private,
880 struct sipmsg *msg)
882 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
884 struct sipe_media_call_private *call_private = sipe_private->media_call;
885 struct sip_session *session;
886 struct sip_dialog *dialog;
888 if (call_private) {
889 if (sipe_strequal(call_private->dialog->callid, callid)) {
890 if (call_private->invitation)
891 sipmsg_free(call_private->invitation);
892 call_private->invitation = sipmsg_copy(msg);
894 sipe_utils_nameval_free(call_private->sdp_attrs);
895 call_private->sdp_attrs = NULL;
896 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private,
897 call_private->invitation->body)) {
898 // TODO: handle error
901 if (!encryption_levels_compatible(call_private)) {
902 handle_incompatible_encryption_level(call_private);
903 return;
906 if (!sipe_media_parse_remote_codecs(call_private)) {
907 g_free(call_private);
908 return;
911 if (call_private->legacy_mode && !call_private->public.remote_on_hold) {
912 sipe_backend_media_hold(call_private->public.backend_private,
913 FALSE);
914 } else if (sipe_utils_nameval_find(call_private->sdp_attrs, "inactive")) {
915 sipe_backend_media_hold(call_private->public.backend_private, FALSE);
916 } else if (call_private->public.remote_on_hold) {
917 sipe_backend_media_unhold(call_private->public.backend_private, FALSE);
918 } else {
919 send_response_with_session_description(call_private,
920 200, "OK");
922 } else {
923 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
925 return;
928 session = sipe_session_find_or_add_chat_by_callid(sipe_private, callid);
929 dialog = sipe_media_dialog_init(session, msg);
931 call_private = sipe_media_call_new(sipe_private, dialog->with, FALSE);
932 call_private->invitation = sipmsg_copy(msg);
933 call_private->session = session;
934 call_private->dialog = dialog;
936 sipe_private->media_call = call_private;
938 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
939 // TODO error
942 call_private->voice_stream =
943 sipe_backend_media_add_stream(call_private->public.backend_private,
944 dialog->with,
945 SIPE_MEDIA_AUDIO,
946 !call_private->legacy_mode, FALSE);
947 sipe_backend_media_add_remote_candidates(call_private->public.backend_private,
948 call_private->voice_stream,
949 call_private->remote_candidates);
951 sip_transport_response(sipe_private, call_private->invitation, 180, "Ringing", NULL);
953 // Processing continues in candidates_prepared_cb
956 static gboolean
957 sipe_media_send_ack(struct sipe_core_private *sipe_private,
958 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
959 struct transaction *trans)
961 struct sipe_media_call_private *call_private = sipe_private->media_call;
962 struct sip_dialog *dialog;
963 int trans_cseq;
964 int tmp_cseq;
966 if (!call_private || !call_private->dialog)
967 return FALSE;
969 dialog = call_private->dialog;
970 tmp_cseq = dialog->cseq;
972 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq);
973 dialog->cseq = trans_cseq - 1;
974 sip_transport_ack(sipe_private, dialog);
975 dialog->cseq = tmp_cseq;
977 dialog->outgoing_invite = NULL;
979 return TRUE;
982 static gboolean
983 process_invite_call_response(struct sipe_core_private *sipe_private,
984 struct sipmsg *msg,
985 struct transaction *trans)
987 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
988 const gchar *with;
989 struct sipe_media_call_private *call_private = sipe_private->media_call;
990 struct sipe_backend_media *backend_private;
992 if (!call_private ||
993 !sipe_strequal(sipe_media_get_callid(call_private), callid))
994 return FALSE;
996 backend_private = call_private->public.backend_private;
997 with = call_private->dialog->with;
999 call_private->dialog->outgoing_invite = NULL;
1001 if (msg->response >= 400) {
1002 // Call rejected by remote peer or an error occurred
1003 gchar *title;
1004 GString *desc = g_string_new("");
1006 sipe_backend_media_reject(backend_private, FALSE);
1007 sipe_media_send_ack(sipe_private, msg, trans);
1009 switch (msg->response) {
1010 case 480:
1011 title = _("User unavailable");
1012 g_string_append_printf(desc, _("User %s is not available"), with);
1013 case 603:
1014 case 605:
1015 title = _("Call rejected");
1016 g_string_append_printf(desc, _("User %s rejected call"), with);
1017 break;
1018 default:
1019 title = _("Error occured");
1020 g_string_append(desc, _("Unable to establish a call"));
1021 break;
1024 g_string_append_printf(desc, "\n%d %s", msg->response, msg->responsestr);
1026 sipe_backend_notify_error(title, desc->str);
1027 g_string_free(desc, TRUE);
1029 return TRUE;
1032 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
1033 return FALSE;
1036 if (!sipe_media_parse_remote_codecs(call_private)) {
1037 g_free(call_private);
1038 return FALSE;
1041 sipe_backend_media_add_remote_candidates(backend_private,
1042 call_private->voice_stream,
1043 call_private->remote_candidates);
1045 sipe_dialog_parse(call_private->dialog, msg, TRUE);
1047 if (msg->response == 183) {
1048 // Session in progress
1049 const gchar *rseq = sipmsg_find_header(msg, "RSeq");
1050 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
1051 gchar *rack = g_strdup_printf("RAck: %s %s\r\n", rseq, cseq);
1052 sip_transport_request(sipe_private,
1053 "PRACK",
1054 with,
1055 with,
1056 rack,
1057 NULL,
1058 call_private->dialog,
1059 NULL);
1060 g_free(rack);
1061 } else {
1062 sipe_media_send_ack(sipe_private, msg, trans);
1064 if (call_private->legacy_mode && call_private->using_nice) {
1065 // We created non-legacy stream as we don't know which version of
1066 // client is on the other side until first SDP response is received.
1067 // This client requires legacy mode, so we must remove current session
1068 // (using ICE) and create new using raw UDP transport.
1069 struct sipe_backend_stream *new_stream;
1071 call_private->using_nice = FALSE;
1073 new_stream = sipe_backend_media_add_stream(backend_private,
1074 with,
1075 SIPE_MEDIA_AUDIO,
1076 FALSE,
1077 TRUE);
1079 sipe_backend_media_remove_stream(backend_private,
1080 call_private->voice_stream);
1081 call_private->voice_stream = new_stream;
1083 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
1084 return FALSE;
1087 if (!sipe_media_parse_remote_codecs(call_private)) {
1088 g_free(call_private);
1089 return FALSE;
1092 sipe_backend_media_add_remote_candidates(backend_private,
1093 call_private->voice_stream,
1094 call_private->remote_candidates);
1096 // New INVITE will be sent in candidates_prepared_cb
1097 } else {
1098 sipe_invite_call(sipe_private, sipe_media_send_ack);
1102 return TRUE;
1106 Local Variables:
1107 mode: c
1108 c-file-style: "bsd"
1109 indent-tabs-mode: t
1110 tab-width: 8
1111 End: