Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / core / sdpmsg.c
blobb9ce6b44192095a8262f7d9278f1b10c81432e17
1 /**
2 * @file sdpmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2013-2017 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 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include <glib.h>
30 #include "sipe-backend.h"
31 #include "sipe-core.h"
32 #include "sdpmsg.h"
33 #include "sipe-utils.h"
35 static gboolean
36 append_attribute(struct sdpmedia *media, gchar *attr)
38 gchar **parts = g_strsplit(attr + 2, ":", 2);
40 if(!parts[0]) {
41 g_strfreev(parts);
42 return FALSE;
45 media->attributes = sipe_utils_nameval_add(media->attributes,
46 parts[0],
47 parts[1] ? parts[1] : "");
48 g_strfreev(parts);
49 return TRUE;
52 static gboolean
53 parse_attributes(struct sdpmsg *smsg, const gchar *msg) {
54 gchar **lines = g_strsplit(msg, "\r\n", 0);
55 gchar **ptr = lines;
57 while (*ptr != NULL) {
58 if (g_str_has_prefix(*ptr, "o=")) {
59 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
60 if (g_strv_length(parts) != 6) {
61 g_strfreev(parts);
62 g_strfreev(lines);
63 return FALSE;
66 smsg->ip = g_strdup(parts[5]);
67 g_strfreev(parts);
68 } else if (g_str_has_prefix(*ptr, "m=")) {
69 gchar **parts;
70 struct sdpmedia *media;
72 parts = g_strsplit(*ptr + 2, " ", 3);
73 if (g_strv_length(parts) < 3) {
74 g_strfreev(parts);
75 g_strfreev(lines);
76 return FALSE;
79 media = g_new0(struct sdpmedia, 1);
81 smsg->media = g_slist_append(smsg->media, media);
83 media->name = g_strdup(parts[0]);
84 media->port = atoi(parts[1]);
85 media->encryption_active =
86 g_strstr_len(parts[2], -1, "/SAVP") != NULL;
88 g_strfreev(parts);
90 while (*(++ptr) && !g_str_has_prefix(*ptr, "m=")) {
92 if (g_str_has_prefix(*ptr, "a=")) {
93 if (!append_attribute(media, *ptr)) {
94 g_strfreev(lines);
95 return FALSE;
99 continue;
102 ++ptr;
105 g_strfreev(lines);
107 return TRUE;
110 static struct sdpcandidate * sdpcandidate_copy(struct sdpcandidate *candidate);
112 static SipeComponentType
113 parse_component(const gchar *str)
115 switch (atoi(str)) {
116 case 1: return SIPE_COMPONENT_RTP;
117 case 2: return SIPE_COMPONENT_RTCP;
118 default: return SIPE_COMPONENT_NONE;
122 static gchar *
123 base64_pad(const gchar* str)
125 size_t str_len = strlen(str);
126 int mod = str_len % 4;
128 if (mod > 0) {
129 gchar *result = NULL;
130 int pad = 4 - mod;
131 gchar *ptr = result = g_malloc(str_len + pad + 1);
133 memcpy(ptr, str, str_len);
134 ptr += str_len;
135 memset(ptr, '=', pad);
136 ptr += pad;
137 *ptr = '\0';
139 return result;
140 } else
141 return g_strdup(str);
144 static gboolean
145 parse_append_candidate_draft_6(gchar **tokens, GSList **candidates)
147 struct sdpcandidate *candidate;
149 if (g_strv_length(tokens) < 7 || strlen(tokens[4]) < 3) {
150 return FALSE;
153 candidate = g_new0(struct sdpcandidate, 1);
155 candidate->username = base64_pad(tokens[0]);
156 candidate->component = parse_component(tokens[1]);
157 candidate->password = base64_pad(tokens[2]);
159 if (sipe_strequal(tokens[3], "UDP"))
160 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
161 else if (sipe_strequal(tokens[3], "TCP"))
162 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
163 else {
164 sdpcandidate_free(candidate);
165 return FALSE;
168 candidate->priority = atoi(tokens[4] + 2);
169 candidate->ip = g_strdup(tokens[5]);
170 candidate->port = atoi(tokens[6]);
172 *candidates = g_slist_append(*candidates, candidate);
174 // draft 6 candidates are both active and passive
175 if (candidate->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
176 candidate = sdpcandidate_copy(candidate);
177 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
178 *candidates = g_slist_append(*candidates, candidate);
181 return TRUE;
184 static gboolean
185 parse_append_candidate_rfc_5245(gchar **tokens, GSList **candidates)
187 struct sdpcandidate *candidate;
189 if (g_strv_length(tokens) < 8) {
190 return FALSE;
193 candidate = g_new0(struct sdpcandidate, 1);
194 candidate->foundation = g_strdup(tokens[0]);
195 candidate->component = parse_component(tokens[1]);
197 if (sipe_strcase_equal(tokens[2], "UDP"))
198 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
199 else if (sipe_strcase_equal(tokens[2], "TCP-ACT"))
200 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
201 else if (sipe_strcase_equal(tokens[2], "TCP-PASS"))
202 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
203 else {
204 sdpcandidate_free(candidate);
205 return FALSE;
208 candidate->priority = atoi(tokens[3]);
209 candidate->ip = g_strdup(tokens[4]);
210 candidate->port = atoi(tokens[5]);
212 if (sipe_strcase_equal(tokens[7], "host"))
213 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
214 else if (sipe_strcase_equal(tokens[7], "relay"))
215 candidate->type = SIPE_CANDIDATE_TYPE_RELAY;
216 else if (sipe_strcase_equal(tokens[7], "srflx"))
217 candidate->type = SIPE_CANDIDATE_TYPE_SRFLX;
218 else if (sipe_strcase_equal(tokens[7], "prflx"))
219 candidate->type = SIPE_CANDIDATE_TYPE_PRFLX;
220 else {
221 sdpcandidate_free(candidate);
222 return FALSE;
225 *candidates = g_slist_append(*candidates, candidate);
227 return TRUE;
230 static gboolean
231 parse_candidates(GSList *attrs, SipeIceVersion *ice_version, GSList **candidates)
233 const gchar *attr;
234 int i = 0;
236 g_return_val_if_fail(*candidates == NULL, FALSE);
238 *ice_version = SIPE_ICE_NO_ICE;
240 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
241 gchar **tokens = g_strsplit_set(attr, " ", 0);
242 gboolean parsed_ok;
244 if (g_strv_length(tokens) < 7) {
245 g_strfreev(tokens);
246 return FALSE;
249 if (sipe_strequal(tokens[6], "typ")) {
250 parsed_ok = parse_append_candidate_rfc_5245(tokens,
251 candidates);
252 if (*candidates)
253 *ice_version = SIPE_ICE_RFC_5245;
254 } else {
255 parsed_ok = parse_append_candidate_draft_6(tokens,
256 candidates);
257 if (*candidates)
258 *ice_version = SIPE_ICE_DRAFT_6;
261 g_strfreev(tokens);
263 if (!parsed_ok) {
264 return FALSE;
268 if (*ice_version == SIPE_ICE_RFC_5245) {
269 const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag");
270 const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd");
272 if (username && password) {
273 GSList *i;
274 for (i = *candidates; i; i = i->next) {
275 struct sdpcandidate *c = i->data;
276 c->username = g_strdup(username);
277 c->password = g_strdup(password);
282 return TRUE;
285 static GSList *
286 create_legacy_candidates(gchar *ip, guint16 port)
288 struct sdpcandidate *candidate;
289 GSList *candidates = NULL;
291 candidate = g_new0(struct sdpcandidate, 1);
292 candidate->foundation = g_strdup("1");
293 candidate->component = SIPE_COMPONENT_RTP;
294 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
295 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
296 candidate->ip = g_strdup(ip);
297 candidate->port = port;
299 candidates = g_slist_append(candidates, candidate);
301 candidate = g_new0(struct sdpcandidate, 1);
302 candidate->foundation = g_strdup("1");
303 candidate->component = SIPE_COMPONENT_RTCP;
304 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
305 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
306 candidate->ip = g_strdup(ip);
307 candidate->port = port + 1;
309 candidates = g_slist_append(candidates, candidate);
311 return candidates;
314 static gboolean
315 parse_codec_parameters(GSList *attrs, struct sdpcodec *codec)
317 const gchar* params;
318 int i = 0;
320 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", i++))) {
321 gchar **tokens;
322 gchar **param;
324 tokens = g_strsplit(params, " ", 0);
325 if (g_strv_length(tokens) < 1) {
326 g_strfreev(tokens);
327 return FALSE;
330 if (atoi(tokens[0]) != codec->id) {
331 g_strfreev(tokens);
332 continue;
335 for (param = tokens + 1; *param; ++param) {
336 gchar **nameval = g_strsplit(*param, "=", 2);
338 if (g_strv_length(nameval) != 2) {
339 g_strfreev(nameval);
340 continue;
343 codec->parameters =
344 sipe_utils_nameval_add(codec->parameters,
345 nameval[0],
346 nameval[1]);
348 g_strfreev(nameval);
351 g_strfreev(tokens);
354 return TRUE;
358 static gboolean
359 parse_codecs(GSList *attrs, SipeMediaType type, GSList **codecs)
361 int i = 0;
362 const gchar *attr;
364 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
365 struct sdpcodec *codec;
366 gchar **tokens;
368 tokens = g_strsplit_set(attr, " /", 4);
369 if (g_strv_length(tokens) < 3) {
370 g_strfreev(tokens);
371 return FALSE;
374 codec = g_new0(struct sdpcodec, 1);
375 codec->id = atoi(tokens[0]);
376 codec->name = g_strdup(tokens[1]);
377 codec->clock_rate = atoi(tokens[2]);
378 codec->type = type;
380 if (type == SIPE_MEDIA_AUDIO) {
381 codec->channels = tokens[3] ? atoi(tokens[3]) : 1;
384 g_strfreev(tokens);
386 if (!parse_codec_parameters(attrs, codec)) {
387 sdpcodec_free(codec);
388 return FALSE;
391 *codecs = g_slist_append(*codecs, codec);
394 return TRUE;
397 static void
398 parse_encryption_key(GSList *attrs, guchar **key, int *key_id)
400 int i = 0;
401 const gchar *attr;
403 while ((attr = sipe_utils_nameval_find_instance(attrs, "crypto", i++))) {
404 gchar **tokens = g_strsplit_set(attr, " :|", 6);
406 if (tokens[0] && tokens[1] && tokens[2] && tokens[3] && tokens[4] &&
407 sipe_strcase_equal(tokens[1], "AES_CM_128_HMAC_SHA1_80") &&
408 sipe_strequal(tokens[2], "inline") &&
409 !tokens[5]) {
410 gsize key_len;
411 *key = g_base64_decode(tokens[3], &key_len);
412 if (key_len != SIPE_SRTP_KEY_LEN) {
413 g_free(*key);
414 *key = NULL;
416 *key_id = atoi(tokens[0]);
419 g_strfreev(tokens);
421 if (*key) {
422 break;
427 struct sdpmsg *
428 sdpmsg_parse_msg(const gchar *msg)
430 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
431 GSList *i;
433 if (!parse_attributes(smsg, msg)) {
434 sdpmsg_free(smsg);
435 return NULL;
438 smsg->ice_version = SIPE_ICE_NO_ICE;
439 for (i = smsg->media; i; i = i->next) {
440 struct sdpmedia *media = i->data;
441 SipeMediaType type;
442 SipeIceVersion detected_ice_version;
444 if (!parse_candidates(media->attributes, &detected_ice_version,
445 &media->candidates)) {
446 sdpmsg_free(smsg);
447 return NULL;
450 if (media->port != 0) {
451 smsg->ice_version = detected_ice_version;
453 if (!media->candidates) {
454 // No a=candidate in SDP message, this seems to be MSOC 2005
455 media->candidates = create_legacy_candidates(smsg->ip, media->port);
459 if (sipe_strequal(media->name, "audio"))
460 type = SIPE_MEDIA_AUDIO;
461 else if (sipe_strequal(media->name, "video"))
462 type = SIPE_MEDIA_VIDEO;
463 else if (sipe_strequal(media->name, "data"))
464 type = SIPE_MEDIA_APPLICATION;
465 else if (sipe_strequal(media->name, "applicationsharing"))
466 type = SIPE_MEDIA_APPLICATION;
467 else {
468 // Unknown media type
469 sdpmsg_free(smsg);
470 return NULL;
473 if (!parse_codecs(media->attributes, type, &media->codecs)) {
474 sdpmsg_free(smsg);
475 return NULL;
478 parse_encryption_key(media->attributes, &media->encryption_key,
479 &media->encryption_key_id);
482 return smsg;
485 static gchar *
486 codecs_to_string(GSList *codecs)
488 GString *result = g_string_new(NULL);
490 for (; codecs; codecs = codecs->next) {
491 struct sdpcodec *c = codecs->data;
492 GSList *params = c->parameters;
494 g_string_append_printf(result,
495 "a=rtpmap:%d %s/%d\r\n",
496 c->id,
497 c->name,
498 c->clock_rate);
500 if (params) {
501 GString *param_str = g_string_new(NULL);
502 int written_params = 0;
504 g_string_append_printf(param_str, "a=fmtp:%d", c->id);
506 for (; params; params = params->next) {
507 struct sipnameval* par = params->data;
508 if (sipe_strequal(par->name, "farsight-send-profile")) {
509 // Lync AVMCU doesn't like this property.
510 continue;
513 g_string_append_printf(param_str, " %s=%s",
514 par->name, par->value);
515 ++written_params;
518 g_string_append(param_str, "\r\n");
520 if (written_params > 0) {
521 g_string_append(result, param_str->str);
524 g_string_free(param_str, TRUE);
528 return g_string_free(result, FALSE);
531 static gchar *
532 codec_ids_to_string(GSList *codecs)
534 GString *result = g_string_new(NULL);
536 for (; codecs; codecs = codecs->next) {
537 struct sdpcodec *c = codecs->data;
538 g_string_append_printf(result, " %d", c->id);
541 return g_string_free(result, FALSE);
544 static gchar *
545 base64_unpad(const gchar *str)
547 gchar *result = g_strdup(str);
548 gchar *ptr;
550 for (ptr = result + strlen(result); ptr != result; --ptr) {
551 if (*(ptr - 1) != '=') {
552 *ptr = '\0';
553 break;
557 return result;
560 static gchar *
561 candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
563 GString *result = g_string_new("");
564 GSList *i;
565 GSList *processed_tcp_candidates = NULL;
567 for (i = candidates; i; i = i->next) {
568 struct sdpcandidate *c = i->data;
569 const gchar *protocol;
570 const gchar *type;
571 gchar *related = NULL;
573 if (ice_version == SIPE_ICE_RFC_5245) {
575 switch (c->protocol) {
576 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
577 protocol = "TCP-ACT";
578 break;
579 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
580 protocol = "TCP-PASS";
581 break;
582 case SIPE_NETWORK_PROTOCOL_UDP:
583 protocol = "UDP";
584 break;
585 default:
586 /* error unknown/unsupported type */
587 protocol = "UNKNOWN";
588 break;
591 switch (c->type) {
592 case SIPE_CANDIDATE_TYPE_HOST:
593 type = "host";
594 break;
595 case SIPE_CANDIDATE_TYPE_RELAY:
596 type = "relay";
597 break;
598 case SIPE_CANDIDATE_TYPE_SRFLX:
599 type = "srflx";
600 break;
601 case SIPE_CANDIDATE_TYPE_PRFLX:
602 type = "prflx";
603 break;
604 default:
605 /* error unknown/unsupported type */
606 type = "unknown";
607 break;
610 switch (c->type) {
611 case SIPE_CANDIDATE_TYPE_RELAY:
612 case SIPE_CANDIDATE_TYPE_SRFLX:
613 case SIPE_CANDIDATE_TYPE_PRFLX:
614 related = g_strdup_printf("raddr %s rport %d",
615 c->base_ip,
616 c->base_port);
617 break;
618 default:
619 break;
622 g_string_append_printf(result,
623 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
624 c->foundation,
625 c->component,
626 protocol,
627 c->priority,
628 c->ip,
629 c->port,
630 type,
631 related ? related : "");
632 g_free(related);
634 } else if (ice_version == SIPE_ICE_DRAFT_6) {
635 gchar *username;
636 gchar *password;
638 switch (c->protocol) {
639 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
640 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: {
641 GSList *prev_cand = processed_tcp_candidates;
642 for (; prev_cand; prev_cand = prev_cand->next) {
643 struct sdpcandidate *c2 = (struct sdpcandidate *)prev_cand->data;
645 if (sipe_strequal(c->ip, c2->ip) &&
646 c->component == c2->component) {
647 break;
651 if (prev_cand) {
652 protocol = NULL;
653 } else {
654 protocol = "TCP";
655 processed_tcp_candidates =
656 g_slist_append(processed_tcp_candidates, c);
658 break;
660 case SIPE_NETWORK_PROTOCOL_UDP:
661 protocol = "UDP";
662 break;
663 default:
664 /* unknown/unsupported type, ignore */
665 protocol = NULL;
666 break;
669 if (!protocol) {
670 continue;
673 username = base64_unpad(c->username);
674 password = base64_unpad(c->password);
676 g_string_append_printf(result,
677 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
678 username,
679 c->component,
680 password,
681 protocol,
682 c->priority,
683 c->ip,
684 c->port);
686 g_free(username);
687 g_free(password);
691 g_slist_free(processed_tcp_candidates);
693 return g_string_free(result, FALSE);
696 static gint
697 remote_candidates_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
699 return c1->component - c2->component;
702 static gchar *
703 remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
705 GString *result = g_string_new("");
707 if (candidates) {
708 // Sort the candidates by increasing component IDs.
709 candidates = g_slist_sort(candidates,
710 (GCompareFunc)remote_candidates_sort_cb);
712 if (ice_version == SIPE_ICE_RFC_5245) {
713 GSList *i;
714 g_string_append(result, "a=remote-candidates:");
716 for (i = candidates; i; i = i->next) {
717 struct sdpcandidate *c = i->data;
718 g_string_append_printf(result, "%u %s %u ",
719 c->component, c->ip, c->port);
722 g_string_append(result, "\r\n");
723 } else if (ice_version == SIPE_ICE_DRAFT_6) {
724 struct sdpcandidate *c = candidates->data;
725 g_string_append_printf(result, "a=remote-candidate:%s\r\n",
726 c->username);
730 return g_string_free(result, FALSE);
733 static gchar *
734 attributes_to_string(GSList *attributes)
736 GString *result = g_string_new("");
738 for (; attributes; attributes = attributes->next) {
739 struct sipnameval *a = attributes->data;
740 g_string_append_printf(result, "a=%s", a->name);
741 if (!sipe_strequal(a->value, ""))
742 g_string_append_printf(result, ":%s", a->value);
743 g_string_append(result, "\r\n");
746 return g_string_free(result, FALSE);
749 static gchar *
750 media_to_string(const struct sdpmsg *msg, const struct sdpmedia *media)
752 gchar *media_str;
754 gchar *transport_profile = NULL;
756 gchar *media_conninfo = NULL;
758 gchar *codecs_str = NULL;
759 gchar *codec_ids_str = codec_ids_to_string(media->codecs);
761 gchar *candidates_str = NULL;
762 gchar *remote_candidates_str = NULL;
764 gchar *attributes_str = NULL;
765 gchar *credentials = NULL;
767 gchar *crypto = NULL;
769 gboolean uses_tcp_transport = TRUE;
771 if (media->port != 0) {
772 if (!sipe_strequal(msg->ip, media->ip)) {
773 media_conninfo = g_strdup_printf("c=IN %s %s\r\n",
774 sipe_utils_ip_sdp_address_marker(media->ip),
775 media->ip);
778 codecs_str = codecs_to_string(media->codecs);
779 candidates_str = candidates_to_string(media->candidates, msg->ice_version);
780 remote_candidates_str = remote_candidates_to_string(media->remote_candidates,
781 msg->ice_version);
783 if (media->remote_candidates) {
784 struct sdpcandidate *c = media->remote_candidates->data;
785 uses_tcp_transport =
786 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE ||
787 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
788 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_SO;
789 } else {
790 GSList *candidates = media->candidates;
791 for (; candidates; candidates = candidates->next) {
792 struct sdpcandidate *c = candidates->data;
793 if (c->protocol == SIPE_NETWORK_PROTOCOL_UDP) {
794 uses_tcp_transport = FALSE;
795 break;
800 attributes_str = attributes_to_string(media->attributes);
802 if (msg->ice_version == SIPE_ICE_RFC_5245 && media->candidates) {
803 struct sdpcandidate *c = media->candidates->data;
805 credentials = g_strdup_printf("a=ice-ufrag:%s\r\n"
806 "a=ice-pwd:%s\r\n",
807 c->username,
808 c->password);
811 if (media->encryption_key) {
812 gchar *key_encoded = g_base64_encode(media->encryption_key, SIPE_SRTP_KEY_LEN);
813 crypto = g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n",
814 media->encryption_key_id, key_encoded);
815 g_free(key_encoded);
819 transport_profile = g_strdup_printf("%sRTP/%sAVP",
820 uses_tcp_transport ? "TCP/" : "",
821 media->encryption_active ? "S" : "");
823 media_str = g_strdup_printf("m=%s %d %s%s\r\n"
824 "%s"
825 "%s"
826 "%s"
827 "%s"
828 "%s"
829 "%s"
830 "%s",
831 media->name, media->port, transport_profile, codec_ids_str,
832 media_conninfo ? media_conninfo : "",
833 candidates_str ? candidates_str : "",
834 crypto ? crypto : "",
835 remote_candidates_str ? remote_candidates_str : "",
836 codecs_str ? codecs_str : "",
837 attributes_str ? attributes_str : "",
838 credentials ? credentials : "");
840 g_free(transport_profile);
841 g_free(media_conninfo);
842 g_free(codecs_str);
843 g_free(codec_ids_str);
844 g_free(candidates_str);
845 g_free(remote_candidates_str);
846 g_free(attributes_str);
847 g_free(credentials);
848 g_free(crypto);
850 return media_str;
853 gchar *
854 sdpmsg_to_string(const struct sdpmsg *msg)
856 GString *body = g_string_new(NULL);
857 GSList *i;
858 const gchar *marker = sipe_utils_ip_sdp_address_marker(msg->ip);
860 g_string_append_printf(
861 body,
862 "v=0\r\n"
863 "o=- 0 0 IN %s %s\r\n"
864 "s=session\r\n"
865 "c=IN %s %s\r\n"
866 "b=CT:99980\r\n"
867 "t=0 0\r\n",
868 marker, msg->ip,
869 marker, msg->ip);
872 for (i = msg->media; i; i = i->next) {
873 gchar *media_str = media_to_string(msg, i->data);
874 g_string_append(body, media_str);
875 g_free(media_str);
878 return g_string_free(body, FALSE);
881 static struct sdpcandidate *
882 sdpcandidate_copy(struct sdpcandidate *candidate)
884 if (candidate) {
885 struct sdpcandidate *copy = g_new0(struct sdpcandidate, 1);
887 copy->foundation = g_strdup(candidate->foundation);
888 copy->component = candidate->component;
889 copy->type = candidate->type;
890 copy->protocol = candidate->protocol;
891 copy->priority = candidate->priority;
892 copy->ip = g_strdup(candidate->ip);
893 copy->port = candidate->port;
894 copy->base_ip = g_strdup(candidate->base_ip);
895 copy->base_port = candidate->base_port;
896 copy->username = g_strdup(candidate->username);
897 copy->password = g_strdup(candidate->password);
899 return copy;
900 } else
901 return NULL;
904 void
905 sdpcandidate_free(struct sdpcandidate *candidate)
907 if (candidate) {
908 g_free(candidate->foundation);
909 g_free(candidate->ip);
910 g_free(candidate->base_ip);
911 g_free(candidate->username);
912 g_free(candidate->password);
913 g_free(candidate);
917 void
918 sdpcodec_free(struct sdpcodec *codec)
920 if (codec) {
921 g_free(codec->name);
922 sipe_utils_nameval_free(codec->parameters);
923 g_free(codec);
927 void
928 sdpmedia_free(struct sdpmedia *media)
930 if (media) {
931 g_free(media->name);
932 g_free(media->ip);
934 sipe_utils_nameval_free(media->attributes);
936 sipe_utils_slist_free_full(media->candidates,
937 (GDestroyNotify) sdpcandidate_free);
938 sipe_utils_slist_free_full(media->codecs,
939 (GDestroyNotify) sdpcodec_free);
940 sipe_utils_slist_free_full(media->remote_candidates,
941 (GDestroyNotify) sdpcandidate_free);
943 g_free(media->encryption_key);
945 g_free(media);
949 void
950 sdpmsg_free(struct sdpmsg *msg)
952 if (msg) {
953 g_free(msg->ip);
954 sipe_utils_slist_free_full(msg->media,
955 (GDestroyNotify) sdpmedia_free);
956 g_free(msg);
961 Local Variables:
962 mode: c
963 c-file-style: "bsd"
964 indent-tabs-mode: t
965 tab-width: 8
966 End: