Use local copy of latest bndlib code for pre-release testing purposes

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1347815 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/BER.java b/bundleplugin/src/main/java/aQute/libg/asn1/BER.java
new file mode 100644
index 0000000..4416dd4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/BER.java
@@ -0,0 +1,472 @@
+package aQute.libg.asn1;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+public class BER implements Types {
+    DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss\\Z");
+    
+    final DataInputStream xin;
+    long                  position;
+
+    public BER(InputStream in) {
+        this.xin = new DataInputStream(in);
+    }
+
+    public void dump(PrintStream out) throws Exception {
+        int type = readByte();
+        long length = readLength();
+        if (type == -1 || length == -1)
+            throw new EOFException("Empty file");
+        dump(out, type, length, "");
+    }
+
+    void dump(PrintStream out, int type, long length, String indent)
+            throws Exception {
+        int clss = type >> 6;
+        int nmbr = type & 0x1F;
+        boolean cnst = (type & 0x20) != 0;
+
+        String tag = "[" + nmbr + "]";
+        if (clss == 0)
+            tag = TAGS[nmbr];
+
+        if (cnst) {
+            System.err.printf("%5d %s %s %s%n", length, indent, CLASSES[clss],
+                    tag);
+            while (length > 1) {
+                long atStart = getPosition();
+                int t2 = read();
+                long l2 = readLength();
+                dump(out, t2, l2, indent + "  ");
+                length -= getPosition() - atStart;
+            }
+        } else {
+            assert length < Integer.MAX_VALUE;
+            assert length >= 0;
+            byte[] data = new byte[(int) length];
+            readFully(data);
+            String summary;
+
+            switch (nmbr) {
+            case BOOLEAN:
+                assert length == 1;
+                summary = data[0] != 0 ? "true" : "false";
+                break;
+
+            case INTEGER:
+                long n = toLong(data);
+                summary = n + "";
+                break;
+
+            case UTF8_STRING:
+            case IA5STRING:
+            case VISIBLE_STRING:
+            case UNIVERSAL_STRING:
+            case PRINTABLE_STRING:
+            case UTCTIME:
+                summary = new String(data, "UTF-8");
+                break;
+
+            case OBJECT_IDENTIFIER:
+                summary = readOID(data);
+                break;
+
+            case GENERALIZED_TIME:
+            case GRAPHIC_STRING:
+            case GENERAL_STRING:
+            case CHARACTER_STRING:
+
+            case REAL:
+            case EOC:
+            case BIT_STRING:
+            case OCTET_STRING:
+            case NULL:
+            case OBJECT_DESCRIPTOR:
+            case EXTERNAL:
+            case ENUMERATED:
+            case EMBEDDED_PDV:
+            case RELATIVE_OID:
+            case NUMERIC_STRING:
+            case T61_STRING:
+            case VIDEOTEX_STRING:
+            case BMP_STRING:
+            default:
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < 10 && i < data.length; i++) {
+                    sb.append(Integer.toHexString(data[i]));
+                }
+                if (data.length > 10) {
+                    sb.append("...");
+                }
+                summary = sb.toString();
+                break;
+            }
+            out.printf("%5d %s %s %s %s\n", length, indent, CLASSES[clss], tag,
+                    summary);
+        }
+    }
+
+    long toLong(byte[] data) {
+        if (data[0] < 0) {
+            for (int i = 0; i < data.length; i++)
+                data[i] = (byte) (0xFF ^ data[i]);
+
+            return -(toLong(data) + 1);
+        }
+        long n = 0;
+        for (int i = 0; i < data.length; i++) {
+            n = n * 256 + data[i];
+        }
+        return n;
+    }
+
+    /**
+     * 8.1.3.3 For the definite form, the length octets shall consist of one or
+     * more octets, and shall represent the number of octets in the contents
+     * octets using either the short form (see 8.1.3.4) or the long form (see
+     * 8.1.3.5) as a sender's option. NOTE – The short form can only be used if
+     * the number of octets in the contents octets is less than or equal to 127.
+     * 8.1.3.4 In the short form, the length octets shall consist of a single
+     * octet in which bit 8 is zero and bits 7 to 1 encode the number of octets
+     * in the contents octets (which may be zero), as an unsigned binary integer
+     * with bit 7 as the most significant bit. EXAMPLE L = 38 can be encoded as
+     * 001001102 8.1.3.5 In the long form, the length octets shall consist of an
+     * initial octet and one or more subsequent octets. The initial octet shall
+     * be encoded as follows: a) bit 8 shall be one; b) bits 7 to 1 shall encode
+     * the number of subsequent octets in the length octets, as an unsigned
+     * binary integer with bit 7 as the most significant bit; c) the value
+     * 111111112 shall not be used. ISO/IEC 8825-1:2003 (E) NOTE 1 – This
+     * restriction is introduced for possible future extension. Bits 8 to 1 of
+     * the first subsequent octet, followed by bits 8 to 1 of the second
+     * subsequent octet, followed in turn by bits 8 to 1 of each further octet
+     * up to and including the last subsequent octet, shall be the encoding of
+     * an unsigned binary integer equal to the number of octets in the contents
+     * octets, with bit 8 of the first subsequent octet as the most significant
+     * bit. EXAMPLE L = 201 can be encoded as: 100000012 110010012 NOTE 2 – In
+     * the long form, it is a sender's option whether to use more length octets
+     * than the minimum necessary. 8.1.3.6 For the indefinite form, the length
+     * octets indicate that the contents octets are terminated by
+     * end-of-contents octets (see 8.1.5), and shall consist of a single octet.
+     * 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to 1
+     * set to zero. 8.1.3.6.2 If this form of length is used, then
+     * end-of-contents octets (see 8.1.5) shall be present in the encoding
+     * following the contents octets. 8.1.4 Contents octets The contents octets
+     * shall consist of zero, one or more octets, and shall encode the data
+     * value as specified in subsequent clauses. NOTE – The contents octets
+     * depend on the type of the data value; subsequent clauses follow the same
+     * sequence as the definition of types in ASN.1. 8.1.5 End-of-contents
+     * octets The end-of-contents octets shall be present if the length is
+     * encoded as specified in 8.1.3.6, otherwise they shall not be present. The
+     * end-of-contents octets shall consist of two zero octets. NOTE – The
+     * end-of-contents octets can be considered as the encoding of a value whose
+     * tag is universal class, whose form is primitive, whose number of the tag
+     * is zero, and whose contents are absent, thus:
+     * 
+     * End-of-contents Length Contents 0016 0016 Absent
+     * 
+     * @return
+     */
+    private long readLength() throws IOException {
+        long n = readByte();
+        if (n > 0) {
+            // short form
+            return n;
+        }
+		// long form
+		int count = (int) (n & 0x7F);
+		if (count == 0) {
+		    // indefinite form
+		    return 0;
+		}
+		n = 0;
+		while (count-- > 0) {
+		    n = n * 256 + read();
+		}
+		return n;
+    }
+
+    private int readByte() throws IOException {
+        position++;
+        return xin.readByte();
+    }
+
+    private void readFully(byte[] data) throws IOException {
+        position += data.length;
+        xin.readFully(data);
+    }
+
+    private long getPosition() {
+        return position;
+    }
+
+    private int read() throws IOException {
+        position++;
+        return xin.read();
+    }
+
+    String readOID(byte[] data) {
+        StringBuilder sb = new StringBuilder();
+        sb.append((0xFF & data[0]) / 40);
+        sb.append(".");
+        sb.append((0xFF & data[0]) % 40);
+
+        int i = 0;
+        while (++i < data.length) {
+            int n = 0;
+            while (data[i] < 0) {
+                n = n * 128 + (0x7F & data[i]);
+                i++;
+            }
+            n = n * 128 + data[i];
+            sb.append(".");
+            sb.append(n);
+        }
+
+        return sb.toString();
+    }
+
+    int getPayloadLength(PDU pdu) throws Exception {
+        switch (pdu.getTag() & 0x1F) {
+        case EOC:
+            return 1;
+
+        case BOOLEAN:
+            return 1;
+
+        case INTEGER:
+            return size(pdu.getInt());
+
+        case UTF8_STRING:
+            String s = pdu.getString();
+            byte[] encoded = s.getBytes("UTF-8");
+            return encoded.length;
+
+        case IA5STRING:
+        case VISIBLE_STRING:
+        case UNIVERSAL_STRING:
+        case PRINTABLE_STRING:
+        case GENERALIZED_TIME:
+        case GRAPHIC_STRING:
+        case GENERAL_STRING:
+        case CHARACTER_STRING:
+        case UTCTIME:
+        case NUMERIC_STRING: {
+            String str = pdu.getString();
+            encoded = str.getBytes("ASCII");
+            return encoded.length;
+        }
+
+        case OBJECT_IDENTIFIER:
+        case REAL:
+        case BIT_STRING:
+            return pdu.getBytes().length;
+
+        case OCTET_STRING:
+        case NULL:
+        case OBJECT_DESCRIPTOR:
+        case EXTERNAL:
+        case ENUMERATED:
+        case EMBEDDED_PDV:
+        case RELATIVE_OID:
+        case T61_STRING:
+        case VIDEOTEX_STRING:
+        case BMP_STRING:
+            return pdu.getBytes().length;
+
+        default:
+            throw new IllegalArgumentException("Invalid type: " + pdu);
+        }
+    }
+
+    int size(long value) {
+        if (value < 128)
+            return 1;
+
+        if (value <= 0xFF)
+            return 2;
+
+        if (value <= 0xFFFF)
+            return 3;
+
+        if (value <= 0xFFFFFF)
+            return 4;
+
+        if (value <= 0xFFFFFFFF)
+            return 5;
+
+        if (value <= 0xFFFFFFFFFFL)
+            return 6;
+
+        if (value <= 0xFFFFFFFFFFFFL)
+            return 7;
+
+        if (value <= 0xFFFFFFFFFFFFFFL)
+            return 8;
+
+        if (value <= 0xFFFFFFFFFFFFFFFFL)
+            return 9;
+
+        throw new IllegalArgumentException("length too long");
+    }
+
+    public void write(OutputStream out, PDU pdu) throws Exception {
+        byte id = 0;
+
+        switch (pdu.getClss()) {
+        case UNIVERSAL:
+            id |= 0;
+            break;
+        case APPLICATION:
+            id |= 0x40;
+            break;
+        case CONTEXT:
+            id |= 0x80;
+            break;
+        case PRIVATE:
+            id |= 0xC0;
+            break;
+        }
+
+        if (pdu.isConstructed())
+            id |= 0x20;
+
+        int tag = pdu.getTag();
+        if (tag >= 0 && tag < 31) {
+            id |= tag;
+        } else {
+            throw new UnsupportedOperationException("Cant do tags > 30");
+        }
+
+        out.write(id);
+
+        int length = getPayloadLength(pdu);
+        int size = size(length);
+        if (size == 1) {
+            out.write(length);
+        } else {
+            out.write(size);
+            while (--size >= 0) {
+                byte data = (byte) ((length >> (size * 8)) & 0xFF);
+                out.write(data);
+            }
+        }
+        writePayload(out, pdu);
+    }
+
+    void writePayload(OutputStream out, PDU pdu) throws Exception {
+        switch (pdu.getTag()) {
+        case EOC:
+            out.write(0);
+            break;
+
+        case BOOLEAN:
+            if (pdu.getBoolean())
+                out.write(-1);
+            else
+                out.write(0);
+            break;
+
+        case ENUMERATED:
+        case INTEGER: {
+            long value = pdu.getInt();
+            int size = size(value);
+            for (int i = size; i >= 0; i--) {
+                byte b = (byte) ((value >> (i * 8)) & 0xFF);
+                out.write(b);
+            }
+        }
+
+        case BIT_STRING: {
+            byte bytes[] = pdu.getBytes();
+            int unused = bytes[0];
+            assert unused <= 7;
+            int[] mask = { 0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1 };
+            bytes[bytes.length - 1] &= (byte) mask[unused];
+            out.write(bytes);
+            break;
+        }
+
+        case RELATIVE_OID:
+        case OBJECT_IDENTIFIER: {
+            int[] oid = pdu.getOID();
+            assert oid.length > 2;
+            assert oid[0] < 4;
+            assert oid[1] < 40;
+            byte top = (byte) (oid[0] * 40 + oid[1]);
+            out.write(top);
+            for (int i = 2; i < oid.length; i++) {
+                putOid(out,oid[i]);
+            }
+            break;
+        }
+
+        case OCTET_STRING: {
+            byte bytes[] = pdu.getBytes();
+            out.write(bytes);
+            break;
+        }
+
+        case NULL:
+            break;
+
+        case BMP_STRING:
+        case GRAPHIC_STRING:
+        case VISIBLE_STRING:
+        case GENERAL_STRING:
+        case UNIVERSAL_STRING:
+        case CHARACTER_STRING:
+        case NUMERIC_STRING:
+        case PRINTABLE_STRING:
+        case VIDEOTEX_STRING:
+        case T61_STRING:
+        case REAL:
+        case EMBEDDED_PDV:
+        case EXTERNAL:
+            throw new UnsupportedEncodingException("dont know real, embedded PDV or external");
+            
+        case UTF8_STRING: {
+            String s = pdu.getString();
+            byte [] data = s.getBytes("UTF-8");
+            out.write(data);
+            break;
+        }
+        
+        case OBJECT_DESCRIPTOR:
+        case IA5STRING:
+            String s = pdu.getString();
+            byte [] data = s.getBytes("ASCII");
+            out.write(data);
+            break;
+            
+            
+        case SEQUENCE:
+        case SET: {
+            PDU pdus[] = pdu.getChildren();
+            for ( PDU p : pdus ) {
+                write(out, p);
+            }
+        }
+            
+        
+        case UTCTIME:
+        case GENERALIZED_TIME:
+            Date date = pdu.getDate();
+            String ss= df.format(date);
+            byte d[] = ss.getBytes("ASCII");
+            out.write(d);
+            break;
+            
+        }
+    }
+    
+
+    private void putOid(OutputStream out, int i) throws IOException {
+        if (i > 127) {
+            putOid(out, i >> 7);
+            out.write(0x80 + (i & 0x7F));
+        } else
+            out.write(i & 0x7F);
+    }
+}