Fix for bug #2834758: First NTLM signature check after startup fails
[siplcs.git] / src / core / sdpmsg.c
blob391a5b4b355769f819a8542380451d56dfc048dd
1 /**
2 * @file sdpmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2010 Jakub Adam <jakub.adam@tieto.com>
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 = parts[0];
67 parts[0] = NULL;
69 media->port = atoi(parts[1]);
71 g_strfreev(parts);
73 while (*(++ptr) && !g_str_has_prefix(*ptr, "m=")) {
75 if (g_str_has_prefix(*ptr, "a=")) {
76 if (!append_attribute(media, *ptr)) {
77 g_strfreev(lines);
78 return FALSE;
82 continue;
85 ++ptr;
88 g_strfreev(lines);
90 return TRUE;
93 static void sdpcandidate_free(struct sdpcandidate *candidate);
95 SipeComponentType parse_component(const gchar *str)
97 switch (atoi(str)) {
98 case 1: return SIPE_COMPONENT_RTP;
99 case 2: return SIPE_COMPONENT_RTCP;
100 default: return SIPE_COMPONENT_NONE;
104 static gchar *
105 base64_pad(const gchar* str)
107 size_t str_len = strlen(str);
108 int mod = str_len % 4;
110 if (mod > 0) {
111 gchar *result = NULL;
112 int pad = 4 - mod;
113 gchar *ptr = result = g_malloc(str_len + pad + 1);
115 memcpy(ptr, str, str_len);
116 ptr += str_len;
117 memset(ptr, '=', pad);
118 ptr += pad;
119 *ptr = '\0';
121 return result;
122 } else
123 return g_strdup(str);
126 static struct sdpcandidate *
127 parse_candidate_draft_6(gchar **tokens)
129 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
131 candidate->username = base64_pad(tokens[0]);
132 candidate->component = parse_component(tokens[1]);
133 candidate->password = base64_pad(tokens[2]);
135 if (sipe_strequal(tokens[3], "UDP"))
136 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
137 else {
138 // Ignore TCP candidates, at least for now...
139 // Also, if this is ICEv6 candidate list, candidates are dropped here
140 sdpcandidate_free(candidate);
141 return NULL;
144 candidate->priority = atoi(tokens[4] + 2);
145 candidate->ip = g_strdup(tokens[5]);
146 candidate->port = atoi(tokens[6]);
148 return candidate;
151 static struct sdpcandidate *
152 parse_candidate_rfc_5245(gchar **tokens)
154 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
156 candidate->foundation = g_strdup(tokens[0]);
157 candidate->component = parse_component(tokens[1]);
159 if (sipe_strequal(tokens[2], "UDP"))
160 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
161 else {
162 // Ignore TCP candidates, at least for now...
163 // Also, if this is ICEv6 candidate list, candidates are dropped here
164 sdpcandidate_free(candidate);
165 return NULL;
168 candidate->priority = atoi(tokens[3]);
169 candidate->ip = g_strdup(tokens[4]);
170 candidate->port = atoi(tokens[5]);
172 if (sipe_strequal(tokens[7], "host"))
173 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
174 else if (sipe_strequal(tokens[7], "relay"))
175 candidate->type = SIPE_CANDIDATE_TYPE_RELAY;
176 else if (sipe_strequal(tokens[7], "srflx"))
177 candidate->type = SIPE_CANDIDATE_TYPE_SRFLX;
178 else if (sipe_strequal(tokens[7], "prflx"))
179 candidate->type = SIPE_CANDIDATE_TYPE_PRFLX;
180 else {
181 sdpcandidate_free(candidate);
182 return NULL;
185 return candidate;
188 static GSList *
189 parse_candidates(GSList *attrs, SipeIceVersion *ice_version)
191 GSList *candidates = NULL;
192 const gchar *attr;
193 int i = 0;
195 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
196 gchar **tokens = g_strsplit_set(attr, " ", 0);
198 if (sipe_strequal(tokens[6], "typ")) {
199 struct sdpcandidate *c = parse_candidate_rfc_5245(tokens);
201 if (c) {
202 *ice_version = SIPE_ICE_RFC_5245;
203 candidates = g_slist_append(candidates, c);
205 } else {
206 struct sdpcandidate *c = parse_candidate_draft_6(tokens);
207 if (c) {
208 *ice_version = SIPE_ICE_DRAFT_6;
209 candidates = g_slist_append(candidates, c);
213 g_strfreev(tokens);
216 if (!candidates)
217 *ice_version = SIPE_ICE_NO_ICE;
219 if (*ice_version == SIPE_ICE_RFC_5245) {
220 const gchar *username = sipe_utils_nameval_find(attrs, "ice-ufrag");
221 const gchar *password = sipe_utils_nameval_find(attrs, "ice-pwd");
223 if (username && password) {
224 GSList *i;
225 for (i = candidates; i; i = i->next) {
226 struct sdpcandidate *c = i->data;
227 c->username = g_strdup(username);
228 c->password = g_strdup(password);
233 return candidates;
236 static GSList *
237 create_legacy_candidates(gchar *ip, guint16 port)
239 struct sdpcandidate *candidate;
240 GSList *candidates = NULL;
242 candidate = g_new0(struct sdpcandidate, 1);
243 candidate->foundation = g_strdup("1");
244 candidate->component = SIPE_COMPONENT_RTP;
245 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
246 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
247 candidate->ip = g_strdup(ip);
248 candidate->port = port;
250 candidates = g_slist_append(candidates, candidate);
252 candidate = g_new0(struct sdpcandidate, 1);
253 candidate->foundation = g_strdup("1");
254 candidate->component = SIPE_COMPONENT_RTCP;
255 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
256 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
257 candidate->ip = g_strdup(ip);
258 candidate->port = port + 1;
260 candidates = g_slist_append(candidates, candidate);
262 return candidates;
265 static GSList *
266 parse_codecs(GSList *attrs, SipeMediaType type)
268 int i = 0;
269 const gchar *attr;
270 GSList *codecs = NULL;
272 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
273 struct sdpcodec *codec = g_new0(struct sdpcodec, 1);
274 gchar **tokens = g_strsplit_set(attr, " /", 3);
276 int j = 0;
277 const gchar* params;
279 codec->id = atoi(tokens[0]);
280 codec->name = g_strdup(tokens[1]);
281 codec->clock_rate = atoi(tokens[2]);
282 codec->type = type;
284 // TODO: more secure and effective implementation
285 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", j++))) {
286 gchar **tokens = g_strsplit_set(params, " ", 0);
287 gchar **next = tokens + 1;
289 if (atoi(tokens[0]) == codec->id) {
290 while (*next) {
291 gchar name[50];
292 gchar value[50];
294 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
295 codec->parameters = sipe_utils_nameval_add(codec->parameters, name, value);
297 ++next;
301 g_strfreev(tokens);
304 codecs = g_slist_append(codecs, codec);
305 g_strfreev(tokens);
308 return codecs;
311 struct sdpmsg *
312 sdpmsg_parse_msg(gchar *msg)
314 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
315 GSList *i;
317 if (!parse_attributes(smsg, msg)) {
318 sdpmsg_free(smsg);
319 return NULL;
322 for (i = smsg->media; i; i = i->next) {
323 struct sdpmedia *media = i->data;
324 SipeMediaType type;
326 media->candidates = parse_candidates(media->attributes,
327 &smsg->ice_version);
329 if (!media->candidates && media->port != 0) {
330 // No a=candidate in SDP message, this seems to be MSOC 2005
331 media->candidates = create_legacy_candidates(smsg->ip, media->port);
334 if (sipe_strequal(media->name, "audio"))
335 type = SIPE_MEDIA_AUDIO;
336 else if (sipe_strequal(media->name, "video"))
337 type = SIPE_MEDIA_VIDEO;
338 else {
339 // TODO unknown media type
342 media->codecs = parse_codecs(media->attributes, type);
345 return smsg;
348 static gchar *
349 codecs_to_string(GSList *codecs)
351 GString *result = g_string_new(NULL);
353 for (; codecs; codecs = codecs->next) {
354 struct sdpcodec *c = codecs->data;
355 GSList *params = c->parameters;
357 g_string_append_printf(result,
358 "a=rtpmap:%d %s/%d\r\n",
359 c->id,
360 c->name,
361 c->clock_rate);
363 if (params) {
364 g_string_append_printf(result, "a=fmtp:%d", c->id);
366 for (; params; params = params->next) {
367 struct sipnameval* par = params->data;
368 g_string_append_printf(result, " %s=%s",
369 par->name, par->value);
372 g_string_append(result, "\r\n");
376 return g_string_free(result, FALSE);
379 static gchar *
380 codec_ids_to_string(GSList *codecs)
382 GString *result = g_string_new(NULL);
384 for (; codecs; codecs = codecs->next) {
385 struct sdpcodec *c = codecs->data;
386 g_string_append_printf(result, " %d", c->id);
389 return g_string_free(result, FALSE);
392 static gchar *
393 base64_unpad(const gchar *str)
395 gchar *result = g_strdup(str);
396 gchar *ptr;
398 for (ptr = result + strlen(result); ptr != result; --ptr) {
399 if (*(ptr - 1) != '=') {
400 *ptr = '\0';
401 break;
405 return result;
408 static gint
409 candidate_sort_cb(struct sdpcandidate *c1, struct sdpcandidate *c2)
411 int cmp = sipe_strcompare(c1->foundation, c2->foundation);
412 if (cmp == 0) {
413 cmp = sipe_strcompare(c1->username, c2->username);
414 if (cmp == 0)
415 cmp = c1->component - c2->component;
418 return cmp;
421 static gchar *
422 candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
424 GString *result = g_string_new("");
426 candidates = g_slist_copy(candidates);
427 candidates = g_slist_sort(candidates, (GCompareFunc)candidate_sort_cb);
429 for (; candidates; candidates = candidates->next) {
430 struct sdpcandidate *c = candidates->data;
431 const gchar *protocol;
432 const gchar *type;
433 gchar *related = NULL;
435 switch (c->protocol) {
436 case SIPE_NETWORK_PROTOCOL_TCP:
437 protocol = "TCP";
438 break;
439 case SIPE_NETWORK_PROTOCOL_UDP:
440 protocol = "UDP";
441 break;
444 if (ice_version == SIPE_ICE_RFC_5245) {
446 switch (c->type) {
447 case SIPE_CANDIDATE_TYPE_HOST:
448 type = "host";
449 break;
450 case SIPE_CANDIDATE_TYPE_RELAY:
451 type = "relay";
452 related = g_strdup_printf("raddr %s rport %d ",
453 c->ip,
454 c->port);
455 break;
456 case SIPE_CANDIDATE_TYPE_SRFLX:
457 type = "srflx";
458 related = g_strdup_printf("raddr %s rport %d",
459 c->base_ip,
460 c->base_port);
461 break;
462 case SIPE_CANDIDATE_TYPE_PRFLX:
463 type = "prflx";
464 break;
465 default:
466 // TODO: error unknown/unsupported type
467 break;
470 g_string_append_printf(result,
471 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
472 c->foundation,
473 c->component,
474 protocol,
475 c->priority,
476 c->ip,
477 c->port,
478 type,
479 related ? related : "");
480 g_free(related);
482 } else if (ice_version == SIPE_ICE_DRAFT_6) {
483 gchar *username = base64_unpad(c->username);
484 gchar *password = base64_unpad(c->password);
486 g_string_append_printf(result,
487 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
488 username,
489 c->component,
490 password,
491 protocol,
492 c->priority,
493 c->ip,
494 c->port);
496 g_free(username);
497 g_free(password);
501 g_slist_free(candidates);
503 return g_string_free(result, FALSE);
506 static gchar *
507 remote_candidates_to_string(GSList *candidates, SipeIceVersion ice_version)
509 GString *result = g_string_new("");
511 candidates = g_slist_copy(candidates);
512 candidates = g_slist_sort(candidates, (GCompareFunc)candidate_sort_cb);
514 if (candidates) {
515 if (ice_version == SIPE_ICE_RFC_5245) {
516 GSList *i;
517 g_string_append(result, "a=remote-candidates:");
519 for (i = candidates; i; i = i->next) {
520 struct sdpcandidate *c = i->data;
521 g_string_append_printf(result, "%u %s %u ",
522 c->component, c->ip, c->port);
525 g_string_append(result, "\r\n");
526 } else if (ice_version == SIPE_ICE_DRAFT_6) {
527 struct sdpcandidate *c = candidates->data;
528 g_string_append_printf(result, "a=remote-candidate:%s\r\n",
529 c->username);
533 g_slist_free(candidates);
535 return g_string_free(result, FALSE);
538 static gchar *
539 attributes_to_string(GSList *attributes)
541 GString *result = g_string_new("");
543 for (; attributes; attributes = attributes->next) {
544 struct sipnameval *a = attributes->data;
545 g_string_append_printf(result, "a=%s", a->name);
546 if (!sipe_strequal(a->value, ""))
547 g_string_append_printf(result, ":%s", a->value);
548 g_string_append(result, "\r\n");
551 return g_string_free(result, FALSE);
554 gchar *
555 media_to_string(const struct sdpmedia *media, SipeIceVersion ice_version)
557 gchar *media_str;
559 gchar *codecs_str = codecs_to_string(media->codecs);
560 gchar *codec_ids_str = codec_ids_to_string(media->codecs);
562 gchar *candidates_str = candidates_to_string(media->candidates, ice_version);
563 gchar *remote_candidates_str = remote_candidates_to_string(media->remote_candidates,
564 ice_version);
566 gchar *attributes_str = attributes_to_string(media->attributes);
567 gchar *credentials = NULL;
569 if (ice_version == SIPE_ICE_RFC_5245 && media->candidates) {
570 struct sdpcandidate *c = media->candidates->data;
572 credentials = g_strdup_printf("a=ice-ufrag:%s\r\n"
573 "a=ice-pwd:%s\r\n",
574 c->username,
575 c->password);
578 media_str = g_strdup_printf("m=%s %d RTP/AVP%s\r\n"
579 "%s"
580 "%s"
581 "%s"
582 "%s"
583 "%s",
584 media->name, media->port, codec_ids_str,
585 candidates_str,
586 remote_candidates_str,
587 codecs_str,
588 attributes_str,
589 credentials ? credentials : "");
591 g_free(codecs_str);
592 g_free(codec_ids_str);
593 g_free(candidates_str);
594 g_free(remote_candidates_str);
595 g_free(attributes_str);
596 g_free(credentials);
598 return media_str;
601 gchar *
602 sdpmsg_to_string(const struct sdpmsg *msg)
604 GString *body = g_string_new(NULL);
605 GSList *i;
607 g_string_append_printf(
608 body,
609 "v=0\r\n"
610 "o=- 0 0 IN IP4 %s\r\n"
611 "s=session\r\n"
612 "c=IN IP4 %s\r\n"
613 "b=CT:99980\r\n"
614 "t=0 0\r\n",
615 msg->ip, msg->ip);
618 for (i = msg->media; i; i = i->next) {
619 gchar *media_str = media_to_string(i->data, msg->ice_version);
620 g_string_append(body, media_str);
621 g_free(media_str);
624 return g_string_free(body, FALSE);
627 static void
628 sdpcandidate_free(struct sdpcandidate *candidate)
630 if (candidate) {
631 g_free(candidate->foundation);
632 g_free(candidate->ip);
633 g_free(candidate->base_ip);
634 g_free(candidate->username);
635 g_free(candidate->password);
636 g_free(candidate);
640 static void
641 sdpcodec_free(struct sdpcodec *codec)
643 if (codec) {
644 g_free(codec->name);
645 sipe_utils_nameval_free(codec->parameters);
646 g_free(codec);
650 static void
651 sdpmedia_free(struct sdpmedia *media)
653 if (media) {
654 GSList *item;
656 g_free(media->name);
657 g_free(media->ip);
659 sipe_utils_nameval_free(media->attributes);
661 for (item = media->candidates; item; item = item->next)
662 sdpcandidate_free(item->data);
663 g_slist_free(media->candidates);
665 for (item = media->codecs; item; item = item->next)
666 sdpcodec_free(item->data);
667 g_slist_free(media->codecs);
669 for (item = media->remote_candidates; item; item = item->next)
670 sdpcandidate_free(item->data);
671 g_slist_free(media->remote_candidates);
673 g_free(media);
677 void
678 sdpmsg_free(struct sdpmsg *msg)
680 if (msg) {
681 GSList *item;
683 g_free(msg->ip);
684 for (item = msg->media; item; item = item->next)
685 sdpmedia_free(item->data);
686 g_slist_free(msg->media);
687 g_free(msg);
692 Local Variables:
693 mode: c
694 c-file-style: "bsd"
695 indent-tabs-mode: t
696 tab-width: 8
697 End: