/*
 * pasn.cxx
 *
 * ASN classes in support of the SNMP code.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Contributor(s): ______________________________________.
 *
 * $Revision: 20385 $
 * $Author: rjongbloed $
 * $Date: 2008-06-04 10:40:38 +0000 (Wed, 04 Jun 2008) $
 */

#ifdef __GNUC__
#pragma implementation "pasn.h"
#endif

#include <ptlib.h>
#include <ptclib/pasn.h>

#define new PNEW


#define ASN_BOOLEAN         (0x01)
#define ASN_INTEGER         (0x02)
#define ASN_BIT_STR         (0x03)
#define ASN_OCTET_STR       (0x04)
#define ASN_NULL            (0x05)
#define ASN_OBJECT_ID       (0x06)
#define ASN_SEQUENCE        (0x10)
#define ASN_SET             (0x11)

#define ASN_UNIVERSAL       (0x00)
#define ASN_APPLICATION     (0x40)
#define ASN_CONTEXT         (0x80)
#define ASN_PRIVATE         (0xC0)

#define ASN_PRIMITIVE       (0x00)
#define ASN_CONSTRUCTOR     (0x20)

#define ASN_LONG_LEN        (0x80)
#define ASN_EXTENSION_ID    (0x1F)
#define ASN_BIT8            (0x80)

#define MAX_OID_LEN   64


static char cannotPerformOnBaseTypeMsg[] =
   "Cannot perform operation on base type PASNObject";


BYTE PASNObject::ASNTypeToType[] = {
  ASN_INTEGER   | ASN_UNIVERSAL | ASN_PRIMITIVE,    // Integer
  ASN_OCTET_STR | ASN_UNIVERSAL | ASN_PRIMITIVE,    // String
  ASN_OBJECT_ID | ASN_UNIVERSAL | ASN_PRIMITIVE,    // ObjectID
  ASN_CONSTRUCTOR | ASN_SEQUENCE,                   // Sequence
  ASN_CONSTRUCTOR | ASN_CONTEXT,                    // Choice
  ASN_APPLICATION | 0,                              // IPAddress
  ASN_APPLICATION | 1,                              // Counter32
  ASN_APPLICATION | 2,                              // Gauge32
  ASN_APPLICATION | 3,                              // TimeTicks

// SNMP v2 types
  ASN_APPLICATION | 4,                              // Opaque
  ASN_APPLICATION | 5,                              // NsapAddress
  ASN_APPLICATION | 6,                              // Counter64
  ASN_APPLICATION | 7,                              // UInteger32
  
// Oops - missed the Null type  
  ASN_NULL | ASN_UNIVERSAL | ASN_PRIMITIVE,         // Null

  0,                // Unknown
};



//////////////////////////////////////////////////////////////////////////
//
//  PASNObject
//     All ASN objects descend from this class. It is the primitive type
//  which can be put into the ASNSequence class
//

PASNObject::PASNObject()
{
}


void PASNObject::PrintOn(ostream &) const
{
  PAssertAlways(cannotPerformOnBaseTypeMsg);
}


void PASNObject::Encode(PBYTEArray &) 
{
  PAssertAlways(cannotPerformOnBaseTypeMsg);
}


WORD PASNObject::GetEncodedLength() 
{
  PAssertAlways(cannotPerformOnBaseTypeMsg);
  return 0;
}


PASNObject::ASNType PASNObject::GetType() const
{
  PAssertAlways(cannotPerformOnBaseTypeMsg);
  return Unknown;
}


PString PASNObject::GetTypeAsString() const
{
  PAssertAlways(cannotPerformOnBaseTypeMsg);
  return PString();
}


PObject * PASNObject::Clone() const
{
  PAssertAlways(cannotPerformOnBaseTypeMsg);
  return NULL;
}


void PASNObject::EncodeASNLength (PBYTEArray & buffer, WORD length)
{
  PINDEX offs = buffer.GetSize();

  // handle lengths less then 128
  if (length < 128) 
    buffer[offs++] = (BYTE)length;

  // handle lengths less than 256
  else if (length < 256) {
    buffer[offs++] = (BYTE)(0x01 | ASN_LONG_LEN);
    buffer[offs++] = (BYTE)length;
  }

  // handle lengths up to 0xffff
  else {
    buffer[offs++] = (u_char)(0x02 | ASN_LONG_LEN);
    buffer[offs++] = (u_char)((length >> 8) & 0xFF);
    buffer[offs++] = (u_char)(length & 0xFF);
  }
}


PBoolean PASNObject::DecodeASNLength (const PBYTEArray & buffer, PINDEX & ptr, WORD & len)
{
  PINDEX s = buffer.GetSize();

  if (ptr >= s)
    return PFalse;

  BYTE ch = buffer[ptr++];

  if ((ch & ASN_LONG_LEN) == 0)
    len = (WORD)ch;
  else if ((ch & ~ASN_LONG_LEN) == 0x01) {
    if (ptr >= s)
      return PFalse;
    len = (WORD)buffer[ptr++];
  } else {
    if (ptr + 1 >= s)
      return PFalse;
    len = (WORD)((buffer[ptr] << 8) + buffer[ptr+1]);
    ptr += 2;
  }
  return PTrue;
}


WORD PASNObject::GetASNLengthLength (WORD  length) 
{
  // handle lengths less then 128
  if (length < 128) 
    return 1;

  // handle lengths less than 256
  else if (length < 256) 
    return 2;

  // handle lengths up to 0xffff
  else 
    return 3;
}


void PASNObject::EncodeASNSequenceStart(PBYTEArray & buffer, BYTE type, WORD length) 
{
  buffer[buffer.GetSize()] = type;
  EncodeASNLength(buffer, length);
}


WORD PASNObject::GetASNSequenceStartLength(WORD length) 
{
  return (WORD)(1 + GetASNLengthLength(length));
}


void PASNObject::EncodeASNHeader(PBYTEArray & buffer, PASNObject::ASNType type, WORD length)

{
  buffer[buffer.GetSize()] = ASNTypeToType[type];
  EncodeASNLength(buffer, length);
}


WORD PASNObject::GetASNHeaderLength(WORD length) 
{
  return (WORD)(1 + GetASNLengthLength(length));
}


void PASNObject::EncodeASNInteger (PBYTEArray & buffer, PASNInt data, PASNObject::ASNType type)
{
  DWORD mask;
  WORD  intsize = sizeof(data);

  // create a mask which is the top nine bits of a DWORD, or 0xFF800000
  // on a big endian machine
  mask = 0x1FFUL << ((8 * (sizeof(DWORD) - 1)) - 1);

  // remove all sequences of nine 0's or 1's at the start of the value
  while ((((data & mask) == 0) || ((data & mask) == mask))
    && intsize > 1) {
    intsize--;
    data <<= 8;
  }

  // insert the header
  EncodeASNHeader(buffer, type, intsize);

  // insert the data
  PINDEX offs = buffer.GetSize();
  mask = 0xFFUL << (8 * (sizeof(DWORD) - 1));
  while (intsize--) {
    buffer[offs++] = (u_char)((data & mask) >> (8 * (sizeof(DWORD) - 1)));
    data <<= 8;
  }
}


void PASNObject::EncodeASNUnsigned (PBYTEArray & buffer, PASNUnsigned data, PASNObject::ASNType type)
{
  long mask;
  WORD intsize = sizeof(data);
  int  add_null_byte = 0;

  mask = 0xFFUL << (8 * (sizeof(long) - 1));
  /* mask is 0xFF000000 on a big-endian machine */
  if ((u_char)((data & mask) >> (8 * (sizeof(PASNUnsigned) - 1))) & 0x80){
    /* if MSB is set */
    add_null_byte = 1;
    intsize++;
  }

  // create a mask which is the top nine bits of a DWORD, or 0xFF800000
  // on a big endian machine
  mask = 0x1FFL << ((8 * (sizeof(DWORD) - 1)) - 1);

  // remove all sequences of nine 0's or 1's at the start of the value
  while ((((data & mask) == 0) || (((long)data & mask) == mask))
    && intsize > 1) {
    intsize--;
    data <<= 8;
  }

  // insert the header
  EncodeASNHeader(buffer, type, intsize);

  // insert the data
  PINDEX offs = buffer.GetSize();
  mask = 0xFFL << (8 * (sizeof(DWORD) - 1));
  while (intsize--) {
    buffer[offs++] = (u_char)((data & mask) >> (8 * (sizeof(DWORD) - 1)));
    data <<= 8;
  }

  if (add_null_byte == 1)
    buffer[offs++] = 0;
}


PBoolean PASNObject::DecodeASNInteger(const PBYTEArray & buffer, PINDEX & ptr, PASNInt & value, PASNObject::ASNType theType)
{
  if (buffer[ptr++] != ASNTypeToType[theType])
    return PFalse;

  WORD len;
  if (!DecodeASNLength(buffer, ptr, len))
    return PFalse;

  if (ptr + len > buffer.GetSize())
    return PFalse;

  if (buffer[ptr] & 0x80)
    value = -1; /* integer is negative */
  else
    value = 0;

  while (len--)
    value = (value << 8) | buffer[ptr++];
  return PTrue;
}


PBoolean PASNObject::DecodeASNUnsigned(const PBYTEArray & buffer, PINDEX & ptr, PASNUnsigned & value, PASNObject::ASNType theType)
{
  if (buffer[ptr++] != ASNTypeToType[theType])
    return PFalse;

  WORD len;
  if (!DecodeASNLength(buffer, ptr, len))
    return PFalse;

  if (ptr + len > buffer.GetSize())
    return PFalse;

//  if (buffer[ptr] & 0x80)
//    value = -1; /* integer is negative */

  value = 0;
  while (len--)
    value = (value << 8) | buffer[ptr++];
  return PTrue;
}


WORD PASNObject::GetASNIntegerLength(PASNInt data) 
{
  DWORD mask;
  WORD  intsize = sizeof(data);

  // create a mask which is the top nine bits of a DWORD, or 0xFF800000
  // on a big endian machine
  mask = 0x1FFUL << ((8 * (sizeof(DWORD) - 1)) - 1);

  // remove all sequences of nine 0's or 1's at the start of the value
  while ((((data & mask) == 0) || ((data & mask) == mask))
    && intsize > 1) {
    intsize--;
    data <<= 8;
  }

  // get the length of the header
  return (WORD)(intsize + GetASNHeaderLength(intsize));
}


WORD PASNObject::GetASNUnsignedLength (PASNUnsigned data)
{
  long mask;
  WORD intsize = sizeof(data);
  int  add_null_byte = 0;

  mask = 0xFFL << (8 * (sizeof(long) - 1));
  /* mask is 0xFF000000 on a big-endian machine */
  if ((u_char)((data & mask) >> (8 * (sizeof(PASNUnsigned) - 1))) & 0x80) {
    /* if MSB is set */
    add_null_byte = 1;
    intsize++;
  }

  // create a mask which is the top nine bits of a DWORD, or 0xFF800000
  // on a big endian machine
  mask = 0x1FFL << ((8 * (sizeof(DWORD) - 1)) - 1);

  // remove all sequences of nine 0's or 1's at the start of the value
  while ((((data & mask) == 0) || (((long)data & mask) == mask))
    && intsize > 1) {
    intsize--;
    data <<= 8;
  }

  // insert the header
  return (WORD)(intsize + GetASNHeaderLength(intsize) + add_null_byte);
}


PASNInt PASNObject::GetInteger () const
{
  PAssertAlways("Cannot return ASN object as Integer");
  return 0;
}


PString PASNObject::GetString  () const
{
  PAssertAlways("Cannot return ASN object as String");
  return PString();
}


PASNUnsigned PASNObject::GetUnsigned() const
{
  PAssertAlways("Cannot return ASN object as Unsigned");
  return 0;
}


const PASNSequence & PASNObject::GetSequence() const
{
  PAssertAlways("Cannot return ASN object as Sequence");
  PASNSequence * ptr = NULL;
  return (PASNSequence &)*ptr;
}


PIPSocket::Address PASNObject::GetIPAddress () const
{
  PAssertAlways("Cannot return ASN object as IP Address");
  return PIPSocket::Address();
}


//////////////////////////////////////////////////////////////////////////
//
//  PASNInteger
//     A descendant of PASNObject which is a simple ASN integer type
//

PASNInteger::PASNInteger(PASNInt val)
{
  value = val;
}


PASNInteger::PASNInteger(const PBYTEArray & buffer, PINDEX & ptr)
{
  DecodeASNInteger(buffer, ptr, value, Integer);
}


void PASNInteger::PrintOn(ostream & strm) const
{
  strm << "Integer: "
       << value
       << endl;
}

void PASNInteger::Encode(PBYTEArray & buffer)
{
  EncodeASNInteger(buffer, value, Integer);
}


WORD PASNInteger::GetEncodedLength()
{
  return GetASNIntegerLength(value);
}


PASNObject::ASNType PASNInteger::GetType() const
{
  return Integer;
}


PString PASNInteger::GetTypeAsString() const
{
  return PString("Integer");
}


PASNInt PASNInteger::GetInteger () const
{
  return value;
}


PString PASNInteger::GetString () const
{
  return PString(PString::Signed, (long)value);
}


PObject * PASNInteger::Clone() const
{
  return new PASNInteger(*this);
}


//////////////////////////////////////////////////////////////////////////
//
//  PASNString
//     A descendant of PASNObject which is a simple ASN OctetStr type
//

PASNString::PASNString(const PString & str)
{
  value    = str;
  valueLen = (WORD)str.GetLength();
}

PASNString::PASNString(const BYTE * ptr, int len)
{
  value = PString((const char *)ptr, len);
  valueLen = (WORD)len;
}

PASNString::PASNString(const PBYTEArray & buffer, PASNObject::ASNType type)
{
  PINDEX ptr = 0;
  Decode(buffer, ptr, type);
}


PASNString::PASNString(const PBYTEArray & buffer, PINDEX & ptr, PASNObject::ASNType type)
{
  Decode(buffer, ptr, type);
}


PBoolean PASNString::Decode(const PBYTEArray & buffer, PINDEX & ptr, PASNObject::ASNType type)
{
  valueLen = 0;
  if (buffer[ptr++] != ASNTypeToType[type])
    return PFalse;

  if (!DecodeASNLength(buffer, ptr, valueLen))
    return PFalse;

  if (ptr + valueLen > buffer.GetSize())
    return PFalse;

  value = PString(ptr + (const char *)(const BYTE *)buffer, valueLen);
  ptr += valueLen;

  return PTrue;
}


void PASNString::PrintOn(ostream & strm) const
{
  strm << GetTypeAsString()
       << ": "
       << value
       << endl; 
}


void PASNString::Encode(PBYTEArray & buffer, PASNObject::ASNType type)
{
  // insert the header
  EncodeASNHeader(buffer, type, valueLen);

  // add the string
  PINDEX offs = buffer.GetSize();
  for (PINDEX i = 0; i < valueLen; i++)
    buffer[offs+i] = value[i];
}


WORD PASNString::GetEncodedLength()
{
  return (WORD)(GetASNHeaderLength(valueLen) + (int)valueLen);
}


PASNObject::ASNType PASNString::GetType() const
{
  return String;
}


PString PASNString::GetTypeAsString() const
{
  return PString("String");
}


PString PASNString::GetString () const
{
  return value;
}


PObject * PASNString::Clone() const
{
  return new PASNString(*this);
}


//////////////////////////////////////////////////////////////////////////
//
//  PASNUnsignedInteger
//     A descendant of PASNObject which is an unsigned integer

PBoolean PASNUnsignedInteger::Decode(const PBYTEArray & buffer, PINDEX & ptr, PASNObject::ASNType theType)
{
  return DecodeASNUnsigned(buffer, ptr, value, theType);
}


void PASNUnsignedInteger::Encode(PBYTEArray & buffer, PASNObject::ASNType theType)
{
  EncodeASNUnsigned(buffer, value, theType);
}


void PASNUnsignedInteger::PrintOn(ostream & strm) const
{
  strm << GetTypeAsString()
       << " : "
       << value
       << endl; 
}


WORD PASNUnsignedInteger::GetEncodedLength()
{
  return GetASNUnsignedLength(value);
}


PString PASNUnsignedInteger::GetString() const
{
  return PString(PString::Unsigned, (long)value);
}


PASNUnsigned PASNUnsignedInteger::GetUnsigned() const
{
  return value;
}


//////////////////////////////////////////////////////////////////////////
//
//  PASNObjectID
//     A descendant of PASNObject which is a simple ASN ObjID type
//

PASNObjectID::PASNObjectID(PASNOid * val, BYTE theLen)
{
  value.SetSize(theLen);
  memcpy(value.GetPointer(theLen), val, theLen * sizeof(PASNOid)); 
}


PASNObjectID::PASNObjectID(const PString & str)
{
  PINDEX strLen = str.GetLength();

  PINDEX i = 0;
  PINDEX len = 0;
  while (i < strLen) {

    // find the first non-dot character
    while (str[i] == '.' && i < strLen)
      i++;

    // find the next dot
    PINDEX j = str.Find('.', i);

    // convert to a PASNOid
    value.SetSize(len+1);
    value.SetAt(len++, str(i, j).AsInteger());
    i = j;
  }
}


PASNObjectID::PASNObjectID(const PBYTEArray & buffer)
{
  PINDEX ptr = 0;
  Decode(buffer, ptr);
}


PASNObjectID::PASNObjectID(const PBYTEArray & buffer, PINDEX & ptr)
{
  Decode(buffer, ptr);
}


void PASNObjectID::PrintOn(ostream & strm) const
{
  strm << "ObjectId: ";
  for (PINDEX i = 0 ; i < value.GetSize(); i++) {
    strm << value[i];
    if (i != value.GetSize()-1)
      strm << '.';
  }
  strm << endl;
}


void PASNObjectID::Encode(PBYTEArray & buffer)
{
  PBYTEArray  eObjId;
  PINDEX      offs = 0;
  PASNOid     subId, mask, testmask;
  int         bits, testbits;
  PINDEX      objIdLen = value.GetSize();
  PASNOid     *objId = value.GetPointer();

  if (objIdLen < 2) {
    eObjId [offs++] = 0;
    objIdLen = 0;
  } else {
    eObjId [offs++] = (BYTE)(objId[1] + (objId[0] * 40));
    objIdLen -= 2;
    objId += 2;
  }

  while (objIdLen-- > 0) {
    subId = *objId++;
    if (subId < 128) 
      eObjId [offs++] = (BYTE)subId;
    else {
      mask = 0x7F; /* handle subid == 0 case */
      bits = 0;

      /* testmask *MUST* !!!! be of an unsigned type */
      for (testmask = 0x7F, testbits = 0;
           testmask != 0;
           testmask <<= 7, testbits += 7) {
        if (subId & testmask) {  /* if any bits set */
          mask = testmask;
          bits = testbits;
        }
      }

      /* mask can't be zero here */
      for(;mask != 0x7F; mask >>= 7, bits -= 7) {
        /* fix a mask that got truncated above */
        if (mask == 0x1E00000)
          mask = 0xFE00000;
        eObjId [offs++] = (u_char)(((subId & mask) >> bits) | ASN_BIT8);
      }
      eObjId [offs++] = (u_char)(subId & mask);
    }
  }

  PINDEX s = eObjId.GetSize();
  EncodeASNHeader (buffer, ObjectID, (WORD)s);
  offs = buffer.GetSize();
  for (PINDEX i = 0; i < s; i++)
    buffer [offs + i] = eObjId[i];
}


WORD PASNObjectID::GetEncodedLength()
{
  PASNOid    subId, mask, testmask;
  int        bits, testbits;
  PINDEX     objIdLen = value.GetSize();
  WORD       theLen = 0;
  PASNOid    *objId = value.GetPointer();

  if (objIdLen < 2) {
    theLen++;
    objIdLen = 0;
  } else {
    theLen++;
    objIdLen -= 2;
    objId += 2;
  }

  while (objIdLen-- > 0) {
    subId = *objId++;
    if (subId < 128) 
      theLen++;
    else {
      mask = 0x7F; /* handle subid == 0 case */
      bits = 0;

      /* testmask *MUST* !!!! be of an unsigned type */
      for (testmask = 0x7F, testbits = 0;
           testmask != 0;
           testmask <<= 7, testbits += 7) {
        if (subId & testmask) {  /* if any bits set */
          mask = testmask;
          bits = testbits;
        }
      }

      /* mask can't be zero here */
      for(;mask != 0x7F; mask >>= 7, bits -= 7) {
        /* fix a mask that got truncated above */
        if (mask == 0x1E00000)
          mask = 0xFE00000;
        theLen++;
      }
      theLen++;
    }
  }

  return (WORD)(theLen + GetASNHeaderLength(theLen));
}


PASNObject::ASNType PASNObjectID::GetType() const
{
  return ObjectID;
}


PString PASNObjectID::GetTypeAsString() const
{
  return PString("Object ID");
}


PString PASNObjectID::GetString() const
{
  PStringStream str;

  for (PINDEX i = 0; i < value.GetSize(); i++) {
    if (i > 0)
      str << '.';
    str << value[i];
  }

  return str;
}


PBoolean PASNObjectID::Decode(const PBYTEArray & buffer, PINDEX & offs)
{
  BYTE type = buffer[offs++];
  PAssert(type == (ASN_OBJECT_ID | ASN_UNIVERSAL | ASN_PRIMITIVE),
          "Attempt to decode non-objectID");
  PASNOid subId;
  
  WORD dataLen;
  if (!DecodeASNLength(buffer, offs, dataLen))
    return PFalse;

  value.SetSize(2);

  // handle zero length strings correctly
  if (dataLen != 0)  {

    // start at the second identifier in the buffer, because we will later
    // expand the first number into the first two IDs
    PINDEX i = 1;
    PINDEX s = buffer.GetSize();

    while (dataLen > 0) {
      subId = 0;
      do {    /* shift and add in low order 7 bits */
        if (dataLen == 0 || offs >= s)
          return PFalse;
        subId = (subId << 7) + (buffer[offs] & ~ASN_BIT8);
        dataLen--;
      } while (buffer[offs++] & ASN_BIT8);
      value.SetAt(i++, subId);
    }

    /*
     * The first two subidentifiers are encoded into the first component
     * with the value (X * 40) + Y, where:
     *  X is the value of the first subidentifier.
     *  Y is the value of the second subidentifier.
     */
    subId = value[1];
    if (subId == 0x2B) {
      value[0] = 1;
      value[1] = 3;
    } else {
      value[1] = subId % 40;
      value[0] = (subId - value[1]) / 40;
    }
  }

  return PTrue;
}


PObject * PASNObjectID::Clone() const
{
  return new PASNObjectID(*this);
}



//////////////////////////////////////////////////////////////////////////
//
//  PASNSequence
//     A descendant of PASNObject which is the complex sequence type
//

PASNSequence::PASNSequence()
{
  encodedLen = 0;
  type    = ASNTypeToType[Sequence];
  asnType = Sequence;
}


PASNSequence::PASNSequence(BYTE selector)
{
  encodedLen = 0;
  PAssert(selector < ASN_CONSTRUCTOR, "Sequence selector too big");
  type    = (BYTE)(ASNTypeToType[Choice] | selector);
  asnType = Choice;
}


void PASNSequence::Append(PASNObject * obj)
{
  sequence.Append(obj);
}


void PASNSequence::AppendInteger(PASNInt value)
{
  Append(new PASNInteger(value));
}


void PASNSequence::AppendString (const PString & str)
{
  Append(new PASNString(str));
}


void PASNSequence::AppendObjectID(const PString & str)
{
  Append(new PASNObjectID(str));
}


void PASNSequence::AppendObjectID(PASNOid * val, BYTE len)
{
  Append(new PASNObjectID(val, len));
}


void PASNSequence::PrintOn(ostream & strm) const
{
  strm << "Sequence:" << endl;
  for (PINDEX i = 0; i < sequence.GetSize(); i++)
    strm << sequence[i];
  strm << "End Sequence" << endl; 
}


void PASNSequence::Encode(PBYTEArray & buffer) 
{
  // calculate the length of the sequence, if it hasn't already been done
  if (encodedLen == 0)
    (void)GetEncodedLength();

  // create the header for the sequence. Note that seqLen was calculated
  // by the call to GetEncodedLength above
  EncodeASNSequenceStart(buffer, type, seqLen);

  // now encode the sequence itself
  for (PINDEX i = 0; i < sequence.GetSize(); i++)
    sequence[i].Encode(buffer);
}

PBoolean PASNSequence::Encode(PBYTEArray & buffer, PINDEX maxLen) 
{
  // calculate the length of the sequence, if it hasn't already been done
  if (encodedLen == 0)
    (void)GetEncodedLength();

  // create the header for the sequence. Note that seqLen was calculated
  // by the call to GetEncodedLength above
  EncodeASNSequenceStart(buffer, type, seqLen);

  // now encode the sequence itself
  for (PINDEX i = 0; i < sequence.GetSize(); i++) {
    sequence[i].Encode(buffer);
    if (buffer.GetSize() > maxLen)
      return PFalse;
  }

  return PTrue;
}


WORD PASNSequence::GetEncodedLength()
{
  // calculate the length of the sequence
  if (encodedLen == 0) {
    seqLen = 0;
    for (PINDEX i = 0; i < sequence.GetSize(); i++)
      seqLen = (WORD)(seqLen + sequence[i].GetEncodedLength());
    encodedLen = (WORD)(GetASNSequenceStartLength(seqLen) + seqLen);
  }
  return encodedLen;
}


PASNObject::ASNType PASNSequence::GetType() const
{
  return asnType;
}


int PASNSequence::GetChoice() const
{
  return type;
}


PString PASNSequence::GetTypeAsString() const
{
  return PString("Sequence");
}


PASNSequence::PASNSequence(const PBYTEArray & buffer)

{
  PINDEX ptr = 0;
  if (!Decode(buffer, ptr))
    sequence.RemoveAll();
}


PASNSequence::PASNSequence(const PBYTEArray & buffer, PINDEX & ptr)
{
  if (!Decode(buffer, ptr))
    sequence.RemoveAll();
}


PBoolean PASNSequence::Decode(const PBYTEArray & buffer, PINDEX & ptr)
{
  PINDEX s = buffer.GetSize();
  BYTE   c;

  // all sequences start with a sequence start
  if (ptr >= s)
    return PFalse;

  // get the sequence header
  c = buffer[ptr++];
  if (c == (ASN_CONSTRUCTOR | ASN_SEQUENCE)) 
    asnType = Sequence;
  else if ((c & ~ASN_EXTENSION_ID) == (ASN_CONSTRUCTOR | ASN_CONTEXT)) {
    type    = (BYTE)(c & ASN_EXTENSION_ID);
    asnType = Choice;
  } else
    return PFalse;

  // get the sequence length
  WORD len;
  if (!DecodeASNLength(buffer, ptr, len))
    return PFalse;

  // check the length
  if (ptr + len > s)
    return PFalse;

  // set new length
  s = ptr + len;

  // now decode the elements
  PBoolean   ok = PTrue;
  while (ptr < s && ok) {
    c = buffer[ptr];
    if ((c & ~ASN_EXTENSION_ID) == (ASN_CONSTRUCTOR | ASN_CONTEXT)) 
      sequence.Append(new PASNSequence(buffer, ptr));
    else switch (c) {

      // Integer
      case ASN_INTEGER | ASN_UNIVERSAL | ASN_PRIMITIVE:
        sequence.Append(new PASNInteger(buffer, ptr));
        break;

      // Octet String
      case ASN_OCTET_STR | ASN_UNIVERSAL | ASN_PRIMITIVE:
        sequence.Append(new PASNString(buffer, ptr));
        break;

      // NULL
      case ASN_NULL | ASN_UNIVERSAL | ASN_PRIMITIVE:
        sequence.Append(new PASNNull(buffer, ptr));
        break;

      // Object ID
      case ASN_OBJECT_ID | ASN_UNIVERSAL | ASN_PRIMITIVE:
        sequence.Append(new PASNObjectID(buffer, ptr));
        break;

      // Sequence
      case ASN_CONSTRUCTOR | ASN_SEQUENCE:
        sequence.Append(new PASNSequence(buffer, ptr));
        break;

      // TimeTicks
      case ASN_APPLICATION | 3:
        sequence.Append(new PASNTimeTicks(buffer, ptr));
        break;

      // Counter
      case ASN_APPLICATION | 1:
        sequence.Append(new PASNCounter(buffer, ptr));
        break;

      // Gauge
      case ASN_APPLICATION | 2:
        sequence.Append(new PASNGauge(buffer, ptr));
        break;

      // IP Address
      case ASN_APPLICATION | 0:
        sequence.Append(new PASNIPAddress(buffer, ptr));
        break;

      default:
        return PTrue;
    }
  }

  return ok;
}


PINDEX PASNSequence::GetSize() const
{
  return sequence.GetSize();
}


PASNObject & PASNSequence::operator [] (PINDEX idx) const
{
  return sequence[idx];
}


const PASNSequence & PASNSequence::GetSequence() const
{
  return *this;
}


PString PASNTimeTicks::GetTypeAsString() const
{
  return PString("TimeTicks");
}


PString PASNCounter::GetTypeAsString() const
{
  return PString("Counter");
}


PString PASNGauge::GetTypeAsString() const
{
  return PString("Gauge");
}


PString PASNIPAddress::GetTypeAsString() const
{
  return PString("IPAddress");
}


PString PASNIPAddress::GetString() const
{
  PINDEX len = value.GetSize();
  if (len == 0) 
    return "(empty)";

  if (len < 4) {
    PString out = "Hex";
    for (PINDEX i = 0; i < len; i++)
      out &= psprintf("%02x", (BYTE)value[i]);
    return out;
  }

  return psprintf("%i.%i.%i.%i",
         (BYTE)value[0], (BYTE)value[1],
         (BYTE)value[2], (BYTE)value[3]);
}


PASNIPAddress::PASNIPAddress(const PString & str)
  : PASNString("")
{
  value.SetSize(4);

  PIPSocket::Address addr;
  if (!PIPSocket::GetHostAddress(str, addr))
    addr = 0;

  int i;
  for (i = 0; i < 4; i++)
    value[i] = addr[i];

  valueLen = 4;
}


PIPSocket::Address PASNIPAddress::GetIPAddress () const
{
  return PIPSocket::Address((BYTE)value[0], (BYTE)value[1],
                            (BYTE)value[2], (BYTE)value[3]);
}

PASNNull::PASNNull()
{
}

PASNNull::PASNNull(const PBYTEArray & buffer, PINDEX & ptr)
{
  PAssert(((buffer.GetSize() - ptr) >= 2) &&
          (buffer[ptr+0] == 0x05) &&
          (buffer[ptr+1] == 0x00),
    "Attempt to decode non-null");
  ptr += 2 ;
}

void PASNNull::PrintOn(ostream & strm) const
{
  strm << "Null"
       << endl;
}

void PASNNull::Encode(PBYTEArray & buffer)
{
  EncodeASNHeader(buffer, Null, 0);
}

WORD PASNNull::GetEncodedLength()
{
  return 2;
}

PObject * PASNNull::Clone() const
{
  return new PASNNull();
}

PASNObject::ASNType PASNNull::GetType() const
{
  return Null;
}

PString PASNNull::GetTypeAsString() const
{
  return PString("Null");
}

PString PASNNull::GetString() const
{
  return PString();
}



// End Of File ///////////////////////////////////////////////////////////////
