Fix #258: V&V call gets rejected when IPv6 is enabled (III)
[siplcs.git] / src / core / sipe-media.c
bloba477ce77e1ffc73108a827aadd00f59461a8e504
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-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;
57 gchar *with;
59 struct sipmsg *invitation;
60 SipeIceVersion ice_version;
61 gboolean encryption_compatible;
63 struct sdpmsg *smsg;
64 GSList *failed_media;
66 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
67 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
69 static void sipe_media_codec_list_free(GList *codecs)
71 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
72 sipe_backend_codec_free(codecs->data);
75 static void sipe_media_candidate_list_free(GList *candidates)
77 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
78 sipe_backend_candidate_free(candidates->data);
81 static void
82 sipe_media_call_free(struct sipe_media_call_private *call_private)
84 if (call_private) {
85 struct sip_session *session;
86 sipe_backend_media_free(call_private->public.backend_private);
88 session = sipe_session_find_call(call_private->sipe_private,
89 call_private->with);
90 if (session)
91 sipe_session_remove(call_private->sipe_private, session);
93 if (call_private->invitation)
94 sipmsg_free(call_private->invitation);
96 sdpmsg_free(call_private->smsg);
97 sipe_utils_slist_free_full(call_private->failed_media,
98 (GDestroyNotify)sdpmedia_free);
99 g_free(call_private->with);
100 g_free(call_private);
104 static gint
105 candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
107 int cmp = sipe_strcompare(c1->foundation, c2->foundation);
108 if (cmp == 0) {
109 cmp = sipe_strcompare(c1->username, c2->username);
110 if (cmp == 0)
111 cmp = c1->component - c2->component;
114 return cmp;
117 static GSList *
118 backend_candidates_to_sdpcandidate(GList *candidates)
120 GSList *result = NULL;
121 GList *i;
123 for (i = candidates; i; i = i->next) {
124 struct sipe_backend_candidate *candidate = i->data;
125 struct sdpcandidate *c;
127 gchar *ip = sipe_backend_candidate_get_ip(candidate);
128 gchar *base_ip = sipe_backend_candidate_get_base_ip(candidate);
129 if (is_empty(ip) || strchr(ip, ':') ||
130 (base_ip && strchr(base_ip, ':'))) {
131 /* Ignore IPv6 candidates. */
132 g_free(ip);
133 g_free(base_ip);
134 continue;
137 c = g_new(struct sdpcandidate, 1);
138 c->foundation = sipe_backend_candidate_get_foundation(candidate);
139 c->component = sipe_backend_candidate_get_component_type(candidate);
140 c->type = sipe_backend_candidate_get_type(candidate);
141 c->protocol = sipe_backend_candidate_get_protocol(candidate);
142 c->ip = ip;
143 c->port = sipe_backend_candidate_get_port(candidate);
144 c->base_ip = base_ip;
145 c->base_port = sipe_backend_candidate_get_base_port(candidate);
146 c->priority = sipe_backend_candidate_get_priority(candidate);
147 c->username = sipe_backend_candidate_get_username(candidate);
148 c->password = sipe_backend_candidate_get_password(candidate);
150 result = g_slist_insert_sorted(result, c,
151 (GCompareFunc)candidate_sort_cb);
154 return result;
157 static void
158 get_stream_ip_and_ports(GSList *candidates,
159 gchar **ip, guint *rtp_port, guint *rtcp_port,
160 SipeCandidateType type)
162 *ip = 0;
163 *rtp_port = 0;
164 *rtcp_port = 0;
166 for (; candidates; candidates = candidates->next) {
167 struct sdpcandidate *candidate = candidates->data;
169 if (type == SIPE_CANDIDATE_TYPE_ANY || candidate->type == type) {
170 if (!*ip) {
171 *ip = g_strdup(candidate->ip);
172 } else if (!sipe_strequal(*ip, candidate->ip)) {
173 continue;
176 if (candidate->component == SIPE_COMPONENT_RTP) {
177 *rtp_port = candidate->port;
178 } else if (candidate->component == SIPE_COMPONENT_RTCP)
179 *rtcp_port = candidate->port;
182 if (*rtp_port != 0 && *rtcp_port != 0)
183 return;
187 static gint
188 sdpcodec_compare(gconstpointer a, gconstpointer b)
190 return ((const struct sdpcodec *)a)->id -
191 ((const struct sdpcodec *)b)->id;
194 static void
195 fill_zero_tcp_act_ports_from_tcp_pass(GSList *candidates)
197 GSList *i;
198 GHashTable *ip_to_port = g_hash_table_new(g_str_hash, g_str_equal);
200 for (i = candidates; i; i = i->next) {
201 struct sdpcandidate *c = i->data;
202 GSList *j;
204 if (c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE &&
205 c->type == SIPE_CANDIDATE_TYPE_HOST) {
206 g_hash_table_insert(ip_to_port, c->ip,
207 &c->port);
210 if (c->protocol != SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
211 continue;
214 for (j = candidates; j; j = j->next) {
215 struct sdpcandidate *passive = j->data;
216 if (passive->protocol != SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
217 c->type != passive->type) {
218 continue;
221 if (sipe_strequal(c->ip, passive->ip) &&
222 sipe_strequal(c->base_ip, passive->base_ip)) {
223 if (c->port == 0) {
224 c->port = passive->port;
227 if (c->base_port == 0) {
228 c->base_port = passive->base_port;
230 break;
235 /* Fill base ports of all TCP relay candidates using what we have
236 * collected from host candidates. */
237 for (i = candidates; i; i = i->next) {
238 struct sdpcandidate *c = i->data;
239 if (c->type == SIPE_CANDIDATE_TYPE_RELAY && c->base_port == 0) {
240 c->base_port = *(guint*)g_hash_table_lookup(ip_to_port,
241 c->base_ip);
245 g_hash_table_destroy(ip_to_port);
248 static struct sdpmedia *
249 backend_stream_to_sdpmedia(struct sipe_backend_media *backend_media,
250 struct sipe_backend_stream *backend_stream)
252 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
253 GList *codecs = sipe_backend_get_local_codecs(backend_media,
254 backend_stream);
255 guint rtcp_port = 0;
256 SipeMediaType type;
257 GSList *attributes = NULL;
258 GList *candidates;
259 GList *i;
261 media->name = g_strdup(sipe_backend_stream_get_id(backend_stream));
263 if (sipe_strequal(media->name, "audio"))
264 type = SIPE_MEDIA_AUDIO;
265 else if (sipe_strequal(media->name, "video"))
266 type = SIPE_MEDIA_VIDEO;
267 else {
268 // TODO: incompatible media, should not happen here
269 g_free(media->name);
270 g_free(media);
271 sipe_media_codec_list_free(codecs);
272 return(NULL);
275 // Process codecs
276 for (i = codecs; i; i = i->next) {
277 struct sipe_backend_codec *codec = i->data;
278 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
279 GList *params;
281 c->id = sipe_backend_codec_get_id(codec);
282 c->name = sipe_backend_codec_get_name(codec);
283 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
284 c->type = type;
286 params = sipe_backend_codec_get_optional_parameters(codec);
287 for (; params; params = params->next) {
288 struct sipnameval *param = params->data;
289 struct sipnameval *copy = g_new0(struct sipnameval, 1);
291 copy->name = g_strdup(param->name);
292 copy->value = g_strdup(param->value);
294 c->parameters = g_slist_append(c->parameters, copy);
297 /* Buggy(?) codecs may report non-unique id (a.k.a. payload
298 * type) that must not appear in SDP messages we send. Thus,
299 * let's ignore any codec having the same id as one we already
300 * have in the converted list. */
301 media->codecs = sipe_utils_slist_insert_unique_sorted(
302 media->codecs, c, sdpcodec_compare,
303 (GDestroyNotify)sdpcodec_free);
306 sipe_media_codec_list_free(codecs);
308 // Process local candidates
309 // If we have established candidate pairs, send them in SDP response.
310 // Otherwise send all available local candidates.
311 candidates = sipe_backend_media_get_active_local_candidates(backend_media,
312 backend_stream);
313 if (!candidates)
314 candidates = sipe_backend_get_local_candidates(backend_media,
315 backend_stream);
317 media->candidates = backend_candidates_to_sdpcandidate(candidates);
318 fill_zero_tcp_act_ports_from_tcp_pass(media->candidates);
320 sipe_media_candidate_list_free(candidates);
322 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
323 &rtcp_port, SIPE_CANDIDATE_TYPE_HOST);
324 // No usable HOST candidates, use any candidate
325 if (media->ip == NULL && media->candidates) {
326 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
327 &rtcp_port, SIPE_CANDIDATE_TYPE_ANY);
330 if (sipe_backend_stream_is_held(backend_stream))
331 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
333 if (rtcp_port) {
334 gchar *tmp = g_strdup_printf("%u", rtcp_port);
335 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
336 g_free(tmp);
339 attributes = sipe_utils_nameval_add(attributes, "encryption", "rejected");
341 media->attributes = attributes;
343 // Process remote candidates
344 candidates = sipe_backend_media_get_active_remote_candidates(backend_media,
345 backend_stream);
346 media->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
347 sipe_media_candidate_list_free(candidates);
349 return media;
352 static struct sdpmsg *
353 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
355 struct sipe_backend_media *backend_media = call_private->public.backend_private;
356 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
357 GSList *streams = sipe_backend_media_get_streams(backend_media);
359 for (; streams; streams = streams->next) {
360 struct sdpmedia *media = backend_stream_to_sdpmedia(backend_media, streams->data);
361 if (media) {
362 msg->media = g_slist_append(msg->media, media);
364 if (msg->ip == NULL)
365 msg->ip = g_strdup(media->ip);
369 msg->media = g_slist_concat(msg->media, call_private->failed_media);
370 call_private->failed_media = NULL;
372 msg->ice_version = call_private->ice_version;
374 return msg;
377 static void
378 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
380 gchar *hdr;
381 gchar *contact;
382 gchar *p_preferred_identity = NULL;
383 gchar *body;
384 struct sipe_media_call_private *call_private = sipe_private->media_call;
385 struct sip_session *session;
386 struct sip_dialog *dialog;
387 struct sdpmsg *msg;
388 gboolean add_2007_fallback = FALSE;
390 session = sipe_session_find_call(sipe_private, call_private->with);
391 dialog = session->dialogs->data;
392 add_2007_fallback = dialog->cseq == 0 &&
393 call_private->ice_version == SIPE_ICE_RFC_5245 &&
394 !sipe_strequal(call_private->with, sipe_private->test_call_bot_uri);
396 contact = get_contact(sipe_private);
398 if (sipe_private->uc_line_uri) {
399 gchar *self = sip_uri_self(sipe_private);
400 p_preferred_identity = g_strdup_printf(
401 "P-Preferred-Identity: <%s>, <%s>\r\n",
402 self, sipe_private->uc_line_uri);
403 g_free(self);
406 hdr = g_strdup_printf(
407 "ms-keep-alive: UAC;hop-hop=yes\r\n"
408 "Contact: %s\r\n"
409 "%s"
410 "Content-Type: %s\r\n",
411 contact,
412 p_preferred_identity ? p_preferred_identity : "",
413 add_2007_fallback ?
414 "multipart/alternative;boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\""
415 : "application/sdp");
416 g_free(contact);
417 g_free(p_preferred_identity);
419 msg = sipe_media_to_sdpmsg(call_private);
420 body = sdpmsg_to_string(msg);
422 if (add_2007_fallback) {
423 gchar *tmp;
424 tmp = g_strdup_printf(
425 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
426 "Content-Type: application/sdp\r\n"
427 "Content-Transfer-Encoding: 7bit\r\n"
428 "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n"
429 "\r\n"
430 "o=- 0 0 IN IP4 %s\r\n"
431 "s=session\r\n"
432 "c=IN IP4 %s\r\n"
433 "m=audio 0 RTP/AVP\r\n"
434 "\r\n"
435 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
436 "Content-Type: application/sdp\r\n"
437 "Content-Transfer-Encoding: 7bit\r\n"
438 "Content-Disposition: session; handling=optional\r\n"
439 "\r\n"
440 "%s"
441 "\r\n"
442 "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n",
443 msg->ip, msg->ip, body);
444 g_free(body);
445 body = tmp;
448 sdpmsg_free(msg);
450 dialog->outgoing_invite = sip_transport_invite(sipe_private,
451 hdr,
452 body,
453 dialog,
454 tc);
456 g_free(body);
457 g_free(hdr);
460 static struct sip_dialog *
461 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
463 gchar *newTag = gentag();
464 const gchar *oldHeader;
465 gchar *newHeader;
466 struct sip_dialog *dialog;
468 oldHeader = sipmsg_find_header(msg, "To");
469 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
470 sipmsg_remove_header_now(msg, "To");
471 sipmsg_add_header_now(msg, "To", newHeader);
472 g_free(newHeader);
474 dialog = sipe_dialog_add(session);
475 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
476 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
477 sipe_dialog_parse(dialog, msg, FALSE);
479 return dialog;
482 static void
483 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
485 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
486 gchar *body = sdpmsg_to_string(msg);
487 sdpmsg_free(msg);
488 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
489 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
490 g_free(body);
493 static gboolean
494 encryption_levels_compatible(struct sdpmsg *msg)
496 GSList *i;
498 for (i = msg->media; i; i = i->next) {
499 const gchar *enc_level;
500 struct sdpmedia *m = i->data;
502 enc_level = sipe_utils_nameval_find(m->attributes, "encryption");
504 // Decline call if peer requires encryption as we don't support it yet.
505 if (sipe_strequal(enc_level, "required"))
506 return FALSE;
509 return TRUE;
512 static gboolean
513 process_invite_call_response(struct sipe_core_private *sipe_private,
514 struct sipmsg *msg,
515 struct transaction *trans);
517 static gboolean
518 update_remote_media(struct sipe_media_call_private* call_private,
519 struct sdpmedia *media)
521 struct sipe_backend_media *backend_media = SIPE_MEDIA_CALL->backend_private;
522 struct sipe_backend_stream *backend_stream;
523 GList *backend_candidates = NULL;
524 GList *backend_codecs = NULL;
525 GSList *i;
526 gboolean result = TRUE;
528 backend_stream = sipe_backend_media_get_stream_by_id(backend_media,
529 media->name);
530 if (media->port == 0) {
531 if (backend_stream)
532 sipe_backend_media_remove_stream(backend_media, backend_stream);
533 return TRUE;
536 if (!backend_stream)
537 return FALSE;
539 for (i = media->codecs; i; i = i->next) {
540 struct sdpcodec *c = i->data;
541 struct sipe_backend_codec *codec;
542 GSList *j;
544 codec = sipe_backend_codec_new(c->id,
545 c->name,
546 c->type,
547 c->clock_rate);
549 for (j = c->parameters; j; j = j->next) {
550 struct sipnameval *attr = j->data;
552 sipe_backend_codec_add_optional_parameter(codec,
553 attr->name,
554 attr->value);
557 backend_codecs = g_list_append(backend_codecs, codec);
560 result = sipe_backend_set_remote_codecs(backend_media,
561 backend_stream,
562 backend_codecs);
563 sipe_media_codec_list_free(backend_codecs);
565 if (result == FALSE) {
566 sipe_backend_media_remove_stream(backend_media, backend_stream);
567 return FALSE;
570 for (i = media->candidates; i; i = i->next) {
571 struct sdpcandidate *c = i->data;
572 struct sipe_backend_candidate *candidate;
573 candidate = sipe_backend_candidate_new(c->foundation,
574 c->component,
575 c->type,
576 c->protocol,
577 c->ip,
578 c->port,
579 c->username,
580 c->password);
581 sipe_backend_candidate_set_priority(candidate, c->priority);
583 backend_candidates = g_list_append(backend_candidates, candidate);
586 sipe_backend_media_add_remote_candidates(backend_media,
587 backend_stream,
588 backend_candidates);
589 sipe_media_candidate_list_free(backend_candidates);
591 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
592 sipe_backend_stream_hold(backend_media, backend_stream, FALSE);
593 } else if (sipe_backend_stream_is_held(backend_stream)) {
594 sipe_backend_stream_unhold(backend_media, backend_stream, FALSE);
597 return TRUE;
600 static void
601 apply_remote_message(struct sipe_media_call_private* call_private,
602 struct sdpmsg* msg)
604 GSList *i;
606 sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free);
607 call_private->failed_media = NULL;
609 for (i = msg->media; i; i = i->next) {
610 struct sdpmedia *media = i->data;
611 if (!update_remote_media(call_private, media)) {
612 media->port = 0;
613 call_private->failed_media =
614 g_slist_append(call_private->failed_media, media);
618 /* We need to keep failed medias until response is sent, remove them
619 * from sdpmsg that is to be freed. */
620 for (i = call_private->failed_media; i; i = i->next) {
621 msg->media = g_slist_remove(msg->media, i->data);
624 call_private->encryption_compatible = encryption_levels_compatible(msg);
627 static gboolean
628 call_initialized(struct sipe_media_call *call)
630 GSList *streams =
631 sipe_backend_media_get_streams(call->backend_private);
633 for (; streams; streams = streams->next) {
634 if (!sipe_backend_stream_initialized(call->backend_private,
635 streams->data)) {
636 return FALSE;
640 return TRUE;
643 // Sends an invite response when the call is accepted and local candidates were
644 // prepared, otherwise does nothing. If error response is sent, call_private is
645 // disposed before function returns. Returns true when response was sent.
646 static gboolean
647 send_invite_response_if_ready(struct sipe_media_call_private *call_private)
649 struct sipe_backend_media *backend_media;
651 backend_media = call_private->public.backend_private;
653 if (!sipe_backend_media_accepted(backend_media) ||
654 !call_initialized(&call_private->public))
655 return FALSE;
657 if (!call_private->encryption_compatible) {
658 struct sipe_core_private *sipe_private = call_private->sipe_private;
660 sipmsg_add_header(call_private->invitation, "Warning",
661 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
662 sip_transport_response(sipe_private,
663 call_private->invitation,
664 488, "Encryption Levels not compatible",
665 NULL);
666 sipe_backend_media_reject(backend_media, FALSE);
667 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
668 _("Unable to establish a call"),
669 _("Encryption settings of peer are incompatible with ours."));
670 } else {
671 send_response_with_session_description(call_private, 200, "OK");
674 return TRUE;
677 static void
678 stream_initialized_cb(struct sipe_media_call *call,
679 struct sipe_backend_stream *stream)
681 if (call_initialized(call)) {
682 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
683 struct sipe_backend_media *backend_private = call->backend_private;
685 if (sipe_backend_media_is_initiator(backend_private, stream)) {
686 sipe_invite_call(call_private->sipe_private,
687 process_invite_call_response);
688 } else if (call_private->smsg) {
689 struct sdpmsg *smsg = call_private->smsg;
690 call_private->smsg = NULL;
692 apply_remote_message(call_private, smsg);
693 send_invite_response_if_ready(call_private);
694 sdpmsg_free(smsg);
699 static void phone_state_publish(struct sipe_core_private *sipe_private)
701 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
702 sipe_ocs2007_phone_state_publish(sipe_private);
703 } else {
704 // TODO: OCS 2005 support. Is anyone still using it at all?
708 static void
709 media_end_cb(struct sipe_media_call *call)
711 g_return_if_fail(call);
713 SIPE_MEDIA_CALL_PRIVATE->sipe_private->media_call = NULL;
714 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
715 sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE);
718 static void
719 call_accept_cb(struct sipe_media_call *call, gboolean local)
721 if (local) {
722 send_invite_response_if_ready(SIPE_MEDIA_CALL_PRIVATE);
724 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
727 static void
728 call_reject_cb(struct sipe_media_call *call, gboolean local)
730 if (local) {
731 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
732 sip_transport_response(call_private->sipe_private,
733 call_private->invitation,
734 603, "Decline", NULL);
738 static gboolean
739 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
740 struct transaction *trans);
742 static void call_hold_cb(struct sipe_media_call *call,
743 gboolean local,
744 SIPE_UNUSED_PARAMETER gboolean state)
746 if (local)
747 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
748 sipe_media_send_ack);
751 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
753 if (local) {
754 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
755 struct sip_session *session;
756 session = sipe_session_find_call(call_private->sipe_private,
757 call_private->with);
759 if (session) {
760 sipe_session_close(call_private->sipe_private, session);
765 static void
766 error_cb(struct sipe_media_call *call, gchar *message)
768 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
769 struct sipe_core_private *sipe_private = call_private->sipe_private;
770 gboolean initiator = sipe_backend_media_is_initiator(call->backend_private, NULL);
771 gboolean accepted = sipe_backend_media_accepted(call->backend_private);
773 gchar *title = g_strdup_printf("Call with %s failed", call_private->with);
774 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message);
775 g_free(title);
777 if (!initiator && !accepted) {
778 sip_transport_response(sipe_private,
779 call_private->invitation,
780 488, "Not Acceptable Here", NULL);
783 sipe_backend_media_hangup(call->backend_private, initiator || accepted);
786 static struct sipe_media_call_private *
787 sipe_media_call_new(struct sipe_core_private *sipe_private,
788 const gchar* with, gboolean initiator, SipeIceVersion ice_version)
790 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
791 gchar *cname;
793 call_private->sipe_private = sipe_private;
795 cname = g_strdup(sipe_private->contact + 1);
796 cname[strlen(cname) - 1] = '\0';
798 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
799 SIPE_MEDIA_CALL,
800 with,
801 initiator);
802 sipe_backend_media_set_cname(call_private->public.backend_private, cname);
804 call_private->ice_version = ice_version;
805 call_private->encryption_compatible = TRUE;
807 call_private->public.stream_initialized_cb = stream_initialized_cb;
808 call_private->public.media_end_cb = media_end_cb;
809 call_private->public.call_accept_cb = call_accept_cb;
810 call_private->public.call_reject_cb = call_reject_cb;
811 call_private->public.call_hold_cb = call_hold_cb;
812 call_private->public.call_hangup_cb = call_hangup_cb;
813 call_private->public.error_cb = error_cb;
815 g_free(cname);
817 return call_private;
820 void sipe_media_hangup(struct sipe_media_call_private *call_private)
822 if (call_private) {
823 sipe_backend_media_hangup(call_private->public.backend_private,
824 FALSE);
828 static struct sipe_backend_stream *
829 sipe_media_stream_add(struct sipe_core_private *sipe_private, const gchar *id,
830 const gchar *with, SipeMediaType type,
831 SipeIceVersion ice_version, gboolean initiator)
833 struct sipe_backend_stream *stream;
834 struct sipe_backend_media_relays *backend_media_relays;
835 struct sipe_backend_media *backend_media;
837 backend_media_relays = sipe_backend_media_relays_convert(
838 sipe_private->media_relays,
839 sipe_private->media_relay_username,
840 sipe_private->media_relay_password);
842 backend_media = sipe_private->media_call->public.backend_private;
843 stream = sipe_backend_media_add_stream(backend_media, id, with, type,
844 ice_version, initiator,
845 backend_media_relays);
847 sipe_backend_media_relays_free(backend_media_relays);
849 return stream;
852 static void
853 sipe_media_initiate_call(struct sipe_core_private *sipe_private,
854 const char *with, SipeIceVersion ice_version,
855 gboolean with_video)
857 struct sip_session *session;
858 struct sip_dialog *dialog;
860 if (sipe_private->media_call)
861 return;
863 sipe_private->media_call = sipe_media_call_new(sipe_private, with, TRUE,
864 ice_version);
866 session = sipe_session_add_call(sipe_private, with);
867 dialog = sipe_dialog_add(session);
868 dialog->callid = gencallid();
869 dialog->with = g_strdup(session->with);
870 dialog->ourtag = gentag();
872 sipe_private->media_call->with = g_strdup(session->with);
874 if (!sipe_media_stream_add(sipe_private, "audio", with, SIPE_MEDIA_AUDIO,
875 sipe_private->media_call->ice_version,
876 TRUE)) {
877 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
878 _("Error occured"),
879 _("Error creating audio stream"));
880 sipe_media_hangup(sipe_private->media_call);
881 return;
884 if (with_video &&
885 !sipe_media_stream_add(sipe_private, "video", with, SIPE_MEDIA_VIDEO,
886 sipe_private->media_call->ice_version,
887 TRUE)) {
888 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
889 _("Error occured"),
890 _("Error creating video stream"));
891 sipe_media_hangup(sipe_private->media_call);
892 return;
895 // Processing continues in stream_initialized_cb
898 void
899 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
900 const char *with,
901 gboolean with_video)
903 sipe_media_initiate_call(SIPE_CORE_PRIVATE, with,
904 SIPE_ICE_RFC_5245, with_video);
907 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
908 struct sipe_chat_session *chat_session)
910 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
911 struct sip_session *session;
912 struct sip_dialog *dialog;
913 SipeIceVersion ice_version;
914 gchar **parts;
915 gchar *av_uri;
917 if (!sipe_conf_supports_mcu_type(sipe_private, "audio-video")) {
918 sipe_backend_notify_error(sipe_public, _("Join conference call"),
919 _("Conference calls are not supported on this server."));
920 return;
923 session = sipe_session_find_chat(sipe_private, chat_session);
925 if (sipe_private->media_call || !session)
926 return;
928 session->is_call = TRUE;
930 parts = g_strsplit(chat_session->id, "app:conf:focus:", 2);
931 av_uri = g_strjoinv("app:conf:audio-video:", parts);
932 g_strfreev(parts);
934 ice_version = SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) ? SIPE_ICE_RFC_5245 :
935 SIPE_ICE_DRAFT_6;
937 sipe_private->media_call = sipe_media_call_new(sipe_private, av_uri,
938 TRUE, ice_version);
940 session = sipe_session_add_call(sipe_private, av_uri);
941 dialog = sipe_dialog_add(session);
942 dialog->callid = gencallid();
943 dialog->with = g_strdup(session->with);
944 dialog->ourtag = gentag();
946 g_free(av_uri);
948 sipe_private->media_call->with = g_strdup(session->with);
950 if (!sipe_media_stream_add(sipe_private, "audio", dialog->with,
951 SIPE_MEDIA_AUDIO,
952 sipe_private->media_call->ice_version,
953 TRUE)) {
954 sipe_backend_notify_error(sipe_public,
955 _("Error occured"),
956 _("Error creating audio stream"));
957 sipe_media_hangup(sipe_private->media_call);
958 sipe_private->media_call = NULL;
961 // Processing continues in stream_initialized_cb
964 gboolean sipe_core_media_in_call(struct sipe_core_public *sipe_public)
966 if (sipe_public) {
967 return SIPE_CORE_PRIVATE->media_call != NULL;
969 return FALSE;
972 static gboolean phone_number_is_valid(const gchar *phone_number)
974 if (!phone_number || sipe_strequal(phone_number, "")) {
975 return FALSE;
978 if (*phone_number == '+') {
979 ++phone_number;
982 while (*phone_number != '\0') {
983 if (!g_ascii_isdigit(*phone_number)) {
984 return FALSE;
986 ++phone_number;
989 return TRUE;
992 void sipe_core_media_phone_call(struct sipe_core_public *sipe_public,
993 const gchar *phone_number)
995 g_return_if_fail(sipe_public);
997 if (phone_number_is_valid(phone_number)) {
998 gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone",
999 phone_number, sipe_public->sip_domain);
1001 sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE);
1003 g_free(phone_uri);
1004 } else {
1005 sipe_backend_notify_error(sipe_public,
1006 _("Unable to establish a call"),
1007 _("Invalid phone number"));
1011 void sipe_core_media_test_call(struct sipe_core_public *sipe_public)
1013 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1014 if (!sipe_private->test_call_bot_uri) {
1015 sipe_backend_notify_error(sipe_public,
1016 _("Unable to establish a call"),
1017 _("Audio Test Service is not available."));
1018 return;
1021 sipe_core_media_initiate_call(sipe_public,
1022 sipe_private->test_call_bot_uri, FALSE);
1025 void
1026 process_incoming_invite_call(struct sipe_core_private *sipe_private,
1027 struct sipmsg *msg)
1029 struct sipe_media_call_private *call_private = sipe_private->media_call;
1030 struct sipe_backend_media *backend_media;
1031 struct sdpmsg *smsg;
1032 gboolean has_new_media = FALSE;
1033 GSList *i;
1035 if (call_private) {
1036 char *self;
1038 if (!is_media_session_msg(call_private, msg)) {
1039 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
1040 return;
1043 self = sip_uri_self(sipe_private);
1044 if (sipe_strequal(call_private->with, self)) {
1045 g_free(self);
1046 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
1047 return;
1049 g_free(self);
1052 smsg = sdpmsg_parse_msg(msg->body);
1053 if (!smsg) {
1054 sip_transport_response(sipe_private, msg,
1055 488, "Not Acceptable Here", NULL);
1056 sipe_media_hangup(call_private);
1057 return;
1060 if (!call_private) {
1061 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
1062 struct sip_session *session;
1064 call_private = sipe_media_call_new(sipe_private, with, FALSE, smsg->ice_version);
1065 session = sipe_session_add_call(sipe_private, with);
1066 sipe_media_dialog_init(session, msg);
1068 call_private->with = g_strdup(session->with);
1069 sipe_private->media_call = call_private;
1070 g_free(with);
1073 backend_media = call_private->public.backend_private;
1075 if (call_private->invitation)
1076 sipmsg_free(call_private->invitation);
1077 call_private->invitation = sipmsg_copy(msg);
1079 // Create any new media streams
1080 for (i = smsg->media; i; i = i->next) {
1081 struct sdpmedia *media = i->data;
1082 gchar *id = media->name;
1083 SipeMediaType type;
1085 if ( media->port != 0
1086 && !sipe_backend_media_get_stream_by_id(backend_media, id)) {
1087 gchar *with;
1089 if (sipe_strequal(id, "audio"))
1090 type = SIPE_MEDIA_AUDIO;
1091 else if (sipe_strequal(id, "video"))
1092 type = SIPE_MEDIA_VIDEO;
1093 else
1094 continue;
1096 with = parse_from(sipmsg_find_header(msg, "From"));
1097 sipe_media_stream_add(sipe_private, id, with, type,
1098 smsg->ice_version, FALSE);
1099 has_new_media = TRUE;
1100 g_free(with);
1104 if (has_new_media) {
1105 sdpmsg_free(call_private->smsg);
1106 call_private->smsg = smsg;
1107 sip_transport_response(sipe_private, call_private->invitation,
1108 180, "Ringing", NULL);
1109 // Processing continues in stream_initialized_cb
1110 } else {
1111 apply_remote_message(call_private, smsg);
1112 send_response_with_session_description(call_private, 200, "OK");
1114 sdpmsg_free(smsg);
1118 void process_incoming_cancel_call(struct sipe_core_private *sipe_private,
1119 struct sipmsg *msg)
1121 struct sipe_media_call_private *call_private = sipe_private->media_call;
1123 // We respond to the CANCEL request with 200 OK response and
1124 // with 487 Request Terminated to the remote INVITE in progress.
1125 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1127 if (call_private->invitation) {
1128 sip_transport_response(sipe_private, call_private->invitation,
1129 487, "Request Terminated", NULL);
1132 sipe_media_hangup(call_private);
1135 static gboolean
1136 sipe_media_send_ack(struct sipe_core_private *sipe_private,
1137 struct sipmsg *msg,
1138 struct transaction *trans)
1140 struct sipe_media_call_private *call_private = sipe_private->media_call;
1141 struct sip_session *session;
1142 struct sip_dialog *dialog;
1143 int tmp_cseq;
1145 if (!is_media_session_msg(call_private, msg))
1146 return FALSE;
1148 session = sipe_session_find_call(sipe_private, call_private->with);
1149 dialog = session->dialogs->data;
1150 if (!dialog)
1151 return FALSE;
1153 tmp_cseq = dialog->cseq;
1155 dialog->cseq = sip_transaction_cseq(trans) - 1;
1156 sip_transport_ack(sipe_private, dialog);
1157 dialog->cseq = tmp_cseq;
1159 dialog->outgoing_invite = NULL;
1161 return TRUE;
1164 static gboolean
1165 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
1166 struct sipmsg *msg,
1167 struct transaction *trans)
1169 if (!sipe_media_send_ack(sipe_private, msg, trans))
1170 return FALSE;
1172 sipe_backend_media_accept(sipe_private->media_call->public.backend_private,
1173 FALSE);
1175 return TRUE;
1178 static void
1179 reinvite_on_candidate_pair_cb(struct sipe_core_public *sipe_public)
1181 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1182 struct sipe_media_call_private *media_call = sipe_private->media_call;
1183 struct sipe_backend_media *backend_media;
1184 GSList *streams;
1186 if (!media_call)
1187 return;
1189 backend_media = media_call->public.backend_private;
1190 streams = sipe_backend_media_get_streams(backend_media);
1192 for (; streams; streams = streams->next) {
1193 struct sipe_backend_stream *s = streams->data;
1194 GList *remote_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, s);
1195 guint components = g_list_length(remote_candidates);
1197 sipe_media_candidate_list_free(remote_candidates);
1199 // We must have candidates for both (RTP + RTCP) components ready
1200 if (components < 2) {
1201 sipe_schedule_mseconds(sipe_private,
1202 "<+media-reinvite-on-candidate-pair>",
1203 NULL,
1204 500,
1205 (sipe_schedule_action) reinvite_on_candidate_pair_cb,
1206 NULL);
1207 return;
1211 sipe_invite_call(sipe_private, sipe_media_send_final_ack);
1214 static gboolean
1215 maybe_retry_call_with_ice_version(struct sipe_core_private *sipe_private,
1216 SipeIceVersion ice_version,
1217 struct transaction *trans)
1219 struct sipe_media_call_private *call_private = sipe_private->media_call;
1221 if (call_private->ice_version != ice_version &&
1222 sip_transaction_cseq(trans) == 1) {
1223 gchar *with = g_strdup(call_private->with);
1224 struct sipe_backend_media *backend_private = call_private->public.backend_private;
1225 gboolean with_video = sipe_backend_media_get_stream_by_id(backend_private, "video") != NULL;
1227 sipe_media_hangup(call_private);
1228 SIPE_DEBUG_INFO("Retrying call with ICEv%d.",
1229 ice_version == SIPE_ICE_DRAFT_6 ? 6 : 19);
1230 sipe_media_initiate_call(sipe_private, with, ice_version,
1231 with_video);
1233 g_free(with);
1234 return TRUE;
1237 return FALSE;
1240 static gboolean
1241 process_invite_call_response(struct sipe_core_private *sipe_private,
1242 struct sipmsg *msg,
1243 struct transaction *trans)
1245 const gchar *with;
1246 struct sipe_media_call_private *call_private = sipe_private->media_call;
1247 struct sip_session *session;
1248 struct sip_dialog *dialog;
1249 struct sdpmsg *smsg;
1251 if (!is_media_session_msg(call_private, msg))
1252 return FALSE;
1254 session = sipe_session_find_call(sipe_private, call_private->with);
1255 dialog = session->dialogs->data;
1257 with = dialog->with;
1259 dialog->outgoing_invite = NULL;
1261 if (msg->response >= 400) {
1262 // Call rejected by remote peer or an error occurred
1263 const gchar *title;
1264 GString *desc = g_string_new("");
1265 gboolean append_responsestr = FALSE;
1267 switch (msg->response) {
1268 case 480: {
1269 title = _("User unavailable");
1271 if (sipmsg_parse_warning(msg, NULL) == 391) {
1272 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
1273 } else
1274 g_string_append_printf(desc, _("User %s is not available"), with);
1275 break;
1277 case 603:
1278 case 605:
1279 title = _("Call rejected");
1280 g_string_append_printf(desc, _("User %s rejected call"), with);
1281 break;
1282 case 415:
1283 // OCS/Lync really sends response string with 'Mutipart' typo.
1284 if (sipe_strequal(msg->responsestr, "Mutipart mime in content type not supported by Archiving CDR service") &&
1285 maybe_retry_call_with_ice_version(sipe_private, SIPE_ICE_DRAFT_6, trans)) {
1286 return TRUE;
1288 title = _("Unsupported media type");
1289 break;
1290 case 488: {
1291 /* Check for incompatible encryption levels error.
1293 * MS Lync 2010:
1294 * 488 Not Acceptable Here
1295 * ms-client-diagnostics: 52017;reason="Encryption levels dont match"
1297 * older clients (and SIPE itself):
1298 * 488 Encryption Levels not compatible
1300 const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics");
1301 SipeIceVersion retry_ice_version = SIPE_ICE_DRAFT_6;
1303 if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") ||
1304 (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) {
1305 title = _("Unable to establish a call");
1306 g_string_append(desc, _("Encryption settings of peer are incompatible with ours."));
1307 break;
1310 /* Check if this is failed conference using
1311 * ICEv6 with reason "Error parsing SDP" and
1312 * retry using ICEv19. */
1313 ms_diag = sipmsg_find_header(msg, "ms-diagnostics");
1314 if (ms_diag && g_str_has_prefix(ms_diag, "7008;")) {
1315 retry_ice_version = SIPE_ICE_RFC_5245;
1318 if (maybe_retry_call_with_ice_version(sipe_private, retry_ice_version, trans)) {
1319 return TRUE;
1321 // Break intentionally omitted
1323 default:
1324 title = _("Error occured");
1325 g_string_append(desc, _("Unable to establish a call"));
1326 append_responsestr = TRUE;
1327 break;
1330 if (append_responsestr) {
1331 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1333 g_string_append_printf(desc, "\n%d %s",
1334 msg->response, msg->responsestr);
1335 if (reason) {
1336 g_string_append_printf(desc, "\n\n%s", reason);
1337 g_free(reason);
1341 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str);
1342 g_string_free(desc, TRUE);
1344 sipe_media_send_ack(sipe_private, msg, trans);
1345 sipe_media_hangup(call_private);
1347 return TRUE;
1350 sipe_dialog_parse(dialog, msg, TRUE);
1351 smsg = sdpmsg_parse_msg(msg->body);
1352 if (!smsg) {
1353 sip_transport_response(sipe_private, msg,
1354 488, "Not Acceptable Here", NULL);
1355 sipe_media_hangup(call_private);
1356 return FALSE;
1359 apply_remote_message(call_private, smsg);
1360 sdpmsg_free(smsg);
1362 sipe_media_send_ack(sipe_private, msg, trans);
1363 reinvite_on_candidate_pair_cb(SIPE_CORE_PUBLIC);
1365 return TRUE;
1368 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
1369 struct sipmsg *msg)
1371 if (call_private) {
1372 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
1373 struct sip_session *session;
1375 session = sipe_session_find_call(call_private->sipe_private,
1376 call_private->with);
1377 if (session) {
1378 struct sip_dialog *dialog = session->dialogs->data;
1379 return sipe_strequal(dialog->callid, callid);
1382 return FALSE;
1385 void sipe_media_handle_going_offline(struct sipe_media_call_private *call_private)
1387 struct sipe_backend_media *backend_private;
1389 backend_private = call_private->public.backend_private;
1391 if ( !sipe_backend_media_is_initiator(backend_private, NULL)
1392 && !sipe_backend_media_accepted(backend_private)) {
1393 sip_transport_response(call_private->sipe_private,
1394 call_private->invitation,
1395 480, "Temporarily Unavailable", NULL);
1396 } else {
1397 struct sip_session *session;
1399 session = sipe_session_find_call(call_private->sipe_private,
1400 call_private->with);
1401 if (session)
1402 sipe_session_close(call_private->sipe_private, session);
1405 sipe_media_hangup(call_private);
1408 gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private)
1410 return g_strstr_len(call_private->with, -1, "app:conf:audio-video:") != NULL;
1413 static void
1414 sipe_media_relay_free(struct sipe_media_relay *relay)
1416 g_free(relay->hostname);
1417 if (relay->dns_query)
1418 sipe_backend_dns_query_cancel(relay->dns_query);
1419 g_free(relay);
1422 void
1423 sipe_media_relay_list_free(GSList *list)
1425 for (; list; list = g_slist_delete_link(list, list))
1426 sipe_media_relay_free(list->data);
1429 static void
1430 relay_ip_resolved_cb(struct sipe_media_relay* relay,
1431 const gchar *ip, SIPE_UNUSED_PARAMETER guint port)
1433 gchar *hostname = relay->hostname;
1434 relay->dns_query = NULL;
1436 if (ip && port) {
1437 relay->hostname = g_strdup(ip);
1438 SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip);
1439 } else {
1440 relay->hostname = NULL;
1441 SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname);
1444 g_free(hostname);
1447 static gboolean
1448 process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private,
1449 struct sipmsg *msg,
1450 SIPE_UNUSED_PARAMETER struct transaction *trans)
1452 g_free(sipe_private->media_relay_username);
1453 g_free(sipe_private->media_relay_password);
1454 sipe_media_relay_list_free(sipe_private->media_relays);
1455 sipe_private->media_relay_username = NULL;
1456 sipe_private->media_relay_password = NULL;
1457 sipe_private->media_relays = NULL;
1459 if (msg->response >= 400) {
1460 SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. "
1461 "Failed to obtain A/V Edge credentials.");
1462 return FALSE;
1465 if (msg->response == 200) {
1466 sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
1468 if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) {
1469 const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials");
1470 const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList");
1471 const sipe_xml *item;
1472 GSList *relays = NULL;
1474 item = sipe_xml_child(xn_credentials, "username");
1475 sipe_private->media_relay_username = sipe_xml_data(item);
1476 item = sipe_xml_child(xn_credentials, "password");
1477 sipe_private->media_relay_password = sipe_xml_data(item);
1479 for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) {
1480 struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1);
1481 const sipe_xml *node;
1482 gchar *tmp;
1484 node = sipe_xml_child(item, "hostName");
1485 relay->hostname = sipe_xml_data(node);
1487 node = sipe_xml_child(item, "udpPort");
1488 if (node) {
1489 relay->udp_port = atoi(tmp = sipe_xml_data(node));
1490 g_free(tmp);
1493 node = sipe_xml_child(item, "tcpPort");
1494 if (node) {
1495 relay->tcp_port = atoi(tmp = sipe_xml_data(node));
1496 g_free(tmp);
1499 relays = g_slist_append(relays, relay);
1501 relay->dns_query = sipe_backend_dns_query_a(
1502 SIPE_CORE_PUBLIC,
1503 relay->hostname,
1504 relay->udp_port,
1505 (sipe_dns_resolved_cb) relay_ip_resolved_cb,
1506 relay);
1508 SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d",
1509 relay->hostname,
1510 relay->tcp_port, relay->udp_port);
1513 sipe_private->media_relays = relays;
1516 sipe_xml_free(xn_response);
1519 return TRUE;
1522 void
1523 sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private)
1525 // TODO: re-request credentials after duration expires?
1526 static const char CRED_REQUEST_XML[] =
1527 "<request requestID=\"%d\" "
1528 "from=\"%s\" "
1529 "version=\"1.0\" "
1530 "to=\"%s\" "
1531 "xmlns=\"http://schemas.microsoft.com/2006/09/sip/mrasp\" "
1532 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
1533 "<credentialsRequest credentialsRequestID=\"%d\">"
1534 "<identity>%s</identity>"
1535 "<location>%s</location>"
1536 "<duration>480</duration>"
1537 "</credentialsRequest>"
1538 "</request>";
1540 int request_id = rand();
1541 gchar *self;
1542 gchar *body;
1544 if (!sipe_private->mras_uri)
1545 return;
1547 self = sip_uri_self(sipe_private);
1549 body = g_strdup_printf(
1550 CRED_REQUEST_XML,
1551 request_id,
1552 self,
1553 sipe_private->mras_uri,
1554 request_id,
1555 self,
1556 SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet");
1557 g_free(self);
1559 sip_transport_service(sipe_private,
1560 sipe_private->mras_uri,
1561 "Content-Type: application/msrtc-media-relay-auth+xml\r\n",
1562 body,
1563 process_get_av_edge_credentials_response);
1565 g_free(body);
1569 Local Variables:
1570 mode: c
1571 c-file-style: "bsd"
1572 indent-tabs-mode: t
1573 tab-width: 8
1574 End: