Updated to release 1.11.1 (Bugfixes release only)
[siplcs.git] / src / core / sipe-media.c
blob56e25c1deb308ace39abd91fbc3169ea8760b241
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 gchar *with;
51 struct sipmsg *invitation;
52 gboolean legacy_mode;
53 gboolean using_nice;
54 gboolean encryption_compatible;
56 struct sdpmsg *smsg;
58 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
59 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
61 static void sipe_media_codec_list_free(GList *codecs)
63 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
64 sipe_backend_codec_free(codecs->data);
67 static void sipe_media_candidate_list_free(GList *candidates)
69 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
70 sipe_backend_candidate_free(candidates->data);
73 static void
74 sipe_media_call_free(struct sipe_media_call_private *call_private)
76 if (call_private) {
77 struct sip_session *session;
78 sipe_backend_media_free(call_private->public.backend_private);
80 session = sipe_session_find_call(call_private->sipe_private,
81 call_private->with);
82 if (session)
83 sipe_session_remove(call_private->sipe_private, session);
85 if (call_private->invitation)
86 sipmsg_free(call_private->invitation);
88 sdpmsg_free(call_private->smsg);
89 g_free(call_private->with);
90 g_free(call_private);
94 static GSList *
95 backend_candidates_to_sdpcandidate(GList *candidates)
97 GSList *result = NULL;
98 GList *i;
100 for (i = candidates; i; i = i->next) {
101 struct sipe_backend_candidate *candidate = i->data;
102 struct sdpcandidate *c = g_new(struct sdpcandidate, 1);
104 c->foundation = sipe_backend_candidate_get_foundation(candidate);
105 c->component = sipe_backend_candidate_get_component_type(candidate);
106 c->type = sipe_backend_candidate_get_type(candidate);
107 c->protocol = sipe_backend_candidate_get_protocol(candidate);
108 c->ip = sipe_backend_candidate_get_ip(candidate);
109 c->port = sipe_backend_candidate_get_port(candidate);
110 c->base_ip = sipe_backend_candidate_get_base_ip(candidate);
111 c->base_port = sipe_backend_candidate_get_base_port(candidate);
112 c->priority = sipe_backend_candidate_get_priority(candidate);
114 result = g_slist_append(result, c);
117 return result;
120 static struct sdpmedia *
121 backend_stream_to_sdpmedia(struct sipe_media_call_private *call_private,
122 struct sipe_backend_stream *backend_stream)
124 struct sipe_backend_media *backend_media = call_private->public.backend_private;
125 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
126 GList *codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
127 backend_stream);
128 guint rtcp_port;
129 SipeMediaType type;
130 GSList *attributes = NULL;
131 GList *candidates;
132 GList *i;
133 GSList *j;
135 media->name = g_strdup(sipe_backend_stream_get_id(backend_stream));
137 if (sipe_strequal(media->name, "audio"))
138 type = SIPE_MEDIA_AUDIO;
139 else if (sipe_strequal(media->name, "video"))
140 type = SIPE_MEDIA_VIDEO;
141 else {
142 // TODO: incompatible media, should not happen here
145 // Process codecs
146 for (i = codecs; i; i = i->next) {
147 struct sipe_backend_codec *codec = i->data;
148 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
149 GList *params;
151 c->id = sipe_backend_codec_get_id(codec);
152 c->name = sipe_backend_codec_get_name(codec);
153 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
154 c->type = type;
156 params = sipe_backend_codec_get_optional_parameters(codec);
157 for (; params; params = params->next) {
158 struct sipnameval *param = params->data;
159 struct sipnameval *copy = g_new0(struct sipnameval, 1);
161 copy->name = g_strdup(param->name);
162 copy->value = g_strdup(param->value);
164 c->parameters = g_slist_append(c->parameters, copy);
167 media->codecs = g_slist_append(media->codecs, c);
170 sipe_media_codec_list_free(codecs);
172 // Process local candidates
173 // If we have established candidate pairs, send them in SDP response.
174 // Otherwise send all available local candidates.
175 candidates = sipe_backend_media_get_active_local_candidates(backend_media,
176 backend_stream);
177 if (!candidates)
178 candidates = sipe_backend_get_local_candidates(backend_media,
179 backend_stream);
181 media->candidates = backend_candidates_to_sdpcandidate(candidates);
183 // Process stream attributes
184 if (!call_private->legacy_mode) {
185 struct sipe_backend_candidate *candidate = candidates->data;
187 gchar *username = sipe_backend_candidate_get_username(candidate);
188 gchar *password = sipe_backend_candidate_get_password(candidate);
190 attributes = sipe_utils_nameval_add(attributes,
191 "ice-ufrag", username);
192 attributes = sipe_utils_nameval_add(attributes,
193 "ice-pwd", password);
195 g_free(username);
196 g_free(password);
199 sipe_media_candidate_list_free(candidates);
201 for (j = media->candidates; j; j = j->next) {
202 struct sdpcandidate *candidate = j->data;
204 if (candidate->type == SIPE_CANDIDATE_TYPE_HOST) {
205 if (candidate->component == SIPE_COMPONENT_RTP)
206 media->port = candidate->port;
207 else if (candidate->component == SIPE_COMPONENT_RTCP)
208 rtcp_port = candidate->port;
212 if (sipe_backend_stream_is_held(backend_stream))
213 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
215 if (rtcp_port) {
216 gchar *tmp = g_strdup_printf("%u", rtcp_port);
217 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
218 g_free(tmp);
221 attributes = sipe_utils_nameval_add(attributes, "encryption", "rejected");
223 media->attributes = attributes;
225 // Process remote candidates
226 candidates = sipe_backend_media_get_active_remote_candidates(backend_media,
227 backend_stream);
228 media->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
229 sipe_media_candidate_list_free(candidates);
231 return media;
234 static struct sdpmsg *
235 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
237 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
238 GSList *streams = sipe_backend_media_get_streams(call_private->public.backend_private);
240 for (; streams; streams = streams->next) {
241 struct sdpmedia *media;
242 media = backend_stream_to_sdpmedia(call_private, streams->data);
243 msg->media = g_slist_append(msg->media, media);
246 msg->legacy = call_private->legacy_mode;
247 msg->ip = g_strdup(sipe_utils_get_suitable_local_ip(-1));
249 return msg;
252 static void
253 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
255 gchar *hdr;
256 gchar *contact;
257 gchar *body;
258 struct sipe_media_call_private *call_private = sipe_private->media_call;
259 struct sip_session *session;
260 struct sip_dialog *dialog;
261 struct sdpmsg *msg;
263 session = sipe_session_find_call(sipe_private, call_private->with);
264 dialog = session->dialogs->data;
266 contact = get_contact(sipe_private);
267 hdr = g_strdup_printf(
268 "Supported: ms-early-media\r\n"
269 "Supported: 100rel\r\n"
270 "ms-keep-alive: UAC;hop-hop=yes\r\n"
271 "Contact: %s\r\n"
272 "Content-Type: application/sdp\r\n",
273 contact);
274 g_free(contact);
276 msg = sipe_media_to_sdpmsg(call_private);
277 body = sdpmsg_to_string(msg);
278 sdpmsg_free(msg);
280 dialog->outgoing_invite = sip_transport_invite(sipe_private,
281 hdr,
282 body,
283 dialog,
284 tc);
286 g_free(body);
287 g_free(hdr);
290 static struct sip_dialog *
291 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
293 gchar *newTag = gentag();
294 const gchar *oldHeader;
295 gchar *newHeader;
296 struct sip_dialog *dialog;
298 oldHeader = sipmsg_find_header(msg, "To");
299 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
300 sipmsg_remove_header_now(msg, "To");
301 sipmsg_add_header_now(msg, "To", newHeader);
302 g_free(newHeader);
304 dialog = sipe_dialog_add(session);
305 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
306 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
307 sipe_dialog_parse(dialog, msg, FALSE);
309 return dialog;
312 static void
313 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
315 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
316 gchar *body = sdpmsg_to_string(msg);
317 sdpmsg_free(msg);
318 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
319 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
320 g_free(body);
323 static gboolean
324 encryption_levels_compatible(struct sdpmsg *msg)
326 GSList *i;
328 for (i = msg->media; i; i = i->next) {
329 const gchar *enc_level;
330 struct sdpmedia *m = i->data;
332 enc_level = sipe_utils_nameval_find(m->attributes, "encryption");
334 // Decline call if peer requires encryption as we don't support it yet.
335 if (sipe_strequal(enc_level, "required"))
336 return FALSE;
339 return TRUE;
342 static void
343 handle_incompatible_encryption_level(struct sipe_media_call_private *call_private)
345 sipmsg_add_header(call_private->invitation, "Warning",
346 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
347 sip_transport_response(call_private->sipe_private,
348 call_private->invitation,
349 488, "Encryption Levels not compatible",
350 NULL);
351 sipe_backend_media_reject(call_private->public.backend_private, FALSE);
352 sipe_backend_notify_error(_("Unable to establish a call"),
353 _("Encryption settings of peer are incompatible with ours."));
356 static gboolean
357 process_invite_call_response(struct sipe_core_private *sipe_private,
358 struct sipmsg *msg,
359 struct transaction *trans);
361 static gboolean
362 update_remote_media(struct sipe_media_call_private* call_private,
363 struct sdpmedia *media)
365 struct sipe_backend_media *backend_media = SIPE_MEDIA_CALL->backend_private;
366 struct sipe_backend_stream *backend_stream;
367 GList *backend_candidates = NULL;
368 GList *backend_codecs = NULL;
369 const gchar *username = sipe_utils_nameval_find(media->attributes, "ice-ufrag");
370 const gchar *password = sipe_utils_nameval_find(media->attributes, "ice-pwd");
371 GSList *i;
372 gboolean result = TRUE;
374 backend_stream = sipe_backend_media_get_stream_by_id(backend_media,
375 media->name);
376 if (media->port == 0) {
377 if (backend_stream)
378 sipe_backend_media_remove_stream(backend_media, backend_stream);
379 return TRUE;
382 if (!backend_stream)
383 return FALSE;
386 for (i = media->candidates; i; i = i->next) {
387 struct sdpcandidate *c = i->data;
388 struct sipe_backend_candidate *candidate;
389 candidate = sipe_backend_candidate_new(c->foundation,
390 c->component,
391 c->type,
392 c->protocol,
393 c->ip,
394 c->port);
395 sipe_backend_candidate_set_priority(candidate, c->priority);
397 if (username)
398 sipe_backend_candidate_set_username_and_pwd(candidate,
399 username,
400 password);
402 backend_candidates = g_list_append(backend_candidates, candidate);
405 sipe_backend_media_add_remote_candidates(backend_media,
406 backend_stream,
407 backend_candidates);
408 sipe_media_candidate_list_free(backend_candidates);
410 for (i = media->codecs; i; i = i->next) {
411 struct sdpcodec *c = i->data;
412 struct sipe_backend_codec *codec;
413 GSList *j;
415 codec = sipe_backend_codec_new(c->id,
416 c->name,
417 c->type,
418 c->clock_rate);
420 for (j = c->parameters; j; j = j->next) {
421 struct sipnameval *attr = j->data;
423 sipe_backend_codec_add_optional_parameter(codec,
424 attr->name,
425 attr->value);
428 backend_codecs = g_list_append(backend_codecs, codec);
431 result = sipe_backend_set_remote_codecs(backend_media,
432 backend_stream,
433 backend_codecs);
434 sipe_media_codec_list_free(backend_codecs);
436 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
437 sipe_backend_stream_hold(backend_media, backend_stream, FALSE);
438 } else if (sipe_backend_stream_is_held(backend_stream)) {
439 sipe_backend_stream_unhold(backend_media, backend_stream, FALSE);
442 return result;
445 static gboolean
446 apply_remote_message(struct sipe_media_call_private* call_private,
447 struct sdpmsg* msg)
449 GSList *i;
450 for (i = msg->media; i; i = i->next) {
451 if (!update_remote_media(call_private, i->data))
452 return FALSE;
455 call_private->legacy_mode = msg->legacy;
456 call_private->encryption_compatible = encryption_levels_compatible(msg);
458 return TRUE;
461 void do_apply_remote_message(struct sipe_media_call_private *call_private,
462 struct sdpmsg *smsg)
464 if (!apply_remote_message(call_private, smsg)) {
465 sip_transport_response(call_private->sipe_private,
466 call_private->invitation,
467 487, "Request Terminated", NULL);
468 sipe_media_hangup(call_private->sipe_private);
469 return;
472 sdpmsg_free(call_private->smsg);
473 call_private->smsg = NULL;
475 if (sipe_backend_media_accepted(call_private->public.backend_private)) {
476 send_response_with_session_description(call_private,
477 200, "OK");
478 return;
481 if (!call_private->legacy_mode && call_private->encryption_compatible)
482 send_response_with_session_description(call_private,
483 183, "Session Progress");
486 static void candidates_prepared_cb(struct sipe_media_call *call,
487 struct sipe_backend_stream *stream)
489 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
491 if (sipe_backend_media_is_initiator(call_private->public.backend_private,
492 stream)) {
493 sipe_invite_call(call_private->sipe_private,
494 process_invite_call_response);
495 return;
496 } else {
497 struct sdpmsg *smsg = call_private->smsg;
498 call_private->smsg = NULL;
500 do_apply_remote_message(call_private, smsg);
501 sdpmsg_free(call_private->smsg);
505 static void media_connected_cb(SIPE_UNUSED_PARAMETER struct sipe_media_call_private *call_private)
509 static void call_accept_cb(struct sipe_media_call *call, gboolean local)
511 if (local) {
512 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
514 if (!call_private->encryption_compatible) {
515 handle_incompatible_encryption_level(call_private);
516 return;
519 send_response_with_session_description(call_private, 200, "OK");
523 static void call_reject_cb(struct sipe_media_call *call, gboolean local)
525 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
527 if (local) {
528 sip_transport_response(call_private->sipe_private, call_private->invitation, 603, "Decline", NULL);
530 call_private->sipe_private->media_call = NULL;
531 sipe_media_call_free(call_private);
534 static gboolean
535 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
536 struct transaction *trans);
538 static void call_hold_cb(struct sipe_media_call *call,
539 gboolean local,
540 SIPE_UNUSED_PARAMETER gboolean state)
542 if (local)
543 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
544 sipe_media_send_ack);
547 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
549 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
551 if (local) {
552 struct sip_session *session;
553 session = sipe_session_find_call(call_private->sipe_private,
554 call_private->with);
556 if (session) {
557 sipe_session_close(call_private->sipe_private, session);
560 call_private->sipe_private->media_call = NULL;
561 sipe_media_call_free(call_private);
564 static struct sipe_media_call_private *
565 sipe_media_call_new(struct sipe_core_private *sipe_private,
566 const gchar* with, gboolean initiator)
568 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
570 call_private->sipe_private = sipe_private;
571 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
572 SIPE_MEDIA_CALL,
573 with,
574 initiator);
576 call_private->legacy_mode = FALSE;
577 call_private->using_nice = TRUE;
578 call_private->encryption_compatible = TRUE;
580 call_private->public.candidates_prepared_cb = candidates_prepared_cb;
581 call_private->public.media_connected_cb = media_connected_cb;
582 call_private->public.call_accept_cb = call_accept_cb;
583 call_private->public.call_reject_cb = call_reject_cb;
584 call_private->public.call_hold_cb = call_hold_cb;
585 call_private->public.call_hangup_cb = call_hangup_cb;
587 return call_private;
590 void sipe_media_hangup(struct sipe_core_private *sipe_private)
592 struct sipe_media_call_private *call_private = sipe_private->media_call;
593 if (call_private)
594 sipe_backend_media_hangup(call_private->public.backend_private,
595 FALSE);
598 void
599 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
600 const char *with,
601 gboolean with_video)
603 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
604 struct sipe_media_call_private *call_private;
605 struct sipe_backend_media *backend_media;
606 struct sip_session *session;
607 struct sip_dialog *dialog;
609 if (sipe_private->media_call)
610 return;
612 call_private = sipe_media_call_new(sipe_private, with, TRUE);
614 session = sipe_session_add_call(sipe_private, with);
615 dialog = sipe_dialog_add(session);
616 dialog->callid = gencallid();
617 dialog->with = g_strdup(session->with);
618 dialog->ourtag = gentag();
620 call_private->with = g_strdup(session->with);
622 backend_media = call_private->public.backend_private;
624 if (!sipe_backend_media_add_stream(backend_media,
625 "audio", with, SIPE_MEDIA_AUDIO,
626 call_private->using_nice, TRUE)) {
627 sipe_backend_notify_error(_("Error occured"),
628 _("Error creating audio stream"));
629 sipe_media_call_free(call_private);
630 return;
633 if ( with_video
634 && !sipe_backend_media_add_stream(backend_media,
635 "video", with, SIPE_MEDIA_VIDEO,
636 call_private->using_nice, TRUE)) {
637 sipe_backend_notify_error(_("Error occured"),
638 _("Error creating video stream"));
639 sipe_media_call_free(call_private);
640 return;
643 sipe_private->media_call = call_private;
645 // Processing continues in candidates_prepared_cb
648 void
649 process_incoming_invite_call(struct sipe_core_private *sipe_private,
650 struct sipmsg *msg)
652 struct sipe_media_call_private *call_private = sipe_private->media_call;
653 struct sipe_backend_media *backend_media;
654 struct sdpmsg *smsg;
655 gboolean has_new_media = FALSE;
656 GSList *i;
658 if (call_private && !is_media_session_msg(call_private, msg)) {
659 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
660 return;
663 smsg = sdpmsg_parse_msg(msg->body);
664 if (!smsg) {
665 sip_transport_response(sipe_private, msg,
666 488, "Not Acceptable Here", NULL);
667 sipe_media_hangup(sipe_private);
668 return;
671 if (!call_private) {
672 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
673 struct sip_session *session;
674 struct sip_dialog *dialog;
676 call_private = sipe_media_call_new(sipe_private, with, FALSE);
677 session = sipe_session_add_call(sipe_private, with);
678 dialog = sipe_media_dialog_init(session, msg);
680 call_private->with = g_strdup(session->with);
681 sipe_private->media_call = call_private;
682 g_free(with);
685 backend_media = call_private->public.backend_private;
687 if (call_private->invitation)
688 sipmsg_free(call_private->invitation);
689 call_private->invitation = sipmsg_copy(msg);
691 // Create any new media streams
692 for (i = smsg->media; i; i = i->next) {
693 struct sdpmedia *media = i->data;
694 gchar *id = media->name;
695 SipeMediaType type;
697 if ( media->port != 0
698 && !sipe_backend_media_get_stream_by_id(backend_media, id)) {
699 gchar *with;
701 if (sipe_strequal(id, "audio"))
702 type = SIPE_MEDIA_AUDIO;
703 else if (sipe_strequal(id, "video"))
704 type = SIPE_MEDIA_VIDEO;
705 else
706 continue;
708 with = parse_from(sipmsg_find_header(msg, "From"));
709 sipe_backend_media_add_stream(backend_media, id, with,
710 type,
711 !call_private->legacy_mode,
712 FALSE);
713 has_new_media = TRUE;
714 g_free(with);
718 if (has_new_media) {
719 call_private->smsg = smsg;
720 sip_transport_response(sipe_private, call_private->invitation,
721 180, "Ringing", NULL);
722 // Processing continues in candidates_prepared_cb
723 } else {
724 do_apply_remote_message(call_private, smsg);
725 sdpmsg_free(smsg);
729 void process_incoming_cancel_call(struct sipe_core_private *sipe_private,
730 struct sipmsg *msg)
732 struct sipe_media_call_private *call_private = sipe_private->media_call;
734 // We respond to the CANCEL request with 200 OK response and
735 // with 487 Request Terminated to the remote INVITE in progress.
736 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
738 if (call_private->invitation) {
739 sip_transport_response(sipe_private, call_private->invitation,
740 487, "Request Terminated", NULL);
743 sipe_media_hangup(sipe_private);
746 static gboolean
747 sipe_media_send_ack(struct sipe_core_private *sipe_private,
748 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
749 struct transaction *trans)
751 struct sipe_media_call_private *call_private = sipe_private->media_call;
752 struct sip_session *session;
753 struct sip_dialog *dialog;
754 int trans_cseq;
755 int tmp_cseq;
757 session = sipe_session_find_call(sipe_private, call_private->with);
758 dialog = session->dialogs->data;
759 if (!dialog)
760 return FALSE;
762 tmp_cseq = dialog->cseq;
764 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq);
765 dialog->cseq = trans_cseq - 1;
766 sip_transport_ack(sipe_private, dialog);
767 dialog->cseq = tmp_cseq;
769 dialog->outgoing_invite = NULL;
771 return TRUE;
774 static gboolean
775 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
776 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
777 struct transaction *trans)
779 sipe_media_send_ack(sipe_private, msg, trans);
780 sipe_backend_media_accept(sipe_private->media_call->public.backend_private,
781 FALSE);
783 return TRUE;
786 static gboolean
787 process_invite_call_response(struct sipe_core_private *sipe_private,
788 struct sipmsg *msg,
789 struct transaction *trans)
791 const gchar *with;
792 struct sipe_media_call_private *call_private = sipe_private->media_call;
793 struct sipe_backend_media *backend_private;
794 struct sip_session *session;
795 struct sip_dialog *dialog;
796 struct sdpmsg *smsg;
798 if (!is_media_session_msg(call_private, msg))
799 return FALSE;
801 session = sipe_session_find_call(sipe_private, call_private->with);
802 dialog = session->dialogs->data;
804 backend_private = call_private->public.backend_private;
805 with = dialog->with;
807 dialog->outgoing_invite = NULL;
809 if (msg->response >= 400) {
810 // Call rejected by remote peer or an error occurred
811 gchar *title;
812 GString *desc = g_string_new("");
813 gboolean append_responsestr = FALSE;
815 switch (msg->response) {
816 case 480: {
817 const gchar *warn = sipmsg_find_header(msg, "Warning");
818 title = _("User unavailable");
820 if (warn && g_str_has_prefix(warn, "391 lcs.microsoft.com")) {
821 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
822 } else
823 g_string_append_printf(desc, _("User %s is not available"), with);
824 break;
826 case 603:
827 case 605:
828 title = _("Call rejected");
829 g_string_append_printf(desc, _("User %s rejected call"), with);
830 break;
831 default:
832 title = _("Error occured");
833 g_string_append(desc, _("Unable to establish a call"));
834 append_responsestr = TRUE;
835 break;
838 if (append_responsestr)
839 g_string_append_printf(desc, "\n%d %s",
840 msg->response, msg->responsestr);
842 sipe_backend_notify_error(title, desc->str);
843 g_string_free(desc, TRUE);
845 sipe_media_send_ack(sipe_private, msg, trans);
846 sipe_media_hangup(sipe_private);
848 return TRUE;
851 sipe_dialog_parse(dialog, msg, TRUE);
852 smsg = sdpmsg_parse_msg(msg->body);
853 if (!smsg) {
854 sip_transport_response(sipe_private, msg,
855 488, "Not Acceptable Here", NULL);
856 sipe_media_hangup(sipe_private);
857 return FALSE;
860 if (!apply_remote_message(call_private, smsg)) {
861 sip_transport_response(sipe_private, msg,
862 487, "Request Terminated", NULL);
863 sipe_media_hangup(sipe_private);
864 } else if (msg->response == 183) {
865 // Session in progress
866 const gchar *rseq = sipmsg_find_header(msg, "RSeq");
867 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
868 gchar *rack = g_strdup_printf("RAck: %s %s\r\n", rseq, cseq);
869 sip_transport_request(sipe_private,
870 "PRACK",
871 with,
872 with,
873 rack,
874 NULL,
875 dialog,
876 NULL);
877 g_free(rack);
878 } else {
879 sipe_media_send_ack(sipe_private, msg, trans);
881 if (call_private->legacy_mode && call_private->using_nice) {
882 // TODO: legacy
883 /* // We created non-legacy stream as we don't know which version of
884 // client is on the other side until first SDP response is received.
885 // This client requires legacy mode, so we must remove current session
886 // (using ICE) and create new using raw UDP transport.
887 struct sipe_backend_stream *new_stream;
889 call_private->using_nice = FALSE;
891 new_stream = sipe_backend_media_add_stream(backend_private,
892 with,
893 SIPE_MEDIA_AUDIO,
894 FALSE,
895 TRUE);
897 sipe_backend_media_remove_stream(backend_private,
898 call_private->voice_stream);
899 call_private->voice_stream = new_stream;
901 apply_remote_message(call_private, smsg);
903 // New INVITE will be sent in candidates_prepared_cb */
904 } else {
905 sipe_invite_call(sipe_private, sipe_media_send_final_ack);
909 sdpmsg_free(smsg);
911 return TRUE;
914 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
915 struct sipmsg *msg)
917 if (call_private) {
918 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
919 struct sip_session *session;
921 session = sipe_session_find_call(call_private->sipe_private,
922 call_private->with);
923 if (session) {
924 struct sip_dialog *dialog = session->dialogs->data;
925 return sipe_strequal(dialog->callid, callid);
928 return FALSE;
931 void sipe_media_handle_going_offline(struct sipe_media_call_private *call_private)
933 struct sipe_backend_media *backend_private;
935 backend_private = call_private->public.backend_private;
937 if ( !sipe_backend_media_is_initiator(backend_private, NULL)
938 && !sipe_backend_media_accepted(backend_private)) {
939 sip_transport_response(call_private->sipe_private,
940 call_private->invitation,
941 480, "Temporarily Unavailable", NULL);
942 } else {
943 struct sip_session *session;
945 session = sipe_session_find_call(call_private->sipe_private,
946 call_private->with);
947 if (session)
948 sipe_session_close(call_private->sipe_private, session);
951 sipe_media_hangup(call_private->sipe_private);
955 Local Variables:
956 mode: c
957 c-file-style: "bsd"
958 indent-tabs-mode: t
959 tab-width: 8
960 End: