Fix #258: V&V call gets rejected when IPv6 is enabled
[siplcs.git] / src / core / sipe-media.c
blob7aa893ed9328a2d68682203fd08437e78a491218
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 = g_new(struct sdpcandidate, 1);
127 c->foundation = sipe_backend_candidate_get_foundation(candidate);
128 c->component = sipe_backend_candidate_get_component_type(candidate);
129 c->type = sipe_backend_candidate_get_type(candidate);
130 c->protocol = sipe_backend_candidate_get_protocol(candidate);
131 c->ip = sipe_backend_candidate_get_ip(candidate);
132 c->port = sipe_backend_candidate_get_port(candidate);
133 c->base_ip = sipe_backend_candidate_get_base_ip(candidate);
134 c->base_port = sipe_backend_candidate_get_base_port(candidate);
135 c->priority = sipe_backend_candidate_get_priority(candidate);
136 c->username = sipe_backend_candidate_get_username(candidate);
137 c->password = sipe_backend_candidate_get_password(candidate);
139 result = g_slist_insert_sorted(result, c,
140 (GCompareFunc)candidate_sort_cb);
143 return result;
146 static void
147 get_stream_ip_and_ports(GSList *candidates,
148 gchar **ip, guint *rtp_port, guint *rtcp_port,
149 SipeCandidateType type)
151 *ip = 0;
152 *rtp_port = 0;
153 *rtcp_port = 0;
155 for (; candidates; candidates = candidates->next) {
156 struct sdpcandidate *candidate = candidates->data;
158 if (type == SIPE_CANDIDATE_TYPE_ANY || candidate->type == type) {
159 if (!(*ip ||
160 is_empty(candidate->ip) ||
161 /* reject IPv6 addresses */
162 strchr(candidate->ip, ':'))) {
163 *ip = g_strdup(candidate->ip);
164 } else if (!(*ip && sipe_strequal(*ip, candidate->ip))) {
165 continue;
168 if (candidate->component == SIPE_COMPONENT_RTP) {
169 *rtp_port = candidate->port;
170 } else if (candidate->component == SIPE_COMPONENT_RTCP)
171 *rtcp_port = candidate->port;
174 if (*rtp_port != 0 && *rtcp_port != 0)
175 return;
179 static gint
180 sdpcodec_compare(gconstpointer a, gconstpointer b)
182 return ((const struct sdpcodec *)a)->id -
183 ((const struct sdpcodec *)b)->id;
186 static void
187 fill_zero_tcp_act_ports_from_tcp_pass(GSList *candidates)
189 GSList *i;
190 GHashTable *ip_to_port = g_hash_table_new(g_str_hash, g_str_equal);
192 for (i = candidates; i; i = i->next) {
193 struct sdpcandidate *c = i->data;
194 GSList *j;
196 if (c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE &&
197 c->type == SIPE_CANDIDATE_TYPE_HOST) {
198 g_hash_table_insert(ip_to_port, c->ip,
199 &c->port);
202 if (c->protocol != SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
203 continue;
206 for (j = candidates; j; j = j->next) {
207 struct sdpcandidate *passive = j->data;
208 if (passive->protocol != SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
209 c->type != passive->type) {
210 continue;
213 if (sipe_strequal(c->ip, passive->ip) &&
214 sipe_strequal(c->base_ip, passive->base_ip)) {
215 if (c->port == 0) {
216 c->port = passive->port;
219 if (c->base_port == 0) {
220 c->base_port = passive->base_port;
222 break;
227 /* Fill base ports of all TCP relay candidates using what we have
228 * collected from host candidates. */
229 for (i = candidates; i; i = i->next) {
230 struct sdpcandidate *c = i->data;
231 if (c->type == SIPE_CANDIDATE_TYPE_RELAY && c->base_port == 0) {
232 c->base_port = *(guint*)g_hash_table_lookup(ip_to_port,
233 c->base_ip);
237 g_hash_table_destroy(ip_to_port);
240 static struct sdpmedia *
241 backend_stream_to_sdpmedia(struct sipe_backend_media *backend_media,
242 struct sipe_backend_stream *backend_stream)
244 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
245 GList *codecs = sipe_backend_get_local_codecs(backend_media,
246 backend_stream);
247 guint rtcp_port = 0;
248 SipeMediaType type;
249 GSList *attributes = NULL;
250 GList *candidates;
251 GList *i;
253 media->name = g_strdup(sipe_backend_stream_get_id(backend_stream));
255 if (sipe_strequal(media->name, "audio"))
256 type = SIPE_MEDIA_AUDIO;
257 else if (sipe_strequal(media->name, "video"))
258 type = SIPE_MEDIA_VIDEO;
259 else {
260 // TODO: incompatible media, should not happen here
261 g_free(media->name);
262 g_free(media);
263 sipe_media_codec_list_free(codecs);
264 return(NULL);
267 // Process codecs
268 for (i = codecs; i; i = i->next) {
269 struct sipe_backend_codec *codec = i->data;
270 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
271 GList *params;
273 c->id = sipe_backend_codec_get_id(codec);
274 c->name = sipe_backend_codec_get_name(codec);
275 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
276 c->type = type;
278 params = sipe_backend_codec_get_optional_parameters(codec);
279 for (; params; params = params->next) {
280 struct sipnameval *param = params->data;
281 struct sipnameval *copy = g_new0(struct sipnameval, 1);
283 copy->name = g_strdup(param->name);
284 copy->value = g_strdup(param->value);
286 c->parameters = g_slist_append(c->parameters, copy);
289 /* Buggy(?) codecs may report non-unique id (a.k.a. payload
290 * type) that must not appear in SDP messages we send. Thus,
291 * let's ignore any codec having the same id as one we already
292 * have in the converted list. */
293 media->codecs = sipe_utils_slist_insert_unique_sorted(
294 media->codecs, c, sdpcodec_compare,
295 (GDestroyNotify)sdpcodec_free);
298 sipe_media_codec_list_free(codecs);
300 // Process local candidates
301 // If we have established candidate pairs, send them in SDP response.
302 // Otherwise send all available local candidates.
303 candidates = sipe_backend_media_get_active_local_candidates(backend_media,
304 backend_stream);
305 if (!candidates)
306 candidates = sipe_backend_get_local_candidates(backend_media,
307 backend_stream);
309 media->candidates = backend_candidates_to_sdpcandidate(candidates);
310 fill_zero_tcp_act_ports_from_tcp_pass(media->candidates);
312 sipe_media_candidate_list_free(candidates);
314 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
315 &rtcp_port, SIPE_CANDIDATE_TYPE_HOST);
316 // No usable HOST candidates, use any candidate
317 if (media->ip == NULL && media->candidates) {
318 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
319 &rtcp_port, SIPE_CANDIDATE_TYPE_ANY);
322 if (sipe_backend_stream_is_held(backend_stream))
323 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
325 if (rtcp_port) {
326 gchar *tmp = g_strdup_printf("%u", rtcp_port);
327 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
328 g_free(tmp);
331 attributes = sipe_utils_nameval_add(attributes, "encryption", "rejected");
333 media->attributes = attributes;
335 // Process remote candidates
336 candidates = sipe_backend_media_get_active_remote_candidates(backend_media,
337 backend_stream);
338 media->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
339 sipe_media_candidate_list_free(candidates);
341 return media;
344 static struct sdpmsg *
345 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
347 struct sipe_backend_media *backend_media = call_private->public.backend_private;
348 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
349 GSList *streams = sipe_backend_media_get_streams(backend_media);
351 for (; streams; streams = streams->next) {
352 struct sdpmedia *media = backend_stream_to_sdpmedia(backend_media, streams->data);
353 if (media) {
354 msg->media = g_slist_append(msg->media, media);
356 if (msg->ip == NULL)
357 msg->ip = g_strdup(media->ip);
361 msg->media = g_slist_concat(msg->media, call_private->failed_media);
362 call_private->failed_media = NULL;
364 msg->ice_version = call_private->ice_version;
366 return msg;
369 static void
370 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
372 gchar *hdr;
373 gchar *contact;
374 gchar *p_preferred_identity = NULL;
375 gchar *body;
376 struct sipe_media_call_private *call_private = sipe_private->media_call;
377 struct sip_session *session;
378 struct sip_dialog *dialog;
379 struct sdpmsg *msg;
380 gboolean add_2007_fallback = FALSE;
382 session = sipe_session_find_call(sipe_private, call_private->with);
383 dialog = session->dialogs->data;
384 add_2007_fallback = dialog->cseq == 0 &&
385 call_private->ice_version == SIPE_ICE_RFC_5245 &&
386 !sipe_strequal(call_private->with, sipe_private->test_call_bot_uri);
388 contact = get_contact(sipe_private);
390 if (sipe_private->uc_line_uri) {
391 gchar *self = sip_uri_self(sipe_private);
392 p_preferred_identity = g_strdup_printf(
393 "P-Preferred-Identity: <%s>, <%s>\r\n",
394 self, sipe_private->uc_line_uri);
395 g_free(self);
398 hdr = g_strdup_printf(
399 "ms-keep-alive: UAC;hop-hop=yes\r\n"
400 "Contact: %s\r\n"
401 "%s"
402 "Content-Type: %s\r\n",
403 contact,
404 p_preferred_identity ? p_preferred_identity : "",
405 add_2007_fallback ?
406 "multipart/alternative;boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\""
407 : "application/sdp");
408 g_free(contact);
409 g_free(p_preferred_identity);
411 msg = sipe_media_to_sdpmsg(call_private);
412 body = sdpmsg_to_string(msg);
414 if (add_2007_fallback) {
415 gchar *tmp;
416 tmp = g_strdup_printf(
417 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
418 "Content-Type: application/sdp\r\n"
419 "Content-Transfer-Encoding: 7bit\r\n"
420 "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n"
421 "\r\n"
422 "o=- 0 0 IN IP4 %s\r\n"
423 "s=session\r\n"
424 "c=IN IP4 %s\r\n"
425 "m=audio 0 RTP/AVP\r\n"
426 "\r\n"
427 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
428 "Content-Type: application/sdp\r\n"
429 "Content-Transfer-Encoding: 7bit\r\n"
430 "Content-Disposition: session; handling=optional\r\n"
431 "\r\n"
432 "%s"
433 "\r\n"
434 "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n",
435 msg->ip, msg->ip, body);
436 g_free(body);
437 body = tmp;
440 sdpmsg_free(msg);
442 dialog->outgoing_invite = sip_transport_invite(sipe_private,
443 hdr,
444 body,
445 dialog,
446 tc);
448 g_free(body);
449 g_free(hdr);
452 static struct sip_dialog *
453 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
455 gchar *newTag = gentag();
456 const gchar *oldHeader;
457 gchar *newHeader;
458 struct sip_dialog *dialog;
460 oldHeader = sipmsg_find_header(msg, "To");
461 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
462 sipmsg_remove_header_now(msg, "To");
463 sipmsg_add_header_now(msg, "To", newHeader);
464 g_free(newHeader);
466 dialog = sipe_dialog_add(session);
467 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
468 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
469 sipe_dialog_parse(dialog, msg, FALSE);
471 return dialog;
474 static void
475 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
477 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
478 gchar *body = sdpmsg_to_string(msg);
479 sdpmsg_free(msg);
480 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
481 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
482 g_free(body);
485 static gboolean
486 encryption_levels_compatible(struct sdpmsg *msg)
488 GSList *i;
490 for (i = msg->media; i; i = i->next) {
491 const gchar *enc_level;
492 struct sdpmedia *m = i->data;
494 enc_level = sipe_utils_nameval_find(m->attributes, "encryption");
496 // Decline call if peer requires encryption as we don't support it yet.
497 if (sipe_strequal(enc_level, "required"))
498 return FALSE;
501 return TRUE;
504 static gboolean
505 process_invite_call_response(struct sipe_core_private *sipe_private,
506 struct sipmsg *msg,
507 struct transaction *trans);
509 static gboolean
510 update_remote_media(struct sipe_media_call_private* call_private,
511 struct sdpmedia *media)
513 struct sipe_backend_media *backend_media = SIPE_MEDIA_CALL->backend_private;
514 struct sipe_backend_stream *backend_stream;
515 GList *backend_candidates = NULL;
516 GList *backend_codecs = NULL;
517 GSList *i;
518 gboolean result = TRUE;
520 backend_stream = sipe_backend_media_get_stream_by_id(backend_media,
521 media->name);
522 if (media->port == 0) {
523 if (backend_stream)
524 sipe_backend_media_remove_stream(backend_media, backend_stream);
525 return TRUE;
528 if (!backend_stream)
529 return FALSE;
531 for (i = media->codecs; i; i = i->next) {
532 struct sdpcodec *c = i->data;
533 struct sipe_backend_codec *codec;
534 GSList *j;
536 codec = sipe_backend_codec_new(c->id,
537 c->name,
538 c->type,
539 c->clock_rate);
541 for (j = c->parameters; j; j = j->next) {
542 struct sipnameval *attr = j->data;
544 sipe_backend_codec_add_optional_parameter(codec,
545 attr->name,
546 attr->value);
549 backend_codecs = g_list_append(backend_codecs, codec);
552 result = sipe_backend_set_remote_codecs(backend_media,
553 backend_stream,
554 backend_codecs);
555 sipe_media_codec_list_free(backend_codecs);
557 if (result == FALSE) {
558 sipe_backend_media_remove_stream(backend_media, backend_stream);
559 return FALSE;
562 for (i = media->candidates; i; i = i->next) {
563 struct sdpcandidate *c = i->data;
564 struct sipe_backend_candidate *candidate;
565 candidate = sipe_backend_candidate_new(c->foundation,
566 c->component,
567 c->type,
568 c->protocol,
569 c->ip,
570 c->port,
571 c->username,
572 c->password);
573 sipe_backend_candidate_set_priority(candidate, c->priority);
575 backend_candidates = g_list_append(backend_candidates, candidate);
578 sipe_backend_media_add_remote_candidates(backend_media,
579 backend_stream,
580 backend_candidates);
581 sipe_media_candidate_list_free(backend_candidates);
583 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
584 sipe_backend_stream_hold(backend_media, backend_stream, FALSE);
585 } else if (sipe_backend_stream_is_held(backend_stream)) {
586 sipe_backend_stream_unhold(backend_media, backend_stream, FALSE);
589 return TRUE;
592 static void
593 apply_remote_message(struct sipe_media_call_private* call_private,
594 struct sdpmsg* msg)
596 GSList *i;
598 sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free);
599 call_private->failed_media = NULL;
601 for (i = msg->media; i; i = i->next) {
602 struct sdpmedia *media = i->data;
603 if (!update_remote_media(call_private, media)) {
604 media->port = 0;
605 call_private->failed_media =
606 g_slist_append(call_private->failed_media, media);
610 /* We need to keep failed medias until response is sent, remove them
611 * from sdpmsg that is to be freed. */
612 for (i = call_private->failed_media; i; i = i->next) {
613 msg->media = g_slist_remove(msg->media, i->data);
616 call_private->encryption_compatible = encryption_levels_compatible(msg);
619 static gboolean
620 call_initialized(struct sipe_media_call *call)
622 GSList *streams =
623 sipe_backend_media_get_streams(call->backend_private);
625 for (; streams; streams = streams->next) {
626 if (!sipe_backend_stream_initialized(call->backend_private,
627 streams->data)) {
628 return FALSE;
632 return TRUE;
635 // Sends an invite response when the call is accepted and local candidates were
636 // prepared, otherwise does nothing. If error response is sent, call_private is
637 // disposed before function returns. Returns true when response was sent.
638 static gboolean
639 send_invite_response_if_ready(struct sipe_media_call_private *call_private)
641 struct sipe_backend_media *backend_media;
643 backend_media = call_private->public.backend_private;
645 if (!sipe_backend_media_accepted(backend_media) ||
646 !call_initialized(&call_private->public))
647 return FALSE;
649 if (!call_private->encryption_compatible) {
650 struct sipe_core_private *sipe_private = call_private->sipe_private;
652 sipmsg_add_header(call_private->invitation, "Warning",
653 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
654 sip_transport_response(sipe_private,
655 call_private->invitation,
656 488, "Encryption Levels not compatible",
657 NULL);
658 sipe_backend_media_reject(backend_media, FALSE);
659 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
660 _("Unable to establish a call"),
661 _("Encryption settings of peer are incompatible with ours."));
662 } else {
663 send_response_with_session_description(call_private, 200, "OK");
666 return TRUE;
669 static void
670 stream_initialized_cb(struct sipe_media_call *call,
671 struct sipe_backend_stream *stream)
673 if (call_initialized(call)) {
674 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
675 struct sipe_backend_media *backend_private = call->backend_private;
677 if (sipe_backend_media_is_initiator(backend_private, stream)) {
678 sipe_invite_call(call_private->sipe_private,
679 process_invite_call_response);
680 } else if (call_private->smsg) {
681 struct sdpmsg *smsg = call_private->smsg;
682 call_private->smsg = NULL;
684 apply_remote_message(call_private, smsg);
685 send_invite_response_if_ready(call_private);
686 sdpmsg_free(smsg);
691 static void phone_state_publish(struct sipe_core_private *sipe_private)
693 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
694 sipe_ocs2007_phone_state_publish(sipe_private);
695 } else {
696 // TODO: OCS 2005 support. Is anyone still using it at all?
700 static void
701 media_end_cb(struct sipe_media_call *call)
703 g_return_if_fail(call);
705 SIPE_MEDIA_CALL_PRIVATE->sipe_private->media_call = NULL;
706 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
707 sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE);
710 static void
711 call_accept_cb(struct sipe_media_call *call, gboolean local)
713 if (local) {
714 send_invite_response_if_ready(SIPE_MEDIA_CALL_PRIVATE);
716 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
719 static void
720 call_reject_cb(struct sipe_media_call *call, gboolean local)
722 if (local) {
723 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
724 sip_transport_response(call_private->sipe_private,
725 call_private->invitation,
726 603, "Decline", NULL);
730 static gboolean
731 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
732 struct transaction *trans);
734 static void call_hold_cb(struct sipe_media_call *call,
735 gboolean local,
736 SIPE_UNUSED_PARAMETER gboolean state)
738 if (local)
739 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
740 sipe_media_send_ack);
743 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
745 if (local) {
746 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
747 struct sip_session *session;
748 session = sipe_session_find_call(call_private->sipe_private,
749 call_private->with);
751 if (session) {
752 sipe_session_close(call_private->sipe_private, session);
757 static void
758 error_cb(struct sipe_media_call *call, gchar *message)
760 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
761 struct sipe_core_private *sipe_private = call_private->sipe_private;
762 gboolean initiator = sipe_backend_media_is_initiator(call->backend_private, NULL);
763 gboolean accepted = sipe_backend_media_accepted(call->backend_private);
765 gchar *title = g_strdup_printf("Call with %s failed", call_private->with);
766 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message);
767 g_free(title);
769 if (!initiator && !accepted) {
770 sip_transport_response(sipe_private,
771 call_private->invitation,
772 488, "Not Acceptable Here", NULL);
775 sipe_backend_media_hangup(call->backend_private, initiator || accepted);
778 static struct sipe_media_call_private *
779 sipe_media_call_new(struct sipe_core_private *sipe_private,
780 const gchar* with, gboolean initiator, SipeIceVersion ice_version)
782 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
783 gchar *cname;
785 call_private->sipe_private = sipe_private;
787 cname = g_strdup(sipe_private->contact + 1);
788 cname[strlen(cname) - 1] = '\0';
790 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
791 SIPE_MEDIA_CALL,
792 with,
793 initiator);
794 sipe_backend_media_set_cname(call_private->public.backend_private, cname);
796 call_private->ice_version = ice_version;
797 call_private->encryption_compatible = TRUE;
799 call_private->public.stream_initialized_cb = stream_initialized_cb;
800 call_private->public.media_end_cb = media_end_cb;
801 call_private->public.call_accept_cb = call_accept_cb;
802 call_private->public.call_reject_cb = call_reject_cb;
803 call_private->public.call_hold_cb = call_hold_cb;
804 call_private->public.call_hangup_cb = call_hangup_cb;
805 call_private->public.error_cb = error_cb;
807 g_free(cname);
809 return call_private;
812 void sipe_media_hangup(struct sipe_media_call_private *call_private)
814 if (call_private) {
815 sipe_backend_media_hangup(call_private->public.backend_private,
816 FALSE);
820 static struct sipe_backend_stream *
821 sipe_media_stream_add(struct sipe_core_private *sipe_private, const gchar *id,
822 const gchar *with, SipeMediaType type,
823 SipeIceVersion ice_version, gboolean initiator)
825 struct sipe_backend_stream *stream;
826 struct sipe_backend_media_relays *backend_media_relays;
827 struct sipe_backend_media *backend_media;
829 backend_media_relays = sipe_backend_media_relays_convert(
830 sipe_private->media_relays,
831 sipe_private->media_relay_username,
832 sipe_private->media_relay_password);
834 backend_media = sipe_private->media_call->public.backend_private;
835 stream = sipe_backend_media_add_stream(backend_media, id, with, type,
836 ice_version, initiator,
837 backend_media_relays);
839 sipe_backend_media_relays_free(backend_media_relays);
841 return stream;
844 static void
845 sipe_media_initiate_call(struct sipe_core_private *sipe_private,
846 const char *with, SipeIceVersion ice_version,
847 gboolean with_video)
849 struct sip_session *session;
850 struct sip_dialog *dialog;
852 if (sipe_private->media_call)
853 return;
855 sipe_private->media_call = sipe_media_call_new(sipe_private, with, TRUE,
856 ice_version);
858 session = sipe_session_add_call(sipe_private, with);
859 dialog = sipe_dialog_add(session);
860 dialog->callid = gencallid();
861 dialog->with = g_strdup(session->with);
862 dialog->ourtag = gentag();
864 sipe_private->media_call->with = g_strdup(session->with);
866 if (!sipe_media_stream_add(sipe_private, "audio", with, SIPE_MEDIA_AUDIO,
867 sipe_private->media_call->ice_version,
868 TRUE)) {
869 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
870 _("Error occured"),
871 _("Error creating audio stream"));
872 sipe_media_hangup(sipe_private->media_call);
873 return;
876 if (with_video &&
877 !sipe_media_stream_add(sipe_private, "video", with, SIPE_MEDIA_VIDEO,
878 sipe_private->media_call->ice_version,
879 TRUE)) {
880 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
881 _("Error occured"),
882 _("Error creating video stream"));
883 sipe_media_hangup(sipe_private->media_call);
884 return;
887 // Processing continues in stream_initialized_cb
890 void
891 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
892 const char *with,
893 gboolean with_video)
895 sipe_media_initiate_call(SIPE_CORE_PRIVATE, with,
896 SIPE_ICE_RFC_5245, with_video);
899 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
900 struct sipe_chat_session *chat_session)
902 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
903 struct sip_session *session;
904 struct sip_dialog *dialog;
905 SipeIceVersion ice_version;
906 gchar **parts;
907 gchar *av_uri;
909 if (!sipe_conf_supports_mcu_type(sipe_private, "audio-video")) {
910 sipe_backend_notify_error(sipe_public, _("Join conference call"),
911 _("Conference calls are not supported on this server."));
912 return;
915 session = sipe_session_find_chat(sipe_private, chat_session);
917 if (sipe_private->media_call || !session)
918 return;
920 session->is_call = TRUE;
922 parts = g_strsplit(chat_session->id, "app:conf:focus:", 2);
923 av_uri = g_strjoinv("app:conf:audio-video:", parts);
924 g_strfreev(parts);
926 ice_version = SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) ? SIPE_ICE_RFC_5245 :
927 SIPE_ICE_DRAFT_6;
929 sipe_private->media_call = sipe_media_call_new(sipe_private, av_uri,
930 TRUE, ice_version);
932 session = sipe_session_add_call(sipe_private, av_uri);
933 dialog = sipe_dialog_add(session);
934 dialog->callid = gencallid();
935 dialog->with = g_strdup(session->with);
936 dialog->ourtag = gentag();
938 g_free(av_uri);
940 sipe_private->media_call->with = g_strdup(session->with);
942 if (!sipe_media_stream_add(sipe_private, "audio", dialog->with,
943 SIPE_MEDIA_AUDIO,
944 sipe_private->media_call->ice_version,
945 TRUE)) {
946 sipe_backend_notify_error(sipe_public,
947 _("Error occured"),
948 _("Error creating audio stream"));
949 sipe_media_hangup(sipe_private->media_call);
950 sipe_private->media_call = NULL;
953 // Processing continues in stream_initialized_cb
956 gboolean sipe_core_media_in_call(struct sipe_core_public *sipe_public)
958 if (sipe_public) {
959 return SIPE_CORE_PRIVATE->media_call != NULL;
961 return FALSE;
964 static gboolean phone_number_is_valid(const gchar *phone_number)
966 if (!phone_number || sipe_strequal(phone_number, "")) {
967 return FALSE;
970 if (*phone_number == '+') {
971 ++phone_number;
974 while (*phone_number != '\0') {
975 if (!g_ascii_isdigit(*phone_number)) {
976 return FALSE;
978 ++phone_number;
981 return TRUE;
984 void sipe_core_media_phone_call(struct sipe_core_public *sipe_public,
985 const gchar *phone_number)
987 g_return_if_fail(sipe_public);
989 if (phone_number_is_valid(phone_number)) {
990 gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone",
991 phone_number, sipe_public->sip_domain);
993 sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE);
995 g_free(phone_uri);
996 } else {
997 sipe_backend_notify_error(sipe_public,
998 _("Unable to establish a call"),
999 _("Invalid phone number"));
1003 void sipe_core_media_test_call(struct sipe_core_public *sipe_public)
1005 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1006 if (!sipe_private->test_call_bot_uri) {
1007 sipe_backend_notify_error(sipe_public,
1008 _("Unable to establish a call"),
1009 _("Audio Test Service is not available."));
1010 return;
1013 sipe_core_media_initiate_call(sipe_public,
1014 sipe_private->test_call_bot_uri, FALSE);
1017 void
1018 process_incoming_invite_call(struct sipe_core_private *sipe_private,
1019 struct sipmsg *msg)
1021 struct sipe_media_call_private *call_private = sipe_private->media_call;
1022 struct sipe_backend_media *backend_media;
1023 struct sdpmsg *smsg;
1024 gboolean has_new_media = FALSE;
1025 GSList *i;
1027 if (call_private) {
1028 char *self;
1030 if (!is_media_session_msg(call_private, msg)) {
1031 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
1032 return;
1035 self = sip_uri_self(sipe_private);
1036 if (sipe_strequal(call_private->with, self)) {
1037 g_free(self);
1038 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
1039 return;
1041 g_free(self);
1044 smsg = sdpmsg_parse_msg(msg->body);
1045 if (!smsg) {
1046 sip_transport_response(sipe_private, msg,
1047 488, "Not Acceptable Here", NULL);
1048 sipe_media_hangup(call_private);
1049 return;
1052 if (!call_private) {
1053 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
1054 struct sip_session *session;
1056 call_private = sipe_media_call_new(sipe_private, with, FALSE, smsg->ice_version);
1057 session = sipe_session_add_call(sipe_private, with);
1058 sipe_media_dialog_init(session, msg);
1060 call_private->with = g_strdup(session->with);
1061 sipe_private->media_call = call_private;
1062 g_free(with);
1065 backend_media = call_private->public.backend_private;
1067 if (call_private->invitation)
1068 sipmsg_free(call_private->invitation);
1069 call_private->invitation = sipmsg_copy(msg);
1071 // Create any new media streams
1072 for (i = smsg->media; i; i = i->next) {
1073 struct sdpmedia *media = i->data;
1074 gchar *id = media->name;
1075 SipeMediaType type;
1077 if ( media->port != 0
1078 && !sipe_backend_media_get_stream_by_id(backend_media, id)) {
1079 gchar *with;
1081 if (sipe_strequal(id, "audio"))
1082 type = SIPE_MEDIA_AUDIO;
1083 else if (sipe_strequal(id, "video"))
1084 type = SIPE_MEDIA_VIDEO;
1085 else
1086 continue;
1088 with = parse_from(sipmsg_find_header(msg, "From"));
1089 sipe_media_stream_add(sipe_private, id, with, type,
1090 smsg->ice_version, FALSE);
1091 has_new_media = TRUE;
1092 g_free(with);
1096 if (has_new_media) {
1097 sdpmsg_free(call_private->smsg);
1098 call_private->smsg = smsg;
1099 sip_transport_response(sipe_private, call_private->invitation,
1100 180, "Ringing", NULL);
1101 // Processing continues in stream_initialized_cb
1102 } else {
1103 apply_remote_message(call_private, smsg);
1104 send_response_with_session_description(call_private, 200, "OK");
1106 sdpmsg_free(smsg);
1110 void process_incoming_cancel_call(struct sipe_core_private *sipe_private,
1111 struct sipmsg *msg)
1113 struct sipe_media_call_private *call_private = sipe_private->media_call;
1115 // We respond to the CANCEL request with 200 OK response and
1116 // with 487 Request Terminated to the remote INVITE in progress.
1117 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1119 if (call_private->invitation) {
1120 sip_transport_response(sipe_private, call_private->invitation,
1121 487, "Request Terminated", NULL);
1124 sipe_media_hangup(call_private);
1127 static gboolean
1128 sipe_media_send_ack(struct sipe_core_private *sipe_private,
1129 struct sipmsg *msg,
1130 struct transaction *trans)
1132 struct sipe_media_call_private *call_private = sipe_private->media_call;
1133 struct sip_session *session;
1134 struct sip_dialog *dialog;
1135 int tmp_cseq;
1137 if (!is_media_session_msg(call_private, msg))
1138 return FALSE;
1140 session = sipe_session_find_call(sipe_private, call_private->with);
1141 dialog = session->dialogs->data;
1142 if (!dialog)
1143 return FALSE;
1145 tmp_cseq = dialog->cseq;
1147 dialog->cseq = sip_transaction_cseq(trans) - 1;
1148 sip_transport_ack(sipe_private, dialog);
1149 dialog->cseq = tmp_cseq;
1151 dialog->outgoing_invite = NULL;
1153 return TRUE;
1156 static gboolean
1157 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
1158 struct sipmsg *msg,
1159 struct transaction *trans)
1161 if (!sipe_media_send_ack(sipe_private, msg, trans))
1162 return FALSE;
1164 sipe_backend_media_accept(sipe_private->media_call->public.backend_private,
1165 FALSE);
1167 return TRUE;
1170 static void
1171 reinvite_on_candidate_pair_cb(struct sipe_core_public *sipe_public)
1173 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1174 struct sipe_media_call_private *media_call = sipe_private->media_call;
1175 struct sipe_backend_media *backend_media;
1176 GSList *streams;
1178 if (!media_call)
1179 return;
1181 backend_media = media_call->public.backend_private;
1182 streams = sipe_backend_media_get_streams(backend_media);
1184 for (; streams; streams = streams->next) {
1185 struct sipe_backend_stream *s = streams->data;
1186 GList *remote_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, s);
1187 guint components = g_list_length(remote_candidates);
1189 sipe_media_candidate_list_free(remote_candidates);
1191 // We must have candidates for both (RTP + RTCP) components ready
1192 if (components < 2) {
1193 sipe_schedule_mseconds(sipe_private,
1194 "<+media-reinvite-on-candidate-pair>",
1195 NULL,
1196 500,
1197 (sipe_schedule_action) reinvite_on_candidate_pair_cb,
1198 NULL);
1199 return;
1203 sipe_invite_call(sipe_private, sipe_media_send_final_ack);
1206 static gboolean
1207 maybe_retry_call_with_ice_version(struct sipe_core_private *sipe_private,
1208 SipeIceVersion ice_version,
1209 struct transaction *trans)
1211 struct sipe_media_call_private *call_private = sipe_private->media_call;
1213 if (call_private->ice_version != ice_version &&
1214 sip_transaction_cseq(trans) == 1) {
1215 gchar *with = g_strdup(call_private->with);
1216 struct sipe_backend_media *backend_private = call_private->public.backend_private;
1217 gboolean with_video = sipe_backend_media_get_stream_by_id(backend_private, "video") != NULL;
1219 sipe_media_hangup(call_private);
1220 SIPE_DEBUG_INFO("Retrying call with ICEv%d.",
1221 ice_version == SIPE_ICE_DRAFT_6 ? 6 : 19);
1222 sipe_media_initiate_call(sipe_private, with, ice_version,
1223 with_video);
1225 g_free(with);
1226 return TRUE;
1229 return FALSE;
1232 static gboolean
1233 process_invite_call_response(struct sipe_core_private *sipe_private,
1234 struct sipmsg *msg,
1235 struct transaction *trans)
1237 const gchar *with;
1238 struct sipe_media_call_private *call_private = sipe_private->media_call;
1239 struct sip_session *session;
1240 struct sip_dialog *dialog;
1241 struct sdpmsg *smsg;
1243 if (!is_media_session_msg(call_private, msg))
1244 return FALSE;
1246 session = sipe_session_find_call(sipe_private, call_private->with);
1247 dialog = session->dialogs->data;
1249 with = dialog->with;
1251 dialog->outgoing_invite = NULL;
1253 if (msg->response >= 400) {
1254 // Call rejected by remote peer or an error occurred
1255 const gchar *title;
1256 GString *desc = g_string_new("");
1257 gboolean append_responsestr = FALSE;
1259 switch (msg->response) {
1260 case 480: {
1261 title = _("User unavailable");
1263 if (sipmsg_parse_warning(msg, NULL) == 391) {
1264 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
1265 } else
1266 g_string_append_printf(desc, _("User %s is not available"), with);
1267 break;
1269 case 603:
1270 case 605:
1271 title = _("Call rejected");
1272 g_string_append_printf(desc, _("User %s rejected call"), with);
1273 break;
1274 case 415:
1275 // OCS/Lync really sends response string with 'Mutipart' typo.
1276 if (sipe_strequal(msg->responsestr, "Mutipart mime in content type not supported by Archiving CDR service") &&
1277 maybe_retry_call_with_ice_version(sipe_private, SIPE_ICE_DRAFT_6, trans)) {
1278 return TRUE;
1280 title = _("Unsupported media type");
1281 break;
1282 case 488: {
1283 /* Check for incompatible encryption levels error.
1285 * MS Lync 2010:
1286 * 488 Not Acceptable Here
1287 * ms-client-diagnostics: 52017;reason="Encryption levels dont match"
1289 * older clients (and SIPE itself):
1290 * 488 Encryption Levels not compatible
1292 const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics");
1293 SipeIceVersion retry_ice_version = SIPE_ICE_DRAFT_6;
1295 if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") ||
1296 (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) {
1297 title = _("Unable to establish a call");
1298 g_string_append(desc, _("Encryption settings of peer are incompatible with ours."));
1299 break;
1302 /* Check if this is failed conference using
1303 * ICEv6 with reason "Error parsing SDP" and
1304 * retry using ICEv19. */
1305 ms_diag = sipmsg_find_header(msg, "ms-diagnostics");
1306 if (ms_diag && g_str_has_prefix(ms_diag, "7008;")) {
1307 retry_ice_version = SIPE_ICE_RFC_5245;
1310 if (maybe_retry_call_with_ice_version(sipe_private, retry_ice_version, trans)) {
1311 return TRUE;
1313 // Break intentionally omitted
1315 default:
1316 title = _("Error occured");
1317 g_string_append(desc, _("Unable to establish a call"));
1318 append_responsestr = TRUE;
1319 break;
1322 if (append_responsestr) {
1323 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1325 g_string_append_printf(desc, "\n%d %s",
1326 msg->response, msg->responsestr);
1327 if (reason) {
1328 g_string_append_printf(desc, "\n\n%s", reason);
1329 g_free(reason);
1333 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str);
1334 g_string_free(desc, TRUE);
1336 sipe_media_send_ack(sipe_private, msg, trans);
1337 sipe_media_hangup(call_private);
1339 return TRUE;
1342 sipe_dialog_parse(dialog, msg, TRUE);
1343 smsg = sdpmsg_parse_msg(msg->body);
1344 if (!smsg) {
1345 sip_transport_response(sipe_private, msg,
1346 488, "Not Acceptable Here", NULL);
1347 sipe_media_hangup(call_private);
1348 return FALSE;
1351 apply_remote_message(call_private, smsg);
1352 sdpmsg_free(smsg);
1354 sipe_media_send_ack(sipe_private, msg, trans);
1355 reinvite_on_candidate_pair_cb(SIPE_CORE_PUBLIC);
1357 return TRUE;
1360 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
1361 struct sipmsg *msg)
1363 if (call_private) {
1364 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
1365 struct sip_session *session;
1367 session = sipe_session_find_call(call_private->sipe_private,
1368 call_private->with);
1369 if (session) {
1370 struct sip_dialog *dialog = session->dialogs->data;
1371 return sipe_strequal(dialog->callid, callid);
1374 return FALSE;
1377 void sipe_media_handle_going_offline(struct sipe_media_call_private *call_private)
1379 struct sipe_backend_media *backend_private;
1381 backend_private = call_private->public.backend_private;
1383 if ( !sipe_backend_media_is_initiator(backend_private, NULL)
1384 && !sipe_backend_media_accepted(backend_private)) {
1385 sip_transport_response(call_private->sipe_private,
1386 call_private->invitation,
1387 480, "Temporarily Unavailable", NULL);
1388 } else {
1389 struct sip_session *session;
1391 session = sipe_session_find_call(call_private->sipe_private,
1392 call_private->with);
1393 if (session)
1394 sipe_session_close(call_private->sipe_private, session);
1397 sipe_media_hangup(call_private);
1400 gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private)
1402 return g_strstr_len(call_private->with, -1, "app:conf:audio-video:") != NULL;
1405 static void
1406 sipe_media_relay_free(struct sipe_media_relay *relay)
1408 g_free(relay->hostname);
1409 if (relay->dns_query)
1410 sipe_backend_dns_query_cancel(relay->dns_query);
1411 g_free(relay);
1414 void
1415 sipe_media_relay_list_free(GSList *list)
1417 for (; list; list = g_slist_delete_link(list, list))
1418 sipe_media_relay_free(list->data);
1421 static void
1422 relay_ip_resolved_cb(struct sipe_media_relay* relay,
1423 const gchar *ip, SIPE_UNUSED_PARAMETER guint port)
1425 gchar *hostname = relay->hostname;
1426 relay->dns_query = NULL;
1428 if (ip && port) {
1429 relay->hostname = g_strdup(ip);
1430 SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip);
1431 } else {
1432 relay->hostname = NULL;
1433 SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname);
1436 g_free(hostname);
1439 static gboolean
1440 process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private,
1441 struct sipmsg *msg,
1442 SIPE_UNUSED_PARAMETER struct transaction *trans)
1444 g_free(sipe_private->media_relay_username);
1445 g_free(sipe_private->media_relay_password);
1446 sipe_media_relay_list_free(sipe_private->media_relays);
1447 sipe_private->media_relay_username = NULL;
1448 sipe_private->media_relay_password = NULL;
1449 sipe_private->media_relays = NULL;
1451 if (msg->response >= 400) {
1452 SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. "
1453 "Failed to obtain A/V Edge credentials.");
1454 return FALSE;
1457 if (msg->response == 200) {
1458 sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
1460 if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) {
1461 const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials");
1462 const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList");
1463 const sipe_xml *item;
1464 GSList *relays = NULL;
1466 item = sipe_xml_child(xn_credentials, "username");
1467 sipe_private->media_relay_username = sipe_xml_data(item);
1468 item = sipe_xml_child(xn_credentials, "password");
1469 sipe_private->media_relay_password = sipe_xml_data(item);
1471 for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) {
1472 struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1);
1473 const sipe_xml *node;
1474 gchar *tmp;
1476 node = sipe_xml_child(item, "hostName");
1477 relay->hostname = sipe_xml_data(node);
1479 node = sipe_xml_child(item, "udpPort");
1480 if (node) {
1481 relay->udp_port = atoi(tmp = sipe_xml_data(node));
1482 g_free(tmp);
1485 node = sipe_xml_child(item, "tcpPort");
1486 if (node) {
1487 relay->tcp_port = atoi(tmp = sipe_xml_data(node));
1488 g_free(tmp);
1491 relays = g_slist_append(relays, relay);
1493 relay->dns_query = sipe_backend_dns_query_a(
1494 SIPE_CORE_PUBLIC,
1495 relay->hostname,
1496 relay->udp_port,
1497 (sipe_dns_resolved_cb) relay_ip_resolved_cb,
1498 relay);
1500 SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d",
1501 relay->hostname,
1502 relay->tcp_port, relay->udp_port);
1505 sipe_private->media_relays = relays;
1508 sipe_xml_free(xn_response);
1511 return TRUE;
1514 void
1515 sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private)
1517 // TODO: re-request credentials after duration expires?
1518 static const char CRED_REQUEST_XML[] =
1519 "<request requestID=\"%d\" "
1520 "from=\"%s\" "
1521 "version=\"1.0\" "
1522 "to=\"%s\" "
1523 "xmlns=\"http://schemas.microsoft.com/2006/09/sip/mrasp\" "
1524 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
1525 "<credentialsRequest credentialsRequestID=\"%d\">"
1526 "<identity>%s</identity>"
1527 "<location>%s</location>"
1528 "<duration>480</duration>"
1529 "</credentialsRequest>"
1530 "</request>";
1532 int request_id = rand();
1533 gchar *self;
1534 gchar *body;
1536 if (!sipe_private->mras_uri)
1537 return;
1539 self = sip_uri_self(sipe_private);
1541 body = g_strdup_printf(
1542 CRED_REQUEST_XML,
1543 request_id,
1544 self,
1545 sipe_private->mras_uri,
1546 request_id,
1547 self,
1548 SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet");
1549 g_free(self);
1551 sip_transport_service(sipe_private,
1552 sipe_private->mras_uri,
1553 "Content-Type: application/msrtc-media-relay-auth+xml\r\n",
1554 body,
1555 process_get_av_edge_credentials_response);
1557 g_free(body);
1561 Local Variables:
1562 mode: c
1563 c-file-style: "bsd"
1564 indent-tabs-mode: t
1565 tab-width: 8
1566 End: