summaryrefslogtreecommitdiffstats
path: root/stream/freesdp/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'stream/freesdp/parser.c')
-rw-r--r--stream/freesdp/parser.c1958
1 files changed, 1958 insertions, 0 deletions
diff --git a/stream/freesdp/parser.c b/stream/freesdp/parser.c
new file mode 100644
index 0000000000..0d6f97238f
--- /dev/null
+++ b/stream/freesdp/parser.c
@@ -0,0 +1,1958 @@
+/*
+ This file is part of FreeSDP
+ Copyright (C) 2001,2002,2003 Federico Montesino Pouzols <fedemp@altern.org>
+
+ FreeSDP is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Benjamin Zores, (C) 2006
+ added support in parser for the a=control: lines.
+ added support in parser for the a=range: lines.
+*/
+
+/**
+ * @file parser.c
+ *
+ * @short Parsing module implementation.
+ *
+ * This file implements the parsing routine <code>fsdp_parse</code>
+ * and the <code>fsdp_get_xxxx</code> routines that allow to get the
+ * session properties from a session description object build through
+ * the application of <code>fsdp_parse</code> to a textual SDP session
+ * description.
+ **/
+
+#include "parserpriv.h"
+
+/**
+ * Moves the <code>c<code> pointer up to the beginning of the next
+ * line.
+ *
+ * @param c char pointer to pointer
+ * @retval FSDPE_ILLEGAL_CHARACTER, when an illegal '\r' character
+ * (not followed by a '\n') is found, returns
+ */
+#define NEXT_LINE(c) \
+({ \
+ while ((*(c) != '\0') && (*(c) != '\r') && (*(c) != '\n')) { \
+ (c)++; \
+ } \
+ if (*(c) == '\n') { \
+ (c)++; \
+ } else if (*(c) == '\r') { \
+ (c)++; \
+ if (*(c) == '\n') { \
+ (c)++; \
+ } else { \
+ return FSDPE_ILLEGAL_CHARACTER; \
+ } \
+ } \
+})
+
+fsdp_error_t
+fsdp_parse (const char *text_description, fsdp_description_t * dsc)
+{
+ fsdp_error_t result;
+ const char *p = text_description, *p2;
+ unsigned int index, j;
+ /* temps for sscanf */
+ const unsigned int TEMPCHARS = 6;
+ char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN];
+ char longfsdp_buf[MAXLONGFIELDLEN];
+ const unsigned int TEMPINTS = 2;
+ unsigned long int wuint[TEMPINTS];
+
+ if ((NULL == text_description) || (NULL == dsc))
+ return FSDPE_INVALID_PARAMETER;
+
+ /***************************************************************************/
+ /* A) parse session-level description */
+ /***************************************************************************/
+
+ /* `v=' line (protocol version) */
+ /* according to the RFC, only `v=0' is valid */
+ if (sscanf (p, "v=%1lu", &wuint[0]))
+ {
+ if (wuint[0] != 0)
+ return FSDPE_INVALID_VERSION;
+ }
+ else
+ {
+ return FSDPE_MISSING_VERSION;
+ }
+ NEXT_LINE (p);
+
+ /* `o=' line (owner/creator and session identifier) */
+ /* o=<username> <session id> <version> <network type> <address type>
+ <address> */
+ if (!strncmp (p, "o=", 2))
+ {
+ p += 2;
+ /* note that the following max lengths may vary in the future and
+ are quite arbitary */
+ if (sscanf
+ (p,
+ "%" MSFLENS "[\x21-\xFF] %" MSFLENS "[0-9] %" MSFLENS
+ "[0-9] %2s %3s %" MSFLENS "s", fsdp_buf[0], fsdp_buf[1],
+ fsdp_buf[2], fsdp_buf[3], fsdp_buf[4], fsdp_buf[5]) != 6)
+ return FSDPE_INVALID_OWNER;
+ dsc->o_username = strdup (fsdp_buf[0]);
+ dsc->o_session_id = strdup (fsdp_buf[1]);
+ dsc->o_announcement_version = strdup (fsdp_buf[2]);
+ if (!strncmp (fsdp_buf[3], "IN", 2))
+ {
+ dsc->o_network_type = FSDP_NETWORK_TYPE_INET;
+ if (!strncmp (fsdp_buf[4], "IP4", 3))
+ dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV4;
+ else if (!strncmp (fsdp_buf[4], "IP6", 3))
+ dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV6;
+ else
+ return FSDPE_INVALID_OWNER;
+ }
+ else
+ {
+ return FSDPE_INVALID_OWNER;
+ }
+ /* TODO? check valid unicast address/FQDN */
+ dsc->o_address = strdup (fsdp_buf[5]);
+ }
+ else
+ {
+ return FSDPE_MISSING_OWNER;
+ }
+ NEXT_LINE (p);
+
+ /* `s=' line (session name) -note that the name string cannot be empty */
+ /* s=<session name> */
+ if (!strncmp (p, "s=", 2))
+ {
+ if (sscanf (p, "s=%" MLFLENS "[^\r\n]", longfsdp_buf) < 1)
+ return FSDPE_EMPTY_NAME;
+ dsc->s_name = strdup (longfsdp_buf);
+ }
+ else
+ {
+ return FSDPE_MISSING_NAME;
+ }
+ NEXT_LINE (p);
+
+ /* `i=' line (session information) [optional] */
+ /* i=<session description> */
+ if (!strncmp (p, "i=", 2)
+ && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf))
+ {
+ dsc->i_information = strdup (longfsdp_buf);
+ NEXT_LINE (p);
+ }
+ else
+ {
+ /* (optional) information absent */
+ }
+
+ /* `u=' line (URI of description) [optional] */
+ /* u=<URI> */
+ if (!strncmp (p, "u=", 2)
+ && sscanf (p, "u=%" MLFLENS "[^\r\n]", longfsdp_buf))
+ {
+ /* TODO? check valid uri */
+ dsc->u_uri = strdup (longfsdp_buf);
+ NEXT_LINE (p);
+ }
+ else
+ {
+ /* (optional) uri absent */
+ }
+
+ /* `e=' lines (email address) [zero or more] */
+ /* e=<email address> */
+ p2 = p;
+ j = 0;
+ while (!strncmp (p2, "e=", 2))
+ {
+ /* First, count how many emails are there */
+ j++;
+ NEXT_LINE (p2);
+ }
+ dsc->emails_count = j;
+ if (dsc->emails_count > 0)
+ {
+ /* Then, build the array of emails */
+ dsc->emails = calloc (j, sizeof (const char *));
+ for (j = 0; j < dsc->emails_count; j++)
+ {
+ sscanf (p, "e=%" MLFLENS "[^\r\n]", longfsdp_buf);
+ /* TODO? check valid email-address. */
+ dsc->emails[j] = strdup (longfsdp_buf);
+ NEXT_LINE (p);
+ }
+ }
+
+ /* `p=' lines (phone number) [zero or more] */
+ /* p=<phone number> */
+ j = 0;
+ /* assert ( p2 == p ); */
+ while (!strncmp (p2, "p=", 2))
+ {
+ j++;
+ NEXT_LINE (p2);
+ }
+ dsc->phones_count = j;
+ if (dsc->phones_count > 0)
+ {
+ dsc->phones = calloc (j, sizeof (const char *));
+ for (j = 0; j < dsc->phones_count; j++)
+ {
+ sscanf (p, "p=%" MLFLENS "[^\r\n]", longfsdp_buf);
+ /* TODO? check valid phone-number. */
+ dsc->phones[j] = strdup (longfsdp_buf);
+ NEXT_LINE (p);
+ }
+ }
+
+ /* `c=' line (connection information - not required if included in all media) [optional] */
+ /* c=<network type> <address type> <connection address> */
+ result = fsdp_parse_c (&p, &(dsc->c_network_type), &(dsc->c_address_type),
+ &(dsc->c_address));
+ if (FSDPE_OK != result)
+ return result;
+
+ /* `b=' lines (bandwidth information) [optional] */
+ /* b=<modifier>:<bandwidth-value> */
+ result =
+ fsdp_parse_b (&p, &(dsc->bw_modifiers), &(dsc->bw_modifiers_count));
+ if (FSDPE_OK != result)
+ return result;
+
+ /* A.1) Time descriptions: */
+
+ /* `t=' lines (time the session is active) [1 or more] */
+ /* t=<start time> <stop time> */
+ j = 0;
+ p2 = p;
+ while (!strncmp (p2, "t=", 2))
+ {
+ j++;
+ NEXT_LINE (p2);
+ while (!strncmp (p2, "r=", 2))
+ NEXT_LINE (p2);
+ }
+ dsc->time_periods_count = j;
+ if (dsc->time_periods_count == 0)
+ return FSDPE_MISSING_TIME;
+ dsc->time_periods = calloc (dsc->time_periods_count,
+ sizeof (fsdp_time_period_t *));
+ index = 0;
+ for (j = 0; j < dsc->time_periods_count; j++)
+ {
+ unsigned int h = 0;
+ if (sscanf (p, "t=%10lu %10lu", &wuint[0], &wuint[1]) != 2)
+ {
+ /* not all periods have been successfully parsed */
+ dsc->time_periods_count = j;
+ return FSDPE_INVALID_TIME;
+ }
+ dsc->time_periods[j] = calloc (1, sizeof (fsdp_time_period_t));
+
+ /* convert from NTP to time_t time */
+ if (wuint[0] != 0)
+ wuint[0] -= NTP_EPOCH_OFFSET;
+ if (wuint[1] != 0)
+ wuint[1] -= NTP_EPOCH_OFFSET;
+ dsc->time_periods[j]->start = wuint[0];
+ dsc->time_periods[j]->stop = wuint[1];
+ NEXT_LINE (p);
+
+ /* `r' lines [zero or more repeat times for each t=] */
+ /*r=<repeat interval> <active duration> <list of offsets from
+ start-time> */
+ p2 = p;
+ while (!strncmp (p2, "r=", 2))
+ {
+ h++;
+ NEXT_LINE (p2);
+ }
+ dsc->time_periods[j]->repeats_count = h;
+ if (h > 0)
+ {
+ unsigned int index2 = 0;
+ dsc->time_periods[j]->repeats =
+ calloc (h, sizeof (fsdp_repeat_t *));
+ for (h = 0; h < dsc->time_periods[j]->repeats_count; h++)
+ {
+ /*
+ get_repeat_values(p,&(dsc->time_periods[index].repeats[index2]));
+ fsdp_error_t get_repeat_values (const char *r, fsdp_repeat_t
+ *repeat);
+ */
+ if (sscanf (p, "r=%10s %10s %" MLFLENS "[^\r\n]",
+ fsdp_buf[0], fsdp_buf[1], longfsdp_buf) == 3)
+ {
+ fsdp_repeat_t *repeat;
+ dsc->time_periods[j]->repeats[h] =
+ calloc (1, sizeof (fsdp_repeat_t));
+ repeat = dsc->time_periods[j]->repeats[h];
+ /* get interval, duration and list of offsets */
+ result =
+ fsdp_repeat_time_to_uint (fsdp_buf[0],
+ &(repeat->interval));
+ if (result == FSDPE_OK)
+ {
+ result =
+ fsdp_repeat_time_to_uint (fsdp_buf[1],
+ &(repeat->duration));
+ if (result == FSDPE_OK)
+ {
+ unsigned int k = 1;
+ const char *i = longfsdp_buf;
+ while (NULL != (i = strchr (i, ' ')))
+ {
+ k++;
+ if (NULL != i)
+ i++;
+ }
+ repeat->offsets_count = k;
+ repeat->offsets = calloc (k, sizeof (time_t));
+ i = longfsdp_buf;
+ for (k = 0;
+ (k < repeat->offsets_count)
+ && (result == FSDPE_OK); k++)
+ {
+ result =
+ fsdp_repeat_time_to_uint (i,
+ &(repeat->
+ offsets[k]));
+ i = strchr (i, ' ');
+ if (NULL != i)
+ i++;
+ }
+ if (k < repeat->offsets_count)
+ {
+ /* there where invalid repeat offsets */
+ dsc->time_periods[j]->repeats_count = k;
+ return FSDPE_INVALID_REPEAT;
+ }
+ }
+ }
+ if (result != FSDPE_OK)
+ {
+ /* not all repeats have been succesfully parsed */
+ dsc->time_periods[j]->repeats_count = h;
+ return FSDPE_INVALID_REPEAT;
+ }
+ NEXT_LINE (p);
+ }
+ else
+ {
+ /* not all repeats have been succesfully parsed */
+ dsc->time_periods[j]->repeats_count = h;
+ return FSDPE_INVALID_REPEAT;
+ }
+ index2++;
+ }
+ }
+ }
+
+ /* `z=' line (time zone adjustments) [zero or more] */
+ /* z=<adjustment time> <offset> <adjustment time> <offset> .... */
+ if (!strncmp (p, "z=", 2))
+ {
+ if (sscanf (p, "z=%" MLFLENS "[^\r\n]", longfsdp_buf))
+ {
+ /* TODO: guess how many pairs are there and process them */
+ dsc->timezone_adj = strdup (longfsdp_buf);
+ NEXT_LINE (p);
+ }
+ else
+ {
+ return FSDPE_INVALID_TIMEZONE;
+ }
+ }
+
+ /* `k=' line (encryption key) [optional] */
+ /* k=<method>
+ k=<method>:<encryption key> */
+ result = fsdp_parse_k (&p, &(dsc->k_encryption_method),
+ &(dsc->k_encryption_content));
+ if (result != FSDPE_OK)
+ return result;
+
+ /* A.2) Attributes */
+ /* `a=' lines (session attribute) [0 or more] */
+ /* a=<attribute>
+ a=<attribute>:<value> */
+ while (!strncmp (p, "a=", 2))
+ {
+ /* The "9" lenght specifier of the first string is subject to
+ changes */
+ if (sscanf
+ (p, "a=%9[^:\r\n]:%" MSFLENS "[^\r\n]", fsdp_buf[0],
+ fsdp_buf[1]) == 2)
+ {
+ /* session-level value attributes */
+ if (!strncmp (fsdp_buf[0], "cat", 3))
+ dsc->a_category = strdup (fsdp_buf[1]);
+ else if (!strncmp (fsdp_buf[0], "keywds", 6))
+ dsc->a_keywords = strdup (fsdp_buf[1]);
+ else if (!strncmp (fsdp_buf[0], "tool", 4))
+ dsc->a_keywords = strdup (fsdp_buf[1]);
+ else if (!strncmp (fsdp_buf[0], "rtpmap", 6))
+ fsdp_parse_rtpmap (&(dsc->a_rtpmaps),
+ &(dsc->a_rtpmaps_count), fsdp_buf[1]);
+ else if (!strncmp (fsdp_buf[0], "type", 4))
+ {
+ if (!strncmp (fsdp_buf[1], "broadcast", 9))
+ dsc->a_type = FSDP_SESSION_TYPE_BROADCAST;
+ else if (!strncmp (fsdp_buf[1], "meeting", 7))
+ dsc->a_type = FSDP_SESSION_TYPE_MEETING;
+ else if (!strncmp (fsdp_buf[1], "moderated", 9))
+ dsc->a_type = FSDP_SESSION_TYPE_MODERATED;
+ else if (!strncmp (fsdp_buf[1], "test", 4))
+ dsc->a_type = FSDP_SESSION_TYPE_TEST;
+ else if (!strncmp (fsdp_buf[1], "H332", 4))
+ dsc->a_type = FSDP_SESSION_TYPE_H332;
+ else
+ return FSDPE_INVALID_SESSION_TYPE;
+ }
+ else if (!strncmp (fsdp_buf[0], "charset", 7))
+ dsc->a_charset = strdup (fsdp_buf[1]);
+ else if (!strncmp (fsdp_buf[0], "sdplang", 7))
+ {
+ if (NULL == dsc->a_sdplangs)
+ {
+ dsc->a_sdplangs_count = 0;
+ dsc->a_sdplangs =
+ calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
+ }
+ if (dsc->a_sdplangs_count < SDPLANGS_MAX_COUNT)
+ {
+ dsc->a_sdplangs[dsc->a_sdplangs_count] =
+ strdup (fsdp_buf[1]);
+ dsc->a_sdplangs_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "lang", 4))
+ {
+ if (NULL == dsc->a_langs)
+ {
+ dsc->a_langs_count = 0;
+ dsc->a_langs = calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
+ }
+ if (dsc->a_langs_count < SDPLANGS_MAX_COUNT)
+ {
+ dsc->a_langs[dsc->a_langs_count] = strdup (fsdp_buf[1]);
+ dsc->a_langs_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "control", 7))
+ {
+ if (NULL == dsc->a_controls)
+ {
+ dsc->a_controls_count = 0;
+ dsc->a_controls =
+ calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *));
+ }
+ if (dsc->a_controls_count < SDPCONTROLS_MAX_COUNT)
+ {
+ dsc->a_controls[dsc->a_controls_count] =
+ strdup (fsdp_buf[1]);
+ dsc->a_controls_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "range", 5))
+ {
+ if (dsc->a_range)
+ free (dsc->a_range);
+ dsc->a_range = strdup (fsdp_buf[1]);
+ }
+ else
+ {
+ /* ignore unknown attributes, but provide access to them */
+ *longfsdp_buf = '\0';
+ strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN);
+ strncat (longfsdp_buf, ":", MAXLONGFIELDLEN);
+ strncat (longfsdp_buf, fsdp_buf[1], MAXLONGFIELDLEN);
+ if (NULL == dsc->unidentified_attributes)
+ {
+ dsc->unidentified_attributes_count = 0;
+ dsc->unidentified_attributes =
+ calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
+ sizeof (char *));
+ }
+ if (dsc->unidentified_attributes_count <
+ UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
+ {
+ dsc->unidentified_attributes
+ [dsc->unidentified_attributes_count] =
+ strdup (longfsdp_buf);
+ dsc->unidentified_attributes_count++;
+ }
+ }
+ NEXT_LINE (p);
+ }
+ else if (sscanf (p, "a=%20s", fsdp_buf[0]) == 1)
+ {
+ /* session-level property attributes */
+ if (!strncmp (fsdp_buf[0], "recvonly", 8))
+ dsc->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY;
+ else if (!strncmp (fsdp_buf[0], "sendonly", 8))
+ dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY;
+ else if (!strncmp (fsdp_buf[0], "inactive", 8))
+ dsc->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE;
+ else if (!strncmp (fsdp_buf[0], "sendrecv", 8))
+ dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV;
+ else
+ {
+ /* ignore unknown attributes, but provide access to them */
+ *longfsdp_buf = '\0';
+ strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN);
+ if (NULL == dsc->unidentified_attributes)
+ {
+ dsc->unidentified_attributes_count = 0;
+ dsc->unidentified_attributes =
+ calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
+ sizeof (char *));
+ }
+ if (dsc->unidentified_attributes_count <
+ UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
+ {
+ dsc->unidentified_attributes
+ [dsc->unidentified_attributes_count] =
+ strdup (longfsdp_buf);
+ dsc->unidentified_attributes_count++;
+ }
+ }
+ NEXT_LINE (p);
+ }
+ else
+ return FSDPE_INVALID_ATTRIBUTE;
+ }
+
+ /***************************************************************************/
+ /* B) parse media-level descriptions */
+ /***************************************************************************/
+ p2 = p;
+ j = 0;
+ while ((*p2 != '\0') && !strncmp (p2, "m=", 2))
+ {
+ char c;
+ j++;
+ NEXT_LINE (p2);
+ while (sscanf (p2, "%c=", &c) == 1)
+ {
+ if (c == 'i' || c == 'c' || c == 'b' || c == 'k' || c == 'a')
+ {
+ NEXT_LINE (p2);
+ }
+ else if (c == 'm')
+ {
+ break;
+ }
+ else
+ {
+ return FSDPE_INVALID_LINE;
+ }
+ }
+ }
+ dsc->media_announcements_count = j;
+ if (dsc->media_announcements_count == 0)
+ {
+ ;
+ /*return FSDPE_MISSING_MEDIA; */
+ }
+ else
+ { /* dsc->media_announcements_count > 0 */
+ dsc->media_announcements =
+ calloc (j, sizeof (fsdp_media_announcement_t *));
+ for (j = 0; j < dsc->media_announcements_count; j++)
+ {
+ fsdp_media_announcement_t *media = NULL;
+ /* `m=' line (media name, transport address and format list) */
+ /* m=<media> <port> <transport> <fmt list> */
+ /* The max. string lengths are subject to change */
+ if (sscanf (p, "m=%11s %8s %7s %" MLFLENS "[^\r\n]",
+ fsdp_buf[0], fsdp_buf[1], fsdp_buf[2],
+ longfsdp_buf) != 4)
+ {
+ return FSDPE_INVALID_MEDIA;
+ }
+ else
+ {
+ dsc->media_announcements[j] =
+ calloc (1, sizeof (fsdp_media_announcement_t));
+ media = dsc->media_announcements[j];
+ if (!strncmp (fsdp_buf[0], "audio", 5))
+ media->media_type = FSDP_MEDIA_AUDIO;
+ else if (!strncmp (fsdp_buf[0], "video", 5))
+ media->media_type = FSDP_MEDIA_VIDEO;
+ else if (!strncmp (fsdp_buf[0], "application", 11))
+ media->media_type = FSDP_MEDIA_APPLICATION;
+ else if (!strncmp (fsdp_buf[0], "data", 4))
+ media->media_type = FSDP_MEDIA_DATA;
+ else if (!strncmp (fsdp_buf[0], "control", 7))
+ media->media_type = FSDP_MEDIA_CONTROL;
+ else
+ return FSDPE_UNKNOWN_MEDIA_TYPE;
+ { /* try to get port specification as port/number */
+ char *slash;
+ if ((slash = strchr (fsdp_buf[1], '/')))
+ {
+ *slash = '\0';
+ slash++;
+ media->port = strtol (fsdp_buf[1], NULL, 10);
+ media->port_count = strtol (slash, NULL, 10);
+ }
+ else
+ {
+ media->port = strtol (fsdp_buf[1], NULL, 10);
+ media->port_count = 0;
+ }
+ }
+ if (!strncmp (fsdp_buf[2], "RTP/AVP", 7))
+ media->transport = FSDP_TP_RTP_AVP;
+ else if (!strncmp (fsdp_buf[2], "udp", 3))
+ media->transport = FSDP_TP_UDP;
+ else if (!strncmp (fsdp_buf[2], "TCP", 3))
+ media->transport = FSDP_TP_TCP;
+ else if (!strncmp (fsdp_buf[2], "UDPTL", 5))
+ media->transport = FSDP_TP_UDPTL;
+ else if (!strncmp (fsdp_buf[2], "vat", 3))
+ media->transport = FSDP_TP_VAT;
+ else if (!strncmp (fsdp_buf[2], "rtp", 3))
+ media->transport = FSDP_TP_OLD_RTP;
+ else
+ return FSDPE_UNKNOWN_MEDIA_TRANSPORT;
+ {
+ unsigned int k = 0;
+ char *s = longfsdp_buf;
+ while (NULL != (s = strchr (s, ' ')))
+ {
+ k++;
+ if (NULL != s)
+ s++;
+ }
+ k++; /* when there is no space left, count the last format */
+ media->formats_count = k;
+ media->formats = calloc (k, sizeof (char *));
+ s = longfsdp_buf;
+ for (k = 0; k < media->formats_count; k++)
+ {
+ char *space = strchr (s, ' ');
+ if (NULL != space)
+ *space = '\0';
+ media->formats[k] = strdup (s);
+ s = space + 1;
+ }
+ }
+ NEXT_LINE (p);
+ }
+
+ /* `i=' line (media title) [optional] */
+ /* i=<media title> */
+ if (!strncmp (p, "i=", 2)
+ && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf))
+ {
+ media->i_title = strdup (longfsdp_buf);
+ NEXT_LINE (p);
+ }
+ else
+ {
+ /* (optional) information absent */
+ }
+
+ /* `c=' line (connection information - overrides session-level
+ line) [optional if provided at session-level] */
+ /* c=<network type> <address type> <connection address> */
+ result = fsdp_parse_c (&p, &(media->c_network_type),
+ &(media->c_address_type),
+ &(media->c_address));
+ if (result != FSDPE_OK)
+ return result;
+
+ /* `b=' lines (bandwidth information) [optional] */
+ /* b=<modifier>:<bandwidth-value> */
+ result = fsdp_parse_b (&p, &(media->bw_modifiers),
+ &(media->bw_modifiers_count));
+ if (FSDPE_OK != result)
+ return result;
+
+ /* `k=' line (encryption key) [optional] */
+ /* k=<method>
+ k=<method>:<encryption key> */
+ result = fsdp_parse_k (&p, &(media->k_encryption_method),
+ &(media->k_encryption_content));
+ if (result != FSDPE_OK)
+ return result;
+
+ /* B.1) Attributes */
+
+ /* `a=' lines (zero or more media attribute lines) [optional] */
+ /* a=<attribute>
+ a=<attribute>:<value> */
+ while (!strncmp (p, "a=", 2))
+ {
+ if (sscanf
+ (p, "a=%9[^:\r\n]:%" MLFLENS "[^\r\n]", fsdp_buf[0],
+ longfsdp_buf) == 2)
+ {
+ /* media-level value attributes */
+ if (!strncmp (fsdp_buf[0], "ptime", 5))
+ media->a_ptime = strtoul (longfsdp_buf, NULL, 10);
+ else if (!strncmp (fsdp_buf[0], "maxptime", 8))
+ media->a_maxptime = strtoul (longfsdp_buf, NULL, 10);
+ else if (!strncmp (fsdp_buf[0], "rtpmap", 6))
+ fsdp_parse_rtpmap (&(media->a_rtpmaps),
+ &(media->a_rtpmaps_count),
+ longfsdp_buf);
+ else if (!strncmp (fsdp_buf[0], "orient", 6))
+ {
+ if (!strncmp (longfsdp_buf, "portrait", 8))
+ media->a_orient = FSDP_ORIENT_PORTRAIT;
+ else if (!strncmp (longfsdp_buf, "landscape", 9))
+ media->a_orient = FSDP_ORIENT_LANDSCAPE;
+ else if (!strncmp (longfsdp_buf, "seascape", 9))
+ media->a_orient = FSDP_ORIENT_SEASCAPE;
+ }
+ else if (!strncmp (fsdp_buf[0], "sdplang", 7))
+ {
+ if (NULL == dsc->a_sdplangs)
+ {
+ media->a_sdplangs_count = 0;
+ media->a_sdplangs =
+ calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
+ }
+ if (media->a_sdplangs_count < SDPLANGS_MAX_COUNT)
+ {
+ media->a_sdplangs[dsc->a_sdplangs_count] =
+ strdup (longfsdp_buf);
+ media->a_sdplangs_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "lang", 4))
+ {
+ if (NULL == dsc->a_langs)
+ {
+ media->a_langs_count = 0;
+ media->a_langs =
+ calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
+ }
+ if (media->a_langs_count < SDPLANGS_MAX_COUNT)
+ {
+ media->a_langs[dsc->a_langs_count] =
+ strdup (longfsdp_buf);
+ media->a_langs_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "control", 7))
+ {
+ if (NULL == media->a_controls)
+ {
+ media->a_controls_count = 0;
+ media->a_controls =
+ calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *));
+ }
+ if (media->a_controls_count < SDPCONTROLS_MAX_COUNT)
+ {
+ media->a_controls[media->a_controls_count] =
+ strdup (longfsdp_buf);
+ media->a_controls_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "range", 5))
+ {
+ if (media->a_range)
+ free (media->a_range);
+ media->a_range = strdup (fsdp_buf[1]);
+ }
+ else if (!strncmp (fsdp_buf[0], "framerate", 9))
+ media->a_framerate = strtod (longfsdp_buf, NULL);
+ else if (!strncmp (fsdp_buf[0], "fmtp", 4))
+ {
+ if (NULL == media->a_fmtps)
+ {
+ media->a_fmtps_count = 0;
+ media->a_fmtps =
+ calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
+ }
+ if (media->a_fmtps_count < SDPLANGS_MAX_COUNT)
+ {
+ media->a_fmtps[media->a_fmtps_count] =
+ strdup (longfsdp_buf);
+ media->a_fmtps_count++;
+ }
+ }
+ else if (!strncmp (fsdp_buf[0], "rtcp", 4))
+ {
+ int opts = 0;
+ /* rtcp attribute: a=rtcp:<port> <nettype> <addrtype> <address> */
+ opts =
+ sscanf (longfsdp_buf, "%lu %2s %3s %" MSFLENS "s",
+ &wuint[0], fsdp_buf[0], fsdp_buf[1],
+ fsdp_buf[2]);
+ if (opts >= 1)
+ {
+ media->a_rtcp_port = wuint[0];
+ if (opts >= 2)
+ {
+ if (!strncmp (fsdp_buf[0], "IN", 2))
+ {
+ media->a_rtcp_network_type =
+ FSDP_NETWORK_TYPE_INET;
+ } /* else
+ ; TODO: define error code? */
+ if (opts >= 3)
+ {
+ if (!strncmp (fsdp_buf[1], "IP4", 3))
+ media->a_rtcp_address_type =
+ FSDP_ADDRESS_TYPE_IPV4;
+ else if (!strncmp (fsdp_buf[1], "IP6", 3))
+ media->a_rtcp_address_type =
+ FSDP_ADDRESS_TYPE_IPV6;
+ else
+ return FSDPE_INVALID_CONNECTION_NETTYPE;
+ /*add specific code? */
+ if (opts >= 4)
+ media->a_rtcp_address =
+ strdup (fsdp_buf[2]);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* ignore unknown attributes, but provide access to them */
+ *fsdp_buf[1] = '\0';
+ strncat (fsdp_buf[1], fsdp_buf[0], MAXLONGFIELDLEN);
+ strncat (fsdp_buf[1], ":", MAXLONGFIELDLEN);
+ strncat (fsdp_buf[1], longfsdp_buf, MAXLONGFIELDLEN);
+ if (NULL == media->unidentified_attributes)
+ {
+ media->unidentified_attributes_count = 0;
+ media->unidentified_attributes =
+ calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
+ sizeof (char *));
+ }
+ if (media->unidentified_attributes_count <
+ UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
+ {
+ media->unidentified_attributes
+ [media->unidentified_attributes_count] =
+ strdup (fsdp_buf[1]);
+ media->unidentified_attributes_count++;
+ }
+ }
+ NEXT_LINE (p);
+ }
+ else if (sscanf (p, "a=%8s", fsdp_buf[0]) == 1)
+ {
+ /* media-level property attributes */
+ if (!strncmp (fsdp_buf[0], "recvonly", 8))
+ media->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY;
+ else if (!strncmp (fsdp_buf[0], "sendonly", 8))
+ media->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY;
+ else if (!strncmp (fsdp_buf[0], "inactive", 8))
+ media->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE;
+ else if (!strncmp (fsdp_buf[0], "sendrecv", 8))
+ media->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV;
+ else
+ {
+ /* ignore unknown attributes, but provide access to them */
+ *longfsdp_buf = '\0';
+ strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN);
+ if (NULL == media->unidentified_attributes)
+ {
+ media->unidentified_attributes_count = 0;
+ media->unidentified_attributes =
+ calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
+ sizeof (char *));
+ }
+ if (media->unidentified_attributes_count <
+ UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
+ {
+ media->unidentified_attributes
+ [media->unidentified_attributes_count] =
+ strdup (longfsdp_buf);
+ media->unidentified_attributes_count++;
+ }
+ }
+ NEXT_LINE (p);
+ }
+ else
+ return FSDPE_INVALID_ATTRIBUTE;
+ }
+ } /* end of for */
+ }
+
+ /* Check c= has been given at session level or at media level for
+ all media */
+ if (NULL == dsc->c_address.address)
+ {
+ unsigned int c;
+ for (c = 0; c < dsc->media_announcements_count; c++)
+ if (NULL == dsc->media_announcements[c]->c_address.address)
+ return FSDPE_MISSING_CONNECTION_INFO;
+ }
+
+ /* finish */
+ if (*p == '\0')
+ return FSDPE_OK;
+ else
+ return FSDPE_OVERFILLED;
+}
+
+static fsdp_error_t
+fsdp_parse_c (const char **p, fsdp_network_type_t * ntype,
+ fsdp_address_type_t * atype,
+ fsdp_connection_address_t * address)
+{
+ const unsigned int TEMPCHARS = 3;
+ char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN];
+
+ if (!strncmp (*p, "c=", 2))
+ {
+ if (sscanf (*p, "c=%2s %3s %" MSFLENS "s",
+ fsdp_buf[0], fsdp_buf[1], fsdp_buf[2]))
+ {
+ if (!strncmp (fsdp_buf[0], "IN", 2))
+ {
+ *ntype = FSDP_NETWORK_TYPE_INET;
+ if (!strncmp (fsdp_buf[1], "IP4", 3))
+ *atype = FSDP_ADDRESS_TYPE_IPV4;
+ else if (!strncmp (fsdp_buf[1], "IP6", 3))
+ *atype = FSDP_ADDRESS_TYPE_IPV6;
+ else
+ return FSDPE_INVALID_CONNECTION_NETTYPE;
+ }
+ else
+ {
+ return FSDPE_INVALID_CONNECTION_ADDRTYPE;
+ }
+ {
+ char *slash = strchr (fsdp_buf[2], '/');
+ if (NULL == slash)
+ {
+ address->address = strdup (fsdp_buf[2]);
+ address->address_ttl = 0;
+ address->address_count = 0;
+ }
+ else
+ {
+ /* address is IP4 multicast */
+ char *slash2;
+ *slash = '\0';
+ slash++;
+ address->address = strdup (fsdp_buf[2]);
+ slash2 = strchr (slash + 1, '/');
+ if (NULL == slash2)
+ {
+ address->address_ttl = strtol (slash, NULL, 10);
+ address->address_count = 0;
+ }
+ else
+ {
+ *slash2 = '\0';
+ slash2++;
+ address->address_ttl = strtol (slash, NULL, 10);
+ address->address_count = strtol (slash2, NULL, 10);
+ }
+ }
+ }
+ NEXT_LINE (*p);
+ }
+ else
+ {
+ return FSDPE_INVALID_CONNECTION;
+ }
+ }
+ return FSDPE_OK;
+}
+
+static fsdp_error_t
+fsdp_parse_b (const char **p, fsdp_bw_modifier_t ** bw_modifiers,
+ unsigned int *bw_modifiers_count)
+{
+ char fsdp_buf[MAXSHORTFIELDLEN];
+ unsigned long int wuint;
+ unsigned int i = 0;
+ char *lp = (char *) *p;
+
+ /* count b= lines */
+ while (!strncmp (lp, "b=", 2))
+ {
+ NEXT_LINE (lp);
+ i++;
+ }
+ *bw_modifiers = calloc (i, sizeof (fsdp_bw_modifier_t));
+ *bw_modifiers_count = i;
+
+ while (i > 0)
+ {
+ unsigned int index = *bw_modifiers_count - i;
+ if (2 == sscanf (*p, "b=%20[^:\r\n]:%lu", fsdp_buf, &wuint))
+ {
+ if (!strncmp (fsdp_buf, "CT", 2))
+ (*bw_modifiers)[index].b_mod_type =
+ FSDP_BW_MOD_TYPE_CONFERENCE_TOTAL;
+ else if (!strncmp (fsdp_buf, "AS", 2))
+ (*bw_modifiers)[index].b_mod_type =
+ FSDP_BW_MOD_TYPE_APPLICATION_SPECIFIC;
+ else if (!strncmp (fsdp_buf, "RS", 2))
+ (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_RTCP_SENDERS;
+ else if (!strncmp (fsdp_buf, "RR", 2))
+ (*bw_modifiers)[index].b_mod_type =
+ FSDP_BW_MOD_TYPE_RTCP_RECEIVERS;
+ else
+ {
+ (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_UNKNOWN;
+ (*bw_