media: also fill zero ports of active candidates
[siplcs.git] / src / core / sdpmsg.c
blob32645fec4aeac039204c1aa0de19b84e8681b77d
1 /**
2 * @file sdpmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2013-2015 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, 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 smsg->ip = g_strdup(parts[5]);
61 g_strfreev(parts);
62 } else if (g_str_has_prefix(*ptr, "m=")) {
63 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
64 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
66 smsg->media = g_slist_append(smsg->media, media);
68 media->name = g_strdup(parts[0]);
69 media->port = atoi(parts[1]);
70 media->encryption_active =
71 g_strstr_len(parts[2], -1, "/SAVP") != NULL;
73 g_strfreev(parts);
75 while (*(++ptr) && !g_str_has_prefix(*ptr, "m=")) {
77 if (g_str_has_prefix(*ptr, "a=")) {
78 if (!append_attribute(media, *ptr)) {
79 g_strfreev(lines);
80 return FALSE;
84 continue;
87 ++ptr;
90 g_strfreev(lines);
92 return TRUE;
95 static struct sdpcandidate * sdpcandidate_copy(struct sdpcandidate *candidate);
97 static SipeComponentType
98 parse_component(const gchar *str)
100 switch (atoi(str)) {
101 case 1: return SIPE_COMPONENT_RTP;
102 case 2: return SIPE_COMPONENT_RTCP;
103 default: return SIPE_COMPONENT_NONE;
107 static gchar *
108 base64_pad(const gchar* str)
110 size_t str_len = strlen(str);
111 int mod = str_len % 4;
113 if (mod > 0) {
114 gchar *result = NULL;
115 int pad = 4 - mod;
116 gchar *ptr = result = g_malloc(str_len + pad + 1);
118 memcpy(ptr, str, str_len);
119 ptr += str_len;
120 memset(ptr, '=', pad);
121 ptr += pad;
122 *ptr = '\0';
124 return result;
125 } else
126 return g_strdup(str);
129 static GSList *
130 parse_append_candidate_draft_6(gchar **tokens, GSList *candidates)
132 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
134 candidate->username = base64_pad(tokens[0]);
135 candidate->component = parse_component(tokens[1]);
136 candidate->password = base64_pad(tokens[2]);
138 if (sipe_strequal(tokens[3], "UDP"))
139 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
140 else if (sipe_strequal(tokens[3], "TCP"))
141 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
142 else {
143 sdpcandidate_free(candidate);
144 return candidates;
147 candidate->priority = atoi(tokens[4] + 2);
148 candidate->ip = g_strdup(tokens[5]);
149 candidate->port = atoi(tokens[6]);
151 candidates = g_slist_append(candidates, candidate);
153 // draft 6 candidates are both active and passive
154 if (candidate->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
155 candidate = sdpcandidate_copy(candidate);
156 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
157 candidates = g_slist_append(candidates, candidate);
160 return candidates;
163 static GSList *
164 parse_append_candidate_rfc_5245(gchar **tokens, GSList *candidates)
166 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
168 candidate->foundation = g_strdup(tokens[0]);
169 candidate->component = parse_component(tokens[1]);
171 if (sipe_strcase_equal(tokens[2], "UDP"))
172 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
173 else if (sipe_strcase_equal(tokens[2], "TCP-ACT"))
174 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
175 else if (sipe_strcase_equal(tokens[2], "TCP-PASS"))
176 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
177 else {
178 sdpcandidate_free(candidate);
179 return candidates;
182 candidate->priority = atoi(tokens[3]);
183 candidate->ip = g_strdup(tokens[4]);
184 candidate->port = atoi(tokens[5]);
186 if (sipe_strcase_equal(tokens[7], "host"))
187 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
188 else if (sipe_strcase_equal(tokens[7], "relay"))
189 candidate->type = SIPE_CANDIDATE_TYPE_RELAY;
190 else if (sipe_strcase_equal(tokens[7], "srflx"))
191 candidate->type = SIPE_CANDIDATE_TYPE_SRFLX;
192 else if (sipe_strcase_equal(tokens[7], "prflx"))
193 candidate->type = SIPE_CANDIDATE_TYPE_PRFLX;
194 else {
195 sdpcandidate_free(candidate);
196 return candidates;
199 candidates = g_slist_append(candidates, candidate);
201 // TCP-ACT candidates are both active and passive
202 if (candidate->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
203 candidate = sdpcandidate_copy(candidate);
204 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
205 candidates = g_slist_append(candidates, candidate);
207 return candidates;
210 static GSList *
211 parse_candidates(GSList *attrs, SipeIceVersion *ice_version)
213 GSList *candidates = NULL;
214 const gchar *attr;
215 int i = 0;
217 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
218 gchar **tokens = g_strsplit_set(attr, " ", 0);
220 if (sipe_strequal(tokens[6], "typ")) {
221 candidates = parse_append_candidate_rfc_5245(tokens, candidates);
222 if (candidates)
223 *ice_version = SIPE_ICE_RFC_5245;
224 } else {
225 candidates = parse_append_candidate_draft_6(tokens, candidates);
226 if (candidates)
227 *ice_version = SIPE_ICE_DRAFT_6;
230 g_strfreev(tokens);
233 if (!candidates)
234 *ice_version = SIPE_ICE_NO_ICE;
236 if (*ice_version == SIPE_ICE_RFC_5245) {
237 const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag");
238 const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd");
240 if (username && password) {
241 GSList *i;
242 for (i = candidates; i; i = i->next) {
243 struct sdpcandidate *c = i->data;
244 c->username = g_strdup(username);
245 c->password = g_strdup(password);
250 return candidates;
253 static GSList *
254 create_legacy_candidates(gchar *ip, guint16 port)
256 struct sdpcandidate *candidate;
257 GSList *candidates = NULL;
259 candidate = g_new0(struct sdpcandidate, 1);
260 candidate->foundation = g_strdup("1");
261 candidate->component = SIPE_COMPONENT_RTP;
262 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
263 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
264 candidate->ip = g_strdup(ip);
265 candidate->port = port;
267 candidates = g_slist_append(candidates, candidate);
269 candidate = g_new0(struct sdpcandidate, 1);
270 candidate->foundation = g_strdup("1");
271 candidate->component = SIPE_COMPONENT_RTCP;
272 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
273 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
274 candidate->ip = g_strdup(ip);
275 candidate->port = port + 1;
277 candidates = g_slist_append(candidates, candidate);
279 return candidates;
282 static GSList *
283 parse_codecs(GSList *attrs, SipeMediaType type)
285 int i = 0;
286 const gchar *attr;
287 GSList *codecs = NULL;
289 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
290 struct sdpcodec *codec = g_new0(struct sdpcodec, 1);
291 gchar **tokens = g_strsplit_set(attr, " /", 3);
293 int j = 0;
294 const gchar* params;
296 codec->id = atoi(tokens[0]);
297 codec->name = g_strdup(tokens[1]);
298 codec->clock_rate = atoi(tokens[2]);
299 codec->type = type;
301 // TODO: more secure and effective implementation
302 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", j++))) {
303 gchar **tokens = g_strsplit_set(params, " ", 0);
304 gchar **next = tokens + 1;
306 if (atoi(tokens[0]) == codec->id) {
307 while (*next) {
308 gchar name[50];
309 gchar value[50];
311 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
312 codec->parameters = sipe_utils_nameval_add(codec->parameters, name, value);
314 ++next;
318 g_strfreev(tokens);
321 codecs = g_slist_append(codecs, codec);
322 g_strfreev(tokens);
325 return codecs;
328 static void
329 parse_encryption_key(GSList *attrs, guchar **key, int *key_id)
331 int i = 0;
332 const gchar *attr;
334 while ((attr = sipe_utils_nameval_find_instance(attrs, "crypto", i++))) {
335 gchar **tokens = g_strsplit_set(attr, " :|", 6);
337 if (tokens[0] && tokens[1] && tokens[2] && tokens[3] && tokens[4] &&
338 sipe_strcase_equal(tokens[1], "AES_CM_128_HMAC_SHA1_80") &&
339 sipe_strequal(tokens[2], "inline") &&
340 !tokens[5]) {
341 gsize key_len;
342 *key = g_base64_decode(tokens[3], &key_len);
343 if (key_len != SIPE_SRTP_KEY_LEN) {
344 g_free(*key);
345 *key = NULL;
347 *key_id = atoi(tokens[0]);
350 g_strfreev(tokens);
352 if (*key) {
353 break;
358 struct sdpmsg *
359 sdpmsg_parse_msg(gchar *msg)
361 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
362 GSList *i;
364 if (!parse_attributes(smsg, msg)) {
365 sdpmsg_free(smsg);
366 return NULL;
369 for (i = smsg->media; i; i = i->next) {
370 struct sdpmedia *media = i->data;
371 SipeMediaType type;
373 media->candidates = parse_candidates(media->attributes,
374 &smsg->ice_version);
376 if (!media->candidates && media->port != 0) {
377 // No a=candidate in SDP message, this seems to be MSOC 2005
378 media->candidates = create_legacy_candidates(smsg->ip, media->port);
381 if (sipe_strequal(media->name, "audio"))
382 type = SIPE_MEDIA_AUDIO;
383 else if (sipe_strequal(media->name, "video"))
384 type = SIPE_MEDIA_VIDEO;
385 else {
386 // Unknown media type
387 sdpmsg_free(smsg);
388 return NULL;
391 media->codecs = parse_codecs(media->attributes, type);
392 parse_encryption_key(media->attributes, &media->encryption_key,
393 &media->encryption_key_id);
396 return smsg;
399 static gchar *
400 codecs_to_string(GSList *codecs)
402 GString *result = g_string_new(NULL);
404 for (; codecs; codecs = codecs->next) {
405 struct sdpcodec *c = codecs->data;
406 GSList *params = c->parameters;
408 g_string_append_printf(result,
409 "a=rtpmap:%d %s/%d\r\n",
410 c->id,
411 c->name,
412 c->clock_rate);
414 if (params) {
415 GString *param_str = g_string_new(NULL);
416 int written_params = 0;
418 g_string_append_printf(param_str, "a=fmtp:%d", c->id);
420 for (; params; params = params->next) {
421 struct sipnameval* par = params->data;
422 if (sipe_strequal(par->name, "farsight-send-profile")) {
423 // Lync AVMCU doesn't like this property.
424 continue;
427 g_string_append_printf(param_str, " %s=%s",
428 par->name, par->value);
429 ++written_params;
432 g_string_append(param_str, "\r\n");
434 if (written_params > 0) {
435 g_string_append(result, param_str->str);
438 g_string_free(param_str, TRUE);
442 return g_string_free(result, FALSE);
445 static gchar *
446 codec_ids_to_string(GSList *codecs)
448 GString *result = g_string_new(NULL);
450 for (; codecs; codecs = codecs->next) {
451 struct sdpcodec *c = codecs->data;
452 g_string_append_printf(result, " %d", c->id);
455 return g_string_free(result, FALSE);
458 static gchar *
459 base64_unpad(const gchar *str)
461 gchar *result = g_strdup(str);
462 gchar *ptr;
464 for (ptr = result + strlen(result); ptr != result; --ptr) {
465 if (*(ptr - 1) != '=') {
466 *ptr = '\0';
467 break;
471 return result;
474 static gchar *
475 candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
477 GString *result = g_string_new("");
478 GSList *i;
479 GSList *processed_tcp_candidates = NULL;
481 for (i = candidates; i; i = i->next) {
482 struct sdpcandidate *c = i->data;
483 const gchar *protocol;
484 const gchar *type;
485 gchar *related = NULL;
487 if (ice_version == SIPE_ICE_RFC_5245) {
489 switch (c->protocol) {
490 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
491 protocol = "TCP-ACT";
492 break;
493 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
494 protocol = "TCP-PASS";
495 break;
496 case SIPE_NETWORK_PROTOCOL_UDP:
497 protocol = "UDP";
498 break;
499 default:
500 /* error unknown/unsupported type */
501 protocol = "UNKNOWN";
502 break;
505 switch (c->type) {
506 case SIPE_CANDIDATE_TYPE_HOST:
507 type = "host";
508 break;
509 case SIPE_CANDIDATE_TYPE_RELAY:
510 type = "relay";
511 break;
512 case SIPE_CANDIDATE_TYPE_SRFLX:
513 type = "srflx";
514 break;
515 case SIPE_CANDIDATE_TYPE_PRFLX:
516 type = "prflx";
517 break;
518 default:
519 /* error unknown/unsupported type */
520 type = "unknown";
521 break;
524 switch (c->type) {
525 case SIPE_CANDIDATE_TYPE_RELAY:
526 case SIPE_CANDIDATE_TYPE_SRFLX:
527 case SIPE_CANDIDATE_TYPE_PRFLX:
528 related = g_strdup_printf("raddr %s rport %d",
529 c->base_ip,
530 c->base_port);
531 break;
532 default:
533 break;
536 g_string_append_printf(result,
537 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
538 c->foundation,
539 c->component,
540 protocol,
541 c->priority,
542 c->ip,
543 c->port,
544 type,
545 related ? related : "");
546 g_free(related);
548 } else if (ice_version == SIPE_ICE_DRAFT_6) {
549 gchar *username;
550 gchar *password;
552 switch (c->protocol) {
553 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
554 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: {
555 GSList *prev_cand = processed_tcp_candidates;
556 for (; prev_cand; prev_cand = prev_cand->next) {
557 struct sdpcandidate *c2 = (struct sdpcandidate *)prev_cand->data;
559 if (sipe_strequal(c->ip, c2->ip) &&
560 c->component == c2->component) {
561 break;
565 if (prev_cand) {
566 protocol = NULL;
567 } else {
568 protocol = "TCP";
569 processed_tcp_candidates =
570 g_slist_append(processed_tcp_candidates, c);
572 break;
574 case SIPE_NETWORK_PROTOCOL_UDP:
575 protocol = "UDP";
576 break;
577 default:
578 /* unknown/unsupported type, ignore */
579 protocol = NULL;
580 break;
583 if (!protocol) {
584 continue;
587 username = base64_unpad(c->username);
588 password = base64_unpad(c->password);
590 g_string_append_printf(result,
591 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
592 username,
593 c->component,
594 password,
595 protocol,
596 c->priority,
597 c->ip,
598 c->port);
600 g_free(username);
601 g_free(password);
605 g_slist_free(processed_tcp_candidates);
607 return g_string_free(result, FALSE);
610 static gchar *
611 remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
613 GString *result = g_string_new("");
615 if (candidates) {
616 if (ice_version == SIPE_ICE_RFC_5245) {
617 GSList *i;
618 g_string_append(result, "a=remote-candidates:");
620 for (i = candidates; i; i = i->next) {
621 struct sdpcandidate *c = i->data;
622 g_string_append_printf(result, "%u %s %u ",
623 c->component, c->ip, c->port);
626 g_string_append(result, "\r\n");
627 } else if (ice_version == SIPE_ICE_DRAFT_6) {
628 struct sdpcandidate *c = candidates->data;
629 g_string_append_printf(result, "a=remote-candidate:%s\r\n",
630 c->username);
634 return g_string_free(result, FALSE);
637 static gchar *
638 attributes_to_string(GSList *attributes)
640 GString *result = g_string_new("");
642 for (; attributes; attributes = attributes->next) {
643 struct sipnameval *a = attributes->data;
644 g_string_append_printf(result, "a=%s", a->name);
645 if (!sipe_strequal(a->value, ""))
646 g_string_append_printf(result, ":%s", a->value);
647 g_string_append(result, "\r\n");
650 return g_string_free(result, FALSE);
653 static gchar *
654 media_to_string(const struct sdpmsg *msg, const struct sdpmedia *media)
656 gchar *media_str;
658 gchar *transport_profile = NULL;
660 gchar *media_conninfo = NULL;
662 gchar *codecs_str = NULL;
663 gchar *codec_ids_str = codec_ids_to_string(media->codecs);
665 gchar *candidates_str = NULL;
666 gchar *remote_candidates_str = NULL;
668 gchar *attributes_str = NULL;
669 gchar *credentials = NULL;
671 gchar *crypto = NULL;
673 gboolean uses_tcp_transport = TRUE;
675 if (media->port != 0) {
676 if (!sipe_strequal(msg->ip, media->ip)) {
677 media_conninfo = g_strdup_printf("c=IN IP4 %s\r\n", media->ip);
680 codecs_str = codecs_to_string(media->codecs);
681 candidates_str = candidates_to_string(media->candidates, msg->ice_version);
682 remote_candidates_str = remote_candidates_to_string(media->remote_candidates,
683 msg->ice_version);
685 if (media->remote_candidates) {
686 struct sdpcandidate *c = media->remote_candidates->data;
687 uses_tcp_transport =
688 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE ||
689 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
690 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_SO;
691 } else {
692 GSList *candidates = media->candidates;
693 for (; candidates; candidates = candidates->next) {
694 struct sdpcandidate *c = candidates->data;
695 if (c->protocol == SIPE_NETWORK_PROTOCOL_UDP) {
696 uses_tcp_transport = FALSE;
697 break;
702 attributes_str = attributes_to_string(media->attributes);
704 if (msg->ice_version == SIPE_ICE_RFC_5245 && media->candidates) {
705 struct sdpcandidate *c = media->candidates->data;
707 credentials = g_strdup_printf("a=ice-ufrag:%s\r\n"
708 "a=ice-pwd:%s\r\n",
709 c->username,
710 c->password);
713 if (media->encryption_key) {
714 gchar *key_encoded = g_base64_encode(media->encryption_key, SIPE_SRTP_KEY_LEN);
715 crypto = g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n",
716 media->encryption_key_id, key_encoded);
717 g_free(key_encoded);
721 transport_profile = g_strdup_printf("%sRTP/%sAVP",
722 uses_tcp_transport ? "TCP/" : "",
723 media->encryption_active ? "S" : "");
725 media_str = g_strdup_printf("m=%s %d %s%s\r\n"
726 "%s"
727 "%s"
728 "%s"
729 "%s"
730 "%s"
731 "%s"
732 "%s",
733 media->name, media->port, transport_profile, codec_ids_str,
734 media_conninfo ? media_conninfo : "",
735 candidates_str ? candidates_str : "",
736 crypto ? crypto : "",
737 remote_candidates_str ? remote_candidates_str : "",
738 codecs_str ? codecs_str : "",
739 attributes_str ? attributes_str : "",
740 credentials ? credentials : "");
742 g_free(transport_profile);
743 g_free(media_conninfo);
744 g_free(codecs_str);
745 g_free(codec_ids_str);
746 g_free(candidates_str);
747 g_free(remote_candidates_str);
748 g_free(attributes_str);
749 g_free(credentials);
750 g_free(crypto);
752 return media_str;
755 gchar *
756 sdpmsg_to_string(const struct sdpmsg *msg)
758 GString *body = g_string_new(NULL);
759 GSList *i;
761 g_string_append_printf(
762 body,
763 "v=0\r\n"
764 "o=- 0 0 IN IP4 %s\r\n"
765 "s=session\r\n"
766 "c=IN IP4 %s\r\n"
767 "b=CT:99980\r\n"
768 "t=0 0\r\n",
769 msg->ip, msg->ip);
772 for (i = msg->media; i; i = i->next) {
773 gchar *media_str = media_to_string(msg, i->data);
774 g_string_append(body, media_str);
775 g_free(media_str);
778 return g_string_free(body, FALSE);
781 static struct sdpcandidate *
782 sdpcandidate_copy(struct sdpcandidate *candidate)
784 if (candidate) {
785 struct sdpcandidate *copy = g_new0(struct sdpcandidate, 1);
787 copy->foundation = g_strdup(candidate->foundation);
788 copy->component = candidate->component;
789 copy->type = candidate->type;
790 copy->protocol = candidate->protocol;
791 copy->priority = candidate->priority;
792 copy->ip = g_strdup(candidate->ip);
793 copy->port = candidate->port;
794 copy->base_ip = g_strdup(candidate->base_ip);
795 copy->base_port = candidate->base_port;
796 copy->username = g_strdup(candidate->username);
797 copy->password = g_strdup(candidate->password);
799 return copy;
800 } else
801 return NULL;
804 void
805 sdpcandidate_free(struct sdpcandidate *candidate)
807 if (candidate) {
808 g_free(candidate->foundation);
809 g_free(candidate->ip);
810 g_free(candidate->base_ip);
811 g_free(candidate->username);
812 g_free(candidate->password);
813 g_free(candidate);
817 void
818 sdpcodec_free(struct sdpcodec *codec)
820 if (codec) {
821 g_free(codec->name);
822 sipe_utils_nameval_free(codec->parameters);
823 g_free(codec);
827 void
828 sdpmedia_free(struct sdpmedia *media)
830 if (media) {
831 g_free(media->name);
832 g_free(media->ip);
834 sipe_utils_nameval_free(media->attributes);
836 sipe_utils_slist_free_full(media->candidates,
837 (GDestroyNotify) sdpcandidate_free);
838 sipe_utils_slist_free_full(media->codecs,
839 (GDestroyNotify) sdpcodec_free);
840 sipe_utils_slist_free_full(media->remote_candidates,
841 (GDestroyNotify) sdpcandidate_free);
843 g_free(media->encryption_key);
845 g_free(media);
849 void
850 sdpmsg_free(struct sdpmsg *msg)
852 if (msg) {
853 g_free(msg->ip);
854 sipe_utils_slist_free_full(msg->media,
855 (GDestroyNotify) sdpmedia_free);
856 g_free(msg);
861 Local Variables:
862 mode: c
863 c-file-style: "bsd"
864 indent-tabs-mode: t
865 tab-width: 8
866 End: