| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @author Alexander V. Esin |
| * @version $Revision$ |
| */ |
| |
| package org.apache.harmony.security.x501; |
| |
| import java.io.IOException; |
| import org.apache.harmony.security.asn1.ASN1StringType; |
| import org.apache.harmony.security.asn1.DerInputStream; |
| import org.apache.harmony.security.x509.Utils; |
| |
| |
| /** |
| * X.501 Attribute Value |
| */ |
| public class AttributeValue { |
| |
| public final boolean wasEncoded; |
| |
| public String escapedString; |
| |
| private String hexString; |
| |
| private int tag = -1; |
| |
| public byte[] encoded; |
| |
| public byte[] bytes; //FIXME remove??? bytes to be encoded |
| |
| public boolean hasQE; // raw string contains '"' or '\' |
| |
| public AttributeValue(String parsedString, boolean hasQorE) { |
| |
| wasEncoded = false; |
| |
| this.hasQE = hasQorE; |
| |
| this.rawString = parsedString; |
| this.escapedString = makeEscaped(rawString); |
| } |
| |
| public AttributeValue(String hexString, byte[] encoded) { |
| |
| wasEncoded = true; |
| |
| this.hexString = hexString; |
| this.encoded = encoded; |
| |
| try { |
| DerInputStream in = new DerInputStream(encoded); |
| |
| tag = in.tag; |
| |
| if (DirectoryString.ASN1.checkTag(tag)) { |
| // has string representation |
| this.rawString = (String) DirectoryString.ASN1.decode(in); |
| this.escapedString = makeEscaped(rawString); |
| } else { |
| this.rawString = hexString; |
| this.escapedString = hexString; |
| } |
| } catch (IOException e) { |
| IllegalArgumentException iae = new IllegalArgumentException(); //FIXME message |
| iae.initCause(e); |
| throw iae; |
| } |
| } |
| |
| public String rawString; |
| |
| public AttributeValue(String rawString, byte[] encoded, int tag) { |
| |
| wasEncoded = true; |
| |
| this.encoded = encoded; |
| this.tag = tag; |
| |
| if (rawString == null) { |
| this.rawString = getHexString(); |
| this.escapedString = hexString; |
| } else { |
| this.rawString = rawString; |
| this.escapedString = makeEscaped(rawString); |
| } |
| } |
| |
| public int getTag() { |
| if (tag == -1) { |
| if (Utils.isPrintableString(rawString)) { |
| tag = ASN1StringType.PRINTABLESTRING.id; |
| } else { |
| tag = ASN1StringType.UTF8STRING.id; |
| } |
| } |
| return tag; |
| } |
| |
| public String getHexString() { |
| if (hexString == null) { |
| |
| if (!wasEncoded) { |
| //FIXME optimize me: what about reusable OutputStream??? |
| if (Utils.isPrintableString(rawString)) { |
| encoded = ASN1StringType.PRINTABLESTRING.encode(rawString); |
| } else { |
| encoded = ASN1StringType.UTF8STRING.encode(rawString); |
| } |
| } |
| |
| StringBuilder buf = new StringBuilder(encoded.length * 2 + 1); |
| buf.append('#'); |
| |
| for (int i = 0, c; i < encoded.length; i++) { |
| c = (encoded[i] >> 4) & 0x0F; |
| if (c < 10) { |
| buf.append((char) (c + 48)); |
| } else { |
| buf.append((char) (c + 87)); |
| } |
| |
| c = encoded[i] & 0x0F; |
| if (c < 10) { |
| buf.append((char) (c + 48)); |
| } else { |
| buf.append((char) (c + 87)); |
| } |
| } |
| hexString = buf.toString(); |
| } |
| return hexString; |
| } |
| |
| public void appendQEString(StringBuffer buf) { |
| buf.append('"'); |
| if (hasQE) { |
| char c; |
| for (int i = 0; i < rawString.length(); i++) { |
| c = rawString.charAt(i); |
| if (c == '"' || c == '\\') { |
| buf.append('\\'); |
| } |
| buf.append(c); |
| } |
| } else { |
| buf.append(rawString); |
| } |
| buf.append('"'); |
| } |
| |
| // |
| // Escapes: |
| // 1) chars ",", "+", """, "\", "<", ">", ";" (RFC 2253) |
| // 2) chars "#", "=" (required by RFC 1779) |
| // 3) a space char at the beginning or end |
| // 4) according to the requirement to be RFC 1779 compatible: |
| // '#' char is escaped in any position |
| // |
| private String makeEscaped(String name) { |
| |
| int length = name.length(); |
| if (length == 0) { |
| return name; |
| } |
| StringBuilder buf = new StringBuilder(length * 2); |
| |
| for (int index = 0; index < length; index++) { |
| |
| char ch = name.charAt(index); |
| |
| switch (ch) { |
| |
| case ' ': |
| if (index == 0 || index == (length - 1)) { |
| // escape first or last space |
| buf.append('\\'); |
| } |
| buf.append(' '); |
| break; |
| |
| case '"': |
| case '\\': |
| hasQE = true; |
| |
| case ',': |
| case '+': |
| case '<': |
| case '>': |
| case ';': |
| case '#': // required by RFC 1779 |
| case '=': // required by RFC 1779 |
| buf.append('\\'); |
| |
| default: |
| buf.append(ch); |
| } |
| } |
| |
| return buf.toString(); |
| } |
| |
| public String makeCanonical() { |
| |
| int length = rawString.length(); |
| if (length == 0) { |
| return rawString; |
| } |
| StringBuilder buf = new StringBuilder(length * 2); |
| |
| int index = 0; |
| if (rawString.charAt(0) == '#') { |
| buf.append('\\'); |
| buf.append('#'); |
| index++; |
| } |
| |
| int bufLength; |
| for (; index < length; index++) { |
| |
| char ch = rawString.charAt(index); |
| |
| switch (ch) { |
| |
| case ' ': |
| bufLength = buf.length(); |
| if (bufLength == 0 || buf.charAt(bufLength - 1) == ' ') { |
| break; |
| } |
| buf.append(' '); |
| break; |
| |
| case '"': |
| case '\\': |
| case ',': |
| case '+': |
| case '<': |
| case '>': |
| case ';': |
| buf.append('\\'); |
| |
| default: |
| buf.append(ch); |
| } |
| } |
| |
| //remove trailing spaces |
| for (bufLength = buf.length() - 1; bufLength > -1 |
| && buf.charAt(bufLength) == ' '; bufLength--) { |
| } |
| buf.setLength(bufLength + 1); |
| |
| return buf.toString(); |
| } |
| } |