content: package local.org.bouncycastle.asn1.x500.style;
  
  import java.io.IOException;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Vector;
  
  import local.org.bouncycastle.asn1.ASN1Encodable;
  import local.org.bouncycastle.asn1.ASN1Encoding;
  import local.org.bouncycastle.asn1.ASN1ObjectIdentifier;
  import local.org.bouncycastle.asn1.ASN1Primitive;
  import local.org.bouncycastle.asn1.ASN1String;
  import local.org.bouncycastle.asn1.DERUniversalString;
  import local.org.bouncycastle.asn1.x500.AttributeTypeAndValue;
  import local.org.bouncycastle.asn1.x500.RDN;
  import local.org.bouncycastle.asn1.x500.X500NameBuilder;
  import local.org.bouncycastle.asn1.x500.X500NameStyle;
  import local.org.bouncycastle.util.Strings;
  import local.org.bouncycastle.util.encoders.Hex;
  
  
  public class IETFUtils
  {
      private static String unescape(String elt)
      {
          if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
          {
              return elt.trim();
          }
  
          char[] elts = elt.toCharArray();
          boolean escaped = false;
          boolean quoted = false;
          StringBuffer buf = new StringBuffer(elt.length());
          int start = 0;
  
          // if it's an escaped hash string and not an actual encoding in string form
          // we need to leave it escaped.
          if (elts[0] == '\\')
          {
              if (elts[1] == '#')
              {
                  start = 2;
                  buf.append("\\#");
              }
          }
  
          boolean nonWhiteSpaceEncountered = false;
          int     lastEscaped = 0;
          char    hex1 = 0;
  
          for (int i = start; i != elts.length; i++)
          {
              char c = elts[i];
  
              if (c != ' ')
              {
                  nonWhiteSpaceEncountered = true;
              }
  
              if (c == '"')
              {
                  if (!escaped)
                  {
                      quoted = !quoted;
                  }
                  else
                  {
                      buf.append(c);
                  }
                  escaped = false;
              }
              else if (c == '\\' && !(escaped || quoted))
              {
                  escaped = true;
                  lastEscaped = buf.length();
              }
              else
              {
                  if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
                  {
                      continue;
                  }
                  if (escaped && isHexDigit(c))
                  {
                      if (hex1 != 0)
                      {
                          buf.append((char)(convertHex(hex1) * 16 + convertHex(c)));
                          escaped = false;
                          hex1 = 0;
                          continue;
                      }
                      hex1 = c;
                      continue;
                  }
                  buf.append(c);
                  escaped = false;
              }
          }
  
          if (buf.length() > 0)
          {
              while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
              {
                  buf.setLength(buf.length() - 1);
              }
          }
  
          return buf.toString();
      }
  
      private static boolean isHexDigit(char c)
      {
          return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
      }
  
      private static int convertHex(char c)
      {
          if ('0' <= c && c <= '9')
          {
              return c - '0';
          }
          if ('a' <= c && c <= 'f')
          {
              return c - 'a' + 10;
          }
          return c - 'A' + 10;
      }
  
      public static RDN[] rDNsFromString(String name, X500NameStyle x500Style)
      {
          X500NameTokenizer nTok = new X500NameTokenizer(name);
          X500NameBuilder builder = new X500NameBuilder(x500Style);
  
          while (nTok.hasMoreTokens())
          {
              String  token = nTok.nextToken();
  
              if (token.indexOf('+') > 0)
              {
                  X500NameTokenizer   pTok = new X500NameTokenizer(token, '+');
                  X500NameTokenizer   vTok = new X500NameTokenizer(pTok.nextToken(), '=');
  
                  String              attr = vTok.nextToken();
  
                  if (!vTok.hasMoreTokens())
                  {
                      throw new IllegalArgumentException("badly formatted directory string");
                  }
  
                  String               value = vTok.nextToken();
                  ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
  
                  if (pTok.hasMoreTokens())
                  {
                      Vector oids = new Vector();
                      Vector values = new Vector();
  
                      oids.addElement(oid);
                      values.addElement(unescape(value));
  
                      while (pTok.hasMoreTokens())
                      {
                          vTok = new X500NameTokenizer(pTok.nextToken(), '=');
  
                          attr = vTok.nextToken();
  
                          if (!vTok.hasMoreTokens())
                          {
                              throw new IllegalArgumentException("badly formatted directory string");
                          }
  
                          value = vTok.nextToken();
                          oid = x500Style.attrNameToOID(attr.trim());
  
  
                          oids.addElement(oid);
                          values.addElement(unescape(value));
                      }
  
                      builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values));
                  }
                  else
                  {
                      builder.addRDN(oid, unescape(value));
                  }
              }
              else
              {
                  X500NameTokenizer   vTok = new X500NameTokenizer(token, '=');
  
                  String              attr = vTok.nextToken();
  
                  if (!vTok.hasMoreTokens())
                  {
                      throw new IllegalArgumentException("badly formatted directory string");
                  }
  
                  String               value = vTok.nextToken();
                  ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr.trim());
  
                  builder.addRDN(oid, unescape(value));
              }
          }
  
          return builder.build().getRDNs();
      }
  
      private static String[] toValueArray(Vector values)
      {
          String[] tmp = new String[values.size()];
  
          for (int i = 0; i != tmp.length; i++)
          {
              tmp[i] = (String)values.elementAt(i);
          }
  
          return tmp;
      }
  
      private static ASN1ObjectIdentifier[] toOIDArray(Vector oids)
      {
          ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()];
  
          for (int i = 0; i != tmp.length; i++)
          {
              tmp[i] = (ASN1ObjectIdentifier)oids.elementAt(i);
          }
  
          return tmp;
      }
  
      public static String[] findAttrNamesForOID(
          ASN1ObjectIdentifier oid,
          Hashtable            lookup)
      {
          int count = 0;
          for (Enumeration en = lookup.elements(); en.hasMoreElements();)
          {
              if (oid.equals(en.nextElement()))
              {
                  count++;
              }
          }
  
          String[] aliases = new String[count];
          count = 0;
  
          for (Enumeration en = lookup.keys(); en.hasMoreElements();)
          {
              String key = (String)en.nextElement();
              if (oid.equals(lookup.get(key)))
              {
                  aliases[count++] = key;
              }
          }
  
          return aliases;
      }
  
      public static ASN1ObjectIdentifier decodeAttrName(
          String      name,
          Hashtable   lookUp)
      {
          if (Strings.toUpperCase(name).startsWith("OID."))
          {
              return new ASN1ObjectIdentifier(name.substring(4));
          }
          else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
          {
              return new ASN1ObjectIdentifier(name);
          }
  
          ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
          if (oid == null)
          {
              throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
          }
  
          return oid;
      }
  
      public static ASN1Encodable valueFromHexString(
          String  str,
          int     off)
          throws IOException
      {
          byte[] data = new byte[(str.length() - off) / 2];
          for (int index = 0; index != data.length; index++)
          {
              char left = str.charAt((index * 2) + off);
              char right = str.charAt((index * 2) + off + 1);
  
              data[index] = (byte)((convertHex(left) << 4) | convertHex(right));
          }
  
          return ASN1Primitive.fromByteArray(data);
      }
  
      public static void appendRDN(
          StringBuffer          buf,
          RDN                   rdn,
          Hashtable             oidSymbols)
      {
          if (rdn.isMultiValued())
          {
              AttributeTypeAndValue[] atv = rdn.getTypesAndValues();
              boolean firstAtv = true;
  
              for (int j = 0; j != atv.length; j++)
              {
                  if (firstAtv)
                  {
                      firstAtv = false;
                  }
                  else
                  {
                      buf.append('+');
                  }
  
                  IETFUtils.appendTypeAndValue(buf, atv[j], oidSymbols);
              }
          }
          else
          {
              IETFUtils.appendTypeAndValue(buf, rdn.getFirst(), oidSymbols);
          }
      }
  
      public static void appendTypeAndValue(
          StringBuffer          buf,
          AttributeTypeAndValue typeAndValue,
          Hashtable             oidSymbols)
      {
          String  sym = (String)oidSymbols.get(typeAndValue.getType());
  
          if (sym != null)
          {
              buf.append(sym);
          }
          else
          {
              buf.append(typeAndValue.getType().getId());
          }
  
          buf.append('=');
  
          buf.append(valueToString(typeAndValue.getValue()));
      }
  
      public static String valueToString(ASN1Encodable value)
      {
          StringBuffer vBuf = new StringBuffer();
  
          if (value instanceof ASN1String && !(value instanceof DERUniversalString))
          {
              String v = ((ASN1String)value).getString();
              if (v.length() > 0 && v.charAt(0) == '#')
              {
                  vBuf.append("\\" + v);
              }
              else
              {
                  vBuf.append(v);
              }
          }
          else
          {
              try
              {
                  vBuf.append("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
              }
              catch (IOException e)
              {
                  throw new IllegalArgumentException("Other value has no encoded form");
              }
          }
  
          int     end = vBuf.length();
          int     index = 0;
  
          if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#')
          {
              index += 2;
          }
  
          while (index != end)
          {
              if ((vBuf.charAt(index) == ',')
                 || (vBuf.charAt(index) == '"')
                 || (vBuf.charAt(index) == '\\')
                 || (vBuf.charAt(index) == '+')
                 || (vBuf.charAt(index) == '=')
                 || (vBuf.charAt(index) == '<')
                 || (vBuf.charAt(index) == '>')
                 || (vBuf.charAt(index) == ';'))
              {
                  vBuf.insert(index, "\\");
                  index++;
                  end++;
              }
  
              index++;
          }
  
          int start = 0;
          if (vBuf.length() > 0)
          {
              while (vBuf.charAt(start) == ' ')
              {
                  vBuf.insert(start, "\\");
                  start += 2;
              }
          }
  
          int endBuf = vBuf.length() - 1;
  
          while (endBuf >= 0 && vBuf.charAt(endBuf) == ' ')
          {
              vBuf.insert(endBuf, '\\');
              endBuf--;
          }
  
          return vBuf.toString();
      }
  
      private static String bytesToString(
          byte[] data)
      {
          char[]  cs = new char[data.length];
  
          for (int i = 0; i != cs.length; i++)
          {
              cs[i] = (char)(data[i] & 0xff);
          }
  
          return new String(cs);
      }
  
      public static String canonicalize(String s)
      {
          String value = Strings.toLowerCase(s.trim());
  
          if (value.length() > 0 && value.charAt(0) == '#')
          {
              ASN1Primitive obj = decodeObject(value);
  
              if (obj instanceof ASN1String)
              {
                  value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
              }
          }
  
          value = stripInternalSpaces(value);
  
          return value;
      }
  
      private static ASN1Primitive decodeObject(String oValue)
      {
          try
          {
              return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
          }
          catch (IOException e)
          {
              throw new IllegalStateException("unknown encoding in name: " + e);
          }
      }
  
      public static String stripInternalSpaces(
          String str)
      {
          StringBuffer res = new StringBuffer();
  
          if (str.length() != 0)
          {
              char c1 = str.charAt(0);
  
              res.append(c1);
  
              for (int k = 1; k < str.length(); k++)
              {
                  char c2 = str.charAt(k);
                  if (!(c1 == ' ' && c2 == ' '))
                  {
                      res.append(c2);
                  }
                  c1 = c2;
              }
          }
  
          return res.toString();
      }
  
      public static boolean rDNAreEqual(RDN rdn1, RDN rdn2)
      {
          if (rdn1.isMultiValued())
          {
              if (rdn2.isMultiValued())
              {
                  AttributeTypeAndValue[] atvs1 = rdn1.getTypesAndValues();
                  AttributeTypeAndValue[] atvs2 = rdn2.getTypesAndValues();
  
                  if (atvs1.length != atvs2.length)
                  {
                      return false;
                  }
  
                  for (int i = 0; i != atvs1.length; i++)
                  {
                      if (!atvAreEqual(atvs1[i], atvs2[i]))
                      {
                          return false;
                      }
                  }
              }
              else
              {
                  return false;
              }
          }
          else
          {
              if (!rdn2.isMultiValued())
              {
                  return atvAreEqual(rdn1.getFirst(), rdn2.getFirst());
              }
              else
              {
                  return false;
              }
          }
  
          return true;
      }
  
      private static boolean atvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2)
      {
          if (atv1 == atv2)
          {
              return true;
          }
  
          if (atv1 == null)
          {
              return false;
          }
  
          if (atv2 == null)
          {
              return false;
          }
  
          ASN1ObjectIdentifier o1 = atv1.getType();
          ASN1ObjectIdentifier o2 = atv2.getType();
  
          if (!o1.equals(o2))
          {
              return false;
          }
  
          String v1 = IETFUtils.canonicalize(IETFUtils.valueToString(atv1.getValue()));
          String v2 = IETFUtils.canonicalize(IETFUtils.valueToString(atv2.getValue()));
  
          if (!v1.equals(v2))
          {
              return false;
          }
  
          return true;
      }
  }
  