audio: extract SDP parsing functionality into separate module
[siplcs.git] / src / core / sipe-media.c
blobc99c4691898b278edf2537f7ccacf3b84e6668be
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>
29 #include <glib.h>
31 #include "sipe-common.h"
32 #include "sipmsg.h"
33 #include "sip-transport.h"
34 #include "sipe-backend.h"
35 #include "sdpmsg.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"
44 struct sipe_media_call_private {
45 struct sipe_media_call public;
47 /* private part starts here */
48 struct sipe_core_private *sipe_private;
49 struct sip_session *session;
50 struct sip_dialog *dialog;
52 struct sipe_backend_stream *voice_stream;
54 struct sipmsg *invitation;
55 gboolean legacy_mode;
56 gboolean using_nice;
57 gboolean encryption_compatible;
59 struct sdpmsg *smsg;
61 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
62 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
64 gchar *
65 sipe_media_get_callid(struct sipe_media_call_private *call)
67 return call->dialog->callid;
70 static void sipe_media_codec_list_free(GList *codecs)
72 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
73 sipe_backend_codec_free(codecs->data);
76 static void sipe_media_candidate_list_free(GList *candidates)
78 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
79 sipe_backend_candidate_free(candidates->data);
82 static void
83 sipe_media_call_free(struct sipe_media_call_private *call_private)
85 if (call_private) {
86 sipe_backend_media_free(call_private->public.backend_private);
88 if (call_private->session)
89 sipe_session_remove(call_private->sipe_private,
90 call_private->session);
92 if (call_private->invitation)
93 sipmsg_free(call_private->invitation);
95 sdpmsg_free(call_private->smsg);
96 g_free(call_private);
100 static gchar *
101 sipe_media_sdp_codec_ids_format(GList *codecs)
103 GString *result = g_string_new(NULL);
105 while (codecs) {
106 struct sipe_backend_codec *c = codecs->data;
108 g_string_append_printf(result,
109 " %d",
110 sipe_backend_codec_get_id(c));
112 codecs = codecs->next;
115 return g_string_free(result, FALSE);
118 static gchar *
119 sipe_media_sdp_codecs_format(GList *codecs)
121 GString *result = g_string_new(NULL);
123 while (codecs) {
124 struct sipe_backend_codec *c = codecs->data;
125 GList *params = NULL;
126 gchar *name = sipe_backend_codec_get_name(c);
128 g_string_append_printf(result,
129 "a=rtpmap:%d %s/%d\r\n",
130 sipe_backend_codec_get_id(c),
131 name,
132 sipe_backend_codec_get_clock_rate(c));
133 g_free(name);
135 if ((params = sipe_backend_codec_get_optional_parameters(c))) {
136 g_string_append_printf(result,
137 "a=fmtp:%d",
138 sipe_backend_codec_get_id(c));
140 while (params) {
141 struct sipnameval* par = params->data;
142 g_string_append_printf(result,
143 " %s=%s",
144 par->name, par->value);
145 params = params->next;
147 g_string_append(result, "\r\n");
150 codecs = codecs->next;
153 return g_string_free(result, FALSE);
156 static gint
157 candidate_compare_by_component_id(struct sipe_backend_candidate *c1, struct sipe_backend_candidate *c2)
159 return sipe_backend_candidate_get_component_type(c1)
160 - sipe_backend_candidate_get_component_type(c2);
163 static gchar *
164 sipe_media_sdp_candidates_format(struct sipe_media_call_private *call_private, guint16 *local_port)
166 struct sipe_backend_media *backend_media = call_private->public.backend_private;
167 struct sipe_backend_stream *voice_stream = call_private->voice_stream;
168 GList *l_candidates;
169 GList *r_candidates;
170 GList *cand;
171 gchar *username;
172 gchar *password;
173 GString *result = g_string_new("");
174 guint16 rtcp_port = 0;
176 // If we have established candidate pairs, send them in SDP response.
177 // Otherwise send all available local candidates.
178 l_candidates = sipe_backend_media_get_active_local_candidates(backend_media, voice_stream);
179 if (!l_candidates)
180 l_candidates = sipe_backend_get_local_candidates(backend_media, voice_stream);
182 // If in legacy mode, just fill local_port variable with local host's RTP
183 // component port and return empty string.
184 if (call_private->legacy_mode) {
185 for (cand = l_candidates; cand; cand = cand->next) {
186 struct sipe_backend_candidate *c = cand->data;
187 SipeCandidateType type = sipe_backend_candidate_get_type(c);
188 SipeComponentType component = sipe_backend_candidate_get_component_type(c);
190 if (type == SIPE_CANDIDATE_TYPE_HOST && component == SIPE_COMPONENT_RTP) {
191 *local_port = sipe_backend_candidate_get_port(c);
192 break;
196 sipe_media_candidate_list_free(l_candidates);
198 return g_string_free(result, FALSE);
201 username = sipe_backend_candidate_get_username(l_candidates->data);
202 password = sipe_backend_candidate_get_password(l_candidates->data);
204 g_string_append_printf(result,
205 "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
206 username, password);
208 g_free(username);
209 g_free(password);
211 for (cand = l_candidates; cand; cand = cand->next) {
212 struct sipe_backend_candidate *c = cand->data;
214 guint16 port;
215 SipeComponentType component;
216 const gchar *protocol;
217 const gchar *type;
218 gchar *related = NULL;
219 gchar *tmp_foundation;
220 gchar *tmp_ip;
222 component = sipe_backend_candidate_get_component_type(c);
223 port = sipe_backend_candidate_get_port(c);
225 switch (sipe_backend_candidate_get_protocol(c)) {
226 case SIPE_NETWORK_PROTOCOL_TCP:
227 protocol = "TCP";
228 break;
229 case SIPE_NETWORK_PROTOCOL_UDP:
230 protocol = "UDP";
231 break;
234 switch (sipe_backend_candidate_get_type(c)) {
235 case SIPE_CANDIDATE_TYPE_HOST:
236 type = "host";
237 if (component == SIPE_COMPONENT_RTP)
238 *local_port = port;
239 else if (component == SIPE_COMPONENT_RTCP)
240 rtcp_port = port;
241 break;
242 case SIPE_CANDIDATE_TYPE_RELAY:
243 type = "relay";
244 break;
245 case SIPE_CANDIDATE_TYPE_SRFLX:
247 gchar *tmp;
249 type = "srflx";
250 related = g_strdup_printf("raddr %s rport %d",
251 tmp = sipe_backend_candidate_get_base_ip(c),
252 sipe_backend_candidate_get_base_port(c));
253 g_free(tmp);
255 break;
256 case SIPE_CANDIDATE_TYPE_PRFLX:
257 type = "prflx";
258 break;
259 default:
260 // TODO: error unknown/unsupported type
261 break;
264 g_string_append_printf(result,
265 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
266 tmp_foundation = sipe_backend_candidate_get_foundation(c),
267 component,
268 protocol,
269 sipe_backend_candidate_get_priority(c),
270 tmp_ip = sipe_backend_candidate_get_ip(c),
271 port,
272 type,
273 related ? related : "");
274 g_free(tmp_ip);
275 g_free(tmp_foundation);
276 g_free(related);
279 r_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, voice_stream);
280 r_candidates = g_list_sort(r_candidates, (GCompareFunc)candidate_compare_by_component_id);
282 if (r_candidates) {
283 g_string_append(result, "a=remote-candidates:");
284 for (cand = r_candidates; cand; cand = cand->next) {
285 struct sipe_backend_candidate *candidate = cand->data;
286 gchar *tmp;
287 g_string_append_printf(result,
288 "%u %s %u ",
289 sipe_backend_candidate_get_component_type(candidate),
290 tmp = sipe_backend_candidate_get_ip(candidate),
291 sipe_backend_candidate_get_port(candidate));
292 g_free(tmp);
294 g_string_append(result, "\r\n");
297 sipe_media_candidate_list_free(l_candidates);
298 sipe_media_candidate_list_free(r_candidates);
300 if (rtcp_port != 0) {
301 g_string_append_printf(result,
302 "a=maxptime:200\r\na=rtcp:%u\r\n",
303 rtcp_port);
306 return g_string_free(result, FALSE);
309 static gchar*
310 sipe_media_create_sdp(struct sipe_media_call_private *call_private) {
311 GList *usable_codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
312 call_private->voice_stream);
313 gchar *body = NULL;
315 const char *ip = sipe_utils_get_suitable_local_ip(-1);
316 guint16 local_port = 0;
318 gchar *sdp_codecs = sipe_media_sdp_codecs_format(usable_codecs);
319 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(usable_codecs);
320 gchar *sdp_candidates = sipe_media_sdp_candidates_format(call_private, &local_port);
321 gchar *inactive = (call_private->public.local_on_hold ||
322 call_private->public.remote_on_hold) ? "a=inactive\r\n" : "";
324 body = g_strdup_printf(
325 "v=0\r\n"
326 "o=- 0 0 IN IP4 %s\r\n"
327 "s=session\r\n"
328 "c=IN IP4 %s\r\n"
329 "b=CT:99980\r\n"
330 "t=0 0\r\n"
331 "m=audio %d RTP/AVP%s\r\n"
332 "%s"
333 "%s"
334 "%s"
335 "a=encryption:rejected\r\n"
336 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
338 g_free(sdp_codecs);
339 g_free(sdp_codec_ids);
340 g_free(sdp_candidates);
342 sipe_media_codec_list_free(usable_codecs);
344 return body;
347 static void
348 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
350 gchar *hdr;
351 gchar *contact;
352 gchar *body;
353 struct sipe_media_call_private *call_private = sipe_private->media_call;
354 struct sip_dialog *dialog = call_private->dialog;
356 contact = get_contact(sipe_private);
357 hdr = g_strdup_printf(
358 "Supported: ms-early-media\r\n"
359 "Supported: 100rel\r\n"
360 "ms-keep-alive: UAC;hop-hop=yes\r\n"
361 "Contact: %s%s\r\n"
362 "Content-Type: application/sdp\r\n",
363 contact,
364 (call_private->public.local_on_hold || call_private->public.remote_on_hold) ? ";+sip.rendering=\"no\"" : "");
365 g_free(contact);
367 body = sipe_media_create_sdp(call_private);
369 dialog->outgoing_invite = sip_transport_invite(sipe_private,
370 hdr,
371 body,
372 dialog,
373 tc);
375 g_free(body);
376 g_free(hdr);
379 static struct sip_dialog *
380 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
382 gchar *newTag = gentag();
383 const gchar *oldHeader;
384 gchar *newHeader;
385 struct sip_dialog *dialog;
387 oldHeader = sipmsg_find_header(msg, "To");
388 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
389 sipmsg_remove_header_now(msg, "To");
390 sipmsg_add_header_now(msg, "To", newHeader);
391 g_free(newHeader);
393 dialog = sipe_dialog_add(session);
394 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
395 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
396 sipe_dialog_parse(dialog, msg, FALSE);
398 return dialog;
401 static void
402 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
404 gchar *body = sipe_media_create_sdp(call_private);
405 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
406 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
407 g_free(body);
410 static gboolean
411 encryption_levels_compatible(struct sdpmsg *smsg)
413 const gchar *encrypion_level;
415 encrypion_level = sipe_utils_nameval_find(smsg->attributes, "encryption");
417 // Decline call if peer requires encryption as we don't support it yet.
418 return !sipe_strequal(encrypion_level, "required");
421 static void
422 handle_incompatible_encryption_level(struct sipe_media_call_private *call_private)
424 sipmsg_add_header(call_private->invitation, "Warning",
425 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
426 sip_transport_response(call_private->sipe_private,
427 call_private->invitation,
428 488, "Encryption Levels not compatible",
429 NULL);
430 sipe_backend_media_reject(call_private->public.backend_private, FALSE);
431 sipe_backend_notify_error(_("Unable to establish a call"),
432 _("Encryption settings of peer are incompatible with ours."));
435 static gboolean
436 process_invite_call_response(struct sipe_core_private *sipe_private,
437 struct sipmsg *msg,
438 struct transaction *trans);
440 static gboolean
441 apply_remote_message(struct sipe_media_call_private* call_private,
442 struct sdpmsg* msg)
444 struct sipe_backend_media *backend_media = SIPE_MEDIA_CALL->backend_private;
445 struct sipe_backend_stream *backend_stream = call_private->voice_stream;
446 GList *backend_candidates = NULL;
447 GList *backend_codecs = NULL;
448 GSList *i;
450 const gchar *username = sipe_utils_nameval_find(msg->attributes, "ice-ufrag");
451 const gchar *password = sipe_utils_nameval_find(msg->attributes, "ice-pwd");
454 for (i = msg->candidates; i; i = i->next) {
455 struct sdpcandidate *c = i->data;
456 struct sipe_backend_candidate *candidate;
457 candidate = sipe_backend_candidate_new(c->foundation,
458 c->component,
459 c->type,
460 c->protocol,
461 c->ip,
462 c->port);
463 sipe_backend_candidate_set_priority(candidate, c->priority);
465 if (username)
466 sipe_backend_candidate_set_username_and_pwd(candidate,
467 username,
468 password);
470 backend_candidates = g_list_append(backend_candidates, candidate);
473 sipe_backend_media_add_remote_candidates(backend_media,
474 backend_stream,
475 backend_candidates);
476 sipe_media_candidate_list_free(backend_candidates);
478 for (i = msg->codecs; i; i = i->next) {
479 struct sdpcodec *c = i->data;
480 struct sipe_backend_codec *codec;
481 GSList *j;
483 codec = sipe_backend_codec_new(c->id,
484 c->name,
485 c->clock_rate,
486 c->type);
488 for (j = c->attributes; j; j = j->next) {
489 struct sipnameval *attr = j->data;
491 sipe_backend_codec_add_optional_parameter(codec,
492 attr->name,
493 attr->value);
496 backend_codecs = g_list_append(backend_codecs, codec);
499 sipe_backend_set_remote_codecs(backend_media,
500 backend_stream,
501 backend_codecs);
502 sipe_media_codec_list_free(backend_codecs);
504 call_private->legacy_mode = msg->legacy;
505 call_private->encryption_compatible = encryption_levels_compatible(msg);
507 return TRUE;
510 static void candidates_prepared_cb(struct sipe_media_call *call)
512 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
514 if (sipe_backend_media_is_initiator(call_private->public.backend_private,
515 call_private->voice_stream)) {
516 sipe_invite_call(call_private->sipe_private,
517 process_invite_call_response);
518 return;
521 if (!apply_remote_message(call_private, call_private->smsg)) {
522 sipe_media_hangup(call_private->sipe_private);
523 return;
526 sdpmsg_free(call_private->smsg);
527 call_private->smsg = NULL;
529 if (!call_private->legacy_mode && call_private->encryption_compatible)
530 send_response_with_session_description(call_private,
531 183, "Session Progress");
534 static void media_connected_cb(SIPE_UNUSED_PARAMETER struct sipe_media_call_private *call_private)
538 static void call_accept_cb(struct sipe_media_call *call, gboolean local)
540 if (local) {
541 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
543 if (!call_private->encryption_compatible) {
544 handle_incompatible_encryption_level(call_private);
545 return;
548 send_response_with_session_description(call_private, 200, "OK");
552 static void call_reject_cb(struct sipe_media_call *call, gboolean local)
554 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
556 if (local) {
557 sip_transport_response(call_private->sipe_private, call_private->invitation, 603, "Decline", NULL);
559 call_private->sipe_private->media_call = NULL;
560 sipe_media_call_free(call_private);
563 static gboolean
564 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
565 struct transaction *trans);
567 static void call_hold_cb(struct sipe_media_call *call, gboolean local, gboolean state)
569 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
571 if (local && (call_private->public.local_on_hold != state)) {
572 call_private->public.local_on_hold = state;
573 sipe_invite_call(call_private->sipe_private, sipe_media_send_ack);
574 } else if (call_private->public.remote_on_hold != state) {
575 call_private->public.remote_on_hold = state;
576 send_response_with_session_description(call_private, 200, "OK");
580 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
582 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
584 if (local) {
585 sip_transport_bye(call_private->sipe_private, call_private->dialog);
587 call_private->sipe_private->media_call = NULL;
588 sipe_media_call_free(call_private);
591 static struct sipe_media_call_private *
592 sipe_media_call_new(struct sipe_core_private *sipe_private,
593 const gchar* with, gboolean initiator)
595 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
597 call_private->sipe_private = sipe_private;
598 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
599 SIPE_MEDIA_CALL,
600 with,
601 initiator);
603 call_private->legacy_mode = FALSE;
604 call_private->using_nice = TRUE;
605 call_private->encryption_compatible = TRUE;
607 call_private->public.candidates_prepared_cb = candidates_prepared_cb;
608 call_private->public.media_connected_cb = media_connected_cb;
609 call_private->public.call_accept_cb = call_accept_cb;
610 call_private->public.call_reject_cb = call_reject_cb;
611 call_private->public.call_hold_cb = call_hold_cb;
612 call_private->public.call_hangup_cb = call_hangup_cb;
614 call_private->public.local_on_hold = FALSE;
615 call_private->public.remote_on_hold = FALSE;
617 return call_private;
620 void sipe_media_hangup(struct sipe_core_private *sipe_private)
622 struct sipe_media_call_private *call_private = sipe_private->media_call;
623 if (call_private)
624 sipe_backend_media_hangup(call_private->public.backend_private,
625 FALSE);
628 void
629 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
630 const char *with)
632 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
633 struct sipe_media_call_private *call_private;
635 if (sipe_private->media_call)
636 return;
638 call_private = sipe_media_call_new(sipe_private, with, TRUE);
640 call_private->session = sipe_session_add_call(sipe_private, with);
641 call_private->dialog = sipe_dialog_add(call_private->session);
642 call_private->dialog->callid = gencallid();
643 call_private->dialog->with = g_strdup(call_private->session->with);
644 call_private->dialog->ourtag = gentag();
646 call_private->voice_stream = sipe_backend_media_add_stream(
647 call_private->public.backend_private,
648 with,
649 SIPE_MEDIA_AUDIO,
650 call_private->using_nice,
651 TRUE);
653 if (!call_private->voice_stream) {
654 sipe_backend_notify_error("Error occured",
655 "Error creating media stream");
656 sipe_media_call_free(call_private);
657 return;
660 sipe_private->media_call = call_private;
662 // Processing continues in candidates_prepared_cb
665 void
666 sipe_media_incoming_invite(struct sipe_core_private *sipe_private,
667 struct sipmsg *msg)
669 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
671 struct sipe_media_call_private *call_private = sipe_private->media_call;
672 struct sdpmsg *smsg;
674 if (call_private && !sipe_strequal(call_private->dialog->callid, callid)) {
675 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
676 return;
679 smsg = sdpmsg_parse_msg(msg->body);
680 if (!smsg) {
681 sipe_media_hangup(sipe_private);
682 return;
685 if (call_private) {
686 if (call_private->invitation)
687 sipmsg_free(call_private->invitation);
688 call_private->invitation = sipmsg_copy(msg);
690 apply_remote_message(call_private, smsg);
692 if (!call_private->encryption_compatible) {
693 handle_incompatible_encryption_level(call_private);
694 } else if (call_private->legacy_mode && !call_private->public.remote_on_hold) {
695 sipe_backend_media_hold(call_private->public.backend_private,
696 FALSE);
697 } else if (sipe_utils_nameval_find(smsg->attributes, "inactive")) {
698 sipe_backend_media_hold(call_private->public.backend_private, FALSE);
699 } else if (call_private->public.remote_on_hold) {
700 sipe_backend_media_unhold(call_private->public.backend_private, FALSE);
701 } else {
702 send_response_with_session_description(call_private,
703 200, "OK");
706 sdpmsg_free(smsg);
707 } else {
708 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
710 call_private = sipe_media_call_new(sipe_private, with, FALSE);
711 call_private->session = sipe_session_add_call(sipe_private, with);
712 call_private->dialog = sipe_media_dialog_init(call_private->session, msg);
713 call_private->invitation = sipmsg_copy(msg);
714 call_private->smsg = smsg;
715 call_private->voice_stream =
716 sipe_backend_media_add_stream(call_private->public.backend_private,
717 with,
718 SIPE_MEDIA_AUDIO,
719 !call_private->legacy_mode, FALSE);
721 sipe_private->media_call = call_private;
723 sip_transport_response(sipe_private, call_private->invitation, 180, "Ringing", NULL);
725 g_free(with);
727 // Processing continues in candidates_prepared_cb
731 static gboolean
732 sipe_media_send_ack(struct sipe_core_private *sipe_private,
733 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
734 struct transaction *trans)
736 struct sipe_media_call_private *call_private = sipe_private->media_call;
737 struct sip_dialog *dialog;
738 int trans_cseq;
739 int tmp_cseq;
741 if (!call_private || !call_private->dialog)
742 return FALSE;
744 dialog = call_private->dialog;
745 tmp_cseq = dialog->cseq;
747 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq);
748 dialog->cseq = trans_cseq - 1;
749 sip_transport_ack(sipe_private, dialog);
750 dialog->cseq = tmp_cseq;
752 dialog->outgoing_invite = NULL;
754 return TRUE;
757 static gboolean
758 process_invite_call_response(struct sipe_core_private *sipe_private,
759 struct sipmsg *msg,
760 struct transaction *trans)
762 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
763 const gchar *with;
764 struct sipe_media_call_private *call_private = sipe_private->media_call;
765 struct sipe_backend_media *backend_private;
766 struct sdpmsg *smsg;
768 if (!call_private ||
769 !sipe_strequal(sipe_media_get_callid(call_private), callid))
770 return FALSE;
772 backend_private = call_private->public.backend_private;
773 with = call_private->dialog->with;
775 call_private->dialog->outgoing_invite = NULL;
777 if (msg->response >= 400) {
778 // Call rejected by remote peer or an error occurred
779 gchar *title;
780 GString *desc = g_string_new("");
782 switch (msg->response) {
783 case 480:
784 title = _("User unavailable");
785 g_string_append_printf(desc, _("User %s is not available"), with);
786 break;
787 case 603:
788 case 605:
789 title = _("Call rejected");
790 g_string_append_printf(desc, _("User %s rejected call"), with);
791 break;
792 default:
793 title = _("Error occured");
794 g_string_append(desc, _("Unable to establish a call"));
795 break;
798 g_string_append_printf(desc, "\n%d %s", msg->response, msg->responsestr);
800 sipe_backend_notify_error(title, desc->str);
801 g_string_free(desc, TRUE);
803 sipe_backend_media_reject(backend_private, FALSE);
804 sipe_media_send_ack(sipe_private, msg, trans);
806 return TRUE;
809 sipe_dialog_parse(call_private->dialog, msg, TRUE);
810 smsg = sdpmsg_parse_msg(msg->body);
811 if (!smsg) {
812 sipe_media_hangup(sipe_private);
813 return FALSE;
816 apply_remote_message(call_private, smsg);
818 if (msg->response == 183) {
819 // Session in progress
820 const gchar *rseq = sipmsg_find_header(msg, "RSeq");
821 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
822 gchar *rack = g_strdup_printf("RAck: %s %s\r\n", rseq, cseq);
823 sip_transport_request(sipe_private,
824 "PRACK",
825 with,
826 with,
827 rack,
828 NULL,
829 call_private->dialog,
830 NULL);
831 g_free(rack);
832 } else {
833 sipe_media_send_ack(sipe_private, msg, trans);
835 if (call_private->legacy_mode && call_private->using_nice) {
836 // We created non-legacy stream as we don't know which version of
837 // client is on the other side until first SDP response is received.
838 // This client requires legacy mode, so we must remove current session
839 // (using ICE) and create new using raw UDP transport.
840 struct sipe_backend_stream *new_stream;
842 call_private->using_nice = FALSE;
844 new_stream = sipe_backend_media_add_stream(backend_private,
845 with,
846 SIPE_MEDIA_AUDIO,
847 FALSE,
848 TRUE);
850 sipe_backend_media_remove_stream(backend_private,
851 call_private->voice_stream);
852 call_private->voice_stream = new_stream;
854 apply_remote_message(call_private, smsg);
856 // New INVITE will be sent in candidates_prepared_cb
857 } else {
858 sipe_invite_call(sipe_private, sipe_media_send_ack);
862 sdpmsg_free(smsg);
864 return TRUE;
868 Local Variables:
869 mode: c
870 c-file-style: "bsd"
871 indent-tabs-mode: t
872 tab-width: 8
873 End: