appshare: move default to core
[siplcs/bazlsi01.git] / src / core / sipe-media.c
blob1d4d95fcb1048387b4b62363cf61f007fc4db291
1 /**
2 * @file sipe-media.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2016 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-xml.h"
51 /* [MS-SDPEXT] 3.1.5.31.2 says a range size of 100 SHOULD be used for video and
52 * some clients really demand this. */
53 #define VIDEO_SSRC_COUNT 100
55 struct sipe_media_call_private {
56 struct sipe_media_call public;
58 /* private part starts here */
59 struct sipe_core_private *sipe_private;
61 struct sip_session *session;
62 struct sip_session *conference_session;
64 GSList *streams;
66 struct sipmsg *invitation;
67 SipeIceVersion ice_version;
68 gboolean encryption_compatible;
69 gchar *extra_invite_section;
70 gchar *invite_content_type;
72 GSList *ssrc_ranges;
74 struct sdpmsg *smsg;
75 GSList *failed_media;
77 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
78 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
80 struct sipe_media_stream_private {
81 struct sipe_media_stream public;
83 guchar *encryption_key;
84 int encryption_key_id;
85 gboolean remote_candidates_and_codecs_set;
86 gboolean established;
87 #ifdef HAVE_XDATA
88 gboolean sdp_negotiation_concluded;
89 gboolean writable;
90 #endif
92 GSList *extra_sdp;
94 GQueue *write_queue;
95 GQueue *async_reads;
96 gssize read_pos;
98 /* User data associated with the stream. */
99 gpointer data;
100 GDestroyNotify data_free_func;
102 #define SIPE_MEDIA_STREAM ((struct sipe_media_stream *) stream_private)
103 #define SIPE_MEDIA_STREAM_PRIVATE ((struct sipe_media_stream_private *) stream)
105 struct async_read_data {
106 guint8 *buffer;
107 gssize len;
108 sipe_media_stream_read_callback callback;
111 static void sipe_media_codec_list_free(GList *codecs)
113 for (; codecs; codecs = g_list_delete_link(codecs, codecs))
114 sipe_backend_codec_free(codecs->data);
117 static void sipe_media_candidate_list_free(GList *candidates)
119 for (; candidates; candidates = g_list_delete_link(candidates, candidates))
120 sipe_backend_candidate_free(candidates->data);
123 static void
124 sipe_media_stream_free(struct sipe_media_stream_private *stream_private)
126 struct sipe_media_call_private *call_private;
128 call_private = (struct sipe_media_call_private *)SIPE_MEDIA_STREAM->call;
130 sipe_media_stream_set_data(SIPE_MEDIA_STREAM, NULL, NULL);
132 if (call_private) {
133 call_private->streams = g_slist_remove(call_private->streams,
134 stream_private);
136 if (SIPE_MEDIA_STREAM->ssrc_range) {
137 call_private->ssrc_ranges =
138 g_slist_remove(call_private->ssrc_ranges,
139 SIPE_MEDIA_STREAM->ssrc_range);
143 if (SIPE_MEDIA_STREAM->backend_private) {
144 sipe_backend_media_stream_free(SIPE_MEDIA_STREAM->backend_private);
146 g_free(SIPE_MEDIA_STREAM->id);
147 g_free(stream_private->encryption_key);
148 g_queue_free_full(stream_private->write_queue,
149 (GDestroyNotify)g_byte_array_unref);
150 g_queue_free_full(stream_private->async_reads, g_free);
151 sipe_utils_nameval_free(stream_private->extra_sdp);
152 g_free(stream_private);
155 static gboolean
156 call_private_equals(SIPE_UNUSED_PARAMETER const gchar *callid,
157 struct sipe_media_call_private *call_private1,
158 struct sipe_media_call_private *call_private2)
160 return call_private1 == call_private2;
163 static void
164 sipe_media_call_free(struct sipe_media_call_private *call_private)
166 if (call_private) {
167 g_hash_table_foreach_remove(call_private->sipe_private->media_calls,
168 (GHRFunc) call_private_equals, call_private);
170 while (call_private->streams) {
171 sipe_media_stream_free(call_private->streams->data);
174 sipe_backend_media_free(call_private->public.backend_private);
176 if (call_private->session) {
177 sipe_session_remove(call_private->sipe_private,
178 call_private->session);
181 if (call_private->invitation)
182 sipmsg_free(call_private->invitation);
184 // Frees any referenced extra invite data.
185 sipe_media_add_extra_invite_section(SIPE_MEDIA_CALL, NULL, NULL);
187 sipe_utils_slist_free_full(call_private->ssrc_ranges, g_free);
189 sdpmsg_free(call_private->smsg);
190 sipe_utils_slist_free_full(call_private->failed_media,
191 (GDestroyNotify)sdpmedia_free);
192 g_free(SIPE_MEDIA_CALL->with);
193 g_free(call_private);
197 static gint
198 candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
200 int cmp = sipe_strcompare(c1->foundation, c2->foundation);
201 if (cmp == 0) {
202 cmp = sipe_strcompare(c1->username, c2->username);
203 if (cmp == 0)
204 cmp = c1->component - c2->component;
207 return cmp;
210 static GSList *
211 backend_candidates_to_sdpcandidate(GList *candidates)
213 GSList *result = NULL;
214 GList *i;
216 for (i = candidates; i; i = i->next) {
217 struct sipe_backend_candidate *candidate = i->data;
218 struct sdpcandidate *c;
220 gchar *ip = sipe_backend_candidate_get_ip(candidate);
221 gchar *base_ip = sipe_backend_candidate_get_base_ip(candidate);
222 if (is_empty(ip) || strchr(ip, ':') ||
223 (base_ip && strchr(base_ip, ':'))) {
224 /* Ignore IPv6 candidates. */
225 g_free(ip);
226 g_free(base_ip);
227 continue;
230 c = g_new(struct sdpcandidate, 1);
231 c->foundation = sipe_backend_candidate_get_foundation(candidate);
232 c->component = sipe_backend_candidate_get_component_type(candidate);
233 c->type = sipe_backend_candidate_get_type(candidate);
234 c->protocol = sipe_backend_candidate_get_protocol(candidate);
235 c->ip = ip;
236 c->port = sipe_backend_candidate_get_port(candidate);
237 c->base_ip = base_ip;
238 c->base_port = sipe_backend_candidate_get_base_port(candidate);
239 c->priority = sipe_backend_candidate_get_priority(candidate);
240 c->username = sipe_backend_candidate_get_username(candidate);
241 c->password = sipe_backend_candidate_get_password(candidate);
243 result = g_slist_insert_sorted(result, c,
244 (GCompareFunc)candidate_sort_cb);
247 return result;
250 static void
251 get_stream_ip_and_ports(GSList *candidates,
252 gchar **ip, guint *rtp_port, guint *rtcp_port)
254 guint32 rtp_max_priority = 0;
255 guint32 rtcp_max_priority = 0;
257 *ip = 0;
258 *rtp_port = 0;
259 *rtcp_port = 0;
261 for (; candidates; candidates = candidates->next) {
262 struct sdpcandidate *candidate = candidates->data;
264 if (candidate->component == SIPE_COMPONENT_RTP &&
265 candidate->priority > rtp_max_priority) {
266 rtp_max_priority = candidate->priority;
267 *rtp_port = candidate->port;
269 g_free(*ip);
270 *ip = g_strdup(candidate->ip);
271 } else if (candidate->component == SIPE_COMPONENT_RTCP &&
272 candidate->priority > rtcp_max_priority) {
273 rtcp_max_priority = candidate->priority;
274 *rtcp_port = candidate->port;
279 static gint
280 sdpcodec_compare(gconstpointer a, gconstpointer b)
282 return ((const struct sdpcodec *)a)->id -
283 ((const struct sdpcodec *)b)->id;
286 static GList *
287 remove_wrong_farstream_0_1_tcp_candidates(GList *candidates)
289 GList *i = candidates;
290 GHashTable *foundation_to_candidate = g_hash_table_new_full(g_str_hash,
291 g_str_equal,
292 g_free,
293 NULL);
295 while (i) {
296 GList *next = i->next;
297 struct sipe_backend_candidate *c1 = i->data;
299 if (sipe_backend_candidate_get_protocol(c1) == SIPE_NETWORK_PROTOCOL_UDP) {
300 gchar *foundation = sipe_backend_candidate_get_foundation(c1);
301 struct sipe_backend_candidate *c2 = g_hash_table_lookup(foundation_to_candidate,
302 foundation);
304 if (c2) {
305 g_free(foundation);
307 if (sipe_backend_candidate_get_port(c1) ==
308 sipe_backend_candidate_get_port(c2) ||
309 (sipe_backend_candidate_get_type(c1) !=
310 SIPE_CANDIDATE_TYPE_HOST &&
311 sipe_backend_candidate_get_base_port(c1) ==
312 sipe_backend_candidate_get_base_port(c2))) {
314 * We assume that RTP+RTCP UDP pairs
315 * that share the same port are
316 * actually mistagged TCP candidates.
318 candidates = g_list_remove(candidates, c2);
319 candidates = g_list_delete_link(candidates, i);
320 sipe_backend_candidate_free(c1);
321 sipe_backend_candidate_free(c2);
323 } else
324 /* hash table takes ownership of "foundation" */
325 g_hash_table_insert(foundation_to_candidate, foundation, c1);
328 i = next;
331 g_hash_table_destroy(foundation_to_candidate);
333 return candidates;
336 static void
337 fill_zero_tcp_act_ports_from_tcp_pass(GSList *candidates, GSList *all_candidates)
339 GSList *i;
340 GHashTable *ip_to_port = g_hash_table_new(g_str_hash, g_str_equal);
342 for (i = candidates; i; i = i->next) {
343 struct sdpcandidate *c = i->data;
344 GSList *j;
346 if (c->protocol != SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
347 continue;
350 for (j = all_candidates; j; j = j->next) {
351 struct sdpcandidate *passive = j->data;
352 if (passive->protocol != SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
353 c->type != passive->type) {
354 continue;
357 if (sipe_strequal(c->ip, passive->ip) &&
358 sipe_strequal(c->base_ip, passive->base_ip)) {
359 if (c->port == 0) {
360 c->port = passive->port;
363 if (c->base_port == 0) {
364 c->base_port = passive->base_port;
366 break;
371 for (i = all_candidates; i; i = i->next) {
372 struct sdpcandidate *c = i->data;
374 if (c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE &&
375 c->type == SIPE_CANDIDATE_TYPE_HOST) {
376 g_hash_table_insert(ip_to_port, c->ip, &c->port);
380 /* Fill base ports of all TCP relay candidates using what we have
381 * collected from host candidates. */
382 for (i = candidates; i; i = i->next) {
383 struct sdpcandidate *c = i->data;
384 if (c->type == SIPE_CANDIDATE_TYPE_RELAY && c->base_port == 0) {
385 guint *base_port = (guint*)g_hash_table_lookup(ip_to_port, c->base_ip);
386 if (base_port) {
387 c->base_port = *base_port;
388 } else {
389 SIPE_DEBUG_WARNING("Couldn't determine base port for candidate "
390 "with foundation %s", c->foundation);
395 g_hash_table_destroy(ip_to_port);
398 static SipeEncryptionPolicy
399 get_encryption_policy(struct sipe_core_private *sipe_private)
401 SipeEncryptionPolicy result =
402 sipe_backend_media_get_encryption_policy(SIPE_CORE_PUBLIC);
403 if (result == SIPE_ENCRYPTION_POLICY_OBEY_SERVER) {
404 result = sipe_private->server_av_encryption_policy;
407 return result;
410 static struct sdpmedia *
411 media_stream_to_sdpmedia(struct sipe_media_call_private *call_private,
412 struct sipe_media_stream_private *stream_private)
414 struct sdpmedia *sdpmedia = g_new0(struct sdpmedia, 1);
415 GList *codecs = sipe_backend_get_local_codecs(SIPE_MEDIA_CALL,
416 SIPE_MEDIA_STREAM);
417 SipeEncryptionPolicy encryption_policy =
418 get_encryption_policy(call_private->sipe_private);
419 guint rtcp_port = 0;
420 SipeMediaType type;
421 GSList *attributes = NULL;
422 GSList *sdpcandidates;
423 GSList *all_sdpcandidates;
424 GList *candidates;
425 GList *i;
426 GSList *j;
428 sdpmedia->name = g_strdup(SIPE_MEDIA_STREAM->id);
430 if (sipe_strequal(sdpmedia->name, "audio"))
431 type = SIPE_MEDIA_AUDIO;
432 else if (sipe_strequal(sdpmedia->name, "video"))
433 type = SIPE_MEDIA_VIDEO;
434 else if (sipe_strequal(sdpmedia->name, "data"))
435 type = SIPE_MEDIA_APPLICATION;
436 else if (sipe_strequal(sdpmedia->name, "applicationsharing"))
437 type = SIPE_MEDIA_APPLICATION;
438 else {
439 // TODO: incompatible media, should not happen here
440 g_free(sdpmedia->name);
441 g_free(sdpmedia);
442 sipe_media_codec_list_free(codecs);
443 return(NULL);
446 // Process codecs
447 for (i = codecs; i; i = i->next) {
448 struct sipe_backend_codec *codec = i->data;
449 struct sdpcodec *c = g_new0(struct sdpcodec, 1);
450 GList *params;
452 c->id = sipe_backend_codec_get_id(codec);
453 c->name = sipe_backend_codec_get_name(codec);
454 c->clock_rate = sipe_backend_codec_get_clock_rate(codec);
455 c->type = type;
457 params = sipe_backend_codec_get_optional_parameters(codec);
458 for (; params; params = params->next) {
459 struct sipnameval *param = params->data;
460 struct sipnameval *copy = g_new0(struct sipnameval, 1);
462 copy->name = g_strdup(param->name);
463 copy->value = g_strdup(param->value);
465 c->parameters = g_slist_append(c->parameters, copy);
468 /* Buggy(?) codecs may report non-unique id (a.k.a. payload
469 * type) that must not appear in SDP messages we send. Thus,
470 * let's ignore any codec having the same id as one we already
471 * have in the converted list. */
472 if (g_slist_find_custom(sdpmedia->codecs, c, sdpcodec_compare)) {
473 sdpcodec_free(c);
474 } else {
475 sdpmedia->codecs = g_slist_append(sdpmedia->codecs, c);
479 sipe_media_codec_list_free(codecs);
481 // Process local candidates
482 // If we have established candidate pairs, send them in SDP response.
483 // Otherwise send all available local candidates.
484 candidates = sipe_backend_media_stream_get_active_local_candidates(SIPE_MEDIA_STREAM);
485 sdpcandidates = backend_candidates_to_sdpcandidate(candidates);
486 sipe_media_candidate_list_free(candidates);
488 candidates = sipe_backend_get_local_candidates(SIPE_MEDIA_CALL,
489 SIPE_MEDIA_STREAM);
490 candidates = remove_wrong_farstream_0_1_tcp_candidates(candidates);
491 all_sdpcandidates = backend_candidates_to_sdpcandidate(candidates);
492 sipe_media_candidate_list_free(candidates);
494 if (!sdpcandidates) {
495 sdpcandidates = all_sdpcandidates;
498 fill_zero_tcp_act_ports_from_tcp_pass(sdpcandidates, all_sdpcandidates);
500 sdpmedia->candidates = sdpcandidates;
502 if (all_sdpcandidates != sdpcandidates) {
503 sipe_utils_slist_free_full(all_sdpcandidates,
504 (GDestroyNotify)sdpcandidate_free);
507 get_stream_ip_and_ports(sdpmedia->candidates, &sdpmedia->ip,
508 &sdpmedia->port, &rtcp_port);
510 if (sipe_backend_stream_is_held(SIPE_MEDIA_STREAM))
511 attributes = sipe_utils_nameval_add(attributes, "inactive", "");
513 if (rtcp_port) {
514 gchar *tmp = g_strdup_printf("%u", rtcp_port);
515 attributes = sipe_utils_nameval_add(attributes, "rtcp", tmp);
516 g_free(tmp);
519 if (encryption_policy != call_private->sipe_private->server_av_encryption_policy) {
520 const gchar *encryption = NULL;
521 switch (encryption_policy) {
522 case SIPE_ENCRYPTION_POLICY_REJECTED:
523 encryption = "rejected";
524 break;
525 case SIPE_ENCRYPTION_POLICY_OPTIONAL:
526 encryption = "optional";
527 break;
528 case SIPE_ENCRYPTION_POLICY_REQUIRED:
529 default:
530 encryption = "required";
531 break;
534 attributes = sipe_utils_nameval_add(attributes, "encryption", encryption);
537 if (SIPE_MEDIA_STREAM->ssrc_range) {
538 gchar *tmp;
540 tmp = g_strdup_printf("%u-%u",
541 SIPE_MEDIA_STREAM->ssrc_range->begin,
542 SIPE_MEDIA_STREAM->ssrc_range->end);
543 attributes = sipe_utils_nameval_add(attributes,
544 "x-ssrc-range", tmp);
545 g_free(tmp);
548 // Process remote candidates
549 candidates = sipe_backend_media_stream_get_active_remote_candidates(SIPE_MEDIA_STREAM);
550 sdpmedia->remote_candidates = backend_candidates_to_sdpcandidate(candidates);
551 sipe_media_candidate_list_free(candidates);
553 sdpmedia->encryption_active = stream_private->encryption_key &&
554 call_private->encryption_compatible &&
555 stream_private->remote_candidates_and_codecs_set &&
556 encryption_policy != SIPE_ENCRYPTION_POLICY_REJECTED;
558 // Set our key if encryption is enabled.
559 if (stream_private->encryption_key &&
560 encryption_policy != SIPE_ENCRYPTION_POLICY_REJECTED) {
561 sdpmedia->encryption_key = g_memdup(stream_private->encryption_key,
562 SIPE_SRTP_KEY_LEN);
563 sdpmedia->encryption_key_id = stream_private->encryption_key_id;
566 // Append extra attributes assigned to the stream.
567 for (j = stream_private->extra_sdp; j; j = g_slist_next(j)) {
568 struct sipnameval *attr = j->data;
569 attributes = sipe_utils_nameval_add(attributes,
570 attr->name, attr->value);
573 sdpmedia->attributes = attributes;
575 return sdpmedia;
578 static struct sdpmsg *
579 sipe_media_to_sdpmsg(struct sipe_media_call_private *call_private)
581 struct sdpmsg *msg = g_new0(struct sdpmsg, 1);
582 GSList *streams = call_private->streams;
584 for (; streams; streams = streams->next) {
585 struct sdpmedia *media = media_stream_to_sdpmedia(call_private,
586 streams->data);
587 if (media) {
588 msg->media = g_slist_append(msg->media, media);
590 if (msg->ip == NULL)
591 msg->ip = g_strdup(media->ip);
595 msg->media = g_slist_concat(msg->media, call_private->failed_media);
596 call_private->failed_media = NULL;
598 msg->ice_version = call_private->ice_version;
600 return msg;
603 static void
604 sipe_invite_call(struct sipe_media_call_private *call_private, TransCallback tc)
606 struct sipe_core_private *sipe_private = call_private->sipe_private;
607 gchar *hdr;
608 gchar *contact;
609 gchar *p_preferred_identity = NULL;
610 gchar *body;
611 struct sip_dialog *dialog;
612 struct sdpmsg *msg;
614 dialog = sipe_media_get_sip_dialog(SIPE_MEDIA_CALL);
616 contact = get_contact(sipe_private);
618 if (sipe_private->uc_line_uri) {
619 gchar *self = sip_uri_self(sipe_private);
620 p_preferred_identity = g_strdup_printf(
621 "P-Preferred-Identity: <%s>, <%s>\r\n",
622 self, sipe_private->uc_line_uri);
623 g_free(self);
626 hdr = g_strdup_printf(
627 "ms-keep-alive: UAC;hop-hop=yes\r\n"
628 "Contact: %s\r\n"
629 "%s"
630 "Content-Type: %s%s\r\n",
631 contact,
632 p_preferred_identity ? p_preferred_identity : "",
633 call_private->invite_content_type ?
634 call_private->invite_content_type : "application/sdp",
635 call_private->invite_content_type ?
636 ";boundary=\"----=_NextPart_000_001E_01CB4397.0B5EB570\"" : "");
638 g_free(contact);
639 g_free(p_preferred_identity);
641 msg = sipe_media_to_sdpmsg(call_private);
642 body = sdpmsg_to_string(msg);
644 if (call_private->extra_invite_section) {
645 gchar *tmp;
646 tmp = g_strdup_printf(
647 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
648 "%s"
649 "\r\n"
650 "------=_NextPart_000_001E_01CB4397.0B5EB570\r\n"
651 "Content-Type: application/sdp\r\n"
652 "Content-Transfer-Encoding: 7bit\r\n"
653 "Content-Disposition: session; handling=optional\r\n"
654 "\r\n"
655 "%s"
656 "\r\n"
657 "------=_NextPart_000_001E_01CB4397.0B5EB570--\r\n",
658 call_private->extra_invite_section, body);
659 g_free(body);
660 body = tmp;
661 sipe_media_add_extra_invite_section(SIPE_MEDIA_CALL, NULL, NULL);
664 sdpmsg_free(msg);
666 dialog->outgoing_invite = sip_transport_invite(sipe_private,
667 hdr,
668 body,
669 dialog,
670 tc);
672 g_free(body);
673 g_free(hdr);
676 static void
677 send_response_with_session_description(struct sipe_media_call_private *call_private, int code, gchar *text)
679 struct sdpmsg *msg = sipe_media_to_sdpmsg(call_private);
680 gchar *body = sdpmsg_to_string(msg);
681 sdpmsg_free(msg);
682 sipmsg_add_header(call_private->invitation, "Content-Type", "application/sdp");
683 sip_transport_response(call_private->sipe_private, call_private->invitation, code, text, body);
684 g_free(body);
687 static gboolean
688 process_invite_call_response(struct sipe_core_private *sipe_private,
689 struct sipmsg *msg,
690 struct transaction *trans);
692 struct sipe_media_stream *
693 sipe_core_media_get_stream_by_id(struct sipe_media_call *call, const gchar *id)
695 GSList *i;
696 for (i = SIPE_MEDIA_CALL_PRIVATE->streams; i; i = i->next) {
697 struct sipe_media_stream *stream = i->data;
698 if (sipe_strequal(stream->id, id))
699 return stream;
701 return NULL;
704 static gboolean
705 update_call_from_remote_sdp(struct sipe_media_call_private* call_private,
706 struct sdpmedia *media)
708 struct sipe_media_stream *stream;
709 GList *backend_candidates = NULL;
710 GList *backend_codecs = NULL;
711 GSList *i;
712 gboolean result = TRUE;
714 stream = sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, media->name);
715 if (media->port == 0) {
716 if (stream) {
717 sipe_backend_media_stream_end(SIPE_MEDIA_CALL, stream);
719 return TRUE;
722 if (!stream)
723 return FALSE;
725 if (sipe_utils_nameval_find(media->attributes, "inactive")) {
726 sipe_backend_stream_hold(SIPE_MEDIA_CALL, stream, FALSE);
727 } else if (sipe_backend_stream_is_held(stream)) {
728 sipe_backend_stream_unhold(SIPE_MEDIA_CALL, stream, FALSE);
731 if (SIPE_MEDIA_STREAM_PRIVATE->remote_candidates_and_codecs_set) {
732 return TRUE;
735 for (i = media->codecs; i; i = i->next) {
736 struct sdpcodec *c = i->data;
737 struct sipe_backend_codec *codec;
738 GSList *j;
740 codec = sipe_backend_codec_new(c->id,
741 c->name,
742 c->type,
743 c->clock_rate,
744 c->channels);
746 for (j = c->parameters; j; j = j->next) {
747 struct sipnameval *attr = j->data;
749 sipe_backend_codec_add_optional_parameter(codec,
750 attr->name,
751 attr->value);
754 backend_codecs = g_list_append(backend_codecs, codec);
757 if (media->encryption_key && SIPE_MEDIA_STREAM_PRIVATE->encryption_key) {
758 sipe_backend_media_set_encryption_keys(SIPE_MEDIA_CALL, stream,
759 SIPE_MEDIA_STREAM_PRIVATE->encryption_key,
760 media->encryption_key);
761 SIPE_MEDIA_STREAM_PRIVATE->encryption_key_id = media->encryption_key_id;
764 result = sipe_backend_set_remote_codecs(SIPE_MEDIA_CALL, stream,
765 backend_codecs);
766 sipe_media_codec_list_free(backend_codecs);
768 if (result == FALSE) {
769 sipe_backend_media_stream_end(SIPE_MEDIA_CALL, stream);
770 return FALSE;
773 for (i = media->candidates; i; i = i->next) {
774 struct sdpcandidate *c = i->data;
775 struct sipe_backend_candidate *candidate;
776 candidate = sipe_backend_candidate_new(c->foundation,
777 c->component,
778 c->type,
779 c->protocol,
780 c->ip,
781 c->port,
782 c->username,
783 c->password);
784 sipe_backend_candidate_set_priority(candidate, c->priority);
786 backend_candidates = g_list_append(backend_candidates, candidate);
789 sipe_backend_media_add_remote_candidates(SIPE_MEDIA_CALL, stream,
790 backend_candidates);
791 sipe_media_candidate_list_free(backend_candidates);
793 SIPE_MEDIA_STREAM_PRIVATE->remote_candidates_and_codecs_set = TRUE;
795 return TRUE;
798 static void
799 apply_remote_message(struct sipe_media_call_private* call_private,
800 struct sdpmsg* msg)
802 GSList *i;
804 sipe_utils_slist_free_full(call_private->failed_media, (GDestroyNotify)sdpmedia_free);
805 call_private->failed_media = NULL;
806 call_private->encryption_compatible = TRUE;
808 for (i = msg->media; i; i = i->next) {
809 struct sdpmedia *media = i->data;
810 const gchar *enc_level =
811 sipe_utils_nameval_find(media->attributes, "encryption");
812 if (sipe_strequal(enc_level, "rejected") &&
813 get_encryption_policy(call_private->sipe_private) == SIPE_ENCRYPTION_POLICY_REQUIRED) {
814 call_private->encryption_compatible = FALSE;
817 if (!update_call_from_remote_sdp(call_private, media)) {
818 media->port = 0;
819 call_private->failed_media =
820 g_slist_append(call_private->failed_media, media);
824 /* We need to keep failed medias until response is sent, remove them
825 * from sdpmsg that is to be freed. */
826 for (i = call_private->failed_media; i; i = i->next) {
827 msg->media = g_slist_remove(msg->media, i->data);
831 static gboolean
832 call_initialized(struct sipe_media_call *call)
834 GSList *streams = SIPE_MEDIA_CALL_PRIVATE->streams;
835 for (; streams; streams = streams->next) {
836 if (!sipe_backend_stream_initialized(call, streams->data)) {
837 return FALSE;
841 return TRUE;
844 // Sends an invite response when the call is accepted and local candidates were
845 // prepared, otherwise does nothing. If error response is sent, call_private is
846 // disposed before function returns.
847 static void
848 maybe_send_first_invite_response(struct sipe_media_call_private *call_private)
850 struct sipe_backend_media *backend_media;
852 backend_media = call_private->public.backend_private;
854 if (!sipe_backend_media_accepted(backend_media) ||
855 !call_initialized(&call_private->public))
856 return;
858 if (!call_private->encryption_compatible) {
859 struct sipe_core_private *sipe_private = call_private->sipe_private;
861 sipmsg_add_header(call_private->invitation, "Warning",
862 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
863 sip_transport_response(sipe_private,
864 call_private->invitation,
865 488, "Encryption Levels not compatible",
866 NULL);
867 sipe_backend_media_reject(backend_media, FALSE);
868 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
869 _("Unable to establish a call"),
870 _("Encryption settings of peer are incompatible with ours."));
871 } else {
872 send_response_with_session_description(call_private, 200, "OK");
873 sipmsg_free(call_private->invitation);
874 call_private->invitation = NULL;
878 static void
879 stream_initialized_cb(struct sipe_media_call *call,
880 struct sipe_media_stream *stream)
882 if (call_initialized(call)) {
883 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
885 if (sipe_backend_media_is_initiator(call, stream)) {
886 sipe_invite_call(call_private,
887 process_invite_call_response);
888 } else if (call_private->smsg) {
889 struct sdpmsg *smsg = call_private->smsg;
890 call_private->smsg = NULL;
892 apply_remote_message(call_private, smsg);
893 maybe_send_first_invite_response(call_private);
894 sdpmsg_free(smsg);
899 static void phone_state_publish(struct sipe_core_private *sipe_private)
901 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
902 sipe_ocs2007_phone_state_publish(sipe_private);
903 } else {
904 // TODO: OCS 2005 support. Is anyone still using it at all?
908 void
909 sipe_core_media_stream_end(struct sipe_media_stream *stream)
911 sipe_media_stream_free(SIPE_MEDIA_STREAM_PRIVATE);
914 static void
915 media_end_cb(struct sipe_media_call *call)
917 struct sipe_core_private *sipe_private;
919 g_return_if_fail(call);
921 sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private;
923 sipe_media_call_free(SIPE_MEDIA_CALL_PRIVATE);
924 phone_state_publish(sipe_private);
927 static void
928 call_accept_cb(struct sipe_media_call *call, gboolean local)
930 if (local) {
931 maybe_send_first_invite_response(SIPE_MEDIA_CALL_PRIVATE);
933 phone_state_publish(SIPE_MEDIA_CALL_PRIVATE->sipe_private);
936 static void
937 call_reject_cb(struct sipe_media_call *call, gboolean local)
939 if (local) {
940 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
942 sip_transport_response(call_private->sipe_private,
943 call_private->invitation,
944 603, "Decline", NULL);
946 if (call_private->session) {
947 sipe_session_remove(call_private->sipe_private,
948 call_private->session);
949 call_private->session = NULL;
954 static void
955 av_call_reject_cb(struct sipe_media_call *call, gboolean local)
957 if (!local) {
958 struct sipe_core_private *sipe_private;
959 gchar *desc;
961 sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private;
963 desc = g_strdup_printf(_("User %s rejected call"), call->with);
964 sipe_backend_notify_error(SIPE_CORE_PUBLIC, _("Call rejected"),
965 desc);
966 g_free(desc);
969 call_reject_cb(call, local);
972 static gboolean
973 sipe_media_send_ack(struct sipe_core_private *sipe_private, struct sipmsg *msg,
974 struct transaction *trans);
976 static void call_hold_cb(struct sipe_media_call *call,
977 gboolean local,
978 SIPE_UNUSED_PARAMETER gboolean state)
980 if (local) {
981 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE, sipe_media_send_ack);
985 static void call_hangup_cb(struct sipe_media_call *call, gboolean local)
987 if (local) {
988 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
990 if (call_private->session) {
991 sipe_session_close(call_private->sipe_private,
992 call_private->session);
993 call_private->session = NULL;
998 static void
999 error_cb(struct sipe_media_call *call, gchar *message)
1001 struct sipe_media_call_private *call_private = SIPE_MEDIA_CALL_PRIVATE;
1002 struct sipe_core_private *sipe_private = call_private->sipe_private;
1003 gboolean initiator = sipe_backend_media_is_initiator(call, NULL);
1004 gboolean accepted = sipe_backend_media_accepted(call->backend_private);
1006 gchar *title = g_strdup_printf("Call with %s failed", call->with);
1007 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, message);
1008 g_free(title);
1010 if (!initiator && !accepted) {
1011 sip_transport_response(sipe_private,
1012 call_private->invitation,
1013 488, "Not Acceptable Here", NULL);
1016 sipe_backend_media_hangup(call->backend_private, initiator || accepted);
1019 struct sipe_media_call *
1020 sipe_media_call_new(struct sipe_core_private *sipe_private, const gchar* with,
1021 struct sipmsg *msg, SipeIceVersion ice_version,
1022 SipeMediaCallFlags flags)
1024 struct sipe_media_call_private *call_private;
1025 struct sip_session *session;
1026 struct sip_dialog *dialog;
1027 gchar *cname;
1029 session = sipe_session_add_call(sipe_private, with);
1031 dialog = sipe_dialog_add(session);
1032 dialog->with = g_strdup(with);
1034 if (msg) {
1035 gchar *newTag = gentag();
1036 const gchar *oldHeader;
1037 gchar *newHeader;
1039 oldHeader = sipmsg_find_header(msg, "To");
1040 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
1041 sipmsg_remove_header_now(msg, "To");
1042 sipmsg_add_header_now(msg, "To", newHeader);
1043 g_free(newTag);
1044 g_free(newHeader);
1046 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
1047 sipe_dialog_parse(dialog, msg, FALSE);
1048 } else {
1049 dialog->callid = gencallid();
1050 dialog->ourtag = gentag();
1051 flags |= SIPE_MEDIA_CALL_INITIATOR;
1054 if (g_hash_table_lookup(sipe_private->media_calls, dialog->callid)) {
1055 SIPE_DEBUG_ERROR("sipe_media_call_new: call already exists for "
1056 "Call-ID %s", dialog->callid);
1057 sipe_session_remove(sipe_private, session);
1058 return NULL;
1061 call_private = g_new0(struct sipe_media_call_private, 1);
1062 call_private->sipe_private = sipe_private;
1063 call_private->session = session;
1064 SIPE_MEDIA_CALL->with = g_strdup(with);
1066 g_hash_table_insert(sipe_private->media_calls,
1067 g_strdup(dialog->callid), call_private);
1069 cname = g_strdup(sipe_private->contact + 1);
1070 cname[strlen(cname) - 1] = '\0';
1072 call_private->public.backend_private = sipe_backend_media_new(SIPE_CORE_PUBLIC,
1073 SIPE_MEDIA_CALL,
1074 with,
1075 flags);
1076 sipe_backend_media_set_cname(call_private->public.backend_private, cname);
1078 call_private->ice_version = ice_version;
1079 call_private->encryption_compatible = TRUE;
1081 call_private->public.stream_initialized_cb = stream_initialized_cb;
1082 call_private->public.media_end_cb = media_end_cb;
1083 call_private->public.call_accept_cb = call_accept_cb;
1084 call_private->public.call_reject_cb = call_reject_cb;
1085 call_private->public.call_hold_cb = call_hold_cb;
1086 call_private->public.call_hangup_cb = call_hangup_cb;
1087 call_private->public.error_cb = error_cb;
1089 g_free(cname);
1091 return SIPE_MEDIA_CALL;
1094 void sipe_media_hangup(struct sipe_media_call_private *call_private)
1096 if (call_private) {
1097 sipe_backend_media_hangup(call_private->public.backend_private,
1098 FALSE);
1102 static gint
1103 ssrc_range_compare(const struct ssrc_range *a, const struct ssrc_range *b)
1105 if (a->begin < b->begin) {
1106 return -1;
1108 if (a->begin > b->begin) {
1109 return 1;
1111 return 0;
1114 static void
1115 ssrc_range_update(GSList **ranges, GSList *media)
1117 for (; media; media = media->next) {
1118 struct sdpmedia *m;
1119 const char *ssrc_range;
1120 gchar **parts;
1122 m = media->data;
1123 ssrc_range = sipe_utils_nameval_find(m->attributes,
1124 "x-ssrc-range");
1125 if (!ssrc_range) {
1126 continue;
1129 parts = g_strsplit(ssrc_range, "-", 2);
1131 if (parts[0] && parts[1]) {
1132 struct ssrc_range *range;
1134 range = g_new0(struct ssrc_range, 1);
1135 range->begin = atoi(parts[0]);
1136 range->end = atoi(parts[1]);
1138 *ranges = sipe_utils_slist_insert_unique_sorted(
1139 *ranges, range,
1140 (GCompareFunc)ssrc_range_compare,
1141 g_free);
1144 g_strfreev(parts);
1148 static struct ssrc_range *
1149 ssrc_range_allocate(GSList **ranges, guint32 len)
1151 struct ssrc_range *range;
1152 GSList *i;
1154 range = g_new0(struct ssrc_range, 1);
1155 range->begin = 1;
1156 range->end = range->begin + (len - 1);
1158 for (i = *ranges; i; i = i->next) {
1159 struct ssrc_range *r = i->data;
1161 if (range->begin < r->begin && range->end < r->begin) {
1162 break;
1165 range->begin = r->end + 1;
1166 range->end = range->begin + (len - 1);
1169 /* As per [MS-SDPEXT] 3.1.5.31.1, a SSRC MUST be from 1 to 4294967040
1170 * inclusive. */
1171 if (range->begin > range->end || range->end > 0xFFFFFF00) {
1172 g_free(range);
1173 SIPE_DEBUG_ERROR("Couldn't allocate SSRC range of %u", len);
1174 return NULL;
1177 *ranges = g_slist_insert_sorted(*ranges, range,
1178 (GCompareFunc)ssrc_range_compare);
1180 return range;
1183 struct sipe_media_stream *
1184 sipe_media_stream_add(struct sipe_media_call *call, const gchar *id,
1185 SipeMediaType type, SipeIceVersion ice_version,
1186 gboolean initiator, guint32 ssrc_count)
1188 struct sipe_core_private *sipe_private;
1189 struct sipe_media_stream_private *stream_private;
1190 struct sipe_backend_media_relays *backend_media_relays;
1191 guint min_port;
1192 guint max_port;
1194 sipe_private = SIPE_MEDIA_CALL_PRIVATE->sipe_private;
1196 backend_media_relays = sipe_backend_media_relays_convert(
1197 sipe_private->media_relays,
1198 sipe_private->media_relay_username,
1199 sipe_private->media_relay_password);
1201 min_port = sipe_private->min_media_port;
1202 max_port = sipe_private->max_media_port;
1203 switch (type) {
1204 case SIPE_MEDIA_AUDIO:
1205 min_port = sipe_private->min_audio_port;
1206 max_port = sipe_private->max_audio_port;
1207 break;
1208 case SIPE_MEDIA_VIDEO:
1209 min_port = sipe_private->min_video_port;
1210 max_port = sipe_private->max_audio_port;
1211 break;
1212 case SIPE_MEDIA_APPLICATION:
1213 if (sipe_strequal(id, "data")) {
1214 min_port = sipe_private->min_filetransfer_port;
1215 max_port = sipe_private->max_filetransfer_port;
1216 } else if (sipe_strequal(id, "applicationsharing")) {
1217 min_port = sipe_private->min_appsharing_port;
1218 max_port = sipe_private->max_appsharing_port;
1220 break;
1223 stream_private = g_new0(struct sipe_media_stream_private, 1);
1224 SIPE_MEDIA_STREAM->call = call;
1225 SIPE_MEDIA_STREAM->id = g_strdup(id);
1226 stream_private->write_queue = g_queue_new();
1227 stream_private->async_reads = g_queue_new();
1229 if (ssrc_count > 0) {
1230 SIPE_MEDIA_STREAM->ssrc_range =
1231 ssrc_range_allocate(&SIPE_MEDIA_CALL_PRIVATE->ssrc_ranges,
1232 ssrc_count);
1235 SIPE_MEDIA_STREAM->backend_private =
1236 sipe_backend_media_add_stream(SIPE_MEDIA_STREAM,
1237 type, ice_version,
1238 initiator,
1239 backend_media_relays,
1240 min_port, max_port);
1242 sipe_backend_media_relays_free(backend_media_relays);
1244 if (!SIPE_MEDIA_STREAM->backend_private) {
1245 sipe_media_stream_free(stream_private);
1246 return NULL;
1249 if (type == SIPE_MEDIA_VIDEO) {
1250 /* Declare that we can send and receive Video Source Requests
1251 * as per [MS-SDPEXT] 3.1.5.30.2. */
1252 sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM,
1253 "rtcp-fb", "* x-message app send:src recv:src");
1255 sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM,
1256 "rtcp-rsize", NULL);
1257 sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM,
1258 "label", "main-video");
1259 sipe_media_stream_add_extra_attribute(SIPE_MEDIA_STREAM,
1260 "x-source", "main-video");
1263 #ifdef HAVE_SRTP
1264 if (get_encryption_policy(sipe_private) != SIPE_ENCRYPTION_POLICY_REJECTED) {
1265 int i;
1266 stream_private->encryption_key = g_new0(guchar, SIPE_SRTP_KEY_LEN);
1267 for (i = 0; i != SIPE_SRTP_KEY_LEN; ++i) {
1268 stream_private->encryption_key[i] = rand() & 0xff;
1270 stream_private->encryption_key_id = 1;
1272 #endif
1274 SIPE_MEDIA_CALL_PRIVATE->streams =
1275 g_slist_append(SIPE_MEDIA_CALL_PRIVATE->streams,
1276 stream_private);
1278 return SIPE_MEDIA_STREAM;
1281 static void
1282 append_2007_fallback_if_needed(struct sipe_media_call_private *call_private)
1284 struct sipe_core_private *sipe_private = call_private->sipe_private;
1285 const gchar *ip = sip_transport_ip_address(sipe_private);
1286 gchar *body;
1288 if (sipe_media_get_sip_dialog(SIPE_MEDIA_CALL)->cseq != 0 ||
1289 call_private->ice_version != SIPE_ICE_RFC_5245 ||
1290 sipe_strequal(SIPE_MEDIA_CALL->with, sipe_private->test_call_bot_uri)) {
1291 return;
1294 body = g_strdup_printf("Content-Type: application/sdp\r\n"
1295 "Content-Transfer-Encoding: 7bit\r\n"
1296 "Content-Disposition: session; handling=optional; ms-proxy-2007fallback\r\n"
1297 "\r\n"
1298 "o=- 0 0 IN IP4 %s\r\n"
1299 "s=session\r\n"
1300 "c=IN IP4 %s\r\n"
1301 "m=audio 0 RTP/AVP\r\n",
1302 ip, ip);
1303 sipe_media_add_extra_invite_section(SIPE_MEDIA_CALL,
1304 "multipart/alternative", body);
1307 static void
1308 sipe_media_initiate_call(struct sipe_core_private *sipe_private,
1309 const char *with, SipeIceVersion ice_version,
1310 gboolean with_video)
1312 struct sipe_media_call_private *call_private;
1314 if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) {
1315 return;
1318 call_private = (struct sipe_media_call_private *)
1319 sipe_media_call_new(sipe_private, with, NULL,
1320 ice_version, 0);
1322 SIPE_MEDIA_CALL->call_reject_cb = av_call_reject_cb;
1324 if (!sipe_media_stream_add(SIPE_MEDIA_CALL, "audio", SIPE_MEDIA_AUDIO,
1325 call_private->ice_version,
1326 TRUE, 0)) {
1327 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
1328 _("Error occurred"),
1329 _("Error creating audio stream"));
1330 sipe_media_hangup(call_private);
1331 return;
1334 if (with_video &&
1335 !sipe_media_stream_add(SIPE_MEDIA_CALL, "video", SIPE_MEDIA_VIDEO,
1336 call_private->ice_version,
1337 TRUE, VIDEO_SSRC_COUNT)) {
1338 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
1339 _("Error occurred"),
1340 _("Error creating video stream"));
1341 sipe_media_hangup(call_private);
1342 return;
1345 append_2007_fallback_if_needed(call_private);
1347 // Processing continues in stream_initialized_cb
1350 void
1351 sipe_core_media_initiate_call(struct sipe_core_public *sipe_public,
1352 const char *with,
1353 gboolean with_video)
1355 sipe_media_initiate_call(SIPE_CORE_PRIVATE, with,
1356 SIPE_ICE_RFC_5245, with_video);
1359 static void
1360 conference_audio_muted_cb(struct sipe_media_stream *stream, gboolean is_muted)
1362 struct sipe_media_call *call = stream->call;
1364 if (!SIPE_MEDIA_CALL_PRIVATE->conference_session) {
1365 return;
1368 sipe_conf_announce_audio_mute_state(SIPE_MEDIA_CALL_PRIVATE->sipe_private,
1369 SIPE_MEDIA_CALL_PRIVATE->conference_session,
1370 is_muted);
1373 void sipe_core_media_connect_conference(struct sipe_core_public *sipe_public,
1374 struct sipe_chat_session *chat_session)
1376 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1377 struct sipe_media_call_private *call_private;
1378 struct sipe_media_stream *stream;
1379 struct sip_session *session;
1380 SipeIceVersion ice_version;
1381 gchar *av_uri;
1383 if (!sipe_conf_supports_mcu_type(sipe_private, "audio-video")) {
1384 sipe_backend_notify_error(sipe_public, _("Join conference call"),
1385 _("Conference calls are not supported on this server."));
1386 return;
1389 session = sipe_session_find_chat(sipe_private, chat_session);
1391 if (sipe_core_media_get_call(sipe_public) || !session) {
1392 return;
1395 av_uri = sipe_conf_build_uri(chat_session->id, "audio-video");
1396 if (!av_uri) {
1397 return;
1400 session->is_call = TRUE;
1402 ice_version = SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013) ? SIPE_ICE_RFC_5245 :
1403 SIPE_ICE_DRAFT_6;
1405 call_private = (struct sipe_media_call_private *)
1406 sipe_media_call_new(sipe_private, av_uri, NULL,
1407 ice_version, 0);
1408 call_private->conference_session = session;
1409 SIPE_MEDIA_CALL->call_reject_cb = av_call_reject_cb;
1411 stream = sipe_media_stream_add(SIPE_MEDIA_CALL, "audio",
1412 SIPE_MEDIA_AUDIO,
1413 call_private->ice_version,
1414 TRUE, 0);
1415 if (!stream) {
1416 sipe_backend_notify_error(sipe_public,
1417 _("Error occurred"),
1418 _("Error creating audio stream"));
1420 sipe_media_hangup(call_private);
1423 stream->mute_cb = conference_audio_muted_cb;
1425 g_free(av_uri);
1427 // Processing continues in stream_initialized_cb
1430 struct sipe_media_call *
1431 sipe_core_media_get_call(struct sipe_core_public *sipe_public)
1433 struct sipe_media_call * result = NULL;
1434 GList *calls = g_hash_table_get_values(SIPE_CORE_PRIVATE->media_calls);
1435 GList *entry = calls;
1437 while (entry) {
1438 if (sipe_core_media_get_stream_by_id(entry->data, "audio")) {
1439 result = entry->data;
1440 break;
1442 entry = entry->next;
1444 g_list_free(calls);
1446 return result;
1449 static gboolean phone_number_is_valid(const gchar *phone_number)
1451 if (!phone_number || sipe_strequal(phone_number, "")) {
1452 return FALSE;
1455 if (*phone_number == '+') {
1456 ++phone_number;
1459 while (*phone_number != '\0') {
1460 if (!g_ascii_isdigit(*phone_number)) {
1461 return FALSE;
1463 ++phone_number;
1466 return TRUE;
1469 void sipe_core_media_phone_call(struct sipe_core_public *sipe_public,
1470 const gchar *phone_number)
1472 g_return_if_fail(sipe_public);
1474 if (phone_number_is_valid(phone_number)) {
1475 gchar *phone_uri = g_strdup_printf("sip:%s@%s;user=phone",
1476 phone_number, sipe_public->sip_domain);
1478 sipe_core_media_initiate_call(sipe_public, phone_uri, FALSE);
1480 g_free(phone_uri);
1481 } else {
1482 sipe_backend_notify_error(sipe_public,
1483 _("Unable to establish a call"),
1484 _("Invalid phone number"));
1488 void sipe_core_media_test_call(struct sipe_core_public *sipe_public)
1490 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1491 if (!sipe_private->test_call_bot_uri) {
1492 sipe_backend_notify_error(sipe_public,
1493 _("Unable to establish a call"),
1494 _("Audio Test Service is not available."));
1495 return;
1498 sipe_core_media_initiate_call(sipe_public,
1499 sipe_private->test_call_bot_uri, FALSE);
1502 static struct sipe_media_call_private *
1503 sipe_media_from_sipmsg(struct sipe_core_private *sipe_private,
1504 struct sipmsg *msg)
1506 return g_hash_table_lookup(sipe_private->media_calls,
1507 sipmsg_find_header(msg, "Call-ID"));
1510 static void
1511 transport_response_unsupported_sdp(struct sipe_core_private *sipe_private,
1512 struct sipmsg *msg)
1514 sipmsg_add_header(msg, "ms-client-diagnostics",
1515 "52063;reason=\"Unsupported session description\"");
1516 sip_transport_response(sipe_private, msg,
1517 488, "Not Acceptable Here", NULL);
1520 static void
1521 maybe_send_second_invite_response(struct sipe_media_call_private *call_private)
1523 GSList *it;
1525 /* Second INVITE request had to be received and all streams must have
1526 * established candidate pairs before the response can be sent. */
1528 if (!call_private->invitation) {
1529 return;
1532 for (it = call_private->streams; it; it = it->next) {
1533 struct sipe_media_stream_private *stream_private = it->data;
1534 if (!stream_private->established) {
1535 return;
1539 send_response_with_session_description(call_private, 200, "OK");
1541 #ifdef HAVE_XDATA
1542 for (it = call_private->streams; it; it = it->next) {
1543 struct sipe_media_stream_private *stream_private = it->data;
1545 stream_private->sdp_negotiation_concluded = TRUE;
1546 if (stream_private->writable) {
1547 // We've become writable.
1548 sipe_core_media_stream_writable(SIPE_MEDIA_STREAM, TRUE);
1551 #endif
1554 struct sipe_media_call *
1555 process_incoming_invite_call(struct sipe_core_private *sipe_private,
1556 struct sipmsg *msg)
1558 struct sipe_media_call_private *call_private;
1559 struct sdpmsg *smsg;
1560 gboolean has_new_media = FALSE;
1561 GSList *i;
1563 // Don't allow two voice calls in parallel.
1564 if (!strstr(msg->body, "m=data") &&
1565 !strstr(msg->body, "m=applicationsharing")) {
1566 struct sipe_media_call *call =
1567 sipe_core_media_get_call(SIPE_CORE_PUBLIC);
1568 if (call && !is_media_session_msg(SIPE_MEDIA_CALL_PRIVATE, msg)) {
1569 sip_transport_response(sipe_private, msg,
1570 486, "Busy Here", NULL);
1571 return NULL;
1575 call_private = sipe_media_from_sipmsg(sipe_private, msg);
1577 if (call_private) {
1578 char *self = sip_uri_self(sipe_private);
1579 if (sipe_strequal(SIPE_MEDIA_CALL->with, self)) {
1580 g_free(self);
1581 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
1582 return NULL;
1584 g_free(self);
1587 smsg = sdpmsg_parse_msg(msg->body);
1588 if (!smsg) {
1589 transport_response_unsupported_sdp(sipe_private, msg);
1590 if (call_private) {
1591 sipe_media_hangup(call_private);
1593 return NULL;
1596 if (!call_private) {
1597 gchar *with = parse_from(sipmsg_find_header(msg, "From"));
1598 SipeMediaCallFlags flags = 0;
1600 if (strstr(msg->body, "m=data") ||
1601 strstr(msg->body, "m=applicationsharing")) {
1602 flags |= SIPE_MEDIA_CALL_NO_UI;
1605 call_private = (struct sipe_media_call_private *)
1606 sipe_media_call_new(sipe_private, with,
1607 msg, smsg->ice_version,
1608 flags);
1610 if (!(flags & SIPE_MEDIA_CALL_NO_UI)) {
1611 SIPE_MEDIA_CALL->call_reject_cb = av_call_reject_cb;
1613 g_free(with);
1616 if (call_private->invitation)
1617 sipmsg_free(call_private->invitation);
1618 call_private->invitation = sipmsg_copy(msg);
1620 ssrc_range_update(&call_private->ssrc_ranges, smsg->media);
1622 // Create any new media streams
1623 for (i = smsg->media; i; i = i->next) {
1624 struct sdpmedia *media = i->data;
1625 gchar *id = media->name;
1626 SipeMediaType type;
1628 if ( media->port != 0
1629 && !sipe_core_media_get_stream_by_id(SIPE_MEDIA_CALL, id)) {
1630 guint32 ssrc_count = 0;
1632 if (sipe_strequal(id, "audio"))
1633 type = SIPE_MEDIA_AUDIO;
1634 else if (sipe_strequal(id, "video")) {
1635 type = SIPE_MEDIA_VIDEO;
1636 ssrc_count = VIDEO_SSRC_COUNT;
1637 } else if (sipe_strequal(id, "data"))
1638 type = SIPE_MEDIA_APPLICATION;
1639 else if (sipe_strequal(id, "applicationsharing"))
1640 type = SIPE_MEDIA_APPLICATION;
1641 else
1642 continue;
1644 sipe_media_stream_add(SIPE_MEDIA_CALL, id, type,
1645 smsg->ice_version, FALSE,
1646 ssrc_count);
1647 has_new_media = TRUE;
1651 if (has_new_media) {
1652 sdpmsg_free(call_private->smsg);
1653 call_private->smsg = smsg;
1654 sip_transport_response(sipe_private, call_private->invitation,
1655 180, "Ringing", NULL);
1656 // Processing continues in stream_initialized_cb
1657 } else {
1658 apply_remote_message(call_private, smsg);
1659 sdpmsg_free(smsg);
1660 maybe_send_second_invite_response(call_private);
1663 return SIPE_MEDIA_CALL;
1666 void process_incoming_cancel_call(struct sipe_media_call_private *call_private,
1667 struct sipmsg *msg)
1669 // We respond to the CANCEL request with 200 OK response and
1670 // with 487 Request Terminated to the remote INVITE in progress.
1671 sip_transport_response(call_private->sipe_private, msg, 200, "OK", NULL);
1673 if (call_private->invitation) {
1674 sip_transport_response(call_private->sipe_private,
1675 call_private->invitation,
1676 487, "Request Terminated", NULL);
1679 sipe_backend_media_reject(SIPE_MEDIA_CALL->backend_private, FALSE);
1682 static gboolean
1683 sipe_media_send_ack(struct sipe_core_private *sipe_private,
1684 struct sipmsg *msg,
1685 struct transaction *trans)
1687 struct sipe_media_call_private *call_private;
1688 struct sip_dialog *dialog;
1689 int tmp_cseq;
1691 call_private = sipe_media_from_sipmsg(sipe_private, msg);
1693 if (!is_media_session_msg(call_private, msg))
1694 return FALSE;
1696 dialog = sipe_media_get_sip_dialog(SIPE_MEDIA_CALL);
1697 if (!dialog)
1698 return FALSE;
1700 tmp_cseq = dialog->cseq;
1702 dialog->cseq = sip_transaction_cseq(trans) - 1;
1703 sip_transport_ack(sipe_private, dialog);
1704 dialog->cseq = tmp_cseq;
1706 dialog->outgoing_invite = NULL;
1708 return TRUE;
1711 static gboolean
1712 sipe_media_send_final_ack(struct sipe_core_private *sipe_private,
1713 struct sipmsg *msg,
1714 struct transaction *trans)
1716 struct sipe_media_call_private *call_private;
1717 #ifdef HAVE_XDATA
1718 GSList *it;
1719 #endif
1721 if (!sipe_media_send_ack(sipe_private, msg, trans))
1722 return FALSE;
1724 call_private = sipe_media_from_sipmsg(sipe_private, msg);
1726 sipe_backend_media_accept(SIPE_MEDIA_CALL->backend_private, FALSE);
1728 #ifdef HAVE_XDATA
1729 for (it = call_private->streams; it; it = it->next) {
1730 struct sipe_media_stream_private *stream_private = it->data;
1732 stream_private->sdp_negotiation_concluded = TRUE;
1733 if (stream_private->writable) {
1734 // We've become writable.
1735 sipe_core_media_stream_writable(SIPE_MEDIA_STREAM, TRUE);
1738 #endif
1740 return TRUE;
1743 void
1744 sipe_core_media_stream_candidate_pair_established(struct sipe_media_stream *stream)
1746 struct sipe_media_call *call = stream->call;
1748 GList *active_candidates =
1749 sipe_backend_media_stream_get_active_local_candidates(stream);
1750 guint ready_components = g_list_length(active_candidates);
1752 sipe_media_candidate_list_free(active_candidates);
1754 if (ready_components != 2) {
1755 // We must have both RTP+RTCP candidate pairs established first.
1756 return;
1759 if (SIPE_MEDIA_STREAM_PRIVATE->established) {
1760 return;
1762 SIPE_MEDIA_STREAM_PRIVATE->established = TRUE;
1764 if (stream->candidate_pairs_established_cb) {
1765 stream->candidate_pairs_established_cb(stream);
1768 if (sipe_backend_media_is_initiator(stream->call, NULL)) {
1769 GSList *streams = SIPE_MEDIA_CALL_PRIVATE->streams;
1770 for (; streams; streams = streams->next) {
1771 struct sipe_media_stream_private *s = streams->data;
1772 if (!s->established) {
1773 break;
1777 if (streams == NULL) {
1778 // All call streams have been established.
1779 sipe_invite_call(SIPE_MEDIA_CALL_PRIVATE,
1780 sipe_media_send_final_ack);
1782 } else {
1783 maybe_send_second_invite_response(SIPE_MEDIA_CALL_PRIVATE);
1787 static gboolean
1788 maybe_retry_call_with_ice_version(struct sipe_media_call_private *call_private,
1789 SipeIceVersion ice_version,
1790 struct transaction *trans)
1792 if (call_private->ice_version != ice_version &&
1793 sip_transaction_cseq(trans) == 1) {
1794 GSList *i;
1795 gchar *with;
1796 gboolean with_video = FALSE;
1798 for (i = call_private->streams; i; i = i->next) {
1799 struct sipe_media_stream *stream = i->data;
1801 if (sipe_strequal(stream->id, "video")) {
1802 with_video = TRUE;
1803 } else if (!sipe_strequal(stream->id, "audio")) {
1804 /* Don't retry calls which are neither audio
1805 * nor video. */
1806 return FALSE;
1810 with = g_strdup(SIPE_MEDIA_CALL->with);
1812 sipe_media_hangup(call_private);
1813 SIPE_DEBUG_INFO("Retrying call with ICEv%d.",
1814 ice_version == SIPE_ICE_DRAFT_6 ? 6 : 19);
1815 sipe_media_initiate_call(call_private->sipe_private, with,
1816 ice_version, with_video);
1818 g_free(with);
1819 return TRUE;
1822 return FALSE;
1825 static gboolean
1826 process_invite_call_response(struct sipe_core_private *sipe_private,
1827 struct sipmsg *msg,
1828 struct transaction *trans)
1830 const gchar *with;
1831 struct sipe_media_call_private *call_private;
1832 struct sip_dialog *dialog;
1833 struct sdpmsg *smsg;
1835 call_private = sipe_media_from_sipmsg(sipe_private,msg);
1837 if (!is_media_session_msg(call_private, msg))
1838 return FALSE;
1840 dialog = sipe_media_get_sip_dialog(SIPE_MEDIA_CALL);
1842 with = dialog->with;
1844 dialog->outgoing_invite = NULL;
1846 if (msg->response == 603 || msg->response == 605) {
1847 // Call rejected by remote peer
1848 sipe_media_send_ack(sipe_private, msg, trans);
1849 sipe_backend_media_reject(SIPE_MEDIA_CALL->backend_private, FALSE);
1851 return TRUE;
1854 if (msg->response >= 400) {
1855 // An error occurred
1856 const gchar *title;
1857 GString *desc = g_string_new("");
1858 gboolean append_responsestr = FALSE;
1860 switch (msg->response) {
1861 case 480: {
1862 title = _("User unavailable");
1864 if (sipmsg_parse_warning(msg, NULL) == 391) {
1865 g_string_append_printf(desc, _("%s does not want to be disturbed"), with);
1866 } else
1867 g_string_append_printf(desc, _("User %s is not available"), with);
1868 break;
1870 case 415:
1871 // OCS/Lync really sends response string with 'Mutipart' typo.
1872 if (sipe_strequal(msg->responsestr, "Mutipart mime in content type not supported by Archiving CDR service") &&
1873 maybe_retry_call_with_ice_version(call_private, SIPE_ICE_DRAFT_6, trans)) {
1874 return TRUE;
1876 title = _("Unsupported media type");
1877 break;
1878 case 488: {
1879 /* Check for incompatible encryption levels error.
1881 * MS Lync 2010:
1882 * 488 Not Acceptable Here
1883 * ms-client-diagnostics: 52017;reason="Encryption levels dont match"
1885 * older clients (and SIPE itself):
1886 * 488 Encryption Levels not compatible
1888 const gchar *ms_diag = sipmsg_find_header(msg, "ms-client-diagnostics");
1889 SipeIceVersion retry_ice_version = SIPE_ICE_DRAFT_6;
1891 if (sipe_strequal(msg->responsestr, "Encryption Levels not compatible") ||
1892 (ms_diag && g_str_has_prefix(ms_diag, "52017;"))) {
1893 title = _("Unable to establish a call");
1894 g_string_append(desc, _("Encryption settings of peer are incompatible with ours."));
1895 break;
1898 /* Check if this is failed conference using
1899 * ICEv6 with reason "Error parsing SDP" and
1900 * retry using ICEv19. */
1901 ms_diag = sipmsg_find_header(msg, "ms-diagnostics");
1902 if (ms_diag && g_str_has_prefix(ms_diag, "7008;")) {
1903 retry_ice_version = SIPE_ICE_RFC_5245;
1906 if (maybe_retry_call_with_ice_version(call_private, retry_ice_version, trans)) {
1907 return TRUE;
1909 // Break intentionally omitted
1911 default:
1912 title = _("Error occurred");
1913 g_string_append(desc, _("Unable to establish a call"));
1914 append_responsestr = TRUE;
1915 break;
1918 if (append_responsestr) {
1919 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1921 g_string_append_printf(desc, "\n%d %s",
1922 msg->response, msg->responsestr);
1923 if (reason) {
1924 g_string_append_printf(desc, "\n\n%s", reason);
1925 g_free(reason);
1929 sipe_backend_notify_error(SIPE_CORE_PUBLIC, title, desc->str);
1930 g_string_free(desc, TRUE);
1932 sipe_media_send_ack(sipe_private, msg, trans);
1933 sipe_media_hangup(call_private);
1935 return TRUE;
1938 sipe_dialog_parse(dialog, msg, TRUE);
1939 smsg = sdpmsg_parse_msg(msg->body);
1940 if (!smsg) {
1941 transport_response_unsupported_sdp(sipe_private, msg);
1942 sipe_media_hangup(call_private);
1943 return FALSE;
1946 ssrc_range_update(&call_private->ssrc_ranges, smsg->media);
1947 apply_remote_message(call_private, smsg);
1948 sdpmsg_free(smsg);
1950 sipe_media_send_ack(sipe_private, msg, trans);
1952 return TRUE;
1954 // Waits until sipe_core_media_candidate_pair_established() is invoked.
1957 gboolean is_media_session_msg(struct sipe_media_call_private *call_private,
1958 struct sipmsg *msg)
1960 if (!call_private) {
1961 return FALSE;
1964 return sipe_media_from_sipmsg(call_private->sipe_private, msg) == call_private;
1967 static void
1968 end_call(SIPE_UNUSED_PARAMETER gpointer key,
1969 struct sipe_media_call_private *call_private,
1970 SIPE_UNUSED_PARAMETER gpointer user_data)
1972 struct sipe_backend_media *backend_private;
1974 backend_private = call_private->public.backend_private;
1976 if (!sipe_backend_media_is_initiator(SIPE_MEDIA_CALL, NULL) &&
1977 !sipe_backend_media_accepted(backend_private)) {
1978 sip_transport_response(call_private->sipe_private,
1979 call_private->invitation,
1980 480, "Temporarily Unavailable", NULL);
1981 } else if (call_private->session) {
1982 sipe_session_close(call_private->sipe_private,
1983 call_private->session);
1984 call_private->session = NULL;
1987 sipe_media_hangup(call_private);
1990 void
1991 sipe_media_handle_going_offline(struct sipe_core_private *sipe_private)
1993 g_hash_table_foreach(sipe_private->media_calls, (GHFunc) end_call, NULL);
1996 gboolean sipe_media_is_conference_call(struct sipe_media_call_private *call_private)
1998 return g_strstr_len(SIPE_MEDIA_CALL->with, -1, "app:conf:audio-video:") != NULL;
2001 struct sipe_core_private *
2002 sipe_media_get_sipe_core_private(struct sipe_media_call *call)
2004 g_return_val_if_fail(call, NULL);
2006 return SIPE_MEDIA_CALL_PRIVATE->sipe_private;
2009 struct sip_dialog *
2010 sipe_media_get_sip_dialog(struct sipe_media_call *call)
2012 struct sip_session *session;
2014 g_return_val_if_fail(call, NULL);
2016 session = SIPE_MEDIA_CALL_PRIVATE->session;
2018 if (!session || !session->dialogs) {
2019 return NULL;
2022 return session->dialogs->data;
2025 static void
2026 sipe_media_relay_free(struct sipe_media_relay *relay)
2028 g_free(relay->hostname);
2029 if (relay->dns_query)
2030 sipe_backend_dns_query_cancel(relay->dns_query);
2031 g_free(relay);
2034 void
2035 sipe_media_relay_list_free(GSList *list)
2037 for (; list; list = g_slist_delete_link(list, list))
2038 sipe_media_relay_free(list->data);
2041 static void
2042 relay_ip_resolved_cb(struct sipe_media_relay* relay,
2043 const gchar *ip, SIPE_UNUSED_PARAMETER guint port)
2045 gchar *hostname = relay->hostname;
2046 relay->dns_query = NULL;
2048 if (ip && port) {
2049 relay->hostname = g_strdup(ip);
2050 SIPE_DEBUG_INFO("Media relay %s resolved to %s.", hostname, ip);
2051 } else {
2052 relay->hostname = NULL;
2053 SIPE_DEBUG_INFO("Unable to resolve media relay %s.", hostname);
2056 g_free(hostname);
2059 static gboolean
2060 process_get_av_edge_credentials_response(struct sipe_core_private *sipe_private,
2061 struct sipmsg *msg,
2062 SIPE_UNUSED_PARAMETER struct transaction *trans)
2064 g_free(sipe_private->media_relay_username);
2065 g_free(sipe_private->media_relay_password);
2066 sipe_media_relay_list_free(sipe_private->media_relays);
2067 sipe_private->media_relay_username = NULL;
2068 sipe_private->media_relay_password = NULL;
2069 sipe_private->media_relays = NULL;
2071 if (msg->response >= 400) {
2072 SIPE_DEBUG_INFO_NOFORMAT("process_get_av_edge_credentials_response: SERVICE response is not 200. "
2073 "Failed to obtain A/V Edge credentials.");
2074 return FALSE;
2077 if (msg->response == 200) {
2078 sipe_xml *xn_response = sipe_xml_parse(msg->body, msg->bodylen);
2080 if (sipe_strequal("OK", sipe_xml_attribute(xn_response, "reasonPhrase"))) {
2081 const sipe_xml *xn_credentials = sipe_xml_child(xn_response, "credentialsResponse/credentials");
2082 const sipe_xml *xn_relays = sipe_xml_child(xn_response, "credentialsResponse/mediaRelayList");
2083 const sipe_xml *item;
2084 GSList *relays = NULL;
2086 item = sipe_xml_child(xn_credentials, "username");
2087 sipe_private->media_relay_username = sipe_xml_data(item);
2088 item = sipe_xml_child(xn_credentials, "password");
2089 sipe_private->media_relay_password = sipe_xml_data(item);
2091 for (item = sipe_xml_child(xn_relays, "mediaRelay"); item; item = sipe_xml_twin(item)) {
2092 struct sipe_media_relay *relay = g_new0(struct sipe_media_relay, 1);
2093 const sipe_xml *node;
2094 gchar *tmp;
2096 node = sipe_xml_child(item, "hostName");
2097 relay->hostname = sipe_xml_data(node);
2099 node = sipe_xml_child(item, "udpPort");
2100 if (node) {
2101 tmp = sipe_xml_data(node);
2102 if (tmp) {
2103 relay->udp_port = atoi(tmp);
2104 g_free(tmp);
2108 node = sipe_xml_child(item, "tcpPort");
2109 if (node) {
2110 tmp = sipe_xml_data(node);
2111 if (tmp) {
2112 relay->tcp_port = atoi(tmp);
2113 g_free(tmp);
2117 relays = g_slist_append(relays, relay);
2119 relay->dns_query = sipe_backend_dns_query_a(
2120 SIPE_CORE_PUBLIC,
2121 relay->hostname,
2122 relay->udp_port,
2123 (sipe_dns_resolved_cb) relay_ip_resolved_cb,
2124 relay);
2126 SIPE_DEBUG_INFO("Media relay: %s TCP: %d UDP: %d",
2127 relay->hostname,
2128 relay->tcp_port, relay->udp_port);
2131 sipe_private->media_relays = relays;
2134 sipe_xml_free(xn_response);
2137 return TRUE;
2140 void
2141 sipe_media_get_av_edge_credentials(struct sipe_core_private *sipe_private)
2143 // TODO: re-request credentials after duration expires?
2144 static const char CRED_REQUEST_XML[] =
2145 "<request requestID=\"%d\" "
2146 "from=\"%s\" "
2147 "version=\"1.0\" "
2148 "to=\"%s\" "
2149 "xmlns=\"http://schemas.microsoft.com/2006/09/sip/mrasp\" "
2150 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
2151 "<credentialsRequest credentialsRequestID=\"%d\">"
2152 "<identity>%s</identity>"
2153 "<location>%s</location>"
2154 "<duration>480</duration>"
2155 "</credentialsRequest>"
2156 "</request>";
2158 int request_id = rand();
2159 gchar *self;
2160 gchar *body;
2162 if (!sipe_private->mras_uri)
2163 return;
2165 self = sip_uri_self(sipe_private);
2167 body = g_strdup_printf(
2168 CRED_REQUEST_XML,
2169 request_id,
2170 self,
2171 sipe_private->mras_uri,
2172 request_id,
2173 self,
2174 SIPE_CORE_PRIVATE_FLAG_IS(REMOTE_USER) ? "internet" : "intranet");
2175 g_free(self);
2177 sip_transport_service(sipe_private,
2178 sipe_private->mras_uri,
2179 "Content-Type: application/msrtc-media-relay-auth+xml\r\n",
2180 body,
2181 process_get_av_edge_credentials_response);
2183 g_free(body);
2186 void
2187 sipe_media_add_extra_invite_section(struct sipe_media_call *call,
2188 const gchar *invite_content_type,
2189 gchar *body)
2191 g_free(SIPE_MEDIA_CALL_PRIVATE->extra_invite_section);
2192 g_free(SIPE_MEDIA_CALL_PRIVATE->invite_content_type);
2193 SIPE_MEDIA_CALL_PRIVATE->extra_invite_section = body;
2194 SIPE_MEDIA_CALL_PRIVATE->invite_content_type =
2195 g_strdup(invite_content_type);
2198 void
2199 sipe_media_stream_add_extra_attribute(struct sipe_media_stream *stream,
2200 const gchar *name, const gchar *value)
2202 SIPE_MEDIA_STREAM_PRIVATE->extra_sdp =
2203 sipe_utils_nameval_add(SIPE_MEDIA_STREAM_PRIVATE->extra_sdp,
2204 name, value);
2207 #ifdef HAVE_XDATA
2208 void
2209 sipe_core_media_stream_readable(struct sipe_media_stream *stream)
2211 g_return_if_fail(stream);
2213 if (g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->async_reads) &&
2214 stream->read_cb) {
2215 stream->read_cb(stream);
2218 while (!g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->async_reads)) {
2219 struct async_read_data *data;
2220 guint8 *pos;
2221 gssize len;
2222 gssize bytes_read;
2224 data = g_queue_peek_head(SIPE_MEDIA_STREAM_PRIVATE->async_reads);
2225 pos = data->buffer + SIPE_MEDIA_STREAM_PRIVATE->read_pos;
2226 len = data->len - SIPE_MEDIA_STREAM_PRIVATE->read_pos;
2228 bytes_read = sipe_backend_media_stream_read(stream, pos, len);
2229 if (bytes_read == -1) {
2230 struct sipe_media_call *call = stream->call;
2231 struct sipe_core_private *sipe_private =
2232 SIPE_MEDIA_CALL_PRIVATE->sipe_private;
2234 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
2235 _("Media error"),
2236 _("Error while reading from stream"));
2237 sipe_media_hangup(SIPE_MEDIA_CALL_PRIVATE);
2238 return;
2241 SIPE_MEDIA_STREAM_PRIVATE->read_pos += bytes_read;
2243 if (SIPE_MEDIA_STREAM_PRIVATE->read_pos == data->len) {
2244 data->callback(stream, data->buffer, data->len);
2245 SIPE_MEDIA_STREAM_PRIVATE->read_pos = 0;
2246 g_queue_pop_head(SIPE_MEDIA_STREAM_PRIVATE->async_reads);
2247 g_free(data);
2248 } else {
2249 // Still not enough data to finish the read.
2250 return;
2255 void
2256 sipe_media_stream_read_async(struct sipe_media_stream *stream,
2257 gpointer buffer, gsize len,
2258 sipe_media_stream_read_callback callback)
2260 struct async_read_data *data;
2262 g_return_if_fail(stream && buffer && callback);
2264 data = g_new0(struct async_read_data, 1);
2265 data->buffer = buffer;
2266 data->len = len;
2267 data->callback = callback;
2269 g_queue_push_tail(SIPE_MEDIA_STREAM_PRIVATE->async_reads, data);
2272 static void
2273 stream_append_buffer(struct sipe_media_stream *stream,
2274 guint8 *buffer, guint len)
2276 GByteArray *b = g_byte_array_sized_new(len);
2277 g_byte_array_append(b, buffer, len);
2278 g_queue_push_tail(SIPE_MEDIA_STREAM_PRIVATE->write_queue, b);
2281 gboolean
2282 sipe_media_stream_write(struct sipe_media_stream *stream,
2283 gpointer buffer, gsize len)
2285 if (!sipe_media_stream_is_writable(stream)) {
2286 stream_append_buffer(stream, buffer, len);
2287 return FALSE;
2288 } else {
2289 guint written;
2291 written = sipe_backend_media_stream_write(stream, buffer, len);
2292 if (written == len) {
2293 return TRUE;
2296 stream_append_buffer(stream,
2297 (guint8 *)buffer + written, len - written);
2298 return FALSE;
2302 void
2303 sipe_core_media_stream_writable(struct sipe_media_stream *stream,
2304 gboolean writable)
2306 SIPE_MEDIA_STREAM_PRIVATE->writable = writable;
2308 if (!writable) {
2309 return;
2312 while (!g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->write_queue)) {
2313 GByteArray *b;
2314 guint written;
2316 b = g_queue_peek_head(SIPE_MEDIA_STREAM_PRIVATE->write_queue);
2318 written = sipe_backend_media_stream_write(stream, b->data, b->len);
2319 if (written != b->len) {
2320 g_byte_array_remove_range(b, 0, written);
2321 return;
2324 g_byte_array_unref(b);
2325 g_queue_pop_head(SIPE_MEDIA_STREAM_PRIVATE->write_queue);
2328 if (sipe_media_stream_is_writable(stream) && stream->writable_cb) {
2329 stream->writable_cb(stream);
2333 gboolean
2334 sipe_media_stream_is_writable(struct sipe_media_stream *stream)
2336 return SIPE_MEDIA_STREAM_PRIVATE->writable &&
2337 SIPE_MEDIA_STREAM_PRIVATE->sdp_negotiation_concluded &&
2338 g_queue_is_empty(SIPE_MEDIA_STREAM_PRIVATE->write_queue);
2340 #endif
2342 void
2343 sipe_media_stream_set_data(struct sipe_media_stream *stream, gpointer data,
2344 GDestroyNotify free_func)
2346 struct sipe_media_stream_private *stream_private =
2347 SIPE_MEDIA_STREAM_PRIVATE;
2349 g_return_if_fail(stream_private);
2351 if (stream_private->data && stream_private->data_free_func) {
2352 stream_private->data_free_func(stream_private->data);
2355 stream_private->data = data;
2356 stream_private->data_free_func = free_func;
2359 gpointer
2360 sipe_media_stream_get_data(struct sipe_media_stream *stream)
2362 g_return_val_if_fail(stream, NULL);
2364 return SIPE_MEDIA_STREAM_PRIVATE->data;
2368 Local Variables:
2369 mode: c
2370 c-file-style: "bsd"
2371 indent-tabs-mode: t
2372 tab-width: 8
2373 End: