/* ***************************************************************** *
 * 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 <asnnames.h>
#include <codesets.h>
#include <chardefs.h>
#include <string.h>


#define MAX_ATTRS 15

unsigned max_attrs = MAX_ATTRS;

// The OID values below need to be checked.
unsigned char attrName_C[2] = {0x43, 0x00};
unsigned char attrName_O[2] = {0x4f, 0x00};
unsigned char attrName_OU[3] = {0x4f, 0x55, 0x00};
unsigned char attrName_CN[3] = {0x43, 0x4e, 0x00};
unsigned char attrName_L[2] = {0x4c, 0x00};
unsigned char attrName_S[2] = {0x53, 0x00};
unsigned char attrName_T[2] = {0x54, 0x00};

unsigned long attrOid_C[4] = {2,5,4,6};
unsigned long attrOid_O[4] = {2,5,4,10};
unsigned long attrOid_OU[4] = {2,5,4,11};
unsigned long attrOid_CN[4] = {2,5,4,3};
unsigned long attrOid_L[4] = {2,5,4,7};
unsigned long attrOid_S[4] = {2,5,4,8};
unsigned long attrOid_T[4] = {2,5,4,12};


typedef struct {
  unsigned long * val;
  uint32 len;
} attrOid_t;

unsigned char * attrName[MAX_ATTRS] = {
  attrName_C,
  attrName_O,
  attrName_OU,
  attrName_CN,
  attrName_L,
  attrName_S,
  attrName_T,
  NULL,
};

attrOid_t attrOid[MAX_ATTRS] = {
  {attrOid_C, sizeof(attrOid_C) / sizeof(uint32)},
  {attrOid_O, sizeof(attrOid_O) / sizeof(uint32)},
  {attrOid_OU, sizeof(attrOid_OU) / sizeof(uint32)},
  {attrOid_CN, sizeof(attrOid_CN) / sizeof(uint32)},
  {attrOid_L, sizeof(attrOid_L) / sizeof(uint32)},
  {attrOid_S, sizeof(attrOid_S) / sizeof(uint32)},
  {attrOid_T, sizeof(attrOid_T) / sizeof(uint32)},
  {NULL, 0},
};

match_t attrMatch[MAX_ATTRS] = {
  match_caseExact, // C
  match_caseIgnore, // O
  match_caseIgnore, // OU
  match_caseIgnore, // CN
  match_caseIgnore, // L
  match_caseIgnore, // S
  match_caseIgnore, // T
};

int asn_AVA::unquote_IA5(r_buffer_t & src, buffer_t & dest) const {
  int i = 0;
  bool in_quotes = false;
	
  while ((i < (int)src.data_len) && (src[i] == IA5_SP)) i++;
  
  while (i < (int)src.data_len) {
    if (src[i] == quote_mark) {
      i++;
      if (i < (int)src.data_len) {
        dest.append(src[i]);
      } else return ASN_VALUE_OVERFLOW;
    } else if (!in_quotes && (src[i] == IA5_SP)) {
// Unless we're in quotes, only transcribe the first of a series of spaces
      dest.append(IA5_SP);
      while ((i < (int)src.data_len) && (src[i] == IA5_SP)) i++;
      continue;
    } else if (in_quotes && (src[i] == close_quote_mark)) {
      in_quotes = false;
    } else if (!in_quotes && (src[i] == open_quote_mark)) {
      in_quotes = true;
    } else dest.append(src[i]);

    i++;
  };
	
  while ((dest.data_len != 0) && (dest[dest.data_len-1] == IA5_SP)) dest.data_len--;
	
  return 0;
}

int asn_AVA::unquote_UTF8(r_buffer_t & src, buffer_t & dest) const {
  int i = 0;
  bool in_quotes = false;
	
// Skip over leading spaces
  while ((i < (int)src.data_len) && (src[i] == IA5_SP)) i++;
  
  while (i < ((int)(src.data_len))) {
    if (src[i] == quote_mark) {
      i++;
      if (i < (int)src.data_len) {
        dest.append(src[i]);
      } else return ASN_VALUE_OVERFLOW;
    } else if (!in_quotes && (src[i] == IA5_SP)) {
// Unless we're in quotes, only transcribe the first of a series of spaces
      dest.append(IA5_SP);
      while ((i < (int)src.data_len) && (src[i] == IA5_SP)) i++;
      continue;
    } else if (in_quotes && (src[i] == close_quote_mark)) {
      in_quotes = false;
    } else if (!in_quotes && (src[i] == open_quote_mark)) {
      in_quotes = true;
    } else dest.append(src[i]);

    i++;
  };
	
  while ((dest.data_len != 0) && (dest[dest.data_len-1] == IA5_SP)) dest.data_len--;
	
  return 0;
}

int asn_AVA::unquote_BMP(r_buffer_t & src, buffer_t & dest) const {
	int i = 0;
	bool in_quotes = false;
	
	while ((i < (int)src.data_len-1) && (src[i] == 0) && (src[i+1] == IA5_SP)) i+=2;
  
	while (i < (int)src.data_len-1) {
		if ((src[i] == 0) && (src[i+1] == quote_mark)) {
			i+= 2;
		  if (i < (int)src.data_len-1) {
				dest.append(src[i]);
				dest.append(src[i+1]);
			} else return ASN_VALUE_OVERFLOW;
		} else if (!in_quotes && (src[i] == 0) && (src[i+1] == IA5_SP)) {
// Unless we're in quotes, only transcribe the first of a series of spaces
			dest.append((unsigned char)0);
			dest.append(IA5_SP);
			while ((i < (int)src.data_len) && (src[i] == 0) && (src[i+1] == IA5_SP)) i+=2;
			continue;
		} else if (in_quotes && (src[i] == 0) && (src[i+1] == close_quote_mark)) {
			in_quotes = false;
		} else if (!in_quotes && (src[i] == 0) && (src[i+1] == open_quote_mark)) {
			in_quotes = true;
		} else {
			dest.append(src[i]);
			dest.append(src[i+1]);
		};
		i+=2;
	};
	
	while ((dest.data_len > 1) && (dest[dest.data_len-1] == 0) && (dest[dest.data_len-2] == IA5_SP)) 
		dest.data_len-=2;
	
	return 0;
}

int asn_AVA::unquote_Univ(r_buffer_t & src, buffer_t & dest) const {
	int i = 0;
	bool in_quotes = false;

	while ((i < (int)src.data_len-3) && (src[i] == 0) && (src[i+1] == 0) 
				 && (src[i+2] == 0) && (src[i+3] == IA5_SP)) i+=2;
  
	while (i < (int)src.data_len-3) {
		if ((src[i] == 0) && (src[i+1] == 0) && (src[i+2] == 0) && (src[i+3] == quote_mark)) {
			i+= 4;
		  if (i < (int)src.data_len-3) {
				dest.append(src[i]);
				dest.append(src[i+1]);
				dest.append(src[i+2]);
				dest.append(src[i+3]);
			} else return ASN_VALUE_OVERFLOW;
		} else if (!in_quotes && (src[i] == 0) && (src[i+1] == 0) 
							 && (src[i+2] == 0) && (src[i+3] == IA5_SP)) {
// Unless we're in quotes, only transcribe the first of a series of spaces
			dest.append((unsigned char)0);
			dest.append((unsigned char)0);
			dest.append((unsigned char)0);
			dest.append(IA5_SP);
			while ((i < (int)src.data_len) && (src[i] == 0) && (src[i+1] == 0) 
						 && (src[i+2] == 0) && (src[i+3] == IA5_SP)) i+=4;
			continue;
		} else if (in_quotes && (src[i] == 0) && (src[i+1] == 0) 
							 && (src[i+2] == 0) && (src[i+3] == close_quote_mark)) {
			in_quotes = false;
		} else if (!in_quotes && (src[i] == 0) && (src[i+1] == 0) 
							 && (src[i+2] == 0) && (src[i+3] == open_quote_mark)) {
			in_quotes = true;
		} else {
			dest.append(src[i]);
			dest.append(src[i+1]);
			dest.append(src[i+2]);
			dest.append(src[i+3]);
		};
		i+=4;
	};

	while ((dest.data_len > 3) && (dest[dest.data_len-1] == 0) && (dest[dest.data_len-2] == 0) 
				 && (dest[dest.data_len-3] == 0) && (dest[dest.data_len-4] == IA5_SP)) 
		dest.data_len-=4;
	
	return 0;
}




bool caseExactMatch_IA5(r_buffer_t & s1, 
												r_buffer_t & s2) {
	unsigned i = 0;
	unsigned j = 0;
	bool whitespace1;
	bool whitespace2;

	while ((i < s1.data_len) && (s1[i] == IA5_SP))  i++;	// Ignore leading whitespace
	while ((j < s2.data_len) && (s2[i] == IA5_SP))  j++;

	while ((i < s1.data_len) && (j < s2.data_len)) {
		whitespace1 = false; whitespace2 = false;
		while ((i < s1.data_len) && (s1[i] == IA5_SP)) {
			whitespace1 = true;
			i++;
		};
		while ((i < s2.data_len) && (s2[j] == IA5_SP)) {
			whitespace1 = true;
			j++;
		};
		if ((i >= s1.data_len) && (j >= s2.data_len)) return true;  // Ignore trailing whitespace
		if (whitespace1 != whitespace2) return false;  // Collapse and compare multiple internal whitespaces
		if (s1[i] != s2[j]) return false;
		i++, j++;
	};
	return (i >= s1.data_len) && (j >= s2.data_len);
}

bool caseExactMatch_IA5(r_buffer_t & s, 
												unsigned char * str) {
	r_buffer_t temp_buffer;
	temp_buffer.data = str;
	temp_buffer.data_len = strlen((char *)str);
	return caseExactMatch_IA5(s, temp_buffer);
}

bool caseIgnoreMatch_IA5(r_buffer_t & s1, 
												 r_buffer_t & s2) {
	unsigned i = 0;
	unsigned j = 0;
	bool whitespace1;
	bool whitespace2;

	while ((i < s1.data_len) && (s1[i] == IA5_SP))  i++;	// Ignore leading whitespace
	while ((j < s2.data_len) && (s2[i] == IA5_SP))  j++;

	while ((i < s1.data_len) && (j < s2.data_len)) {
		whitespace1 = false; whitespace2 = false;
		while ((i < s1.data_len) && (s1[i] == IA5_SP)) {
			whitespace1 = true;
			i++;
		};
		while ((i < s2.data_len) && (s2[j] == IA5_SP)) {
			whitespace2 = true;
			j++;
		};
		if ((i >= s1.data_len) && (j >= s2.data_len)) return true;  // Ignore trailing whitespace
		if (whitespace1 != whitespace2) return false;  // Collapse and compare multiple internal whitespaces
		if (toupper_IA5(s1[i]) != toupper_IA5(s2[j])) return false;
		i++, j++;
	};
	return (i >= s1.data_len) && (j >= s2.data_len);
}

bool caseIgnoreMatch_IA5(r_buffer_t & s, 
												 unsigned char * str) {
	r_buffer_t temp_buffer;
	temp_buffer.data = str;
	temp_buffer.data_len = strlen((char *)str);
	return caseIgnoreMatch_IA5(s, temp_buffer);
}

bool asn_AVA::operator == (const asn_AVA & o) const {
  buffer_t s1;
  buffer_t s2;

// 2 AVAs are not equal if their attribute types differ...
  if (o.attr != attr) return false;

// The two attribute types are equal.  We need to compare their values.
// To do this canonically, we should convert both values to 
// UniversalString, and compare the resulting code-strings...
// However, PKIX-1 specifies arcane rules for deciding whether
// comparisons are case-sensitive: If both strings are represented
// using printableString syntax, comparison should be case-insensitive,
// otherwise case is important.

  if ((value.get_codeset() == PRINTABLE_STRING_TAG) &&
    (o.value.get_codeset() == PRINTABLE_STRING_TAG)) {

    if (value.get_value_printable(s1)) return false;
    if (o.value.get_value_printable(s2)) return false;
    return caseIgnoreMatch_IA5(s1, s2);
// It's OK to use the _IA5 routine above, as printableString 
// encoding is a subset of IA5
  };

// OK, the strings weren't printableString, so we do a case-exact match...
  if (value.get_value_Univ(s1)) return false;
  if (o.value.get_value_Univ(s2)) return false;
  return s1 == s2;

}

bool asn_RDN::operator == (const asn_RDN & o) const {
	unsigned i;
	asn_AVA * my_child;
	asn_AVA * other_child;
	sort_children();
	o.sort_children();
	if (!is_present()) return false;

	for (i=0; i< child_count; i++) {
		my_child = (asn_AVA *) (get_child(i));
		other_child = (asn_AVA *) (o.get_child(i));
		if ((*my_child)  != (*other_child)) return false;
	};
	return true;

};

bool asn_x500name::operator == (const asn_x500name & o) const {
	unsigned i;
	asn_RDN * my_child;
	asn_RDN * other_child;
	if (!is_present()) return false;

	for (i=0; i< child_count; i++) {
		my_child = (asn_RDN *) (get_child(i));
		other_child = (asn_RDN *) (o.get_child(i));
		if ((*my_child)  != (*other_child)) return false;
	};
	return true;
};

asn_x500name::asn_x500name(security_t s) {
// Set default printable syntax.
  strcpy(objType, "X500NAME");
	set_syntax_OSF();
}

void asn_x500name::set_syntax_OSF(void) {
	set_rdn_separator();
	set_ava_separator();
	set_quote_mark();
	set_open_quote_mark();
	set_close_quote_mark();
	set_attr_value_separator();
	set_oid_subident_separator();
	set_rdn_bigendian();
	set_leading_rdn_separator();
}

void asn_x500name::set_syntax_1779(void) {
	set_rdn_separator(';');
	set_ava_separator('+');
	set_quote_mark('\\');
	set_open_quote_mark('\"');
	set_close_quote_mark('\"');
	set_attr_value_separator('=');
	set_oid_subident_separator('.');
	set_rdn_bigendian(false);
	set_leading_rdn_separator(false);
}

asn_x500name::~asn_x500name() {
}


int asn_x500name::set_value_C(char * s, char splat) {
// Convert the name to an IA5 string, then call set_value_IA5
	buffer_t p;
	unsigned i;
	int c;
	for (i=0; s[i]!=0; i++) {
		if ((c = Local2IA5(s[i])) < 0) p.append(Local2IA5(splat));
		else p.append(c);
	}
	return set_value_IA5(p);
	return 0;
}

int asn_x500name::set_value_IA5(r_buffer_t & s) {
	buffer_t rdn_buffer;
	asn_RDN * rdn;
	unsigned i = 0;
	int result;
	bool in_quotes = false;

	empty();
	if ((s.data_len > 0) && (s[0] == rdn_separator)) i++; // Ignore leading separator
	while (i < s.data_len) {
		rdn_buffer.clear();
		while ((i < s.data_len) && (!in_quotes) && (s[i] != rdn_separator)) {
			if ((!in_quotes) && (s[i] == open_quote_mark)) in_quotes = true;
			else if (in_quotes && (s[i] == close_quote_mark)) in_quotes = false;
			else if ((s[i] == quote_mark) && (i < s.data_len-1) && (s[i+1] == rdn_separator)) {
// Ignore quoted rdn separator.
				rdn_buffer.append(s[i]);
				i++;
			};
			rdn_buffer.append(s[i]);
			i++;
		};
// Now we have the complete RDN in rdn.  Process it.
		if (RdnBigEndian) rdn = add_child();
		else rdn->add_child_before();
		rdn->set_ava_separator_IA5(ava_separator);
		rdn->set_attr_value_separator_IA5(attr_value_separator);
		rdn->set_quote_mark_IA5(quote_mark);
		rdn->set_open_quote_mark_IA5(open_quote_mark);
		rdn->set_close_quote_mark_IA5(close_quote_mark);

		result = rdn->set_value_IA5(rdn_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i++;
	};
	set_value_valid();
	return 0;
}

int asn_x500name::set_value_UTF8(r_buffer_t & s) {
	buffer_t rdn_buffer;
	asn_RDN * rdn;
	unsigned i = 0;
	int result;
	bool in_quotes = false;

	empty();
	if ((s.data_len > 0) && (s[0] == rdn_separator)) i++; // Ignore leading separator
	while (i < s.data_len) {
		rdn_buffer.clear();
		while ((i < s.data_len) && (!in_quotes) && (s[i] != rdn_separator)) {
			if ((!in_quotes) && (s[i] == open_quote_mark)) in_quotes = true;
			else if (in_quotes && (s[i] == close_quote_mark)) in_quotes = false;
			else if ((s[i] == quote_mark) && (i < s.data_len-1) && (s[i+1] == rdn_separator)) {
// Ignore quoted rdn separator.
				rdn_buffer.append(s[i]);
				i++;
			};
			rdn_buffer.append(s[i]);
			i++;
		};
// Now we have the complete RDN in rdn.  Process it.
		if (RdnBigEndian) rdn = add_child();
		else rdn->add_child_before();
		rdn->set_ava_separator_IA5(ava_separator);
		rdn->set_attr_value_separator_IA5(attr_value_separator);
		rdn->set_quote_mark_IA5(quote_mark);
		rdn->set_open_quote_mark_IA5(open_quote_mark);
		rdn->set_close_quote_mark_IA5(close_quote_mark);

		result = rdn->set_value_UTF8(rdn_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i++;
	};
	set_value_valid();
	return 0;
}

int asn_x500name::set_value_BMP(r_buffer_t & s) {
	buffer_t rdn_buffer;
	asn_RDN * rdn;
	unsigned i = 0;
	int result;
	bool in_quotes = false;

	empty();
	if ((s.data_len > 1) && (s[0] == 0) && (s[1] == rdn_separator)) i+=2; // Ignore leading separator
	while (i < s.data_len-1) {
		rdn_buffer.clear();
		while ((i < s.data_len-1) && (!in_quotes) && (s[i] != 0) && (s[i+1] != rdn_separator)) {
			if ((!in_quotes) && (s[i] == 0) && (s[i+1] == open_quote_mark)) in_quotes = true;
			else if (in_quotes && (s[i] == 0) && (s[i+1] == close_quote_mark)) in_quotes = false;
			else if ((s[i] == 0) && (s[i+1] == quote_mark) && 
				  (i < s.data_len-3) && 
					(s[i+2] == 0) && (s[i+3] == rdn_separator)) {
// Ignore quoted rdn separator.
				rdn_buffer.append(s[i]);
				rdn_buffer.append(s[i+1]);
				i+=2;
			};
			rdn_buffer.append(s[i]);
			rdn_buffer.append(s[i+1]);
			i+=2;
		};
// Now we have the complete RDN in rdn.  Process it.
		if (RdnBigEndian) rdn = add_child();
		else rdn->add_child_before();
		rdn->set_ava_separator_IA5(ava_separator);
		rdn->set_attr_value_separator_IA5(attr_value_separator);
		rdn->set_quote_mark_IA5(quote_mark);
		rdn->set_open_quote_mark_IA5(open_quote_mark);
		rdn->set_close_quote_mark_IA5(close_quote_mark);

		result = rdn->set_value_BMP(rdn_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i+=2;
	};
	set_value_valid();
	return 0;
}

int asn_x500name::set_value_Univ(r_buffer_t & s) {
	buffer_t rdn_buffer;
	asn_RDN * rdn;
	unsigned i = 0;
	int result;
	bool in_quotes = false;

	empty();
	if ((s.data_len > 3) && (s[0] == 0) && (s[1] == 0) && (s[2] == 0) && (s[3] == rdn_separator)) 
		i+=4; // Ignore leading separator
	while (i < s.data_len-3) {
		rdn_buffer.clear();
		while ((i < s.data_len-3) && 
					 (s[i] != 0) && (s[i+1] != 0) && (s[i+2] != 0) && (s[i+3] != rdn_separator)) {
			if ((!in_quotes) && (s[i] == 0) && (s[i+1] == 0) && (s[i+2] == 0) && (s[i+3] == open_quote_mark)) in_quotes = true;
			else if (in_quotes && (s[i] == 0) && (s[i+1] == 0) && (s[i+2] == 0) && (s[i+3] == close_quote_mark)) in_quotes = false;
			else if ((s[i] == 0) && (s[i+1] == 0) && (s[i+2] == 0) && (s[i+3] == quote_mark) && 
				  (i < s.data_len-7) && 
					(s[i+4] == 0) && (s[i+5] == 0) && (s[i+6] == 0) && (s[i+7] == rdn_separator)) {
// Ignore quoted rdn separator.
				rdn_buffer.append(s[i]);
				rdn_buffer.append(s[i+1]);
				rdn_buffer.append(s[i+2]);
				rdn_buffer.append(s[i+3]);
				i+=4;
			};
			rdn_buffer.append(s[i]);
			rdn_buffer.append(s[i+1]);
			rdn_buffer.append(s[i+2]);
			rdn_buffer.append(s[i+3]);
			i+=4;
		};
// Now we have the complete RDN in rdn.  Process it.
		if (RdnBigEndian) rdn = add_child();
		else rdn->add_child_before();
		rdn->set_ava_separator_IA5(ava_separator);
		rdn->set_attr_value_separator_IA5(attr_value_separator);
		rdn->set_quote_mark_IA5(quote_mark);
		rdn->set_open_quote_mark_IA5(open_quote_mark);
		rdn->set_close_quote_mark_IA5(close_quote_mark);

		result = rdn->set_value_Univ(rdn_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i+=4;
	};
	set_value_valid();
	return 0;
}

int asn_x500name::get_value_IA5(buffer_t & s) const {
	int i;	
	int result;
	uint32 saved_l = s.data_len;

	if (!is_present()) return ASN_VALUE_NOT_SET;
	if (child_count == 0) {
		s.append(rdn_separator);
		return 0;
	};

	if (RdnBigEndian) {
		for (i=0; i < (int)child_count; i++) {
			if ((i > 0) || leadingRdnSeparator) s.append(rdn_separator);
			result = get_child(i)->get_value_IA5(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	} else {
		for (i=(int)child_count-1; i >= 0; i--) {
			if ((i < (int)child_count-1) || leadingRdnSeparator) s.append(rdn_separator);
			result = get_child(i)->get_value_IA5(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	};
	return 0;
}

int asn_x500name::get_value_UTF8(buffer_t & s) const {
	int i;	
	int result;
	uint32 saved_l = s.data_len;

	if (!is_present()) return ASN_VALUE_NOT_SET;
	if (child_count == 0) {
		s.append(rdn_separator);
		return 0;
	};

	if (RdnBigEndian) {
		for (i=0; i < (int)child_count; i++) {
			if ((i > 0) || leadingRdnSeparator) s.append(rdn_separator);
			result = get_child(i)->get_value_UTF8(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	} else {
		for (i=(int)child_count-1; i >= 0; i--) {
			if ((i < (int)child_count-1) || leadingRdnSeparator) s.append(rdn_separator);
			result = get_child(i)->get_value_UTF8(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	};
	return 0;
}

int asn_x500name::get_value_BMP(buffer_t & s) const {
	int i;	
	int result;
	uint32 saved_l = s.data_len;

	if (!is_present()) return ASN_VALUE_NOT_SET;
	if (child_count == 0) {
		s.append((unsigned char)0);
		s.append(rdn_separator);
		return 0;
	};

	if (RdnBigEndian) {
		for (i=0; i < (int)child_count; i++) {
			if ((i > 0) || leadingRdnSeparator) {
				s.append((unsigned char) 0);
				s.append(rdn_separator);
			};
			result = get_child(i)->get_value_BMP(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	} else {
		for (i=(int)child_count-1; i >= 0; i--) {
			if ((i < (int)child_count-1) || leadingRdnSeparator) {
				s.append((unsigned char) 0);
				s.append(rdn_separator);
			};
			result = get_child(i)->get_value_BMP(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	};
	return 0;
}

int asn_x500name::get_value_Univ(buffer_t & s) const {
	int i;	
	int result;
	uint32 saved_l = s.data_len;

	if (!is_present()) return ASN_VALUE_NOT_SET;
	if (child_count == 0) {
		s.append((unsigned char)0);
		s.append((unsigned char)0);
		s.append((unsigned char)0);
		s.append(rdn_separator);
		return 0;
	};

	if (RdnBigEndian) {
		for (i=0; i < (int)child_count; i++) {
			if ((i > 0) || leadingRdnSeparator) {
				s.append((unsigned char) 0);
				s.append((unsigned char) 0);
				s.append((unsigned char) 0);
				s.append(rdn_separator);
			};
			result = get_child(i)->get_value_Univ(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	} else {
		for (i=(int)child_count-1; i >= 0; i--) {
			if ((i < (int)child_count-1) || leadingRdnSeparator) {
				s.append((unsigned char) 0);
				s.append((unsigned char) 0);
				s.append((unsigned char) 0);
				s.append(rdn_separator);
			};
			result = get_child(i)->get_value_Univ(s);
			if (result!=0) {
				s.data_len = saved_l;
				return result;
			};
		};
	};
	return 0;
}


int asn_x500name::get_parent(asn_x500name & p) {
	return 0;
}


int asn_RDN::set_value_IA5(r_buffer_t & s) {
	buffer_t ava_buffer;
	asn_AVA * ava;
	unsigned i = 0;
	int result;

	empty();
	while (i < s.data_len) {
		ava_buffer.clear();
		while ((i < s.data_len) && (s[i] != ava_separator)) {
			if ((s[i] == quote_mark) && (i < s.data_len-1) && (s[i+1] == ava_separator)) {
// Ignore quoted ava separator.
				ava_buffer.append(s[i]);
				i++;
			};
			ava_buffer.append(s[i]);
			i++;
		};
// Now we have the complete AVA in ava.  Process it.
		ava = add_child();
		ava->set_attr_value_separator_IA5(attr_value_separator);
		ava->set_quote_mark_IA5(quote_mark);
		ava->set_open_quote_mark_IA5(open_quote_mark);
		ava->set_close_quote_mark_IA5(close_quote_mark);
		result = ava->set_value_IA5(ava_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i++;
	};
	return 0;
}

int asn_RDN::set_value_UTF8(r_buffer_t & s) {
	buffer_t ava_buffer;
	asn_AVA * ava;
	unsigned i = 0;
	int result;

	empty();
	while (i < s.data_len) {
		ava_buffer.clear();
		while ((i < s.data_len) && (s[i] != ava_separator)) {
			if ((s[i] == quote_mark) && (i < s.data_len-1) && (s[i+1] == ava_separator)) {
// Ignore quoted ava separator.
				ava_buffer.append(s[i]);
				i++;
			};
			ava_buffer.append(s[i]);
			i++;
		};
// Now we have the complete AVA in ava.  Process it.
		ava = add_child();
		ava->set_attr_value_separator_IA5(attr_value_separator);
		ava->set_quote_mark_IA5(quote_mark);
		ava->set_open_quote_mark_IA5(open_quote_mark);
		ava->set_close_quote_mark_IA5(close_quote_mark);
		result = ava->set_value_UTF8(ava_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i++;
	};
	return 0;
}

int asn_RDN::set_value_BMP(r_buffer_t & s) {
	buffer_t ava_buffer;
	asn_AVA * ava;
	unsigned i = 0;
	int result;

	empty();
	while (i < s.data_len-1) {
		ava_buffer.clear();
		while ((i < s.data_len-1) && (s[i] != 0) && (s[i+1] != ava_separator)) {
			if ((s[i] == 0) && (s[i+1] == quote_mark) && 
					(i < s.data_len-3) && 
					(s[i+2] == 0) && (s[i+3] == ava_separator)) {
// Ignore quoted ava separator.
				ava_buffer.append(s[i]);
				ava_buffer.append(s[i+1]);
				i+=2;
			};
			ava_buffer.append(s[i]);
			ava_buffer.append(s[i+1]);
			i+=2;
		};
// Now we have the complete AVA in ava.  Process it.
		ava = add_child();
		ava->set_attr_value_separator_IA5(attr_value_separator);
		ava->set_quote_mark_IA5(quote_mark);
		ava->set_open_quote_mark_IA5(open_quote_mark);
		ava->set_close_quote_mark_IA5(close_quote_mark);
		result = ava->set_value_BMP(ava_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i+=2;
	};
	return 0;
}

int asn_RDN::set_value_Univ(r_buffer_t & s) {
	buffer_t ava_buffer;
	asn_AVA * ava;
	unsigned i = 0;
	int result;

	empty();
	while (i < s.data_len-3) {
		ava_buffer.clear();
		while ((i < s.data_len-3) && 
					 (s[i] != 0) && (s[i+1] != 0) && (s[i+2] != 0) && (s[i+3] != ava_separator)) {
			if ((s[i] == 0) && (s[i+1] == 0) && (s[i+2] == 0) && (s[i+3] == quote_mark) && 
					(i < s.data_len-7) && 
					(s[i+4] == 0) && (s[i+5] == 0) && (s[i+6] == 0) && (s[i+7] == ava_separator)) {
// Ignore quoted ava separator.
				ava_buffer.append(s[i]);
				ava_buffer.append(s[i+1]);
				ava_buffer.append(s[i+2]);
				ava_buffer.append(s[i+3]);
				i+=4;
			};
			ava_buffer.append(s[i]);
			ava_buffer.append(s[i+1]);
			ava_buffer.append(s[i+2]);
			ava_buffer.append(s[i+3]);
			i+=4;
		};
// Now we have the complete AVA in ava.  Process it.
		ava = add_child();
		ava->set_attr_value_separator_IA5(attr_value_separator);
		ava->set_quote_mark_IA5(quote_mark);
		ava->set_open_quote_mark_IA5(open_quote_mark);
		ava->set_close_quote_mark_IA5(close_quote_mark);
		result = ava->set_value_Univ(ava_buffer);
		if (result != 0) {
			empty();
			return result;
		};
		i+=4;
	};
	return 0;
}

int asn_RDN::get_value_IA5(buffer_t & s) const {
	unsigned i;	
	int result;
	uint32 saved_l = s.data_len;

	if (!is_present()) return ASN_VALUE_NOT_SET;
	if (child_count == 0) {
		return 0;
	};
	for (i=0; i<child_count; i++) {
		if (i>0) s.append(ava_separator);
		result = get_child(i)->get_value_IA5(s);
		if (result!=0) {
			s.data_len = saved_l;
			return result;
		};
	};
	return 0;
}


int asn_RDN::get_value_UTF8(buffer_t & s) const {
	unsigned i;	
	int result;
	uint32 saved_l = s.data_len;

	if (!is_present()) return ASN_VALUE_NOT_SET;
	if (child_count == 0) {
		return 0;
	};
	for (i=0; i<child_count; i++) {
		if (i>0) s.append(ava_separator);
		result = get_child(i)->get_value_UTF8(s);
		if (result!=0) {
			s.data_len = saved_l;
			return result;
		};
	};
	return 0;
}

int asn_RDN::get_value_BMP(buffer_t & s) const {
	unsigned i;	
	int result;
	uint32 saved_l = s.data_len;

  if (!is_present()) return ASN_VALUE_NOT_SET;
  if (child_count == 0) {
    return 0;
  };
  for (i=0; i<child_count; i++) {
    if (i>0) {
      s.append((unsigned char)0);
      s.append(ava_separator);
    }
    result = get_child(i)->get_value_BMP(s);
    if (result!=0) {
      s.data_len = saved_l;
      return result;
    };
  };
  return 0;
}

int asn_RDN::get_value_Univ(buffer_t & s) const {
  unsigned i;	
  int result;
  uint32 saved_l = s.data_len;

  if (!is_present()) return ASN_VALUE_NOT_SET;
  if (child_count == 0) {
    return 0;
  };
  for (i=0; i<child_count; i++) {
    if (i>0) {
      s.append((unsigned char)0);
      s.append((unsigned char)0);
      s.append((unsigned char)0);
      s.append(ava_separator);
    }
    result = get_child(i)->get_value_Univ(s);
    if (result!=0) {
      s.data_len = saved_l;
      return result;
    };
  };
  return 0;
}


int asn_AVA::set_value_IA5(r_buffer_t & s) {
// Process a single attribute-value assertion.
  int result;
  r_buffer_t a;
  r_buffer_t v;
  buffer_t temp;
  
  a.data = s.data;
  for (a.data_len=0; 
       (a.data_len < s.data_len) && (s[a.data_len] != attr_value_separator);
       a.data_len++);
  if (a.data_len >= s.data_len) return ASN_X500_NO_AVA_SEP;
  v.data = a.data + a.data_len + 1;
  v.data_len = s.data_len - a.data_len - 1;
  if (v.data_len == 0) return ASN_X500_NO_VALUE;

  if ((result = attr_lookup(a, oid_subident_separator, attr, matching_rule)) != 0) 
    return result;
  if ((result = unquote_IA5(v, temp)) != 0) return result;
  return value.set_value_IA5(temp);	

}


int asn_AVA::set_value_UTF8(r_buffer_t & s) {
// Process a single attribute-value assertion.
  int result;
  r_buffer_t a;
  r_buffer_t v;
  buffer_t temp;

  a.data = s.data;
  for (a.data_len=0; 
       (a.data_len < s.data_len) && (s[a.data_len] != attr_value_separator);
       a.data_len++);
  if (a.data_len >= s.data_len) return ASN_X500_NO_AVA_SEP;
  v.data = a.data + a.data_len + 1;
  v.data_len = s.data_len - a.data_len - 1;
  if (v.data_len == 0) return ASN_X500_NO_VALUE;

  if ((result = attr_lookup(a, oid_subident_separator, attr, matching_rule)) != 0) 
    return result;
  if ((result = unquote_UTF8(v, temp)) != 0) return result;
  return value.set_value_UTF8(temp);	

}

int asn_AVA::set_value_BMP(r_buffer_t & s) {
// Process a single attribute-value assertion.
	int result;
	r_buffer_t a;
	r_buffer_t v;
	buffer_t temp;

	a.data = s.data;
	for (a.data_len=0; 
			 (a.data_len < s.data_len-1) && 
				(s[a.data_len] != 0) && (s[a.data_len+1] != attr_value_separator);
			 a.data_len+=2);
  if (a.data_len >= s.data_len) return ASN_X500_NO_AVA_SEP;
	v.data = a.data + a.data_len + 2;
	v.data_len = s.data_len - a.data_len - 2;
  if (v.data_len == 0) return ASN_X500_NO_VALUE;

	result = BMP2IA5(a, temp);
	if (result) return result;
	if ((result = attr_lookup(temp, oid_subident_separator, attr, matching_rule)) != 0) return result;
	temp.clear();
  if ((result = unquote_BMP(v, temp)) != 0) return result;
	return value.set_value_BMP(temp);	

}

int asn_AVA::set_value_Univ(r_buffer_t & s) {
// Process a single attribute-value assertion.
	int result;
	r_buffer_t a;
	r_buffer_t v;
	buffer_t temp;

	a.data = s.data;
	for (a.data_len=0; 
			 (a.data_len < s.data_len-3) && 
				(s[a.data_len] != 0) && (s[a.data_len+1] != 0) && (s[a.data_len+2] != 0) && (s[a.data_len+3] != attr_value_separator);
			 a.data_len+=4);
  if (a.data_len >= s.data_len) return ASN_X500_NO_AVA_SEP;
	v.data = a.data + a.data_len + 4;
	v.data_len = s.data_len - a.data_len - 4;
  if (v.data_len == 0) return ASN_X500_NO_VALUE;

	result = U2IA5(a, temp);
	if (result) return result;
	if ((result = attr_lookup(temp, oid_subident_separator, attr, matching_rule)) != 0) return result;
	temp.clear();
  if ((result = unquote_Univ(v, temp)) != 0) return result;
	return value.set_value_Univ(temp);	

}

int asn_AVA::get_value_IA5(buffer_t & s) const {
	uint32 saved_l = s.data_len;
	int result=0;
	unsigned i;
	unsigned j;
	bool decoded = false;

// Try converting the attr to a text form.  If that fails, display it as a numeric OID.
  if (attr.display_name_printable(s) == 0) decoded = true;
  else {
    for (i=0; i < max_attrs; i++) {
      if (attrOid[i].val == NULL) break;  // No more attribute names
      if (attr.is_equal(attrOid[i].val, attrOid[i].len)) {
// Copy the matching attribute name to the buffer and return
        j = 0;
        while (attrName[i][j] != '\0') s.append(attrName[i][j++]);
        decoded = true;
        break;
      };
    };
  };

	if (!decoded && ((result = attr.display_printable(s)) != 0)) return result;  // printable is a proper subset of IA5
  
	if ((result = s.append(attr_value_separator)) != 0) goto fail;
	if ((result = value.get_value_IA5(s)) != 0) goto fail;

	return 0;
fail:
	s.data_len = saved_l;
	return result;
}

int asn_AVA::get_value_UTF8(buffer_t & s) const {
	uint32 saved_l = s.data_len;
	int result=0;
	unsigned i;
	unsigned j;
	bool decoded = false;

// Try converting the attr to a text form.  If that fails, display it as a numeric OID.
  if (attr.display_name_printable(s) == 0) decoded = true;
  else {
    for (i=0; i < max_attrs; i++) {
      if (attrOid[i].val == NULL) break;  // No more attribute names
      if (attr.is_equal(attrOid[i].val, attrOid[i].len)) {
// Copy the matching attribute name to the buffer and return
        j = 0;
        while (attrName[i][j] != '\0') s.append(attrName[i][j++]);
        decoded = true;
        break;
      };
    };
  };

	if (!decoded && ((result = attr.display_printable(s)) != 0)) return result;  // printable is a proper subset of IA5
  
	if ((result = s.append(attr_value_separator)) != 0) goto fail;
	if ((result = value.get_value_UTF8(s)) != 0) goto fail;

	return 0;
fail:
	s.data_len = saved_l;
	return result;
}

int asn_AVA::get_value_BMP(buffer_t & s) const {
	uint32 saved_l = s.data_len;
	int result=0;
	unsigned i;
	unsigned j;
	bool decoded = false;
	buffer_t temp;

// Try converting the attr to a text form.  If that fails, display it as a numeric OID.
  if (attr.display_name_printable(temp) == 0) decoded = true;
  else {
    for (i=0; i < max_attrs; i++) {
  		if (attrOid[i].val == NULL) break;  // No more attribute names
		  if (attr.is_equal(attrOid[i].val, attrOid[i].len)) {
// Copy the matching attribute name to the buffer and return
  			j = 0;
			  while (attrName[i][j] != '\0') temp.append(attrName[i][j++]);
			  decoded = true;
			  break;
		  };
	  };
  };

	if ((!decoded) && ((result = attr.display_printable(temp)) != 0)) return result;  // printable is a proper subset of IA5

	result = IA52BMP(temp, s);
	if (result) return result;
		
	s.append((unsigned char) 0);
	s.append(attr_value_separator);

	if ((result = value.get_value_BMP(s)) == 0) return 0;

	s.data_len = saved_l;
	return result;
}

int asn_AVA::get_value_Univ(buffer_t & s) const {
	uint32 saved_l = s.data_len;
	int result=0;
	unsigned i;
	unsigned j;
	bool decoded = false;
	buffer_t temp;

// Try converting the attr to a text form.  If that fails, display it as a numeric OID.
  if (attr.display_name_printable(s) == 0) decoded = true;
  else {
    for (i=0; i < max_attrs; i++) {
  		if (attrOid[i].val == NULL) break;  // No more attribute names
		  if (attr.is_equal(attrOid[i].val, attrOid[i].len)) {
// Copy the matching attribute name to the buffer and return
  			j = 0;
			  while (attrName[i][j] != '\0') temp.append(attrName[i][j++]);
			  decoded = true;
			  break;
		  };
	  };
  };

	if ((!decoded) && ((result = attr.display_printable(temp)) != 0)) return result;  // printable is a proper subset of IA5

	result = IA52U(temp, s);
	if (result) return result;
		
	s.append((unsigned char) 0);
	s.append((unsigned char) 0);
	s.append((unsigned char) 0);
	s.append(attr_value_separator);

	if ((result = value.get_value_Univ(s)) == 0) return 0;

	s.data_len = saved_l;
	return result;
}


int attr_lookup(r_buffer_t & s,
								unsigned char oid_subident_separator,
								asn_oid & oid,
								match_t & match) {
// The attribute name or OID is assumed to be expressed in IA5.
	
	unsigned i;
	unsigned long subident;

	if (s.data_len == 0) return ASN_X500_OID_SYNTAX_ERROR;

// first see if we have a symbolic name...
	for (i=0; i<max_attrs; i++) {
		if (attrName[i] == NULL) break;
		if (caseExactMatch_IA5(s, attrName[i])) {
			oid.set_value(attrOid[i].val, attrOid[i].len);
			match = attrMatch[i];
			return 0;
		};
	};

// If we didn't match a symbolic name, try for a numeric representation.
// Assume that the OID will be expressed as a series of integers, separated by
// periods (or whatever the separator has been configured as).
	
	oid.empty();
	i = 0;
	while (i <= s.data_len) {
		if ((i == s.data_len) || (s[i] == oid_subident_separator)) {
			oid.append_subident(subident);
			subident = 0;
		} else if ((s[i] < 0x30u) || (s[i] > 0x39u)) {
			oid.empty();
			return ASN_X500_OID_SYNTAX_ERROR;
		} else {
			subident = subident*10 + (s[i] - 0x30u);
		};
		i++;
	};
	
	match = match_caseExact;

	return 0;
}

asn_AVA::asn_AVA(security_t s) : asn_sequence(s) {
  strcpy(objType, "AVA");

  register_child(&attr);
  register_child(&value);
  
  set_attr_value_separator();
  set_quote_mark();
  set_open_quote_mark();
  set_close_quote_mark();
  set_oid_subident_separator();
};


// Presentation option stuff...

int asn_x500name::set_rdn_bigendian(bool bigendian) {
	RdnBigEndian = bigendian;
	return 0;
}

int asn_x500name::set_leading_rdn_separator(bool b) {
	leadingRdnSeparator = b;
	return 0;
}

void asn_RDN::propagate_syntax_options(void) {
	asn_AVA * ava;
	unsigned i;
	for (i=0; i<child_count; i++) {
		ava = (asn_AVA *) (get_child(i));
		ava->set_quote_mark_IA5(quote_mark);
		ava->set_attr_value_separator_IA5(attr_value_separator);
		ava->set_open_quote_mark_IA5(open_quote_mark);
		ava->set_close_quote_mark_IA5(close_quote_mark);
		ava->set_oid_subident_separator_IA5(oid_subident_separator);
	};
}
	
void asn_x500name::propagate_syntax_options(void) {
	asn_RDN * rdn;
	unsigned i;
	for (i=0; i<child_count; i++) {
		rdn = (asn_RDN *) (get_child(i));
		rdn->set_quote_mark_IA5(quote_mark);
		rdn->set_attr_value_separator_IA5(attr_value_separator);
		rdn->set_open_quote_mark_IA5(open_quote_mark);
		rdn->set_close_quote_mark_IA5(close_quote_mark);
		rdn->set_oid_subident_separator_IA5(oid_subident_separator);
		rdn->set_ava_separator_IA5(ava_separator);
	};
}
	
int asn_AVA::set_quote_mark_IA5(unsigned char c) {
	quote_mark = c;
	return 0;
}

int asn_AVA::set_open_quote_mark_IA5(unsigned char c) {
	open_quote_mark = c;
	return 0;
}

int asn_AVA::set_close_quote_mark_IA5(unsigned char c) {
	close_quote_mark = c;
	return 0;
}

int asn_AVA::set_attr_value_separator_IA5(unsigned char c) {
	attr_value_separator = c;
	return 0;
}

int asn_AVA::set_oid_subident_separator_IA5(unsigned char c) {
	oid_subident_separator = c;
	return 0;
}

int asn_RDN::set_quote_mark_IA5(unsigned char c) {
	quote_mark = c;
	propagate_syntax_options();
	return 0;
}

int asn_RDN::set_open_quote_mark_IA5(unsigned char c) {
	open_quote_mark = c;
	propagate_syntax_options();
	return 0;
}

int asn_RDN::set_close_quote_mark_IA5(unsigned char c) {
	close_quote_mark = c;
	propagate_syntax_options();
	return 0;
}

int asn_RDN::set_attr_value_separator_IA5(unsigned char c) {
	attr_value_separator = c;
	propagate_syntax_options();
	return 0;
}


int asn_RDN::set_ava_separator_IA5(unsigned char c) {
	ava_separator = c;
	propagate_syntax_options();
	return 0;
}

int asn_RDN::set_oid_subident_separator_IA5(unsigned char c) {
	oid_subident_separator = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_oid_subident_separator_IA5(unsigned char c) {
	oid_subident_separator = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_rdn_separator_IA5(unsigned char c) {
	rdn_separator = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_ava_separator_IA5(unsigned char c) {
	ava_separator = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_attr_value_separator_IA5(unsigned char c) {
	attr_value_separator = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_quote_mark_IA5(unsigned char c) {
	quote_mark = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_open_quote_mark_IA5(unsigned char c) {
	open_quote_mark = c;
	propagate_syntax_options();
	return 0;
}

int asn_x500name::set_close_quote_mark_IA5(unsigned char c) {
	close_quote_mark = c;
	propagate_syntax_options();
	return 0;
}




int asn_AVA::set_quote_mark(char c) {
	return set_quote_mark_IA5(Local2IA5(c));
}

int asn_AVA::set_open_quote_mark(char c) {
	return set_open_quote_mark_IA5(Local2IA5(c));
}

int asn_AVA::set_close_quote_mark(char c) {
	return set_close_quote_mark_IA5(Local2IA5(c));
}

int asn_AVA::set_attr_value_separator(char c) {
	return set_attr_value_separator_IA5(Local2IA5(c));
}

int asn_AVA::set_oid_subident_separator(char c) {
	return set_oid_subident_separator_IA5(Local2IA5(c));
}


int asn_RDN::set_quote_mark(char c) {
	return set_quote_mark_IA5(Local2IA5(c));
}

int asn_RDN::set_open_quote_mark(char c) {
	return set_open_quote_mark_IA5(Local2IA5(c));
}

int asn_RDN::set_close_quote_mark(char c) {
	return set_close_quote_mark_IA5(Local2IA5(c));
}

int asn_RDN::set_attr_value_separator(char c) {
	return set_attr_value_separator_IA5(Local2IA5(c));
}

int asn_RDN::set_ava_separator(char c) {
	return set_ava_separator_IA5(Local2IA5(c));
}

int asn_RDN::set_oid_subident_separator(char c) {
	return set_oid_subident_separator_IA5(Local2IA5(c));
}


int asn_x500name::set_oid_subident_separator(char c) {
	return set_oid_subident_separator_IA5(Local2IA5(c));
}

int asn_x500name::set_rdn_separator(char c) {
	return set_rdn_separator_IA5(Local2IA5(c));
}

int asn_x500name::set_ava_separator(char c) {
	return set_ava_separator_IA5(Local2IA5(c));
}

int asn_x500name::set_attr_value_separator(char c) {
	return set_attr_value_separator_IA5(Local2IA5(c));
}

int asn_x500name::set_quote_mark(char c) {
	return set_quote_mark_IA5(Local2IA5(c));
}

int asn_x500name::set_open_quote_mark(char c) {
	return set_open_quote_mark_IA5(Local2IA5(c));
}

int asn_x500name::set_close_quote_mark(char c) {
	return set_close_quote_mark_IA5(Local2IA5(c));
}
