001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander V. Esin
020: * @version $Revision$
021: */package org.apache.harmony.security.x501;
022:
023: import java.io.IOException;
024:
025: import org.apache.harmony.security.asn1.ASN1StringType;
026: import org.apache.harmony.security.asn1.DerInputStream;
027: import org.apache.harmony.security.x509.Utils;
028:
029: /**
030: * X.501 Attribute Value
031: */
032: public class AttributeValue {
033:
034: public final boolean wasEncoded;
035:
036: public String escapedString;
037:
038: private String hexString;
039:
040: private int tag = -1;
041:
042: public byte[] encoded;
043:
044: public byte[] bytes; //FIXME remove??? bytes to be encoded
045:
046: public boolean hasQE; // raw string contains '"' or '\'
047:
048: public AttributeValue(String parsedString, boolean hasQorE) {
049:
050: wasEncoded = false;
051:
052: this .hasQE = hasQorE;
053:
054: this .rawString = parsedString;
055: this .escapedString = makeEscaped(rawString);
056: }
057:
058: public AttributeValue(String hexString, byte[] encoded) {
059:
060: wasEncoded = true;
061:
062: this .hexString = hexString;
063: this .encoded = encoded;
064:
065: try {
066: DerInputStream in = new DerInputStream(encoded);
067:
068: tag = in.tag;
069:
070: if (DirectoryString.ASN1.checkTag(tag)) {
071: // has string representation
072: this .rawString = (String) DirectoryString.ASN1
073: .decode(in);
074: this .escapedString = makeEscaped(rawString);
075: } else {
076: this .rawString = hexString;
077: this .escapedString = hexString;
078: }
079: } catch (IOException e) {
080: IllegalArgumentException iae = new IllegalArgumentException(); //FIXME message
081: iae.initCause(e);
082: throw iae;
083: }
084: }
085:
086: public String rawString;
087:
088: public AttributeValue(String rawString, byte[] encoded, int tag) {
089:
090: wasEncoded = true;
091:
092: this .encoded = encoded;
093: this .tag = tag;
094:
095: if (rawString == null) {
096: this .rawString = getHexString();
097: this .escapedString = hexString;
098: } else {
099: this .rawString = rawString;
100: this .escapedString = makeEscaped(rawString);
101: }
102: }
103:
104: public int getTag() {
105: if (tag == -1) {
106: if (Utils.isPrintableString(rawString)) {
107: tag = ASN1StringType.PRINTABLESTRING.id;
108: } else {
109: tag = ASN1StringType.UTF8STRING.id;
110: }
111: }
112: return tag;
113: }
114:
115: public String getHexString() {
116: if (hexString == null) {
117:
118: if (!wasEncoded) {
119: //FIXME optimize me: what about reusable OutputStream???
120: if (Utils.isPrintableString(rawString)) {
121: encoded = ASN1StringType.PRINTABLESTRING
122: .encode(rawString);
123: } else {
124: encoded = ASN1StringType.UTF8STRING
125: .encode(rawString);
126: }
127: }
128:
129: StringBuffer buf = new StringBuffer(encoded.length * 2 + 1);
130: buf.append('#');
131:
132: for (int i = 0, c; i < encoded.length; i++) {
133: c = (encoded[i] >> 4) & 0x0F;
134: if (c < 10) {
135: buf.append((char) (c + 48));
136: } else {
137: buf.append((char) (c + 87));
138: }
139:
140: c = encoded[i] & 0x0F;
141: if (c < 10) {
142: buf.append((char) (c + 48));
143: } else {
144: buf.append((char) (c + 87));
145: }
146: }
147: hexString = buf.toString();
148: }
149: return hexString;
150: }
151:
152: public void appendQEString(StringBuffer buf) {
153: buf.append('"');
154: if (hasQE) {
155: char c;
156: for (int i = 0; i < rawString.length(); i++) {
157: c = rawString.charAt(i);
158: if (c == '"' || c == '\\') {
159: buf.append('\\');
160: }
161: buf.append(c);
162: }
163: } else {
164: buf.append(rawString);
165: }
166: buf.append('"');
167: }
168:
169: //
170: // Escapes:
171: // 1) chars ",", "+", """, "\", "<", ">", ";" (RFC 2253)
172: // 2) chars "#", "=" (required by RFC 1779)
173: // 3) a space char at the beginning or end
174: // 4) according to the requirement to be RFC 1779 compatible:
175: // '#' char is escaped in any position
176: //
177: private String makeEscaped(String name) {
178:
179: int length = name.length();
180: if (length == 0) {
181: return name;
182: }
183: StringBuffer buf = new StringBuffer(length * 2);
184:
185: for (int index = 0; index < length; index++) {
186:
187: char ch = name.charAt(index);
188:
189: switch (ch) {
190:
191: case ' ':
192: if (index == 0 || index == (length - 1)) {
193: // escape first or last space
194: buf.append('\\');
195: }
196: buf.append(' ');
197: break;
198:
199: case '"':
200: case '\\':
201: hasQE = true;
202:
203: case ',':
204: case '+':
205: case '<':
206: case '>':
207: case ';':
208: case '#': // required by RFC 1779
209: case '=': // required by RFC 1779
210: buf.append('\\');
211:
212: default:
213: buf.append(ch);
214: }
215: }
216:
217: return buf.toString();
218: }
219:
220: public String makeCanonical() {
221:
222: int length = rawString.length();
223: if (length == 0) {
224: return rawString;
225: }
226: StringBuffer buf = new StringBuffer(length * 2);
227:
228: int index = 0;
229: if (rawString.charAt(0) == '#') {
230: buf.append('\\');
231: buf.append('#');
232: index++;
233: }
234:
235: int bufLength;
236: for (; index < length; index++) {
237:
238: char ch = rawString.charAt(index);
239:
240: switch (ch) {
241:
242: case ' ':
243: bufLength = buf.length();
244: if (bufLength == 0 || buf.charAt(bufLength - 1) == ' ') {
245: break;
246: }
247: buf.append(' ');
248: break;
249:
250: case '"':
251: case '\\':
252: case ',':
253: case '+':
254: case '<':
255: case '>':
256: case ';':
257: buf.append('\\');
258:
259: default:
260: buf.append(ch);
261: }
262: }
263:
264: //remove trailing spaces
265: for (bufLength = buf.length() - 1; bufLength > -1
266: && buf.charAt(bufLength) == ' '; bufLength--) {
267: }
268: buf.setLength(bufLength + 1);
269:
270: return buf.toString();
271: }
272: }
|