chat: add sipe_core_chat_type()
[siplcs.git] / src / core / sdpmsg.c
blob70e1255e643111d7221fa0bce7cd067c3911db0c
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 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
239 gchar **tokens = g_strsplit_set(attr, " ", 0);
240 gboolean parsed_ok;
242 if (g_strv_length(tokens) < 7) {
243 g_strfreev(tokens);
244 return FALSE;
247 if (sipe_strequal(tokens[6], "typ")) {
248 parsed_ok = parse_append_candidate_rfc_5245(tokens,
249 candidates);
250 if (*candidates)
251 *ice_version = SIPE_ICE_RFC_5245;
252 } else {
253 parsed_ok = parse_append_candidate_draft_6(tokens,
254 candidates);
255 if (*candidates)
256 *ice_version = SIPE_ICE_DRAFT_6;
259 g_strfreev(tokens);
261 if (!parsed_ok) {
262 return FALSE;
266 if (!(*candidates))
267 *ice_version = SIPE_ICE_NO_ICE;
269 if (*ice_version == SIPE_ICE_RFC_5245) {
270 const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag");
271 const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd");
273 if (username && password) {
274 GSList *i;
275 for (i = *candidates; i; i = i->next) {
276 struct sdpcandidate *c = i->data;
277 c->username = g_strdup(username);
278 c->password = g_strdup(password);
283 return TRUE;
286 static GSList *
287 create_legacy_candidates(gchar *ip, guint16 port)
289 struct sdpcandidate *candidate;
290 GSList *candidates = NULL;
292 candidate = g_new0(struct sdpcandidate, 1);
293 candidate->foundation = g_strdup("1");
294 candidate->component = SIPE_COMPONENT_RTP;
295 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
296 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
297 candidate->ip = g_strdup(ip);
298 candidate->port = port;
300 candidates = g_slist_append(candidates, candidate);
302 candidate = g_new0(struct sdpcandidate, 1);
303 candidate->foundation = g_strdup("1");
304 candidate->component = SIPE_COMPONENT_RTCP;
305 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
306 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
307 candidate->ip = g_strdup(ip);
308 candidate->port = port + 1;
310 candidates = g_slist_append(candidates, candidate);
312 return candidates;
315 static gboolean
316 parse_codec_parameters(GSList *attrs, struct sdpcodec *codec)
318 const gchar* params;
319 int i = 0;
321 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", i++))) {
322 gchar **tokens;
323 gchar **param;
325 tokens = g_strsplit(params, " ", 0);
326 if (g_strv_length(tokens) < 1) {
327 g_strfreev(tokens);
328 return FALSE;
331 if (atoi(tokens[0]) != codec->id) {
332 g_strfreev(tokens);
333 continue;
336 for (param = tokens + 1; *param; ++param) {
337 gchar **nameval = g_strsplit(*param, "=", 2);
339 if (g_strv_length(nameval) != 2) {
340 g_strfreev(nameval);
341 continue;
344 codec->parameters =
345 sipe_utils_nameval_add(codec->parameters,
346 nameval[0],
347 nameval[1]);
349 g_strfreev(nameval);
352 g_strfreev(tokens);
355 return TRUE;
359 static gboolean
360 parse_codecs(GSList *attrs, SipeMediaType type, GSList **codecs)
362 int i = 0;
363 const gchar *attr;
365 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
366 struct sdpcodec *codec;
367 gchar **tokens;
369 tokens = g_strsplit_set(attr, " /", 4);
370 if (g_strv_length(tokens) < 3) {
371 g_strfreev(tokens);
372 return FALSE;
375 codec = g_new0(struct sdpcodec, 1);
376 codec->id = atoi(tokens[0]);
377 codec->name = g_strdup(tokens[1]);
378 codec->clock_rate = atoi(tokens[2]);
379 codec->type = type;
381 if (type == SIPE_MEDIA_AUDIO) {
382 codec->channels = tokens[3] ? atoi(tokens[3]) : 1;
385 g_strfreev(tokens);
387 if (!parse_codec_parameters(attrs, codec)) {
388 sdpcodec_free(codec);
389 return FALSE;
392 *codecs = g_slist_append(*codecs, codec);
395 return TRUE;
398 static void
399 parse_encryption_key(GSList *attrs, guchar **key, int *key_id)
401 int i = 0;
402 const gchar *attr;
404 while ((attr = sipe_utils_nameval_find_instance(attrs, "crypto", i++))) {
405 gchar **tokens = g_strsplit_set(attr, " :|", 6);
407 if (tokens[0] && tokens[1] && tokens[2] && tokens[3] && tokens[4] &&
408 sipe_strcase_equal(tokens[1], "AES_CM_128_HMAC_SHA1_80") &&
409 sipe_strequal(tokens[2], "inline") &&
410 !tokens[5]) {
411 gsize key_len;
412 *key = g_base64_decode(tokens[3], &key_len);
413 if (key_len != SIPE_SRTP_KEY_LEN) {
414 g_free(*key);
415 *key = NULL;
417 *key_id = atoi(tokens[0]);
420 g_strfreev(tokens);
422 if (*key) {
423 break;
428 struct sdpmsg *
429 sdpmsg_parse_msg(const gchar *msg)
431 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
432 GSList *i;
434 if (!parse_attributes(smsg, msg)) {
435 sdpmsg_free(smsg);
436 return NULL;
439 for (i = smsg->media; i; i = i->next) {
440 struct sdpmedia *media = i->data;
441 SipeMediaType type;
443 if (!parse_candidates(media->attributes, &smsg->ice_version,
444 &media->candidates)) {
445 sdpmsg_free(smsg);
446 return NULL;
449 if (!media->candidates && media->port != 0) {
450 // No a=candidate in SDP message, this seems to be MSOC 2005
451 media->candidates = create_legacy_candidates(smsg->ip, media->port);
454 if (sipe_strequal(media->name, "audio"))
455 type = SIPE_MEDIA_AUDIO;
456 else if (sipe_strequal(media->name, "video"))
457 type = SIPE_MEDIA_VIDEO;
458 else if (sipe_strequal(media->name, "data"))
459 type = SIPE_MEDIA_APPLICATION;
460 else if (sipe_strequal(media->name, "applicationsharing"))
461 type = SIPE_MEDIA_APPLICATION;
462 else {
463 // Unknown media type
464 sdpmsg_free(smsg);
465 return NULL;
468 if (!parse_codecs(media->attributes, type, &media->codecs)) {
469 sdpmsg_free(smsg);
470 return NULL;
473 parse_encryption_key(media->attributes, &media->encryption_key,
474 &media->encryption_key_id);
477 return smsg;
480 static gchar *
481 codecs_to_string(GSList *codecs)
483 GString *result = g_string_new(NULL);
485 for (; codecs; codecs = codecs->next) {
486 struct sdpcodec *c = codecs->data;
487 GSList *params = c->parameters;
489 g_string_append_printf(result,
490 "a=rtpmap:%d %s/%d\r\n",
491 c->id,
492 c->name,
493 c->clock_rate);
495 if (params) {
496 GString *param_str = g_string_new(NULL);
497 int written_params = 0;
499 g_string_append_printf(param_str, "a=fmtp:%d", c->id);
501 for (; params; params = params->next) {
502 struct sipnameval* par = params->data;
503 if (sipe_strequal(par->name, "farsight-send-profile")) {
504 // Lync AVMCU doesn't like this property.
505 continue;
508 g_string_append_printf(param_str, " %s=%s",
509 par->name, par->value);
510 ++written_params;
513 g_string_append(param_str, "\r\n");
515 if (written_params > 0) {
516 g_string_append(result, param_str->str);
519 g_string_free(param_str, TRUE);
523 return g_string_free(result, FALSE);
526 static gchar *
527 codec_ids_to_string(GSList *codecs)
529 GString *result = g_string_new(NULL);
531 for (; codecs; codecs = codecs->next) {
532 struct sdpcodec *c = codecs->data;
533 g_string_append_printf(result, " %d", c->id);
536 return g_string_free(result, FALSE);
539 static gchar *
540 base64_unpad(const gchar *str)
542 gchar *result = g_strdup(str);
543 gchar *ptr;
545 for (ptr = result + strlen(result); ptr != result; --ptr) {
546 if (*(ptr - 1) != '=') {
547 *ptr = '\0';
548 break;
552 return result;
555 static gchar *
556 candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
558 GString *result = g_string_new("");
559 GSList *i;
560 GSList *processed_tcp_candidates = NULL;
562 for (i = candidates; i; i = i->next) {
563 struct sdpcandidate *c = i->data;
564 const gchar *protocol;
565 const gchar *type;
566 gchar *related = NULL;
568 if (ice_version == SIPE_ICE_RFC_5245) {
570 switch (c->protocol) {
571 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
572 protocol = "TCP-ACT";
573 break;
574 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
575 protocol = "TCP-PASS";
576 break;
577 case SIPE_NETWORK_PROTOCOL_UDP:
578 protocol = "UDP";
579 break;
580 default:
581 /* error unknown/unsupported type */
582 protocol = "UNKNOWN";
583 break;
586 switch (c->type) {
587 case SIPE_CANDIDATE_TYPE_HOST:
588 type = "host";
589 break;
590 case SIPE_CANDIDATE_TYPE_RELAY:
591 type = "relay";
592 break;
593 case SIPE_CANDIDATE_TYPE_SRFLX:
594 type = "srflx";
595 break;
596 case SIPE_CANDIDATE_TYPE_PRFLX:
597 type = "prflx";
598 break;
599 default:
600 /* error unknown/unsupported type */
601 type = "unknown";
602 break;
605 switch (c->type) {
606 case SIPE_CANDIDATE_TYPE_RELAY:
607 case SIPE_CANDIDATE_TYPE_SRFLX:
608 case SIPE_CANDIDATE_TYPE_PRFLX:
609 related = g_strdup_printf("raddr %s rport %d",
610 c->base_ip,
611 c->base_port);
612 break;
613 default:
614 break;
617 g_string_append_printf(result,
618 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
619 c->foundation,
620 c->component,
621 protocol,
622 c->priority,
623 c->ip,
624 c->port,
625 type,
626 related ? related : "");
627 g_free(related);
629 } else if (ice_version == SIPE_ICE_DRAFT_6) {
630 gchar *username;
631 gchar *password;
633 switch (c->protocol) {
634 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
635 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE: {
636 GSList *prev_cand = processed_tcp_candidates;
637 for (; prev_cand; prev_cand = prev_cand->next) {
638 struct sdpcandidate *c2 = (struct sdpcandidate *)prev_cand->data;
640 if (sipe_strequal(c->ip, c2->ip) &&
641 c->component == c2->component) {
642 break;
646 if (prev_cand) {
647 protocol = NULL;
648 } else {
649 protocol = "TCP";
650 processed_tcp_candidates =
651 g_slist_append(processed_tcp_candidates, c);
653 break;
655 case SIPE_NETWORK_PROTOCOL_UDP:
656 protocol = "UDP";
657 break;
658 default:
659 /* unknown/unsupported type, ignore */
660 protocol = NULL;
661 break;
664 if (!protocol) {
665 continue;
668 username = base64_unpad(c->username);
669 password = base64_unpad(c->password);
671 g_string_append_printf(result,
672 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
673 username,
674 c->component,
675 password,
676 protocol,
677 c->priority,
678 c->ip,
679 c->port);
681 g_free(username);
682 g_free(password);
686 g_slist_free(processed_tcp_candidates);
688 return g_string_free(result, FALSE);
691 static gchar *
692 remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
694 GString *result = g_string_new("");
696 if (candidates) {
697 if (ice_version == SIPE_ICE_RFC_5245) {
698 GSList *i;
699 g_string_append(result, "a=remote-candidates:");
701 for (i = candidates; i; i = i->next) {
702 struct sdpcandidate *c = i->data;
703 g_string_append_printf(result, "%u %s %u ",
704 c->component, c->ip, c->port);
707 g_string_append(result, "\r\n");
708 } else if (ice_version == SIPE_ICE_DRAFT_6) {
709 struct sdpcandidate *c = candidates->data;
710 g_string_append_printf(result, "a=remote-candidate:%s\r\n",
711 c->username);
715 return g_string_free(result, FALSE);
718 static gchar *
719 attributes_to_string(GSList *attributes)
721 GString *result = g_string_new("");
723 for (; attributes; attributes = attributes->next) {
724 struct sipnameval *a = attributes->data;
725 g_string_append_printf(result, "a=%s", a->name);
726 if (!sipe_strequal(a->value, ""))
727 g_string_append_printf(result, ":%s", a->value);
728 g_string_append(result, "\r\n");
731 return g_string_free(result, FALSE);
734 static gchar *
735 media_to_string(const struct sdpmsg *msg, const struct sdpmedia *media)
737 gchar *media_str;
739 gchar *transport_profile = NULL;
741 gchar *media_conninfo = NULL;
743 gchar *codecs_str = NULL;
744 gchar *codec_ids_str = codec_ids_to_string(media->codecs);
746 gchar *candidates_str = NULL;
747 gchar *remote_candidates_str = NULL;
749 gchar *attributes_str = NULL;
750 gchar *credentials = NULL;
752 gchar *crypto = NULL;
754 gboolean uses_tcp_transport = TRUE;
756 if (media->port != 0) {
757 if (!sipe_strequal(msg->ip, media->ip)) {
758 media_conninfo = g_strdup_printf("c=IN IP4 %s\r\n", media->ip);
761 codecs_str = codecs_to_string(media->codecs);
762 candidates_str = candidates_to_string(media->candidates, msg->ice_version);
763 remote_candidates_str = remote_candidates_to_string(media->remote_candidates,
764 msg->ice_version);
766 if (media->remote_candidates) {
767 struct sdpcandidate *c = media->remote_candidates->data;
768 uses_tcp_transport =
769 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_ACTIVE ||
770 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_PASSIVE ||
771 c->protocol == SIPE_NETWORK_PROTOCOL_TCP_SO;
772 } else {
773 GSList *candidates = media->candidates;
774 for (; candidates; candidates = candidates->next) {
775 struct sdpcandidate *c = candidates->data;
776 if (c->protocol == SIPE_NETWORK_PROTOCOL_UDP) {
777 uses_tcp_transport = FALSE;
778 break;
783 attributes_str = attributes_to_string(media->attributes);
785 if (msg->ice_version == SIPE_ICE_RFC_5245 && media->candidates) {
786 struct sdpcandidate *c = media->candidates->data;
788 credentials = g_strdup_printf("a=ice-ufrag:%s\r\n"
789 "a=ice-pwd:%s\r\n",
790 c->username,
791 c->password);
794 if (media->encryption_key) {
795 gchar *key_encoded = g_base64_encode(media->encryption_key, SIPE_SRTP_KEY_LEN);
796 crypto = g_strdup_printf("a=crypto:%d AES_CM_128_HMAC_SHA1_80 inline:%s|2^31\r\n",
797 media->encryption_key_id, key_encoded);
798 g_free(key_encoded);
802 transport_profile = g_strdup_printf("%sRTP/%sAVP",
803 uses_tcp_transport ? "TCP/" : "",
804 media->encryption_active ? "S" : "");
806 media_str = g_strdup_printf("m=%s %d %s%s\r\n"
807 "%s"
808 "%s"
809 "%s"
810 "%s"
811 "%s"
812 "%s"
813 "%s",
814 media->name, media->port, transport_profile, codec_ids_str,
815 media_conninfo ? media_conninfo : "",
816 candidates_str ? candidates_str : "",
817 crypto ? crypto : "",
818 remote_candidates_str ? remote_candidates_str : "",
819 codecs_str ? codecs_str : "",
820 attributes_str ? attributes_str : "",
821 credentials ? credentials : "");
823 g_free(transport_profile);
824 g_free(media_conninfo);
825 g_free(codecs_str);
826 g_free(codec_ids_str);
827 g_free(candidates_str);
828 g_free(remote_candidates_str);
829 g_free(attributes_str);
830 g_free(credentials);
831 g_free(crypto);
833 return media_str;
836 gchar *
837 sdpmsg_to_string(const struct sdpmsg *msg)
839 GString *body = g_string_new(NULL);
840 GSList *i;
842 g_string_append_printf(
843 body,
844 "v=0\r\n"
845 "o=- 0 0 IN IP4 %s\r\n"
846 "s=session\r\n"
847 "c=IN IP4 %s\r\n"
848 "b=CT:99980\r\n"
849 "t=0 0\r\n",
850 msg->ip, msg->ip);
853 for (i = msg->media; i; i = i->next) {
854 gchar *media_str = media_to_string(msg, i->data);
855 g_string_append(body, media_str);
856 g_free(media_str);
859 return g_string_free(body, FALSE);
862 static struct sdpcandidate *
863 sdpcandidate_copy(struct sdpcandidate *candidate)
865 if (candidate) {
866 struct sdpcandidate *copy = g_new0(struct sdpcandidate, 1);
868 copy->foundation = g_strdup(candidate->foundation);
869 copy->component = candidate->component;
870 copy->type = candidate->type;
871 copy->protocol = candidate->protocol;
872 copy->priority = candidate->priority;
873 copy->ip = g_strdup(candidate->ip);
874 copy->port = candidate->port;
875 copy->base_ip = g_strdup(candidate->base_ip);
876 copy->base_port = candidate->base_port;
877 copy->username = g_strdup(candidate->username);
878 copy->password = g_strdup(candidate->password);
880 return copy;
881 } else
882 return NULL;
885 void
886 sdpcandidate_free(struct sdpcandidate *candidate)
888 if (candidate) {
889 g_free(candidate->foundation);
890 g_free(candidate->ip);
891 g_free(candidate->base_ip);
892 g_free(candidate->username);
893 g_free(candidate->password);
894 g_free(candidate);
898 void
899 sdpcodec_free(struct sdpcodec *codec)
901 if (codec) {
902 g_free(codec->name);
903 sipe_utils_nameval_free(codec->parameters);
904 g_free(codec);
908 void
909 sdpmedia_free(struct sdpmedia *media)
911 if (media) {
912 g_free(media->name);
913 g_free(media->ip);
915 sipe_utils_nameval_free(media->attributes);
917 sipe_utils_slist_free_full(media->candidates,
918 (GDestroyNotify) sdpcandidate_free);
919 sipe_utils_slist_free_full(media->codecs,
920 (GDestroyNotify) sdpcodec_free);
921 sipe_utils_slist_free_full(media->remote_candidates,
922 (GDestroyNotify) sdpcandidate_free);
924 g_free(media->encryption_key);
926 g_free(media);
930 void
931 sdpmsg_free(struct sdpmsg *msg)
933 if (msg) {
934 g_free(msg->ip);
935 sipe_utils_slist_free_full(msg->media,
936 (GDestroyNotify) sdpmedia_free);
937 g_free(msg);
942 Local Variables:
943 mode: c
944 c-file-style: "bsd"
945 indent-tabs-mode: t
946 tab-width: 8
947 End: