audio: improved handling of declined calls
[siplcs.git] / src / core / sipe-media.c
blobe155472e8fc8ea36adbf9aa1b599adf4fe93aa56
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 gchar *tmp = g_strdup_printf(" %d", sipe_backend_codec_get_id(c));
292 g_string_append(result,tmp);
293 g_free(tmp);
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 gchar *tmp = g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
312 sipe_backend_codec_get_id(c),
313 name,
314 sipe_backend_codec_get_clock_rate(c));
316 g_free(name);
317 g_string_append(result, tmp);
318 g_free(tmp);
320 if ((params = sipe_backend_codec_get_optional_parameters(c))) {
321 tmp = g_strdup_printf("a=fmtp:%d",sipe_backend_codec_get_id(c));
322 g_string_append(result, tmp);
323 g_free(tmp);
325 while (params) {
326 struct sipnameval* par = params->data;
327 tmp = g_strdup_printf(" %s=%s", par->name, par->value);
328 g_string_append(result, tmp);
329 g_free(tmp);
330 params = params->next;
332 g_string_append(result, "\r\n");
335 codecs = codecs->next;
338 return g_string_free(result, FALSE);
341 static gint
342 candidate_compare_by_component_id(struct sipe_backend_candidate *c1, struct sipe_backend_candidate *c2)
344 return sipe_backend_candidate_get_component_type(c1)
345 - sipe_backend_candidate_get_component_type(c2);
348 static gchar *
349 sipe_media_sdp_candidates_format(struct sipe_media_call_private *call_private, guint16 *local_port)
351 struct sipe_backend_media *backend_media = call_private->public.backend_private;
352 struct sipe_backend_stream *voice_stream = call_private->voice_stream;
353 GList *l_candidates;
354 GList *r_candidates;
355 GList *cand;
356 gchar *username;
357 gchar *password;
358 GString *result = g_string_new("");
359 gchar *tmp;
360 guint16 rtcp_port = 0;
362 // If we have established candidate pairs, send them in SDP response.
363 // Otherwise send all available local candidates.
364 l_candidates = sipe_backend_media_get_active_local_candidates(backend_media, voice_stream);
365 if (!l_candidates)
366 l_candidates = sipe_backend_get_local_candidates(backend_media, voice_stream);
368 // If in legacy mode, just fill local_port variable with local host's RTP
369 // component port and return empty string.
370 if (call_private->legacy_mode) {
371 for (cand = l_candidates; cand; cand = cand->next) {
372 struct sipe_backend_candidate *c = cand->data;
373 SipeCandidateType type = sipe_backend_candidate_get_type(c);
374 SipeComponentType component = sipe_backend_candidate_get_component_type(c);
376 if (type == SIPE_CANDIDATE_TYPE_HOST && component == SIPE_COMPONENT_RTP) {
377 *local_port = sipe_backend_candidate_get_port(c);
378 break;
382 sipe_media_candidate_list_free(l_candidates);
384 return g_string_free(result, FALSE);
387 username = sipe_backend_candidate_get_username(l_candidates->data);
388 password = sipe_backend_candidate_get_password(l_candidates->data);
390 tmp = g_strdup_printf("a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",username, password);
391 g_string_append(result, tmp);
392 g_free(tmp);
394 g_free(username);
395 g_free(password);
397 for (cand = l_candidates; cand; cand = cand->next) {
398 struct sipe_backend_candidate *c = cand->data;
400 guint16 port;
401 SipeComponentType component;
402 gchar *protocol;
403 gchar *type;
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:
429 type = "srflx";
430 break;
431 case SIPE_CANDIDATE_TYPE_PRFLX:
432 type = "prflx";
433 break;
434 default:
435 // TODO: error unknown/unsupported type
436 break;
439 tmp = g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
440 sipe_backend_candidate_get_foundation(c),
441 component,
442 protocol,
443 sipe_backend_candidate_get_priority(c),
444 sipe_backend_candidate_get_ip(c),
445 port,
446 type);
448 g_string_append(result, tmp);
449 g_free(tmp);
452 r_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, voice_stream);
453 r_candidates = g_list_sort(r_candidates, (GCompareFunc)candidate_compare_by_component_id);
455 if (r_candidates) {
456 g_string_append(result, "a=remote-candidates:");
457 for (cand = r_candidates; cand; cand = cand->next) {
458 struct sipe_backend_candidate *candidate = cand->data;
459 tmp = g_strdup_printf("%u %s %u ",
460 sipe_backend_candidate_get_component_type(candidate),
461 sipe_backend_candidate_get_ip(candidate),
462 sipe_backend_candidate_get_port(candidate));
463 g_string_append(result, tmp);
464 g_free(tmp);
466 g_string_append(result, "\r\n");
469 sipe_media_candidate_list_free(l_candidates);
470 sipe_media_candidate_list_free(r_candidates);
472 if (rtcp_port != 0) {
473 tmp = g_strdup_printf("a=maxptime:200\r\na=rtcp:%u\r\n", rtcp_port);
474 g_string_append(result, tmp);
475 g_free(tmp);
478 return g_string_free(result, FALSE);
481 static gchar*
482 sipe_media_create_sdp(struct sipe_media_call_private *call_private) {
483 GList *usable_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
484 call_private->voice_stream);
485 gchar *body = NULL;
487 const char *ip = sipe_utils_get_suitable_local_ip(-1);
488 guint16 local_port = 0;
490 gchar *sdp_codecs = sipe_media_sdp_codecs_format(usable_codecs);
491 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(usable_codecs);
492 gchar *sdp_candidates = sipe_media_sdp_candidates_format(call_private, &local_port);
493 gchar *inactive = (call_private->public.local_on_hold ||
494 call_private->public.remote_on_hold) ? "a=inactive\r\n" : "";
496 body = g_strdup_printf(
497 "v=0\r\n"
498 "o=- 0 0 IN IP4 %s\r\n"
499 "s=session\r\n"
500 "c=IN IP4 %s\r\n"
501 "b=CT:99980\r\n"
502 "t=0 0\r\n"
503 "m=audio %d RTP/AVP%s\r\n"
504 "%s"
505 "%s"
506 "%s"
507 "a=encryption:rejected\r\n"
508 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
510 g_free(sdp_codecs);
511 g_free(sdp_codec_ids);
512 g_free(sdp_candidates);
514 sipe_media_codec_list_free(usable_codecs);
516 return body;
519 static void
520 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
522 gchar *hdr;
523 gchar *contact;
524 gchar *body;
525 struct sipe_media_call_private *call_private = sipe_private->media_call;
526 struct sip_dialog *dialog = call_private->dialog;
528 contact = get_contact(sipe_private);
529 hdr = g_strdup_printf(
530 "Supported: ms-early-media\r\n"
531 "Supported: 100rel\r\n"
532 "ms-keep-alive: UAC;hop-hop=yes\r\n"
533 "Contact: %s%s\r\n"
534 "Content-Type: application/sdp\r\n",
535 contact,
536 (call_private->public.local_on_hold || call_private->public.remote_on_hold) ? ";+sip.rendering=\"no\"" : "");
537 g_free(contact);
539 body = sipe_media_create_sdp(call_private);
541 sip_transport_invite(sipe_private,
542 hdr,
543 body,
544 dialog,
545 tc);
547 g_free(body);
548 g_free(hdr);
551 static gboolean
552 sipe_media_parse_remote_codecs(struct sipe_media_call_private *call_private);
554 static gboolean
555 sipe_media_parse_sdp_attributes_and_candidates(struct sipe_media_call_private *call_private, gchar *frame) {
556 gchar **lines = g_strsplit(frame, "\r\n", 0);
557 GSList *sdp_attrs = NULL;
558 gchar *remote_ip = NULL;
559 guint16 remote_port = 0;
560 GList *remote_candidates;
561 gchar **ptr;
562 gboolean no_error = TRUE;
564 for (ptr = lines; *ptr != NULL; ++ptr) {
565 if (g_str_has_prefix(*ptr, "a=")) {
566 gchar **parts = g_strsplit(*ptr + 2, ":", 2);
567 if(!parts[0]) {
568 g_strfreev(parts);
569 sipe_utils_nameval_free(sdp_attrs);
570 sdp_attrs = NULL;
571 no_error = FALSE;
572 break;
574 sdp_attrs = sipe_utils_nameval_add(sdp_attrs, parts[0], parts[1]);
575 g_strfreev(parts);
577 } else if (g_str_has_prefix(*ptr, "o=")) {
578 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
579 remote_ip = g_strdup(parts[5]);
580 g_strfreev(parts);
581 } else if (g_str_has_prefix(*ptr, "m=")) {
582 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
583 remote_port = atoi(parts[1]);
584 g_strfreev(parts);
588 g_strfreev(lines);
590 remote_candidates = sipe_media_parse_remote_candidates(sdp_attrs);
591 if (!remote_candidates) {
592 // No a=candidate in SDP message, revert to OC2005 behaviour
593 remote_candidates = sipe_media_parse_remote_candidates_legacy(remote_ip, remote_port);
594 // This seems to be pre-OC2007 R2 UAC
595 call_private->legacy_mode = TRUE;
598 if (no_error) {
599 sipe_utils_nameval_free(call_private->sdp_attrs);
600 sipe_media_candidate_list_free(call_private->remote_candidates);
602 call_private->sdp_attrs = sdp_attrs;
603 call_private->remote_ip = remote_ip;
604 call_private->remote_port = remote_port;
605 call_private->remote_candidates = remote_candidates;
606 } else {
607 sipe_utils_nameval_free(sdp_attrs);
608 sipe_media_candidate_list_free(remote_candidates);
611 return no_error;
614 static gboolean
615 sipe_media_parse_remote_codecs(struct sipe_media_call_private *call_private)
617 GList *local_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
618 call_private->voice_stream);
619 GList *remote_codecs;
621 remote_codecs = sipe_media_parse_codecs(call_private->sdp_attrs);
622 remote_codecs = sipe_media_prune_remote_codecs(local_codecs, remote_codecs);
624 sipe_media_codec_list_free(local_codecs);
626 if (remote_codecs) {
627 sipe_media_codec_list_free(call_private->public.remote_codecs);
629 call_private->public.remote_codecs = remote_codecs;
631 if (!sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL,
632 call_private->voice_stream)) {
633 SIPE_DEBUG_ERROR_NOFORMAT("ERROR SET REMOTE CODECS"); // TODO
634 return FALSE;
637 return TRUE;
638 } else {
639 sipe_media_codec_list_free(remote_codecs);
640 SIPE_DEBUG_ERROR_NOFORMAT("ERROR NO CANDIDATES OR CODECS");
642 return FALSE;
646 static struct sip_dialog *
647 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
649 gchar *newTag = gentag();
650 const gchar *oldHeader;
651 gchar *newHeader;
652 struct sip_dialog *dialog;
654 oldHeader = sipmsg_find_header(msg, "To");
655 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
656 sipmsg_remove_header_now(msg, "To");
657 sipmsg_add_header_now(msg, "To", newHeader);
658 g_free(newHeader);
660 dialog = sipe_dialog_add(session);
661 dialog->callid = g_strdup(session->callid);
662 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
663 sipe_dialog_parse(dialog, msg, FALSE);
665 return dialog;
668 static void
669 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
671 gchar *body = sipe_media_create_sdp(call_private);
672 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
673 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
674 g_free(body);
677 static gboolean
678 sipe_media_process_invite_response(struct sipe_core_private *sipe_private,
679 struct sipmsg *msg,
680 struct transaction *trans);
682 static void candidates_prepared_cb(struct sipe_media_call *call)
684 struct sipe_media_call_private *call_private = (struct sipe_media_call_private *) call;
686 if (sipe_backend_media_is_initiator(call_private->public.backend_private,
687 call_private->voice_stream)) {
688 sipe_invite_call(call_private->sipe_private, sipe_media_process_invite_response);
689 } else {
690 if (!sipe_media_parse_remote_codecs(call_private)) {
691 g_free(call_private);
692 return;
695 if (!call_private->legacy_mode)
696 send_response_with_session_description(call_private, 183, "Session Progress");
700 static void media_connected_cb(SIPE_UNUSED_PARAMETER struct sipe_media_call_private *call_private)
704 static void call_accept_cb(struct sipe_media_call *call, gboolean local)
706 if (local) {
707 send_response_with_session_description(SIPE_MEDIA_CALL_PRIVATE,
708 200, "OK");
712 static void call_reject_cb(struct sipe_media_call *call, gboolean local)
714 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
716 if (local) {
717 sip_transport_response(call_private->sipe_private, call_private->invitation, 603, "Decline", NULL);
719 call_private->sipe_private->media_call = NULL;
720 sipe_media_call_free(call_private);
723 static gboolean
724 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
725 struct transaction *trans);
727 static void call_hold_cb(struct sipe_media_call *call, gboolean local, gboolean state)
729 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
731 if (local && (call_private->public.local_on_hold != state)) {
732 call_private->public.local_on_hold = state;
733 sipe_invite_call(call_private->sipe_private, sipe_media_send_ack);
734 } else if (call_private->public.remote_on_hold != state) {
735 call_private->public.remote_on_hold = state;
736 send_response_with_session_description(call_private, 200, "OK");
740 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
742 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
744 if (local) {
745 sip_transport_bye(call_private->sipe_private, call_private->dialog);
747 call_private->sipe_private->media_call = NULL;
748 sipe_media_call_free(call_private);
751 static struct sipe_media_call_private *
752 sipe_media_call_init(struct sipe_core_private *sipe_private, const gchar* participant, gboolean initiator)
754 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
756 call_private->sipe_private = sipe_private;
757 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
758 SIPE_MEDIA_CALL,
759 participant,
760 initiator);
762 call_private->legacy_mode = FALSE;
763 call_private->using_nice = TRUE;
765 call_private->public.candidates_prepared_cb = candidates_prepared_cb;
766 call_private->public.media_connected_cb = media_connected_cb;
767 call_private->public.call_accept_cb = call_accept_cb;
768 call_private->public.call_reject_cb = call_reject_cb;
769 call_private->public.call_hold_cb = call_hold_cb;
770 call_private->public.call_hangup_cb = call_hangup_cb;
772 call_private->public.local_on_hold = FALSE;
773 call_private->public.remote_on_hold = FALSE;
775 return call_private;
778 void sipe_media_hangup(struct sipe_core_private *sipe_private)
780 struct sipe_media_call_private *call_private = sipe_private->media_call;
781 if (call_private)
782 sipe_backend_media_hangup(call_private->public.backend_private,
783 FALSE);
786 void
787 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
788 const char *participant)
790 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
791 struct sipe_media_call_private *call_private;
793 if (sipe_private->media_call)
794 return;
796 call_private = sipe_media_call_init(sipe_private, participant, TRUE);
798 sipe_private->media_call = call_private;
800 call_private->session = sipe_session_add_chat(sipe_private);
801 call_private->dialog = sipe_dialog_add(call_private->session);
802 call_private->dialog->callid = gencallid();
803 call_private->dialog->with = g_strdup(participant);
804 call_private->dialog->ourtag = gentag();
806 call_private->voice_stream =
807 sipe_backend_media_add_stream(call_private->public.backend_private,
808 participant,
809 SIPE_MEDIA_AUDIO,
810 call_private->using_nice, TRUE);
814 void
815 sipe_media_incoming_invite(struct sipe_core_private *sipe_private,
816 struct sipmsg *msg)
818 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
820 struct sipe_media_call_private *call_private = sipe_private->media_call;
821 struct sip_session *session;
822 struct sip_dialog *dialog;
824 if (call_private) {
825 if (sipe_strequal(call_private->dialog->callid, callid)) {
826 if (call_private->invitation)
827 sipmsg_free(call_private->invitation);
828 call_private->invitation = sipmsg_copy(msg);
830 sipe_utils_nameval_free(call_private->sdp_attrs);
831 call_private->sdp_attrs = NULL;
832 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private,
833 call_private->invitation->body)) {
834 // TODO: handle error
836 if (!sipe_media_parse_remote_codecs(call_private)) {
837 g_free(call_private);
838 return;
841 if (call_private->legacy_mode && !call_private->public.remote_on_hold) {
842 sipe_backend_media_hold(call_private->public.backend_private,
843 FALSE);
844 } else if (sipe_utils_nameval_find(call_private->sdp_attrs, "inactive")) {
845 sipe_backend_media_hold(call_private->public.backend_private, FALSE);
846 } else if (call_private->public.remote_on_hold) {
847 sipe_backend_media_unhold(call_private->public.backend_private, FALSE);
848 } else {
849 send_response_with_session_description(call_private,
850 200, "OK");
852 } else {
853 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
855 return;
858 session = sipe_session_find_or_add_chat_by_callid(sipe_private, callid);
859 dialog = sipe_media_dialog_init(session, msg);
861 call_private = sipe_media_call_init(sipe_private, dialog->with, FALSE);
862 call_private->invitation = sipmsg_copy(msg);
863 call_private->session = session;
864 call_private->dialog = dialog;
866 sipe_private->media_call = call_private;
868 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
869 // TODO error
872 call_private->voice_stream =
873 sipe_backend_media_add_stream(call_private->public.backend_private,
874 dialog->with,
875 SIPE_MEDIA_AUDIO,
876 !call_private->legacy_mode, FALSE);
877 sipe_backend_media_add_remote_candidates(call_private->public.backend_private,
878 call_private->voice_stream,
879 call_private->remote_candidates);
881 sip_transport_response(sipe_private, call_private->invitation, 180, "Ringing", NULL);
883 // Processing continues in candidates_prepared_cb
886 static gboolean
887 sipe_media_send_ack(struct sipe_core_private *sipe_private,
888 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
889 struct transaction *trans)
891 struct sipe_media_call_private *call_private = sipe_private->media_call;
892 struct sip_dialog *dialog;
893 int trans_cseq;
894 int tmp_cseq;
896 if (!call_private || !call_private->dialog)
897 return FALSE;
899 dialog = call_private->dialog;
900 tmp_cseq = dialog->cseq;
902 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq);
903 dialog->cseq = trans_cseq - 1;
904 sip_transport_ack(sipe_private, dialog);
905 dialog->cseq = tmp_cseq;
907 return TRUE;
910 static gboolean
911 sipe_media_process_invite_response(struct sipe_core_private *sipe_private,
912 struct sipmsg *msg,
913 struct transaction *trans)
915 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
916 const gchar *with;
917 struct sipe_media_call_private *call_private = sipe_private->media_call;
918 struct sipe_backend_media *backend_private;
920 if (!call_private ||
921 !sipe_strequal(sipe_media_get_callid(call_private), callid))
922 return FALSE;
924 backend_private = call_private->public.backend_private;
925 with = call_private->dialog->with;
927 if (msg->response == 603 || msg->response == 605 || msg->response == 480) {
928 // Call rejected by remote peer
929 gchar *errmsg;
930 gchar *title;
932 sipe_backend_media_reject(backend_private, FALSE);
933 sipe_media_send_ack(sipe_private, msg, trans);
935 if (msg->response > 600) {
936 title = _("Call rejected");
937 errmsg = g_strdup_printf(_("User %s rejected call"), with);
938 } else {
939 errmsg = g_strdup_printf(_("User %s is not available"), with);
940 title = _("User unavailable");
942 sipe_backend_notify_error(title, errmsg);
943 g_free(errmsg);
945 return TRUE;
948 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
949 return FALSE;
952 if (!sipe_media_parse_remote_codecs(call_private)) {
953 g_free(call_private);
954 return FALSE;
957 sipe_backend_media_add_remote_candidates(backend_private,
958 call_private->voice_stream,
959 call_private->remote_candidates);
961 sipe_dialog_parse(call_private->dialog, msg, TRUE);
963 if (msg->response == 183) {
964 // Session in progress
965 const gchar *rseq = sipmsg_find_header(msg, "RSeq");
966 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
967 gchar *rack = g_strdup_printf("RAck: %s %s\r\n", rseq, cseq);
968 sip_transport_request(sipe_private,
969 "PRACK",
970 with,
971 with,
972 rack,
973 NULL,
974 call_private->dialog,
975 NULL);
976 g_free(rack);
977 } else {
978 sipe_media_send_ack(sipe_private, msg, trans);
980 if (call_private->legacy_mode && call_private->using_nice) {
981 // We created non-legacy stream as we don't know which version of
982 // client is on the other side until first SDP response is received.
983 // This client requires legacy mode, so we must remove current session
984 // (using ICE) and create new using raw UDP transport.
985 struct sipe_backend_stream *new_stream;
987 call_private->using_nice = FALSE;
989 new_stream = sipe_backend_media_add_stream(backend_private,
990 with,
991 SIPE_MEDIA_AUDIO,
992 FALSE,
993 TRUE);
995 sipe_backend_media_remove_stream(backend_private,
996 call_private->voice_stream);
997 call_private->voice_stream = new_stream;
999 if (!sipe_media_parse_sdp_attributes_and_candidates(call_private, msg->body)) {
1000 return FALSE;
1003 if (!sipe_media_parse_remote_codecs(call_private)) {
1004 g_free(call_private);
1005 return FALSE;
1008 sipe_backend_media_add_remote_candidates(backend_private,
1009 call_private->voice_stream,
1010 call_private->remote_candidates);
1012 // New INVITE will be sent in candidates_prepared_cb
1013 } else {
1014 sipe_invite_call(sipe_private, sipe_media_send_ack);
1018 return TRUE;
1022 Local Variables:
1023 mode: c
1024 c-file-style: "bsd"
1025 indent-tabs-mode: t
1026 tab-width: 8
1027 End: