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
29 #include "sipe-backend.h"
31 #include "sipe-utils.h"
34 append_attribute(struct sdpmedia
*media
, gchar
*attr
)
36 gchar
**parts
= g_strsplit(attr
+ 2, ":", 2);
43 media
->attributes
= sipe_utils_nameval_add(media
->attributes
,
45 parts
[1] ? parts
[1] : "");
51 parse_attributes(struct sdpmsg
*smsg
, gchar
*msg
) {
52 gchar
**lines
= g_strsplit(msg
, "\r\n", 0);
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]);
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]);
71 while (*(++ptr
) && !g_str_has_prefix(*ptr
, "m=")) {
73 if (g_str_has_prefix(*ptr
, "a=")) {
74 if (!append_attribute(media
, *ptr
)) {
91 static struct sdpcandidate
* sdpcandidate_copy(struct sdpcandidate
*candidate
);
92 static void sdpcandidate_free(struct sdpcandidate
*candidate
);
94 static SipeComponentType
95 parse_component(const gchar
*str
)
98 case 1: return SIPE_COMPONENT_RTP
;
99 case 2: return SIPE_COMPONENT_RTCP
;
100 default: return SIPE_COMPONENT_NONE
;
105 base64_pad(const gchar
* str
)
107 size_t str_len
= strlen(str
);
108 int mod
= str_len
% 4;
111 gchar
*result
= NULL
;
113 gchar
*ptr
= result
= g_malloc(str_len
+ pad
+ 1);
115 memcpy(ptr
, str
, str_len
);
117 memset(ptr
, '=', pad
);
123 return g_strdup(str
);
127 parse_append_candidate_draft_6(gchar
**tokens
, GSList
*candidates
)
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 if (sipe_strequal(tokens
[3], "TCP"))
138 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
140 sdpcandidate_free(candidate
);
144 candidate
->priority
= atoi(tokens
[4] + 2);
145 candidate
->ip
= g_strdup(tokens
[5]);
146 candidate
->port
= atoi(tokens
[6]);
148 candidates
= g_slist_append(candidates
, candidate
);
150 // draft 6 candidates are both active and passive
151 if (candidate
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
) {
152 candidate
= sdpcandidate_copy(candidate
);
153 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
154 candidates
= g_slist_append(candidates
, candidate
);
161 parse_append_candidate_rfc_5245(gchar
**tokens
, GSList
*candidates
)
163 struct sdpcandidate
*candidate
= g_new0(struct sdpcandidate
, 1);
165 candidate
->foundation
= g_strdup(tokens
[0]);
166 candidate
->component
= parse_component(tokens
[1]);
168 if (sipe_strequal(tokens
[2], "UDP"))
169 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
170 else if (sipe_strequal(tokens
[2], "TCP-ACT"))
171 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
172 else if (sipe_strequal(tokens
[2], "TCP-PASS"))
173 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
175 sdpcandidate_free(candidate
);
179 candidate
->priority
= atoi(tokens
[3]);
180 candidate
->ip
= g_strdup(tokens
[4]);
181 candidate
->port
= atoi(tokens
[5]);
183 if (sipe_strequal(tokens
[7], "host"))
184 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
185 else if (sipe_strequal(tokens
[7], "relay"))
186 candidate
->type
= SIPE_CANDIDATE_TYPE_RELAY
;
187 else if (sipe_strequal(tokens
[7], "srflx"))
188 candidate
->type
= SIPE_CANDIDATE_TYPE_SRFLX
;
189 else if (sipe_strequal(tokens
[7], "prflx"))
190 candidate
->type
= SIPE_CANDIDATE_TYPE_PRFLX
;
192 sdpcandidate_free(candidate
);
196 return g_slist_append(candidates
, candidate
);
200 parse_candidates(GSList
*attrs
, SipeIceVersion
*ice_version
)
202 GSList
*candidates
= NULL
;
206 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "candidate", i
++))) {
207 gchar
**tokens
= g_strsplit_set(attr
, " ", 0);
209 if (sipe_strequal(tokens
[6], "typ")) {
210 candidates
= parse_append_candidate_rfc_5245(tokens
, candidates
);
212 *ice_version
= SIPE_ICE_RFC_5245
;
214 candidates
= parse_append_candidate_draft_6(tokens
, candidates
);
216 *ice_version
= SIPE_ICE_DRAFT_6
;
223 *ice_version
= SIPE_ICE_NO_ICE
;
225 if (*ice_version
== SIPE_ICE_RFC_5245
) {
226 const gchar
*username
= sipe_utils_nameval_find(attrs
, "ice-ufrag");
227 const gchar
*password
= sipe_utils_nameval_find(attrs
, "ice-pwd");
229 if (username
&& password
) {
231 for (i
= candidates
; i
; i
= i
->next
) {
232 struct sdpcandidate
*c
= i
->data
;
233 c
->username
= g_strdup(username
);
234 c
->password
= g_strdup(password
);
243 create_legacy_candidates(gchar
*ip
, guint16 port
)
245 struct sdpcandidate
*candidate
;
246 GSList
*candidates
= NULL
;
248 candidate
= g_new0(struct sdpcandidate
, 1);
249 candidate
->foundation
= g_strdup("1");
250 candidate
->component
= SIPE_COMPONENT_RTP
;
251 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
252 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
253 candidate
->ip
= g_strdup(ip
);
254 candidate
->port
= port
;
256 candidates
= g_slist_append(candidates
, candidate
);
258 candidate
= g_new0(struct sdpcandidate
, 1);
259 candidate
->foundation
= g_strdup("1");
260 candidate
->component
= SIPE_COMPONENT_RTCP
;
261 candidate
->type
= SIPE_CANDIDATE_TYPE_HOST
;
262 candidate
->protocol
= SIPE_NETWORK_PROTOCOL_UDP
;
263 candidate
->ip
= g_strdup(ip
);
264 candidate
->port
= port
+ 1;
266 candidates
= g_slist_append(candidates
, candidate
);
272 parse_codecs(GSList
*attrs
, SipeMediaType type
)
276 GSList
*codecs
= NULL
;
278 while ((attr
= sipe_utils_nameval_find_instance(attrs
, "rtpmap", i
++))) {
279 struct sdpcodec
*codec
= g_new0(struct sdpcodec
, 1);
280 gchar
**tokens
= g_strsplit_set(attr
, " /", 3);
285 codec
->id
= atoi(tokens
[0]);
286 codec
->name
= g_strdup(tokens
[1]);
287 codec
->clock_rate
= atoi(tokens
[2]);
290 // TODO: more secure and effective implementation
291 while((params
= sipe_utils_nameval_find_instance(attrs
, "fmtp", j
++))) {
292 gchar
**tokens
= g_strsplit_set(params
, " ", 0);
293 gchar
**next
= tokens
+ 1;
295 if (atoi(tokens
[0]) == codec
->id
) {
300 if (sscanf(*next
, "%[a-zA-Z0-9]=%s", name
, value
) == 2)
301 codec
->parameters
= sipe_utils_nameval_add(codec
->parameters
, name
, value
);
310 codecs
= g_slist_append(codecs
, codec
);
318 sdpmsg_parse_msg(gchar
*msg
)
320 struct sdpmsg
*smsg
= g_new0(struct sdpmsg
, 1);
323 if (!parse_attributes(smsg
, msg
)) {
328 for (i
= smsg
->media
; i
; i
= i
->next
) {
329 struct sdpmedia
*media
= i
->data
;
332 media
->candidates
= parse_candidates(media
->attributes
,
335 if (!media
->candidates
&& media
->port
!= 0) {
336 // No a=candidate in SDP message, this seems to be MSOC 2005
337 media
->candidates
= create_legacy_candidates(smsg
->ip
, media
->port
);
340 if (sipe_strequal(media
->name
, "audio"))
341 type
= SIPE_MEDIA_AUDIO
;
342 else if (sipe_strequal(media
->name
, "video"))
343 type
= SIPE_MEDIA_VIDEO
;
345 // Unknown media type
350 media
->codecs
= parse_codecs(media
->attributes
, type
);
357 codecs_to_string(GSList
*codecs
)
359 GString
*result
= g_string_new(NULL
);
361 for (; codecs
; codecs
= codecs
->next
) {
362 struct sdpcodec
*c
= codecs
->data
;
363 GSList
*params
= c
->parameters
;
365 g_string_append_printf(result
,
366 "a=rtpmap:%d %s/%d\r\n",
372 g_string_append_printf(result
, "a=fmtp:%d", c
->id
);
374 for (; params
; params
= params
->next
) {
375 struct sipnameval
* par
= params
->data
;
376 g_string_append_printf(result
, " %s=%s",
377 par
->name
, par
->value
);
380 g_string_append(result
, "\r\n");
384 return g_string_free(result
, FALSE
);
388 codec_ids_to_string(GSList
*codecs
)
390 GString
*result
= g_string_new(NULL
);
392 for (; codecs
; codecs
= codecs
->next
) {
393 struct sdpcodec
*c
= codecs
->data
;
394 g_string_append_printf(result
, " %d", c
->id
);
397 return g_string_free(result
, FALSE
);
401 base64_unpad(const gchar
*str
)
403 gchar
*result
= g_strdup(str
);
406 for (ptr
= result
+ strlen(result
); ptr
!= result
; --ptr
) {
407 if (*(ptr
- 1) != '=') {
417 candidate_sort_cb(struct sdpcandidate
*c1
, struct sdpcandidate
*c2
)
419 int cmp
= sipe_strcompare(c1
->foundation
, c2
->foundation
);
421 cmp
= sipe_strcompare(c1
->username
, c2
->username
);
423 cmp
= c1
->component
- c2
->component
;
430 candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
432 GString
*result
= g_string_new("");
435 candidates
= g_slist_copy(candidates
);
436 candidates
= g_slist_sort(candidates
, (GCompareFunc
)candidate_sort_cb
);
438 for (i
= candidates
; i
; i
= i
->next
) {
439 struct sdpcandidate
*c
= i
->data
;
440 const gchar
*protocol
;
442 gchar
*related
= NULL
;
444 if (ice_version
== SIPE_ICE_RFC_5245
) {
446 switch (c
->protocol
) {
447 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
448 protocol
= "TCP-ACT";
450 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
451 protocol
= "TCP-PASS";
453 case SIPE_NETWORK_PROTOCOL_UDP
:
457 /* error unknown/unsupported type */
458 protocol
= "UNKNOWN";
463 case SIPE_CANDIDATE_TYPE_HOST
:
466 case SIPE_CANDIDATE_TYPE_RELAY
:
468 related
= g_strdup_printf("raddr %s rport %d ",
472 case SIPE_CANDIDATE_TYPE_SRFLX
:
474 related
= g_strdup_printf("raddr %s rport %d",
478 case SIPE_CANDIDATE_TYPE_PRFLX
:
482 /* error unknown/unsupported type */
487 g_string_append_printf(result
,
488 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
496 related
? related
: "");
499 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
500 gchar
*username
= base64_unpad(c
->username
);
501 gchar
*password
= base64_unpad(c
->password
);
503 // TODO: remove active/passive pairs for the same host IP
504 switch (c
->protocol
) {
505 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
506 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
509 case SIPE_NETWORK_PROTOCOL_UDP
:
513 /* error unknown/unsupported type */
514 protocol
= "UNKNOWN";
518 g_string_append_printf(result
,
519 "a=candidate:%s %u %s %s 0.%u %s %d\r\n",
533 g_slist_free(candidates
);
535 return g_string_free(result
, FALSE
);
539 remote_candidates_to_string(GSList
*candidates
, SipeIceVersion ice_version
)
541 GString
*result
= g_string_new("");
543 candidates
= g_slist_copy(candidates
);
544 candidates
= g_slist_sort(candidates
, (GCompareFunc
)candidate_sort_cb
);
547 if (ice_version
== SIPE_ICE_RFC_5245
) {
549 g_string_append(result
, "a=remote-candidates:");
551 for (i
= candidates
; i
; i
= i
->next
) {
552 struct sdpcandidate
*c
= i
->data
;
553 g_string_append_printf(result
, "%u %s %u ",
554 c
->component
, c
->ip
, c
->port
);
557 g_string_append(result
, "\r\n");
558 } else if (ice_version
== SIPE_ICE_DRAFT_6
) {
559 struct sdpcandidate
*c
= candidates
->data
;
560 g_string_append_printf(result
, "a=remote-candidate:%s\r\n",
565 g_slist_free(candidates
);
567 return g_string_free(result
, FALSE
);
571 attributes_to_string(GSList
*attributes
)
573 GString
*result
= g_string_new("");
575 for (; attributes
; attributes
= attributes
->next
) {
576 struct sipnameval
*a
= attributes
->data
;
577 g_string_append_printf(result
, "a=%s", a
->name
);
578 if (!sipe_strequal(a
->value
, ""))
579 g_string_append_printf(result
, ":%s", a
->value
);
580 g_string_append(result
, "\r\n");
583 return g_string_free(result
, FALSE
);
587 media_to_string(const struct sdpmsg
*msg
, const struct sdpmedia
*media
)
591 gchar
*media_conninfo
= NULL
;
593 gchar
*codecs_str
= NULL
;
594 gchar
*codec_ids_str
= codec_ids_to_string(media
->codecs
);
596 gchar
*candidates_str
= NULL
;
597 gchar
*remote_candidates_str
= NULL
;
599 gchar
*attributes_str
= NULL
;
600 gchar
*credentials
= NULL
;
602 gboolean uses_tcp_transport
= FALSE
;
604 if (media
->port
!= 0) {
605 if (!sipe_strequal(msg
->ip
, media
->ip
)) {
606 media_conninfo
= g_strdup_printf("c=IN IP4 %s\r\n", media
->ip
);
609 codecs_str
= codecs_to_string(media
->codecs
);
610 candidates_str
= candidates_to_string(media
->candidates
, msg
->ice_version
);
611 remote_candidates_str
= remote_candidates_to_string(media
->remote_candidates
,
614 if (media
->remote_candidates
) {
615 struct sdpcandidate
*c
= media
->remote_candidates
->data
;
617 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
||
618 c
->protocol
== SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
621 attributes_str
= attributes_to_string(media
->attributes
);
625 if (msg
->ice_version
== SIPE_ICE_RFC_5245
&& media
->candidates
) {
626 struct sdpcandidate
*c
= media
->candidates
->data
;
628 credentials
= g_strdup_printf("a=ice-ufrag:%s\r\n"
635 media_str
= g_strdup_printf("m=%s %d %sRTP/AVP%s\r\n"
642 media
->name
, media
->port
, uses_tcp_transport
? "TCP/" : "", codec_ids_str
,
643 media_conninfo
? media_conninfo
: "",
644 candidates_str
? candidates_str
: "",
645 remote_candidates_str
? remote_candidates_str
: "",
646 codecs_str
? codecs_str
: "",
647 attributes_str
? attributes_str
: "",
648 credentials
? credentials
: "");
650 g_free(media_conninfo
);
652 g_free(codec_ids_str
);
653 g_free(candidates_str
);
654 g_free(remote_candidates_str
);
655 g_free(attributes_str
);
662 sdpmsg_to_string(const struct sdpmsg
*msg
)
664 GString
*body
= g_string_new(NULL
);
667 g_string_append_printf(
670 "o=- 0 0 IN IP4 %s\r\n"
678 for (i
= msg
->media
; i
; i
= i
->next
) {
679 gchar
*media_str
= media_to_string(msg
, i
->data
);
680 g_string_append(body
, media_str
);
684 return g_string_free(body
, FALSE
);
687 static struct sdpcandidate
*
688 sdpcandidate_copy(struct sdpcandidate
*candidate
)
691 struct sdpcandidate
*copy
= g_new0(struct sdpcandidate
, 1);
693 copy
->foundation
= g_strdup(candidate
->foundation
);
694 copy
->component
= candidate
->component
;
695 copy
->type
= candidate
->type
;
696 copy
->protocol
= candidate
->protocol
;
697 copy
->priority
= candidate
->priority
;
698 copy
->ip
= g_strdup(candidate
->ip
);
699 copy
->port
= candidate
->port
;
700 copy
->base_ip
= g_strdup(candidate
->base_ip
);
701 copy
->base_port
= candidate
->base_port
;
702 copy
->username
= g_strdup(candidate
->username
);
703 copy
->password
= g_strdup(candidate
->password
);
711 sdpcandidate_free(struct sdpcandidate
*candidate
)
714 g_free(candidate
->foundation
);
715 g_free(candidate
->ip
);
716 g_free(candidate
->base_ip
);
717 g_free(candidate
->username
);
718 g_free(candidate
->password
);
724 sdpcodec_free(struct sdpcodec
*codec
)
728 sipe_utils_nameval_free(codec
->parameters
);
734 sdpmedia_free(struct sdpmedia
*media
)
742 sipe_utils_nameval_free(media
->attributes
);
744 for (item
= media
->candidates
; item
; item
= item
->next
)
745 sdpcandidate_free(item
->data
);
746 g_slist_free(media
->candidates
);
748 for (item
= media
->codecs
; item
; item
= item
->next
)
749 sdpcodec_free(item
->data
);
750 g_slist_free(media
->codecs
);
752 for (item
= media
->remote_candidates
; item
; item
= item
->next
)
753 sdpcandidate_free(item
->data
);
754 g_slist_free(media
->remote_candidates
);
761 sdpmsg_free(struct sdpmsg
*msg
)
767 for (item
= msg
->media
; item
; item
= item
->next
)
768 sdpmedia_free(item
->data
);
769 g_slist_free(msg
->media
);