tls: add Server Key Exchange message
[siplcs.git] / src / core / sdpmsg.c
blob09272389fd1601b9e89bb3a60d2351490f01f1bb
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);
96 static void sdpcandidate_free(struct sdpcandidate *candidate);
98 static SipeComponentType
99 parse_component(const gchar *str)
101 switch (atoi(str)) {
102 case 1: return SIPE_COMPONENT_RTP;
103 case 2: return SIPE_COMPONENT_RTCP;
104 default: return SIPE_COMPONENT_NONE;
108 static gchar *
109 base64_pad(const gchar* str)
111 size_t str_len = strlen(str);
112 int mod = str_len % 4;
114 if (mod > 0) {
115 gchar *result = NULL;
116 int pad = 4 - mod;
117 gchar *ptr = result = g_malloc(str_len + pad + 1);
119 memcpy(ptr, str, str_len);
120 ptr += str_len;
121 memset(ptr, '=', pad);
122 ptr += pad;
123 *ptr = '\0';
125 return result;
126 } else
127 return g_strdup(str);
130 static GSList *
131 parse_append_candidate_draft_6(gchar **tokens, GSList *candidates)
133 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
135 candidate->username = base64_pad(tokens[0]);
136 candidate->component = parse_component(tokens[1]);
137 candidate->password = base64_pad(tokens[2]);
139 if (sipe_strequal(tokens[3], "UDP"))
140 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
141 else if (sipe_strequal(tokens[3], "TCP"))
142 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
143 else {
144 sdpcandidate_free(candidate);
145 return candidates;
148 candidate->priority = atoi(tokens[4] + 2);
149 candidate->ip = g_strdup(tokens[5]);
150 candidate->port = atoi(tokens[6]);
152 candidates = g_slist_append(candidates, candidate);
154 // draft 6 candidates are both active and passive
155 if (candidate->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
156 candidate = sdpcandidate_copy(candidate);
157 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
158 candidates = g_slist_append(candidates, candidate);
161 return candidates;
164 static GSList *
165 parse_append_candidate_rfc_5245(gchar **tokens, GSList *candidates)
167 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
169 candidate->foundation = g_strdup(tokens[0]);
170 candidate->component = parse_component(tokens[1]);
172 if (sipe_strcase_equal(tokens[2], "UDP"))
173 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
174 else if (sipe_strcase_equal(tokens[2], "TCP-ACT"))
175 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
176 else if (sipe_strcase_equal(tokens[2], "TCP-PASS"))
177 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
178 else {
179 sdpcandidate_free(candidate);
180 return candidates;
183 candidate->priority = atoi(tokens[3]);
184 candidate->ip = g_strdup(tokens[4]);
185 candidate->port = atoi(tokens[5]);
187 if (sipe_strcase_equal(tokens[7], "host"))
188 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
189 else if (sipe_strcase_equal(tokens[7], "relay"))
190 candidate->type = SIPE_CANDIDATE_TYPE_RELAY;
191 else if (sipe_strcase_equal(tokens[7], "srflx"))
192 candidate->type = SIPE_CANDIDATE_TYPE_SRFLX;
193 else if (sipe_strcase_equal(tokens[7], "prflx"))
194 candidate->type = SIPE_CANDIDATE_TYPE_PRFLX;
195 else {
196 sdpcandidate_free(candidate);
197 return candidates;
200 candidates = g_slist_append(candidates, candidate);
202 // TCP-ACT candidates are both active and passive
203 if (candidate->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE) {
204 candidate = sdpcandidate_copy(candidate);
205 candidate->protocol = SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
206 candidates = g_slist_append(candidates, candidate);
208 return candidates;
211 static GSList *
212 parse_candidates(GSList *attrs, SipeIceVersion *ice_version)
214 GSList *candidates = NULL;
215 const gchar *attr;
216 int i = 0;
218 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
219 gchar **tokens = g_strsplit_set(attr, " ", 0);
221 if (sipe_strequal(tokens[6], "typ")) {
222 candidates = parse_append_candidate_rfc_5245(tokens, candidates);
223 if (candidates)
224 *ice_version = SIPE_ICE_RFC_5245;
225 } else {
226 candidates = parse_append_candidate_draft_6(tokens, candidates);
227 if (candidates)
228 *ice_version = SIPE_ICE_DRAFT_6;
231 g_strfreev(tokens);
234 if (!candidates)
235 *ice_version = SIPE_ICE_NO_ICE;
237 if (*ice_version == SIPE_ICE_RFC_5245) {
238 const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag");
239 const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd");
241 if (username && password) {
242 GSList *i;
243 for (i = candidates; i; i = i->next) {
244 struct sdpcandidate *c = i->data;
245 c->username = g_strdup(username);
246 c->password = g_strdup(password);
251 return candidates;
254 static GSList *
255 create_legacy_candidates(gchar *ip, guint16 port)
257 struct sdpcandidate *candidate;
258 GSList *candidates = NULL;
260 candidate = g_new0(struct sdpcandidate, 1);
261 candidate->foundation = g_strdup("1");
262 candidate->component = SIPE_COMPONENT_RTP;
263 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
264 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
265 candidate->ip = g_strdup(ip);
266 candidate->port = port;
268 candidates = g_slist_append(candidates, candidate);
270 candidate = g_new0(struct sdpcandidate, 1);
271 candidate->foundation = g_strdup("1");
272 candidate->component = SIPE_COMPONENT_RTCP;
273 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
274 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
275 candidate->ip = g_strdup(ip);
276 candidate->port = port + 1;
278 candidates = g_slist_append(candidates, candidate);
280 return candidates;
283 static GSList *
284 parse_codecs(GSList *attrs, SipeMediaType type)
286 int i = 0;
287 const gchar *attr;
288 GSList *codecs = NULL;
290 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
291 struct sdpcodec *codec = g_new0(struct sdpcodec, 1);
292 gchar **tokens = g_strsplit_set(attr, " /", 3);
294 int j = 0;
295 const gchar* params;
297 codec->id = atoi(tokens[0]);
298 codec->name = g_strdup(tokens[1]);
299 codec->clock_rate = atoi(tokens[2]);
300 codec->type = type;
302 // TODO: more secure and effective implementation
303 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", j++))) {
304 gchar **tokens = g_strsplit_set(params, " ", 0);
305 gchar **next = tokens + 1;
307 if (atoi(tokens[0]) == codec->id) {
308 while (*next) {
309 gchar name[50];
310 gchar value[50];
312 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
313 codec->parameters = sipe_utils_nameval_add(codec->parameters, name, value);
315 ++next;
319 g_strfreev(tokens);
322 codecs = g_slist_append(codecs, codec);
323 g_strfreev(tokens);
326 return codecs;
329 static void
330 parse_encryption_key(GSList *attrs, guchar **key, int *key_id)
332 int i = 0;
333 const gchar *attr;
335 while ((attr = sipe_utils_nameval_find_instance(attrs, "crypto", i++))) {
336 gchar **tokens = g_strsplit_set(attr, " :|", 6);
338 if (tokens[0] && tokens[1] && tokens[2] && tokens[3] && tokens[4] &&
339 sipe_strcase_equal(tokens[1], "AES_CM_128_HMAC_SHA1_80") &&
340 sipe_strequal(tokens[2], "inline") &&
341 !tokens[5]) {
342 gsize key_len;
343 *key = g_base64_decode(tokens[3], &key_len);
344 if (key_len != SIPE_SRTP_KEY_LEN) {
345 g_free(*key);
346 *key = NULL;
348 *key_id = atoi(tokens[0]);
351 g_strfreev(tokens);
353 if (*key) {
354 break;
359 struct sdpmsg *
360 sdpmsg_parse_msg(gchar *msg)
362 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
363 GSList *i;
365 if (!parse_attributes(smsg, msg)) {
366 sdpmsg_free(smsg);
367 return NULL;
370 for (i = smsg->media; i; i = i->next) {
371 struct sdpmedia *media = i->data;
372 SipeMediaType type;
374 media->candidates = parse_candidates(media->attributes,
375 &smsg->ice_version);
377 if (!media->candidates && media->port != 0) {
378 // No a=candidate in SDP message, this seems to be MSOC 2005
379 media->candidates = create_legacy_candidates(smsg->ip, media->port);
382 if (sipe_strequal(media->name, "audio"))
383 type = SIPE_MEDIA_AUDIO;
384 else if (sipe_strequal(media->name, "video"))
385 type = SIPE_MEDIA_VIDEO;
386 else {
387 // Unknown media type
388 sdpmsg_free(smsg);
389 return NULL;
392 media->codecs = parse_codecs(media->attributes, type);
393 parse_encryption_key(media->attributes, &media->encryption_key,
394 &media->encryption_key_id);
397 return smsg;
400 static gchar *
401 codecs_to_string(GSList *codecs)
403 GString *result = g_string_new(NULL);
405 for (; codecs; codecs = codecs->next) {
406 struct sdpcodec *c = codecs->data;
407 GSList *params = c->parameters;
409 g_string_append_printf(result,
410 "a=rtpmap:%d %s/%d\r\n",
411 c->id,
412 c->name,
413 c->clock_rate);
415 if (params) {
416 GString *param_str = g_string_new(NULL);
417 int written_params = 0;
419 g_string_append_printf(param_str, "a=fmtp:%d", c->id);
421 for (; params; params = params->next) {
422 struct sipnameval* par = params->data;
423 if (sipe_strequal(par->name, "farsight-send-profile")) {
424 // Lync AVMCU doesn't like this property.
425 continue;
428 g_string_append_printf(param_str, " %s=%s",
429 par->name, par->value);
430 ++written_params;
433 g_string_append(param_str, "\r\n");
435 if (written_params > 0) {
436 g_string_append(result, param_str->str);
439 g_string_free(param_str, TRUE);
443 return g_string_free(result, FALSE);
446 static gchar *
447 codec_ids_to_string(GSList *codecs)
449 GString *result = g_string_new(NULL);
451 for (; codecs; codecs = codecs->next) {
452 struct sdpcodec *c = codecs->data;
453 g_string_append_printf(result, " %d", c->id);
456 return g_string_free(result, FALSE);
459 static gchar *
460 base64_unpad(const gchar *str)
462 gchar *result = g_strdup(str);
463 gchar *ptr;
465 for (ptr = result + strlen(result); ptr != result; --ptr) {
466 if (*(ptr - 1) != '=') {
467 *ptr = '\0';
468 break;
472 return result;
475 static gchar *
476 candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
478 GString *result = g_string_new("");
479 GSList *i;
480 GSList *processed_tcp_candidates = NULL;
482 for (i = candidates; i; i = i->next) {
483 struct sdpcandidate *c = i->data;
484 const gchar *protocol;
485 const gchar *type;
486 gchar *related = NULL;
488 if (ice_version == SIPE_ICE_RFC_5245) {
490 switch (c->protocol) {
491 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
492 protocol = "TCP-ACT";
493 break;
494 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
495 protocol = "TCP-PASS";
496 break;
497 case SIPE_NETWORK_PROTOCOL_UDP:
498 protocol = "UDP";
499 break;
500 default:
501 /* error unknown/unsupported type */
502 protocol = "UNKNOWN";
503 break;
506 switch (c->type) {
507 case SIPE_CANDIDATE_TYPE_HOST:
508 type = "host";
509 break;
510 case SIPE_CANDIDATE_TYPE_RELAY:
511 type = "relay";
512 break;
513 case SIPE_CANDIDATE_TYPE_SRFLX:
514 type = "srflx";
515 break;
516 case SIPE_CANDIDATE_TYPE_PRFLX:
517 type = "prflx";
518 break;
519 default:
520 /* error unknown/unsupported type */
521 type = "unknown";
522 break;
525 switch (c->type) {
526 case SIPE_CANDIDATE_TYPE_RELAY:
527 case SIPE_CANDIDATE_TYPE_SRFLX:
528 case SIPE_CANDIDATE_TYPE_PRFLX:
529 related = g_strdup_printf("raddr %s rport %d",
530 c->base_ip,
531 c->base_port);
532 break;
533 default:
534 break;
537 g_string_append_printf(result,
538 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
539 c->foundation,
540 c->component,
541 protocol,
542 c->priority,
543 c->ip,
544 c->port,
545 type,
546 related ? related : "");
547 g_free(related);
549 } else if (ice_version == SIPE_ICE_DRAFT_6) {
550 gchar *username;
551 gchar *password;
553 switch (c->protocol) {
554 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
555 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: {
556 GSList *prev_cand = processed_tcp_candidates;
557 for (; prev_cand; prev_cand = prev_cand->next) {
558 struct sdpcandidate *c2 = (struct sdpcandidate *)prev_cand->data;
560 if (sipe_strequal(c->ip, c2->ip) &&
561 c->component == c2->component) {
562 break;
566 if (prev_cand) {
567 protocol = NULL;
568 } else {
569 protocol = "TCP";
570 processed_tcp_candidates =
571 g_slist_append(processed_tcp_candidates, c);
573 break;
575 case SIPE_NETWORK_PROTOCOL_UDP:
576 protocol = "UDP";
577 break;
578 default:
579 /* unknown/unsupported type, ignore */
580 protocol = NULL;
581 break;
584 if (!protocol) {
585 continue;
588 username = base64_unpad(c->username);
589 password = base64_unpad(c->password);
591 g_string_append_printf(result,
592 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
593 username,
594 c->component,
595 password,
596 protocol,
597 c->priority,
598 c->ip,
599 c->port);
601 g_free(username);
602 g_free(password);
606 g_slist_free(processed_tcp_candidates);
608 return g_string_free(result, FALSE);
611 static gchar *
612 remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
614 GString *result = g_string_new("");
616 if (candidates) {
617 if (ice_version == SIPE_ICE_RFC_5245) {
618 GSList *i;
619 g_string_append(result, "a=remote-candidates:");
621 for (i = candidates; i; i = i->next) {
622 struct sdpcandidate *c = i->data;
623 g_string_append_printf(result, "%u %s %u ",
624 c->component, c->ip, c->port);
627 g_string_append(result, "\r\n");
628 } else if (ice_version == SIPE_ICE_DRAFT_6) {
629 struct sdpcandidate *c = candidates->data;
630 g_string_append_printf(result, "a=remote-candidate:%s\r\n",
631 c->username);
635 return g_string_free(result, FALSE);
638 static gchar *
639 attributes_to_string(GSList *attributes)
641 GString *result = g_string_new("");
643 for (; attributes; attributes = attributes->next) {
644 struct sipnameval *a = attributes->data;
645 g_string_append_printf(result, "a=%s", a->name);
646 if (!sipe_strequal(a->value, ""))
647 g_string_append_printf(result, ":%s", a->value);
648 g_string_append(result, "\r\n");
651 return g_string_free(result, FALSE);
654 static gchar *
655 media_to_string(const struct sdpmsg *msg, const struct sdpmedia *media)
657 gchar *media_str;
659 gchar *transport_profile = NULL;
661 gchar *media_conninfo = NULL;
663 gchar *codecs_str = NULL;
664 gchar *codec_ids_str = codec_ids_to_string(media->codecs);
666 gchar *candidates_str = NULL;
667 gchar *remote_candidates_str = NULL;
669 gchar *attributes_str = NULL;
670 gchar *credentials = NULL;
672 gchar *crypto = NULL;
674 gboolean uses_tcp_transport = TRUE;
676 if (media->port != 0) {
677 if (!sipe_strequal(msg->ip, media->ip)) {
678 media_conninfo = g_strdup_printf("c=IN IP4 %s\r\n", media->ip);
681 codecs_str = codecs_to_string(media->codecs);
682 candidates_str = candidates_to_string(media->candidates, msg->ice_version);
683 remote_candidates_str = remote_candidates_to_string(media->remote_candidates,
684 msg->ice_version);
686 if (media->remote_candidates) {
687 struct sdpcandidate *c = media->remote_candidates->data;
688 uses_tcp_transport =
689 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE ||
690 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
691 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_SO;
692 } else {
693 GSList *candidates = media->candidates;
694 for (; candidates; candidates = candidates->next) {
695 struct sdpcandidate *c = candidates->data;
696 if (c->protocol == SIPE_NETWORK_PROTOCOL_UDP) {
697 uses_tcp_transport = FALSE;
698 break;
703 attributes_str = attributes_to_string(media->attributes);
705 if (msg->ice_version == SIPE_ICE_RFC_5245 && media->candidates) {
706 struct sdpcandidate *c = media->candidates->data;
708 credentials = g_strdup_printf("a=ice-ufrag:%s\r\n"
709 "a=ice-pwd:%s\r\n",
710 c->username,
711 c->password);
714 if (media->encryption_key) {
715 gchar *key_encoded = g_base64_encode(media->encryption_key, SIPE_SRTP_KEY_LEN);
716 crypto = g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n",
717 media->encryption_key_id, key_encoded);
718 g_free(key_encoded);
722 transport_profile = g_strdup_printf("%sRTP/%sAVP",
723 uses_tcp_transport ? "TCP/" : "",
724 media->encryption_active ? "S" : "");
726 media_str = g_strdup_printf("m=%s %d %s%s\r\n"
727 "%s"
728 "%s"
729 "%s"
730 "%s"
731 "%s"
732 "%s"
733 "%s",
734 media->name, media->port, transport_profile, codec_ids_str,
735 media_conninfo ? media_conninfo : "",
736 candidates_str ? candidates_str : "",
737 crypto ? crypto : "",
738 remote_candidates_str ? remote_candidates_str : "",
739 codecs_str ? codecs_str : "",
740 attributes_str ? attributes_str : "",
741 credentials ? credentials : "");
743 g_free(transport_profile);
744 g_free(media_conninfo);
745 g_free(codecs_str);
746 g_free(codec_ids_str);
747 g_free(candidates_str);
748 g_free(remote_candidates_str);
749 g_free(attributes_str);
750 g_free(credentials);
751 g_free(crypto);
753 return media_str;
756 gchar *
757 sdpmsg_to_string(const struct sdpmsg *msg)
759 GString *body = g_string_new(NULL);
760 GSList *i;
762 g_string_append_printf(
763 body,
764 "v=0\r\n"
765 "o=- 0 0 IN IP4 %s\r\n"
766 "s=session\r\n"
767 "c=IN IP4 %s\r\n"
768 "b=CT:99980\r\n"
769 "t=0 0\r\n",
770 msg->ip, msg->ip);
773 for (i = msg->media; i; i = i->next) {
774 gchar *media_str = media_to_string(msg, i->data);
775 g_string_append(body, media_str);
776 g_free(media_str);
779 return g_string_free(body, FALSE);
782 static struct sdpcandidate *
783 sdpcandidate_copy(struct sdpcandidate *candidate)
785 if (candidate) {
786 struct sdpcandidate *copy = g_new0(struct sdpcandidate, 1);
788 copy->foundation = g_strdup(candidate->foundation);
789 copy->component = candidate->component;
790 copy->type = candidate->type;
791 copy->protocol = candidate->protocol;
792 copy->priority = candidate->priority;
793 copy->ip = g_strdup(candidate->ip);
794 copy->port = candidate->port;
795 copy->base_ip = g_strdup(candidate->base_ip);
796 copy->base_port = candidate->base_port;
797 copy->username = g_strdup(candidate->username);
798 copy->password = g_strdup(candidate->password);
800 return copy;
801 } else
802 return NULL;
805 static void
806 sdpcandidate_free(struct sdpcandidate *candidate)
808 if (candidate) {
809 g_free(candidate->foundation);
810 g_free(candidate->ip);
811 g_free(candidate->base_ip);
812 g_free(candidate->username);
813 g_free(candidate->password);
814 g_free(candidate);
818 void
819 sdpcodec_free(struct sdpcodec *codec)
821 if (codec) {
822 g_free(codec->name);
823 sipe_utils_nameval_free(codec->parameters);
824 g_free(codec);
828 void
829 sdpmedia_free(struct sdpmedia *media)
831 if (media) {
832 g_free(media->name);
833 g_free(media->ip);
835 sipe_utils_nameval_free(media->attributes);
837 sipe_utils_slist_free_full(media->candidates,
838 (GDestroyNotify) sdpcandidate_free);
839 sipe_utils_slist_free_full(media->codecs,
840 (GDestroyNotify) sdpcodec_free);
841 sipe_utils_slist_free_full(media->remote_candidates,
842 (GDestroyNotify) sdpcandidate_free);
844 g_free(media->encryption_key);
846 g_free(media);
850 void
851 sdpmsg_free(struct sdpmsg *msg)
853 if (msg) {
854 g_free(msg->ip);
855 sipe_utils_slist_free_full(msg->media,
856 (GDestroyNotify) sdpmedia_free);
857 g_free(msg);
862 Local Variables:
863 mode: c
864 c-file-style: "bsd"
865 indent-tabs-mode: t
866 tab-width: 8
867 End: