/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/SdpAttribute.h"
#include "sdp/SdpHelper.h"
#include <iomanip>
#include <bitset>

#ifdef CRLF
#  undef CRLF
#endif
#define CRLF "\r\n"

namespace mozilla {

static unsigned char PeekChar(std::istream& is, std::string* error) {
  int next = is.peek();
  if (next == EOF) {
    *error = "Truncated";
    return 0;
  }

  return next;
}

static std::string ParseToken(std::istream& is, const std::string& delims,
                              std::string* error) {
  std::string token;
  while (is) {
    unsigned char c = PeekChar(is, error);
    if (!c || (delims.find(c) != std::string::npos)) {
      break;
    }
    token.push_back(std::tolower(is.get()));
  }
  return token;
}

static bool SkipChar(std::istream& is, unsigned char c, std::string* error) {
  if (PeekChar(is, error) != c) {
    *error = "Expected \'";
    error->push_back(c);
    error->push_back('\'');
    return false;
  }

  is.get();
  return true;
}

void SdpConnectionAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mValue << CRLF;
}

void SdpDirectionAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mValue << CRLF;
}

void SdpDtlsMessageAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mRole << " " << mValue << CRLF;
}

bool SdpDtlsMessageAttribute::Parse(std::istream& is, std::string* error) {
  std::string roleToken = ParseToken(is, " ", error);
  if (roleToken == "server") {
    mRole = kServer;
  } else if (roleToken == "client") {
    mRole = kClient;
  } else {
    *error = "Invalid dtls-message role; must be either client or server";
    return false;
  }

  is >> std::ws;

  std::string s(std::istreambuf_iterator<char>(is), {});
  mValue = s;

  return true;
}

void SdpExtmapAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mExtmaps.begin(); i != mExtmaps.end(); ++i) {
    os << "a=" << mType << ":" << i->entry;
    if (i->direction_specified) {
      os << "/" << i->direction;
    }
    os << " " << i->extensionname;
    if (i->extensionattributes.length()) {
      os << " " << i->extensionattributes;
    }
    os << CRLF;
  }
}

void SdpFingerprintAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mFingerprints.begin(); i != mFingerprints.end(); ++i) {
    os << "a=" << mType << ":" << i->hashFunc << " "
       << FormatFingerprint(i->fingerprint) << CRLF;
  }
}

// Format the fingerprint in RFC 4572 Section 5 attribute format
std::string SdpFingerprintAttributeList::FormatFingerprint(
    const std::vector<uint8_t>& fp) {
  if (fp.empty()) {
    MOZ_ASSERT(false, "Cannot format an empty fingerprint.");
    return "";
  }

  std::ostringstream os;
  for (auto i = fp.begin(); i != fp.end(); ++i) {
    os << ":" << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
       << static_cast<uint32_t>(*i);
  }
  return os.str().substr(1);
}

static uint8_t FromUppercaseHex(char ch) {
  if ((ch >= '0') && (ch <= '9')) {
    return ch - '0';
  }
  if ((ch >= 'A') && (ch <= 'F')) {
    return ch - 'A' + 10;
  }
  return 16;  // invalid
}

// Parse the fingerprint from RFC 4572 Section 5 attribute format
std::vector<uint8_t> SdpFingerprintAttributeList::ParseFingerprint(
    const std::string& str) {
  size_t targetSize = (str.length() + 1) / 3;
  std::vector<uint8_t> fp(targetSize);
  size_t fpIndex = 0;

  if (str.length() % 3 != 2) {
    fp.clear();
    return fp;
  }

  for (size_t i = 0; i < str.length(); i += 3) {
    uint8_t high = FromUppercaseHex(str[i]);
    uint8_t low = FromUppercaseHex(str[i + 1]);
    if (high > 0xf || low > 0xf ||
        (i + 2 < str.length() && str[i + 2] != ':')) {
      fp.clear();  // error
      return fp;
    }
    fp[fpIndex++] = high << 4 | low;
  }
  return fp;
}

bool SdpFmtpAttributeList::operator==(const SdpFmtpAttributeList& other) const {
  return mFmtps == other.mFmtps;
}

void SdpFmtpAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mFmtps.begin(); i != mFmtps.end(); ++i) {
    if (i->parameters) {
      os << "a=" << mType << ":" << i->format << " ";
      i->parameters->Serialize(os);
      os << CRLF;
    }
  }
}

void SdpGroupAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mGroups.begin(); i != mGroups.end(); ++i) {
    os << "a=" << mType << ":" << i->semantics;
    for (auto j = i->tags.begin(); j != i->tags.end(); ++j) {
      os << " " << (*j);
    }
    os << CRLF;
  }
}

// We're just using an SdpStringAttribute for this right now
#if 0
void SdpIdentityAttribute::Serialize(std::ostream& os) const
{
  os << "a=" << mType << ":" << mAssertion;
  for (auto i = mExtensions.begin(); i != mExtensions.end(); i++) {
    os << (i == mExtensions.begin() ? " " : ";") << (*i);
  }
  os << CRLF;
}
#endif

// Class to help with omitting a leading delimiter for the first item in a list
class SkipFirstDelimiter {
 public:
  explicit SkipFirstDelimiter(const std::string& delim)
      : mDelim(delim), mFirst(true) {}

  std::ostream& print(std::ostream& os) {
    if (!mFirst) {
      os << mDelim;
    }
    mFirst = false;
    return os;
  }

 private:
  std::string mDelim;
  bool mFirst;
};

static std::ostream& operator<<(std::ostream& os, SkipFirstDelimiter& delim) {
  return delim.print(os);
}

void SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const {
  if (discreteValues.empty()) {
    os << "[" << min << ":";
    if (step != 1) {
      os << step << ":";
    }
    os << max << "]";
  } else if (discreteValues.size() == 1) {
    os << discreteValues.front();
  } else {
    os << "[";
    SkipFirstDelimiter comma(",");
    for (auto value : discreteValues) {
      os << comma << value;
    }
    os << "]";
  }
}

template <typename T>
bool GetUnsigned(std::istream& is, T min, T max, T* value, std::string* error) {
  if (PeekChar(is, error) == '-') {
    *error = "Value is less than 0";
    return false;
  }

  is >> std::noskipws >> *value;

  if (is.fail()) {
    *error = "Malformed";
    return false;
  }

  if (*value < min) {
    *error = "Value too small";
    return false;
  }

  if (*value > max) {
    *error = "Value too large";
    return false;
  }

  return true;
}

static bool GetXYValue(std::istream& is, uint32_t* value, std::string* error) {
  return GetUnsigned<uint32_t>(is, 1, 999999, value, error);
}

bool SdpImageattrAttributeList::XYRange::ParseDiscreteValues(
    std::istream& is, std::string* error) {
  do {
    uint32_t value;
    if (!GetXYValue(is, &value, error)) {
      return false;
    }
    discreteValues.push_back(value);
  } while (SkipChar(is, ',', error));

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::XYRange::ParseAfterMin(std::istream& is,
                                                       std::string* error) {
  // We have already parsed "[320:", and now expect another uint
  uint32_t value;
  if (!GetXYValue(is, &value, error)) {
    return false;
  }

  if (SkipChar(is, ':', error)) {
    // Range with step eg [320:16:640]
    step = value;
    // Now |value| should be the max
    if (!GetXYValue(is, &value, error)) {
      return false;
    }
  }

  max = value;
  if (min >= max) {
    *error = "Min is not smaller than max";
    return false;
  }

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::XYRange::ParseAfterBracket(std::istream& is,
                                                           std::string* error) {
  // Either a range, or a list of discrete values
  // [320:640], [320:16:640], or [320,640]
  uint32_t value;
  if (!GetXYValue(is, &value, error)) {
    return false;
  }

  if (SkipChar(is, ':', error)) {
    // Range - [640:480] or [640:16:480]
    min = value;
    return ParseAfterMin(is, error);
  }

  if (SkipChar(is, ',', error)) {
    discreteValues.push_back(value);
    return ParseDiscreteValues(is, error);
  }

  *error = "Expected \':\' or \',\'";
  return false;
}

bool SdpImageattrAttributeList::XYRange::Parse(std::istream& is,
                                               std::string* error) {
  if (SkipChar(is, '[', error)) {
    return ParseAfterBracket(is, error);
  }

  // Single discrete value
  uint32_t value;
  if (!GetXYValue(is, &value, error)) {
    return false;
  }
  discreteValues.push_back(value);

  return true;
}

static bool GetSPValue(std::istream& is, float* value, std::string* error) {
  return GetUnsigned<float>(is, 0.1f, 9.9999f, value, error);
}

static bool GetQValue(std::istream& is, float* value, std::string* error) {
  return GetUnsigned<float>(is, 0.0f, 1.0f, value, error);
}

bool SdpImageattrAttributeList::SRange::ParseDiscreteValues(
    std::istream& is, std::string* error) {
  do {
    float value;
    if (!GetSPValue(is, &value, error)) {
      return false;
    }
    discreteValues.push_back(value);
  } while (SkipChar(is, ',', error));

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::SRange::ParseAfterMin(std::istream& is,
                                                      std::string* error) {
  if (!GetSPValue(is, &max, error)) {
    return false;
  }

  if (min >= max) {
    *error = "Min is not smaller than max";
    return false;
  }

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::SRange::ParseAfterBracket(std::istream& is,
                                                          std::string* error) {
  // Either a range, or a list of discrete values
  float value;
  if (!GetSPValue(is, &value, error)) {
    return false;
  }

  if (SkipChar(is, '-', error)) {
    min = value;
    return ParseAfterMin(is, error);
  }

  if (SkipChar(is, ',', error)) {
    discreteValues.push_back(value);
    return ParseDiscreteValues(is, error);
  }

  *error = "Expected either \'-\' or \',\'";
  return false;
}

bool SdpImageattrAttributeList::SRange::Parse(std::istream& is,
                                              std::string* error) {
  if (SkipChar(is, '[', error)) {
    return ParseAfterBracket(is, error);
  }

  // Single discrete value
  float value;
  if (!GetSPValue(is, &value, error)) {
    return false;
  }
  discreteValues.push_back(value);
  return true;
}

bool SdpImageattrAttributeList::PRange::Parse(std::istream& is,
                                              std::string* error) {
  if (!SkipChar(is, '[', error)) {
    return false;
  }

  if (!GetSPValue(is, &min, error)) {
    return false;
  }

  if (!SkipChar(is, '-', error)) {
    return false;
  }

  if (!GetSPValue(is, &max, error)) {
    return false;
  }

  if (min >= max) {
    *error = "min must be smaller than max";
    return false;
  }

  if (!SkipChar(is, ']', error)) {
    return false;
  }
  return true;
}

void SdpImageattrAttributeList::SRange::Serialize(std::ostream& os) const {
  os << std::setprecision(4) << std::fixed;
  if (discreteValues.empty()) {
    os << "[" << min << "-" << max << "]";
  } else if (discreteValues.size() == 1) {
    os << discreteValues.front();
  } else {
    os << "[";
    SkipFirstDelimiter comma(",");
    for (auto value : discreteValues) {
      os << comma << value;
    }
    os << "]";
  }
}

void SdpImageattrAttributeList::PRange::Serialize(std::ostream& os) const {
  os << std::setprecision(4) << std::fixed;
  os << "[" << min << "-" << max << "]";
}

static std::string ParseKey(std::istream& is, std::string* error) {
  std::string token = ParseToken(is, "=", error);
  if (!SkipChar(is, '=', error)) {
    return "";
  }
  return token;
}

static bool SkipBraces(std::istream& is, std::string* error) {
  if (PeekChar(is, error) != '[') {
    *error = "Expected \'[\'";
    return false;
  }

  size_t braceCount = 0;
  do {
    switch (PeekChar(is, error)) {
      case '[':
        ++braceCount;
        break;
      case ']':
        --braceCount;
        break;
      default:
        break;
    }
    is.get();
  } while (braceCount && is);

  if (!is) {
    *error = "Expected closing brace";
    return false;
  }

  return true;
}

// Assumptions:
// 1. If the value contains '[' or ']', they are balanced.
// 2. The value contains no ',' outside of brackets.
static bool SkipValue(std::istream& is, std::string* error) {
  while (is) {
    switch (PeekChar(is, error)) {
      case ',':
      case ']':
        return true;
      case '[':
        if (!SkipBraces(is, error)) {
          return false;
        }
        break;
      default:
        is.get();
    }
  }

  *error = "No closing \']\' on set";
  return false;
}

bool SdpImageattrAttributeList::Set::Parse(std::istream& is,
                                           std::string* error) {
  if (!SkipChar(is, '[', error)) {
    return false;
  }

  if (ParseKey(is, error) != "x") {
    *error = "Expected x=";
    return false;
  }

  if (!xRange.Parse(is, error)) {
    return false;
  }

  if (!SkipChar(is, ',', error)) {
    return false;
  }

  if (ParseKey(is, error) != "y") {
    *error = "Expected y=";
    return false;
  }

  if (!yRange.Parse(is, error)) {
    return false;
  }

  qValue = 0.5f;  // default

  bool gotSar = false;
  bool gotPar = false;
  bool gotQ = false;

  while (SkipChar(is, ',', error)) {
    std::string key = ParseKey(is, error);
    if (key.empty()) {
      *error = "Expected key-value";
      return false;
    }

    if (key == "sar") {
      if (gotSar) {
        *error = "Extra sar parameter";
        return false;
      }
      gotSar = true;
      if (!sRange.Parse(is, error)) {
        return false;
      }
    } else if (key == "par") {
      if (gotPar) {
        *error = "Extra par parameter";
        return false;
      }
      gotPar = true;
      if (!pRange.Parse(is, error)) {
        return false;
      }
    } else if (key == "q") {
      if (gotQ) {
        *error = "Extra q parameter";
        return false;
      }
      gotQ = true;
      if (!GetQValue(is, &qValue, error)) {
        return false;
      }
    } else {
      if (!SkipValue(is, error)) {
        return false;
      }
    }
  }

  return SkipChar(is, ']', error);
}

void SdpImageattrAttributeList::Set::Serialize(std::ostream& os) const {
  os << "[x=";
  xRange.Serialize(os);
  os << ",y=";
  yRange.Serialize(os);
  if (sRange.IsSet()) {
    os << ",sar=";
    sRange.Serialize(os);
  }
  if (pRange.IsSet()) {
    os << ",par=";
    pRange.Serialize(os);
  }
  if (qValue >= 0) {
    os << std::setprecision(2) << std::fixed << ",q=" << qValue;
  }
  os << "]";
}

bool SdpImageattrAttributeList::Imageattr::ParseSets(std::istream& is,
                                                     std::string* error) {
  std::string type = ParseToken(is, " \t", error);

  bool* isAll = nullptr;
  std::vector<Set>* sets = nullptr;

  if (type == "send") {
    isAll = &sendAll;
    sets = &sendSets;
  } else if (type == "recv") {
    isAll = &recvAll;
    sets = &recvSets;
  } else {
    *error = "Unknown type, must be either send or recv";
    return false;
  }

  if (*isAll || !sets->empty()) {
    *error = "Multiple send or recv set lists";
    return false;
  }

  is >> std::ws;
  if (SkipChar(is, '*', error)) {
    *isAll = true;
    return true;
  }

  do {
    Set set;
    if (!set.Parse(is, error)) {
      return false;
    }

    sets->push_back(set);
    is >> std::ws;
  } while (PeekChar(is, error) == '[');

  return true;
}

bool SdpImageattrAttributeList::Imageattr::Parse(std::istream& is,
                                                 std::string* error) {
  if (!SkipChar(is, '*', error)) {
    uint16_t value;
    if (!GetUnsigned<uint16_t>(is, 0, UINT16_MAX, &value, error)) {
      return false;
    }
    pt = Some(value);
  }

  is >> std::ws;
  if (!ParseSets(is, error)) {
    return false;
  }

  // There might be a second one
  is >> std::ws;
  if (is.eof()) {
    return true;
  }

  if (!ParseSets(is, error)) {
    return false;
  }

  is >> std::ws;
  if (!is.eof()) {
    *error = "Trailing characters";
    return false;
  }

  return true;
}

void SdpImageattrAttributeList::Imageattr::Serialize(std::ostream& os) const {
  if (pt.isSome()) {
    os << *pt;
  } else {
    os << "*";
  }

  if (sendAll) {
    os << " send *";
  } else if (!sendSets.empty()) {
    os << " send";
    for (auto& set : sendSets) {
      os << " ";
      set.Serialize(os);
    }
  }

  if (recvAll) {
    os << " recv *";
  } else if (!recvSets.empty()) {
    os << " recv";
    for (auto& set : recvSets) {
      os << " ";
      set.Serialize(os);
    }
  }
}

void SdpImageattrAttributeList::Serialize(std::ostream& os) const {
  for (auto& imageattr : mImageattrs) {
    os << "a=" << mType << ":";
    imageattr.Serialize(os);
    os << CRLF;
  }
}

bool SdpImageattrAttributeList::PushEntry(const std::string& raw,
                                          std::string* error,
                                          size_t* errorPos) {
  std::istringstream is(raw);

  Imageattr imageattr;
  if (!imageattr.Parse(is, error)) {
    is.clear();
    *errorPos = is.tellg();
    return false;
  }

  mImageattrs.push_back(imageattr);
  return true;
}

void SdpMsidAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mMsids.begin(); i != mMsids.end(); ++i) {
    os << "a=" << mType << ":" << i->identifier;
    if (i->appdata.length()) {
      os << " " << i->appdata;
    }
    os << CRLF;
  }
}

void SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) {
    os << "a=" << mType << ":" << i->semantic;
    for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
      os << " " << *j;
    }
    os << CRLF;
  }
}

void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const {
  if (mCandidates.empty()) {
    return;
  }

  os << "a=" << mType;
  for (auto i = mCandidates.begin(); i != mCandidates.end(); i++) {
    os << (i == mCandidates.begin() ? ":" : " ") << i->id << " " << i->address
       << " " << i->port;
  }
  os << CRLF;
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::ParseParameters(std::istream& is,
                                               std::string* error) {
  if (!PeekChar(is, error)) {
    // No parameters
    return true;
  }

  do {
    is >> std::ws;
    std::string key = ParseKey(is, error);
    if (key.empty()) {
      return false;  // Illegal trailing cruft
    }

    // This allows pt= to appear anywhere, instead of only at the beginning, but
    // this ends up being significantly less code.
    if (key == "pt") {
      if (!ParseFormats(is, error)) {
        return false;
      }
    } else if (key == "max-width") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxWidth,
                                 error)) {
        return false;
      }
    } else if (key == "max-height") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxHeight,
                                 error)) {
        return false;
      }
    } else if (key == "max-fps") {
      uint32_t maxFps;
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) {
        return false;
      }
      constraints.maxFps = Some(maxFps);
    } else if (key == "max-fs") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFs,
                                 error)) {
        return false;
      }
    } else if (key == "max-br") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxBr,
                                 error)) {
        return false;
      }
    } else if (key == "max-pps") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxPps,
                                 error)) {
        return false;
      }
    } else if (key == "depend") {
      if (!ParseDepend(is, error)) {
        return false;
      }
    } else {
      (void)ParseToken(is, ";", error);
    }
  } while (SkipChar(is, ';', error));
  return true;
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::ParseDepend(std::istream& is,
                                           std::string* error) {
  do {
    std::string id = ParseToken(is, ",;", error);
    if (id.empty()) {
      return false;
    }
    dependIds.push_back(id);
  } while (SkipChar(is, ',', error));

  return true;
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::ParseFormats(std::istream& is,
                                            std::string* error) {
  do {
    uint16_t fmt;
    if (!GetUnsigned<uint16_t>(is, 0, 127, &fmt, error)) {
      return false;
    }
    formats.push_back(fmt);
  } while (SkipChar(is, ',', error));

  return true;
}

void SdpRidAttributeList::Rid::SerializeParameters(std::ostream& os) const {
  if (!HasParameters()) {
    return;
  }

  os << " ";

  SkipFirstDelimiter semic(";");

  if (!formats.empty()) {
    os << semic << "pt=";
    SkipFirstDelimiter comma(",");
    for (uint16_t fmt : formats) {
      os << comma << fmt;
    }
  }

  if (constraints.maxWidth) {
    os << semic << "max-width=" << constraints.maxWidth;
  }

  if (constraints.maxHeight) {
    os << semic << "max-height=" << constraints.maxHeight;
  }

  if (constraints.maxFps) {
    os << semic << "max-fps=" << constraints.maxFps;
  }

  if (constraints.maxFs) {
    os << semic << "max-fs=" << constraints.maxFs;
  }

  if (constraints.maxBr) {
    os << semic << "max-br=" << constraints.maxBr;
  }

  if (constraints.maxPps) {
    os << semic << "max-pps=" << constraints.maxPps;
  }

  if (!dependIds.empty()) {
    os << semic << "depend=";
    SkipFirstDelimiter comma(",");
    for (const std::string& id : dependIds) {
      os << comma << id;
    }
  }
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::Parse(std::istream& is, std::string* error) {
  id = ParseToken(is, " ", error);
  if (!CheckRidValidity(id, error)) {
    return false;
  }

  is >> std::ws;
  std::string directionToken = ParseToken(is, " ", error);
  if (directionToken == "send") {
    direction = sdp::kSend;
  } else if (directionToken == "recv") {
    direction = sdp::kRecv;
  } else {
    *error = "Invalid direction, must be either send or recv";
    return false;
  }

  return ParseParameters(is, error);
}

static std::bitset<256> GetAllowedRidCharacters() {
  // From RFC 8851:
  // rid-id            = 1*(alpha-numeric / "-" / "_")
  std::bitset<256> result;
  for (unsigned char c = 'a'; c <= 'z'; ++c) {
    result.set(c);
  }
  for (unsigned char c = 'A'; c <= 'Z'; ++c) {
    result.set(c);
  }
  for (unsigned char c = '0'; c <= '9'; ++c) {
    result.set(c);
  }
  // NOTE: RFC 8851 says these are allowed, but RFC 8852 says they are not
  // https://www.rfc-editor.org/errata/eid7132
  // result.set('-');
  // result.set('_');
  return result;
}

/* static */
bool SdpRidAttributeList::CheckRidValidity(const std::string& aRid,
                                           std::string* aError) {
  if (aRid.empty()) {
    *aError = "Rid must be non-empty (according to RFC 8851)";
    return false;
  }

  // We need to check against a maximum length, but that is nowhere
  // specified in webrtc-pc right now.
  if (aRid.size() > 255) {
    *aError = "Rid can be at most 255 characters long (according to RFC 8852)";
    return false;
  }

  // TODO: Right now, if the rid is longer than kMaxRidLength, we don't treat it
  // as a parse error, since the grammar does not have this restriction.
  // Instead, our JSEP code ignores rids that exceed this limit. However, there
  // is a possibility that the IETF grammar (in RFC 8852) will change the limit
  // from 255 to 16, in which case we will need to revise this code.

  static const std::bitset<256> allowed = GetAllowedRidCharacters();
  for (unsigned char c : aRid) {
    if (!allowed[c]) {
      *aError =
          "Rid can contain only alphanumeric characters (according to RFC "
          "8852)";
      return false;
    }
  }

  return true;
}

// This can be overridden if necessary
size_t SdpRidAttributeList::kMaxRidLength = 255;

void SdpRidAttributeList::Rid::Serialize(std::ostream& os) const {
  os << id << " " << direction;
  SerializeParameters(os);
}

bool SdpRidAttributeList::Rid::HasFormat(const std::string& format) const {
  if (formats.empty()) {
    return true;
  }

  uint16_t formatAsInt;
  if (!SdpHelper::GetPtAsInt(format, &formatAsInt)) {
    return false;
  }

  return (std::find(formats.begin(), formats.end(), formatAsInt) !=
          formats.end());
}

void SdpRidAttributeList::Serialize(std::ostream& os) const {
  for (const Rid& rid : mRids) {
    os << "a=" << mType << ":";
    rid.Serialize(os);
    os << CRLF;
  }
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::PushEntry(const std::string& raw, std::string* error,
                                    size_t* errorPos) {
  std::istringstream is(raw);

  Rid rid;
  if (!rid.Parse(is, error)) {
    is.clear();
    *errorPos = is.tellg();
    return false;
  }

  mRids.push_back(rid);
  return true;
}

void SdpRidAttributeList::PushEntry(const std::string& id, sdp::Direction dir,
                                    const std::vector<uint16_t>& formats,
                                    const EncodingConstraints& constraints,
                                    const std::vector<std::string>& dependIds) {
  SdpRidAttributeList::Rid rid;

  rid.id = id;
  rid.direction = dir;
  rid.formats = formats;
  rid.constraints = constraints;
  rid.dependIds = dependIds;

  mRids.push_back(std::move(rid));
}

void SdpRtcpAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mPort;
  if (!mAddress.empty()) {
    os << " " << mNetType << " " << mAddrType << " " << mAddress;
  }
  os << CRLF;
}

const char* SdpRtcpFbAttributeList::pli = "pli";
const char* SdpRtcpFbAttributeList::sli = "sli";
const char* SdpRtcpFbAttributeList::rpsi = "rpsi";
const char* SdpRtcpFbAttributeList::app = "app";

const char* SdpRtcpFbAttributeList::fir = "fir";
const char* SdpRtcpFbAttributeList::tmmbr = "tmmbr";
const char* SdpRtcpFbAttributeList::tstr = "tstr";
const char* SdpRtcpFbAttributeList::vbcm = "vbcm";

void SdpRtcpFbAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mFeedbacks.begin(); i != mFeedbacks.end(); ++i) {
    os << "a=" << mType << ":" << i->pt << " " << i->type;
    if (i->parameter.length()) {
      os << " " << i->parameter;
      if (i->extra.length()) {
        os << " " << i->extra;
      }
    }
    os << CRLF;
  }
}

static bool ShouldSerializeChannels(SdpRtpmapAttributeList::CodecType type) {
  switch (type) {
    case SdpRtpmapAttributeList::kOpus:
    case SdpRtpmapAttributeList::kG722:
      return true;
    case SdpRtpmapAttributeList::kPCMU:
    case SdpRtpmapAttributeList::kPCMA:
    case SdpRtpmapAttributeList::kVP8:
    case SdpRtpmapAttributeList::kVP9:
    case SdpRtpmapAttributeList::kiLBC:
    case SdpRtpmapAttributeList::kiSAC:
    case SdpRtpmapAttributeList::kH264:
    case SdpRtpmapAttributeList::kRed:
    case SdpRtpmapAttributeList::kUlpfec:
    case SdpRtpmapAttributeList::kTelephoneEvent:
    case SdpRtpmapAttributeList::kRtx:
      return false;
    case SdpRtpmapAttributeList::kOtherCodec:
      return true;
  }
  MOZ_CRASH();
}

void SdpRtpmapAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mRtpmaps.begin(); i != mRtpmaps.end(); ++i) {
    os << "a=" << mType << ":" << i->pt << " " << i->name << "/" << i->clock;
    if (i->channels && ShouldSerializeChannels(i->codec)) {
      os << "/" << i->channels;
    }
    os << CRLF;
  }
}

void SdpSctpmapAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mSctpmaps.begin(); i != mSctpmaps.end(); ++i) {
    os << "a=" << mType << ":" << i->pt << " " << i->name << " " << i->streams
       << CRLF;
  }
}

void SdpSetupAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mRole << CRLF;
}

void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const {
  SkipFirstDelimiter comma(",");
  for (const Encoding& choice : choices) {
    os << comma;
    if (choice.paused) {
      os << '~';
    }
    os << choice.rid;
  }
}

bool SdpSimulcastAttribute::Version::Parse(std::istream& is,
                                           std::string* error) {
  do {
    bool paused = SkipChar(is, '~', error);
    std::string value = ParseToken(is, ",; ", error);
    if (value.empty()) {
      *error = "Missing rid";
      return false;
    }
    if (!SdpRidAttributeList::CheckRidValidity(value, error)) {
      return false;
    }
    choices.push_back(Encoding(value, paused));
  } while (SkipChar(is, ',', error));

  return true;
}

void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const {
  SkipFirstDelimiter semic(";");
  for (const Version& version : *this) {
    if (!version.IsSet()) {
      continue;
    }
    os << semic;
    version.Serialize(os);
  }
}

bool SdpSimulcastAttribute::Versions::Parse(std::istream& is,
                                            std::string* error) {
  do {
    Version version;
    if (!version.Parse(is, error)) {
      return false;
    }
    push_back(version);
  } while (SkipChar(is, ';', error));

  return true;
}

void SdpSimulcastAttribute::Serialize(std::ostream& os) const {
  MOZ_ASSERT(sendVersions.IsSet() || recvVersions.IsSet());

  os << "a=" << mType << ":";

  if (sendVersions.IsSet()) {
    os << "send ";
    sendVersions.Serialize(os);
  }

  if (recvVersions.IsSet()) {
    if (sendVersions.IsSet()) {
      os << " ";
    }
    os << "recv ";
    recvVersions.Serialize(os);
  }

  os << CRLF;
}

bool SdpSimulcastAttribute::Parse(std::istream& is, std::string* error) {
  bool gotRecv = false;
  bool gotSend = false;

  while (true) {
    is >> std::ws;
    std::string token = ParseToken(is, " \t", error);
    if (token.empty()) {
      break;
    }

    if (token == "send") {
      if (gotSend) {
        *error = "Already got a send list";
        return false;
      }
      gotSend = true;

      is >> std::ws;
      if (!sendVersions.Parse(is, error)) {
        return false;
      }
    } else if (token == "recv") {
      if (gotRecv) {
        *error = "Already got a recv list";
        return false;
      }
      gotRecv = true;

      is >> std::ws;
      if (!recvVersions.Parse(is, error)) {
        return false;
      }
    } else {
      *error = "Type must be either 'send' or 'recv'";
      return false;
    }
  }

  if (!gotSend && !gotRecv) {
    *error = "Empty simulcast attribute";
    return false;
  }

  return true;
}

void SdpSsrcAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mSsrcs.begin(); i != mSsrcs.end(); ++i) {
    os << "a=" << mType << ":" << i->ssrc << " " << i->attribute << CRLF;
  }
}

void SdpSsrcGroupAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mSsrcGroups.begin(); i != mSsrcGroups.end(); ++i) {
    os << "a=" << mType << ":" << i->semantics;
    for (auto j = i->ssrcs.begin(); j != i->ssrcs.end(); ++j) {
      os << " " << (*j);
    }
    os << CRLF;
  }
}

void SdpMultiStringAttribute::Serialize(std::ostream& os) const {
  for (auto i = mValues.begin(); i != mValues.end(); ++i) {
    os << "a=" << mType << ":" << *i << CRLF;
  }
}

void SdpOptionsAttribute::Serialize(std::ostream& os) const {
  if (mValues.empty()) {
    return;
  }

  os << "a=" << mType << ":";

  for (auto i = mValues.begin(); i != mValues.end(); ++i) {
    if (i != mValues.begin()) {
      os << " ";
    }
    os << *i;
  }
  os << CRLF;
}

void SdpOptionsAttribute::Load(const std::string& value) {
  size_t start = 0;
  size_t end = value.find(' ');
  while (end != std::string::npos) {
    PushEntry(value.substr(start, end));
    start = end + 1;
    end = value.find(' ', start);
  }
  PushEntry(value.substr(start));
}

void SdpFlagAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << CRLF;
}

void SdpStringAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mValue << CRLF;
}

void SdpNumberAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mValue << CRLF;
}

bool SdpAttribute::IsAllowedAtMediaLevel(AttributeType type) {
  switch (type) {
    case kBundleOnlyAttribute:
      return true;
    case kCandidateAttribute:
      return true;
    case kConnectionAttribute:
      return true;
    case kDirectionAttribute:
      return true;
    case kDtlsMessageAttribute:
      return false;
    case kEndOfCandidatesAttribute:
      return true;
    case kExtmapAttribute:
      return true;
    case kFingerprintAttribute:
      return true;
    case kFmtpAttribute:
      return true;
    case kGroupAttribute:
      return false;
    case kIceLiteAttribute:
      return false;
    case kIceMismatchAttribute:
      return true;
    // RFC 5245 says this is session-level only, but
    // draft-ietf-mmusic-ice-sip-sdp-03 updates this to allow at the media
    // level.
    case kIceOptionsAttribute:
      return true;
    case kIcePwdAttribute:
      return true;
    case kIceUfragAttribute:
      return true;
    case kIdentityAttribute:
      return false;
    case kImageattrAttribute:
      return true;
    case kLabelAttribute:
      return true;
    case kMaxptimeAttribute:
      return true;
    case kMidAttribute:
      return true;
    case kMsidAttribute:
      return true;
    case kMsidSemanticAttribute:
      return false;
    case kPtimeAttribute:
      return true;
    case kRemoteCandidatesAttribute:
      return true;
    case kRidAttribute:
      return true;
    case kRtcpAttribute:
      return true;
    case kRtcpFbAttribute:
      return true;
    case kRtcpMuxAttribute:
      return true;
    case kRtcpRsizeAttribute:
      return true;
    case kRtpmapAttribute:
      return true;
    case kSctpmapAttribute:
      return true;
    case kSetupAttribute:
      return true;
    case kSimulcastAttribute:
      return true;
    case kSsrcAttribute:
      return true;
    case kSsrcGroupAttribute:
      return true;
    case kSctpPortAttribute:
      return true;
    case kMaxMessageSizeAttribute:
      return true;
  }
  MOZ_CRASH("Unknown attribute type");
}

bool SdpAttribute::IsAllowedAtSessionLevel(AttributeType type) {
  switch (type) {
    case kBundleOnlyAttribute:
      return false;
    case kCandidateAttribute:
      return false;
    case kConnectionAttribute:
      return true;
    case kDirectionAttribute:
      return true;
    case kDtlsMessageAttribute:
      return true;
    case kEndOfCandidatesAttribute:
      return true;
    case kExtmapAttribute:
      return true;
    case kFingerprintAttribute:
      return true;
    case kFmtpAttribute:
      return false;
    case kGroupAttribute:
      return true;
    case kIceLiteAttribute:
      return true;
    case kIceMismatchAttribute:
      return false;
    case kIceOptionsAttribute:
      return true;
    case kIcePwdAttribute:
      return true;
    case kIceUfragAttribute:
      return true;
    case kIdentityAttribute:
      return true;
    case kImageattrAttribute:
      return false;
    case kLabelAttribute:
      return false;
    case kMaxptimeAttribute:
      return false;
    case kMidAttribute:
      return false;
    case kMsidSemanticAttribute:
      return true;
    case kMsidAttribute:
      return false;
    case kPtimeAttribute:
      return false;
    case kRemoteCandidatesAttribute:
      return false;
    case kRidAttribute:
      return false;
    case kRtcpAttribute:
      return false;
    case kRtcpFbAttribute:
      return false;
    case kRtcpMuxAttribute:
      return false;
    case kRtcpRsizeAttribute:
      return false;
    case kRtpmapAttribute:
      return false;
    case kSctpmapAttribute:
      return false;
    case kSetupAttribute:
      return true;
    case kSimulcastAttribute:
      return false;
    case kSsrcAttribute:
      return false;
    case kSsrcGroupAttribute:
      return false;
    case kSctpPortAttribute:
      return false;
    case kMaxMessageSizeAttribute:
      return false;
  }
  MOZ_CRASH("Unknown attribute type");
}

const std::string SdpAttribute::GetAttributeTypeString(AttributeType type) {
  switch (type) {
    case kBundleOnlyAttribute:
      return "bundle-only";
    case kCandidateAttribute:
      return "candidate";
    case kConnectionAttribute:
      return "connection";
    case kDtlsMessageAttribute:
      return "dtls-message";
    case kEndOfCandidatesAttribute:
      return "end-of-candidates";
    case kExtmapAttribute:
      return "extmap";
    case kFingerprintAttribute:
      return "fingerprint";
    case kFmtpAttribute:
      return "fmtp";
    case kGroupAttribute:
      return "group";
    case kIceLiteAttribute:
      return "ice-lite";
    case kIceMismatchAttribute:
      return "ice-mismatch";
    case kIceOptionsAttribute:
      return "ice-options";
    case kIcePwdAttribute:
      return "ice-pwd";
    case kIceUfragAttribute:
      return "ice-ufrag";
    case kIdentityAttribute:
      return "identity";
    case kImageattrAttribute:
      return "imageattr";
    case kLabelAttribute:
      return "label";
    case kMaxptimeAttribute:
      return "maxptime";
    case kMidAttribute:
      return "mid";
    case kMsidAttribute:
      return "msid";
    case kMsidSemanticAttribute:
      return "msid-semantic";
    case kPtimeAttribute:
      return "ptime";
    case kRemoteCandidatesAttribute:
      return "remote-candidates";
    case kRidAttribute:
      return "rid";
    case kRtcpAttribute:
      return "rtcp";
    case kRtcpFbAttribute:
      return "rtcp-fb";
    case kRtcpMuxAttribute:
      return "rtcp-mux";
    case kRtcpRsizeAttribute:
      return "rtcp-rsize";
    case kRtpmapAttribute:
      return "rtpmap";
    case kSctpmapAttribute:
      return "sctpmap";
    case kSetupAttribute:
      return "setup";
    case kSimulcastAttribute:
      return "simulcast";
    case kSsrcAttribute:
      return "ssrc";
    case kSsrcGroupAttribute:
      return "ssrc-group";
    case kSctpPortAttribute:
      return "sctp-port";
    case kMaxMessageSizeAttribute:
      return "max-message-size";
    case kDirectionAttribute:
      MOZ_CRASH("kDirectionAttribute not valid here");
  }
  MOZ_CRASH("Unknown attribute type");
}

}  // namespace mozilla
