media: fix error message typo
[siplcs.git] / src / core / sipe-media.c
blob6233b76e39b9b3bf244d5954f2eb72f8271f7add
1 /**
2 * @file sipe-media.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2015 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-conf.h"
41 #include "sipe-core.h"
42 #include "sipe-core-private.h"
43 #include "sipe-dialog.h"
44 #include "sipe-media.h"
45 #include "sipe-ocs2007.h"
46 #include "sipe-session.h"
47 #include "sipe-utils.h"
48 #include "sipe-nls.h"
49 #include "sipe-schedule.h"
50 #include "sipe-xml.h"
52 struct sipe_media_call_private {
53 struct sipe_media_call public;
55 /* private part starts here */
56 struct sipe_core_private *sipe_private;
58 GSList *streams;
60 struct sipmsg *invitation;
61 SipeIceVersion ice_version;
62 gboolean encryption_compatible;
64 struct sdpmsg *smsg;
65 GSList *failed_media;
67 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
68 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
70 struct sipe_media_stream_private {
71 struct sipe_media_stream public;
73 guchar *encryption_key;
74 int encryption_key_id;
75 gboolean remote_candidates_and_codecs_set;
77 #define SIPE_MEDIA_STREAM ((struct sipe_media_stream *) stream_private)
78 #define SIPE_MEDIA_STREAM_PRIVATE ((struct sipe_media_stream_private *) stream)
80 static void sipe_media_codec_list_free(GList *codecs)
82 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
83 sipe_backend_codec_free(codecs->data);
86 static void sipe_media_candidate_list_free(GList *candidates)
88 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
89 sipe_backend_candidate_free(candidates->data);
92 static void
93 remove_stream(struct sipe_media_call* call,
94 struct sipe_media_stream_private *stream_private)
96 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
97 call_private->streams =
98 g_slist_remove(call_private->streams, stream_private);
99 sipe_backend_media_stream_free(SIPE_MEDIA_STREAM->backend_private);
100 g_free(SIPE_MEDIA_STREAM->id);
101 g_free(stream_private->encryption_key);
102 g_free(stream_private);
105 static void
106 sipe_media_call_free(struct sipe_media_call_private *call_private)
108 if (call_private) {
109 struct sip_session *session;
111 while (call_private->streams) {
112 remove_stream(SIPE_MEDIA_CALL,
113 call_private->streams->data);
116 sipe_backend_media_free(call_private->public.backend_private);
118 session = sipe_session_find_call(call_private->sipe_private,
119 SIPE_MEDIA_CALL->with);
120 if (session)
121 sipe_session_remove(call_private->sipe_private, session);
123 if (call_private->invitation)
124 sipmsg_free(call_private->invitation);
126 sdpmsg_free(call_private->smsg);
127 sipe_utils_slist_free_full(call_private->failed_media,
128 (GDestroyNotify)sdpmedia_free);
129 g_free(SIPE_MEDIA_CALL->with);
130 g_free(call_private);
134 static gint
135 candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
137 int cmp = sipe_strcompare(c1->foundation, c2->foundation);
138 if (cmp == 0) {
139 cmp = sipe_strcompare(c1->username, c2->username);
140 if (cmp == 0)
141 cmp = c1->component - c2->component;
144 return cmp;
147 static GSList *
148 backend_candidates_to_sdpcandidate(GList *candidates)
150 GSList *result = NULL;
151 GList *i;
153 for (i = candidates; i; i = i->next) {
154 struct sipe_backend_candidate *candidate = i->data;
155 struct sdpcandidate *c;
157 gchar *ip = sipe_backend_candidate_get_ip(candidate);
158 gchar *base_ip = sipe_backend_candidate_get_base_ip(candidate);
159 if (is_empty(ip) || strchr(ip, ':') ||
160 (base_ip && strchr(base_ip, ':'))) {
161 /* Ignore IPv6 candidates. */
162 g_free(ip);
163 g_free(base_ip);
164 continue;
167 c = g_new(struct sdpcandidate, 1);
168 c->foundation = sipe_backend_candidate_get_foundation(candidate);
169 c->component = sipe_backend_candidate_get_component_type(candidate);
170 c->type = sipe_backend_candidate_get_type(candidate);
171 c->protocol = sipe_backend_candidate_get_protocol(candidate);
172 c->ip = ip;
173 c->port = sipe_backend_candidate_get_port(candidate);
174 c->base_ip = base_ip;
175 c->base_port = sipe_backend_candidate_get_base_port(candidate);
176 c->priority = sipe_backend_candidate_get_priority(candidate);
177 c->username = sipe_backend_candidate_get_username(candidate);
178 c->password = sipe_backend_candidate_get_password(candidate);
180 result = g_slist_insert_sorted(result, c,
181 (GCompareFunc)candidate_sort_cb);
184 return result;
187 static void
188 get_stream_ip_and_ports(GSList *candidates,
189 gchar **ip, guint *rtp_port, guint *rtcp_port,
190 SipeCandidateType type)
192 *ip = 0;
193 *rtp_port = 0;
194 *rtcp_port = 0;
196 for (; candidates; candidates = candidates->next) {
197 struct sdpcandidate *candidate = candidates->data;
199 if (type == SIPE_CANDIDATE_TYPE_ANY || candidate->type == type) {
200 if (!*ip) {
201 *ip = g_strdup(candidate->ip);
202 } else if (!sipe_strequal(*ip, candidate->ip)) {
203 continue;
206 if (candidate->component == SIPE_COMPONENT_RTP) {
207 *rtp_port = candidate->port;
208 } else if (candidate->component == SIPE_COMPONENT_RTCP)
209 *rtcp_port = candidate->port;
212 if (*rtp_port != 0 && *rtcp_port != 0)
213 return;
217 static gint
218 sdpcodec_compare(gconstpointer a, gconstpointer b)
220 return ((const struct sdpcodec *)a)->id -
221 ((const struct sdpcodec *)b)->id;
224 static GList *
225 remove_wrong_farstream_0_1_tcp_candidates(GList *candidates)
227 GList *i = candidates;
228 GHashTable *foundation_to_candidate = g_hash_table_new_full(g_str_hash,
229 g_str_equal,
230 g_free,
231 NULL);
233 while (i) {
234 GList *next = i->next;
235 struct sipe_backend_candidate *c1 = i->data;
237 if (sipe_backend_candidate_get_protocol(c1) == SIPE_NETWORK_PROTOCOL_UDP) {
238 gchar *foundation = sipe_backend_candidate_get_foundation(c1);
239 struct sipe_backend_candidate *c2 = g_hash_table_lookup(foundation_to_candidate,
240 foundation);
242 if (c2) {
243 g_free(foundation);
245 if (sipe_backend_candidate_get_port(c1) ==
246 sipe_backend_candidate_get_port(c2) ||
247 (sipe_backend_candidate_get_type(c1) !=
248 SIPE_CANDIDATE_TYPE_HOST &&
249 sipe_backend_candidate_get_base_port(c1) ==
250 sipe_backend_candidate_get_base_port(c2))) {
252 * We assume that RTP+RTCP UDP pairs
253 * that share the same port are
254 * actually mistagged TCP candidates.
256 candidates = g_list_remove(candidates, c2);
257 candidates = g_list_delete_link(candidates, i);
258 sipe_backend_candidate_free(c1);
259 sipe_backend_candidate_free(c2);
261 } else
262 /* hash table takes ownership of "foundation" */
263 g_hash_table_insert(foundation_to_candidate, foundation, c1);
266 i = next;
269 g_hash_table_destroy(foundation_to_candidate);
271 return candidates;
274 static void
275 fill_zero_tcp_act_ports_from_tcp_pass(GSList *candidates)
277 GSList *i;
278 GHashTable *ip_to_port = g_hash_table_new(g_str_hash, g_str_equal);
280 for (i = candidates; i; i = i->next) {
281 struct sdpcandidate *c = i->data;
282 GSList *j;
284 if (c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE &&
285 c->type == SIPE_CANDIDATE_TYPE_HOST) {
286 g_hash_table_insert(ip_to_port, c->ip,
287 &c->port);
290 if (c->protocol != SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
291 continue;
294 for (j = candidates; j; j = j->next) {
295 struct sdpcandidate *passive = j->data;
296 if (passive->protocol != SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
297 c->type != passive->type) {
298 continue;
301 if (sipe_strequal(c->ip, passive->ip) &&
302 sipe_strequal(c->base_ip, passive->base_ip)) {
303 if (c->port == 0) {
304 c->port = passive->port;
307 if (c->base_port == 0) {
308 c->base_port = passive->base_port;
310 break;
315 /* Fill base ports of all TCP relay candidates using what we have
316 * collected from host candidates. */
317 for (i = candidates; i; i = i->next) {
318 struct sdpcandidate *c = i->data;
319 if (c->type == SIPE_CANDIDATE_TYPE_RELAY && c->base_port == 0) {
320 guint *base_port = (guint*)g_hash_table_lookup(ip_to_port, c->base_ip);
321 if (base_port) {
322 c->base_port = *base_port;
323 } else {
324 SIPE_DEBUG_WARNING("Couldn't determine base port for candidate "
325 "with foundation %s", c->foundation);
330 g_hash_table_destroy(ip_to_port);
333 static SipeEncryptionPolicy
334 get_encryption_policy(struct sipe_core_private *sipe_private)
336 SipeEncryptionPolicy result =
337 sipe_backend_media_get_encryption_policy(SIPE_CORE_PUBLIC);
338 if (result == SIPE_ENCRYPTION_POLICY_OBEY_SERVER) {
339 result = sipe_private->server_av_encryption_policy;
342 return result;
345 static struct sdpmedia *
346 media_stream_to_sdpmedia(struct sipe_media_call_private *call_private,
347 struct sipe_media_stream_private *stream_private)
349 struct sdpmedia *sdpmedia = g_new0(struct sdpmedia, 1);
350 GList *codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
351 SIPE_MEDIA_STREAM);
352 SipeEncryptionPolicy encryption_policy =
353 get_encryption_policy(call_private->sipe_private);
354 guint rtcp_port = 0;
355 SipeMediaType type;
356 GSList *attributes = NULL;
357 GList *candidates;
358 GList *i;
360 sdpmedia->name = g_strdup(SIPE_MEDIA_STREAM->id);
362 if (sipe_strequal(sdpmedia->name, "audio"))
363 type = SIPE_MEDIA_AUDIO;
364 else if (sipe_strequal(sdpmedia->name, "video"))
365 type = SIPE_MEDIA_VIDEO;
366 else {
367 // TODO: incompatible media, should not happen here
368 g_free(sdpmedia->name);
369 g_free(sdpmedia);
370 sipe_media_codec_list_free(codecs);
371 return(NULL);
374 // Process codecs
375 for (i = codecs; i; i = i->next) {
376 struct sipe_backend_codec *codec = i->data;
377 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
378 GList *params;
380 c->id = sipe_backend_codec_get_id(codec);
381 c->name = sipe_backend_codec_get_name(codec);
382 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
383 c->type = type;
385 params = sipe_backend_codec_get_optional_parameters(codec);
386 for (; params; params = params->next) {
387 struct sipnameval *param = params->data;
388 struct sipnameval *copy = g_new0(struct sipnameval, 1);
390 copy->name = g_strdup(param->name);
391 copy->value = g_strdup(param->value);
393 c->parameters = g_slist_append(c->parameters, copy);
396 /* Buggy(?) codecs may report non-unique id (a.k.a. payload
397 * type) that must not appear in SDP messages we send. Thus,
398 * let's ignore any codec having the same id as one we already
399 * have in the converted list. */
400 sdpmedia->codecs = sipe_utils_slist_insert_unique_sorted(
401 sdpmedia->codecs, c, sdpcodec_compare,
402 (GDestroyNotify)sdpcodec_free);
405 sipe_media_codec_list_free(codecs);
407 // Process local candidates
408 // If we have established candidate pairs, send them in SDP response.
409 // Otherwise send all available local candidates.
410 candidates = sipe_backend_media_get_active_local_candidates(SIPE_MEDIA_CALL,
411 SIPE_MEDIA_STREAM);
412 if (!candidates) {
413 candidates = sipe_backend_get_local_candidates(SIPE_MEDIA_CALL,
414 SIPE_MEDIA_STREAM);
415 candidates = remove_wrong_farstream_0_1_tcp_candidates(candidates);
418 sdpmedia->candidates = backend_candidates_to_sdpcandidate(candidates);
419 fill_zero_tcp_act_ports_from_tcp_pass(sdpmedia->candidates);
421 sipe_media_candidate_list_free(candidates);
423 get_stream_ip_and_ports(sdpmedia->candidates, &sdpmedia->ip, &sdpmedia->port,
424 &rtcp_port, SIPE_CANDIDATE_TYPE_HOST);
425 // No usable HOST candidates, use any candidate
426 if (sdpmedia->ip == NULL && sdpmedia->candidates) {
427 get_stream_ip_and_ports(sdpmedia->candidates, &sdpmedia->ip, &sdpmedia->port,
428 &rtcp_port, SIPE_CANDIDATE_TYPE_ANY);
431 if (sipe_backend_stream_is_held(SIPE_MEDIA_STREAM))
432 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
434 if (rtcp_port) {
435 gchar *tmp = g_strdup_printf("%u", rtcp_port);
436 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
437 g_free(tmp);
440 if (encryption_policy != call_private->sipe_private->server_av_encryption_policy) {
441 const gchar *encryption = NULL;
442 switch (encryption_policy) {
443 case SIPE_ENCRYPTION_POLICY_REJECTED:
444 encryption = "rejected";
445 break;
446 case SIPE_ENCRYPTION_POLICY_OPTIONAL:
447 encryption = "optional";
448 break;
449 case SIPE_ENCRYPTION_POLICY_REQUIRED:
450 default:
451 encryption = "required";
452 break;
455 attributes = sipe_utils_nameval_add(attributes, "encryption", encryption);
458 sdpmedia->attributes = attributes;
460 // Process remote candidates
461 candidates = sipe_backend_media_get_active_remote_candidates(SIPE_MEDIA_CALL,
462 SIPE_MEDIA_STREAM);
463 sdpmedia->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
464 sipe_media_candidate_list_free(candidates);
466 sdpmedia->encryption_active = stream_private->encryption_key &&
467 call_private->encryption_compatible &&
468 stream_private->remote_candidates_and_codecs_set &&
469 encryption_policy != SIPE_ENCRYPTION_POLICY_REJECTED;
471 // Set our key if encryption is enabled.
472 if (stream_private->encryption_key &&
473 encryption_policy != SIPE_ENCRYPTION_POLICY_REJECTED) {
474 sdpmedia->encryption_key = g_memdup(stream_private->encryption_key,
475 SIPE_SRTP_KEY_LEN);
476 sdpmedia->encryption_key_id = stream_private->encryption_key_id;
479 return sdpmedia;
482 static struct sdpmsg *
483 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
485 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
486 GSList *streams = call_private->streams;
488 for (; streams; streams = streams->next) {
489 struct sdpmedia *media = media_stream_to_sdpmedia(call_private,
490 streams->data);
491 if (media) {
492 msg->media = g_slist_append(msg->media, media);
494 if (msg->ip == NULL)
495 msg->ip = g_strdup(media->ip);
499 msg->media = g_slist_concat(msg->media, call_private->failed_media);
500 call_private->failed_media = NULL;
502 msg->ice_version = call_private->ice_version;
504 return msg;
507 static void
508 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
510 gchar *hdr;
511 gchar *contact;
512 gchar *p_preferred_identity = NULL;
513 gchar *body;
514 struct sipe_media_call_private *call_private = sipe_private->media_call;
515 struct sip_session *session;
516 struct sip_dialog *dialog;
517 struct sdpmsg *msg;
518 gboolean add_2007_fallback = FALSE;
520 session = sipe_session_find_call(sipe_private, SIPE_MEDIA_CALL->with);
521 dialog = session->dialogs->data;
522 add_2007_fallback = dialog->cseq == 0 &&
523 call_private->ice_version == SIPE_ICE_RFC_5245 &&
524 !sipe_strequal(SIPE_MEDIA_CALL->with, sipe_private->test_call_bot_uri);
526 contact = get_contact(sipe_private);
528 if (sipe_private->uc_line_uri) {
529 gchar *self = sip_uri_self(sipe_private);
530 p_preferred_identity = g_strdup_printf(
531 "P-Preferred-Identity: <%s>, <%s>\r\n",
532 self, sipe_private->uc_line_uri);
533 g_free(self);
536 hdr = g_strdup_printf(
537 "ms-keep-alive: UAC;hop-hop=yes\r\n"
538 "Contact: %s\r\n"
539 "%s"
540 "Content-Type: %s\r\n",
541 contact,
542 p_preferred_identity ? p_preferred_identity : "",
543 add_2007_fallback ?
544 "multipart/alternative;boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\""
545 : "application/sdp");
546 g_free(contact);
547 g_free(p_preferred_identity);
549 msg = sipe_media_to_sdpmsg(call_private);
550 body = sdpmsg_to_string(msg);
552 if (add_2007_fallback) {
553 gchar *tmp;
554 tmp = g_strdup_printf(
555 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
556 "Content-Type: application/sdp\r\n"
557 "Content-Transfer-Encoding: 7bit\r\n"
558 "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n"
559 "\r\n"
560 "o=- 0 0 IN IP4 %s\r\n"
561 "s=session\r\n"
562 "c=IN IP4 %s\r\n"
563 "m=audio 0 RTP/AVP\r\n"
564 "\r\n"
565 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
566 "Content-Type: application/sdp\r\n"
567 "Content-Transfer-Encoding: 7bit\r\n"
568 "Content-Disposition: session; handling=optional\r\n"
569 "\r\n"
570 "%s"
571 "\r\n"
572 "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n",
573 msg->ip, msg->ip, body);
574 g_free(body);
575 body = tmp;
578 sdpmsg_free(msg);
580 dialog->outgoing_invite = sip_transport_invite(sipe_private,
581 hdr,
582 body,
583 dialog,
584 tc);
586 g_free(body);
587 g_free(hdr);
590 static struct sip_dialog *
591 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
593 gchar *newTag = gentag();
594 const gchar *oldHeader;
595 gchar *newHeader;
596 struct sip_dialog *dialog;
598 oldHeader = sipmsg_find_header(msg, "To");
599 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
600 sipmsg_remove_header_now(msg, "To");
601 sipmsg_add_header_now(msg, "To", newHeader);
602 g_free(newHeader);
604 dialog = sipe_dialog_add(session);
605 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
606 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
607 sipe_dialog_parse(dialog, msg, FALSE);
609 return dialog;
612 static void
613 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
615 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
616 gchar *body = sdpmsg_to_string(msg);
617 sdpmsg_free(msg);
618 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
619 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
620 g_free(body);
623 static gboolean
624 process_invite_call_response(struct sipe_core_private *sipe_private,
625 struct sipmsg *msg,
626 struct transaction *trans);
628 struct sipe_media_stream *
629 sipe_core_media_get_stream_by_id(struct sipe_media_call *call, const gchar *id)
631 GSList *i;
632 for (i = SIPE_MEDIA_CALL_PRIVATE->streams; i; i = i->next) {
633 struct sipe_media_stream *stream = i->data;
634 if (sipe_strequal(stream->id, id))
635 return stream;
637 return NULL;
640 static gboolean
641 update_call_from_remote_sdp(struct sipe_media_call_private* call_private,
642 struct sdpmedia *media)
644 struct sipe_media_stream *stream;
645 GList *backend_candidates = NULL;
646 GList *backend_codecs = NULL;
647 GSList *i;
648 gboolean result = TRUE;
650 stream = sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, media->name);
651 if (media->port == 0) {
652 if (stream) {
653 sipe_backend_media_stream_end(SIPE_MEDIA_CALL, stream);
655 return TRUE;
658 if (!stream)
659 return FALSE;
661 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
662 sipe_backend_stream_hold(SIPE_MEDIA_CALL, stream, FALSE);
663 } else if (sipe_backend_stream_is_held(stream)) {
664 sipe_backend_stream_unhold(SIPE_MEDIA_CALL, stream, FALSE);
667 if (SIPE_MEDIA_STREAM_PRIVATE->remote_candidates_and_codecs_set) {
668 return TRUE;
671 for (i = media->codecs; i; i = i->next) {
672 struct sdpcodec *c = i->data;
673 struct sipe_backend_codec *codec;
674 GSList *j;
676 codec = sipe_backend_codec_new(c->id,
677 c->name,
678 c->type,
679 c->clock_rate);
681 for (j = c->parameters; j; j = j->next) {
682 struct sipnameval *attr = j->data;
684 sipe_backend_codec_add_optional_parameter(codec,
685 attr->name,
686 attr->value);
689 backend_codecs = g_list_append(backend_codecs, codec);
692 if (media->encryption_key && SIPE_MEDIA_STREAM_PRIVATE->encryption_key) {
693 sipe_backend_media_set_encryption_keys(SIPE_MEDIA_CALL, stream,
694 SIPE_MEDIA_STREAM_PRIVATE->encryption_key,
695 media->encryption_key);
696 SIPE_MEDIA_STREAM_PRIVATE->encryption_key_id = media->encryption_key_id;
699 result = sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL, stream,
700 backend_codecs);
701 sipe_media_codec_list_free(backend_codecs);
703 if (result == FALSE) {
704 sipe_backend_media_stream_end(SIPE_MEDIA_CALL, stream);
705 return FALSE;
708 for (i = media->candidates; i; i = i->next) {
709 struct sdpcandidate *c = i->data;
710 struct sipe_backend_candidate *candidate;
711 candidate = sipe_backend_candidate_new(c->foundation,
712 c->component,
713 c->type,
714 c->protocol,
715 c->ip,
716 c->port,
717 c->username,
718 c->password);
719 sipe_backend_candidate_set_priority(candidate, c->priority);
721 backend_candidates = g_list_append(backend_candidates, candidate);
724 sipe_backend_media_add_remote_candidates(SIPE_MEDIA_CALL, stream,
725 backend_candidates);
726 sipe_media_candidate_list_free(backend_candidates);
728 SIPE_MEDIA_STREAM_PRIVATE->remote_candidates_and_codecs_set = TRUE;
730 return TRUE;
733 static void
734 apply_remote_message(struct sipe_media_call_private* call_private,
735 struct sdpmsg* msg)
737 GSList *i;
739 sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free);
740 call_private->failed_media = NULL;
741 call_private->encryption_compatible = TRUE;
743 for (i = msg->media; i; i = i->next) {
744 struct sdpmedia *media = i->data;
745 const gchar *enc_level =
746 sipe_utils_nameval_find(media->attributes, "encryption");
747 if (sipe_strequal(enc_level, "rejected") &&
748 get_encryption_policy(call_private->sipe_private) == SIPE_ENCRYPTION_POLICY_REQUIRED) {
749 call_private->encryption_compatible = FALSE;
752 if (!update_call_from_remote_sdp(call_private, media)) {
753 media->port = 0;
754 call_private->failed_media =
755 g_slist_append(call_private->failed_media, media);
759 /* We need to keep failed medias until response is sent, remove them
760 * from sdpmsg that is to be freed. */
761 for (i = call_private->failed_media; i; i = i->next) {
762 msg->media = g_slist_remove(msg->media, i->data);
766 static gboolean
767 call_initialized(struct sipe_media_call *call)
769 GSList *streams = SIPE_MEDIA_CALL_PRIVATE->streams;
770 for (; streams; streams = streams->next) {
771 if (!sipe_backend_stream_initialized(call, streams->data)) {
772 return FALSE;
776 return TRUE;
779 // Sends an invite response when the call is accepted and local candidates were
780 // prepared, otherwise does nothing. If error response is sent, call_private is
781 // disposed before function returns. Returns true when response was sent.
782 static gboolean
783 send_invite_response_if_ready(struct sipe_media_call_private *call_private)
785 struct sipe_backend_media *backend_media;
787 backend_media = call_private->public.backend_private;
789 if (!sipe_backend_media_accepted(backend_media) ||
790 !call_initialized(&call_private->public))
791 return FALSE;
793 if (!call_private->encryption_compatible) {
794 struct sipe_core_private *sipe_private = call_private->sipe_private;
796 sipmsg_add_header(call_private->invitation, "Warning",
797 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
798 sip_transport_response(sipe_private,
799 call_private->invitation,
800 488, "Encryption Levels not compatible",
801 NULL);
802 sipe_backend_media_reject(backend_media, FALSE);
803 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
804 _("Unable to establish a call"),
805 _("Encryption settings of peer are incompatible with ours."));
806 } else {
807 send_response_with_session_description(call_private, 200, "OK");
810 return TRUE;
813 static void
814 stream_initialized_cb(struct sipe_media_call *call,
815 struct sipe_media_stream *stream)
817 if (call_initialized(call)) {
818 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
820 if (sipe_backend_media_is_initiator(call, stream)) {
821 sipe_invite_call(call_private->sipe_private,
822 process_invite_call_response);
823 } else if (call_private->smsg) {
824 struct sdpmsg *smsg = call_private->smsg;
825 call_private->smsg = NULL;
827 apply_remote_message(call_private, smsg);
828 send_invite_response_if_ready(call_private);
829 sdpmsg_free(smsg);
834 static void phone_state_publish(struct sipe_core_private *sipe_private)
836 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
837 sipe_ocs2007_phone_state_publish(sipe_private);
838 } else {
839 // TODO: OCS 2005 support. Is anyone still using it at all?
843 static void
844 stream_end_cb(struct sipe_media_call* call, struct sipe_media_stream* stream)
846 remove_stream(call, SIPE_MEDIA_STREAM_PRIVATE);
849 static void
850 media_end_cb(struct sipe_media_call *call)
852 g_return_if_fail(call);
854 SIPE_MEDIA_CALL_PRIVATE->sipe_private->media_call = NULL;
855 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
856 sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE);
859 static void
860 call_accept_cb(struct sipe_media_call *call, gboolean local)
862 if (local) {
863 send_invite_response_if_ready(SIPE_MEDIA_CALL_PRIVATE);
865 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
868 static void
869 call_reject_cb(struct sipe_media_call *call, gboolean local)
871 if (local) {
872 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
873 sip_transport_response(call_private->sipe_private,
874 call_private->invitation,
875 603, "Decline", NULL);
879 static gboolean
880 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
881 struct transaction *trans);
883 static void call_hold_cb(struct sipe_media_call *call,
884 gboolean local,
885 SIPE_UNUSED_PARAMETER gboolean state)
887 if (local)
888 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
889 sipe_media_send_ack);
892 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
894 if (local) {
895 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
896 struct sip_session *session;
897 session = sipe_session_find_call(call_private->sipe_private,
898 call->with);
900 if (session) {
901 sipe_session_close(call_private->sipe_private, session);
906 static void
907 error_cb(struct sipe_media_call *call, gchar *message)
909 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
910 struct sipe_core_private *sipe_private = call_private->sipe_private;
911 gboolean initiator = sipe_backend_media_is_initiator(call, NULL);
912 gboolean accepted = sipe_backend_media_accepted(call->backend_private);
914 gchar *title = g_strdup_printf("Call with %s failed", call->with);
915 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message);
916 g_free(title);
918 if (!initiator && !accepted) {
919 sip_transport_response(sipe_private,
920 call_private->invitation,
921 488, "Not Acceptable Here", NULL);
924 sipe_backend_media_hangup(call->backend_private, initiator || accepted);
927 static struct sipe_media_call_private *
928 sipe_media_call_new(struct sipe_core_private *sipe_private,
929 const gchar* with, gboolean initiator, SipeIceVersion ice_version)
931 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
932 gchar *cname;
934 call_private->sipe_private = sipe_private;
936 cname = g_strdup(sipe_private->contact + 1);
937 cname[strlen(cname) - 1] = '\0';
939 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
940 SIPE_MEDIA_CALL,
941 with,
942 initiator);
943 sipe_backend_media_set_cname(call_private->public.backend_private, cname);
945 call_private->ice_version = ice_version;
946 call_private->encryption_compatible = TRUE;
948 call_private->public.stream_initialized_cb = stream_initialized_cb;
949 call_private->public.stream_end_cb = stream_end_cb;
950 call_private->public.media_end_cb = media_end_cb;
951 call_private->public.call_accept_cb = call_accept_cb;
952 call_private->public.call_reject_cb = call_reject_cb;
953 call_private->public.call_hold_cb = call_hold_cb;
954 call_private->public.call_hangup_cb = call_hangup_cb;
955 call_private->public.error_cb = error_cb;
957 g_free(cname);
959 return call_private;
962 void sipe_media_hangup(struct sipe_media_call_private *call_private)
964 if (call_private) {
965 sipe_backend_media_hangup(call_private->public.backend_private,
966 FALSE);
970 static gboolean
971 sipe_media_stream_add(struct sipe_core_private *sipe_private, const gchar *id,
972 const gchar *with, SipeMediaType type,
973 SipeIceVersion ice_version, gboolean initiator)
975 struct sipe_media_call_private *call_private = sipe_private->media_call;
976 struct sipe_media_stream_private *stream_private;
977 struct sipe_backend_media_stream *backend_stream;
978 struct sipe_backend_media_relays *backend_media_relays;
980 backend_media_relays = sipe_backend_media_relays_convert(
981 sipe_private->media_relays,
982 sipe_private->media_relay_username,
983 sipe_private->media_relay_password);
985 backend_stream = sipe_backend_media_add_stream(SIPE_MEDIA_CALL,
986 id, with, type, ice_version,
987 initiator, backend_media_relays);
989 sipe_backend_media_relays_free(backend_media_relays);
991 if (!backend_stream) {
992 return FALSE;
995 stream_private = g_new0(struct sipe_media_stream_private, 1);
996 SIPE_MEDIA_STREAM->id = g_strdup(id);
997 SIPE_MEDIA_STREAM->backend_private = backend_stream;
999 #ifdef HAVE_SRTP
1001 int i;
1002 stream_private->encryption_key = g_new0(guchar, SIPE_SRTP_KEY_LEN);
1003 for (i = 0; i != SIPE_SRTP_KEY_LEN; ++i) {
1004 stream_private->encryption_key[i] = rand() & 0xff;
1006 stream_private->encryption_key_id = 1;
1008 #endif
1010 sipe_private->media_call->streams =
1011 g_slist_append(sipe_private->media_call->streams,
1012 stream_private);
1014 return TRUE;
1017 static void
1018 sipe_media_initiate_call(struct sipe_core_private *sipe_private,
1019 const char *with, SipeIceVersion ice_version,
1020 gboolean with_video)
1022 struct sip_session *session;
1023 struct sip_dialog *dialog;
1025 if (sipe_private->media_call)
1026 return;
1028 sipe_private->media_call = sipe_media_call_new(sipe_private, with, TRUE,
1029 ice_version);
1031 session = sipe_session_add_call(sipe_private, with);
1032 dialog = sipe_dialog_add(session);
1033 dialog->callid = gencallid();
1034 dialog->with = g_strdup(session->with);
1035 dialog->ourtag = gentag();
1037 sipe_private->media_call->public.with = g_strdup(session->with);
1039 if (!sipe_media_stream_add(sipe_private, "audio", with, SIPE_MEDIA_AUDIO,
1040 sipe_private->media_call->ice_version,
1041 TRUE)) {
1042 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
1043 _("Error occured"),
1044 _("Error creating audio stream"));
1045 sipe_media_hangup(sipe_private->media_call);
1046 return;
1049 if (with_video &&
1050 !sipe_media_stream_add(sipe_private, "video", with, SIPE_MEDIA_VIDEO,
1051 sipe_private->media_call->ice_version,
1052 TRUE)) {
1053 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
1054 _("Error occured"),
1055 _("Error creating video stream"));
1056 sipe_media_hangup(sipe_private->media_call);
1057 return;
1060 // Processing continues in stream_initialized_cb
1063 void
1064 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
1065 const char *with,
1066 gboolean with_video)
1068 sipe_media_initiate_call(SIPE_CORE_PRIVATE, with,
1069 SIPE_ICE_RFC_5245, with_video);
1072 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
1073 struct sipe_chat_session *chat_session)
1075 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1076 struct sip_session *session;
1077 struct sip_dialog *dialog;
1078 SipeIceVersion ice_version;
1079 gchar **parts;
1080 gchar *av_uri;
1082 if (!sipe_conf_supports_mcu_type(sipe_private, "audio-video")) {
1083 sipe_backend_notify_error(sipe_public, _("Join conference call"),
1084 _("Conference calls are not supported on this server."));
1085 return;
1088 session = sipe_session_find_chat(sipe_private, chat_session);
1090 if (sipe_private->media_call || !session)
1091 return;
1093 session->is_call = TRUE;
1095 parts = g_strsplit(chat_session->id, "app:conf:focus:", 2);
1096 av_uri = g_strjoinv("app:conf:audio-video:", parts);
1097 g_strfreev(parts);
1099 ice_version = SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) ? SIPE_ICE_RFC_5245 :
1100 SIPE_ICE_DRAFT_6;
1102 sipe_private->media_call = sipe_media_call_new(sipe_private, av_uri,
1103 TRUE, ice_version);
1105 session = sipe_session_add_call(sipe_private, av_uri);
1106 dialog = sipe_dialog_add(session);
1107 dialog->callid = gencallid();
1108 dialog->with = g_strdup(session->with);
1109 dialog->ourtag = gentag();
1111 g_free(av_uri);
1113 sipe_private->media_call->public.with = g_strdup(session->with);
1115 if (!sipe_media_stream_add(sipe_private, "audio", dialog->with,
1116 SIPE_MEDIA_AUDIO,
1117 sipe_private->media_call->ice_version,
1118 TRUE)) {
1119 sipe_backend_notify_error(sipe_public,
1120 _("Error occurred"),
1121 _("Error creating audio stream"));
1122 sipe_media_hangup(sipe_private->media_call);
1123 sipe_private->media_call = NULL;
1126 // Processing continues in stream_initialized_cb
1129 gboolean sipe_core_media_in_call(struct sipe_core_public *sipe_public)
1131 if (sipe_public) {
1132 return SIPE_CORE_PRIVATE->media_call != NULL;
1134 return FALSE;
1137 static gboolean phone_number_is_valid(const gchar *phone_number)
1139 if (!phone_number || sipe_strequal(phone_number, "")) {
1140 return FALSE;
1143 if (*phone_number == '+') {
1144 ++phone_number;
1147 while (*phone_number != '\0') {
1148 if (!g_ascii_isdigit(*phone_number)) {
1149 return FALSE;
1151 ++phone_number;
1154 return TRUE;
1157 void sipe_core_media_phone_call(struct sipe_core_public *sipe_public,
1158 const gchar *phone_number)
1160 g_return_if_fail(sipe_public);
1162 if (phone_number_is_valid(phone_number)) {
1163 gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone",
1164 phone_number, sipe_public->sip_domain);
1166 sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE);
1168 g_free(phone_uri);
1169 } else {
1170 sipe_backend_notify_error(sipe_public,
1171 _("Unable to establish a call"),
1172 _("Invalid phone number"));
1176 void sipe_core_media_test_call(struct sipe_core_public *sipe_public)
1178 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1179 if (!sipe_private->test_call_bot_uri) {
1180 sipe_backend_notify_error(sipe_public,
1181 _("Unable to establish a call"),
1182 _("Audio Test Service is not available."));
1183 return;
1186 sipe_core_media_initiate_call(sipe_public,
1187 sipe_private->test_call_bot_uri, FALSE);
1190 void
1191 process_incoming_invite_call(struct sipe_core_private *sipe_private,
1192 struct sipmsg *msg)
1194 struct sipe_media_call_private *call_private = sipe_private->media_call;
1195 struct sdpmsg *smsg;
1196 gboolean has_new_media = FALSE;
1197 GSList *i;
1199 if (call_private) {
1200 char *self;
1202 if (!is_media_session_msg(call_private, msg)) {
1203 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
1204 return;
1207 self = sip_uri_self(sipe_private);
1208 if (sipe_strequal(SIPE_MEDIA_CALL->with, self)) {
1209 g_free(self);
1210 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
1211 return;
1213 g_free(self);
1216 smsg = sdpmsg_parse_msg(msg->body);
1217 if (!smsg) {
1218 sip_transport_response(sipe_private, msg,
1219 488, "Not Acceptable Here", NULL);
1220 sipe_media_hangup(call_private);
1221 return;
1224 if (!call_private) {
1225 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
1226 struct sip_session *session;
1228 call_private = sipe_media_call_new(sipe_private, with, FALSE, smsg->ice_version);
1229 session = sipe_session_add_call(sipe_private, with);
1230 sipe_media_dialog_init(session, msg);
1232 SIPE_MEDIA_CALL->with = g_strdup(session->with);
1233 sipe_private->media_call = call_private;
1234 g_free(with);
1237 if (call_private->invitation)
1238 sipmsg_free(call_private->invitation);
1239 call_private->invitation = sipmsg_copy(msg);
1241 // Create any new media streams
1242 for (i = smsg->media; i; i = i->next) {
1243 struct sdpmedia *media = i->data;
1244 gchar *id = media->name;
1245 SipeMediaType type;
1247 if ( media->port != 0
1248 && !sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, id)) {
1249 gchar *with;
1251 if (sipe_strequal(id, "audio"))
1252 type = SIPE_MEDIA_AUDIO;
1253 else if (sipe_strequal(id, "video"))
1254 type = SIPE_MEDIA_VIDEO;
1255 else
1256 continue;
1258 with = parse_from(sipmsg_find_header(msg, "From"));
1259 sipe_media_stream_add(sipe_private, id, with, type,
1260 smsg->ice_version, FALSE);
1261 has_new_media = TRUE;
1262 g_free(with);
1266 if (has_new_media) {
1267 sdpmsg_free(call_private->smsg);
1268 call_private->smsg = smsg;
1269 sip_transport_response(sipe_private, call_private->invitation,
1270 180, "Ringing", NULL);
1271 // Processing continues in stream_initialized_cb
1272 } else {
1273 apply_remote_message(call_private, smsg);
1274 send_response_with_session_description(call_private, 200, "OK");
1276 sdpmsg_free(smsg);
1280 void process_incoming_cancel_call(struct sipe_core_private *sipe_private,
1281 struct sipmsg *msg)
1283 struct sipe_media_call_private *call_private = sipe_private->media_call;
1285 // We respond to the CANCEL request with 200 OK response and
1286 // with 487 Request Terminated to the remote INVITE in progress.
1287 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1289 if (call_private->invitation) {
1290 sip_transport_response(sipe_private, call_private->invitation,
1291 487, "Request Terminated", NULL);
1294 sipe_media_hangup(call_private);
1297 static gboolean
1298 sipe_media_send_ack(struct sipe_core_private *sipe_private,
1299 struct sipmsg *msg,
1300 struct transaction *trans)
1302 struct sipe_media_call_private *call_private = sipe_private->media_call;
1303 struct sip_session *session;
1304 struct sip_dialog *dialog;
1305 int tmp_cseq;
1307 if (!is_media_session_msg(call_private, msg))
1308 return FALSE;
1310 session = sipe_session_find_call(sipe_private, SIPE_MEDIA_CALL->with);
1311 dialog = session->dialogs->data;
1312 if (!dialog)
1313 return FALSE;
1315 tmp_cseq = dialog->cseq;
1317 dialog->cseq = sip_transaction_cseq(trans) - 1;
1318 sip_transport_ack(sipe_private, dialog);
1319 dialog->cseq = tmp_cseq;
1321 dialog->outgoing_invite = NULL;
1323 return TRUE;
1326 static gboolean
1327 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
1328 struct sipmsg *msg,
1329 struct transaction *trans)
1331 if (!sipe_media_send_ack(sipe_private, msg, trans))
1332 return FALSE;
1334 sipe_backend_media_accept(sipe_private->media_call->public.backend_private,
1335 FALSE);
1337 return TRUE;
1340 static void
1341 reinvite_on_candidate_pair_cb(struct sipe_core_public *sipe_public)
1343 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1344 struct sipe_media_call_private *media_call = sipe_private->media_call;
1345 GSList *streams;
1347 if (!media_call)
1348 return;
1350 streams = media_call->streams;
1352 for (; streams; streams = streams->next) {
1353 struct sipe_media_stream *s = streams->data;
1354 GList *remote_candidates = sipe_backend_media_get_active_remote_candidates(&media_call->public, s);
1355 guint components = g_list_length(remote_candidates);
1357 sipe_media_candidate_list_free(remote_candidates);
1359 // We must have candidates for both (RTP + RTCP) components ready
1360 if (components < 2) {
1361 sipe_schedule_mseconds(sipe_private,
1362 "<+media-reinvite-on-candidate-pair>",
1363 NULL,
1364 500,
1365 (sipe_schedule_action) reinvite_on_candidate_pair_cb,
1366 NULL);
1367 return;
1371 sipe_invite_call(sipe_private, sipe_media_send_final_ack);
1374 static gboolean
1375 maybe_retry_call_with_ice_version(struct sipe_core_private *sipe_private,
1376 SipeIceVersion ice_version,
1377 struct transaction *trans)
1379 struct sipe_media_call_private *call_private = sipe_private->media_call;
1381 if (call_private->ice_version != ice_version &&
1382 sip_transaction_cseq(trans) == 1) {
1383 gchar *with = g_strdup(SIPE_MEDIA_CALL->with);
1384 gboolean with_video = sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, "video") != NULL;
1386 sipe_media_hangup(call_private);
1387 SIPE_DEBUG_INFO("Retrying call with ICEv%d.",
1388 ice_version == SIPE_ICE_DRAFT_6 ? 6 : 19);
1389 sipe_media_initiate_call(sipe_private, with, ice_version,
1390 with_video);
1392 g_free(with);
1393 return TRUE;
1396 return FALSE;
1399 static gboolean
1400 process_invite_call_response(struct sipe_core_private *sipe_private,
1401 struct sipmsg *msg,
1402 struct transaction *trans)
1404 const gchar *with;
1405 struct sipe_media_call_private *call_private = sipe_private->media_call;
1406 struct sip_session *session;
1407 struct sip_dialog *dialog;
1408 struct sdpmsg *smsg;
1410 if (!is_media_session_msg(call_private, msg))
1411 return FALSE;
1413 session = sipe_session_find_call(sipe_private, SIPE_MEDIA_CALL->with);
1414 dialog = session->dialogs->data;
1416 with = dialog->with;
1418 dialog->outgoing_invite = NULL;
1420 if (msg->response >= 400) {
1421 // Call rejected by remote peer or an error occurred
1422 const gchar *title;
1423 GString *desc = g_string_new("");
1424 gboolean append_responsestr = FALSE;
1426 switch (msg->response) {
1427 case 480: {
1428 title = _("User unavailable");
1430 if (sipmsg_parse_warning(msg, NULL) == 391) {
1431 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
1432 } else
1433 g_string_append_printf(desc, _("User %s is not available"), with);
1434 break;
1436 case 603:
1437 case 605:
1438 title = _("Call rejected");
1439 g_string_append_printf(desc, _("User %s rejected call"), with);
1440 break;
1441 case 415:
1442 // OCS/Lync really sends response string with 'Mutipart' typo.
1443 if (sipe_strequal(msg->responsestr, "Mutipart mime in content type not supported by Archiving CDR service") &&
1444 maybe_retry_call_with_ice_version(sipe_private, SIPE_ICE_DRAFT_6, trans)) {
1445 return TRUE;
1447 title = _("Unsupported media type");
1448 break;
1449 case 488: {
1450 /* Check for incompatible encryption levels error.
1452 * MS Lync 2010:
1453 * 488 Not Acceptable Here
1454 * ms-client-diagnostics: 52017;reason="Encryption levels dont match"
1456 * older clients (and SIPE itself):
1457 * 488 Encryption Levels not compatible
1459 const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics");
1460 SipeIceVersion retry_ice_version = SIPE_ICE_DRAFT_6;
1462 if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") ||
1463 (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) {
1464 title = _("Unable to establish a call");
1465 g_string_append(desc, _("Encryption settings of peer are incompatible with ours."));
1466 break;
1469 /* Check if this is failed conference using
1470 * ICEv6 with reason "Error parsing SDP" and
1471 * retry using ICEv19. */
1472 ms_diag = sipmsg_find_header(msg, "ms-diagnostics");
1473 if (ms_diag && g_str_has_prefix(ms_diag, "7008;")) {
1474 retry_ice_version = SIPE_ICE_RFC_5245;
1477 if (maybe_retry_call_with_ice_version(sipe_private, retry_ice_version, trans)) {
1478 return TRUE;
1480 // Break intentionally omitted
1482 default:
1483 title = _("Error occured");
1484 g_string_append(desc, _("Unable to establish a call"));
1485 append_responsestr = TRUE;
1486 break;
1489 if (append_responsestr) {
1490 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1492 g_string_append_printf(desc, "\n%d %s",
1493 msg->response, msg->responsestr);
1494 if (reason) {
1495 g_string_append_printf(desc, "\n\n%s", reason);
1496 g_free(reason);
1500 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str);
1501 g_string_free(desc, TRUE);
1503 sipe_media_send_ack(sipe_private, msg, trans);
1504 sipe_media_hangup(call_private);
1506 return TRUE;
1509 sipe_dialog_parse(dialog, msg, TRUE);
1510 smsg = sdpmsg_parse_msg(msg->body);
1511 if (!smsg) {
1512 sip_transport_response(sipe_private, msg,
1513 488, "Not Acceptable Here", NULL);
1514 sipe_media_hangup(call_private);
1515 return FALSE;
1518 apply_remote_message(call_private, smsg);
1519 sdpmsg_free(smsg);
1521 sipe_media_send_ack(sipe_private, msg, trans);
1522 reinvite_on_candidate_pair_cb(SIPE_CORE_PUBLIC);
1524 return TRUE;
1527 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
1528 struct sipmsg *msg)
1530 if (call_private) {
1531 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
1532 struct sip_session *session;
1534 session = sipe_session_find_call(call_private->sipe_private,
1535 SIPE_MEDIA_CALL->with);
1536 if (session) {
1537 struct sip_dialog *dialog = session->dialogs->data;
1538 return sipe_strequal(dialog->callid, callid);
1541 return FALSE;
1544 void sipe_media_handle_going_offline(struct sipe_media_call_private *call_private)
1546 struct sipe_backend_media *backend_private;
1548 backend_private = call_private->public.backend_private;
1550 if (!sipe_backend_media_is_initiator(SIPE_MEDIA_CALL, NULL) &&
1551 !sipe_backend_media_accepted(backend_private)) {
1552 sip_transport_response(call_private->sipe_private,
1553 call_private->invitation,
1554 480, "Temporarily Unavailable", NULL);
1555 } else {
1556 struct sip_session *session;
1558 session = sipe_session_find_call(call_private->sipe_private,
1559 SIPE_MEDIA_CALL->with);
1560 if (session)
1561 sipe_session_close(call_private->sipe_private, session);
1564 sipe_media_hangup(call_private);
1567 gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private)
1569 return g_strstr_len(SIPE_MEDIA_CALL->with, -1, "app:conf:audio-video:") != NULL;
1572 static void
1573 sipe_media_relay_free(struct sipe_media_relay *relay)
1575 g_free(relay->hostname);
1576 if (relay->dns_query)
1577 sipe_backend_dns_query_cancel(relay->dns_query);
1578 g_free(relay);
1581 void
1582 sipe_media_relay_list_free(GSList *list)
1584 for (; list; list = g_slist_delete_link(list, list))
1585 sipe_media_relay_free(list->data);
1588 static void
1589 relay_ip_resolved_cb(struct sipe_media_relay* relay,
1590 const gchar *ip, SIPE_UNUSED_PARAMETER guint port)
1592 gchar *hostname = relay->hostname;
1593 relay->dns_query = NULL;
1595 if (ip && port) {
1596 relay->hostname = g_strdup(ip);
1597 SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip);
1598 } else {
1599 relay->hostname = NULL;
1600 SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname);
1603 g_free(hostname);
1606 static gboolean
1607 process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private,
1608 struct sipmsg *msg,
1609 SIPE_UNUSED_PARAMETER struct transaction *trans)
1611 g_free(sipe_private->media_relay_username);
1612 g_free(sipe_private->media_relay_password);
1613 sipe_media_relay_list_free(sipe_private->media_relays);
1614 sipe_private->media_relay_username = NULL;
1615 sipe_private->media_relay_password = NULL;
1616 sipe_private->media_relays = NULL;
1618 if (msg->response >= 400) {
1619 SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. "
1620 "Failed to obtain A/V Edge credentials.");
1621 return FALSE;
1624 if (msg->response == 200) {
1625 sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
1627 if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) {
1628 const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials");
1629 const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList");
1630 const sipe_xml *item;
1631 GSList *relays = NULL;
1633 item = sipe_xml_child(xn_credentials, "username");
1634 sipe_private->media_relay_username = sipe_xml_data(item);
1635 item = sipe_xml_child(xn_credentials, "password");
1636 sipe_private->media_relay_password = sipe_xml_data(item);
1638 for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) {
1639 struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1);
1640 const sipe_xml *node;
1641 gchar *tmp;
1643 node = sipe_xml_child(item, "hostName");
1644 relay->hostname = sipe_xml_data(node);
1646 node = sipe_xml_child(item, "udpPort");
1647 if (node) {
1648 relay->udp_port = atoi(tmp = sipe_xml_data(node));
1649 g_free(tmp);
1652 node = sipe_xml_child(item, "tcpPort");
1653 if (node) {
1654 relay->tcp_port = atoi(tmp = sipe_xml_data(node));
1655 g_free(tmp);
1658 relays = g_slist_append(relays, relay);
1660 relay->dns_query = sipe_backend_dns_query_a(
1661 SIPE_CORE_PUBLIC,
1662 relay->hostname,
1663 relay->udp_port,
1664 (sipe_dns_resolved_cb) relay_ip_resolved_cb,
1665 relay);
1667 SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d",
1668 relay->hostname,
1669 relay->tcp_port, relay->udp_port);
1672 sipe_private->media_relays = relays;
1675 sipe_xml_free(xn_response);
1678 return TRUE;
1681 void
1682 sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private)
1684 // TODO: re-request credentials after duration expires?
1685 static const char CRED_REQUEST_XML[] =
1686 "<request requestID=\"%d\" "
1687 "from=\"%s\" "
1688 "version=\"1.0\" "
1689 "to=\"%s\" "
1690 "xmlns=\"http://schemas.microsoft.com/2006/09/sip/mrasp\" "
1691 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
1692 "<credentialsRequest credentialsRequestID=\"%d\">"
1693 "<identity>%s</identity>"
1694 "<location>%s</location>"
1695 "<duration>480</duration>"
1696 "</credentialsRequest>"
1697 "</request>";
1699 int request_id = rand();
1700 gchar *self;
1701 gchar *body;
1703 if (!sipe_private->mras_uri)
1704 return;
1706 self = sip_uri_self(sipe_private);
1708 body = g_strdup_printf(
1709 CRED_REQUEST_XML,
1710 request_id,
1711 self,
1712 sipe_private->mras_uri,
1713 request_id,
1714 self,
1715 SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet");
1716 g_free(self);
1718 sip_transport_service(sipe_private,
1719 sipe_private->mras_uri,
1720 "Content-Type: application/msrtc-media-relay-auth+xml\r\n",
1721 body,
1722 process_get_av_edge_credentials_response);
1724 g_free(body);
1728 Local Variables:
1729 mode: c
1730 c-file-style: "bsd"
1731 indent-tabs-mode: t
1732 tab-width: 8
1733 End: