ae13df862e150249bbc844abbb4503ef9ac3be90
[siplcs.git] / src / core / sipe-media.c
blobae13df862e150249bbc844abbb4503ef9ac3be90
1 /**
2 * @file sipe-media.c
4 * pidgin-sipe
6 * Copyright (C) 2011-13 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include <glib.h>
34 #include "sipe-common.h"
35 #include "sipmsg.h"
36 #include "sip-transport.h"
37 #include "sipe-backend.h"
38 #include "sdpmsg.h"
39 #include "sipe-chat.h"
40 #include "sipe-core.h"
41 #include "sipe-core-private.h"
42 #include "sipe-dialog.h"
43 #include "sipe-media.h"
44 #include "sipe-ocs2007.h"
45 #include "sipe-session.h"
46 #include "sipe-utils.h"
47 #include "sipe-nls.h"
48 #include "sipe-schedule.h"
49 #include "sipe-xml.h"
51 struct sipe_media_call_private {
52 struct sipe_media_call public;
54 /* private part starts here */
55 struct sipe_core_private *sipe_private;
56 gchar *with;
58 struct sipmsg *invitation;
59 SipeIceVersion ice_version;
60 gboolean encryption_compatible;
62 struct sdpmsg *smsg;
63 GSList *failed_media;
65 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
66 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
68 static void sipe_media_codec_list_free(GList *codecs)
70 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
71 sipe_backend_codec_free(codecs->data);
74 static void sipe_media_candidate_list_free(GList *candidates)
76 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
77 sipe_backend_candidate_free(candidates->data);
80 static void
81 sipe_media_call_free(struct sipe_media_call_private *call_private)
83 if (call_private) {
84 struct sip_session *session;
85 sipe_backend_media_free(call_private->public.backend_private);
87 session = sipe_session_find_call(call_private->sipe_private,
88 call_private->with);
89 if (session)
90 sipe_session_remove(call_private->sipe_private, session);
92 if (call_private->invitation)
93 sipmsg_free(call_private->invitation);
95 sdpmsg_free(call_private->smsg);
96 sipe_utils_slist_free_full(call_private->failed_media,
97 (GDestroyNotify)sdpmedia_free);
98 g_free(call_private->with);
99 g_free(call_private);
103 static GSList *
104 backend_candidates_to_sdpcandidate(GList *candidates)
106 GSList *result = NULL;
107 GList *i;
109 for (i = candidates; i; i = i->next) {
110 struct sipe_backend_candidate *candidate = i->data;
111 struct sdpcandidate *c = g_new(struct sdpcandidate, 1);
113 c->foundation = sipe_backend_candidate_get_foundation(candidate);
114 c->component = sipe_backend_candidate_get_component_type(candidate);
115 c->type = sipe_backend_candidate_get_type(candidate);
116 c->protocol = sipe_backend_candidate_get_protocol(candidate);
117 c->ip = sipe_backend_candidate_get_ip(candidate);
118 c->port = sipe_backend_candidate_get_port(candidate);
119 c->base_ip = sipe_backend_candidate_get_base_ip(candidate);
120 c->base_port = sipe_backend_candidate_get_base_port(candidate);
121 c->priority = sipe_backend_candidate_get_priority(candidate);
122 c->username = sipe_backend_candidate_get_username(candidate);
123 c->password = sipe_backend_candidate_get_password(candidate);
125 result = g_slist_append(result, c);
128 return result;
131 static void
132 get_stream_ip_and_ports(GSList *candidates,
133 gchar **ip, guint *rtp_port, guint *rtcp_port,
134 SipeCandidateType type)
136 *rtp_port = 0;
137 *rtcp_port = 0;
139 for (; candidates; candidates = candidates->next) {
140 struct sdpcandidate *candidate = candidates->data;
142 if (type == SIPE_CANDIDATE_TYPE_ANY || candidate->type == type) {
143 if (candidate->component == SIPE_COMPONENT_RTP) {
144 *rtp_port = candidate->port;
145 *ip = g_strdup(candidate->ip);
146 } else if (candidate->component == SIPE_COMPONENT_RTCP)
147 *rtcp_port = candidate->port;
150 if (*rtp_port != 0 && *rtcp_port != 0)
151 return;
155 static struct sdpmedia *
156 backend_stream_to_sdpmedia(struct sipe_backend_media *backend_media,
157 struct sipe_backend_stream *backend_stream)
159 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
160 GList *codecs = sipe_backend_get_local_codecs(backend_media,
161 backend_stream);
162 guint rtcp_port = 0;
163 SipeMediaType type;
164 GSList *attributes = NULL;
165 GList *candidates;
166 GList *i;
168 media->name = g_strdup(sipe_backend_stream_get_id(backend_stream));
170 if (sipe_strequal(media->name, "audio"))
171 type = SIPE_MEDIA_AUDIO;
172 else if (sipe_strequal(media->name, "video"))
173 type = SIPE_MEDIA_VIDEO;
174 else {
175 // TODO: incompatible media, should not happen here
176 g_free(media->name);
177 g_free(media);
178 sipe_media_codec_list_free(codecs);
179 return(NULL);
182 // Process codecs
183 for (i = codecs; i; i = i->next) {
184 struct sipe_backend_codec *codec = i->data;
185 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
186 GList *params;
188 c->id = sipe_backend_codec_get_id(codec);
189 c->name = sipe_backend_codec_get_name(codec);
190 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
191 c->type = type;
193 params = sipe_backend_codec_get_optional_parameters(codec);
194 for (; params; params = params->next) {
195 struct sipnameval *param = params->data;
196 struct sipnameval *copy = g_new0(struct sipnameval, 1);
198 copy->name = g_strdup(param->name);
199 copy->value = g_strdup(param->value);
201 c->parameters = g_slist_append(c->parameters, copy);
204 media->codecs = g_slist_append(media->codecs, c);
207 sipe_media_codec_list_free(codecs);
209 // Process local candidates
210 // If we have established candidate pairs, send them in SDP response.
211 // Otherwise send all available local candidates.
212 candidates = sipe_backend_media_get_active_local_candidates(backend_media,
213 backend_stream);
214 if (!candidates)
215 candidates = sipe_backend_get_local_candidates(backend_media,
216 backend_stream);
218 media->candidates = backend_candidates_to_sdpcandidate(candidates);
220 sipe_media_candidate_list_free(candidates);
222 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
223 &rtcp_port, SIPE_CANDIDATE_TYPE_HOST);
224 // No usable HOST candidates, use any candidate
225 if (media->ip == NULL && media->candidates) {
226 get_stream_ip_and_ports(media->candidates, &media->ip, &media->port,
227 &rtcp_port, SIPE_CANDIDATE_TYPE_ANY);
230 if (sipe_backend_stream_is_held(backend_stream))
231 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
233 if (rtcp_port) {
234 gchar *tmp = g_strdup_printf("%u", rtcp_port);
235 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
236 g_free(tmp);
239 attributes = sipe_utils_nameval_add(attributes, "encryption", "rejected");
241 media->attributes = attributes;
243 // Process remote candidates
244 candidates = sipe_backend_media_get_active_remote_candidates(backend_media,
245 backend_stream);
246 media->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
247 sipe_media_candidate_list_free(candidates);
249 return media;
252 static struct sdpmsg *
253 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
255 struct sipe_backend_media *backend_media = call_private->public.backend_private;
256 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
257 GSList *streams = sipe_backend_media_get_streams(backend_media);
259 for (; streams; streams = streams->next) {
260 struct sdpmedia *media = backend_stream_to_sdpmedia(backend_media, streams->data);
261 if (media) {
262 msg->media = g_slist_append(msg->media, media);
264 if (msg->ip == NULL)
265 msg->ip = g_strdup(media->ip);
269 msg->media = g_slist_concat(msg->media, call_private->failed_media);
270 call_private->failed_media = NULL;
272 msg->ice_version = call_private->ice_version;
274 return msg;
277 static void
278 sipe_invite_call(struct sipe_core_private *sipe_private, TransCallback tc)
280 gchar *hdr;
281 gchar *contact;
282 gchar *p_preferred_identity = NULL;
283 gchar *body;
284 struct sipe_media_call_private *call_private = sipe_private->media_call;
285 struct sip_session *session;
286 struct sip_dialog *dialog;
287 struct sdpmsg *msg;
288 gboolean add_2007_fallback = FALSE;
290 session = sipe_session_find_call(sipe_private, call_private->with);
291 dialog = session->dialogs->data;
292 add_2007_fallback = dialog->cseq == 0 &&
293 call_private->ice_version == SIPE_ICE_RFC_5245 &&
294 !sipe_strequal(call_private->with, sipe_private->test_call_bot_uri);
296 contact = get_contact(sipe_private);
298 if (sipe_private->uc_line_uri) {
299 gchar *self = sip_uri_self(sipe_private);
300 p_preferred_identity = g_strdup_printf(
301 "P-Preferred-Identity: <%s>, <%s>\r\n",
302 self, sipe_private->uc_line_uri);
303 g_free(self);
306 hdr = g_strdup_printf(
307 "ms-keep-alive: UAC;hop-hop=yes\r\n"
308 "Contact: %s\r\n"
309 "%s"
310 "Content-Type: %s\r\n",
311 contact,
312 p_preferred_identity ? p_preferred_identity : "",
313 add_2007_fallback ?
314 "multipart/alternative;boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\""
315 : "application/sdp");
316 g_free(contact);
317 g_free(p_preferred_identity);
319 msg = sipe_media_to_sdpmsg(call_private);
320 body = sdpmsg_to_string(msg);
322 if (add_2007_fallback) {
323 gchar *tmp;
324 tmp = g_strdup_printf(
325 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
326 "Content-Type: application/sdp\r\n"
327 "Content-Transfer-Encoding: 7bit\r\n"
328 "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n"
329 "\r\n"
330 "o=- 0 0 IN IP4 %s\r\n"
331 "s=session\r\n"
332 "c=IN IP4 %s\r\n"
333 "m=audio 0 RTP/AVP\r\n"
334 "\r\n"
335 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
336 "Content-Type: application/sdp\r\n"
337 "Content-Transfer-Encoding: 7bit\r\n"
338 "Content-Disposition: session; handling=optional\r\n"
339 "\r\n"
340 "%s"
341 "\r\n"
342 "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n",
343 msg->ip, msg->ip, body);
344 g_free(body);
345 body = tmp;
348 sdpmsg_free(msg);
350 dialog->outgoing_invite = sip_transport_invite(sipe_private,
351 hdr,
352 body,
353 dialog,
354 tc);
356 g_free(body);
357 g_free(hdr);
360 static struct sip_dialog *
361 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
363 gchar *newTag = gentag();
364 const gchar *oldHeader;
365 gchar *newHeader;
366 struct sip_dialog *dialog;
368 oldHeader = sipmsg_find_header(msg, "To");
369 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
370 sipmsg_remove_header_now(msg, "To");
371 sipmsg_add_header_now(msg, "To", newHeader);
372 g_free(newHeader);
374 dialog = sipe_dialog_add(session);
375 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
376 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
377 sipe_dialog_parse(dialog, msg, FALSE);
379 return dialog;
382 static void
383 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
385 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
386 gchar *body = sdpmsg_to_string(msg);
387 sdpmsg_free(msg);
388 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
389 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
390 g_free(body);
393 static gboolean
394 encryption_levels_compatible(struct sdpmsg *msg)
396 GSList *i;
398 for (i = msg->media; i; i = i->next) {
399 const gchar *enc_level;
400 struct sdpmedia *m = i->data;
402 enc_level = sipe_utils_nameval_find(m->attributes, "encryption");
404 // Decline call if peer requires encryption as we don't support it yet.
405 if (sipe_strequal(enc_level, "required"))
406 return FALSE;
409 return TRUE;
412 static gboolean
413 process_invite_call_response(struct sipe_core_private *sipe_private,
414 struct sipmsg *msg,
415 struct transaction *trans);
417 static gboolean
418 update_remote_media(struct sipe_media_call_private* call_private,
419 struct sdpmedia *media)
421 struct sipe_backend_media *backend_media = SIPE_MEDIA_CALL->backend_private;
422 struct sipe_backend_stream *backend_stream;
423 GList *backend_candidates = NULL;
424 GList *backend_codecs = NULL;
425 GSList *i;
426 gboolean result = TRUE;
428 backend_stream = sipe_backend_media_get_stream_by_id(backend_media,
429 media->name);
430 if (media->port == 0) {
431 if (backend_stream)
432 sipe_backend_media_remove_stream(backend_media, backend_stream);
433 return TRUE;
436 if (!backend_stream)
437 return FALSE;
439 for (i = media->codecs; i; i = i->next) {
440 struct sdpcodec *c = i->data;
441 struct sipe_backend_codec *codec;
442 GSList *j;
444 codec = sipe_backend_codec_new(c->id,
445 c->name,
446 c->type,
447 c->clock_rate);
449 for (j = c->parameters; j; j = j->next) {
450 struct sipnameval *attr = j->data;
452 sipe_backend_codec_add_optional_parameter(codec,
453 attr->name,
454 attr->value);
457 backend_codecs = g_list_append(backend_codecs, codec);
460 result = sipe_backend_set_remote_codecs(backend_media,
461 backend_stream,
462 backend_codecs);
463 sipe_media_codec_list_free(backend_codecs);
465 if (result == FALSE) {
466 sipe_backend_media_remove_stream(backend_media, backend_stream);
467 return FALSE;
470 for (i = media->candidates; i; i = i->next) {
471 struct sdpcandidate *c = i->data;
472 struct sipe_backend_candidate *candidate;
473 candidate = sipe_backend_candidate_new(c->foundation,
474 c->component,
475 c->type,
476 c->protocol,
477 c->ip,
478 c->port,
479 c->username,
480 c->password);
481 sipe_backend_candidate_set_priority(candidate, c->priority);
483 backend_candidates = g_list_append(backend_candidates, candidate);
486 sipe_backend_media_add_remote_candidates(backend_media,
487 backend_stream,
488 backend_candidates);
489 sipe_media_candidate_list_free(backend_candidates);
491 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
492 sipe_backend_stream_hold(backend_media, backend_stream, FALSE);
493 } else if (sipe_backend_stream_is_held(backend_stream)) {
494 sipe_backend_stream_unhold(backend_media, backend_stream, FALSE);
497 return TRUE;
500 static void
501 apply_remote_message(struct sipe_media_call_private* call_private,
502 struct sdpmsg* msg)
504 GSList *i;
506 sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free);
507 call_private->failed_media = NULL;
509 for (i = msg->media; i; i = i->next) {
510 struct sdpmedia *media = i->data;
511 if (!update_remote_media(call_private, media)) {
512 media->port = 0;
513 call_private->failed_media =
514 g_slist_append(call_private->failed_media, media);
518 /* We need to keep failed medias until response is sent, remove them
519 * from sdpmsg that is to be freed. */
520 for (i = call_private->failed_media; i; i = i->next) {
521 msg->media = g_slist_remove(msg->media, i->data);
524 call_private->encryption_compatible = encryption_levels_compatible(msg);
527 static gboolean
528 call_initialized(struct sipe_media_call *call)
530 GSList *streams =
531 sipe_backend_media_get_streams(call->backend_private);
533 for (; streams; streams = streams->next) {
534 if (!sipe_backend_stream_initialized(call->backend_private,
535 streams->data)) {
536 return FALSE;
540 return TRUE;
543 // Sends an invite response when the call is accepted and local candidates were
544 // prepared, otherwise does nothing. If error response is sent, call_private is
545 // disposed before function returns. Returns true when response was sent.
546 static gboolean
547 send_invite_response_if_ready(struct sipe_media_call_private *call_private)
549 struct sipe_backend_media *backend_media;
551 backend_media = call_private->public.backend_private;
553 if (!sipe_backend_media_accepted(backend_media) ||
554 !call_initialized(&call_private->public))
555 return FALSE;
557 if (!call_private->encryption_compatible) {
558 struct sipe_core_private *sipe_private = call_private->sipe_private;
560 sipmsg_add_header(call_private->invitation, "Warning",
561 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
562 sip_transport_response(sipe_private,
563 call_private->invitation,
564 488, "Encryption Levels not compatible",
565 NULL);
566 sipe_backend_media_reject(backend_media, FALSE);
567 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
568 _("Unable to establish a call"),
569 _("Encryption settings of peer are incompatible with ours."));
570 } else {
571 send_response_with_session_description(call_private, 200, "OK");
574 return TRUE;
577 static void
578 stream_initialized_cb(struct sipe_media_call *call,
579 struct sipe_backend_stream *stream)
581 if (call_initialized(call)) {
582 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
583 struct sipe_backend_media *backend_private = call->backend_private;
585 if (sipe_backend_media_is_initiator(backend_private, stream)) {
586 sipe_invite_call(call_private->sipe_private,
587 process_invite_call_response);
588 } else if (call_private->smsg) {
589 struct sdpmsg *smsg = call_private->smsg;
590 call_private->smsg = NULL;
592 apply_remote_message(call_private, smsg);
593 send_invite_response_if_ready(call_private);
594 sdpmsg_free(smsg);
599 static void phone_state_publish(struct sipe_core_private *sipe_private)
601 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
602 sipe_ocs2007_phone_state_publish(sipe_private);
603 } else {
604 // TODO: OCS 2005 support. Is anyone still using it at all?
608 static void
609 media_end_cb(struct sipe_media_call *call)
611 g_return_if_fail(call);
613 SIPE_MEDIA_CALL_PRIVATE->sipe_private->media_call = NULL;
614 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
615 sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE);
618 static void
619 call_accept_cb(struct sipe_media_call *call, gboolean local)
621 if (local) {
622 send_invite_response_if_ready(SIPE_MEDIA_CALL_PRIVATE);
624 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
627 static void
628 call_reject_cb(struct sipe_media_call *call, gboolean local)
630 if (local) {
631 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
632 sip_transport_response(call_private->sipe_private,
633 call_private->invitation,
634 603, "Decline", NULL);
638 static gboolean
639 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
640 struct transaction *trans);
642 static void call_hold_cb(struct sipe_media_call *call,
643 gboolean local,
644 SIPE_UNUSED_PARAMETER gboolean state)
646 if (local)
647 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
648 sipe_media_send_ack);
651 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
653 if (local) {
654 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
655 struct sip_session *session;
656 session = sipe_session_find_call(call_private->sipe_private,
657 call_private->with);
659 if (session) {
660 sipe_session_close(call_private->sipe_private, session);
665 static void
666 error_cb(struct sipe_media_call *call, gchar *message)
668 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
669 struct sipe_core_private *sipe_private = call_private->sipe_private;
670 gboolean initiator = sipe_backend_media_is_initiator(call->backend_private, NULL);
671 gboolean accepted = sipe_backend_media_accepted(call->backend_private);
673 gchar *title = g_strdup_printf("Call with %s failed", call_private->with);
674 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message);
675 g_free(title);
677 if (!initiator && !accepted) {
678 sip_transport_response(sipe_private,
679 call_private->invitation,
680 488, "Not Acceptable Here", NULL);
683 sipe_backend_media_hangup(call->backend_private, initiator || accepted);
686 static struct sipe_media_call_private *
687 sipe_media_call_new(struct sipe_core_private *sipe_private,
688 const gchar* with, gboolean initiator, SipeIceVersion ice_version)
690 struct sipe_media_call_private *call_private = g_new0(struct sipe_media_call_private, 1);
691 gchar *cname;
693 call_private->sipe_private = sipe_private;
695 cname = g_strdup(sipe_private->contact + 1);
696 cname[strlen(cname) - 1] = '\0';
698 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
699 SIPE_MEDIA_CALL,
700 with,
701 initiator);
702 sipe_backend_media_set_cname(call_private->public.backend_private, cname);
704 call_private->ice_version = ice_version;
705 call_private->encryption_compatible = TRUE;
707 call_private->public.stream_initialized_cb = stream_initialized_cb;
708 call_private->public.media_end_cb = media_end_cb;
709 call_private->public.call_accept_cb = call_accept_cb;
710 call_private->public.call_reject_cb = call_reject_cb;
711 call_private->public.call_hold_cb = call_hold_cb;
712 call_private->public.call_hangup_cb = call_hangup_cb;
713 call_private->public.error_cb = error_cb;
715 g_free(cname);
717 return call_private;
720 void sipe_media_hangup(struct sipe_media_call_private *call_private)
722 if (call_private) {
723 sipe_backend_media_hangup(call_private->public.backend_private,
724 FALSE);
728 static void
729 sipe_media_initiate_call(struct sipe_core_private *sipe_private,
730 const char *with, SipeIceVersion ice_version,
731 gboolean with_video)
733 struct sipe_media_call_private *call_private;
734 struct sipe_backend_media *backend_media;
735 struct sipe_backend_media_relays *backend_media_relays;
736 struct sip_session *session;
737 struct sip_dialog *dialog;
739 if (sipe_private->media_call)
740 return;
742 call_private = sipe_media_call_new(sipe_private, with, TRUE, ice_version);
744 session = sipe_session_add_call(sipe_private, with);
745 dialog = sipe_dialog_add(session);
746 dialog->callid = gencallid();
747 dialog->with = g_strdup(session->with);
748 dialog->ourtag = gentag();
750 call_private->with = g_strdup(session->with);
752 backend_media = call_private->public.backend_private;
754 backend_media_relays =
755 sipe_backend_media_relays_convert(sipe_private->media_relays,
756 sipe_private->media_relay_username,
757 sipe_private->media_relay_password);
759 if (!sipe_backend_media_add_stream(backend_media,
760 "audio", with, SIPE_MEDIA_AUDIO,
761 call_private->ice_version, TRUE,
762 backend_media_relays)) {
763 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
764 _("Error occured"),
765 _("Error creating audio stream"));
766 sipe_media_call_free(call_private);
767 sipe_backend_media_relays_free(backend_media_relays);
768 return;
771 if ( with_video
772 && !sipe_backend_media_add_stream(backend_media,
773 "video", with, SIPE_MEDIA_VIDEO,
774 call_private->ice_version, TRUE,
775 backend_media_relays)) {
776 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
777 _("Error occured"),
778 _("Error creating video stream"));
779 sipe_media_call_free(call_private);
780 sipe_backend_media_relays_free(backend_media_relays);
781 return;
784 sipe_private->media_call = call_private;
786 sipe_backend_media_relays_free(backend_media_relays);
788 // Processing continues in stream_initialized_cb
791 void
792 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
793 const char *with,
794 gboolean with_video)
796 sipe_media_initiate_call(SIPE_CORE_PRIVATE, with,
797 SIPE_ICE_RFC_5245, with_video);
800 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
801 struct sipe_chat_session *chat_session)
803 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
804 struct sipe_backend_media_relays *backend_media_relays;
805 struct sip_session *session;
806 struct sip_dialog *dialog;
807 gchar **parts;
808 gchar *av_uri;
810 session = sipe_session_find_chat(sipe_private, chat_session);
812 if (sipe_private->media_call || !session)
813 return;
815 session->is_call = TRUE;
817 parts = g_strsplit(chat_session->id, "app:conf:focus:", 2);
818 av_uri = g_strjoinv("app:conf:audio-video:", parts);
819 g_strfreev(parts);
821 sipe_private->media_call = sipe_media_call_new(sipe_private, av_uri,
822 TRUE, SIPE_ICE_DRAFT_6);
824 session = sipe_session_add_call(sipe_private, av_uri);
825 dialog = sipe_dialog_add(session);
826 dialog->callid = gencallid();
827 dialog->with = g_strdup(session->with);
828 dialog->ourtag = gentag();
830 g_free(av_uri);
832 sipe_private->media_call->with = g_strdup(session->with);
834 backend_media_relays =
835 sipe_backend_media_relays_convert(sipe_private->media_relays,
836 sipe_private->media_relay_username,
837 sipe_private->media_relay_password);
839 if (!sipe_backend_media_add_stream(sipe_private->media_call->public.backend_private,
840 "audio", dialog->with,
841 SIPE_MEDIA_AUDIO,
842 sipe_private->media_call->ice_version,
843 TRUE, backend_media_relays)) {
844 sipe_backend_notify_error(sipe_public,
845 _("Error occured"),
846 _("Error creating audio stream"));
847 sipe_media_call_free(sipe_private->media_call);
848 sipe_private->media_call = NULL;
851 sipe_backend_media_relays_free(backend_media_relays);
853 // Processing continues in stream_initialized_cb
856 gboolean sipe_core_media_in_call(struct sipe_core_public *sipe_public)
858 if (sipe_public) {
859 return SIPE_CORE_PRIVATE->media_call != NULL;
861 return FALSE;
864 static gboolean phone_number_is_valid(const gchar *phone_number)
866 if (!phone_number || sipe_strequal(phone_number, "")) {
867 return FALSE;
870 if (*phone_number == '+') {
871 ++phone_number;
874 while (*phone_number != '\0') {
875 if (!g_ascii_isdigit(*phone_number)) {
876 return FALSE;
878 ++phone_number;
881 return TRUE;
884 void sipe_core_media_phone_call(struct sipe_core_public *sipe_public,
885 const gchar *phone_number)
887 g_return_if_fail(sipe_public);
889 if (phone_number_is_valid(phone_number)) {
890 gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone",
891 phone_number, sipe_public->sip_domain);
893 sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE);
895 g_free(phone_uri);
896 } else {
897 sipe_backend_notify_error(sipe_public,
898 _("Unable to establish a call"),
899 _("Invalid phone number"));
903 void sipe_core_media_test_call(struct sipe_core_public *sipe_public)
905 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
906 if (!sipe_private->test_call_bot_uri) {
907 sipe_backend_notify_error(sipe_public,
908 _("Unable to establish a call"),
909 _("Audio Test Service is not available."));
910 return;
913 sipe_core_media_initiate_call(sipe_public,
914 sipe_private->test_call_bot_uri, FALSE);
917 void
918 process_incoming_invite_call(struct sipe_core_private *sipe_private,
919 struct sipmsg *msg)
921 struct sipe_media_call_private *call_private = sipe_private->media_call;
922 struct sipe_backend_media *backend_media;
923 struct sipe_backend_media_relays *backend_media_relays = NULL;
924 struct sdpmsg *smsg;
925 gboolean has_new_media = FALSE;
926 GSList *i;
928 if (call_private && !is_media_session_msg(call_private, msg)) {
929 sip_transport_response(sipe_private, msg, 486, "Busy Here", NULL);
930 return;
933 smsg = sdpmsg_parse_msg(msg->body);
934 if (!smsg) {
935 sip_transport_response(sipe_private, msg,
936 488, "Not Acceptable Here", NULL);
937 sipe_media_hangup(call_private);
938 return;
941 if (!call_private) {
942 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
943 struct sip_session *session;
945 call_private = sipe_media_call_new(sipe_private, with, FALSE, smsg->ice_version);
946 session = sipe_session_add_call(sipe_private, with);
947 sipe_media_dialog_init(session, msg);
949 call_private->with = g_strdup(session->with);
950 sipe_private->media_call = call_private;
951 g_free(with);
954 backend_media = call_private->public.backend_private;
956 if (call_private->invitation)
957 sipmsg_free(call_private->invitation);
958 call_private->invitation = sipmsg_copy(msg);
960 if (smsg->media)
961 backend_media_relays = sipe_backend_media_relays_convert(
962 sipe_private->media_relays,
963 sipe_private->media_relay_username,
964 sipe_private->media_relay_password);
966 // Create any new media streams
967 for (i = smsg->media; i; i = i->next) {
968 struct sdpmedia *media = i->data;
969 gchar *id = media->name;
970 SipeMediaType type;
972 if ( media->port != 0
973 && !sipe_backend_media_get_stream_by_id(backend_media, id)) {
974 gchar *with;
976 if (sipe_strequal(id, "audio"))
977 type = SIPE_MEDIA_AUDIO;
978 else if (sipe_strequal(id, "video"))
979 type = SIPE_MEDIA_VIDEO;
980 else
981 continue;
983 with = parse_from(sipmsg_find_header(msg, "From"));
984 sipe_backend_media_add_stream(backend_media, id, with,
985 type,
986 smsg->ice_version,
987 FALSE,
988 backend_media_relays);
989 has_new_media = TRUE;
990 g_free(with);
994 sipe_backend_media_relays_free(backend_media_relays);
996 if (has_new_media) {
997 sdpmsg_free(call_private->smsg);
998 call_private->smsg = smsg;
999 sip_transport_response(sipe_private, call_private->invitation,
1000 180, "Ringing", NULL);
1001 // Processing continues in stream_initialized_cb
1002 } else {
1003 apply_remote_message(call_private, smsg);
1004 send_response_with_session_description(call_private, 200, "OK");
1006 sdpmsg_free(smsg);
1010 void process_incoming_cancel_call(struct sipe_core_private *sipe_private,
1011 struct sipmsg *msg)
1013 struct sipe_media_call_private *call_private = sipe_private->media_call;
1015 // We respond to the CANCEL request with 200 OK response and
1016 // with 487 Request Terminated to the remote INVITE in progress.
1017 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1019 if (call_private->invitation) {
1020 sip_transport_response(sipe_private, call_private->invitation,
1021 487, "Request Terminated", NULL);
1024 sipe_media_hangup(call_private);
1027 static gboolean
1028 sipe_media_send_ack(struct sipe_core_private *sipe_private,
1029 struct sipmsg *msg,
1030 struct transaction *trans)
1032 struct sipe_media_call_private *call_private = sipe_private->media_call;
1033 struct sip_session *session;
1034 struct sip_dialog *dialog;
1035 int tmp_cseq;
1037 if (!is_media_session_msg(call_private, msg))
1038 return FALSE;
1040 session = sipe_session_find_call(sipe_private, call_private->with);
1041 dialog = session->dialogs->data;
1042 if (!dialog)
1043 return FALSE;
1045 tmp_cseq = dialog->cseq;
1047 dialog->cseq = sip_transaction_cseq(trans) - 1;
1048 sip_transport_ack(sipe_private, dialog);
1049 dialog->cseq = tmp_cseq;
1051 dialog->outgoing_invite = NULL;
1053 return TRUE;
1056 static gboolean
1057 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
1058 struct sipmsg *msg,
1059 struct transaction *trans)
1061 if (!sipe_media_send_ack(sipe_private, msg, trans))
1062 return FALSE;
1064 sipe_backend_media_accept(sipe_private->media_call->public.backend_private,
1065 FALSE);
1067 return TRUE;
1070 static void
1071 reinvite_on_candidate_pair_cb(struct sipe_core_public *sipe_public)
1073 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1074 struct sipe_media_call_private *media_call = sipe_private->media_call;
1075 struct sipe_backend_media *backend_media;
1076 GSList *streams;
1078 if (!media_call)
1079 return;
1081 backend_media = media_call->public.backend_private;
1082 streams = sipe_backend_media_get_streams(backend_media);
1084 for (; streams; streams = streams->next) {
1085 struct sipe_backend_stream *s = streams->data;
1086 GList *remote_candidates = sipe_backend_media_get_active_remote_candidates(backend_media, s);
1087 guint components = g_list_length(remote_candidates);
1089 sipe_media_candidate_list_free(remote_candidates);
1091 // We must have candidates for both (RTP + RTCP) components ready
1092 if (components < 2) {
1093 sipe_schedule_mseconds(sipe_private,
1094 "<+media-reinvite-on-candidate-pair>",
1095 NULL,
1096 500,
1097 (sipe_schedule_action) reinvite_on_candidate_pair_cb,
1098 NULL);
1099 return;
1103 sipe_invite_call(sipe_private, sipe_media_send_final_ack);
1106 static gboolean
1107 process_invite_call_response(struct sipe_core_private *sipe_private,
1108 struct sipmsg *msg,
1109 struct transaction *trans)
1111 const gchar *with;
1112 struct sipe_media_call_private *call_private = sipe_private->media_call;
1113 struct sipe_backend_media *backend_private;
1114 struct sip_session *session;
1115 struct sip_dialog *dialog;
1116 struct sdpmsg *smsg;
1118 if (!is_media_session_msg(call_private, msg))
1119 return FALSE;
1121 session = sipe_session_find_call(sipe_private, call_private->with);
1122 dialog = session->dialogs->data;
1124 backend_private = call_private->public.backend_private;
1125 with = dialog->with;
1127 dialog->outgoing_invite = NULL;
1129 if (msg->response >= 400) {
1130 // Call rejected by remote peer or an error occurred
1131 const gchar *title;
1132 GString *desc = g_string_new("");
1133 gboolean append_responsestr = FALSE;
1135 switch (msg->response) {
1136 case 480: {
1137 title = _("User unavailable");
1139 if (sipmsg_parse_warning(msg, NULL) == 391) {
1140 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
1141 } else
1142 g_string_append_printf(desc, _("User %s is not available"), with);
1143 break;
1145 case 603:
1146 case 605:
1147 title = _("Call rejected");
1148 g_string_append_printf(desc, _("User %s rejected call"), with);
1149 break;
1150 case 488: {
1151 /* Check for incompatible encryption levels error.
1153 * MS Lync 2010:
1154 * 488 Not Acceptable Here
1155 * ms-client-diagnostics: 52017;reason="Encryption levels dont match"
1157 * older clients (and SIPE itself):
1158 * 488 Encryption Levels not compatible
1160 const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics");
1162 if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") ||
1163 (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) {
1164 title = _("Unable to establish a call");
1165 g_string_append(desc, _("Encryption settings of peer are incompatible with ours."));
1166 break;
1169 if (call_private->ice_version == SIPE_ICE_RFC_5245 &&
1170 sip_transaction_cseq(trans) == 1) {
1171 gchar *with = g_strdup(call_private->with);
1172 gboolean with_video = sipe_backend_media_get_stream_by_id(backend_private, "video") != NULL;
1174 sipe_media_hangup(call_private);
1175 // We might be calling to OC 2007 instance, retry with ICEv6
1176 sipe_media_initiate_call(sipe_private, with,
1177 SIPE_ICE_DRAFT_6, with_video);
1179 g_free(with);
1180 return TRUE;
1182 // Break intentionally omitted
1184 default:
1185 title = _("Error occured");
1186 g_string_append(desc, _("Unable to establish a call"));
1187 append_responsestr = TRUE;
1188 break;
1191 if (append_responsestr) {
1192 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1194 g_string_append_printf(desc, "\n%d %s",
1195 msg->response, msg->responsestr);
1196 if (reason) {
1197 g_string_append_printf(desc, "\n\n%s", reason);
1198 g_free(reason);
1202 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str);
1203 g_string_free(desc, TRUE);
1205 sipe_media_send_ack(sipe_private, msg, trans);
1206 sipe_media_hangup(call_private);
1208 return TRUE;
1211 sipe_dialog_parse(dialog, msg, TRUE);
1212 smsg = sdpmsg_parse_msg(msg->body);
1213 if (!smsg) {
1214 sip_transport_response(sipe_private, msg,
1215 488, "Not Acceptable Here", NULL);
1216 sipe_media_hangup(call_private);
1217 return FALSE;
1220 apply_remote_message(call_private, smsg);
1221 sdpmsg_free(smsg);
1223 sipe_media_send_ack(sipe_private, msg, trans);
1224 reinvite_on_candidate_pair_cb(SIPE_CORE_PUBLIC);
1226 return TRUE;
1229 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
1230 struct sipmsg *msg)
1232 if (call_private) {
1233 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
1234 struct sip_session *session;
1236 session = sipe_session_find_call(call_private->sipe_private,
1237 call_private->with);
1238 if (session) {
1239 struct sip_dialog *dialog = session->dialogs->data;
1240 return sipe_strequal(dialog->callid, callid);
1243 return FALSE;
1246 void sipe_media_handle_going_offline(struct sipe_media_call_private *call_private)
1248 struct sipe_backend_media *backend_private;
1250 backend_private = call_private->public.backend_private;
1252 if ( !sipe_backend_media_is_initiator(backend_private, NULL)
1253 && !sipe_backend_media_accepted(backend_private)) {
1254 sip_transport_response(call_private->sipe_private,
1255 call_private->invitation,
1256 480, "Temporarily Unavailable", NULL);
1257 } else {
1258 struct sip_session *session;
1260 session = sipe_session_find_call(call_private->sipe_private,
1261 call_private->with);
1262 if (session)
1263 sipe_session_close(call_private->sipe_private, session);
1266 sipe_media_hangup(call_private);
1269 gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private)
1271 return g_strstr_len(call_private->with, -1, "app:conf:audio-video:") != NULL;
1274 static void
1275 sipe_media_relay_free(struct sipe_media_relay *relay)
1277 g_free(relay->hostname);
1278 if (relay->dns_query)
1279 sipe_backend_dns_query_cancel(relay->dns_query);
1280 g_free(relay);
1283 void
1284 sipe_media_relay_list_free(GSList *list)
1286 for (; list; list = g_slist_delete_link(list, list))
1287 sipe_media_relay_free(list->data);
1290 static void
1291 relay_ip_resolved_cb(struct sipe_media_relay* relay,
1292 const gchar *ip, SIPE_UNUSED_PARAMETER guint port)
1294 gchar *hostname = relay->hostname;
1295 relay->dns_query = NULL;
1297 if (ip && port) {
1298 relay->hostname = g_strdup(ip);
1299 SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip);
1300 } else {
1301 relay->hostname = NULL;
1302 SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname);
1305 g_free(hostname);
1308 static gboolean
1309 process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private,
1310 struct sipmsg *msg,
1311 SIPE_UNUSED_PARAMETER struct transaction *trans)
1313 g_free(sipe_private->media_relay_username);
1314 g_free(sipe_private->media_relay_password);
1315 sipe_media_relay_list_free(sipe_private->media_relays);
1316 sipe_private->media_relay_username = NULL;
1317 sipe_private->media_relay_password = NULL;
1318 sipe_private->media_relays = NULL;
1320 if (msg->response >= 400) {
1321 SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. "
1322 "Failed to obtain A/V Edge credentials.");
1323 return FALSE;
1326 if (msg->response == 200) {
1327 sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
1329 if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) {
1330 const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials");
1331 const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList");
1332 const sipe_xml *item;
1333 GSList *relays = NULL;
1335 item = sipe_xml_child(xn_credentials, "username");
1336 sipe_private->media_relay_username = sipe_xml_data(item);
1337 item = sipe_xml_child(xn_credentials, "password");
1338 sipe_private->media_relay_password = sipe_xml_data(item);
1340 for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) {
1341 struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1);
1342 const sipe_xml *node;
1343 gchar *tmp;
1345 node = sipe_xml_child(item, "hostName");
1346 relay->hostname = sipe_xml_data(node);
1348 node = sipe_xml_child(item, "udpPort");
1349 if (node) {
1350 relay->udp_port = atoi(tmp = sipe_xml_data(node));
1351 g_free(tmp);
1354 node = sipe_xml_child(item, "tcpPort");
1355 if (node) {
1356 relay->tcp_port = atoi(tmp = sipe_xml_data(node));
1357 g_free(tmp);
1360 relays = g_slist_append(relays, relay);
1362 relay->dns_query = sipe_backend_dns_query_a(
1363 SIPE_CORE_PUBLIC,
1364 relay->hostname,
1365 relay->udp_port,
1366 (sipe_dns_resolved_cb) relay_ip_resolved_cb,
1367 relay);
1369 SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d",
1370 relay->hostname,
1371 relay->tcp_port, relay->udp_port);
1374 sipe_private->media_relays = relays;
1377 sipe_xml_free(xn_response);
1380 return TRUE;
1383 void
1384 sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private)
1386 // TODO: re-request credentials after duration expires?
1387 const char CRED_REQUEST_XML[] =
1388 "<request requestID=\"%d\" "
1389 "from=\"%s\" "
1390 "version=\"1.0\" "
1391 "to=\"%s\" "
1392 "xmlns=\"http://schemas.microsoft.com/2006/09/sip/mrasp\" "
1393 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
1394 "<credentialsRequest credentialsRequestID=\"%d\">"
1395 "<identity>%s</identity>"
1396 "<location>%s</location>"
1397 "<duration>480</duration>"
1398 "</credentialsRequest>"
1399 "</request>";
1401 int request_id = rand();
1402 gchar *self;
1403 gchar *body;
1405 if (!sipe_private->mras_uri)
1406 return;
1408 self = sip_uri_self(sipe_private);
1410 body = g_strdup_printf(
1411 CRED_REQUEST_XML,
1412 request_id,
1413 self,
1414 sipe_private->mras_uri,
1415 request_id,
1416 self,
1417 SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet");
1418 g_free(self);
1420 sip_transport_service(sipe_private,
1421 sipe_private->mras_uri,
1422 "Content-Type: application/msrtc-media-relay-auth+xml\r\n",
1423 body,
1424 process_get_av_edge_credentials_response);
1426 g_free(body);
1430 Local Variables:
1431 mode: c
1432 c-file-style: "bsd"
1433 indent-tabs-mode: t
1434 tab-width: 8
1435 End: