audio: set dialog->outgoing_invite when sending media invite messages
[siplcs.git] / src / core / sipe-media.c
bloba27f2db62fffb41352d78e34f293f66440505a3e
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 gchar *remote_ip;
56 guint16 remote_port;
58 GSList *sdp_attrs;
59 struct sipmsg *invitation;
60 GList *remote_candidates;
61 gboolean legacy_mode;
62 gboolean using_nice;
64 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
65 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
67 gchar *
68 sipe_media_get_callid(struct sipe_media_call_private *call)
70 return call->dialog->callid;
73 static void sipe_media_codec_list_free(GList *codecs)
75 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
76 sipe_backend_codec_free(codecs->data);
79 static void sipe_media_candidate_list_free(GList *candidates)
81 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
82 sipe_backend_candidate_free(candidates->data);
85 static void
86 sipe_media_call_free(struct sipe_media_call_private *call_private)
88 if (call_private) {
89 sipe_utils_nameval_free(call_private->sdp_attrs);
90 if (call_private->invitation)
91 sipmsg_free(call_private->invitation);
92 sipe_media_codec_list_free(call_private->public.remote_codecs);
93 sipe_media_candidate_list_free(call_private->remote_candidates);
94 g_free(call_private);
98 static GList *
99 sipe_media_parse_codecs(GSList *sdp_attrs)
101 int i = 0;
102 const gchar *attr;
103 GList *codecs = NULL;
105 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "rtpmap", i++))) {
106 gchar **tokens = g_strsplit_set(attr, " /", 3);
108 int id = atoi(tokens[0]);
109 gchar *name = tokens[1];
110 int clock_rate = atoi(tokens[2]);
111 SipeMediaType type = SIPE_MEDIA_AUDIO;
113 struct sipe_backend_codec *codec = sipe_backend_codec_new(id, name, clock_rate, type);
115 // TODO: more secure and effective implementation
116 int j = 0;
117 const gchar* params;
118 while((params = sipe_utils_nameval_find_instance(sdp_attrs, "fmtp", j++))) {
119 gchar **tokens = g_strsplit_set(params, " ", 0);
120 gchar **next = tokens + 1;
122 if (atoi(tokens[0]) == id) {
123 while (*next) {
124 gchar name[50];
125 gchar value[50];
127 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
128 sipe_backend_codec_add_optional_parameter(codec, name, value);
130 ++next;
134 g_strfreev(tokens);
137 codecs = g_list_append(codecs, codec);
138 g_strfreev(tokens);
141 return codecs;
144 static gint
145 codec_name_compare(struct sipe_backend_codec *codec1, struct sipe_backend_codec *codec2)
147 gchar *name1 = sipe_backend_codec_get_name(codec1);
148 gchar *name2 = sipe_backend_codec_get_name(codec2);
150 gint result = g_strcmp0(name1, name2);
152 g_free(name1);
153 g_free(name2);
155 return result;
158 static GList *
159 sipe_media_prune_remote_codecs(GList *local_codecs, GList *remote_codecs)
161 GList *remote_codecs_head = remote_codecs;
162 GList *pruned_codecs = NULL;
164 while (remote_codecs) {
165 struct sipe_backend_codec *c = remote_codecs->data;
167 if (g_list_find_custom(local_codecs, c, (GCompareFunc)codec_name_compare)) {
168 pruned_codecs = g_list_append(pruned_codecs, c);
169 remote_codecs->data = NULL;
171 remote_codecs = remote_codecs->next;
174 sipe_media_codec_list_free(remote_codecs_head);
176 return pruned_codecs;
179 static GList *
180 sipe_media_parse_remote_candidates_legacy(gchar *remote_ip, guint16 remote_port)
182 struct sipe_backend_candidate *candidate;
183 GList *candidates = NULL;
185 candidate = sipe_backend_candidate_new("foundation",
186 SIPE_COMPONENT_RTP,
187 SIPE_CANDIDATE_TYPE_HOST,
188 SIPE_NETWORK_PROTOCOL_UDP,
189 remote_ip, remote_port);
190 candidates = g_list_append(candidates, candidate);
192 candidate = sipe_backend_candidate_new("foundation",
193 SIPE_COMPONENT_RTCP,
194 SIPE_CANDIDATE_TYPE_HOST,
195 SIPE_NETWORK_PROTOCOL_UDP,
196 remote_ip, remote_port + 1);
197 candidates = g_list_append(candidates, candidate);
199 return candidates;
202 static GList *
203 sipe_media_parse_remote_candidates(GSList *sdp_attrs)
205 struct sipe_backend_candidate *candidate;
206 GList *candidates = NULL;
207 const gchar *attr;
208 int i = 0;
210 const gchar* username = sipe_utils_nameval_find(sdp_attrs, "ice-ufrag");
211 const gchar* password = sipe_utils_nameval_find(sdp_attrs, "ice-pwd");
213 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "candidate", i++))) {
214 gchar **tokens;
215 gchar *foundation;
216 SipeComponentType component;
217 SipeNetworkProtocol protocol;
218 guint32 priority;
219 gchar* ip;
220 guint16 port;
221 SipeCandidateType type;
223 tokens = g_strsplit_set(attr, " ", 0);
225 foundation = tokens[0];
227 switch (atoi(tokens[1])) {
228 case 1:
229 component = SIPE_COMPONENT_RTP;
230 break;
231 case 2:
232 component = SIPE_COMPONENT_RTCP;
233 break;
234 default:
235 component = SIPE_COMPONENT_NONE;
238 if (sipe_strequal(tokens[2], "UDP"))
239 protocol = SIPE_NETWORK_PROTOCOL_UDP;
240 else {
241 // Ignore TCP candidates, at least for now...
242 // Also, if this is ICEv6 candidate list, candidates are dropped here
243 g_strfreev(tokens);
244 continue;
247 priority = atoi(tokens[3]);
248 ip = tokens[4];
249 port = atoi(tokens[5]);
251 if (sipe_strequal(tokens[7], "host"))
252 type = SIPE_CANDIDATE_TYPE_HOST;
253 else if (sipe_strequal(tokens[7], "relay"))
254 type = SIPE_CANDIDATE_TYPE_RELAY;
255 else if (sipe_strequal(tokens[7], "srflx"))
256 type = SIPE_CANDIDATE_TYPE_SRFLX;
257 else if (sipe_strequal(tokens[7], "prflx"))
258 type = SIPE_CANDIDATE_TYPE_PRFLX;
259 else {
260 g_strfreev(tokens);
261 continue;
264 candidate = sipe_backend_candidate_new(foundation, component,
265 type, protocol, ip, port);
266 sipe_backend_candidate_set_priority(candidate, priority);
267 candidates = g_list_append(candidates, candidate);
269 g_strfreev(tokens);
272 if (username) {
273 GList *it = candidates;
274 while (it) {
275 sipe_backend_candidate_set_username_and_pwd(it->data, username, password);
276 it = it->next;
280 return candidates;
283 static gchar *
284 sipe_media_sdp_codec_ids_format(GList *codecs)
286 GString *result = g_string_new(NULL);
288 while (codecs) {
289 struct sipe_backend_codec *c = codecs->data;
291 g_string_append_printf(result,
292 " %d",
293 sipe_backend_codec_get_id(c));
295 codecs = codecs->next;
298 return g_string_free(result, FALSE);
301 static gchar *
302 sipe_media_sdp_codecs_format(GList *codecs)
304 GString *result = g_string_new(NULL);
306 while (codecs) {
307 struct sipe_backend_codec *c = codecs->data;
308 GList *params = NULL;
309 gchar *name = sipe_backend_codec_get_name(c);
311 g_string_append_printf(result,
312 "a=rtpmap:%d %s/%d\r\n",
313 sipe_backend_codec_get_id(c),
314 name,
315 sipe_backend_codec_get_clock_rate(c));
316 g_free(name);
318 if ((params = sipe_backend_codec_get_optional_parameters(c))) {
319 g_string_append_printf(result,
320 "a=fmtp:%d",
321 sipe_backend_codec_get_id(c));
323 while (params) {
324 struct sipnameval* par = params->data;
325 g_string_append_printf(result,
326 " %s=%s",
327 par->name, par->value);
328 params = params->next;
330 g_string_append(result, "\r\n");
333 codecs = codecs->next;
336 return g_string_free(result, FALSE);
339 static gint
340 candidate_compare_by_component_id(struct sipe_backend_candidate *c1, struct sipe_backend_candidate *c2)
342 return sipe_backend_candidate_get_component_type(c1)
343 - sipe_backend_candidate_get_component_type(c2);
346 static gchar *
347 sipe_media_sdp_candidates_format(struct sipe_media_call_private *call_private, guint16 *local_port)
349 struct sipe_backend_media *backend_media = call_private->public.backend_private;
350 struct sipe_backend_stream *voice_stream = call_private->voice_stream;
351 GList *l_candidates;
352 GList *r_candidates;
353 GList *cand;
354 gchar *username;
355 gchar *password;
356 GString *result = g_string_new("");
357 guint16 rtcp_port = 0;
359 // If we have established candidate pairs, send them in SDP response.
360 // Otherwise send all available local candidates.
361 l_candidates = sipe_backend_media_get_active_local_candidates(backend_media, voice_stream);
362 if (!l_candidates)
363 l_candidates = sipe_backend_get_local_candidates(backend_media, voice_stream);
365 // If in legacy mode, just fill local_port variable with local host's RTP
366 // component port and return empty string.
367 if (call_private->legacy_mode) {
368 for (cand = l_candidates; cand; cand = cand->next) {
369 struct sipe_backend_candidate *c = cand->data;
370 SipeCandidateType type = sipe_backend_candidate_get_type(c);
371 SipeComponentType component = sipe_backend_candidate_get_component_type(c);
373 if (type == SIPE_CANDIDATE_TYPE_HOST && component == SIPE_COMPONENT_RTP) {
374 *local_port = sipe_backend_candidate_get_port(c);
375 break;
379 sipe_media_candidate_list_free(l_candidates);
381 return g_string_free(result, FALSE);
384 username = sipe_backend_candidate_get_username(l_candidates->data);
385 password = sipe_backend_candidate_get_password(l_candidates->data);
387 g_string_append_printf(result,
388 "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
389 username, password);
391 g_free(username);
392 g_free(password);
394 for (cand = l_candidates; cand; cand = cand->next) {
395 struct sipe_backend_candidate *c = cand->data;
397 guint16 port;
398 SipeComponentType component;
399 const gchar *protocol;
400 const gchar *type;
401 gchar *related = NULL;
402 gchar *tmp_foundation;
403 gchar *tmp_ip;
405 component = sipe_backend_candidate_get_component_type(c);
406 port = sipe_backend_candidate_get_port(c);
408 switch (sipe_backend_candidate_get_protocol(c)) {
409 case SIPE_NETWORK_PROTOCOL_TCP:
410 protocol = "TCP";
411 break;
412 case SIPE_NETWORK_PROTOCOL_UDP:
413 protocol = "UDP";
414 break;
417 switch (sipe_backend_candidate_get_type(c)) {
418 case SIPE_CANDIDATE_TYPE_HOST:
419 type = "host";
420 if (component == SIPE_COMPONENT_RTP)
421 *local_port = port;
422 else if (component == SIPE_COMPONENT_RTCP)
423 rtcp_port = port;
424 break;
425 case SIPE_CANDIDATE_TYPE_RELAY:
426 type = "relay";
427 break;
428 case SIPE_CANDIDATE_TYPE_SRFLX:
430 gchar *tmp;
432 type = "srflx";
433 related = g_strdup_printf("raddr %s rport %d",
434 tmp = sipe_backend_candidate_get_base_ip(c),
435 sipe_backend_candidate_get_base_port(c));
436 g_free(tmp);
438 break;
439 case SIPE_CANDIDATE_TYPE_PRFLX:
440 type = "prflx";
441 break;
442 default:
443 // TODO: error unknown/unsupported type
444 break;
447 g_string_append_printf(result,
448 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
449 tmp_foundation = sipe_backend_candidate_get_foundation(c),
450 component,
451 protocol,
452 sipe_backend_candidate_get_priority(c),
453 tmp_ip = sipe_backend_candidate_get_ip(c),
454 port,
455 type,
456 related ? related : "");
457 g_free(tmp_ip);
458 g_free(tmp_foundation);
459 g_free(related);
462 r_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, voice_stream);
463 r_candidates = g_list_sort(r_candidates, (GCompareFunc)candidate_compare_by_component_id);
465 if (r_candidates) {
466 g_string_append(result, "a=remote-candidates:");
467 for (cand = r_candidates; cand; cand = cand->next) {
468 struct sipe_backend_candidate *candidate = cand->data;
469 gchar *tmp;
470 g_string_append_printf(result,
471 "%u %s %u ",
472 sipe_backend_candidate_get_component_type(candidate),
473 tmp = sipe_backend_candidate_get_ip(candidate),
474 sipe_backend_candidate_get_port(candidate));
475 g_free(tmp);
477 g_string_append(result, "\r\n");
480 sipe_media_candidate_list_free(l_candidates);
481 sipe_media_candidate_list_free(r_candidates);
483 if (rtcp_port != 0) {
484 g_string_append_printf(result,
485 "a=maxptime:200\r\na=rtcp:%u\r\n",
486 rtcp_port);
489 return g_string_free(result, FALSE);
492 static gchar*
493 sipe_media_create_sdp(struct sipe_media_call_private *call_private) {
494 GList *usable_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
495 call_private->voice_stream);
496 gchar *body = NULL;
498 const char *ip = sipe_utils_get_suitable_local_ip(-1);
499 guint16 local_port = 0;
501 gchar *sdp_codecs = sipe_media_sdp_codecs_format(usable_codecs);
502 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(usable_codecs);
503 gchar *sdp_candidates = sipe_media_sdp_candidates_format(call_private, &local_port);
504 gchar *inactive = (call_private->public.local_on_hold ||
505 call_private->public.remote_on_hold) ? "a=inactive\r\n" : "";
507 body = g_strdup_printf(
508 "v=0\r\n"
509 "o=- 0 0 IN IP4 %s\r\n"
510 "s=session\r\n"
511 "c=IN IP4 %s\r\n"
512 "b=CT:99980\r\n"
513 "t=0 0\r\n"
514 "m=audio %d RTP/AVP%s\r\n"
515 "%s"
516 "%s"
517 "%s"
518 "a=encryption:rejected\r\n"
519 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
521 g_free(sdp_codecs);
522 g_free(sdp_codec_ids);
523 g_free(sdp_candidates);
525 sipe_media_codec_list_free(usable_codecs);
527 return body;
530 static void
531 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
533 gchar *hdr;
534 gchar *contact;
535 gchar *body;
536 struct sipe_media_call_private *call_private = sipe_private->media_call;
537 struct sip_dialog *dialog = call_private->dialog;
539 contact = get_contact(sipe_private);
540 hdr = g_strdup_printf(
541 "Supported: ms-early-media\r\n"
542 "Supported: 100rel\r\n"
543 "ms-keep-alive: UAC;hop-hop=yes\r\n"
544 "Contact: %s%s\r\n"
545 "Content-Type: application/sdp\r\n",
546 contact,
547 (call_private->public.local_on_hold || call_private->public.remote_on_hold) ? ";+sip.rendering=\"no\"" : "");
548 g_free(contact);
550 body = sipe_media_create_sdp(call_private);
552 dialog->outgoing_invite = sip_transport_invite(sipe_private,
553 hdr,
554 body,
555 dialog,
556 tc);
558 g_free(body);
559 g_free(hdr);
562 static gboolean
563 sipe_media_parse_remote_codecs(struct sipe_media_call_private *call_private);
565 static gboolean
566 sipe_media_parse_sdp_attributes_and_candidates(struct sipe_media_call_private *call_private, gchar *frame) {
567 gchar **lines = g_strsplit(frame, "\r\n", 0);
568 GSList *sdp_attrs = NULL;
569 gchar *remote_ip = NULL;
570 guint16 remote_port = 0;
571 GList *remote_candidates;
572 gchar **ptr;
573 gboolean no_error = TRUE;
575 for (ptr = lines; *ptr != NULL; ++ptr) {
576 if (g_str_has_prefix(*ptr, "a=")) {
577 gchar **parts = g_strsplit(*ptr + 2, ":", 2);
578 if(!parts[0]) {
579 g_strfreev(parts);
580 sipe_utils_nameval_free(sdp_attrs);
581 sdp_attrs = NULL;
582 no_error = FALSE;
583 break;
585 sdp_attrs = sipe_utils_nameval_add(sdp_attrs, parts[0], parts[1]);
586 g_strfreev(parts);
588 } else if (g_str_has_prefix(*ptr, "o=")) {
589 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
590 remote_ip = g_strdup(parts[5]);
591 g_strfreev(parts);
592 } else if (g_str_has_prefix(*ptr, "m=")) {
593 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
594 remote_port = atoi(parts[1]);
595 g_strfreev(parts);
599 g_strfreev(lines);
601 remote_candidates = sipe_media_parse_remote_candidates(sdp_attrs);
602 if (!remote_candidates) {
603 // No a=candidate in SDP message, revert to OC2005 behaviour
604 remote_candidates = sipe_media_parse_remote_candidates_legacy(remote_ip, remote_port);
605 // This seems to be pre-OC2007 R2 UAC
606 call_private->legacy_mode = TRUE;
609 if (no_error) {
610 sipe_utils_nameval_free(call_private->sdp_attrs);
611 sipe_media_candidate_list_free(call_private->remote_candidates);
613 call_private->sdp_attrs = sdp_attrs;
614 call_private->remote_ip = remote_ip;
615 call_private->remote_port = remote_port;
616 call_private->remote_candidates = remote_candidates;
617 } else {
618 sipe_utils_nameval_free(sdp_attrs);
619 sipe_media_candidate_list_free(remote_candidates);
622 return no_error;
625 static gboolean
626 sipe_media_parse_remote_codecs(struct sipe_media_call_private *call_private)
628 GList *local_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
629 call_private->voice_stream);
630 GList *remote_codecs;
632 remote_codecs = sipe_media_parse_codecs(call_private->sdp_attrs);
633 remote_codecs = sipe_media_prune_remote_codecs(local_codecs, remote_codecs);
635 sipe_media_codec_list_free(local_codecs);
637 if (remote_codecs) {
638 sipe_media_codec_list_free(call_private->public.remote_codecs);
640 call_private->public.remote_codecs = remote_codecs;
642 if (!sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL,
643 call_private->voice_stream)) {
644 SIPE_DEBUG_ERROR_NOFORMAT("ERROR SET REMOTE CODECS"); // TODO
645 return FALSE;
648 return TRUE;
649 } else {
650 sipe_media_codec_list_free(remote_codecs);
651 SIPE_DEBUG_ERROR_NOFORMAT("ERROR NO CANDIDATES OR CODECS");
653 return FALSE;
657 static struct sip_dialog *
658 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
660 gchar *newTag = gentag();
661 const gchar *oldHeader;
662 gchar *newHeader;
663 struct sip_dialog *dialog;
665 oldHeader = sipmsg_find_header(msg, "To");
666 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
667 sipmsg_remove_header_now(msg, "To");
668 sipmsg_add_header_now(msg, "To", newHeader);
669 g_free(newHeader);
671 dialog = sipe_dialog_add(session);
672 dialog->callid = g_strdup(session->callid);
673 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
674 sipe_dialog_parse(dialog, msg, FALSE);
676 return dialog;
679 static void
680 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
682 gchar *body = sipe_media_create_sdp(call_private);
683 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
684 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
685 g_free(body);
688 static gboolean
689 encryption_levels_compatible(struct sipe_media_call_private *call_private)
691 const gchar *encrypion_level;
693 encrypion_level = sipe_utils_nameval_find(call_private->sdp_attrs,
694 "encryption");
696 // Decline call if peer requires encryption as we don't support it yet.
697 return !sipe_strequal(encrypion_level, "required");
700 static void
701 handle_incompatible_encryption_level(struct sipe_media_call_private *call_private)
703 sipmsg_add_header(call_private->invitation, "Warning",
704 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
705 sip_transport_response(call_private->sipe_private,
706 call_private->invitation,
707 488, "Encryption Levels not compatible",
708 NULL);
709 sipe_backend_media_reject(call_private->public.backend_private, FALSE);
710 sipe_backend_notify_error(_("Unable to establish a call"),
711 _("Encryption settings of peer are incompatible with ours."));
714 static gboolean
715 process_invite_call_response(struct sipe_core_private *sipe_private,
716 struct sipmsg *msg,
717 struct transaction *trans);
719 static void candidates_prepared_cb(struct sipe_media_call *call)
721 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
723 if (sipe_backend_media_is_initiator(call_private->public.backend_private,
724 call_private->voice_stream)) {
725 sipe_invite_call(call_private->sipe_private,
726 process_invite_call_response);
727 return;
730 if (!sipe_media_parse_remote_codecs(call_private)) {
731 g_free(call_private);
732 return;
735 if ( !call_private->legacy_mode
736 && encryption_levels_compatible(SIPE_MEDIA_CALL_PRIVATE))
737 send_response_with_session_description(call_private,
738 183, "Session Progress");
741 static void media_connected_cb(SIPE_UNUSED_PARAMETER struct sipe_media_call_private *call_private)
745 static void call_accept_cb(struct sipe_media_call *call, gboolean local)
747 if (local) {
748 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
750 if (!encryption_levels_compatible(call_private)) {
751 handle_incompatible_encryption_level(call_private);
752 return;
755 send_response_with_session_description(call_private, 200, "OK");
759 static void call_reject_cb(struct sipe_media_call *call, gboolean local)
761 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
763 if (local) {
764 sip_transport_response(call_private->sipe_private, call_private->invitation, 603, "Decline", NULL);
766 call_private->sipe_private->media_call = NULL;
767 sipe_media_call_free(call_private);
770 static gboolean
771 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
772 struct transaction *trans);
774 static void call_hold_cb(struct sipe_media_call *call, gboolean local, gboolean state)
776 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
778 if (local && (call_private->public.local_on_hold != state)) {
779 call_private->public.local_on_hold = state;
780 sipe_invite_call(call_private->sipe_private, sipe_media_send_ack);
781 } else if (call_private->public.remote_on_hold != state) {
782 call_private->public.remote_on_hold = state;
783 send_response_with_session_description(call_private, 200, "OK");
787 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
789 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
791 if (local) {
792 sip_transport_bye(call_private->sipe_private, call_private->dialog);
794 call_private->sipe_private->media_call = NULL;
795 sipe_media_call_free(call_private);
798 static struct sipe_media_call_private *
799 sipe_media_call_init(struct sipe_core_private *sipe_private, const gchar* participant, gboolean initiator)
801 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
803 call_private->sipe_private = sipe_private;
804 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
805 SIPE_MEDIA_CALL,
806 participant,
807 initiator);
809 call_private->legacy_mode = FALSE;
810 call_private->using_nice = TRUE;
812 call_private->public.candidates_prepared_cb = candidates_prepared_cb;
813 call_private->public.media_connected_cb = media_connected_cb;
814 call_private->public.call_accept_cb = call_accept_cb;
815 call_private->public.call_reject_cb = call_reject_cb;
816 call_private->public.call_hold_cb = call_hold_cb;
817 call_private->public.call_hangup_cb = call_hangup_cb;
819 call_private->public.local_on_hold = FALSE;
820 call_private->public.remote_on_hold = FALSE;
822 return call_private;
825 void sipe_media_hangup(struct sipe_core_private *sipe_private)
827 struct sipe_media_call_private *call_private = sipe_private->media_call;
828 if (call_private)
829 sipe_backend_media_hangup(call_private->public.backend_private,
830 FALSE);
833 void
834 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
835 const char *participant)
837 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
838 struct sipe_media_call_private *call_private;
840 if (sipe_private->media_call)
841 return;
843 call_private = sipe_media_call_init(sipe_private, participant, TRUE);
845 sipe_private->media_call = call_private;
847 call_private->session = sipe_session_add_chat(sipe_private);
848 call_private->dialog = sipe_dialog_add(call_private->session);
849 call_private->dialog->callid = gencallid();
850 call_private->dialog->with = g_strdup(participant);
851 call_private->dialog->ourtag = gentag();
853 call_private->voice_stream =
854 sipe_backend_media_add_stream(call_private->public.backend_private,
855 participant,
856 SIPE_MEDIA_AUDIO,
857 call_private->using_nice, TRUE);
861 void
862 sipe_media_incoming_invite(struct sipe_core_private *sipe_private,
863 struct sipmsg *msg)
865 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
867 struct sipe_media_call_private *call_private = sipe_private->media_call;
868 struct sip_session *session;
869 struct sip_dialog *dialog;
871 if (call_private) {
872 if (sipe_strequal(call_private->dialog->callid, callid)) {
873 if (call_private->invitation)
874 sipmsg_free(call_private->invitation);
875 call_private->invitation = sipmsg_copy(msg);
877 sipe_utils_nameval_free(call_private->sdp_attrs);
878 call_private->sdp_attrs = NULL;
879 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private,
880 call_private->invitation->body)) {
881 // TODO: handle error
884 if (!encryption_levels_compatible(call_private)) {
885 handle_incompatible_encryption_level(call_private);
886 return;
889 if (!sipe_media_parse_remote_codecs(call_private)) {
890 g_free(call_private);
891 return;
894 if (call_private->legacy_mode && !call_private->public.remote_on_hold) {
895 sipe_backend_media_hold(call_private->public.backend_private,
896 FALSE);
897 } else if (sipe_utils_nameval_find(call_private->sdp_attrs, "inactive")) {
898 sipe_backend_media_hold(call_private->public.backend_private, FALSE);
899 } else if (call_private->public.remote_on_hold) {
900 sipe_backend_media_unhold(call_private->public.backend_private, FALSE);
901 } else {
902 send_response_with_session_description(call_private,
903 200, "OK");
905 } else {
906 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
908 return;
911 session = sipe_session_find_or_add_chat_by_callid(sipe_private, callid);
912 dialog = sipe_media_dialog_init(session, msg);
914 call_private = sipe_media_call_init(sipe_private, dialog->with, FALSE);
915 call_private->invitation = sipmsg_copy(msg);
916 call_private->session = session;
917 call_private->dialog = dialog;
919 sipe_private->media_call = call_private;
921 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
922 // TODO error
925 call_private->voice_stream =
926 sipe_backend_media_add_stream(call_private->public.backend_private,
927 dialog->with,
928 SIPE_MEDIA_AUDIO,
929 !call_private->legacy_mode, FALSE);
930 sipe_backend_media_add_remote_candidates(call_private->public.backend_private,
931 call_private->voice_stream,
932 call_private->remote_candidates);
934 sip_transport_response(sipe_private, call_private->invitation, 180, "Ringing", NULL);
936 // Processing continues in candidates_prepared_cb
939 static gboolean
940 sipe_media_send_ack(struct sipe_core_private *sipe_private,
941 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
942 struct transaction *trans)
944 struct sipe_media_call_private *call_private = sipe_private->media_call;
945 struct sip_dialog *dialog;
946 int trans_cseq;
947 int tmp_cseq;
949 if (!call_private || !call_private->dialog)
950 return FALSE;
952 dialog = call_private->dialog;
953 tmp_cseq = dialog->cseq;
955 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq);
956 dialog->cseq = trans_cseq - 1;
957 sip_transport_ack(sipe_private, dialog);
958 dialog->cseq = tmp_cseq;
960 dialog->outgoing_invite = NULL;
962 return TRUE;
965 static gboolean
966 process_invite_call_response(struct sipe_core_private *sipe_private,
967 struct sipmsg *msg,
968 struct transaction *trans)
970 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
971 const gchar *with;
972 struct sipe_media_call_private *call_private = sipe_private->media_call;
973 struct sipe_backend_media *backend_private;
975 if (!call_private ||
976 !sipe_strequal(sipe_media_get_callid(call_private), callid))
977 return FALSE;
979 backend_private = call_private->public.backend_private;
980 with = call_private->dialog->with;
982 call_private->dialog->outgoing_invite = NULL;
984 if (msg->response >= 400) {
985 // Call rejected by remote peer or an error occurred
986 gchar *title;
987 GString *desc = g_string_new("");
989 sipe_backend_media_reject(backend_private, FALSE);
990 sipe_media_send_ack(sipe_private, msg, trans);
992 switch (msg->response) {
993 case 480:
994 title = _("User unavailable");
995 g_string_append_printf(desc, _("User %s is not available"), with);
996 case 603:
997 case 605:
998 title = _("Call rejected");
999 g_string_append_printf(desc, _("User %s rejected call"), with);
1000 break;
1001 default:
1002 title = _("Error occured");
1003 g_string_append(desc, _("Unable to establish a call"));
1004 break;
1007 g_string_append_printf(desc, "\n%d %s", msg->response, msg->responsestr);
1009 sipe_backend_notify_error(title, desc->str);
1010 g_string_free(desc, TRUE);
1012 return TRUE;
1015 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
1016 return FALSE;
1019 if (!sipe_media_parse_remote_codecs(call_private)) {
1020 g_free(call_private);
1021 return FALSE;
1024 sipe_backend_media_add_remote_candidates(backend_private,
1025 call_private->voice_stream,
1026 call_private->remote_candidates);
1028 sipe_dialog_parse(call_private->dialog, msg, TRUE);
1030 if (msg->response == 183) {
1031 // Session in progress
1032 const gchar *rseq = sipmsg_find_header(msg, "RSeq");
1033 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
1034 gchar *rack = g_strdup_printf("RAck: %s %s\r\n", rseq, cseq);
1035 sip_transport_request(sipe_private,
1036 "PRACK",
1037 with,
1038 with,
1039 rack,
1040 NULL,
1041 call_private->dialog,
1042 NULL);
1043 g_free(rack);
1044 } else {
1045 sipe_media_send_ack(sipe_private, msg, trans);
1047 if (call_private->legacy_mode && call_private->using_nice) {
1048 // We created non-legacy stream as we don't know which version of
1049 // client is on the other side until first SDP response is received.
1050 // This client requires legacy mode, so we must remove current session
1051 // (using ICE) and create new using raw UDP transport.
1052 struct sipe_backend_stream *new_stream;
1054 call_private->using_nice = FALSE;
1056 new_stream = sipe_backend_media_add_stream(backend_private,
1057 with,
1058 SIPE_MEDIA_AUDIO,
1059 FALSE,
1060 TRUE);
1062 sipe_backend_media_remove_stream(backend_private,
1063 call_private->voice_stream);
1064 call_private->voice_stream = new_stream;
1066 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
1067 return FALSE;
1070 if (!sipe_media_parse_remote_codecs(call_private)) {
1071 g_free(call_private);
1072 return FALSE;
1075 sipe_backend_media_add_remote_candidates(backend_private,
1076 call_private->voice_stream,
1077 call_private->remote_candidates);
1079 // New INVITE will be sent in candidates_prepared_cb
1080 } else {
1081 sipe_invite_call(sipe_private, sipe_media_send_ack);
1085 return TRUE;
1089 Local Variables:
1090 mode: c
1091 c-file-style: "bsd"
1092 indent-tabs-mode: t
1093 tab-width: 8
1094 End: