/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include <asnbase.h>
#include <asnstrng.h>
#include <codesets.h>
#include <chardefs.h>
#include <JonahIni.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <ctype.h>

// CHECK_VALUE_PRESENT verifies that the object contains a value - that is, it 
// either contains an explicit value, or has a default value.
#define CHECK_VALUE_PRESENT \
  if (!is_present() && !is_defaultable()) return ASN_VALUE_NOT_SET

// CHECK_VALUE_VALID verifies that the object has an explicit value.
#define CHECK_VALUE_VALID \
  if (!is_present()) return ASN_VALUE_NOT_SET

#define ASN_CHUNKSIZE 64

class oidName {
public:
  char * val;
  oidName(const char * n) {
    val = (char *)malloc(strlen(n)+1);
    strcpy(val, n);
  };
  ~oidName() {
    free(val);
  };
};

class oidValue {
public:
  unsigned long *p;
  unsigned n;
  oidValue(const char * s) {
    int i;
    i=0; p = NULL; n = 0;
    while (isdigit(s[i])) {
      p = (unsigned long *)realloc(p, sizeof(unsigned long) * (++n));
      p[n-1] = 0;
      while (isdigit(s[i])) {
        p[n-1] = p[n-1] * 10 + (s[i++] - '0');
      };
      if (s[i] == '.') i++;
    };
    if (s[i] != 0) {
      n=0;
      free(p);
      p = NULL;
    };
  };
  ~oidValue() {
    free(p);
  };
};

oidName ** known_OID_names = NULL;
oidValue ** known_OID_values = NULL;
unsigned known_OID_count = 0;

class init_t {
public:
  ~init_t();
};

static init_t init;

void AsnInitialize(void) {

  StringList sl;
  unsigned i;
  char buf[128];

  sl.DuplicatesAllowed = false;
  IniGetKeys("OIDs", sl);
// Now sl contains the names of the OIDs in the inifile
  for (i=0; i<sl.Count(); i++) {
    if (IniReadString("OIDs", sl[i], buf, sizeof(buf))) {
      known_OID_values = (oidValue **)realloc(known_OID_values, (known_OID_count+1) * sizeof(oidValue *));
      known_OID_values[known_OID_count] = new oidValue(buf);
      if (known_OID_values[known_OID_count]->p != NULL) {
        known_OID_names = (oidName **)realloc(known_OID_names, (known_OID_count+1) * sizeof(oidName *));
        known_OID_names[known_OID_count] = new oidName(sl[i]);
        known_OID_count++;
      };
    };
  };
}


init_t::~init_t(void) {
  unsigned i;
  for (i=0; i<known_OID_count; i++) {
    delete known_OID_names[i];
    delete known_OID_values[i];
  };
  free(known_OID_names);  
  free(known_OID_values);  
}

void * memory_funcs_t::malloc(uint32 Size) {
  return ((fnPresent) ? malloc_func(Size)
          : ((xfnPresent) ? xmalloc_func(handle, Size) 
          : ::malloc(Size)));
}

void memory_funcs_t::free(void *MemPtr) {
  ((fnPresent) ? free_func(MemPtr)
  : ((xfnPresent) ? xfree_func(handle, MemPtr) 
  : ::free(MemPtr)));
}

void * memory_funcs_t::realloc(void *MemPtr, uint32 Size) {
  return ((fnPresent) ? realloc_func(MemPtr, Size)
          : ((xfnPresent) ? xrealloc_func(handle, MemPtr, Size) 
          : ::realloc(MemPtr, Size)));
}

void * memory_funcs_t::calloc(uint32 Num, uint32 Size) {
  return ((fnPresent) ? calloc_func(Num, Size)
          : ((xfnPresent) ? xcalloc_func(handle, Num, Size)
          : ::calloc(Num, Size)));
}


static int write1d(buffer_t & s, 
                   unsigned v) {
  if (v > 9) return ASN_INVALID_VALUE;

  s.append((unsigned char)(v) + IA5_0);
  return 0;
}

static int write2d(buffer_t & s, 
                   unsigned v) {
  if (v > 99) return ASN_INVALID_VALUE;

  s.append((unsigned char)(v / 10) + IA5_0);
  s.append((unsigned char)(v % 10) + IA5_0);
  return 0;
}

static int write4d(buffer_t & s, 
                   unsigned v) {
  if (v > 9999) return ASN_INVALID_VALUE;

  s.append((unsigned char)(v / 1000) + IA5_0);
  v = v % 1000;
  s.append((unsigned char)(v / 100) + IA5_0);
  v = v % 100;
  s.append((unsigned char)(v / 10) + IA5_0);
  v = v % 10;
  s.append((unsigned char)(v) + IA5_0);
  return 0;
}


static unsigned dim[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

unsigned daysInMonth(unsigned year, unsigned month) {
// This routine isn't quite correct, as it assumes a Gregorian calendar
// for the entire period 0AD-9999AD.  That's not right in the past, and
// there's no guarantee it'll be correct in the future, but it's good 
// enough.
  if ((month < 1) || (month > 12)) return 0;
  if (month != 2) return dim[month];
  else if (((year % 4) == 0) && ((year % 100) != 0)) return 29;
  else if ((year % 400) == 0) return 29;
  else return 28;
}

static int timeToZulu(unsigned & year, 
                      unsigned & month, 
                      unsigned & day, 
                      unsigned & hour, 
                      unsigned & minute, 
                      int & tz_hour, 
                      int & tz_min) {
// Convert the presented time/TZ to ZULU

  if ((tz_hour > 0) && (tz_min < 0)) return ASN_INVALID_VALUE;
  if ((tz_hour < 0) && (tz_min > 0)) return ASN_INVALID_VALUE;

  if ((tz_hour > 14) || (tz_hour < -14)) return ASN_INVALID_VALUE;
  if ((tz_min > 59) || (tz_min < -59)) return ASN_INVALID_VALUE;

  hour -= tz_hour;
  minute -= tz_min;

  tz_hour = 0;
  tz_min = 0;

// Now do carries...
  if (minute < 0) {
    hour -= 1;
    minute += 60;
  };
  if (minute > 59) {
    hour += 1;
    minute -= 60;
  };

  if (hour < 0) {
    day -= 1;
    hour += 24;
  };
  if (hour > 23) {
    day += 1;
    hour -= 24;
  };

  if (day < 1) {
    month -= 1;
    if (month < 1) {
      year -= 1;
      month = 12;
    };
    day += daysInMonth(year, month);
  };

  if (day > daysInMonth(year, month)) {
    month += 1;
    if (month > 12) {
      year += 1;
      month = 1;
    };
    day = 1;
  };
  
  return 0;
}


static int parseUTCstring(const r_buffer_t & s,
                          unsigned & year,
                          unsigned & month,
                          unsigned & day,
                          unsigned & hour,
                          unsigned & minute,
                          unsigned & second,
                          int & tz_hour,
                          int & tz_min) {
  unsigned i;
  unsigned tzi;
  int tzs;
  if ((s.data_len < 11) || (s.data_len > 17)) return ASN_INVALID_ENCODING;
  for (i=0; i<10; i++)
    if ((s[i] < IA5_0) || (s[i] > IA5_9)) return ASN_INVALID_ENCODING;

  year = (s[0] - IA5_0) * 10 + (s[1] - IA5_0);
  month = (s[2] - IA5_0) * 10 + (s[3] - IA5_0);
  day = (s[4] - IA5_0) * 10 + (s[5] - IA5_0);
  hour = (s[6] - IA5_0) * 10 + (s[7] - IA5_0);
  minute = (s[8] - IA5_0) * 10 + (s[9] - IA5_0);

  if (year < 50) year += 2000;
  else year += 1900;

  if ((s[10] >= IA5_0) && (s[10] <= IA5_9) &&
    (s[11] >= IA5_0) && (s[11] <= IA5_9)) {
    second = (s[10] - IA5_0) * 10 + (s[11] - IA5_0);
    tzi = 12;
  } else {
    second = 0;
    tzi = 10;
  };

  if (s[tzi] == IA5_CAP_Z) {
    tz_hour = 0;
    tz_min = 0;
    if (s.data_len == tzi+1) return 0;
    else return ASN_INVALID_ENCODING;
  } else if (s[tzi] == IA5_PLUS) tzs = 1;
  else if (s[tzi] == IA5_MINUS) tzs = -1;
  else return ASN_INVALID_ENCODING;

  if (s.data_len != tzi + 5) return ASN_INVALID_ENCODING;
  
  for (i=tzi+1;i<tzi+5;i++)
    if ((s[i] < IA5_0) || (s[i] > IA5_9)) return ASN_INVALID_ENCODING;

  tz_hour = ((s[tzi+1] - IA5_0) * 10 + (s[tzi+2] - IA5_0)) * tzs;
  tz_min = ((s[tzi+3] - IA5_0) * 10 + (s[tzi+4] - IA5_0)) * tzs;

  return 0;
}



static int parseGeneralizedstring(const r_buffer_t & s,
                                  unsigned & year,
                                  unsigned & month,
                                  unsigned & day,
                                  unsigned & hour,
                                  unsigned & minute,
                                  unsigned & second,
                                  unsigned & millisecond,
                                  int & tz_hour,
                                  int & tz_min) {
  unsigned i;
  int tzs;
  unsigned ept;
  if ((s.data_len < 14) || (s.data_len > 19)) return ASN_INVALID_ENCODING;
  for (i=0; i<14; i++)
    if ((s[i] < IA5_0) || (s[i] > IA5_9)) return ASN_INVALID_ENCODING;

  year = (((((s[0] - IA5_0) * 10 + 
             (s[1] - IA5_0)) * 10) + 
             (s[2] - IA5_0)) * 10) + 
             (s[3] - IA5_0);
  month = (s[4] - IA5_0) * 10 + (s[5] - IA5_0);
  day = (s[6] - IA5_0) * 10 + (s[7] - IA5_0);
  hour = (s[8] - IA5_0) * 10 + (s[9] - IA5_0);
  minute = (s[10] - IA5_0) * 10 + (s[11] - IA5_0);
  second = (s[12] - IA5_0) * 10 + (s[13] - IA5_0);


  millisecond = 0;
  
  ept = 14;
  if ((ept < s.data_len) && ((s[14] == IA5_COMMA) || (s[14] == IA5_PERIOD))) {
// Parse milliseconds...
    if ((ept < s.data_len) && (s[15] >= IA5_0) && (s[15] <= IA5_9)) {
      millisecond += (s[15] - IA5_0) * 100;
      ept = 16;
      if ((ept < s.data_len) && (s[16] >= IA5_0) && (s[16] <= IA5_9)) {
        millisecond += (s[16] - IA5_0) * 10;
        ept = 17;
        if ((ept < s.data_len) && (s[17] >= IA5_0) && (s[17] <= IA5_9)) {
          millisecond += (s[16] - IA5_0);
          ept = 18;
          while ((ept < s.data_len) && (s[ept] >= IA5_0) && (s[ept] <= IA5_9)) 
            ept++;
        };
      };
    };
  };
// Now ept points either past the end of the string, to the 'Z' or to 
// the timezone indicator.
  if (ept >= s.data_len) {
    tz_hour = 100;
    tz_min = 0;
    return 0;
  };

  if (s[ept] == IA5_CAP_Z) {
    tz_hour = 0;
    tz_min = 0;
    if (s.data_len == ept+1) return 0;
    else return ASN_INVALID_ENCODING;
  } else if (s[ept] == IA5_PLUS) tzs = 1;
  else if (s[ept] == IA5_MINUS) tzs = -1;
  else return ASN_INVALID_ENCODING;

// We've got a timezone to parse...
  if (s.data_len != ept + 5) return ASN_INVALID_ENCODING;
  
  for (i=ept+1;i<ept+5;i++)
    if ((s[i] < IA5_0) || (s[i] > IA5_9)) return ASN_INVALID_ENCODING;

  tz_hour = ((s[ept+1] - IA5_0) * 10 + (s[ept+2] - IA5_0)) * tzs;
  tz_min = ((s[ept+3] - IA5_0) * 10 + (s[ept+4] - IA5_0)) * tzs;

  return 0;
}



static size_t chunk(uint32 x) {
  return (1 + (x / ASN_CHUNKSIZE)) * ASN_CHUNKSIZE;
}



bool check_EOC(r_buffer_t buf) {
  if (buf.data_len < 2) return false;
  if ((buf.data[0] != 0) || (buf.data[1] != 0)) return false;
  return true;
};

r_buffer_t::r_buffer_t(void) {
  data = NULL;
  data_len = 0;
  init_data = NULL;
  init_data_len = 0;
  harmless = 0;
}

r_buffer_t::r_buffer_t(unsigned char * d, uint32 l) {
  data = d;
  data_len = l;
}

void r_buffer_t::set(unsigned char * d, uint32 l) {
  init_data = d; 
  init_data_len = l;
  data = init_data; 
  data_len = init_data_len;
}

void r_buffer_t::reset(void) {
  data = init_data;
  data_len = init_data_len;
}

r_buffer_t & r_buffer_t::operator = (const r_buffer_t & o) {
  init_data = o.init_data;
  data = o.data;
  init_data_len = o.init_data_len;
  data_len = o.data_len;
  return *this;
}

bool r_buffer_t::operator == (const r_buffer_t & o) const {
  if (data_len != o.data_len) return false;
  return (memcmp(data, o.data, data_len) == 0);
}


buffer_t::buffer_t(memory_funcs_t mems) : r_buffer_t () {
  buf_len = 0;
  buf = NULL;
  security = ASN_PUBLIC;
  mem = mems;
}

buffer_t::buffer_t(security_t s) : r_buffer_t () {
  buf_len = 0;
  buf = NULL;
  security = s;
}
  
buffer_t::buffer_t(memory_funcs_t mems,
                   security_t s) : r_buffer_t () {
  buf_len = 0;
  buf = NULL;
  security = s;
  mem = mems;
}
  
buffer_t::buffer_t(uint32 init_size, 
                   security_t s) : r_buffer_t () {
  buf_len = 0;
  buf = NULL;
  security = s;
  extend(init_size);
}
  
buffer_t::buffer_t(uint32 init_size, 
                   memory_funcs_t mems) : r_buffer_t () {
  buf_len = 0;
  buf = NULL;
  security = ASN_PUBLIC;
  mem = mems;
  extend(init_size);
}

buffer_t::buffer_t(uint32 init_size, 
                   memory_funcs_t mems,
                   security_t s) : r_buffer_t () {
  buf_len = 0;
  buf = NULL;
  security = s;
  mem = mems;
  extend(init_size);
}

void buffer_t::detach(void) {
  buf = NULL;
  buf_len = 0; 
  clear();
}

buffer_t::~buffer_t() {
  if (security == ASN_SECRET) {
    memset(buf, 0, buf_len);
  };
  mem.free(buf);
}

int buffer_t::clear(void) { 
  data = buf;
  data_len = 0; 
  if (security == ASN_SECRET) {
    memset(buf, 0, buf_len);
  };
  return 0;
}

int buffer_t::extend(uint32 delta) {
  unsigned char * temp;
  uint32 temp_len = chunk(buf_len + delta);
  uint32 offset = data - buf;
  if (security == ASN_SECRET) {
    if ((temp = (unsigned char *)mem.malloc(temp_len)) == NULL)
      throw "Out of memory in buffer_t::extend";
    memcpy(temp, buf, buf_len);
    memset(buf, 0, buf_len);
    mem.free(buf);
  } else {
    if ((temp = (unsigned char *)mem.realloc(buf, temp_len)) == NULL)
      throw "Out of memory in buffer_t::extend";
  };
  buf = temp;
  buf_len = temp_len;
  init_data = temp;
  init_data_len = temp_len;
  data = buf + offset;
  return 0;
}

int buffer_t::append(unsigned char c) {
  if (buf_len <= data_len) extend();
  buf[data_len++] = c;
  return 0;
}

int buffer_t::append(unsigned char * cp, uint32 len) {
  if (data_len + len > buf_len) extend(len);
  memcpy(&buf[data_len], cp, len);
  data_len+= len;
  return 0;
}

int buffer_t::append_int(long v) {
  char vbuf[11];
  sprintf(vbuf, "%ld", v);
  return append(vbuf);
}

int buffer_t::append(const char * c) {
  return append((unsigned char *)c, strlen(c));
}

int buffer_t::append(const r_buffer_t b) {
  if (data_len + b.data_len > buf_len) extend(b.data_len);
  memcpy(&buf[data_len], b.data, b.data_len);
  data_len += b.data_len;
  return 0;
}


bool asn_composite::check_encode_flags(bool expected) const {
  if (is_encoding_valid() != expected) return false;
  return check_encode_flags();
}

bool asn_composite::check_encode_flags(void) const {
  unsigned i;
  if (!is_encoding_valid()) return true;
  for (i=0; i<child_count; i++) {
    if (get_child(i)->is_optional() && !get_child(i)->is_present()) continue;
    if (!(get_child(i)->check_encode_flags(true))) return false;
  };
  return true;
}


bool asn_choice::check_encode_flags(bool expected) const {
  if (is_encoding_valid() != expected) return false;
  return check_encode_flags();
}

bool asn_choice::check_encode_flags(void) const {
  if (!is_encoding_valid()) return true;
  if (selected() == -1) return false;
  if (!(get_child(selected())->check_encode_flags(true))) return false;
  return true;
}


bool asn_object::check_encode_flags(void) const {
  return true;
}

bool asn_object::check_encode_flags(bool expected) const {
  if (expected != is_encoding_valid()) return false;
  return check_encode_flags();
}

int asn_object::display_state_flags(buffer_t & buf, int inset) const {
  int i;
  for (i=0; i<inset; i++) buf.append(' ');
  buf.append(objType);
  buf.append("(valid=");
  if (is_valid()) buf.append("Y"); else buf.append("N");
  buf.append(", present=");  
  if (is_present()) buf.append("Y"); else buf.append("N");
  buf.append(", optional=");  
  if (is_optional()) buf.append("Y"); else buf.append("N");
  buf.append(", defaultable=");  
  if (is_defaultable()) buf.append("Y"); else buf.append("N");
  buf.append(", enc_valid=");
  if (is_encoding_valid()) buf.append("Y"); else buf.append("N");
  buf.append(")\n");
  return 0;
}

int asn_composite::display_state_flags(buffer_t & buf, int inset) const {
  unsigned i;
  asn_object::display_state_flags(buf, inset);  
  for (i=0; i<child_count; i++) {
    get_child(i)->display_state_flags(buf, inset+2);
  }
  return 0;
}

int asn_choice::display_state_flags(buffer_t & buf, int inset) const {
  unsigned i;
  int j;
  asn_object::display_state_flags(buf, inset);  
  for (i=0; i<child_count; i++) {
    if ((int)i == selected()) {
      for (j=0; j<=inset; j++) buf.append(' ');
      buf.append("SELECTED:\n");
    };
    get_child(i)->display_state_flags(buf, inset+2);
  }
  return 0;
}



int asn_object::set_optional(bool opt) {
  value_optional = opt;
  invalidate_encoding();  
  return 0;
}

int asn_object::emptyi(void) {
  return 0;
}

int asn_object::empty(void) {
  emptyi();
  invalidate_value();
  return 0;
}

asn_object & asn_object::operator = (const asn_object & o) {
  throw "Can't assign asn_objects";
  return *this;
}

asn_object::asn_object(const asn_object & o) {
  throw "Can't copy asn_objects";
};

int asn_object::normalize(void) {
// default behavior is to do nothing, as most asn_objects are automatically normalized
  return 0;  
}
  
asn_object * asn_object::get_default_value(void) const {
  return internalDefaultValue;
}

const asn_object * asn_object::get_parent(void) {
  return parent;
}

bool asn_object::is_encoding_valid(void) const {
  return encoding_valid;
}

bool asn_object::is_valid(void) const {
  return (is_present() || is_defaultable() || is_optional());
}

bool asn_object::is_present(void) const {
  return (value_valid);
}

bool asn_object::operator == (const asn_object & o) const {
  buffer_t temp1, temp2;
  write(temp1);
  o.write(temp2);
  if (temp1.data_len != temp2.data_len) return false;
  return (memcmp(temp1.data, temp2.data, temp1.data_len) == 0);
}

void asn_object::check_valid(void) {
// This member will get invoked in a non-composite asn_object only if the
// default value of the object is changed.  We don't need to do anything
// when this happens.
}


int asn_object::set_preread(void (* callback)(asn_object * o, r_buffer_t & buf)) {
  preread_callback = callback;
  return 0;
}

int asn_object::set_postread(void (* callback)(asn_object * o, r_buffer_t & buf, int status)) {
  postread_callback = callback;
  return 0;
}

int asn_object::read(r_buffer_t & buf) {
  int result;
  int is_constructed;
  int is_definite;
  uint32 value_length;
  unsigned char * saved_ptr = buf.data;
  uint32 saved_length = buf.data_len;
  uint32 tagRead;
  int classRead;
  
  invalidate_value();
  constructed = false;
  indefinite_length = false;
  
  if (preread_callback) preread_callback(this, buf);
  
  result = AsnGetType(&buf.data, 
                      &buf.data_len,
                      &tagRead,
                      &is_constructed,
                      &classRead);
  if (result) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    if (is_optional() || is_defaultable()) {
  
      if (postread_callback) postread_callback(this, buf, 0);
  
      return 0;
    };
    
    if (postread_callback) postread_callback(this, buf, result);
  
    return result;
  };
  if (!check_type(tagRead, classRead)) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    if (is_optional() || is_defaultable()) {
      if (postread_callback) postread_callback(this, buf, 0);
      return 0;
    }
    if (postread_callback) postread_callback(this, buf, ASN_WRONG_TYPE);
  
    return ASN_WRONG_TYPE;
  };
  set_tag(tagRead);
  set_class(classRead);
  constructed = (is_constructed != 0);
  if (constructed && !encoding_constructed) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    
    if (postread_callback) postread_callback(this, buf, ASN_MUST_BE_PRIMITIVE);
  
    return ASN_MUST_BE_PRIMITIVE;
  };
  if (!constructed && !encoding_primitive) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    
    if (postread_callback) postread_callback(this, buf, ASN_MUST_BE_CONSTRUCTED);
  
    return ASN_MUST_BE_CONSTRUCTED;
  };
  result = AsnGetLength(&buf.data,
                        &buf.data_len,
                        &is_definite,
                        &value_length);
  if (result) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    
    if (postread_callback) postread_callback(this, buf, result);
  
    return result;
  };
  indefinite_length = !is_definite;
// If indefinite length encoded, check that's allowed.
  if (indefinite_length && !encoding_indefinite) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    
    if (postread_callback) postread_callback(this, buf, ASN_INDEFINITE_NOT_ALLOWED);
  
    return ASN_INDEFINITE_NOT_ALLOWED;
  };
  if (value_length > buf.data_len) {
    buf.data = saved_ptr;
    buf.data_len = saved_length;
    
    if (postread_callback) postread_callback(this, buf, ASN_NO_MORE_DATA);
  
    return ASN_NO_MORE_DATA;
  };
  result = decode_value(buf, value_length);
  if (result != 0) {
    invalidate_value();
    buf.data = saved_ptr;
    buf.data_len = saved_length;
  } else if (indefinite_length) {
    if (check_EOC(buf)) {
      buf.data+=2;
      buf.data_len-=2;
      set_value_valid();
    } else {
      invalidate_value();
      result = ASN_INVALID_ENCODING;
    };
  } else set_value_valid();
    
  if (postread_callback) postread_callback(this, buf, result);

  return result;

}  

bool asn_object::is_optional(void) const {
  return value_optional;
}

bool asn_object::is_defaultable(void) const {
  return value_defaultable;
}

bool asn_object::check_type(uint32 tagRead, int classRead) const {
// Most objects have to be of a fixed type.  However, objects that can be encoded as a 
// variety of class or type marks can override this function with a weaker check.
  return (tagRead == asn_tag) && (classRead == asn_class);
}

void asn_object::set_secure(security_t s) {
  security = s;
  encode_cache.security = s;
}

void asn_object::invalidate_encoding(void) const {
// Whenever an encoding becomes invalid, the encoding of any parents in the hierarchy
// must also be invalidated.
  asn_object * this_var = (asn_object *)this;
//  if (is_encoding_valid()) {
// For now, this optimization is removed, as it's not quite correct
    this_var->encoding_valid = false;
    if (parent != NULL) parent->invalidate_encoding();
//  };
}

void asn_object::invalidate_value(invalidate_t dir) {
  value_valid = 0;
//  if (is_encoding_valid()) invalidate_encoding();
  invalidate_encoding();
  if (parent && (dir != INVALIDATE_CHILD)) parent->check_valid();
}

void asn_object::set_value_valid(void) {
  value_valid = 1;
  if (parent) parent->check_valid();
}

int asn_object::write_type(buffer_t & buf) const {
  unsigned char t;
  uint32 theTag = asn_tag;
  switch (asn_class) {
    case CLASS_UNIVERSAL : t = 0; break;
    case CLASS_APPLICATION : t = 0x40u; break;
    case CLASS_CONTEXT_SPECIFIC : t = 0x80u; break;
    case CLASS_PRIVATE : t = 0xc0u; break;
  };
  if (generate_constructed) t |= 0x20;
  if (asn_tag <= 30) {
// Low tag form
    buf.append((unsigned char)(t + asn_tag));
  } else {
// High tag form
    buf.append(t + 0x1fu);
    if ((asn_tag & ~0x7flu) == 0) {
      buf.append((unsigned char)(asn_tag));
    } else if ((asn_tag & ~0x3ffflu) == 0) {
      buf.append((unsigned char)(((asn_tag >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(asn_tag & 0x7fu));
    } else if ((asn_tag & ~0x1ffffflu) == 0) {
      buf.append((unsigned char)(((asn_tag >> 14) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((asn_tag >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(asn_tag & 0x7fu));
    } else if ((asn_tag & ~0xffffffflu) == 0) {
      buf.append((unsigned char)(((asn_tag >> 21) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((asn_tag >> 14) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((asn_tag >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(asn_tag & 0x7fu));
    } else {
      buf.append((unsigned char)(((asn_tag >> 28) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((asn_tag >> 21) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((asn_tag >> 14) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((asn_tag >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(asn_tag & 0x7fu));
    };
  };
  return 0;
}

int asn_object::write_length(buffer_t & buf) const {
  if (!is_encoding_valid()) return ASN_INVALID_ENCODING;
  if (encode_cache.data_len <= 0x7fu) {
// Short-form length  
    buf.append((unsigned char)(encode_cache.data_len));
  } else {
// Long-form length 
    if (encode_cache.data_len <= 0xffu) {
      buf.append((unsigned char)(0x81u));
      buf.append((unsigned char)(encode_cache.data_len));
    } else if (encode_cache.data_len <= 0xffffu) {
      buf.append((unsigned char)(0x82u));
      buf.append((unsigned char)((encode_cache.data_len>>8) & 0xffu));
      buf.append((unsigned char)((encode_cache.data_len) & 0xffu));
    } else if (encode_cache.data_len <= 0xffffffu) {
      buf.append((unsigned char)(0x83u));
      buf.append((unsigned char)((encode_cache.data_len>>16) & 0xffu));
      buf.append((unsigned char)((encode_cache.data_len>>8) & 0xffu));
      buf.append((unsigned char)((encode_cache.data_len) & 0xffu));
    } else {
      buf.append((unsigned char)(0x84u));
      buf.append((unsigned char)((encode_cache.data_len>>24) & 0xffu));
      buf.append((unsigned char)((encode_cache.data_len>>16) & 0xffu));
      buf.append((unsigned char)((encode_cache.data_len>>8) & 0xffu));
      buf.append((unsigned char)((encode_cache.data_len) & 0xffu));
    };
  };
  return 0;
}

int asn_object::get_encoding(void) const {
  int result;
  asn_object * this_var = (asn_object *)this;
  CHECK_VALUE_PRESENT;
  if (!is_encoding_valid()) {
    this_var->encode_cache.clear();
    if (is_present()) {
      if ((result = encode_value(this_var->encode_cache)) != 0) 
        return result;
    } else if ((result = get_default_value()->get_encoding(this_var->encode_cache)) != 0)
      return result;
    this_var->encoding_valid = true;
  };
  return 0;
}

int asn_object::get_encoding(buffer_t & buf) const {
  int result;
  if ((result = get_encoding()) != 0) return result;
  buf.append(encode_cache);
  return 0;
}

int asn_object::write(buffer_t & buf) const {
  int result;
  uint32 offset = buf.data_len;

// If it's optional but absent, we don't need to do anything
  if (is_optional() && !is_present()) return 0;  

// If it's its default value, we also don't need to do anything
  if (is_defaultable() && is_default_value()) return 0;

// We need to generate some DER.  Do we have a value?
  CHECK_VALUE_PRESENT;

  if ((result = write_type(buf)) != 0) return result;
  if ((result = get_encoding()) != 0) {
      buf.data_len = offset;
      return result;
  };
  if ((result = write_length(buf)) != 0) {
    buf.data_len = offset;
    return result;
  }
  return buf.append(encode_cache);
}

asn_object::asn_object(security_t s) {
  constructed = false;
  indefinite_length = false;
  internalDefaultValue = NULL;
  encoding_valid = false;
  encode_cache.security = s;
  parent = NULL;
  strcpy(objType, "OBJECT");
  value_valid = false;
  value_defaultable = false;
  value_optional = false;
  encoding_primitive = false;
  encoding_constructed = false;
  encoding_indefinite = false;
  generate_constructed = false;
  preread_callback = NULL;
  postread_callback = NULL;
  invalidate_value();
}

void asn_object::set_parent(asn_object * p) {
  parent = p;
}

void asn_object::set_class(int the_class) {
  asn_class = the_class;
  invalidate_encoding();
}

void asn_object::set_tag(uint32 the_tag) {
  asn_tag = the_tag;
  invalidate_encoding();
}

int asn_object::get_class(void) const {
  return asn_class;
}

uint32 asn_object::get_tag(void) const {
  return asn_tag;
}


void asn_object::set_default_value(asn_object * o) {
  value_defaultable = false;  // Just in case there's an error while we're setting it
  invalidate_encoding();
  if (internalDefaultValue) delete internalDefaultValue;
  internalDefaultValue = o;
  o->set_parent(this);
  value_defaultable = true;
}

asn_object::~asn_object() {
  if (internalDefaultValue) delete internalDefaultValue;
  internalDefaultValue = NULL;
  strcpy(objType, "Deleted");
}

bool asn_object::is_default_value(void) const {
  buffer_t temp_encode;

// If the object isn't defaultable, it can't be its default value
  if (!is_defaultable()) return 0;  

// If the object value isn't set, make it its default value.
  if (!is_present()) return 1;

// The object has a value assigned.  Is it the same as the default value?
  if (get_encoding()) return 0;
  get_default_value()->get_encoding(temp_encode);
  return (encode_cache == temp_encode);
}

int asn_integer::decode_value(r_buffer_t & buf, uint32 value_length) {
  invalidate_value();
  str_val.clear();
  is_int = false;
  str_val.append(buf.data, value_length);
  buf.data += value_length;
  buf.data_len -= value_length;
  update_int_val();
  set_value_valid();
  return 0;
}

int asn_integer::encode_value(buffer_t & buf) const {
  buf.append(str_val);
  return 0;
}

int asn_integer::get_value(long & val) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_integer *)get_default_value())->get_value(val);
  if (!is_int) return ASN_VALUE_OVERFLOW;
  val = int_val;
  return 0;
}

int asn_integer::set_value(long val) {
  int i;
  bool copying = false;
  unsigned char c;
  invalidate_value();
  int_val = val;
  str_val.clear();
  is_int = true;
// Copy the minimum number of bytes necessary to represent the value...
  for (i=sizeof(val)-1; i>=0; i--) {
    c = (val >> (i*8)) & 0xffu;
    if (copying || 
        ((val >= 0) && (c != 0)) ||
        ((val < 0) && (c != 0xffu))) {
// Here once we've skipped over leading zero/ff bytes.
      if (!copying) {
// This is the first significant byte of a number.  Make sure that the
// leading bit of what's left is consistent with the sign of the number.
        if ((val >= 0) && ((c & 0x80u) != 0)) {
// This is the first significant byte of a positive number, but its leading
// bit is set.  Prepend a zero byte.
          str_val.append((unsigned char) 0);
        } else if ((val < 0) && ((c & 0x80u) == 0)) {
// This is the first significant byte of a negative number, but its leading
// bit is clear.  Prepend a 0xff byte.
          str_val.append((unsigned char) 0xffu);
        };
      };
      str_val.append(c);
      copying = true;
    };
  };
  if (str_val.data_len == 0) {
// The value was zero or -1, which the above algorithm doesn't cope 
// with, so special-case these values.
    if (val >= 0) str_val.append((unsigned char) 0);
    else str_val.append((unsigned char) 0xffu);
  };
  set_value_valid();
  return 0;
}

int asn_integer::set_default_value(long val) {
  asn_integer * i = new asn_integer;
  i->set_tag(get_tag());
  i->set_class(get_class());
  i->set_value(val);
  asn_primitive::set_default_value(i);
  return 0;
}

int asn_integer::set_default_value(unsigned char * p, uint32 s) {
  asn_integer * i = new asn_integer;
  i->set_tag(get_tag());
  i->set_class(get_class());
  i->set_value(p,s);
  asn_primitive::set_default_value(i);
  return 0;
}

int asn_integer::get_value(unsigned char * &p, uint32 & s) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_integer *)get_default_value())->get_value(p,s);
  p = str_val.data;
  s = str_val.data_len;
  return 0;
}

int asn_integer::set_value(unsigned char * p, uint32 s) {
  invalidate_value();
  str_val.clear();
  int_val = 0;
  str_val.append(p, s);
  update_int_val();
  set_value_valid();
  return 0;
}

void asn_integer::update_int_val(void) {
  unsigned i;
  unsigned long temp_val = 0;
  if (str_val.data_len <= 4) {
// We can also store the value as a native integer.
// This next piece of code doesn't assume that assignments from unsigned long to long 
// will preserve bit pattern for values that don't fit in a long.  If the implementation 
// does preserve such bit patterns, defining the pre-processor symbol DirectLongConv 
// will result in slightly faster code.  In either case, the use of two's complement 
// representation for longs is assumed.
    is_int = true;
    for (i=1; i<=str_val.data_len; i++) {
      temp_val |= (unsigned long)(str_val.data[i-1]) << ((str_val.data_len-i) * 8);
    }
    if (str_val.data_len == 0) {
      temp_val = 0;
    } else if (str_val.data[0] & 0x80u) {
// The value is negative, so we have to sign-extend it if it's shorter than a long
      for (i=str_val.data_len; i<sizeof(temp_val); i++) temp_val |= (0xffu << (i * 8));
    };
// Now we have the correct integer bit-pattern in temp_val.  bitblt it into int_val.
#ifdef DirectLongConv
    int_val = (long)temp_val;
#else
    memcpy(&int_val, &temp_val, sizeof(long));
#endif
  } else is_int = false;
}

void asn_integer::set_secure(security_t s) {
  str_val.security = s;
  asn_primitive::set_secure(s);
}

asn_object * asn_composite::operator [] (unsigned i) const {
  return get_child(i);
}

asn_object * asn_composite::get_child(unsigned i) const {
  if (i < child_count) return child[i];
  else return NULL;
}

int asn_composite::emptyi(void) {
  unsigned i;
  int result;
  for (i=0; i<child_count; i++) if ((result = child[i]->emptyi()) != 0) return result;
  return 0;
}

int asn_composite::normalize(void) {
  unsigned i;
  int result;
  for (i=0;i<child_count;i++) if ((result = child[i]->normalize()) != 0) return result;
  return 0;
}

void asn_composite::invalidate_value(invalidate_t dir) {
  unsigned i;
  if (dir != INVALIDATE_PARENT) {
    for (i=0;i<child_count;i++) child[i]->invalidate_value(INVALIDATE_CHILD);
  };
  asn_object::invalidate_value(INVALIDATE_PARENT);

}

int asn_composite::register_child(asn_object * c) {
  asn_object ** temp_children;
  temp_children = child;
  if (child_count >= children_size) {
    child = (asn_object **)malloc((child_count + 1) * sizeof(asn_object *));
    if (child == NULL) throw "Out of memory in asn_composite::register_child";
    if (temp_children) {
      memcpy(child, temp_children, child_count * sizeof(asn_object *));
      free(temp_children);
    };
    children_size=child_count+1;
  };
  child[child_count++] = c;
  c->set_parent(this);
  if (security == ASN_SECRET) c->set_secure(ASN_SECRET);
  check_valid();
  return 0;
}

int asn_composite::register_child_before(asn_object * c) {
  asn_object ** temp_children;
  temp_children = child;
  if (child_count >= children_size) {
    child = (asn_object **)malloc((child_count + 1) * sizeof(asn_object *));
    if (child == NULL) throw "Out of memory in asn_composite::register_child";
    child[0] = c;
    if (temp_children) {
      memcpy(&(child[1]), temp_children, child_count * sizeof(asn_object *));
      free(temp_children);
    };
    children_size=child_count+1;
  };
  child_count++;
  c->set_parent(this);
  if (security == ASN_SECRET) c->set_secure(ASN_SECRET);
  return 0;
}

asn_composite::asn_composite(security_t s) : asn_object(s) {
  security = s;
  constructed = true;
  encoding_primitive = false;
  encoding_constructed = true;
  encoding_indefinite = true; // Allow indefinite encoding
  generate_constructed = true;
  child_count = 0; child = NULL;
  children_size = 0;
}

asn_composite::asn_composite(unsigned children,
                             security_t s) : asn_object(s) {
  unsigned i;
  security = s;
  encoding_primitive = false;
  encoding_constructed = true;
  child_count = 0; child = NULL;
  children_size = children;
  child = (asn_object **)malloc(children * sizeof(asn_object *));
  if (child == NULL) throw "Out of memory in asn_composite::asn_composite";
  for (i=0;i<children_size;i++) child[i] = NULL;
}

asn_composite::~asn_composite() {
  if (child) free(child);
  child = NULL;
}

void asn_composite::check_valid(void) {
// Called by a child when it has become valid or invalid.  By default, constructed objects 
// are valid only when all their children are valid (although asn_choice will override
// this), so we need to check the validity of all our children.
  unsigned i;
  for (i=0; i < child_count; i++) {
    if (!child[i]->is_valid()) {
      if (is_present()) invalidate_value(INVALIDATE_PARENT);
      return;
    };
  };
// All children are valid, so we are too!
  if (!is_present()) set_value_valid();
}


int asn_bitstring::decode_value(r_buffer_t & buf, 
                                uint32 value_length) {
// Bitstring values may be constructed, in which case they may also use indefinite 
// length encoding.
  int result;
  bool done = false;
  bool expecting_done = false;
  bool data_read = false;
  unsigned char * p;
  uint32 bc;
  invalidate_value(); 
  str_val.clear();
  if (!constructed) {
// Everything's simple
    if (value_length == 0) return ASN_BAD_UNUSED_BITCOUNT;
    unused_bits = *buf.data;
    if (unused_bits > 7) return ASN_BAD_UNUSED_BITCOUNT;
    data_read = true;
    str_val.append(buf.data+1, value_length-1);
    buf.data += value_length;
    buf.data_len -= value_length;
  } else {
// The bit-string is encoded as a sequence of partial strings.
    asn_bitstring subString(security);
    r_buffer_t subBuffer = buf;
    if (!indefinite_length) subBuffer.data_len = value_length;
    while (!done) {
      if (indefinite_length) {
        if (check_EOC(subBuffer)) done = 1;
      } else if (subBuffer.data_len == 0) done = 1;
      else if (expecting_done) {
        return ASN_SEGMENTED_BITCOUNT_ERROR;
      };
      if (!done) {
        result = subString.read(subBuffer);
        if (result) return result;
        data_read = true;
        subString.get_value(p, bc);
        unused_bits = (unsigned char)((8 - (bc % 8)) % 8);
        expecting_done = (unused_bits != 0);
        str_val.append(p, (bc+7) / 8);
      };
    };
    if (indefinite_length) {
      buf = subBuffer;
    } else {
      buf.data_len -= value_length;
      buf.data += value_length;
    };
  };
  if (!data_read) return ASN_BAD_UNUSED_BITCOUNT;
// Now the value's been read.  Clamp ununsed trailing bits to zero...
  switch (unused_bits) {
  case 0: break;
  case 1: str_val.data[str_val.data_len-1] &= 0xfeu; break;
  case 2: str_val.data[str_val.data_len-1] &= 0xfcu; break;
  case 3: str_val.data[str_val.data_len-1] &= 0xf8u; break;
  case 4: str_val.data[str_val.data_len-1] &= 0xf0u; break;
  case 5: str_val.data[str_val.data_len-1] &= 0xe0u; break;
  case 6: str_val.data[str_val.data_len-1] &= 0xc0u; break;
  case 7: str_val.data[str_val.data_len-1] &= 0x80u; break;
  };

  set_value_valid();
  return 0;
}

int asn_bitstring::encode_value(buffer_t & buf) const {
  CHECK_VALUE_VALID;
  buf.append(unused_bits);
  buf.append(str_val);
  return 0;
}

int asn_bitstring::get_value(unsigned char * &p, uint32 & bc) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_bitstring *)get_default_value())->get_value(p, bc);
  p = str_val.data;
  bc = str_val.data_len * 8 - unused_bits;
  return 0;
}

int asn_bitstring::set_default_value(unsigned char * p, uint32 bc) {
  asn_bitstring * i = new asn_bitstring;
  i->set_tag(get_tag());
  i->set_class(get_class());
  i->set_value(p, bc);
  asn_primitive::set_default_value(i);
  return 0;
}

int asn_bitstring::set_value(unsigned char * p, uint32 bc) {
  uint32 octet_count;
  invalidate_value();
  str_val.clear();
  octet_count = bc / 8;
  unused_bits = (unsigned char)(bc % 8);
  str_val.append(p, octet_count);
  switch (unused_bits) {
    case 0: break;
    case 1: str_val.append(p[octet_count] & 0x80u); break;
    case 2: str_val.append(p[octet_count] & 0xc0u); break;
    case 3: str_val.append(p[octet_count] & 0xe0u); break;
    case 4: str_val.append(p[octet_count] & 0xf0u); break;
    case 5: str_val.append(p[octet_count] & 0xf8u); break;
    case 6: str_val.append(p[octet_count] & 0xfcu); break;
    case 7: str_val.append(p[octet_count] & 0xfeu); break;
  };
  set_value_valid();
  return 0;
}

void asn_bitstring::set_secure(security_t s) {
  str_val.security = s;
  asn_primitive::set_secure(s);
}


int asn_namedbits::write(buffer_t & buf) const {
  asn_namedbits * var_this = (asn_namedbits *) this;

// If it's optional but absent, we don't need to do anything
  if (is_optional() && !is_present()) return 0;  

// If it's its default value, we also don't need to do anything
// This test really needs to be tightened up, since is_default_value() doesn't
// know that trailing unused bits are to be taken as zero.  But for now...
  if (is_defaultable() && is_default_value()) return 0;

  CHECK_VALUE_PRESENT;

// Remove trailing zero bytes from the value, then trailing zero bits...
  while ((str_val.data_len > 0) && (str_val[str_val.data_len-1] == 0)) {
    var_this->unused_bits = 0;
    var_this->str_val.data_len--;
  };
  if (str_val.data_len > 0) {
    if ((str_val[str_val.data_len-1] & 0x7fu) == 0) var_this->unused_bits = 7;
    else if ((str_val[str_val.data_len-1] & 0x3fu) == 0) var_this->unused_bits = 6;
    else if ((str_val[str_val.data_len-1] & 0x1fu) == 0) var_this->unused_bits = 5;
    else if ((str_val[str_val.data_len-1] & 0x0fu) == 0) var_this->unused_bits = 4;
    else if ((str_val[str_val.data_len-1] & 0x07u) == 0) var_this->unused_bits = 3;
    else if ((str_val[str_val.data_len-1] & 0x01u) == 0) var_this->unused_bits = 1;
  };
  return asn_bitstring::write(buf);
}

int asn_namedbits::set_bit(unsigned bitNum, bool value) {
  uint32 byteNum; 
  unsigned bit;
  byteNum = bitNum / 8;
  bit = bitNum % 8;
  if (!is_present()) {
    str_val.clear();
  };
  invalidate_value();

  if (value) {
    if ((byteNum + 1) > str_val.data_len) {
// We need to add empty bytes to the end of the value...
      while ((byteNum + 1) > str_val.data_len) {
        str_val.append((unsigned char) 0);
      };
      unused_bits = (unsigned char)(7 - bit);
    } else {
      if (unused_bits > (7 - bit)) {
        unused_bits = 7 - bit;
      };
    };
    switch (bit) { 
    case 0: str_val[byteNum] |= (unsigned char)0x80u; break;
    case 1: str_val[byteNum] |= (unsigned char)0x40u; break;
    case 2: str_val[byteNum] |= (unsigned char)0x20u; break;
    case 3: str_val[byteNum] |= (unsigned char)0x10u; break;
    case 4: str_val[byteNum] |= (unsigned char)0x08u; break;
    case 5: str_val[byteNum] |= (unsigned char)0x04u; break;
    case 6: str_val[byteNum] |= (unsigned char)0x02u; break;
    case 7: str_val[byteNum] |= (unsigned char)0x01u; break;
    };
  } else {
    switch (bit) { 
    case 0: str_val[byteNum] &= ~(unsigned char)0x80u; break;
    case 1: str_val[byteNum] &= ~(unsigned char)0x40u; break;
    case 2: str_val[byteNum] &= ~(unsigned char)0x20u; break;
    case 3: str_val[byteNum] &= ~(unsigned char)0x10u; break;
    case 4: str_val[byteNum] &= ~(unsigned char)0x08u; break;
    case 5: str_val[byteNum] &= ~(unsigned char)0x04u; break;
    case 6: str_val[byteNum] &= ~(unsigned char)0x02u; break;
    case 7: str_val[byteNum] &= ~(unsigned char)0x01u; break;
    };
  };
  set_value_valid();
  return 0;
}


const uint32 asn_namedbits::get_bit(unsigned bitNum, bool & value) {
  uint32 byteNum; 
  unsigned bit;

  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_namedbits *)get_default_value())->get_bit(bitNum, value);

  byteNum = bitNum / 8;
  bit = bitNum % 8;
  if (byteNum >= str_val.data_len) {
    value = false;
  } else {
    switch (bit) {
    case 0: value = ((str_val[byteNum] & 0x80u) != 0); break;
    case 1: value = ((str_val[byteNum] & 0x40u) != 0); break;
    case 2: value = ((str_val[byteNum] & 0x20u) != 0); break;
    case 3: value = ((str_val[byteNum] & 0x10u) != 0); break;
    case 4: value = ((str_val[byteNum] & 0x08u) != 0); break;
    case 5: value = ((str_val[byteNum] & 0x04u) != 0); break;
    case 6: value = ((str_val[byteNum] & 0x02u) != 0); break;
    case 7: value = ((str_val[byteNum] & 0x01u) != 0); break;
    };
  };
  return 0;
}


int asn_boolean::decode_value(r_buffer_t & buf, 
                              uint32 value_length) {
  invalidate_value();
  if (value_length != 1) return ASN_INVALID_ENCODING;
  value = (buf.data[0] != 0);
  buf.data++;
  buf.data_len--;
  set_value_valid();
  return 0;
}

int asn_boolean::encode_value(buffer_t & buf) const {
  CHECK_VALUE_VALID;
  if (value) buf.append((unsigned char)0xffu);
  else buf.append((unsigned char)0);
  return 0;
}

int asn_boolean::get_value(bool &b) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_boolean *)get_default_value())->get_value(b);
  b = value;
  return 0;
}

int asn_boolean::set_value(bool b) {
  invalidate_value();
  value = b;
  set_value_valid();
  return 0;
}

int asn_boolean::set_default_value(bool b) {
  asn_boolean * i = new asn_boolean;
  i->set_tag(get_tag());
  i->set_class(get_class());
  i->set_value(b);
  asn_primitive::set_default_value(i);
  return 0;
};



int asn_octetstring::decode_value(r_buffer_t & buf, 
                                  uint32 value_length) {
// Octetstring values may be constructed, in which case they may also use indefinite 
// length encoding.
  int result;
  bool done = false;
  unsigned char * p;
  uint32 bc;
  invalidate_value(); 
  str_val.clear();
  if (!constructed) {
// Everything's simple
    str_val.append(buf.data, value_length);
    buf.data += value_length;
    buf.data_len -= value_length;
  } else {
// The octet-string is represented as a sequence of partial strings.
    asn_octetstring subString(security);
    r_buffer_t subBuffer = buf;
    if (!indefinite_length) subBuffer.data_len = value_length;
    while (!done) {
      if (indefinite_length) {
        if (check_EOC(subBuffer)) done = 1;
      } else if (subBuffer.data_len == 0) done = 1;
      if (!done) {
        result = subString.read(subBuffer);
        if (result) return result;
        subString.get_value(p, bc);
        str_val.append(p, bc);
      };
    };
    if (indefinite_length) {
      buf = subBuffer;
    } else {
      buf.data_len -= value_length;
      buf.data += value_length;
    };
  };

// Null-terminate the buffer.  This isn't needed here, but it's useful for the
// charstring types which are derived from this
    if (str_val.data_len >= str_val.buf_len) str_val.extend(1);
    str_val.data[str_val.data_len] = 0;

    set_value_valid();
    return 0;
}

int asn_octetstring::encode_value(buffer_t & buf) const {
  CHECK_VALUE_VALID;
  buf.append(str_val);
  return 0;
}

int asn_octetstring::get_value(unsigned char * &p, uint32 & bc) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_octetstring *)get_default_value())->get_value(p, bc);
  p = str_val.data;
  bc = str_val.data_len;
  return 0;
}

int asn_octetstring::set_default_value(unsigned char * p, uint32 bc) {
  asn_octetstring * i = new asn_octetstring;
  i->set_tag(get_tag());
  i->set_class(get_class());
  i->set_value(p, bc);
  asn_primitive::set_default_value(i);
  return 0;
}

int asn_octetstring::set_value(unsigned char * p, uint32 bc) {
  invalidate_value();
  str_val.clear();
  str_val.append(p, bc);
  set_value_valid();
  return 0;
}

void asn_octetstring::set_secure(security_t s) {
  str_val.security = s;
  asn_primitive::set_secure(s);
}


int asn_null::decode_value(r_buffer_t & buf, 
                           uint32 value_length) {
  if (value_length != 0) return ASN_INVALID_ENCODING;
  return 0;
}

int asn_null::encode_value(buffer_t & buf) const {
  return 0;
}


int asn_object::display(buffer_t & s) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) {
    s.append("Default:");
    return (get_default_value())->display(s);
  };
  s.append("OBJECT(tag=");
  s.append_int(get_tag());
  s.append(", class=");
  s.append_int(get_class());
  s.append(")");
  return 0;
}

bool asn_oid::operator == (const asn_oid & o) const {
  unsigned long *p1;
  unsigned n1;
  unsigned long *p2;
  unsigned n2;
  unsigned i;

  if (get_value(p1, n1)) return false;
  if (o.get_value(p2, n2)) return false;
  if (n1 != n2) return false;
  for (i=0; i<n1; i++) if (p1[i] != p2[i]) return false;
  return true;
} 

bool asn_oid::operator != (const asn_oid & o) const {
  return !(*this == o);
}

int asn_oid::display_printable(buffer_t & s) const {
  unsigned i;
  int result;
  buffer_t temp;
  result = display(temp);
  if (result) return result;
  for (i=0; i<temp.data_len; i++) temp[i] = Local2IA5(temp[i]);
  return s.append(temp);
}

int asn_oid::display(buffer_t & s) const {
  unsigned i;
  CHECK_VALUE_PRESENT;
  if (!is_present()) {
    return (get_default_value())->display(s);
  };
  for (i=0; i<id_count; i++) {
    if (i>0) s.append(".");
    s.append_int(id[i]);
  };
  return 0;
}

int asn_oid::display_name(buffer_t & s) const {
  unsigned i;

  for (i=0; i< known_OID_count; i++) {
    if (is_equal(known_OID_values[i]->p, known_OID_values[i]->n)) {
      s.append(known_OID_names[i]->val);
      return 0;
    };
  };
  return ASN_CANT_CONVERT;
}

int asn_oid::display_name_printable(buffer_t & s) const {
  int result;
  unsigned i;
  if ((result = display_name(s)) != 0) return result;

  for (i=0; i<s.data_len; i++) s[i] = Local2IA5(s[i]);
  return 0;
}

int asn_oid::set_value(char * s) {
// s is a null-terminated character string containing an OID name.
// See whether it's a name we recognize...
  unsigned i;
  for (i=0; i< known_OID_count; i++) {
    if (strcmp(known_OID_names[i]->val, s) == 0) {
      return set_value(known_OID_values[i]->p, known_OID_values[i]->n);
    };
  };

// We didn't find the name.  Is it a dot-separated series of integers?
  return ASN_CANT_CONVERT;  

}

int asn_composite::display(buffer_t & s) const {
  unsigned i;
  int result;
  s.append("COMPOSITE-OBJECT(tag=");
  s.append_int(get_tag());
  s.append(", class=");
  s.append_int(get_class());
  s.append(") {");
  for (i=0; i<child_count; i++) {
    result = child[i]->display(s);
    if (result) return result;
  }
  return 0;
}


int asn_oid::emptyi(void) {
  id_count = 0;
  if (id != NULL) id[id_count] = 0;
  return 0;
}

int asn_oid::append_subident(unsigned long p) {
  invalidate_encoding();

  if (id == NULL) {
    id = (unsigned long *)malloc(10 * sizeof(unsigned long));
    if (id == NULL) throw "Out of memory in asn_oid::decode_value";
    id_size = 10;
    id_count = 0;
  };

// id[id_count] should always exist.  we write into it, and then extend the
// buffer so that there's always at least one free slot.
  id[id_count] = p;
  
  id_count++;
  if (id_count >= id_size) {
    id = (unsigned long *)realloc(id, (id_size+10) * sizeof(unsigned long));
    if (id == NULL) throw "Out of memory in asn_oid::append_subident";
    id_size += 10;
  };
  id[id_count] = 0;

  set_value_valid();
  return 0;
}

bool asn_oid::is_equal(const unsigned long *p, unsigned n) const {
  unsigned i;
  if (!is_present() && !is_defaultable()) return false;
  if (n != id_count) return false;
  for (i=0; i<id_count; i++)  {
    if (id[i] != p[i]) return false;
  };
  return true;
}

int asn_oid::decode_value(r_buffer_t & buf, 
                          uint32 value_length) {
  r_buffer_t temp_buffer = buf;
  bool finished;
  
  empty();

  if (id == NULL) {
    id = (unsigned long *)malloc(10 * sizeof(unsigned long));
    if (id == NULL) throw "Out of memory in asn_oid::decode_value";
    id_size = 10;
  };

  if (value_length < 1) return ASN_NO_MORE_DATA;
  
  id_count = 2;
  if (buf.data[0] < 40) id[0] = 0;
  else if (buf.data[0] < 80) id[0] = 1;
  else id[0] = 2;
  id[1] = buf.data[0] - id[0] * 40;
  buf.data++; buf.data_len--; value_length--;

  id[2] = 0;
  while (value_length > 0) {
    id[id_count] = (id[id_count] << 7) + (buf.data[0] & 0x7fu);
    finished = (buf.data[0]& 0x80) == 0;
    buf.data++; buf.data_len--; value_length--;
    if (finished) {
      id_count++;
      if (id_count >= id_size) {
        id = (unsigned long *)realloc(id, (id_size+10) * sizeof(unsigned long));
        if (id == NULL) throw "Out of memory in asn_oid::decode_value";
        id_size += 10;
      };
      id[id_count] = 0;
    } else if (value_length == 0) {
      buf = temp_buffer;
      return ASN_INVALID_ENCODING;
    };
  };
  
  set_value_valid();
  return 0;
}


asn_oid::~asn_oid() {
  if (id) free(id);
  id = NULL;
  id_size = 0;
}

int asn_oid::set_value(unsigned char *p, unsigned n) {
  r_buffer_t rbuf;
  invalidate_value();
  rbuf.data = p;
  rbuf.data_len = n;
  return decode_value(rbuf, n);
}


int asn_oid::encode_value(buffer_t & buf) const {
  unsigned i;
  CHECK_VALUE_VALID;
  buf.append(40 * (unsigned char)id[0] + (unsigned char)id[1]);
  for (i=2; i<id_count; i++) {
    if ((id[i] & ~0x7flu) == 0) {
      buf.append((unsigned char)(id[i]));
    } else if ((id[i] & ~0x3ffflu) == 0) {
      buf.append((unsigned char)(((id[i] >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(id[i] & 0x7fu));
    } else if ((asn_tag & ~0x1ffffflu) == 0) {
      buf.append((unsigned char)(((id[i] >> 14) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((id[i] >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(id[i] & 0x7fu));
    } else if ((asn_tag & ~0xffffffflu) == 0) {
      buf.append((unsigned char)(((id[i] >> 21) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((id[i] >> 14) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((id[i] >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(id[i] & 0x7fu));
    } else {
      buf.append((unsigned char)(((id[i] >> 28) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((id[i] >> 21) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((id[i] >> 14) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(((id[i] >> 7) & 0x7fu) | 0x80u));
      buf.append((unsigned char)(id[i] & 0x7fu));
    };
  };
  return 0;
}

int asn_oid::get_value(buffer_t & buffer) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_oid *)get_default_value())->get_value(buffer);
  return encode_value(buffer);
}

int asn_oid::get_value(unsigned long *&p, unsigned &n) const {
  CHECK_VALUE_PRESENT;
  if (!is_present()) return ((asn_oid *)get_default_value())->get_value(p, n);
  p = id;
  n = id_count;
  return 0;
}

int asn_oid::set_default_value(unsigned long *p, unsigned n) {
  asn_oid * i = new asn_oid;
  i->set_tag(get_tag());
  i->set_class(get_class());
  i->set_value(p,n);
  set_default_value(p,n);
  return 0;
}

int asn_oid::set_value(unsigned long *p, unsigned n) {
  invalidate_value();
  if (id) free(id);
  id_size = 0;
  id = (unsigned long *)malloc((n+1) * sizeof(unsigned long));
  if (id == NULL) throw "Out of memory in asn_oid::set_value";
  id_size = n+1;
  id_count = n;
  memcpy(id, p, n * sizeof(unsigned long));
  set_value_valid();
  return 0;
}

int asn_sequence::decode_value(r_buffer_t & buf, 
                               uint32 value_length) {
  unsigned i;
  int result;
  r_buffer_t temp_buf = buf;
  if (!indefinite_length) temp_buf.data_len = value_length;

  for (i=0; i < child_count; i++) 
    if ((result = child[i]->read(temp_buf)) != 0) return result;
  if (indefinite_length) buf.data_len = temp_buf.data_len;
  else if (temp_buf.data_len != 0)
    return ASN_UNCONSUMED_DATA;
  else buf.data_len -= value_length;
  buf.data = temp_buf.data;
  return 0;
}

int asn_sequence::encode_value(buffer_t & buf) const {
  unsigned i;
  int result;
  for (i=0; i < child_count; i++) 
    if ((result = child[i]->write(buf)) != 0) return result;
  return 0;
}


void asn_sorted::invalidate_value(invalidate_t dir) {
  is_sorted = false;
//  asn_composite::invalidate_value(INVALIDATE_PARENT); // Shouldn't this be dir?
  asn_composite::invalidate_value(dir);
}
  

int asn_sorted::decode_value(r_buffer_t & buf, 
                             uint32 value_length) {
  unsigned i;
  int result;
  r_buffer_t temp_buf = buf;
  bool done = false;

  if (!indefinite_length) temp_buf.data_len = value_length;
  done = false;
  while (!done) {
    if (!indefinite_length && (temp_buf.data_len == 0)) done = true;
    else if (indefinite_length && check_EOC(temp_buf)) done = true;
    else {
      for (i=0; i < child_count; i++) 
        if ((result = child[i]->read(temp_buf)) == 0) break;
      if (result) return result;
    };
  };

// Now we've read all the data.  Did we get all our required values?
  if (!is_present()) {
    return ASN_ELEMENTS_MISSING;
  };

  if (indefinite_length) buf.data_len = temp_buf.data_len;
  else if (temp_buf.data_len != 0) return ASN_UNCONSUMED_DATA;
  else buf.data_len -= value_length;

  buf.data = temp_buf.data;

  return 0;
}

int asn_sorted::encode_value(buffer_t & buf) const {
  unsigned i;
  int result;
  sort_children();
  for (i=0; i < child_count; i++) 
    if ((result = sorted_child[i]->write(buf)) != 0) return result;
  return 0;
}

asn_sorted::~asn_sorted() {
  if (sorted_child) free(sorted_child);
}

int asn_sorted::sort_children(void) const {
// Default behavior of a sorted class is to sort in ascending order of tag.
  int result;
  unsigned i;
  bool done;
  asn_sorted * var_this = (asn_sorted *)this;
  asn_object * temp;

  CHECK_VALUE_VALID;

  if (is_sorted) return 0;

  var_this->sorted_child = (asn_object **)realloc(sorted_child, child_count * sizeof(asn_object *));
  if (sorted_child == NULL)
    throw "Out of memory in asn_sorted::sort_children";

// First ensure that we have encodings for every child
  for (i=0; i<child_count; i++) {
    if ((result = child[i]->get_encoding()) != 0) return result;
    var_this->sorted_child[i] = child[i];
  };

// Now sort encodings based on tag.
// As there typically won't be many children to sort, a bubblesort is good enough.
  done = (child_count == 0);
  while (!done) {
    done = true;
    for (i=0; i<child_count-1; i++) {
      if (sorted_child[i]->get_tag() > sorted_child[i+1]->get_tag()) {
        temp = sorted_child[i];
        var_this->sorted_child[i] = sorted_child[i+1];
        var_this->sorted_child[i+1] = temp;
        done = false;
      };
    }
  };
  var_this->is_sorted = true;
  return 0;
}


int asn_choice::normalize(void) {
  if (selected_child >= 0) return child[selected_child]->normalize();
  return 0;
}
  
int asn_choice::select(unsigned i) {
  if (i >= child_count) return ASN_SELECTION_OUT_OF_RANGE;
  if (selected_child != (int)i) {
    selected_child = i;
    invalidate_encoding();
    check_valid();
  };
  return 0;
}

int asn_choice::selected(void) const {
  return selected_child;
}

bool asn_choice::is_encoding_valid(void) const {
  return ((selected_child >= 0) 
          && (get_child(selected_child)->is_encoding_valid()));
}

int asn_choice::decode_value(r_buffer_t & buf, 
                             uint32 value_length) {
  throw "Error: asn_choice::decode_value called";
  return 0;
}

int asn_choice::encode_value(buffer_t & buf) const {
  throw "Error: asn_choice::encode_value called";
  return 0;
}

int asn_choice::read(r_buffer_t & buf) {
  int result;
  unsigned i;
  
  if (preread_callback) preread_callback(this, buf);

  selected_child = -1;
  invalidate_value();
  for (i=0; i<child_count; i++) 
    if ((result = child[i]->read(buf)) == 0) {
      selected_child = i;
      set_value_valid();
  
      if (postread_callback) postread_callback(this, buf, 0);
  
      return 0;
    };

  if (result) {
    if (is_optional() || is_defaultable()) {
      if (postread_callback) postread_callback(this, buf, 0);
      return 0;
    };
  };

  if (postread_callback) postread_callback(this, buf, ASN_WRONG_TYPE);
  
  return ASN_WRONG_TYPE;
}

int asn_choice::write(buffer_t & buf) const {
  if (is_optional() && !is_present()) return 0;  
  if (is_defaultable() && is_default_value()) return 0;
  if (selected_child == -1) return ASN_NO_SELECTION;
  return child[selected_child]->write(buf);
}

void asn_choice::check_valid(void) {
// Called by a child when it has become valid or invalid.  Basically, we just want to
// propagate the selected child's validity up the hierarchy.
  bool temp_valid;
  if (selected_child == -1) return;
  temp_valid = child[selected_child]->is_valid();
  if (temp_valid && !is_present()) set_value_valid();
  else if (!temp_valid && is_present()) invalidate_value(INVALIDATE_PARENT);
}

asn_any::asn_any(security_t s) : unparsedValue(s), asn_object(s) {
  strcpy(objType, "ANY");
  specific_syntax = NULL;
  preParse = NULL;
  preEncode = NULL;
  encoding_indefinite = false;  // Forbid indefinite encoding (for unparsed ANY values)
  encoding_primitive = true;    // Permit both primitive
  encoding_constructed = true;  // and constructed encodings.
};

asn_any::~asn_any() {
}

int asn_any::set_syntax(asn_object * o) {
  if (specific_syntax) {
    specific_syntax->set_parent(NULL);
    specific_syntax->invalidate_value();
  };
  specific_syntax = o;
  o->set_parent(this);
  check_valid();
  if ((security == ASN_SECRET) && o) o->set_secure(security);
  return 0;
}

int asn_any::set_preparse(int (* callback)(asn_any * o)) {
  
  preParse = callback;
  return 0;
}

int asn_any::set_preencode(int (* callback)(asn_any * o)) {
  
  preEncode = callback;
  return 0;
}


int asn_any::get_value(unsigned char * &p, uint32 & bc) const {

  if (specific_syntax) return ASN_SPECIFIC_SYNTAX_ESTABLISHED;
  CHECK_VALUE_PRESENT;
  
  p = unparsedValue.data;
  bc = unparsedValue.data_len;
  
  return 0;
}

int asn_any::set_value(unsigned char * p, uint32 bc) {
  if (specific_syntax) return ASN_SPECIFIC_SYNTAX_ESTABLISHED;
  invalidate_value();
  unparsedValue.clear();
  unparsedValue.append(p, bc);
  set_value_valid();
  return 0;
}

int asn_any::read(r_buffer_t & buf) {
  int result;
  
  
  if (preread_callback) preread_callback(this, buf);
  
  if (preParse) {
    result = preParse(this);
    if (result) return result;
  };
  if (specific_syntax) {
    result = specific_syntax->read(buf);
  
    if (postread_callback) postread_callback(this, buf, result);
  
    return result;
  };
// No syntax specified.  Use the generic read rtn.
  result = asn_object::read(buf);
  if (result) {
  
    if (postread_callback) postread_callback(this, buf, result);
  
    return result;
  };
// Fix up the generate_constructed flag so if we're asked to re-encode
// this object, we generate the same encoding we just read.
  generate_constructed = constructed;
  
  if (postread_callback) postread_callback(this, buf, 0);
  
  return 0;
}

int asn_any::write(buffer_t & buf) const {
  int result;
  asn_any * var_this = (asn_any *)this;
  if (preEncode) {
    result = preEncode(var_this);
    if (result) return result;
  };
  if (specific_syntax) {
    return specific_syntax->write(buf);
  };
// No syntax specified.  Use the generic write rtn.
  return asn_object::write(buf);
}

void asn_any::invalidate_value(invalidate_t dir) {
  parsedValue = false;
  asn_object::invalidate_value(dir);
}

void asn_any::check_valid(void) {
// Called by a child when it has become valid or invalid.  By default, constructed objects 
// are valid only when all their children are valid (although asn_choice will override
// this), so we need to check the validity of all our children.
  if (specific_syntax == NULL) return; 
// This is an error - this rtn should only be invoked by the expected syntax object

  if (specific_syntax->is_valid()) {
    if (is_present()) invalidate_value();
    return;
  };
  if (!is_present()) set_value_valid();
}

void asn_any::set_secure(security_t s) {
  asn_object::set_secure(s);
  if ((security == ASN_SECRET) && specific_syntax) 
    specific_syntax->set_secure(security);
}

int asn_any::decode_value(r_buffer_t & buf, 
                          uint32 value_length) {
  if (specific_syntax) return ASN_SPECIFIC_SYNTAX_ESTABLISHED;

// We only deal with definite-length encoded values.
  invalidate_value(); 
  unparsedValue.clear();
  unparsedValue.append(buf.data, value_length);
  buf.data += value_length;
  buf.data_len -= value_length;
  return 0;
}

int asn_any::encode_value(buffer_t & buf) const {

  if (specific_syntax) return ASN_SPECIFIC_SYNTAX_ESTABLISHED;

  buf.append(unparsedValue);
  return 0;

}

bool asn_any::check_type(uint32 tagRead, int classRead) const {
  return true;  // Any type is fine for an ANY!
}


int asn_UTCtime::decode_value(r_buffer_t & buf, 
                              uint32 value_length) {
  int result;
  unsigned year;
  unsigned month;
  unsigned day;
  unsigned hour;
  unsigned minute;
  unsigned second;
  int tz_hour;
  int tz_min;
  
  result = asn_octetstring::decode_value(buf, value_length);
  if (result) return result;
// asn_octetstring's done the basic ASN parsing.  Simply verify that 
// the value read is a legal UTCtime string
  result = parseUTCstring(str_val,
                          year,
                          month,
                          day,
                          hour,
                          minute,
                          second,
                          tz_hour,
                          tz_min);
  return result;

}


int asn_UTCtime::normalize(void) {

  int result;
  unsigned year;
  unsigned month;
  unsigned day;
  unsigned hour;
  unsigned minute;
  unsigned second;
  int tz_hour;
  int tz_min;
  
  CHECK_VALUE_VALID;
// asn_octetstring's done the basic ASN parsing.  Simply verify that
// the value read is a legal UTCtime string
  result = parseUTCstring(str_val,
                          year,
                          month,
                          day,
                          hour,
                          minute,
                          second,
                          tz_hour,
                          tz_min);
  if (result) return result;
// The only thing we have to normalize is the time-zone, which has to
// be expressed as ZULU time.

  if ((tz_hour != 0) || (tz_min != 0)) {
// we have some fix-up to do  
    result = timeToZulu(year, month, day, hour, minute, tz_hour, tz_min);
    if (result) return result;
  };

  return set_value(year,
                   month,
                   day,
                   hour,
                   minute,
                   second,
                   tz_hour,
                   tz_min);

}

int asn_UTCtime::get_value(unsigned & year,   // 1950..2049
                           unsigned & month,  // 1..12
                           unsigned & day,    // 0..31
                           unsigned & hour,   // 0..23
                           unsigned & minute, // 0..59
                           unsigned & second, // 0..59
                           int & tz_hour,     // -12..+12
                           int & tz_min) const {    // -59..+59
  CHECK_VALUE_VALID;
  return parseUTCstring(str_val,
                        year,
                        month,
                        day,
                        hour,
                        minute,
                        second,
                        tz_hour,
                        tz_min);
}

int asn_UTCtime::set_value(unsigned year,   // 1950..2049
                           unsigned month,  // 1..12
                           unsigned day,    // 0..31
                           unsigned hour,   // 0..23
                           unsigned minute, // 0..59
                           unsigned second, // 0..59
                           int tz_hour,     // -12..+12
                           int tz_min) {    // -59..+59
  int tzs;
  int result;

  invalidate_value();
  str_val.clear();
  if (year >= 1950) year -= 1900;
  else return ASN_INVALID_VALUE;
  
  if (year > 99) year -= 100;

  if (year > 99) return ASN_INVALID_VALUE;

  if ((tz_hour > 0) && (tz_min < 0)) return ASN_INVALID_VALUE;
  if ((tz_hour < 0) && (tz_min > 0)) return ASN_INVALID_VALUE;

  if ((tz_hour > 14) || (tz_hour < -14)) return ASN_INVALID_VALUE;
  if ((tz_min > 59) || (tz_min < -59)) return ASN_INVALID_VALUE;

  if (tz_hour > 0) tzs = 1;
  else if (tz_hour < 0) tzs = -1;
  else tzs = 0;

  if (tzs < 0) {
    tz_hour = - tz_hour;
    tz_min = - tz_min;
  };

  if ((result = write2d(str_val, year)) != 0) return result;
  if ((result = write2d(str_val, month)) != 0) return result;
  if ((result = write2d(str_val, day)) != 0) return result;
  if ((result = write2d(str_val, hour)) != 0) return result;
  if ((result = write2d(str_val, minute)) != 0) return result;
  if ((result = write2d(str_val, second)) != 0) return result;

  if (tzs == 0) {
    str_val.append(IA5_CAP_Z);
  } else {
    if (tzs > 0) {
      str_val.append(IA5_PLUS);
    } else {
      str_val.append(IA5_MINUS);
    };
    if ((result = write2d(str_val, tz_hour)) != 0) return result;
    if ((result = write2d(str_val, tz_min)) != 0) return result;
  };

  set_value_valid();
  return 0;
}


int asn_generalizedtime::decode_value(r_buffer_t & buf, 
                                      uint32 value_length) {
  int result;
  unsigned year;
  unsigned month;
  unsigned day;
  unsigned hour;
  unsigned minute;
  unsigned second;
  unsigned millisecond;
  int tz_hour;
  int tz_min;
  
  result = asn_octetstring::decode_value(buf, value_length);
  if (result) return result;
// asn_octetstring's done the basic ASN parsing.  Simply verify that 
// the value read is a legal generalizedtime string
  result = parseGeneralizedstring(str_val,
                                  year,
                                  month,
                                  day,
                                  hour,
                                  minute,
                                  second,
                                  millisecond,
                                  tz_hour,
                                  tz_min);
  return result;

}


int asn_generalizedtime::normalize(void) {

  int result;
  unsigned year;
  unsigned month;
  unsigned day;
  unsigned hour;
  unsigned minute;
  unsigned second;
  unsigned millisecond;
  int tz_hour;
  int tz_min;
  
  CHECK_VALUE_VALID;

  result = parseGeneralizedstring(str_val,
                                  year,
                                  month,
                                  day,
                                  hour,
                                  minute,
                                  second,
                                  millisecond,
                                  tz_hour,
                                  tz_min);
  if (result) return result;
// The only thing we have to normalize is the time-zone, which has to
// be expressed as ZULU time.

  if ((tz_hour != 0) || (tz_min != 0)) {
// we have some fix-up to do  
    result = timeToZulu(year, month, day, hour, minute, tz_hour, tz_min);
    if (result) return result;
  };

  return set_value(year,
                   month,
                   day,
                   hour,
                   minute,
                   second,
                   millisecond,
                   tz_hour,
                   tz_min);

}

int asn_generalizedtime::get_value(unsigned & year,   // 0000..9999
                                   unsigned & month,  // 1..12
                                   unsigned & day,    // 0..31
                                   unsigned & hour,   // 0..23
                                   unsigned & minute, // 0..59
                                   unsigned & second, // 0..59
                                   unsigned & millisecond, // 0..999
                                   int & tz_hour,     // -12..+12
                                   int & tz_min) const {    // -59..+59
  return parseGeneralizedstring(str_val,
                                year,
                                month,
                                day,
                                hour,
                                minute,
                                second,
                                millisecond,
                                tz_hour,
                                tz_min);
}

int asn_generalizedtime::set_value(unsigned year,   // 0000..9999
                                   unsigned month,  // 1..12
                                   unsigned day,    // 0..31
                                   unsigned hour,   // 0..23
                                   unsigned minute, // 0..59
                                   unsigned second, // 0..59
                                   unsigned millisecond,  // 0..999
                                   int tz_hour,     // -12..+12
                                   int tz_min) {    // -59..+59
  int tzs;
  int result;

  invalidate_value();
  str_val.clear();
  
  if (year > 9999) return ASN_INVALID_VALUE;

  if ((tz_hour > 0) && (tz_min < 0)) return ASN_INVALID_VALUE;
  if ((tz_hour < 0) && (tz_min > 0)) return ASN_INVALID_VALUE;

  if ((tz_hour > 14) || (tz_hour < -14)) return ASN_INVALID_VALUE;
  if ((tz_min > 59) || (tz_min < -59)) return ASN_INVALID_VALUE;

  if (tz_hour > 0) tzs = 1;
  else if (tz_hour < 0) tzs = -1;
  else tzs = 0;

  if (tzs < 0) {
    tz_hour = - tz_hour;
    tz_min = - tz_min;
  };

  if ((tz_hour != 0) || (tz_min != 0)) {
// DER for generalized time requires ZULU time.
    result = timeToZulu(year, month, day, hour, minute, tz_hour, tz_min);
    if (result) return result;
  };

  if ((result = write4d(str_val, year)) != 0) return result;
  if ((result = write2d(str_val, month)) != 0) return result;
  if ((result = write2d(str_val, day)) != 0) return result;
  if ((result = write2d(str_val, hour)) != 0) return result;
  if ((result = write2d(str_val, minute)) != 0) return result;
  if ((result = write2d(str_val, second)) != 0) return result;
  if (millisecond != 0) {
    str_val.append(IA5_PERIOD);
    if ((result = write1d(str_val, (millisecond / 100))) != 0) return result;
    millisecond -= ((millisecond / 100) * 100);
  };

  if (millisecond != 0) {
    if ((result = write1d(str_val, (millisecond / 10))) != 0) return result;
    millisecond -= ((millisecond / 10) * 10);
  };

  if (millisecond != 0) {
    if ((result = write1d(str_val, millisecond)) != 0) return result;
  };

  str_val.append(IA5_CAP_Z);

  set_value_valid();
  return 0;
}
