media: Fix uninitialized variable compilation errors
[siplcs.git] / src / core / sdpmsg.c
blob1812c52b31e36ef6f00238732132f154fef59bd9
1 /**
2 * @file sdpmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include <glib.h>
29 #include "sipe-backend.h"
30 #include "sdpmsg.h"
31 #include "sipe-utils.h"
33 static gboolean
34 append_attribute(struct sdpmedia *media, gchar *attr)
36 gchar **parts = g_strsplit(attr + 2, ":", 2);
38 if(!parts[0]) {
39 g_strfreev(parts);
40 return FALSE;
43 media->attributes = sipe_utils_nameval_add(media->attributes,
44 parts[0],
45 parts[1] ? parts[1] : "");
46 g_strfreev(parts);
47 return TRUE;
50 static gboolean
51 parse_attributes(struct sdpmsg *smsg, gchar *msg) {
52 gchar **lines = g_strsplit(msg, "\r\n", 0);
53 gchar **ptr = lines;
55 while (*ptr != NULL) {
56 if (g_str_has_prefix(*ptr, "o=")) {
57 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
58 smsg->ip = g_strdup(parts[5]);
59 g_strfreev(parts);
60 } else if (g_str_has_prefix(*ptr, "m=")) {
61 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
62 struct sdpmedia *media = g_new0(struct sdpmedia, 1);
64 smsg->media = g_slist_append(smsg->media, media);
66 media->name = g_strdup(parts[0]);
67 media->port = atoi(parts[1]);
69 g_strfreev(parts);
71 while (*(++ptr) && !g_str_has_prefix(*ptr, "m=")) {
73 if (g_str_has_prefix(*ptr, "a=")) {
74 if (!append_attribute(media, *ptr)) {
75 g_strfreev(lines);
76 return FALSE;
80 continue;
83 ++ptr;
86 g_strfreev(lines);
88 return TRUE;
91 static void sdpcandidate_free(struct sdpcandidate *candidate);
93 static SipeComponentType
94 parse_component(const gchar *str)
96 switch (atoi(str)) {
97 case 1: return SIPE_COMPONENT_RTP;
98 case 2: return SIPE_COMPONENT_RTCP;
99 default: return SIPE_COMPONENT_NONE;
103 static gchar *
104 base64_pad(const gchar* str)
106 size_t str_len = strlen(str);
107 int mod = str_len % 4;
109 if (mod > 0) {
110 gchar *result = NULL;
111 int pad = 4 - mod;
112 gchar *ptr = result = g_malloc(str_len + pad + 1);
114 memcpy(ptr, str, str_len);
115 ptr += str_len;
116 memset(ptr, '=', pad);
117 ptr += pad;
118 *ptr = '\0';
120 return result;
121 } else
122 return g_strdup(str);
125 static struct sdpcandidate *
126 parse_candidate_draft_6(gchar **tokens)
128 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
130 candidate->username = base64_pad(tokens[0]);
131 candidate->component = parse_component(tokens[1]);
132 candidate->password = base64_pad(tokens[2]);
134 if (sipe_strequal(tokens[3], "UDP"))
135 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
136 else {
137 // Ignore TCP candidates, at least for now...
138 // Also, if this is ICEv6 candidate list, candidates are dropped here
139 sdpcandidate_free(candidate);
140 return NULL;
143 candidate->priority = atoi(tokens[4] + 2);
144 candidate->ip = g_strdup(tokens[5]);
145 candidate->port = atoi(tokens[6]);
147 return candidate;
150 static struct sdpcandidate *
151 parse_candidate_rfc_5245(gchar **tokens)
153 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
155 candidate->foundation = g_strdup(tokens[0]);
156 candidate->component = parse_component(tokens[1]);
158 if (sipe_strequal(tokens[2], "UDP"))
159 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
160 else {
161 // Ignore TCP candidates, at least for now...
162 // Also, if this is ICEv6 candidate list, candidates are dropped here
163 sdpcandidate_free(candidate);
164 return NULL;
167 candidate->priority = atoi(tokens[3]);
168 candidate->ip = g_strdup(tokens[4]);
169 candidate->port = atoi(tokens[5]);
171 if (sipe_strequal(tokens[7], "host"))
172 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
173 else if (sipe_strequal(tokens[7], "relay"))
174 candidate->type = SIPE_CANDIDATE_TYPE_RELAY;
175 else if (sipe_strequal(tokens[7], "srflx"))
176 candidate->type = SIPE_CANDIDATE_TYPE_SRFLX;
177 else if (sipe_strequal(tokens[7], "prflx"))
178 candidate->type = SIPE_CANDIDATE_TYPE_PRFLX;
179 else {
180 sdpcandidate_free(candidate);
181 return NULL;
184 return candidate;
187 static GSList *
188 parse_candidates(GSList *attrs, SipeIceVersion *ice_version)
190 GSList *candidates = NULL;
191 const gchar *attr;
192 int i = 0;
194 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
195 gchar **tokens = g_strsplit_set(attr, " ", 0);
197 if (sipe_strequal(tokens[6], "typ")) {
198 struct sdpcandidate *c = parse_candidate_rfc_5245(tokens);
200 if (c) {
201 *ice_version = SIPE_ICE_RFC_5245;
202 candidates = g_slist_append(candidates, c);
204 } else {
205 struct sdpcandidate *c = parse_candidate_draft_6(tokens);
206 if (c) {
207 *ice_version = SIPE_ICE_DRAFT_6;
208 candidates = g_slist_append(candidates, c);
212 g_strfreev(tokens);
215 if (!candidates)
216 *ice_version = SIPE_ICE_NO_ICE;
218 if (*ice_version == SIPE_ICE_RFC_5245) {
219 const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag");
220 const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd");
222 if (username && password) {
223 GSList *i;
224 for (i = candidates; i; i = i->next) {
225 struct sdpcandidate *c = i->data;
226 c->username = g_strdup(username);
227 c->password = g_strdup(password);
232 return candidates;
235 static GSList *
236 create_legacy_candidates(gchar *ip, guint16 port)
238 struct sdpcandidate *candidate;
239 GSList *candidates = NULL;
241 candidate = g_new0(struct sdpcandidate, 1);
242 candidate->foundation = g_strdup("1");
243 candidate->component = SIPE_COMPONENT_RTP;
244 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
245 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
246 candidate->ip = g_strdup(ip);
247 candidate->port = port;
249 candidates = g_slist_append(candidates, candidate);
251 candidate = g_new0(struct sdpcandidate, 1);
252 candidate->foundation = g_strdup("1");
253 candidate->component = SIPE_COMPONENT_RTCP;
254 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
255 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
256 candidate->ip = g_strdup(ip);
257 candidate->port = port + 1;
259 candidates = g_slist_append(candidates, candidate);
261 return candidates;
264 static GSList *
265 parse_codecs(GSList *attrs, SipeMediaType type)
267 int i = 0;
268 const gchar *attr;
269 GSList *codecs = NULL;
271 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
272 struct sdpcodec *codec = g_new0(struct sdpcodec, 1);
273 gchar **tokens = g_strsplit_set(attr, " /", 3);
275 int j = 0;
276 const gchar* params;
278 codec->id = atoi(tokens[0]);
279 codec->name = g_strdup(tokens[1]);
280 codec->clock_rate = atoi(tokens[2]);
281 codec->type = type;
283 // TODO: more secure and effective implementation
284 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", j++))) {
285 gchar **tokens = g_strsplit_set(params, " ", 0);
286 gchar **next = tokens + 1;
288 if (atoi(tokens[0]) == codec->id) {
289 while (*next) {
290 gchar name[50];
291 gchar value[50];
293 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
294 codec->parameters = sipe_utils_nameval_add(codec->parameters, name, value);
296 ++next;
300 g_strfreev(tokens);
303 codecs = g_slist_append(codecs, codec);
304 g_strfreev(tokens);
307 return codecs;
310 struct sdpmsg *
311 sdpmsg_parse_msg(gchar *msg)
313 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
314 GSList *i;
316 if (!parse_attributes(smsg, msg)) {
317 sdpmsg_free(smsg);
318 return NULL;
321 for (i = smsg->media; i; i = i->next) {
322 struct sdpmedia *media = i->data;
323 SipeMediaType type;
325 media->candidates = parse_candidates(media->attributes,
326 &smsg->ice_version);
328 if (!media->candidates && media->port != 0) {
329 // No a=candidate in SDP message, this seems to be MSOC 2005
330 media->candidates = create_legacy_candidates(smsg->ip, media->port);
333 if (sipe_strequal(media->name, "audio"))
334 type = SIPE_MEDIA_AUDIO;
335 else if (sipe_strequal(media->name, "video"))
336 type = SIPE_MEDIA_VIDEO;
337 else {
338 // Unknown media type
339 sdpmsg_free(smsg);
340 return NULL;
343 media->codecs = parse_codecs(media->attributes, type);
346 return smsg;
349 static gchar *
350 codecs_to_string(GSList *codecs)
352 GString *result = g_string_new(NULL);
354 for (; codecs; codecs = codecs->next) {
355 struct sdpcodec *c = codecs->data;
356 GSList *params = c->parameters;
358 g_string_append_printf(result,
359 "a=rtpmap:%d %s/%d\r\n",
360 c->id,
361 c->name,
362 c->clock_rate);
364 if (params) {
365 g_string_append_printf(result, "a=fmtp:%d", c->id);
367 for (; params; params = params->next) {
368 struct sipnameval* par = params->data;
369 g_string_append_printf(result, " %s=%s",
370 par->name, par->value);
373 g_string_append(result, "\r\n");
377 return g_string_free(result, FALSE);
380 static gchar *
381 codec_ids_to_string(GSList *codecs)
383 GString *result = g_string_new(NULL);
385 for (; codecs; codecs = codecs->next) {
386 struct sdpcodec *c = codecs->data;
387 g_string_append_printf(result, " %d", c->id);
390 return g_string_free(result, FALSE);
393 static gchar *
394 base64_unpad(const gchar *str)
396 gchar *result = g_strdup(str);
397 gchar *ptr;
399 for (ptr = result + strlen(result); ptr != result; --ptr) {
400 if (*(ptr - 1) != '=') {
401 *ptr = '\0';
402 break;
406 return result;
409 static gint
410 candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
412 int cmp = sipe_strcompare(c1->foundation, c2->foundation);
413 if (cmp == 0) {
414 cmp = sipe_strcompare(c1->username, c2->username);
415 if (cmp == 0)
416 cmp = c1->component - c2->component;
419 return cmp;
422 static gchar *
423 candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
425 GString *result = g_string_new("");
426 GSList *i;
428 candidates = g_slist_copy(candidates);
429 candidates = g_slist_sort(candidates, (GCompareFunc)candidate_sort_cb);
431 for (i = candidates; i; i = i->next) {
432 struct sdpcandidate *c = i->data;
433 const gchar *protocol;
434 const gchar *type;
435 gchar *related = NULL;
437 switch (c->protocol) {
438 case SIPE_NETWORK_PROTOCOL_TCP:
439 protocol = "TCP";
440 break;
441 case SIPE_NETWORK_PROTOCOL_UDP:
442 protocol = "UDP";
443 break;
444 default:
445 /* error unknown/unsupported type */
446 protocol = "UNKOWN";
447 break;
450 if (ice_version == SIPE_ICE_RFC_5245) {
452 switch (c->type) {
453 case SIPE_CANDIDATE_TYPE_HOST:
454 type = "host";
455 break;
456 case SIPE_CANDIDATE_TYPE_RELAY:
457 type = "relay";
458 related = g_strdup_printf("raddr %s rport %d ",
459 c->ip,
460 c->port);
461 break;
462 case SIPE_CANDIDATE_TYPE_SRFLX:
463 type = "srflx";
464 related = g_strdup_printf("raddr %s rport %d",
465 c->base_ip,
466 c->base_port);
467 break;
468 case SIPE_CANDIDATE_TYPE_PRFLX:
469 type = "prflx";
470 break;
471 default:
472 /* error unknown/unsupported type */
473 type = "unknown";
474 break;
477 g_string_append_printf(result,
478 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
479 c->foundation,
480 c->component,
481 protocol,
482 c->priority,
483 c->ip,
484 c->port,
485 type,
486 related ? related : "");
487 g_free(related);
489 } else if (ice_version == SIPE_ICE_DRAFT_6) {
490 gchar *username = base64_unpad(c->username);
491 gchar *password = base64_unpad(c->password);
493 g_string_append_printf(result,
494 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
495 username,
496 c->component,
497 password,
498 protocol,
499 c->priority,
500 c->ip,
501 c->port);
503 g_free(username);
504 g_free(password);
508 g_slist_free(candidates);
510 return g_string_free(result, FALSE);
513 static gchar *
514 remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
516 GString *result = g_string_new("");
518 candidates = g_slist_copy(candidates);
519 candidates = g_slist_sort(candidates, (GCompareFunc)candidate_sort_cb);
521 if (candidates) {
522 if (ice_version == SIPE_ICE_RFC_5245) {
523 GSList *i;
524 g_string_append(result, "a=remote-candidates:");
526 for (i = candidates; i; i = i->next) {
527 struct sdpcandidate *c = i->data;
528 g_string_append_printf(result, "%u %s %u ",
529 c->component, c->ip, c->port);
532 g_string_append(result, "\r\n");
533 } else if (ice_version == SIPE_ICE_DRAFT_6) {
534 struct sdpcandidate *c = candidates->data;
535 g_string_append_printf(result, "a=remote-candidate:%s\r\n",
536 c->username);
540 g_slist_free(candidates);
542 return g_string_free(result, FALSE);
545 static gchar *
546 attributes_to_string(GSList *attributes)
548 GString *result = g_string_new("");
550 for (; attributes; attributes = attributes->next) {
551 struct sipnameval *a = attributes->data;
552 g_string_append_printf(result, "a=%s", a->name);
553 if (!sipe_strequal(a->value, ""))
554 g_string_append_printf(result, ":%s", a->value);
555 g_string_append(result, "\r\n");
558 return g_string_free(result, FALSE);
561 static gchar *
562 media_to_string(const struct sdpmedia *media, SipeIceVersion ice_version)
564 gchar *media_str;
566 gchar *codecs_str = NULL;
567 gchar *codec_ids_str = codec_ids_to_string(media->codecs);
569 gchar *candidates_str = NULL;
570 gchar *remote_candidates_str = NULL;
572 gchar *attributes_str = NULL;
573 gchar *credentials = NULL;
575 if (media->port != 0) {
576 codecs_str = codecs_to_string(media->codecs);
577 candidates_str = candidates_to_string(media->candidates, ice_version);
578 remote_candidates_str = remote_candidates_to_string(media->remote_candidates,
579 ice_version);
581 attributes_str = attributes_to_string(media->attributes);
582 credentials = NULL;
585 if (ice_version == SIPE_ICE_RFC_5245 && media->candidates) {
586 struct sdpcandidate *c = media->candidates->data;
588 credentials = g_strdup_printf("a=ice-ufrag:%s\r\n"
589 "a=ice-pwd:%s\r\n",
590 c->username,
591 c->password);
595 media_str = g_strdup_printf("m=%s %d RTP/AVP%s\r\n"
596 "%s"
597 "%s"
598 "%s"
599 "%s"
600 "%s",
601 media->name, media->port, codec_ids_str,
602 candidates_str ? candidates_str : "",
603 remote_candidates_str ? remote_candidates_str : "",
604 codecs_str ? codecs_str : "",
605 attributes_str ? attributes_str : "",
606 credentials ? credentials : "");
608 g_free(codecs_str);
609 g_free(codec_ids_str);
610 g_free(candidates_str);
611 g_free(remote_candidates_str);
612 g_free(attributes_str);
613 g_free(credentials);
615 return media_str;
618 gchar *
619 sdpmsg_to_string(const struct sdpmsg *msg)
621 GString *body = g_string_new(NULL);
622 GSList *i;
624 g_string_append_printf(
625 body,
626 "v=0\r\n"
627 "o=- 0 0 IN IP4 %s\r\n"
628 "s=session\r\n"
629 "c=IN IP4 %s\r\n"
630 "b=CT:99980\r\n"
631 "t=0 0\r\n",
632 msg->ip, msg->ip);
635 for (i = msg->media; i; i = i->next) {
636 gchar *media_str = media_to_string(i->data, msg->ice_version);
637 g_string_append(body, media_str);
638 g_free(media_str);
641 return g_string_free(body, FALSE);
644 static void
645 sdpcandidate_free(struct sdpcandidate *candidate)
647 if (candidate) {
648 g_free(candidate->foundation);
649 g_free(candidate->ip);
650 g_free(candidate->base_ip);
651 g_free(candidate->username);
652 g_free(candidate->password);
653 g_free(candidate);
657 static void
658 sdpcodec_free(struct sdpcodec *codec)
660 if (codec) {
661 g_free(codec->name);
662 sipe_utils_nameval_free(codec->parameters);
663 g_free(codec);
667 void
668 sdpmedia_free(struct sdpmedia *media)
670 if (media) {
671 GSList *item;
673 g_free(media->name);
674 g_free(media->ip);
676 sipe_utils_nameval_free(media->attributes);
678 for (item = media->candidates; item; item = item->next)
679 sdpcandidate_free(item->data);
680 g_slist_free(media->candidates);
682 for (item = media->codecs; item; item = item->next)
683 sdpcodec_free(item->data);
684 g_slist_free(media->codecs);
686 for (item = media->remote_candidates; item; item = item->next)
687 sdpcandidate_free(item->data);
688 g_slist_free(media->remote_candidates);
690 g_free(media);
694 void
695 sdpmsg_free(struct sdpmsg *msg)
697 if (msg) {
698 GSList *item;
700 g_free(msg->ip);
701 for (item = msg->media; item; item = item->next)
702 sdpmedia_free(item->data);
703 g_slist_free(msg->media);
704 g_free(msg);
709 Local Variables:
710 mode: c
711 c-file-style: "bsd"
712 indent-tabs-mode: t
713 tab-width: 8
714 End: