cce76c50bdd47655e7fdcb83a299ca86f917766f
[siplcs.git] / src / core / sipe-media.c
blobcce76c50bdd47655e7fdcb83a299ca86f917766f
1 /**
2 * @file sipe-media.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2014 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include <glib.h>
34 #include "sipe-common.h"
35 #include "sipmsg.h"
36 #include "sip-transport.h"
37 #include "sipe-backend.h"
38 #include "sdpmsg.h"
39 #include "sipe-chat.h"
40 #include "sipe-core.h"
41 #include "sipe-core-private.h"
42 #include "sipe-dialog.h"
43 #include "sipe-media.h"
44 #include "sipe-ocs2007.h"
45 #include "sipe-session.h"
46 #include "sipe-utils.h"
47 #include "sipe-nls.h"
48 #include "sipe-schedule.h"
49 #include "sipe-xml.h"
51 struct sipe_media_call_private {
52 struct sipe_media_call public;
54 /* private part starts here */
55 struct sipe_core_private *sipe_private;
56 gchar *with;
58 struct sipmsg *invitation;
59 SipeIceVersion ice_version;
60 gboolean encryption_compatible;
62 struct sdpmsg *smsg;
63 GSList *failed_media;
65 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
66 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
68 static void sipe_media_codec_list_free(GList *codecs)
70 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
71 sipe_backend_codec_free(codecs->data);
74 static void sipe_media_candidate_list_free(GList *candidates)
76 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
77 sipe_backend_candidate_free(candidates->data);
80 static void
81 sipe_media_call_free(struct sipe_media_call_private *call_private)
83 if (call_private) {
84 struct sip_session *session;
85 sipe_backend_media_free(call_private->public.backend_private);
87 session = sipe_session_find_call(call_private->sipe_private,
88 call_private->with);
89 if (session)
90 sipe_session_remove(call_private->sipe_private, session);
92 if (call_private->invitation)
93 sipmsg_free(call_private->invitation);
95 sdpmsg_free(call_private->smsg);
96 sipe_utils_slist_free_full(call_private->failed_media,
97 (GDestroyNotify)sdpmedia_free);
98 g_free(call_private->with);
99 g_free(call_private);
103 static gint
104 candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
106 int cmp = sipe_strcompare(c1->foundation, c2->foundation);
107 if (cmp == 0) {
108 cmp = sipe_strcompare(c1->username, c2->username);
109 if (cmp == 0)
110 cmp = c1->component - c2->component;
113 return cmp;
116 static GSList *
117 backend_candidates_to_sdpcandidate(GList *candidates)
119 GSList *result = NULL;
120 GList *i;
122 for (i = candidates; i; i = i->next) {
123 struct sipe_backend_candidate *candidate = i->data;
124 struct sdpcandidate *c = g_new(struct sdpcandidate, 1);
126 c->foundation = sipe_backend_candidate_get_foundation(candidate);
127 c->component = sipe_backend_candidate_get_component_type(candidate);
128 c->type = sipe_backend_candidate_get_type(candidate);
129 c->protocol = sipe_backend_candidate_get_protocol(candidate);
130 c->ip = sipe_backend_candidate_get_ip(candidate);
131 c->port = sipe_backend_candidate_get_port(candidate);
132 c->base_ip = sipe_backend_candidate_get_base_ip(candidate);
133 c->base_port = sipe_backend_candidate_get_base_port(candidate);
134 c->priority = sipe_backend_candidate_get_priority(candidate);
135 c->username = sipe_backend_candidate_get_username(candidate);
136 c->password = sipe_backend_candidate_get_password(candidate);
138 result = g_slist_insert_sorted(result, c,
139 (GCompareFunc)candidate_sort_cb);
142 return result;
145 static void
146 get_stream_ip_and_ports(GSList *candidates,
147 gchar **ip, guint *rtp_port, guint *rtcp_port,
148 SipeCandidateType type)
150 *ip = 0;
151 *rtp_port = 0;
152 *rtcp_port = 0;
154 for (; candidates; candidates = candidates->next) {
155 struct sdpcandidate *candidate = candidates->data;
157 if (type == SIPE_CANDIDATE_TYPE_ANY || candidate->type == type) {
158 if (!*ip) {
159 *ip = g_strdup(candidate->ip);
160 } else if (!sipe_strequal(*ip, candidate->ip)) {
161 continue;
164 if (candidate->component == SIPE_COMPONENT_RTP) {
165 *rtp_port = candidate->port;
166 } else if (candidate->component == SIPE_COMPONENT_RTCP)
167 *rtcp_port = candidate->port;
170 if (*rtp_port != 0 && *rtcp_port != 0)
171 return;
175 static struct sdpmedia *
176 backend_stream_to_sdpmedia(struct sipe_backend_media *backend_media,
177 struct sipe_backend_stream *backend_stream)
179 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
180 GList *codecs = sipe_backend_get_local_codecs(backend_media,
181 backend_stream);
182 guint rtcp_port = 0;
183 SipeMediaType type;
184 GSList *attributes = NULL;
185 GList *candidates;
186 GList *i;
188 media->name = g_strdup(sipe_backend_stream_get_id(backend_stream));
190 if (sipe_strequal(media->name, "audio"))
191 type = SIPE_MEDIA_AUDIO;
192 else if (sipe_strequal(media->name, "video"))
193 type = SIPE_MEDIA_VIDEO;
194 else {
195 // TODO: incompatible media, should not happen here
196 g_free(media->name);
197 g_free(media);
198 sipe_media_codec_list_free(codecs);
199 return(NULL);
202 // Process codecs
203 for (i = codecs; i; i = i->next) {
204 struct sipe_backend_codec *codec = i->data;
205 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
206 GList *params;
208 c->id = sipe_backend_codec_get_id(codec);
209 c->name = sipe_backend_codec_get_name(codec);
210 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
211 c->type = type;
213 params = sipe_backend_codec_get_optional_parameters(codec);
214 for (; params; params = params->next) {
215 struct sipnameval *param = params->data;
216 struct sipnameval *copy = g_new0(struct sipnameval, 1);
218 copy->name = g_strdup(param->name);
219 copy->value = g_strdup(param->value);
221 c->parameters = g_slist_append(c->parameters, copy);
224 media->codecs = g_slist_append(media->codecs, c);
227 sipe_media_codec_list_free(codecs);
229 // Process local candidates
230 // If we have established candidate pairs, send them in SDP response.
231 // Otherwise send all available local candidates.
232 candidates = sipe_backend_media_get_active_local_candidates(backend_media,
233 backend_stream);
234 if (!candidates)
235 candidates = sipe_backend_get_local_candidates(backend_media,
236 backend_stream);
238 media->candidates = backend_candidates_to_sdpcandidate(candidates);
240 sipe_media_candidate_list_free(candidates);
242 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
243 &rtcp_port, SIPE_CANDIDATE_TYPE_HOST);
244 // No usable HOST candidates, use any candidate
245 if (media->ip == NULL && media->candidates) {
246 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
247 &rtcp_port, SIPE_CANDIDATE_TYPE_ANY);
250 if (sipe_backend_stream_is_held(backend_stream))
251 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
253 if (rtcp_port) {
254 gchar *tmp = g_strdup_printf("%u", rtcp_port);
255 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
256 g_free(tmp);
259 attributes = sipe_utils_nameval_add(attributes, "encryption", "rejected");
261 media->attributes = attributes;
263 // Process remote candidates
264 candidates = sipe_backend_media_get_active_remote_candidates(backend_media,
265 backend_stream);
266 media->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
267 sipe_media_candidate_list_free(candidates);
269 return media;
272 static struct sdpmsg *
273 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
275 struct sipe_backend_media *backend_media = call_private->public.backend_private;
276 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
277 GSList *streams = sipe_backend_media_get_streams(backend_media);
279 for (; streams; streams = streams->next) {
280 struct sdpmedia *media = backend_stream_to_sdpmedia(backend_media, streams->data);
281 if (media) {
282 msg->media = g_slist_append(msg->media, media);
284 if (msg->ip == NULL)
285 msg->ip = g_strdup(media->ip);
289 msg->media = g_slist_concat(msg->media, call_private->failed_media);
290 call_private->failed_media = NULL;
292 msg->ice_version = call_private->ice_version;
294 return msg;
297 static void
298 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
300 gchar *hdr;
301 gchar *contact;
302 gchar *p_preferred_identity = NULL;
303 gchar *body;
304 struct sipe_media_call_private *call_private = sipe_private->media_call;
305 struct sip_session *session;
306 struct sip_dialog *dialog;
307 struct sdpmsg *msg;
308 gboolean add_2007_fallback = FALSE;
310 session = sipe_session_find_call(sipe_private, call_private->with);
311 dialog = session->dialogs->data;
312 add_2007_fallback = dialog->cseq == 0 &&
313 call_private->ice_version == SIPE_ICE_RFC_5245 &&
314 !sipe_strequal(call_private->with, sipe_private->test_call_bot_uri);
316 contact = get_contact(sipe_private);
318 if (sipe_private->uc_line_uri) {
319 gchar *self = sip_uri_self(sipe_private);
320 p_preferred_identity = g_strdup_printf(
321 "P-Preferred-Identity: <%s>, <%s>\r\n",
322 self, sipe_private->uc_line_uri);
323 g_free(self);
326 hdr = g_strdup_printf(
327 "ms-keep-alive: UAC;hop-hop=yes\r\n"
328 "Contact: %s\r\n"
329 "%s"
330 "Content-Type: %s\r\n",
331 contact,
332 p_preferred_identity ? p_preferred_identity : "",
333 add_2007_fallback ?
334 "multipart/alternative;boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\""
335 : "application/sdp");
336 g_free(contact);
337 g_free(p_preferred_identity);
339 msg = sipe_media_to_sdpmsg(call_private);
340 body = sdpmsg_to_string(msg);
342 if (add_2007_fallback) {
343 gchar *tmp;
344 tmp = g_strdup_printf(
345 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
346 "Content-Type: application/sdp\r\n"
347 "Content-Transfer-Encoding: 7bit\r\n"
348 "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n"
349 "\r\n"
350 "o=- 0 0 IN IP4 %s\r\n"
351 "s=session\r\n"
352 "c=IN IP4 %s\r\n"
353 "m=audio 0 RTP/AVP\r\n"
354 "\r\n"
355 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
356 "Content-Type: application/sdp\r\n"
357 "Content-Transfer-Encoding: 7bit\r\n"
358 "Content-Disposition: session; handling=optional\r\n"
359 "\r\n"
360 "%s"
361 "\r\n"
362 "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n",
363 msg->ip, msg->ip, body);
364 g_free(body);
365 body = tmp;
368 sdpmsg_free(msg);
370 dialog->outgoing_invite = sip_transport_invite(sipe_private,
371 hdr,
372 body,
373 dialog,
374 tc);
376 g_free(body);
377 g_free(hdr);
380 static struct sip_dialog *
381 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
383 gchar *newTag = gentag();
384 const gchar *oldHeader;
385 gchar *newHeader;
386 struct sip_dialog *dialog;
388 oldHeader = sipmsg_find_header(msg, "To");
389 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
390 sipmsg_remove_header_now(msg, "To");
391 sipmsg_add_header_now(msg, "To", newHeader);
392 g_free(newHeader);
394 dialog = sipe_dialog_add(session);
395 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
396 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
397 sipe_dialog_parse(dialog, msg, FALSE);
399 return dialog;
402 static void
403 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
405 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
406 gchar *body = sdpmsg_to_string(msg);
407 sdpmsg_free(msg);
408 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
409 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
410 g_free(body);
413 static gboolean
414 encryption_levels_compatible(struct sdpmsg *msg)
416 GSList *i;
418 for (i = msg->media; i; i = i->next) {
419 const gchar *enc_level;
420 struct sdpmedia *m = i->data;
422 enc_level = sipe_utils_nameval_find(m->attributes, "encryption");
424 // Decline call if peer requires encryption as we don't support it yet.
425 if (sipe_strequal(enc_level, "required"))
426 return FALSE;
429 return TRUE;
432 static gboolean
433 process_invite_call_response(struct sipe_core_private *sipe_private,
434 struct sipmsg *msg,
435 struct transaction *trans);
437 static gboolean
438 update_remote_media(struct sipe_media_call_private* call_private,
439 struct sdpmedia *media)
441 struct sipe_backend_media *backend_media = SIPE_MEDIA_CALL->backend_private;
442 struct sipe_backend_stream *backend_stream;
443 GList *backend_candidates = NULL;
444 GList *backend_codecs = NULL;
445 GSList *i;
446 gboolean result = TRUE;
448 backend_stream = sipe_backend_media_get_stream_by_id(backend_media,
449 media->name);
450 if (media->port == 0) {
451 if (backend_stream)
452 sipe_backend_media_remove_stream(backend_media, backend_stream);
453 return TRUE;
456 if (!backend_stream)
457 return FALSE;
459 for (i = media->codecs; i; i = i->next) {
460 struct sdpcodec *c = i->data;
461 struct sipe_backend_codec *codec;
462 GSList *j;
464 codec = sipe_backend_codec_new(c->id,
465 c->name,
466 c->type,
467 c->clock_rate);
469 for (j = c->parameters; j; j = j->next) {
470 struct sipnameval *attr = j->data;
472 sipe_backend_codec_add_optional_parameter(codec,
473 attr->name,
474 attr->value);
477 backend_codecs = g_list_append(backend_codecs, codec);
480 result = sipe_backend_set_remote_codecs(backend_media,
481 backend_stream,
482 backend_codecs);
483 sipe_media_codec_list_free(backend_codecs);
485 if (result == FALSE) {
486 sipe_backend_media_remove_stream(backend_media, backend_stream);
487 return FALSE;
490 for (i = media->candidates; i; i = i->next) {
491 struct sdpcandidate *c = i->data;
492 struct sipe_backend_candidate *candidate;
493 candidate = sipe_backend_candidate_new(c->foundation,
494 c->component,
495 c->type,
496 c->protocol,
497 c->ip,
498 c->port,
499 c->username,
500 c->password);
501 sipe_backend_candidate_set_priority(candidate, c->priority);
503 backend_candidates = g_list_append(backend_candidates, candidate);
506 sipe_backend_media_add_remote_candidates(backend_media,
507 backend_stream,
508 backend_candidates);
509 sipe_media_candidate_list_free(backend_candidates);
511 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
512 sipe_backend_stream_hold(backend_media, backend_stream, FALSE);
513 } else if (sipe_backend_stream_is_held(backend_stream)) {
514 sipe_backend_stream_unhold(backend_media, backend_stream, FALSE);
517 return TRUE;
520 static void
521 apply_remote_message(struct sipe_media_call_private* call_private,
522 struct sdpmsg* msg)
524 GSList *i;
526 sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free);
527 call_private->failed_media = NULL;
529 for (i = msg->media; i; i = i->next) {
530 struct sdpmedia *media = i->data;
531 if (!update_remote_media(call_private, media)) {
532 media->port = 0;
533 call_private->failed_media =
534 g_slist_append(call_private->failed_media, media);
538 /* We need to keep failed medias until response is sent, remove them
539 * from sdpmsg that is to be freed. */
540 for (i = call_private->failed_media; i; i = i->next) {
541 msg->media = g_slist_remove(msg->media, i->data);
544 call_private->encryption_compatible = encryption_levels_compatible(msg);
547 static gboolean
548 call_initialized(struct sipe_media_call *call)
550 GSList *streams =
551 sipe_backend_media_get_streams(call->backend_private);
553 for (; streams; streams = streams->next) {
554 if (!sipe_backend_stream_initialized(call->backend_private,
555 streams->data)) {
556 return FALSE;
560 return TRUE;
563 // Sends an invite response when the call is accepted and local candidates were
564 // prepared, otherwise does nothing. If error response is sent, call_private is
565 // disposed before function returns. Returns true when response was sent.
566 static gboolean
567 send_invite_response_if_ready(struct sipe_media_call_private *call_private)
569 struct sipe_backend_media *backend_media;
571 backend_media = call_private->public.backend_private;
573 if (!sipe_backend_media_accepted(backend_media) ||
574 !call_initialized(&call_private->public))
575 return FALSE;
577 if (!call_private->encryption_compatible) {
578 struct sipe_core_private *sipe_private = call_private->sipe_private;
580 sipmsg_add_header(call_private->invitation, "Warning",
581 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
582 sip_transport_response(sipe_private,
583 call_private->invitation,
584 488, "Encryption Levels not compatible",
585 NULL);
586 sipe_backend_media_reject(backend_media, FALSE);
587 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
588 _("Unable to establish a call"),
589 _("Encryption settings of peer are incompatible with ours."));
590 } else {
591 send_response_with_session_description(call_private, 200, "OK");
594 return TRUE;
597 static void
598 stream_initialized_cb(struct sipe_media_call *call,
599 struct sipe_backend_stream *stream)
601 if (call_initialized(call)) {
602 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
603 struct sipe_backend_media *backend_private = call->backend_private;
605 if (sipe_backend_media_is_initiator(backend_private, stream)) {
606 sipe_invite_call(call_private->sipe_private,
607 process_invite_call_response);
608 } else if (call_private->smsg) {
609 struct sdpmsg *smsg = call_private->smsg;
610 call_private->smsg = NULL;
612 apply_remote_message(call_private, smsg);
613 send_invite_response_if_ready(call_private);
614 sdpmsg_free(smsg);
619 static void phone_state_publish(struct sipe_core_private *sipe_private)
621 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
622 sipe_ocs2007_phone_state_publish(sipe_private);
623 } else {
624 // TODO: OCS 2005 support. Is anyone still using it at all?
628 static void
629 media_end_cb(struct sipe_media_call *call)
631 g_return_if_fail(call);
633 SIPE_MEDIA_CALL_PRIVATE->sipe_private->media_call = NULL;
634 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
635 sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE);
638 static void
639 call_accept_cb(struct sipe_media_call *call, gboolean local)
641 if (local) {
642 send_invite_response_if_ready(SIPE_MEDIA_CALL_PRIVATE);
644 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
647 static void
648 call_reject_cb(struct sipe_media_call *call, gboolean local)
650 if (local) {
651 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
652 sip_transport_response(call_private->sipe_private,
653 call_private->invitation,
654 603, "Decline", NULL);
658 static gboolean
659 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
660 struct transaction *trans);
662 static void call_hold_cb(struct sipe_media_call *call,
663 gboolean local,
664 SIPE_UNUSED_PARAMETER gboolean state)
666 if (local)
667 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
668 sipe_media_send_ack);
671 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
673 if (local) {
674 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
675 struct sip_session *session;
676 session = sipe_session_find_call(call_private->sipe_private,
677 call_private->with);
679 if (session) {
680 sipe_session_close(call_private->sipe_private, session);
685 static void
686 error_cb(struct sipe_media_call *call, gchar *message)
688 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
689 struct sipe_core_private *sipe_private = call_private->sipe_private;
690 gboolean initiator = sipe_backend_media_is_initiator(call->backend_private, NULL);
691 gboolean accepted = sipe_backend_media_accepted(call->backend_private);
693 gchar *title = g_strdup_printf("Call with %s failed", call_private->with);
694 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message);
695 g_free(title);
697 if (!initiator && !accepted) {
698 sip_transport_response(sipe_private,
699 call_private->invitation,
700 488, "Not Acceptable Here", NULL);
703 sipe_backend_media_hangup(call->backend_private, initiator || accepted);
706 static struct sipe_media_call_private *
707 sipe_media_call_new(struct sipe_core_private *sipe_private,
708 const gchar* with, gboolean initiator, SipeIceVersion ice_version)
710 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
711 gchar *cname;
713 call_private->sipe_private = sipe_private;
715 cname = g_strdup(sipe_private->contact + 1);
716 cname[strlen(cname) - 1] = '\0';
718 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
719 SIPE_MEDIA_CALL,
720 with,
721 initiator);
722 sipe_backend_media_set_cname(call_private->public.backend_private, cname);
724 call_private->ice_version = ice_version;
725 call_private->encryption_compatible = TRUE;
727 call_private->public.stream_initialized_cb = stream_initialized_cb;
728 call_private->public.media_end_cb = media_end_cb;
729 call_private->public.call_accept_cb = call_accept_cb;
730 call_private->public.call_reject_cb = call_reject_cb;
731 call_private->public.call_hold_cb = call_hold_cb;
732 call_private->public.call_hangup_cb = call_hangup_cb;
733 call_private->public.error_cb = error_cb;
735 g_free(cname);
737 return call_private;
740 void sipe_media_hangup(struct sipe_media_call_private *call_private)
742 if (call_private) {
743 sipe_backend_media_hangup(call_private->public.backend_private,
744 FALSE);
748 static void
749 sipe_media_initiate_call(struct sipe_core_private *sipe_private,
750 const char *with, SipeIceVersion ice_version,
751 gboolean with_video)
753 struct sipe_media_call_private *call_private;
754 struct sipe_backend_media *backend_media;
755 struct sipe_backend_media_relays *backend_media_relays;
756 struct sip_session *session;
757 struct sip_dialog *dialog;
759 if (sipe_private->media_call)
760 return;
762 call_private = sipe_media_call_new(sipe_private, with, TRUE, ice_version);
764 session = sipe_session_add_call(sipe_private, with);
765 dialog = sipe_dialog_add(session);
766 dialog->callid = gencallid();
767 dialog->with = g_strdup(session->with);
768 dialog->ourtag = gentag();
770 call_private->with = g_strdup(session->with);
772 backend_media = call_private->public.backend_private;
774 backend_media_relays =
775 sipe_backend_media_relays_convert(sipe_private->media_relays,
776 sipe_private->media_relay_username,
777 sipe_private->media_relay_password);
779 if (!sipe_backend_media_add_stream(backend_media,
780 "audio", with, SIPE_MEDIA_AUDIO,
781 call_private->ice_version, TRUE,
782 backend_media_relays)) {
783 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
784 _("Error occured"),
785 _("Error creating audio stream"));
786 sipe_media_hangup(call_private);
787 sipe_backend_media_relays_free(backend_media_relays);
788 return;
791 if ( with_video
792 && !sipe_backend_media_add_stream(backend_media,
793 "video", with, SIPE_MEDIA_VIDEO,
794 call_private->ice_version, TRUE,
795 backend_media_relays)) {
796 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
797 _("Error occured"),
798 _("Error creating video stream"));
799 sipe_media_hangup(call_private);
800 sipe_backend_media_relays_free(backend_media_relays);
801 return;
804 sipe_private->media_call = call_private;
806 sipe_backend_media_relays_free(backend_media_relays);
808 // Processing continues in stream_initialized_cb
811 void
812 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
813 const char *with,
814 gboolean with_video)
816 sipe_media_initiate_call(SIPE_CORE_PRIVATE, with,
817 SIPE_ICE_RFC_5245, with_video);
820 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
821 struct sipe_chat_session *chat_session)
823 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
824 struct sipe_backend_media_relays *backend_media_relays;
825 struct sip_session *session;
826 struct sip_dialog *dialog;
827 SipeIceVersion ice_version;
828 gchar **parts;
829 gchar *av_uri;
831 session = sipe_session_find_chat(sipe_private, chat_session);
833 if (sipe_private->media_call || !session)
834 return;
836 session->is_call = TRUE;
838 parts = g_strsplit(chat_session->id, "app:conf:focus:", 2);
839 av_uri = g_strjoinv("app:conf:audio-video:", parts);
840 g_strfreev(parts);
842 ice_version = SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) ? SIPE_ICE_RFC_5245 :
843 SIPE_ICE_DRAFT_6;
845 sipe_private->media_call = sipe_media_call_new(sipe_private, av_uri,
846 TRUE, ice_version);
848 session = sipe_session_add_call(sipe_private, av_uri);
849 dialog = sipe_dialog_add(session);
850 dialog->callid = gencallid();
851 dialog->with = g_strdup(session->with);
852 dialog->ourtag = gentag();
854 g_free(av_uri);
856 sipe_private->media_call->with = g_strdup(session->with);
858 backend_media_relays =
859 sipe_backend_media_relays_convert(sipe_private->media_relays,
860 sipe_private->media_relay_username,
861 sipe_private->media_relay_password);
863 if (!sipe_backend_media_add_stream(sipe_private->media_call->public.backend_private,
864 "audio", dialog->with,
865 SIPE_MEDIA_AUDIO,
866 sipe_private->media_call->ice_version,
867 TRUE, backend_media_relays)) {
868 sipe_backend_notify_error(sipe_public,
869 _("Error occured"),
870 _("Error creating audio stream"));
871 sipe_media_hangup(sipe_private->media_call);
872 sipe_private->media_call = NULL;
875 sipe_backend_media_relays_free(backend_media_relays);
877 // Processing continues in stream_initialized_cb
880 gboolean sipe_core_media_in_call(struct sipe_core_public *sipe_public)
882 if (sipe_public) {
883 return SIPE_CORE_PRIVATE->media_call != NULL;
885 return FALSE;
888 static gboolean phone_number_is_valid(const gchar *phone_number)
890 if (!phone_number || sipe_strequal(phone_number, "")) {
891 return FALSE;
894 if (*phone_number == '+') {
895 ++phone_number;
898 while (*phone_number != '\0') {
899 if (!g_ascii_isdigit(*phone_number)) {
900 return FALSE;
902 ++phone_number;
905 return TRUE;
908 void sipe_core_media_phone_call(struct sipe_core_public *sipe_public,
909 const gchar *phone_number)
911 g_return_if_fail(sipe_public);
913 if (phone_number_is_valid(phone_number)) {
914 gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone",
915 phone_number, sipe_public->sip_domain);
917 sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE);
919 g_free(phone_uri);
920 } else {
921 sipe_backend_notify_error(sipe_public,
922 _("Unable to establish a call"),
923 _("Invalid phone number"));
927 void sipe_core_media_test_call(struct sipe_core_public *sipe_public)
929 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
930 if (!sipe_private->test_call_bot_uri) {
931 sipe_backend_notify_error(sipe_public,
932 _("Unable to establish a call"),
933 _("Audio Test Service is not available."));
934 return;
937 sipe_core_media_initiate_call(sipe_public,
938 sipe_private->test_call_bot_uri, FALSE);
941 void
942 process_incoming_invite_call(struct sipe_core_private *sipe_private,
943 struct sipmsg *msg)
945 struct sipe_media_call_private *call_private = sipe_private->media_call;
946 struct sipe_backend_media *backend_media;
947 struct sipe_backend_media_relays *backend_media_relays = NULL;
948 struct sdpmsg *smsg;
949 gboolean has_new_media = FALSE;
950 GSList *i;
952 if (call_private) {
953 char *self;
955 if (!is_media_session_msg(call_private, msg)) {
956 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
957 return;
960 self = sip_uri_self(sipe_private);
961 if (sipe_strequal(call_private->with, self)) {
962 g_free(self);
963 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
964 return;
966 g_free(self);
969 smsg = sdpmsg_parse_msg(msg->body);
970 if (!smsg) {
971 sip_transport_response(sipe_private, msg,
972 488, "Not Acceptable Here", NULL);
973 sipe_media_hangup(call_private);
974 return;
977 if (!call_private) {
978 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
979 struct sip_session *session;
981 call_private = sipe_media_call_new(sipe_private, with, FALSE, smsg->ice_version);
982 session = sipe_session_add_call(sipe_private, with);
983 sipe_media_dialog_init(session, msg);
985 call_private->with = g_strdup(session->with);
986 sipe_private->media_call = call_private;
987 g_free(with);
990 backend_media = call_private->public.backend_private;
992 if (call_private->invitation)
993 sipmsg_free(call_private->invitation);
994 call_private->invitation = sipmsg_copy(msg);
996 if (smsg->media)
997 backend_media_relays = sipe_backend_media_relays_convert(
998 sipe_private->media_relays,
999 sipe_private->media_relay_username,
1000 sipe_private->media_relay_password);
1002 // Create any new media streams
1003 for (i = smsg->media; i; i = i->next) {
1004 struct sdpmedia *media = i->data;
1005 gchar *id = media->name;
1006 SipeMediaType type;
1008 if ( media->port != 0
1009 && !sipe_backend_media_get_stream_by_id(backend_media, id)) {
1010 gchar *with;
1012 if (sipe_strequal(id, "audio"))
1013 type = SIPE_MEDIA_AUDIO;
1014 else if (sipe_strequal(id, "video"))
1015 type = SIPE_MEDIA_VIDEO;
1016 else
1017 continue;
1019 with = parse_from(sipmsg_find_header(msg, "From"));
1020 sipe_backend_media_add_stream(backend_media, id, with,
1021 type,
1022 smsg->ice_version,
1023 FALSE,
1024 backend_media_relays);
1025 has_new_media = TRUE;
1026 g_free(with);
1030 sipe_backend_media_relays_free(backend_media_relays);
1032 if (has_new_media) {
1033 sdpmsg_free(call_private->smsg);
1034 call_private->smsg = smsg;
1035 sip_transport_response(sipe_private, call_private->invitation,
1036 180, "Ringing", NULL);
1037 // Processing continues in stream_initialized_cb
1038 } else {
1039 apply_remote_message(call_private, smsg);
1040 send_response_with_session_description(call_private, 200, "OK");
1042 sdpmsg_free(smsg);
1046 void process_incoming_cancel_call(struct sipe_core_private *sipe_private,
1047 struct sipmsg *msg)
1049 struct sipe_media_call_private *call_private = sipe_private->media_call;
1051 // We respond to the CANCEL request with 200 OK response and
1052 // with 487 Request Terminated to the remote INVITE in progress.
1053 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1055 if (call_private->invitation) {
1056 sip_transport_response(sipe_private, call_private->invitation,
1057 487, "Request Terminated", NULL);
1060 sipe_media_hangup(call_private);
1063 static gboolean
1064 sipe_media_send_ack(struct sipe_core_private *sipe_private,
1065 struct sipmsg *msg,
1066 struct transaction *trans)
1068 struct sipe_media_call_private *call_private = sipe_private->media_call;
1069 struct sip_session *session;
1070 struct sip_dialog *dialog;
1071 int tmp_cseq;
1073 if (!is_media_session_msg(call_private, msg))
1074 return FALSE;
1076 session = sipe_session_find_call(sipe_private, call_private->with);
1077 dialog = session->dialogs->data;
1078 if (!dialog)
1079 return FALSE;
1081 tmp_cseq = dialog->cseq;
1083 dialog->cseq = sip_transaction_cseq(trans) - 1;
1084 sip_transport_ack(sipe_private, dialog);
1085 dialog->cseq = tmp_cseq;
1087 dialog->outgoing_invite = NULL;
1089 return TRUE;
1092 static gboolean
1093 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
1094 struct sipmsg *msg,
1095 struct transaction *trans)
1097 if (!sipe_media_send_ack(sipe_private, msg, trans))
1098 return FALSE;
1100 sipe_backend_media_accept(sipe_private->media_call->public.backend_private,
1101 FALSE);
1103 return TRUE;
1106 static void
1107 reinvite_on_candidate_pair_cb(struct sipe_core_public *sipe_public)
1109 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1110 struct sipe_media_call_private *media_call = sipe_private->media_call;
1111 struct sipe_backend_media *backend_media;
1112 GSList *streams;
1114 if (!media_call)
1115 return;
1117 backend_media = media_call->public.backend_private;
1118 streams = sipe_backend_media_get_streams(backend_media);
1120 for (; streams; streams = streams->next) {
1121 struct sipe_backend_stream *s = streams->data;
1122 GList *remote_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, s);
1123 guint components = g_list_length(remote_candidates);
1125 sipe_media_candidate_list_free(remote_candidates);
1127 // We must have candidates for both (RTP + RTCP) components ready
1128 if (components < 2) {
1129 sipe_schedule_mseconds(sipe_private,
1130 "<+media-reinvite-on-candidate-pair>",
1131 NULL,
1132 500,
1133 (sipe_schedule_action) reinvite_on_candidate_pair_cb,
1134 NULL);
1135 return;
1139 sipe_invite_call(sipe_private, sipe_media_send_final_ack);
1142 static gboolean
1143 maybe_retry_call_with_ice_v6(struct sipe_core_private *sipe_private,
1144 struct transaction *trans)
1146 struct sipe_media_call_private *call_private = sipe_private->media_call;
1148 if (call_private->ice_version == SIPE_ICE_RFC_5245 &&
1149 sip_transaction_cseq(trans) == 1) {
1150 gchar *with = g_strdup(call_private->with);
1151 struct sipe_backend_media *backend_private = call_private->public.backend_private;
1152 gboolean with_video = sipe_backend_media_get_stream_by_id(backend_private, "video") != NULL;
1154 sipe_media_hangup(call_private);
1155 SIPE_DEBUG_INFO_NOFORMAT("Retrying call witn ICEv6.");
1156 // We might be calling to OC 2007 instance, retry with ICEv6
1157 sipe_media_initiate_call(sipe_private, with,
1158 SIPE_ICE_DRAFT_6, with_video);
1160 g_free(with);
1161 return TRUE;
1164 return FALSE;
1167 static gboolean
1168 process_invite_call_response(struct sipe_core_private *sipe_private,
1169 struct sipmsg *msg,
1170 struct transaction *trans)
1172 const gchar *with;
1173 struct sipe_media_call_private *call_private = sipe_private->media_call;
1174 struct sip_session *session;
1175 struct sip_dialog *dialog;
1176 struct sdpmsg *smsg;
1178 if (!is_media_session_msg(call_private, msg))
1179 return FALSE;
1181 session = sipe_session_find_call(sipe_private, call_private->with);
1182 dialog = session->dialogs->data;
1184 with = dialog->with;
1186 dialog->outgoing_invite = NULL;
1188 if (msg->response >= 400) {
1189 // Call rejected by remote peer or an error occurred
1190 const gchar *title;
1191 GString *desc = g_string_new("");
1192 gboolean append_responsestr = FALSE;
1194 switch (msg->response) {
1195 case 480: {
1196 title = _("User unavailable");
1198 if (sipmsg_parse_warning(msg, NULL) == 391) {
1199 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
1200 } else
1201 g_string_append_printf(desc, _("User %s is not available"), with);
1202 break;
1204 case 603:
1205 case 605:
1206 title = _("Call rejected");
1207 g_string_append_printf(desc, _("User %s rejected call"), with);
1208 break;
1209 case 415:
1210 // OCS/Lync really sends response string with 'Mutipart' typo.
1211 if (sipe_strequal(msg->responsestr, "Mutipart mime in content type not supported by Archiving CDR service") &&
1212 maybe_retry_call_with_ice_v6(sipe_private, trans)) {
1213 return TRUE;
1215 title = _("Unsupported media type");
1216 break;
1217 case 488: {
1218 /* Check for incompatible encryption levels error.
1220 * MS Lync 2010:
1221 * 488 Not Acceptable Here
1222 * ms-client-diagnostics: 52017;reason="Encryption levels dont match"
1224 * older clients (and SIPE itself):
1225 * 488 Encryption Levels not compatible
1227 const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics");
1229 if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") ||
1230 (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) {
1231 title = _("Unable to establish a call");
1232 g_string_append(desc, _("Encryption settings of peer are incompatible with ours."));
1233 break;
1236 if (maybe_retry_call_with_ice_v6(sipe_private, trans)) {
1237 return TRUE;
1239 // Break intentionally omitted
1241 default:
1242 title = _("Error occured");
1243 g_string_append(desc, _("Unable to establish a call"));
1244 append_responsestr = TRUE;
1245 break;
1248 if (append_responsestr) {
1249 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1251 g_string_append_printf(desc, "\n%d %s",
1252 msg->response, msg->responsestr);
1253 if (reason) {
1254 g_string_append_printf(desc, "\n\n%s", reason);
1255 g_free(reason);
1259 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str);
1260 g_string_free(desc, TRUE);
1262 sipe_media_send_ack(sipe_private, msg, trans);
1263 sipe_media_hangup(call_private);
1265 return TRUE;
1268 sipe_dialog_parse(dialog, msg, TRUE);
1269 smsg = sdpmsg_parse_msg(msg->body);
1270 if (!smsg) {
1271 sip_transport_response(sipe_private, msg,
1272 488, "Not Acceptable Here", NULL);
1273 sipe_media_hangup(call_private);
1274 return FALSE;
1277 apply_remote_message(call_private, smsg);
1278 sdpmsg_free(smsg);
1280 sipe_media_send_ack(sipe_private, msg, trans);
1281 reinvite_on_candidate_pair_cb(SIPE_CORE_PUBLIC);
1283 return TRUE;
1286 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
1287 struct sipmsg *msg)
1289 if (call_private) {
1290 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
1291 struct sip_session *session;
1293 session = sipe_session_find_call(call_private->sipe_private,
1294 call_private->with);
1295 if (session) {
1296 struct sip_dialog *dialog = session->dialogs->data;
1297 return sipe_strequal(dialog->callid, callid);
1300 return FALSE;
1303 void sipe_media_handle_going_offline(struct sipe_media_call_private *call_private)
1305 struct sipe_backend_media *backend_private;
1307 backend_private = call_private->public.backend_private;
1309 if ( !sipe_backend_media_is_initiator(backend_private, NULL)
1310 && !sipe_backend_media_accepted(backend_private)) {
1311 sip_transport_response(call_private->sipe_private,
1312 call_private->invitation,
1313 480, "Temporarily Unavailable", NULL);
1314 } else {
1315 struct sip_session *session;
1317 session = sipe_session_find_call(call_private->sipe_private,
1318 call_private->with);
1319 if (session)
1320 sipe_session_close(call_private->sipe_private, session);
1323 sipe_media_hangup(call_private);
1326 gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private)
1328 return g_strstr_len(call_private->with, -1, "app:conf:audio-video:") != NULL;
1331 static void
1332 sipe_media_relay_free(struct sipe_media_relay *relay)
1334 g_free(relay->hostname);
1335 if (relay->dns_query)
1336 sipe_backend_dns_query_cancel(relay->dns_query);
1337 g_free(relay);
1340 void
1341 sipe_media_relay_list_free(GSList *list)
1343 for (; list; list = g_slist_delete_link(list, list))
1344 sipe_media_relay_free(list->data);
1347 static void
1348 relay_ip_resolved_cb(struct sipe_media_relay* relay,
1349 const gchar *ip, SIPE_UNUSED_PARAMETER guint port)
1351 gchar *hostname = relay->hostname;
1352 relay->dns_query = NULL;
1354 if (ip && port) {
1355 relay->hostname = g_strdup(ip);
1356 SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip);
1357 } else {
1358 relay->hostname = NULL;
1359 SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname);
1362 g_free(hostname);
1365 static gboolean
1366 process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private,
1367 struct sipmsg *msg,
1368 SIPE_UNUSED_PARAMETER struct transaction *trans)
1370 g_free(sipe_private->media_relay_username);
1371 g_free(sipe_private->media_relay_password);
1372 sipe_media_relay_list_free(sipe_private->media_relays);
1373 sipe_private->media_relay_username = NULL;
1374 sipe_private->media_relay_password = NULL;
1375 sipe_private->media_relays = NULL;
1377 if (msg->response >= 400) {
1378 SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. "
1379 "Failed to obtain A/V Edge credentials.");
1380 return FALSE;
1383 if (msg->response == 200) {
1384 sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
1386 if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) {
1387 const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials");
1388 const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList");
1389 const sipe_xml *item;
1390 GSList *relays = NULL;
1392 item = sipe_xml_child(xn_credentials, "username");
1393 sipe_private->media_relay_username = sipe_xml_data(item);
1394 item = sipe_xml_child(xn_credentials, "password");
1395 sipe_private->media_relay_password = sipe_xml_data(item);
1397 for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) {
1398 struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1);
1399 const sipe_xml *node;
1400 gchar *tmp;
1402 node = sipe_xml_child(item, "hostName");
1403 relay->hostname = sipe_xml_data(node);
1405 node = sipe_xml_child(item, "udpPort");
1406 if (node) {
1407 relay->udp_port = atoi(tmp = sipe_xml_data(node));
1408 g_free(tmp);
1411 node = sipe_xml_child(item, "tcpPort");
1412 if (node) {
1413 relay->tcp_port = atoi(tmp = sipe_xml_data(node));
1414 g_free(tmp);
1417 relays = g_slist_append(relays, relay);
1419 relay->dns_query = sipe_backend_dns_query_a(
1420 SIPE_CORE_PUBLIC,
1421 relay->hostname,
1422 relay->udp_port,
1423 (sipe_dns_resolved_cb) relay_ip_resolved_cb,
1424 relay);
1426 SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d",
1427 relay->hostname,
1428 relay->tcp_port, relay->udp_port);
1431 sipe_private->media_relays = relays;
1434 sipe_xml_free(xn_response);
1437 return TRUE;
1440 void
1441 sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private)
1443 // TODO: re-request credentials after duration expires?
1444 const char CRED_REQUEST_XML[] =
1445 "<request requestID=\"%d\" "
1446 "from=\"%s\" "
1447 "version=\"1.0\" "
1448 "to=\"%s\" "
1449 "xmlns=\"http://schemas.microsoft.com/2006/09/sip/mrasp\" "
1450 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
1451 "<credentialsRequest credentialsRequestID=\"%d\">"
1452 "<identity>%s</identity>"
1453 "<location>%s</location>"
1454 "<duration>480</duration>"
1455 "</credentialsRequest>"
1456 "</request>";
1458 int request_id = rand();
1459 gchar *self;
1460 gchar *body;
1462 if (!sipe_private->mras_uri)
1463 return;
1465 self = sip_uri_self(sipe_private);
1467 body = g_strdup_printf(
1468 CRED_REQUEST_XML,
1469 request_id,
1470 self,
1471 sipe_private->mras_uri,
1472 request_id,
1473 self,
1474 SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet");
1475 g_free(self);
1477 sip_transport_service(sipe_private,
1478 sipe_private->mras_uri,
1479 "Content-Type: application/msrtc-media-relay-auth+xml\r\n",
1480 body,
1481 process_get_av_edge_credentials_response);
1483 g_free(body);
1487 Local Variables:
1488 mode: c
1489 c-file-style: "bsd"
1490 indent-tabs-mode: t
1491 tab-width: 8
1492 End: