58 |
* Constructor. |
* Constructor. |
59 |
* @param utf whether to use UTF-8 for encoding strings |
* @param utf whether to use UTF-8 for encoding strings |
60 |
*/ |
*/ |
61 |
public BEREncoder (boolean utf8) |
public BEREncoder(boolean utf8) |
62 |
{ |
{ |
63 |
this (utf8, 1024); |
this(utf8, 1024); |
64 |
} |
} |
65 |
|
|
66 |
/** |
/** |
68 |
* @param utf whether to use UTF-8 for encoding strings |
* @param utf whether to use UTF-8 for encoding strings |
69 |
* @param initialSize the initial buffer size |
* @param initialSize the initial buffer size |
70 |
*/ |
*/ |
71 |
public BEREncoder (boolean utf8, int initialSize) |
public BEREncoder(boolean utf8, int initialSize) |
72 |
{ |
{ |
73 |
this.utf8 = utf8; |
this.utf8 = utf8; |
74 |
buffer = new byte[initialSize]; |
buffer = new byte[initialSize]; |
80 |
/** |
/** |
81 |
* Reset this encoder for reuse. |
* Reset this encoder for reuse. |
82 |
*/ |
*/ |
83 |
public void reset () |
public void reset() |
84 |
{ |
{ |
85 |
for (int i = 0; i < offset; i++) |
for (int i = 0; i < offset; i++) |
86 |
{ |
{ |
97 |
/** |
/** |
98 |
* Returns the current size of the encoded data. |
* Returns the current size of the encoded data. |
99 |
*/ |
*/ |
100 |
public int size () |
public int size() |
101 |
{ |
{ |
102 |
return offset; |
return offset; |
103 |
} |
} |
105 |
/** |
/** |
106 |
* Returns the encoded data. |
* Returns the encoded data. |
107 |
*/ |
*/ |
108 |
public byte[] toByteArray () |
public byte[] toByteArray() |
109 |
{ |
{ |
110 |
byte[] ret = new byte[offset]; |
byte[] ret = new byte[offset]; |
111 |
System.arraycopy (buffer, 0, ret, 0, offset); |
System.arraycopy(buffer, 0, ret, 0, offset); |
112 |
return ret; |
return ret; |
113 |
} |
} |
114 |
|
|
118 |
* Appends a boolean value. |
* Appends a boolean value. |
119 |
* @param value the value |
* @param value the value |
120 |
*/ |
*/ |
121 |
public void append (boolean value) |
public void append(boolean value) |
122 |
{ |
{ |
123 |
append (value, BERConstants.BOOLEAN); |
append(value, BERConstants.BOOLEAN); |
124 |
} |
} |
125 |
|
|
126 |
/** |
/** |
128 |
* @param value the value |
* @param value the value |
129 |
* @param code the type code |
* @param code the type code |
130 |
*/ |
*/ |
131 |
public void append (boolean value, int code) |
public void append(boolean value, int code) |
132 |
{ |
{ |
133 |
allocate (3); |
allocate(3); |
134 |
buffer[offset++] = (byte) code; |
buffer[offset++] = (byte) code; |
135 |
buffer[offset++] = (byte) 1; /* length */ |
buffer[offset++] = (byte) 1; /* length */ |
136 |
buffer[offset++] = value ? (byte) 0xff : (byte) 0; |
buffer[offset++] = value ? (byte) 0xff :(byte) 0; |
137 |
} |
} |
138 |
|
|
139 |
// -- 8.3 Encoding of an integer value -- |
// -- 8.3 Encoding of an integer value -- |
142 |
* Appends an integer value. |
* Appends an integer value. |
143 |
* @param value the value |
* @param value the value |
144 |
*/ |
*/ |
145 |
public void append (int value) |
public void append(int value) |
146 |
{ |
{ |
147 |
append (value, BERConstants.INTEGER); |
append(value, BERConstants.INTEGER); |
148 |
} |
} |
149 |
|
|
150 |
/** |
/** |
152 |
* @param value the value |
* @param value the value |
153 |
* @param code the type code |
* @param code the type code |
154 |
*/ |
*/ |
155 |
public void append (int value, int code) |
public void append(int value, int code) |
156 |
{ |
{ |
157 |
final int mask = 0xff800000; |
final int mask = 0xff800000; |
158 |
int len = 4; |
int len = 4; |
161 |
len--; |
len--; |
162 |
value <<= 8; |
value <<= 8; |
163 |
} |
} |
164 |
allocate (len + 2); |
allocate(len + 2); |
165 |
buffer[offset++] = (byte) code; |
buffer[offset++] = (byte) code; |
166 |
buffer[offset++] = (byte) len; |
buffer[offset++] = (byte) len; |
167 |
for (; len > 0; len--) |
for (; len > 0; len--) |
180 |
* Appends an octetstring value. |
* Appends an octetstring value. |
181 |
* @param bytes the value |
* @param bytes the value |
182 |
*/ |
*/ |
183 |
public void append (byte[] bytes) |
public void append(byte[] bytes) |
184 |
throws BERException |
throws BERException |
185 |
{ |
{ |
186 |
append (bytes, BERConstants.OCTET_STRING); |
append(bytes, BERConstants.OCTET_STRING); |
187 |
} |
} |
188 |
|
|
189 |
/** |
/** |
193 |
* @param bytes the value |
* @param bytes the value |
194 |
* @param code the type code |
* @param code the type code |
195 |
*/ |
*/ |
196 |
public void append (byte[] bytes, int code) |
public void append(byte[] bytes, int code) |
197 |
throws BERException |
throws BERException |
198 |
{ |
{ |
199 |
int len = (bytes == null) ? 0 : bytes.length; |
int len = (bytes == null) ? 0 : bytes.length; |
200 |
append (bytes, 0, len, code); |
append(bytes, 0, len, code); |
201 |
} |
} |
202 |
|
|
203 |
void append (byte[] bytes, int off, int len, int code) |
void append(byte[] bytes, int off, int len, int code) |
204 |
throws BERException |
throws BERException |
205 |
{ |
{ |
206 |
allocate (len + 5); |
allocate(len + 5); |
207 |
buffer[offset++] = (byte) code; |
buffer[offset++] = (byte) code; |
208 |
appendLength (len); |
appendLength(len); |
209 |
if (len > 0) |
if (len > 0) |
210 |
{ |
{ |
211 |
System.arraycopy (bytes, off, buffer, offset, len); |
System.arraycopy(bytes, off, buffer, offset, len); |
212 |
offset += len; |
offset += len; |
213 |
} |
} |
214 |
} |
} |
217 |
* Appends a string value. |
* Appends a string value. |
218 |
* @param value the value |
* @param value the value |
219 |
*/ |
*/ |
220 |
public void append (String value) |
public void append(String value) |
221 |
throws BERException |
throws BERException |
222 |
{ |
{ |
223 |
append (value, BERConstants.UTF8_STRING); |
append(value, BERConstants.UTF8_STRING); |
224 |
} |
} |
225 |
|
|
226 |
/** |
/** |
228 |
* @param value the value |
* @param value the value |
229 |
* @param code the type code |
* @param code the type code |
230 |
*/ |
*/ |
231 |
public void append (String value, int code) |
public void append(String value, int code) |
232 |
throws BERException |
throws BERException |
233 |
{ |
{ |
234 |
byte[] bytes = null; |
byte[] bytes = null; |
241 |
String encoding = utf8 ? "UTF-8" : "ISO-8859-1"; |
String encoding = utf8 ? "UTF-8" : "ISO-8859-1"; |
242 |
try |
try |
243 |
{ |
{ |
244 |
bytes = value.getBytes (encoding); |
bytes = value.getBytes(encoding); |
245 |
} |
} |
246 |
catch (UnsupportedEncodingException e) |
catch (UnsupportedEncodingException e) |
247 |
{ |
{ |
248 |
throw new BERException ("JVM does not support " + encoding); |
throw new BERException("JVM does not support " + encoding); |
249 |
} |
} |
250 |
} |
} |
251 |
int len = bytes.length; |
int len = bytes.length; |
252 |
allocate (len + 5); |
allocate(len + 5); |
253 |
buffer[offset++] = (byte) code; |
buffer[offset++] = (byte) code; |
254 |
appendLength (len); |
appendLength(len); |
255 |
System.arraycopy (bytes, 0, buffer, offset, len); |
System.arraycopy(bytes, 0, buffer, offset, len); |
256 |
offset += len; |
offset += len; |
257 |
} |
} |
258 |
|
|
261 |
/** |
/** |
262 |
* Appends a BER NULL value. |
* Appends a BER NULL value. |
263 |
*/ |
*/ |
264 |
public void appendNull () |
public void appendNull() |
265 |
{ |
{ |
266 |
allocate (2); |
allocate(2); |
267 |
buffer[offset++] = BERConstants.NULL; |
buffer[offset++] = BERConstants.NULL; |
268 |
buffer[offset++] = (byte) 0; /* length */ |
buffer[offset++] = (byte) 0; /* length */ |
269 |
} |
} |
271 |
/** |
/** |
272 |
* Allocate at least len bytes. |
* Allocate at least len bytes. |
273 |
*/ |
*/ |
274 |
private void allocate (int len) |
private void allocate(int len) |
275 |
{ |
{ |
276 |
if (buffer.length - offset < len) |
if (buffer.length - offset < len) |
277 |
{ |
{ |
282 |
} |
} |
283 |
while (size - offset < len); |
while (size - offset < len); |
284 |
byte[] ret = new byte[size]; |
byte[] ret = new byte[size]; |
285 |
System.arraycopy (buffer, 0, ret, 0, offset); |
System.arraycopy(buffer, 0, ret, 0, offset); |
286 |
buffer = ret; |
buffer = ret; |
287 |
} |
} |
288 |
} |
} |
290 |
/** |
/** |
291 |
* Append the specified length for a string. |
* Append the specified length for a string. |
292 |
*/ |
*/ |
293 |
private void appendLength (int len) |
private void appendLength(int len) |
294 |
throws BERException |
throws BERException |
295 |
{ |
{ |
296 |
if (len < 0x80) |
if (len < 0x80) |
305 |
else if (len < 0x10000) |
else if (len < 0x10000) |
306 |
{ |
{ |
307 |
buffer[offset++] = (byte) 0x82; |
buffer[offset++] = (byte) 0x82; |
308 |
buffer[offset++] = (byte) (len >> 0x08); |
buffer[offset++] = (byte)(len >> 0x08); |
309 |
buffer[offset++] = (byte) (len & 0xff); |
buffer[offset++] = (byte)(len & 0xff); |
310 |
} |
} |
311 |
else if (len < 0x1000000) |
else if (len < 0x1000000) |
312 |
{ |
{ |
313 |
buffer[offset++] = (byte) 0x83; |
buffer[offset++] = (byte) 0x83; |
314 |
buffer[offset++] = (byte) (len >> 0x10); |
buffer[offset++] = (byte)(len >> 0x10); |
315 |
buffer[offset++] = (byte) (len >> 0x08); |
buffer[offset++] = (byte)(len >> 0x08); |
316 |
buffer[offset++] = (byte) (len & 0xff); |
buffer[offset++] = (byte)(len & 0xff); |
317 |
} |
} |
318 |
else |
else |
319 |
{ |
{ |
320 |
throw new BERException ("Data too long: " + len); |
throw new BERException("Data too long: " + len); |
321 |
} |
} |
322 |
} |
} |
323 |
|
|
325 |
* Appends an RFC2254 search filter to this encoder. |
* Appends an RFC2254 search filter to this encoder. |
326 |
* @param filter the filter expression |
* @param filter the filter expression |
327 |
*/ |
*/ |
328 |
public void appendFilter (String filter) |
public void appendFilter(String filter) |
329 |
throws BERException |
throws BERException |
330 |
{ |
{ |
331 |
if (filter == null || filter.length () == 0) |
if (filter == null || filter.length() == 0) |
332 |
{ |
{ |
333 |
throw new BERException ("Empty filter expression"); |
throw new BERException("Empty filter expression"); |
334 |
} |
} |
335 |
final byte[] bytes; |
final byte[] bytes; |
336 |
String charset = utf8 ? "UTF-8" : "ISO-8859-1"; |
String charset = utf8 ? "UTF-8" : "ISO-8859-1"; |
337 |
try |
try |
338 |
{ |
{ |
339 |
bytes = filter.getBytes (charset); |
bytes = filter.getBytes(charset); |
340 |
} |
} |
341 |
catch (UnsupportedEncodingException e) |
catch (UnsupportedEncodingException e) |
342 |
{ |
{ |
343 |
throw new BERException ("JVM does not support " + charset); |
throw new BERException("JVM does not support " + charset); |
344 |
} |
} |
345 |
appendFilter (bytes, 0); |
appendFilter(bytes, 0); |
346 |
} |
} |
347 |
|
|
348 |
int appendFilter (final byte[] bytes, int off) |
int appendFilter(final byte[] bytes, int off) |
349 |
throws BERException |
throws BERException |
350 |
{ |
{ |
351 |
int depth = 0; |
int depth = 0; |
352 |
while (off < bytes.length) |
while(off < bytes.length) |
353 |
{ |
{ |
354 |
switch (bytes[off]) |
switch (bytes[off]) |
355 |
{ |
{ |
356 |
case 0x20: // SP |
case 0x20: // SP |
357 |
off++; |
off++; |
358 |
break; /* NOOP */ |
break; /* NOOP */ |
359 |
case 0x28: // ( |
case 0x28: //( |
360 |
depth++; |
depth++; |
361 |
off++; |
off++; |
362 |
break; |
break; |
368 |
} |
} |
369 |
break; |
break; |
370 |
case 0x26: // & |
case 0x26: // & |
371 |
off = appendFilterList (bytes, off + 1, |
off = appendFilterList(bytes, off + 1, |
372 |
BERConstants.FILTER_AND); |
BERConstants.FILTER_AND); |
373 |
break; |
break; |
374 |
case 0x2c: // | |
case 0x2c: // | |
375 |
off = appendFilterList (bytes, off + 1, |
off = appendFilterList(bytes, off + 1, |
376 |
BERConstants.FILTER_OR); |
BERConstants.FILTER_OR); |
377 |
break; |
break; |
378 |
case 0x21: // ! |
case 0x21: // ! |
379 |
off = appendFilterList (bytes, off + 1, |
off = appendFilterList(bytes, off + 1, |
380 |
BERConstants.FILTER_NOT); |
BERConstants.FILTER_NOT); |
381 |
break; |
break; |
382 |
default: |
default: |
383 |
off = appendFilterItem (bytes, off); |
off = appendFilterItem(bytes, off); |
384 |
} |
} |
385 |
} |
} |
386 |
if (depth != 0) |
if (depth != 0) |
387 |
{ |
{ |
388 |
//System.err.println("depth="+depth+", off="+off); |
//System.err.println("depth="+depth+", off="+off); |
389 |
throw new BERException ("Unbalanced parentheses"); |
throw new BERException("Unbalanced parentheses"); |
390 |
} |
} |
391 |
return off; |
return off; |
392 |
} |
} |
393 |
|
|
394 |
int appendFilterList (final byte[] bytes, int off, int code) |
int appendFilterList(final byte[] bytes, int off, int code) |
395 |
throws BERException |
throws BERException |
396 |
{ |
{ |
397 |
BEREncoder sequence = new BEREncoder (utf8); |
BEREncoder sequence = new BEREncoder(utf8); |
398 |
while (off < bytes.length && bytes[off] == '(') |
while (off < bytes.length && bytes[off] == '(') |
399 |
{ |
{ |
400 |
off = sequence.appendFilter (bytes, off); |
off = sequence.appendFilter(bytes, off); |
401 |
} |
} |
402 |
append (sequence.toByteArray (), code); |
append(sequence.toByteArray(), code); |
403 |
return off; |
return off; |
404 |
} |
} |
405 |
|
|
406 |
int appendFilterItem (final byte[] bytes, int off) |
int appendFilterItem(final byte[] bytes, int off) |
407 |
throws BERException |
throws BERException |
408 |
{ |
{ |
409 |
int ei = indexOf (bytes, (byte) 0x3d, off); // = |
int ei = indexOf(bytes,(byte) 0x3d, off); // = |
410 |
if (ei == -1) |
if (ei == -1) |
411 |
{ |
{ |
412 |
throw new BERException ("Missing '='"); |
throw new BERException("Missing '='"); |
413 |
} |
} |
414 |
int end = ei; |
int end = ei; |
415 |
int code; |
int code; |
416 |
BEREncoder item = new BEREncoder (utf8); |
BEREncoder item = new BEREncoder(utf8); |
417 |
switch (bytes[ei - 1]) |
switch (bytes[ei - 1]) |
418 |
{ |
{ |
419 |
case 0x7e: // ~ approx |
case 0x7e: // ~ approx |
430 |
break; |
break; |
431 |
case 0x3a: // : ext |
case 0x3a: // : ext |
432 |
code = BERConstants.FILTER_EXTENSIBLE; |
code = BERConstants.FILTER_EXTENSIBLE; |
433 |
// TODO return appendFilterExtensibleMatch (bytes, off, ei); |
// TODO return appendFilterExtensibleMatch(bytes, off, ei); |
434 |
break; |
break; |
435 |
default: // equal/substring |
default: // equal/substring |
436 |
int si = indexOf (bytes, (byte) 0x2a, ei + 1); // * |
int si = indexOf(bytes,(byte) 0x2a, ei + 1); // * |
437 |
if (si == -1) |
if (si == -1) |
438 |
{ |
{ |
439 |
code = BERConstants.FILTER_EQUAL; |
code = BERConstants.FILTER_EQUAL; |
448 |
else |
else |
449 |
{ |
{ |
450 |
// substring |
// substring |
451 |
BEREncoder substring = new BEREncoder (utf8); |
BEREncoder substring = new BEREncoder(utf8); |
452 |
substring.append (bytes, off, end, BERConstants.OCTET_STRING); |
substring.append(bytes, off, end, BERConstants.OCTET_STRING); |
453 |
end = indexOf (bytes, (byte) 0x29, ei + 1); // ) |
end = indexOf(bytes,(byte) 0x29, ei + 1); // ) |
454 |
if (end == -1) |
if (end == -1) |
455 |
{ |
{ |
456 |
throw new BERException ("No terminating ')'"); |
throw new BERException("No terminating ')'"); |
457 |
} |
} |
458 |
BEREncoder value = new BEREncoder (utf8); |
BEREncoder value = new BEREncoder(utf8); |
459 |
value.append (unencode (bytes, ei + 1, end)); |
value.append(unencode(bytes, ei + 1, end)); |
460 |
substring.append (value.toByteArray (), 48); |
substring.append(value.toByteArray(), 48); |
461 |
append (substring.toByteArray (), |
append(substring.toByteArray(), |
462 |
BERConstants.FILTER_SUBSTRING); |
BERConstants.FILTER_SUBSTRING); |
463 |
off = end; |
off = end; |
464 |
return off; |
return off; |
465 |
} |
} |
466 |
} |
} |
467 |
|
|
468 |
} |
} |
469 |
item.append (bytes, off, (end - off), BERConstants.OCTET_STRING); |
item.append(bytes, off,(end - off), BERConstants.OCTET_STRING); |
470 |
end = indexOf (bytes, (byte) 0x29, ei + 1); // ) |
end = indexOf(bytes,(byte) 0x29, ei + 1); // ) |
471 |
if (end == -1) |
if (end == -1) |
472 |
{ |
{ |
473 |
throw new BERException ("No terminating ')'"); |
throw new BERException("No terminating ')'"); |
474 |
} |
} |
475 |
if (code != BERConstants.FILTER_PRESENT) |
if (code != BERConstants.FILTER_PRESENT) |
476 |
{ |
{ |
477 |
item.append (unencode (bytes, ei + 1, end)); |
item.append(unencode(bytes, ei + 1, end)); |
478 |
} |
} |
479 |
append (item.toByteArray (), code); |
append(item.toByteArray(), code); |
480 |
off = end; |
off = end; |
481 |
return off; |
return off; |
482 |
} |
} |
486 |
* octet-string, starting at the given index. The filterlist terminator |
* octet-string, starting at the given index. The filterlist terminator |
487 |
* ')' stops the search. |
* ')' stops the search. |
488 |
*/ |
*/ |
489 |
static int indexOf (final byte[] bytes, byte c, int off) |
static int indexOf(final byte[] bytes, byte c, int off) |
490 |
{ |
{ |
491 |
for (int i = off; i < bytes.length; i++) |
for (int i = off; i < bytes.length; i++) |
492 |
{ |
{ |
508 |
* This routine converts each character encoded as "\xx" where xx is the ASCII |
* This routine converts each character encoded as "\xx" where xx is the ASCII |
509 |
* character code to a single character. |
* character code to a single character. |
510 |
*/ |
*/ |
511 |
static byte[] unencode (final byte[] bytes, int off, int end) |
static byte[] unencode(final byte[] bytes, int off, int end) |
512 |
throws BERException |
throws BERException |
513 |
{ |
{ |
514 |
byte[] buf = new byte[end - off]; |
byte[] buf = new byte[end - off]; |
515 |
int pos = 0; |
int pos = 0; |
516 |
int bsi = indexOf (bytes, (byte) 0x5c, off); // \ |
int bsi = indexOf(bytes,(byte) 0x5c, off); // \ |
517 |
while (bsi != -1) |
while(bsi != -1) |
518 |
{ |
{ |
519 |
if (bsi + 3 > end) |
if (bsi + 3 > end) |
520 |
{ |
{ |
521 |
throw new BERException ("Illegal filter value encoding"); |
throw new BERException("Illegal filter value encoding"); |
522 |
} |
} |
523 |
int l = bsi - off; |
int l = bsi - off; |
524 |
System.arraycopy (bytes, off, buf, pos, l); |
System.arraycopy(bytes, off, buf, pos, l); |
525 |
pos += l; |
pos += l; |
526 |
int c = Character.digit ((char) bytes[bsi + 2], 0x10); |
int c = Character.digit((char) bytes[bsi + 2], 0x10); |
527 |
c += Character.digit ((char) bytes[bsi + 1], 0x10) * 0x10; |
c += Character.digit((char) bytes[bsi + 1], 0x10) * 0x10; |
528 |
buf[pos++] = (byte) c; |
buf[pos++] = (byte) c; |
529 |
off += l + 3; |
off += l + 3; |
530 |
bsi = indexOf (bytes, (byte) 0x5c, off); // \ |
bsi = indexOf(bytes,(byte) 0x5c, off); // \ |
531 |
} |
} |
532 |
int l = end - off; |
int l = end - off; |
533 |
System.arraycopy (bytes, off, buf, pos, l); |
System.arraycopy(bytes, off, buf, pos, l); |
534 |
pos += l; |
pos += l; |
535 |
off += l; |
off += l; |
536 |
if (pos != buf.length) |
if (pos != buf.length) |
537 |
{ |
{ |
538 |
byte[] swap = new byte[pos]; |
byte[] swap = new byte[pos]; |
539 |
System.arraycopy (buf, 0, swap, 0, pos); |
System.arraycopy(buf, 0, swap, 0, pos); |
540 |
buf = swap; |
buf = swap; |
541 |
} |
} |
542 |
return buf; |
return buf; |
543 |
} |
} |
544 |
|
|
545 |
} |
} |
546 |
|
|