/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "sdp_os_defs.h"
#include "sdp.h"
#include "sdp_private.h"

#include "CSFLog.h"

static const char* logTag = "sdp_main";

/* Note: These *must* be in the same order as the enum types. */
const sdp_tokenarray_t sdp_token[SDP_MAX_TOKENS] =
{
    {"v=", sdp_parse_version,      sdp_build_version },
    {"o=", sdp_parse_owner,        sdp_build_owner },
    {"s=", sdp_parse_sessname,     sdp_build_sessname },
    {"i=", sdp_parse_sessinfo,     sdp_build_sessinfo },
    {"u=", sdp_parse_uri,          sdp_build_uri },
    {"e=", sdp_parse_email,        sdp_build_email },
    {"p=", sdp_parse_phonenum,     sdp_build_phonenum },
    {"c=", sdp_parse_connection,   sdp_build_connection },
    {"b=", sdp_parse_bandwidth,    sdp_build_bandwidth },
    {"t=", sdp_parse_timespec,     sdp_build_timespec },
    {"r=", sdp_parse_repeat_time,  sdp_build_repeat_time },
    {"z=", sdp_parse_timezone_adj, sdp_build_timezone_adj },
    {"k=", sdp_parse_encryption,   sdp_build_encryption },
    {"a=", sdp_parse_attribute,    sdp_build_attribute },
    {"m=", sdp_parse_media,        sdp_build_media }
};


/* Note: These *must* be in the same order as the enum types. */
const sdp_attrarray_t sdp_attr[SDP_MAX_ATTR_TYPES] =
{
    {"bearer", sizeof("bearer"),
     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"called", sizeof("called"),
     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"connection_type", sizeof("connection_type"),
     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"dialed", sizeof("dialed"),
     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"dialing", sizeof("dialing"),
     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"direction", sizeof("direction"),
     sdp_parse_attr_comediadir, sdp_build_attr_comediadir },
    {"eecid", sizeof("eecid"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"fmtp", sizeof("fmtp"),
     sdp_parse_attr_fmtp, sdp_build_attr_fmtp },
    {"sctpmap", sizeof("sctpmap"),
     sdp_parse_attr_sctpmap, sdp_build_attr_sctpmap },
    {"framing", sizeof("framing"),
     sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"inactive", sizeof("inactive"),
     sdp_parse_attr_direction, sdp_build_attr_direction },
    {"ptime", sizeof("ptime"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"qos", sizeof("qos"),
     sdp_parse_attr_qos, sdp_build_attr_qos },
    {"curr", sizeof("curr"),
     sdp_parse_attr_curr, sdp_build_attr_curr },
    {"des", sizeof("des"),
     sdp_parse_attr_des, sdp_build_attr_des},
    {"conf", sizeof("conf"),
     sdp_parse_attr_conf, sdp_build_attr_conf},
    {"recvonly", sizeof("recvonly"),
     sdp_parse_attr_direction, sdp_build_attr_direction },
    {"rtpmap", sizeof("rtpmap"),
     sdp_parse_attr_transport_map, sdp_build_attr_transport_map },
    {"secure", sizeof("secure"),
     sdp_parse_attr_qos, sdp_build_attr_qos },
    {"sendonly", sizeof("sendonly"),
     sdp_parse_attr_direction, sdp_build_attr_direction },
    {"sendrecv", sizeof("sendrecv"),
     sdp_parse_attr_direction, sdp_build_attr_direction },
    {"subnet", sizeof("subnet"),
     sdp_parse_attr_subnet, sdp_build_attr_subnet },
    {"T38FaxVersion", sizeof("T38FaxVersion"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"T38MaxBitRate", sizeof("T38MaxBitRate"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"T38FaxFillBitRemoval", sizeof("T38FaxFillBitRemoval"),
     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
    {"T38FaxTranscodingMMR", sizeof("T38FaxTranscodingMMR"),
     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
    {"T38FaxTranscodingJBIG", sizeof("T38FaxTranscodingJBIG"),
     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
    {"T38FaxRateManagement", sizeof("T38FaxRateManagement"),
     sdp_parse_attr_t38_ratemgmt, sdp_build_attr_t38_ratemgmt },
    {"T38FaxMaxBuffer", sizeof("T38FaxMaxBuffer"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"T38FaxMaxDatagram", sizeof("T38FaxMaxDatagram"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"T38FaxUdpEC", sizeof("T38FaxUdpEC"),
     sdp_parse_attr_t38_udpec, sdp_build_attr_t38_udpec },
    {"X-cap", sizeof("X-cap"),
     sdp_parse_attr_cap, sdp_build_attr_cap },
    {"X-cpar", sizeof("X-cpar"),
     sdp_parse_attr_cpar, sdp_build_attr_cpar },
    {"X-pc-codec", sizeof("X-pc-codec"),
     sdp_parse_attr_pc_codec, sdp_build_attr_pc_codec },
    {"X-pc-qos", sizeof("X-pc-qos"),
     sdp_parse_attr_qos, sdp_build_attr_qos },
    {"X-qos", sizeof("X-qos"),
     sdp_parse_attr_qos, sdp_build_attr_qos },
    {"X-sqn", sizeof("X-sqn"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"TMRGwXid", sizeof("TMRGwXid"),
     sdp_parse_attr_simple_bool, sdp_build_attr_simple_bool },
    {"TC1PayloadBytes", sizeof("TC1PayloadBytes"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"TC1WindowSize", sizeof("TC1WindowSize"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"TC2PayloadBytes", sizeof("TC2PayloadBytes"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"TC2WindowSize", sizeof("TC2WindowSize"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"rtcp", sizeof("rtcp"),
     sdp_parse_attr_rtcp, sdp_build_attr_rtcp },
    {"rtr", sizeof("rtr"),
     sdp_parse_attr_rtr, sdp_build_attr_rtr},
    {"silenceSupp", sizeof("silenceSupp"),
     sdp_parse_attr_silencesupp, sdp_build_attr_silencesupp },
    {"X-crypto", sizeof("X-crypto"),
     sdp_parse_attr_srtpcontext, sdp_build_attr_srtpcontext },
    {"mptime", sizeof("mptime"),
      sdp_parse_attr_mptime, sdp_build_attr_mptime },
    {"X-sidin", sizeof("X-sidin"),
      sdp_parse_attr_x_sidin, sdp_build_attr_x_sidin },
    {"X-sidout", sizeof("X-sidout"),
      sdp_parse_attr_x_sidout, sdp_build_attr_x_sidout },
    {"X-confid", sizeof("X-confid"),
      sdp_parse_attr_x_confid, sdp_build_attr_x_confid },
    {"group", sizeof("group"),
      sdp_parse_attr_group, sdp_build_attr_group },
    {"mid", sizeof("mid"),
      sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"source-filter", sizeof("source-filter"),
      sdp_parse_attr_source_filter, sdp_build_source_filter},
    {"rtcp-unicast", sizeof("rtcp-unicast"),
      sdp_parse_attr_rtcp_unicast, sdp_build_attr_rtcp_unicast},
    {"maxprate", sizeof("maxprate"),
      sdp_parse_attr_maxprate, sdp_build_attr_simple_string},
    {"sqn", sizeof("sqn"),
     sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"cdsc", sizeof("cdsc"),
     sdp_parse_attr_cap, sdp_build_attr_cap },
    {"cpar", sizeof("cpar"),
     sdp_parse_attr_cpar, sdp_build_attr_cpar },
    {"sprtmap", sizeof("sprtmap"),
     sdp_parse_attr_transport_map, sdp_build_attr_transport_map },
    {"crypto", sizeof("crypto"),
     sdp_parse_attr_sdescriptions, sdp_build_attr_sdescriptions },
    {"label", sizeof("label"),
      sdp_parse_attr_simple_string, sdp_build_attr_simple_string },
    {"framerate", sizeof("framerate"),
      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32 },
    {"candidate", sizeof("candidate"),
      sdp_parse_attr_ice_attr, sdp_build_attr_ice_attr },
    {"ice-ufrag", sizeof("ice-ufrag"),
      sdp_parse_attr_ice_attr, sdp_build_attr_ice_attr },
    {"ice-pwd", sizeof("ice-pwd"),
      sdp_parse_attr_ice_attr, sdp_build_attr_ice_attr},
    {"ice-lite", sizeof("ice-lite"),
      sdp_parse_attr_simple_flag, sdp_build_attr_simple_flag},
    {"rtcp-mux", sizeof("rtcp-mux"),
      sdp_parse_attr_simple_flag, sdp_build_attr_simple_flag},
    {"fingerprint", sizeof("fingerprint"),
      sdp_parse_attr_complete_line, sdp_build_attr_simple_string},
    {"maxptime", sizeof("maxptime"),
      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32},
    {"rtcp-fb", sizeof("rtcp-fb"),
      sdp_parse_attr_rtcp_fb, sdp_build_attr_rtcp_fb},
    {"setup", sizeof("setup"),
      sdp_parse_attr_setup, sdp_build_attr_setup},
    {"connection", sizeof("connection"),
      sdp_parse_attr_connection, sdp_build_attr_connection},
    {"extmap", sizeof("extmap"),
      sdp_parse_attr_extmap, sdp_build_attr_extmap},
    {"identity", sizeof("identity"),
      sdp_parse_attr_long_line, sdp_build_attr_long_line},
    {"msid", sizeof("msid"),
      sdp_parse_attr_msid, sdp_build_attr_msid},
    {"msid-semantic", sizeof("msid-semantic"),
      sdp_parse_attr_msid_semantic, sdp_build_attr_msid_semantic},
    {"bundle-only", sizeof("bundle-only"),
      sdp_parse_attr_simple_flag, sdp_build_attr_simple_flag},
    {"end-of-candidates", sizeof("end-of-candidates"),
      sdp_parse_attr_simple_flag, sdp_build_attr_simple_flag},
    {"ice-options", sizeof("ice-options"),
      sdp_parse_attr_complete_line, sdp_build_attr_simple_string},
    {"ssrc", sizeof("ssrc"),
      sdp_parse_attr_ssrc, sdp_build_attr_ssrc},
    {"imageattr", sizeof("imageattr"),
      sdp_parse_attr_complete_line, sdp_build_attr_simple_string},
    {"simulcast", sizeof("simulcast"),
      sdp_parse_attr_complete_line, sdp_build_attr_simple_string},
    {"rid", sizeof("rid"),
      sdp_parse_attr_complete_line, sdp_build_attr_simple_string},
    {"dtls-message", sizeof("dtls-message"),
      sdp_parse_attr_long_line, sdp_build_attr_long_line},
    {"sctp-port", sizeof("sctp-port"),
      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32},
    {"max-message-size", sizeof("max-message-size"),
      sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32},
};

/* Note: These *must* be in the same order as the enum types. */
const sdp_namearray_t sdp_media[SDP_MAX_MEDIA_TYPES] =
{
    {"audio",        sizeof("audio")},
    {"video",        sizeof("video")},
    {"application",  sizeof("application")},
    {"data",         sizeof("data")},
    {"control",      sizeof("control")},
    {"nas/radius",   sizeof("nas/radius")},
    {"nas/tacacs",   sizeof("nas/tacacs")},
    {"nas/diameter", sizeof("nas/diameter")},
    {"nas/l2tp",     sizeof("nas/l2tp")},
    {"nas/login",    sizeof("nas/login")},
    {"nas/none",     sizeof("nas/none")},
    {"image",        sizeof("image")},
    {"text",         sizeof("text")}
};


/* Note: These *must* be in the same order as the enum types. */
const sdp_namearray_t sdp_nettype[SDP_MAX_NETWORK_TYPES] =
{
    {"IN",           sizeof("IN")},
    {"ATM",          sizeof("ATM")},
    {"FR",           sizeof("FR")},
    {"LOCAL",        sizeof("LOCAL")}
};


/* Note: These *must* be in the same order as the enum types. */
const sdp_namearray_t sdp_addrtype[SDP_MAX_ADDR_TYPES] =
{
    {"IP4",          sizeof("IP4")},
    {"IP6",          sizeof("IP6")},
    {"NSAP",         sizeof("NSAP")},
    {"EPN",          sizeof("EPN")},
    {"E164",         sizeof("E164")},
    {"GWID",         sizeof("GWID")}
};


/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_transport[SDP_MAX_TRANSPORT_TYPES] =
{
    {"RTP/AVP",      sizeof("RTP/AVP")},
    {"udp",          sizeof("udp")},
    {"udptl",        sizeof("udptl")},
    {"ces10",        sizeof("ces10")},
    {"LOCAL",        sizeof("LOCAL")},
    {"AAL2/ITU",     sizeof("AAL2/ITU")},
    {"AAL2/ATMF",    sizeof("AAL2/ATMF")},
    {"AAL2/custom",  sizeof("AAL2/custom")},
    {"AAL1/AVP",     sizeof("AAL1/AVP")},
    {"udpsprt",      sizeof("udpsprt")},
    {"RTP/SAVP",     sizeof("RTP/SAVP")},
    {"tcp",          sizeof("tcp")},
    {"RTP/SAVPF",    sizeof("RTP/SAVPF")},
    {"DTLS/SCTP",    sizeof("DTLS/SCTP")},
    {"RTP/AVPF",     sizeof("RTP/AVPF")},
    {"UDP/TLS/RTP/SAVP", sizeof("UDP/TLS/RTP/SAVP")},
    {"UDP/TLS/RTP/SAVPF", sizeof("UDP/TLS/RTP/SAVPF")},
    {"TCP/TLS/RTP/SAVP", sizeof("TCP/TLS/RTP/SAVP")},
    {"TCP/TLS/RTP/SAVPF", sizeof("TCP/TLS/RTP/SAVPF")},
    {"UDP/DTLS/SCTP",    sizeof("UDP/DTLS/SCTP")},
    {"TCP/DTLS/SCTP",    sizeof("TCP/DTLS/SCTP")},
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_encrypt[SDP_MAX_ENCRYPT_TYPES] =
{
    {"clear",        sizeof("clear")},
    {"base64",       sizeof("base64")},
    {"uri",          sizeof("uri")},
    {"prompt",       sizeof("prompt")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_payload[SDP_MAX_STRING_PAYLOAD_TYPES] =
{
    {"t38",          sizeof("t38")},
    {"X-tmr",        sizeof("X-tmr")},
    {"T120",         sizeof("T120")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_t38_rate[SDP_T38_MAX_RATES] =
{
    {"localTCF",        sizeof("localTCF")},
    {"transferredTCF",  sizeof("transferredTCF")},
    {"unknown",         sizeof("unknown")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_t38_udpec[SDP_T38_MAX_UDPEC] =
{
    {"t38UDPRedundancy",  sizeof("t38UDPRedundancy")},
    {"t38UDPFEC",         sizeof("t38UDPFEC")},
    {"unknown",           sizeof("unknown")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_qos_strength[SDP_MAX_QOS_STRENGTH] =
{
    {"optional",          sizeof("optional")},
    {"mandatory",         sizeof("mandatory")},
    {"success",           sizeof("success")},
    {"failure",           sizeof("failure")},
    {"none",              sizeof("none")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_qos_status_type[SDP_MAX_QOS_STATUS_TYPES] =
{
    {"local",          sizeof("local")},
    {"remote",         sizeof("remote")},
    {"e2e",            sizeof("e2e")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_curr_type[SDP_MAX_CURR_TYPES] =
{
    {"qos",            sizeof("qos")},
    {"unknown",         sizeof("unknown")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_des_type[SDP_MAX_DES_TYPES] =
{
    {"qos",            sizeof("qos")},
    {"unknown",         sizeof("unknown")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_conf_type[SDP_MAX_CONF_TYPES] =
{
    {"qos",            sizeof("qos")},
    {"unknown",         sizeof("unknown")}
};
/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_qos_direction[SDP_MAX_QOS_DIR] =
{
    {"send",              sizeof("send")},
    {"recv",              sizeof("recv")},
    {"sendrecv",          sizeof("sendrecv")},
    {"none",              sizeof("none")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_silencesupp_pref[SDP_MAX_SILENCESUPP_PREF] = {
    {"standard",          sizeof("standard")},
    {"custom",            sizeof("custom")},
    {"-",                 sizeof("-")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_silencesupp_siduse[SDP_MAX_SILENCESUPP_SIDUSE] = {
    {"No SID",            sizeof("No SID")},
    {"Fixed Noise",       sizeof("Fixed Noise")},
    {"Sampled Noise",     sizeof("Sampled Noise")},
    {"-",                 sizeof("-")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_mediadir_role[SDP_MAX_MEDIADIR_ROLES] =
{
    {"passive",       sizeof("passive")},
    {"active",        sizeof("active")},
    {"both",          sizeof("both")},
    {"reuse",         sizeof("reuse")},
    {"unknown",       sizeof("unknown")}
};

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_fmtp_codec_param[SDP_MAX_FMTP_PARAM] =
{
    {"annexa",              sizeof("annexa")}, /* 0 */
    {"annexb",              sizeof("annexb")}, /* 1 */
    {"bitrate",             sizeof("bitrate")}, /* 2 */
    {"QCIF",                sizeof("QCIF")}, /* 3 */
    {"CIF",                 sizeof("CIF")},  /* 4 */
    {"MAXBR",               sizeof("MAXBR")}, /* 5 */
    {"SQCIF",               sizeof("SQCIF")}, /* 6 */
    {"CIF4",                sizeof("CIF4")}, /* 7 */
    {"CIF16",               sizeof("CIF16")}, /* 8 */
    {"CUSTOM",              sizeof("CUSTOM")}, /* 9 */
    {"PAR",                 sizeof("PAR")}, /* 10 */
    {"CPCF",                sizeof("CPCF")}, /* 11 */
    {"BPP",                 sizeof("BPP")}, /* 12 */
    {"HRD",                 sizeof("HRD")}, /* 13 */
    {"PROFILE",             sizeof("PROFILE")}, /* 14 */
    {"LEVEL",               sizeof("LEVEL")}, /* 15 */
    {"INTERLACE",           sizeof("INTERLACE")}, /* 16 */

    /* H.264 related */
    {"profile-level-id",      sizeof("profile-level-id")}, /* 17 */
    {"sprop-parameter-sets",  sizeof("sprop-parameter-sets")}, /* 18 */
    {"packetization-mode",    sizeof("packetization-mode")}, /* 19 */
    {"sprop-interleaving-depth",    sizeof("sprop-interleaving-depth")}, /* 20 */
    {"sprop-deint-buf-req",   sizeof("sprop-deint-buf-req")}, /* 21 */
    {"sprop-max-don-diff",    sizeof("sprop-max-don-diff")}, /* 22 */
    {"sprop-init-buf-time",   sizeof("sprop-init-buf-time")}, /* 23 */

    {"max-mbps",              sizeof("max-mbps")}, /* 24 */
    {"max-fs",                sizeof("max-fs")}, /* 25 */
    {"max-cpb",               sizeof("max-cpb")}, /* 26 */
    {"max-dpb",               sizeof("max-dpb")}, /* 27 */
    {"max-br",                sizeof("max-br")}, /* 28 */
    {"redundant-pic-cap",     sizeof("redundant-pic-cap")}, /* 29 */
    {"deint-buf-cap",         sizeof("deint-buf-cap")}, /* 30 */
    {"max-rcmd-nalu-size",    sizeof("max-rcmd_nali-size")}, /* 31 */
    {"parameter-add",         sizeof("parameter-add")}, /* 32 */

    /* Annexes - require special handling */
     {"D", sizeof("D")}, /* 33 */
     {"F", sizeof("F")}, /* 34 */
     {"I", sizeof("I")}, /* 35 */
     {"J", sizeof("J")}, /* 36 */
     {"T", sizeof("T")}, /* 37 */
     {"K", sizeof("K")}, /* 38 */
     {"N", sizeof("N")}, /* 39 */
     {"P", sizeof("P")}, /* 40 */

     {"mode",                sizeof("mode")},  /* 41 */
    {"level-asymmetry-allowed",         sizeof("level-asymmetry-allowed")}, /* 42 */
    {"maxaveragebitrate",               sizeof("maxaveragebitrate")}, /* 43 */
    {"usedtx",                          sizeof("usedtx")}, /* 44 */
    {"stereo",                          sizeof("stereo")}, /* 45 */
    {"useinbandfec",                    sizeof("useinbandfec")}, /* 46 */
    {"maxcodedaudiobandwidth",          sizeof("maxcodedaudiobandwidth")}, /* 47 */
    {"cbr",                             sizeof("cbr")}, /* 48 */
    {"max-fr",                          sizeof("max-fr")}, /* 49 */
    {"maxplaybackrate",                 sizeof("maxplaybackrate")} /* 50 */
} ;

/* Note: These *must* be in the same order as the enum type. */
const sdp_namearray_t sdp_fmtp_codec_param_val[SDP_MAX_FMTP_PARAM_VAL] =
{
    {"yes",                 sizeof("yes")},
    {"no",                  sizeof("no")}
};

const sdp_namearray_t sdp_bw_modifier_val[SDP_MAX_BW_MODIFIER_VAL] =
{
    {"AS",                  sizeof("AS")},
    {"CT",                  sizeof("CT")},
    {"TIAS",                sizeof("TIAS")}
};

const sdp_namearray_t sdp_group_attr_val[SDP_MAX_GROUP_ATTR_VAL] =
{
    {"FID",                 sizeof("FID")},
    {"LS",                  sizeof("LS")},
    {"ANAT",                sizeof("ANAT")},
    {"BUNDLE",              sizeof("BUNDLE")}
};

const sdp_namearray_t sdp_srtp_context_crypto_suite[SDP_SRTP_MAX_NUM_CRYPTO_SUITES] =
{
  {"UNKNOWN_CRYPTO_SUITE",    sizeof("UNKNOWN_CRYPTO_SUITE")},
  {"AES_CM_128_HMAC_SHA1_32", sizeof("AES_CM_128_HMAC_SHA1_32")},
  {"AES_CM_128_HMAC_SHA1_80", sizeof("AES_CM_128_HMAC_SHA1_80")},
  {"F8_128_HMAC_SHA1_80", sizeof("F8_128_HMAC_SHA1_80")}
};

/* Maintain the same order as defined in typedef sdp_src_filter_mode_e */
const sdp_namearray_t sdp_src_filter_mode_val[SDP_MAX_FILTER_MODE] =
{
    {"incl", sizeof("incl")},
    {"excl", sizeof("excl")}
};

/* Maintain the same order as defined in typdef sdp_rtcp_unicast_mode_e */
const sdp_namearray_t sdp_rtcp_unicast_mode_val[SDP_RTCP_MAX_UNICAST_MODE] =
{
    {"reflection", sizeof("reflection")},
    {"rsi",        sizeof("rsi")}
};

#define SDP_NAME(x) {x, sizeof(x)}
/* Maintain the same order as defined in typdef sdp_rtcp_fb_type_e */
const sdp_namearray_t sdp_rtcp_fb_type_val[SDP_MAX_RTCP_FB] =
{
    SDP_NAME("ack"),
    SDP_NAME("ccm"),
    SDP_NAME("nack"),
    SDP_NAME("trr-int"),
    SDP_NAME("goog-remb")
};

/* Maintain the same order as defined in typdef sdp_rtcp_fb_nack_type_e */
const sdp_namearray_t sdp_rtcp_fb_nack_type_val[SDP_MAX_RTCP_FB_NACK] =
{
    SDP_NAME(""),
    SDP_NAME("sli"),
    SDP_NAME("pli"),
    SDP_NAME("rpsi"),
    SDP_NAME("app"),
    SDP_NAME("rai"),
    SDP_NAME("tllei"),
    SDP_NAME("pslei"),
    SDP_NAME("ecn")
};

/* Maintain the same order as defined in typdef sdp_rtcp_fb_ack_type_e */
const sdp_namearray_t sdp_rtcp_fb_ack_type_val[SDP_MAX_RTCP_FB_ACK] =
{
    SDP_NAME("rpsi"),
    SDP_NAME("app")
};

/* Maintain the same order as defined in typdef sdp_rtcp_fb_ccm_type_e */
const sdp_namearray_t sdp_rtcp_fb_ccm_type_val[SDP_MAX_RTCP_FB_CCM] =
{
    SDP_NAME("fir"),
    SDP_NAME("tmmbr"),
    SDP_NAME("tstr"),
    SDP_NAME("vbcm")
};

/* Maintain the same order as defined in typedef sdp_setup_type_e */
const sdp_namearray_t sdp_setup_type_val[SDP_MAX_SETUP] =
{
    SDP_NAME("active"),
    SDP_NAME("passive"),
    SDP_NAME("actpass"),
    SDP_NAME("holdconn")
};

/* Maintain the same order as defined in typedef sdp_connection_type_e */
const sdp_namearray_t sdp_connection_type_val[SDP_MAX_CONNECTION] =
{
    SDP_NAME("new"),
    SDP_NAME("existing")
};

/*  Maintain same order as defined in typedef sdp_srtp_crypto_suite_t */
const sdp_srtp_crypto_suite_list sdp_srtp_crypto_suite_array[SDP_SRTP_MAX_NUM_CRYPTO_SUITES] =
{
  {SDP_SRTP_UNKNOWN_CRYPTO_SUITE, UNKNOWN_CRYPTO_SUITE, 0, 0},
  {SDP_SRTP_AES_CM_128_HMAC_SHA1_32, AES_CM_128_HMAC_SHA1_32,
      SDP_SRTP_AES_CM_128_HMAC_SHA1_32_KEY_BYTES,
      SDP_SRTP_AES_CM_128_HMAC_SHA1_32_SALT_BYTES},
  {SDP_SRTP_AES_CM_128_HMAC_SHA1_80, AES_CM_128_HMAC_SHA1_80,
      SDP_SRTP_AES_CM_128_HMAC_SHA1_80_KEY_BYTES,
      SDP_SRTP_AES_CM_128_HMAC_SHA1_80_SALT_BYTES},
  {SDP_SRTP_F8_128_HMAC_SHA1_80, F8_128_HMAC_SHA1_80,
      SDP_SRTP_F8_128_HMAC_SHA1_80_KEY_BYTES,
      SDP_SRTP_F8_128_HMAC_SHA1_80_SALT_BYTES}
};

const char* sdp_result_name[SDP_MAX_RC] =
    {"SDP_SUCCESS",
     "SDP_FAILURE",
     "SDP_INVALID_SDP_PTR",
     "SDP_NOT_SDP_DESCRIPTION",
     "SDP_INVALID_TOKEN_ORDERING",
     "SDP_INVALID_PARAMETER",
     "SDP_INVALID_MEDIA_LEVEL",
     "SDP_INVALID_CAPABILITY",
     "SDP_NO_RESOURCE",
     "SDP_UNRECOGNIZED_TOKEN",
     "SDP_NULL_BUF_PTR",
     "SDP_POTENTIAL_SDP_OVERFLOW",
     "SDP_EMPTY_TOKEN"};

const char *sdp_get_result_name ( sdp_result_e rc )
{
    if (rc >= SDP_MAX_RC) {
        return ("Invalid SDP result code");
    } else {
        return (sdp_result_name[rc]);
    }
}

const char *sdp_get_attr_name ( sdp_attr_e attr_type )
{
    if (attr_type >= SDP_MAX_ATTR_TYPES) {
        return ("Invalid attribute type");
    } else {
        return (sdp_attr[attr_type].name);
    }
}

const char *sdp_get_media_name ( sdp_media_e media_type )
{
    if (media_type == SDP_MEDIA_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (media_type >= SDP_MAX_MEDIA_TYPES) {
        return ("Invalid media type");
    } else {
        return (sdp_media[media_type].name);
    }
}

const char *sdp_get_network_name ( sdp_nettype_e network_type )
{
    if (network_type == SDP_NT_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (network_type >= SDP_MAX_NETWORK_TYPES) {
        return ("Invalid network type");
    } else {
        return (sdp_nettype[network_type].name);
    }
}

const char *sdp_get_address_name ( sdp_addrtype_e addr_type )
{
    if (addr_type == SDP_AT_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (addr_type >= SDP_MAX_ADDR_TYPES) {
        if (addr_type == SDP_AT_FQDN) {
            return ("*");
        } else {
            return ("Invalid address type");
        }
    } else {
        return (sdp_addrtype[addr_type].name);
    }
}

const char *sdp_get_transport_name ( sdp_transport_e transport_type )
{
    if (transport_type == SDP_TRANSPORT_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (transport_type >= SDP_MAX_TRANSPORT_TYPES) {
        return ("Invalid transport type");
    } else {
        return (sdp_transport[transport_type].name);
    }
}

const char *sdp_get_encrypt_name ( sdp_encrypt_type_e encrypt_type )
{
    if (encrypt_type == SDP_ENCRYPT_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (encrypt_type >= SDP_MAX_ENCRYPT_TYPES) {
        return ("Invalid encryption type");
    } else {
        return (sdp_encrypt[encrypt_type].name);
    }
}

const char *sdp_get_payload_name ( sdp_payload_e payload )
{
    if (payload == SDP_PAYLOAD_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (payload >= SDP_MAX_STRING_PAYLOAD_TYPES) {
        return ("Invalid payload type");
    } else {
        return (sdp_payload[payload].name);
    }
}

const char *sdp_get_t38_ratemgmt_name ( sdp_t38_ratemgmt_e rate )
{
    if (rate >= SDP_T38_MAX_RATES) {
        return ("Invalid rate");
    } else {
        return (sdp_t38_rate[rate].name);
    }
}

const char *sdp_get_t38_udpec_name ( sdp_t38_udpec_e udpec )
{
    if (udpec >= SDP_T38_MAX_UDPEC) {
        return ("Invalid udpec");
    } else {
        return (sdp_t38_udpec[udpec].name);
    }
}

const char *sdp_get_qos_strength_name ( sdp_qos_strength_e strength )
{
    if (strength >= SDP_MAX_QOS_STRENGTH) {
        return ("Invalid qos strength");
    } else {
        return (sdp_qos_strength[strength].name);
    }
}

const char *sdp_get_qos_direction_name ( sdp_qos_dir_e direction )
{
    if (direction >= SDP_MAX_QOS_DIR) {
        return ("Invalid qos direction");
    } else {
        return (sdp_qos_direction[direction].name);
    }
}

const char *sdp_get_qos_status_type_name ( sdp_qos_status_types_e status_type )
{
    if (status_type >= SDP_MAX_QOS_STATUS_TYPES) {
        return ("Invalid qos status type");
    } else {
        return (sdp_qos_status_type[status_type].name);
    }
}

const char *sdp_get_curr_type_name (sdp_curr_type_e curr_type )
{
    if (curr_type >= SDP_MAX_CURR_TYPES) {
        return ("Invalid curr type");
    } else {
        return (sdp_curr_type[curr_type].name);
    }
}

const char *sdp_get_des_type_name (sdp_des_type_e des_type )
{
    if (des_type >= SDP_MAX_DES_TYPES) {
        return ("Invalid des type");
    } else {
        return (sdp_des_type[des_type].name);
    }
}

const char *sdp_get_conf_type_name (sdp_conf_type_e conf_type )
{
    if (conf_type >= SDP_MAX_CONF_TYPES) {
        return ("Invalid conf type");
    } else {
        return (sdp_conf_type[conf_type].name);
    }
}

const char *sdp_get_silencesupp_pref_name (sdp_silencesupp_pref_e pref)
{
    if (pref >= SDP_MAX_SILENCESUPP_PREF) {
        return ("Invalid silencesupp pref");
    } else {
        return (sdp_silencesupp_pref[pref].name);
    }
}

const char *sdp_get_silencesupp_siduse_name (sdp_silencesupp_siduse_e siduse)
{
    if (siduse >= SDP_MAX_SILENCESUPP_SIDUSE) {
        return ("Invalid silencesupp siduse");
    } else {
        return (sdp_silencesupp_siduse[siduse].name);
    }
}

const char *sdp_get_mediadir_role_name (sdp_mediadir_role_e role)
{
    if (role >= SDP_MEDIADIR_ROLE_UNKNOWN) {
        return ("Invalid media direction role");
    } else {
        return (sdp_mediadir_role[role].name);
    }
}


const char *sdp_get_bw_modifier_name (sdp_bw_modifier_e bw_modifier_type)
{
    if (bw_modifier_type == SDP_BW_MODIFIER_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (bw_modifier_type < SDP_BW_MODIFIER_AS ||
            bw_modifier_type >= SDP_MAX_BW_MODIFIER_VAL) {
        return ("Invalid bw modifier type");
    } else {
        return (sdp_bw_modifier_val[bw_modifier_type].name);
    }
}

const char *sdp_get_group_attr_name (sdp_group_attr_e group_attr_type)
{
    if (group_attr_type == SDP_GROUP_ATTR_UNSUPPORTED) {
        return (SDP_UNSUPPORTED);
    } else if (group_attr_type >= SDP_MAX_GROUP_ATTR_VAL) {
        return ("Invalid a=group: attribute type");
    } else {
        return (sdp_group_attr_val[group_attr_type].name);
    }
}

const char *sdp_get_src_filter_mode_name (sdp_src_filter_mode_e type)
{
    if (type >= SDP_MAX_FILTER_MODE) {
        return ("Invalid source filter mode");
    } else {
        return (sdp_src_filter_mode_val[type].name);
    }
}

const char *sdp_get_rtcp_unicast_mode_name (sdp_rtcp_unicast_mode_e type)
{
    if (type >= SDP_RTCP_MAX_UNICAST_MODE) {
        return ("Invalid rtcp unicast mode");
    } else {
        return (sdp_rtcp_unicast_mode_val[type].name);
    }
}

/* Function:    sdp_init_description
 * Description:	Allocates a new SDP structure that can be used for either
 *              parsing or building an SDP description.  This routine
 *              saves the config pointer passed in the SDP structure so
 *              SDP will know how to parse/build based on the options defined.
 *              An SDP structure must be allocated before parsing or building
 *              since the handle must be passed to these routines.
 * Parameters:  config_p     The config handle returned by sdp_init_config
 * Returns:     A handle for a new SDP structure as a void ptr.
*/
sdp_t *sdp_init_description (sdp_conf_options_t *conf_p)
{
    int i;
    sdp_t *sdp_p;

    if (!conf_p) {
        return (NULL);
    }

    sdp_p = (sdp_t *)SDP_MALLOC(sizeof(sdp_t));
    if (sdp_p == NULL) {
        return (NULL);
    }

    sdp_p->conf_p             = conf_p;
    sdp_p->version            = SDP_CURRENT_VERSION;
    sdp_p->owner_name[0]      = '\0';
    sdp_p->owner_sessid[0]    = '\0';
    sdp_p->owner_version[0]   = '\0';
    sdp_p->owner_network_type = SDP_NT_INVALID;
    sdp_p->owner_addr_type    = SDP_AT_INVALID;
    sdp_p->owner_addr[0]      = '\0';
    sdp_p->sessname[0]        = '\0';
    sdp_p->sessinfo_found     = FALSE;
    sdp_p->uri_found          = FALSE;

    sdp_p->default_conn.nettype      = SDP_NT_INVALID;
    sdp_p->default_conn.addrtype     = SDP_AT_INVALID;
    sdp_p->default_conn.conn_addr[0] = '\0';
    sdp_p->default_conn.is_multicast = FALSE;
    sdp_p->default_conn.ttl          = 0;
    sdp_p->default_conn.num_of_addresses = 0;

    sdp_p->bw.bw_data_count   = 0;
    sdp_p->bw.bw_data_list    = NULL;

    sdp_p->timespec_p         = NULL;
    sdp_p->sess_attrs_p       = NULL;
    sdp_p->mca_p              = NULL;
    sdp_p->mca_count          = 0;

    /* Set default debug flags from application config. */
    for (i=0; i < SDP_MAX_DEBUG_TYPES; i++) {
        sdp_p->debug_flag[i] = conf_p->debug_flag[i];
    }

    return (sdp_p);
}


/* Function:    void sdp_debug(sdp_t *sdp_p, sdp_debug_e debug_type,
 *                             tinybool my_bool);
 * Description:	Define the type of debug for this particular SDP structure.
 *              By default, each SDP description has the settings that are
 *              set for the application.
 *              Valid debug types are ERRORS, WARNINGS, and TRACE.  Each
 *              debug type can be turned on/off individually.  The
 *              debug level can be redefined at any time.
 * Parameters:	sdp_ptr    The SDP handle returned by sdp_init_description.
 *              debug_type Specifies the debug type being enabled/disabled.
 *              my_bool    Defines whether the debug should be enabled or not.
 * Returns:	Nothing.
 */
void sdp_debug (sdp_t *sdp_p, sdp_debug_e debug_type, tinybool debug_flag)
{
    if (!sdp_p) {
        return;
    }

    if (debug_type < SDP_MAX_DEBUG_TYPES)  {
        sdp_p->debug_flag[debug_type] = debug_flag;
    }
}


/* Function:    void sdp_set_string_debug(sdp_t *sdp_p, char *debug_str)
 * Description: Define a string to be associated with all debug output
 *              for this SDP. The string will be copied into the SDP
 *              structure and so the library will not be dependent on
 *              the application's memory for this string.
 * Parameters:  sdp_p      The SDP handle returned by sdp_init_description.
 *              debug_str  Pointer to a string that should be printed out
 *                         with every debug msg.
 * Returns:     Nothing.
 */
void sdp_set_string_debug (sdp_t *sdp_p, const char *debug_str)
{
    if (!sdp_p) {
        return;
    }

    sstrncpy(sdp_p->debug_str, debug_str, sizeof(sdp_p->debug_str));
}


/* Function:    sdp_validate_sdp
 * Description: Validate an SDP structure.
 * Parameters:  sdp_p    The SDP handle of the struct to validate.
 * Returns:     A result value indicating if the validation was successful.
 *              If not, what type of error was encountered.
 */
sdp_result_e sdp_validate_sdp (sdp_t *sdp_p)
{
    int i;
    uint16_t num_media_levels;

    /* Need to validate c= info is specified at session level or
     * at all m= levels.
     */
    if (sdp_connection_valid((void *)sdp_p, SDP_SESSION_LEVEL) == FALSE) {
        num_media_levels = sdp_get_num_media_lines((void *)sdp_p);
        for (i=1; i <= num_media_levels; i++) {
            if (sdp_connection_valid((void *)sdp_p, (unsigned short)i) == FALSE) {
                sdp_parse_error(sdp_p,
                    "%s c= connection line not specified for "
                    "every media level, validation failed.",
                    sdp_p->debug_str);
                return (SDP_FAILURE);
            }
        }
    }

    /* Validate required lines were specified */
    if ((sdp_owner_valid((void *)sdp_p) == FALSE) &&
        (sdp_p->conf_p->owner_reqd == TRUE)) {
        sdp_parse_error(sdp_p,
            "%s o= owner line not specified, validation failed.",
            sdp_p->debug_str);
        return (SDP_FAILURE);
    }

    if ((sdp_session_name_valid((void *)sdp_p) == FALSE) &&
        (sdp_p->conf_p->session_name_reqd == TRUE)) {
        sdp_parse_error(sdp_p,
            "%s s= session name line not specified, validation failed.",
            sdp_p->debug_str);
        return (SDP_FAILURE);
    }

    if ((sdp_timespec_valid((void *)sdp_p) == FALSE) &&
        (sdp_p->conf_p->timespec_reqd == TRUE)) {
        sdp_parse_error(sdp_p,
            "%s t= timespec line not specified, validation failed.",
            sdp_p->debug_str);
        return (SDP_FAILURE);
    }

    return (SDP_SUCCESS);
}

/* Function:    sdp_parse
 * Description: Parse an SDP description in the specified buffer.
 * Parameters:  sdp_p    The SDP handle returned by sdp_init_description
 *              bufp     Pointer to the buffer containing the SDP
 *                       description to parse.
 *              len      The length of the buffer.
 * Returns:     A result value indicating if the parse was successful and
 *              if not, what type of error was encountered.  The
 *              information from the parse is stored in the sdp_p structure.
 */
sdp_result_e sdp_parse (sdp_t *sdp_p, const char *buf, size_t len)
{
    uint8_t           i;
    uint16_t          cur_level = SDP_SESSION_LEVEL;
    const char  *ptr;
    const char  *next_ptr = NULL;
    char        *line_end;
    sdp_token_e  last_token = SDP_TOKEN_V;
    sdp_result_e result = SDP_SUCCESS;
    tinybool     parse_done = FALSE;
    tinybool     end_found = FALSE;
    tinybool     first_line = TRUE;
    tinybool     unrec_token = FALSE;
    const char   **bufp = &buf;

    if (!sdp_p) {
        return (SDP_INVALID_SDP_PTR);
    }

    if ((bufp == NULL) || (*bufp == NULL)) {
        return (SDP_NULL_BUF_PTR);
    }

    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
        SDP_PRINT("%s Trace SDP Parse:", sdp_p->debug_str);
    }

    next_ptr = *bufp;
    sdp_p->conf_p->num_parses++;

    /* Initialize the last valid capability instance to zero.  Used
     * to help in parsing X-cpar attrs. */
    sdp_p->cap_valid = FALSE;
    sdp_p->last_cap_inst = 0;

    sdp_p->parse_line = 0;

    /* We want to try to find the end of the SDP description, even if
     * we find a parsing error.
     */
    while (!end_found) {
        /* If the last char of this line goes beyond the end of the buffer,
         * we don't parse it.
         */
        ptr = next_ptr;
        sdp_p->parse_line++;
        line_end = sdp_findchar(ptr, "\n");
        if ((line_end >= (*bufp + len)) ||
           (*line_end == '\0')) {
            /* As this does not update the result value the SDP up to this point
             * is still accept as valid. So encountering this is not treated as
             * an error.
             */
            sdp_parse_error(sdp_p,
                "%s End of line beyond end of buffer.",
                sdp_p->debug_str);
            CSFLogError(logTag, "SDP: Invalid SDP, no \\n (len %u): %*s",
                        (unsigned)len, (int)len, *bufp);
            end_found = TRUE;
            break;
        }

        /* Print the line if we're tracing. */
        if ((parse_done == FALSE) &&
          (sdp_p->debug_flag[SDP_DEBUG_TRACE])) {
            SDP_PRINT("%s ", sdp_p->debug_str);

            SDP_PRINT("%*s", (int)(line_end - ptr), ptr);

        }

        /* Find out which token this line has, if any. */
        for (i=0; i < SDP_MAX_TOKENS; i++) {
            if (strncmp(ptr, sdp_token[i].name, SDP_TOKEN_LEN) == 0) {
                break;
            }
        }
        if (i == SDP_MAX_TOKENS) {
            /* See if the second char on the next line is an '=' char.
             * If so, we note this as an unrecognized token line. */
            if (ptr[1] == '=') {
                unrec_token = TRUE;
            }
            if (first_line == TRUE) {
                sdp_parse_error(sdp_p,
                    "%s Attempt to parse text not recognized as "
                    "SDP text, parse fails.", sdp_p->debug_str);
                    /* If we haven't already printed out the line we
                     * were trying to parse, do it now.
                     */
                if (!sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
                    SDP_PRINT("%s ", sdp_p->debug_str);
                    SDP_PRINT("%*s", (int)(line_end - ptr), ptr);
                }
                sdp_p->conf_p->num_not_sdp_desc++;
                return (SDP_NOT_SDP_DESCRIPTION);
            } else {
                end_found = TRUE;
                break;
            }
        }

        /* This is the beginning of a new SDP description. */
        if ((first_line != TRUE) && (i == SDP_TOKEN_V)) {
            end_found = TRUE;
            break;
        }

        /* Advance the next ptr to one char beyond the end of the line. */
        next_ptr = line_end + 1;
        if (next_ptr >= (*bufp + len)) {
            end_found = TRUE;
        }

        /* If we've finished parsing and are just looking for the end of
         * the SDP description, we don't need to do anything else here.
         */
        if (parse_done == TRUE) {
            continue;
        }

        /* Only certain tokens are valid at the media level. */
        if (cur_level != SDP_SESSION_LEVEL) {
            if ((i != SDP_TOKEN_I) && (i != SDP_TOKEN_C) &&
                (i != SDP_TOKEN_B) && (i != SDP_TOKEN_K) &&
                (i != SDP_TOKEN_A) && (i != SDP_TOKEN_M)) {
                sdp_p->conf_p->num_invalid_token_order++;
                sdp_parse_error(sdp_p,
                    "%s Warning: Invalid token %s found at media level",
                    sdp_p->debug_str, sdp_token[i].name);
                continue;
            }
        }

        /* Verify the token ordering. */
        if (first_line == TRUE) {
            if (i != SDP_TOKEN_V) {
                if (sdp_p->conf_p->version_reqd == TRUE) {
                    sdp_parse_error(sdp_p,
                        "%s First line not v=, parse fails",
                        sdp_p->debug_str);
                    sdp_p->conf_p->num_invalid_token_order++;
                    result = SDP_INVALID_TOKEN_ORDERING;
                    parse_done = TRUE;
                } else {
                    last_token = (sdp_token_e)i;
                }
            } else {
                last_token = (sdp_token_e)i;
            }
            first_line = FALSE;
        } else {
            if (i < last_token) {
                sdp_p->conf_p->num_invalid_token_order++;
                sdp_parse_error(sdp_p,
                    "%s Warning: Invalid token ordering detected, "
                    "token %s found after token %s", sdp_p->debug_str,
                    sdp_token[i].name, sdp_token[last_token].name);
            }
        }

        /* Finally parse the line. */
        ptr += SDP_TOKEN_LEN;
        result = sdp_token[i].parse_func(sdp_p, cur_level, (const char *)ptr);
        last_token = (sdp_token_e)i;
        if (last_token == SDP_TOKEN_M) {
            if (cur_level == SDP_SESSION_LEVEL) {
                cur_level = 1;
            } else {
                cur_level++;
            }
            /* The token ordering can start again at i= */
            last_token = (sdp_token_e)(SDP_TOKEN_I - 1);
        }
        if (result != SDP_SUCCESS) {
            parse_done = TRUE;
        }

        /* Skip the new line char at the end of this line and see if
         * this is the end of the buffer.
         */
        if ((line_end + 1) == (*bufp + len)) {
            end_found = TRUE;
        }
    }

    /* If we found no valid lines, return an error. */
    if (first_line == TRUE) {
        sdp_p->conf_p->num_not_sdp_desc++;
        return (SDP_NOT_SDP_DESCRIPTION);
    }

    /* If no errors were found yet, validate the overall sdp. */
    if (result == SDP_SUCCESS) {
        result = sdp_validate_sdp(sdp_p);
    }
    /* Return the pointer where we left off. */
    *bufp = next_ptr;
    /* If the SDP is valid, but the next line following was an
     * unrecognized <token>= line, indicate this on the return. */
    if ((result == SDP_SUCCESS) && (unrec_token == TRUE)) {
        return (SDP_UNRECOGNIZED_TOKEN);
    } else {
        return (result);
    }
}


/* Function:    sdp_build
 * Description: Build an SDP description in the specified buffer based
 *              on the information in the given SDP structure.
 * Parameters:  sdp_p    The SDP handle returned by sdp_init_description
 *              fs A flex_string where the SDP description should be built.
 * Returns:     A result value indicating if the build was successful and
 *              if not, what type of error was encountered - e.g.,
 *              description was too long for the given buffer.
 */
sdp_result_e sdp_build (sdp_t *sdp_p, flex_string *fs)
{
    int i, j;
    sdp_result_e        result = SDP_SUCCESS;

    if (!sdp_p) {
        return (SDP_INVALID_SDP_PTR);
    }

    if (!fs) {
        return (SDP_NULL_BUF_PTR);
    }

    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
        SDP_PRINT("%s Trace SDP Build:", sdp_p->debug_str);
    }

    sdp_p->conf_p->num_builds++;

    for (i=0; ((i < SDP_TOKEN_M) &&
               (result == SDP_SUCCESS)); i++) {
        result = sdp_token[i].build_func(sdp_p, SDP_SESSION_LEVEL, fs);
        /* ok not to check buffer space (yet) as the if() checks it */
    }
    /* If the session level was ok, build the media lines. */
    if (result == SDP_SUCCESS) {
        for (i=1; ((i <= sdp_p->mca_count) &&
                   (result == SDP_SUCCESS)); i++) {
            result = sdp_token[SDP_TOKEN_M].build_func(sdp_p, (uint16_t)i, fs);

            /* ok not to check buffer space (yet) as the for() checks it */
            for (j=SDP_TOKEN_I;
                 ((j < SDP_TOKEN_M) && (result == SDP_SUCCESS));
                 j++) {
                if ((j == SDP_TOKEN_U) || (j == SDP_TOKEN_E) ||
                    (j == SDP_TOKEN_P) || (j == SDP_TOKEN_T) ||
                    (j == SDP_TOKEN_R) || (j == SDP_TOKEN_Z)) {
                    /* These tokens not valid at media level. */
                    continue;
                }
                result = sdp_token[j].build_func(sdp_p, (uint16_t)i, fs);
                /* ok not to check buffer space (yet) as the for() checks it */
            }
        }
    }

    return (result);
}

/* Function:    sdp_free_description
 * Description:	Free an SDP description and all memory associated with it.
 * Parameters:  sdp_p  The SDP handle returned by sdp_init_description
 * Returns:     A result value indicating if the free was successful and
 *              if not, what type of error was encountered - e.g., sdp_p
 *              was invalid and didn't point to an SDP structure.
*/
sdp_result_e sdp_free_description (sdp_t *sdp_p)
{
    sdp_timespec_t  *time_p, *next_time_p;
    sdp_attr_t      *attr_p, *next_attr_p;
    sdp_mca_t       *mca_p, *next_mca_p;
    sdp_bw_t        *bw_p;
    sdp_bw_data_t   *bw_data_p;

    if (!sdp_p) {
        return (SDP_INVALID_SDP_PTR);
    }

    /* Free the config structure */
    sdp_free_config(sdp_p->conf_p);

    /* Free any timespec structures - should be only one since
     * this is all we currently support.
     */
    time_p = sdp_p->timespec_p;
    while (time_p != NULL) {
        next_time_p = time_p->next_p;
        SDP_FREE(time_p);
        time_p = next_time_p;
    }

    bw_p = &(sdp_p->bw);
    bw_data_p = bw_p->bw_data_list;
    while (bw_data_p != NULL) {
        bw_p->bw_data_list = bw_data_p->next_p;
        SDP_FREE(bw_data_p);
        bw_data_p = bw_p->bw_data_list;
    }

    /* Free any session attr structures */
    attr_p = sdp_p->sess_attrs_p;
    while (attr_p != NULL) {
        next_attr_p = attr_p->next_p;
        sdp_free_attr(attr_p);
        attr_p = next_attr_p;
    }

    /* Free any mca structures */
    mca_p = sdp_p->mca_p;
    while (mca_p != NULL) {
        next_mca_p = mca_p->next_p;

        /* Free any media attr structures */
        attr_p = mca_p->media_attrs_p;
        while (attr_p != NULL) {
            next_attr_p = attr_p->next_p;
            sdp_free_attr(attr_p);
            attr_p = next_attr_p;
        }

        /* Free the media profiles struct if allocated. */
        if (mca_p->media_profiles_p != NULL) {
            SDP_FREE(mca_p->media_profiles_p);
        }

        bw_p = &(mca_p->bw);
        bw_data_p = bw_p->bw_data_list;
        while (bw_data_p != NULL) {
            bw_p->bw_data_list = bw_data_p->next_p;
            SDP_FREE(bw_data_p);
            bw_data_p = bw_p->bw_data_list;
        }

        SDP_FREE(mca_p);
        mca_p = next_mca_p;
    }

    SDP_FREE(sdp_p);

    return (SDP_SUCCESS);
}

/*
 * sdp_parse_error
 * Send SDP parsing errors to log and up to peerconnection
 */
void sdp_parse_error(sdp_t* sdp, const char *format, ...) {
    flex_string fs;
    va_list ap;

    flex_string_init(&fs);

    va_start(ap, format);
    flex_string_vsprintf(&fs, format, ap);
    va_end(ap);

    CSFLogError("SDP Parse", "SDP Parse Error %s, line %u", fs.buffer,
                sdp->parse_line);

    if (sdp->conf_p->error_handler) {
        sdp->conf_p->error_handler(sdp->conf_p->error_handler_context,
                                   sdp->parse_line,
                                   fs.buffer);
    }

    flex_string_free(&fs);
}
