From: Alexandre Rossi <alexandre.rossi@gmail.com>
Description: Embed private jdk APIs
 Davmail uses private JDK APIs for ASN1 encoding and decoding related to
 LDAP support. This make the same davmail jar impossible to run with both
 openJDK 11 and an older openJDK: openJDK 11 needs --add-exports which is
 incompatible with producing Jaba bytecode compatible with an older openJDK.
 Embedding is the best solution as of now. There is no ASN1 Java library
 available in Debian.
Forwarded: https://sourceforge.net/p/davmail/feature-requests/114/

Index: davmail.git/src/java/davmail/ldap/Ber.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ davmail.git/src/java/davmail/ldap/Ber.java	2019-11-18 22:18:15.458957359 +0100
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * This class was stolen as is from com.sun.jndi.ldap in order to prevent
+ * compilation against private jdk APIs.
+ */
+
+package davmail.ldap;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+
+/**
+  * Base class that defines common fields, constants, and debug method.
+  *
+  * @author Jagane Sundar
+  */
+public abstract class Ber {
+
+    protected byte buf[];
+    protected int offset;
+    protected int bufsize;
+
+    protected Ber() {
+    }
+
+    public static void dumpBER(OutputStream outStream, String tag, byte[] bytes,
+        int from, int to) {
+
+        try {
+            outStream.write('\n');
+            outStream.write(tag.getBytes("UTF8"));
+
+            new HexDumpEncoder().encodeBuffer(
+                new ByteArrayInputStream(bytes, from, to),
+                outStream);
+
+            outStream.write('\n');
+        } catch (IOException e) {
+            try {
+                outStream.write(
+                    "Ber.dumpBER(): error encountered\n".getBytes("UTF8"));
+            } catch (IOException e2) {
+                // ignore
+            }
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    //
+    // some ASN defines
+    //
+    ////////////////////////////////////////////////////////////////////////////
+
+    public static final int ASN_BOOLEAN         = 0x01;
+    public static final int ASN_INTEGER         = 0x02;
+    public static final int ASN_BIT_STRING      = 0x03;
+    public static final int ASN_SIMPLE_STRING   = 0x04;
+    public static final int ASN_OCTET_STR       = 0x04;
+    public static final int ASN_NULL            = 0x05;
+    public static final int ASN_OBJECT_ID       = 0x06;
+    public static final int ASN_SEQUENCE        = 0x10;
+    public static final int ASN_SET             = 0x11;
+
+
+    public static final int ASN_PRIMITIVE       = 0x00;
+    public static final int ASN_UNIVERSAL       = 0x00;
+    public static final int ASN_CONSTRUCTOR     = 0x20;
+    public static final int ASN_APPLICATION     = 0x40;
+    public static final int ASN_CONTEXT         = 0x80;
+    public static final int ASN_PRIVATE         = 0xC0;
+
+    public static final int ASN_ENUMERATED      = 0x0a;
+
+    final static class EncodeException extends IOException {
+        private static final long serialVersionUID = -5247359637775781768L;
+        EncodeException(String msg) {
+            super(msg);
+        }
+    }
+
+    final static class DecodeException extends IOException {
+        private static final long serialVersionUID = 8735036969244425583L;
+        DecodeException(String msg) {
+            super(msg);
+        }
+    }
+}
Index: davmail.git/src/java/davmail/ldap/BerDecoder.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ davmail.git/src/java/davmail/ldap/BerDecoder.java	2019-11-18 22:18:15.458957359 +0100
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * This class was stolen as is from com.sun.jndi.ldap in order to prevent
+ * compilation against private jdk APIs.
+ */
+
+package davmail.ldap;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+  * A BER decoder. Contains methods to parse a BER buffer.
+  *
+  * @author Jagane Sundar
+  * @author Vincent Ryan
+  */
+public final class BerDecoder extends Ber {
+
+    private int origOffset;  // The start point in buf to decode
+
+    /**
+     * Creates a BER decoder that reads bytes from the specified buffer.
+     */
+    public BerDecoder(byte buf[], int offset, int bufsize) {
+
+        this.buf = buf;         // shared buffer, be careful to use this class
+        this.bufsize = bufsize;
+        this.origOffset = offset;
+
+        reset();
+    }
+
+    /**
+     * Resets this decode to start parsing from the initial offset
+     * (ie., same state as after calling the constructor).
+     */
+    public void reset() {
+        offset = origOffset;
+    }
+
+    /**
+      * Returns the current parse position.
+      * It points to the byte that will be parsed next.
+      * Useful for parsing sequences.
+      */
+    public int getParsePosition() {
+        return offset;
+    }
+
+    /**
+      * Parses a possibly variable length field.
+      */
+    public int parseLength() throws DecodeException {
+
+        int lengthbyte = parseByte();
+
+        if ((lengthbyte & 0x80) == 0x80) {
+
+            lengthbyte &= 0x7f;
+
+            if (lengthbyte == 0) {
+                throw new DecodeException(
+                    "Indefinite length not supported");
+            }
+
+            if (lengthbyte > 4) {
+                throw new DecodeException("encoding too long");
+            }
+
+            if (bufsize - offset < lengthbyte) {
+                throw new DecodeException("Insufficient data");
+            }
+
+            int retval = 0;
+
+            for( int i = 0; i < lengthbyte; i++) {
+                retval = (retval << 8) + (buf[offset++] & 0xff);
+            }
+            if (retval < 0) {
+              throw new DecodeException("Invalid length bytes");
+            }
+            return retval;
+        } else {
+            return lengthbyte;
+        }
+    }
+
+    /**
+     * Parses the next sequence in this BER buffer.
+     * @param rlen An array for returning size of the sequence in bytes. If null,
+     *          the size is not returned.
+     * @return The sequence's tag.
+     */
+    public int parseSeq(int rlen[]) throws DecodeException {
+
+        int seq = parseByte();
+        int len = parseLength();
+        if (rlen != null) {
+            rlen[0] = len;
+        }
+        return seq;
+    }
+
+    /**
+     * Used to skip bytes. Usually used when trying to recover from parse error.
+     * Don't need to be public right now?
+     * @param i The number of bytes to skip
+     */
+    void seek(int i) throws DecodeException {
+        if (offset + i > bufsize || offset + i < 0) {
+            throw new DecodeException("array index out of bounds");
+        }
+        offset += i;
+    }
+
+    /**
+     * Parses the next byte in this BER buffer.
+     * @return The byte parsed.
+     */
+    public int parseByte() throws DecodeException {
+        if (bufsize - offset < 1) {
+            throw new DecodeException("Insufficient data");
+        }
+        return buf[offset++] & 0xff;
+    }
+
+
+    /**
+     * Returns the next byte in this BER buffer without consuming it.
+     * @return The next byte.
+     */
+    public int peekByte() throws DecodeException {
+        if (bufsize - offset < 1) {
+            throw new DecodeException("Insufficient data");
+        }
+        return buf[offset] & 0xff;
+    }
+
+    /**
+     * Parses an ASN_BOOLEAN tagged integer from this BER buffer.
+     * @return true if the tagged integer is 0; false otherwise.
+     */
+    public boolean parseBoolean() throws DecodeException {
+        return ((parseIntWithTag(ASN_BOOLEAN) == 0x00) ? false : true);
+    }
+
+    /**
+     * Parses an ASN_ENUMERATED tagged integer from this BER buffer.
+     * @return The tag of enumeration.
+     */
+    public int parseEnumeration() throws DecodeException {
+        return parseIntWithTag(ASN_ENUMERATED);
+    }
+
+    /**
+     * Parses an ASN_INTEGER tagged integer from this BER buffer.
+     * @return The value of the integer.
+     */
+    public int parseInt() throws DecodeException {
+        return parseIntWithTag(ASN_INTEGER);
+    }
+
+    /**
+      * Parses an integer that's preceded by a tag.
+      *<blockquote><pre>
+      * BER integer ::= tag length byte {byte}*
+      *</pre></blockquote>
+      */
+    private int parseIntWithTag(int tag) throws DecodeException {
+
+
+        if (parseByte() != tag) {
+            throw new DecodeException("Encountered ASN.1 tag " +
+                Integer.toString(buf[offset - 1] & 0xff) +
+                " (expected tag " + Integer.toString(tag) + ")");
+        }
+
+        int len = parseLength();
+
+        if (len > 4) {
+            throw new DecodeException("INTEGER too long");
+        } else if (len > bufsize - offset) {
+            throw new DecodeException("Insufficient data");
+        }
+
+        byte fb = buf[offset++];
+        int value = 0;
+
+        value = fb & 0x7F;
+        for( int i = 1 /* first byte already read */ ; i < len; i++) {
+            value <<= 8;
+            value |= (buf[offset++] & 0xff);
+        }
+
+        if ((fb & 0x80) == 0x80) {
+            value = -value;
+        }
+
+        return value;
+    }
+
+    /**
+      * Parses a string.
+      */
+    public String parseString(boolean decodeUTF8) throws DecodeException {
+        return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null);
+    }
+
+    /**
+      * Parses a string of a given tag from this BER buffer.
+      *<blockquote><pre>
+      *BER simple string ::= tag length {byte}*
+      *</pre></blockquote>
+      * @param rlen An array for holding the relative parsed offset; if null
+      *  offset not set.
+      * @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise
+      * use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2.
+      * @param tag The tag that precedes the string.
+      * @return The non-null parsed string.
+      */
+    public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])
+        throws DecodeException {
+
+        int st;
+        int origOffset = offset;
+
+        if ((st = parseByte()) != tag) {
+            throw new DecodeException("Encountered ASN.1 tag " +
+                Integer.toString((byte)st) + " (expected tag " + tag + ")");
+        }
+
+        int len = parseLength();
+
+        if (len > bufsize - offset) {
+            throw new DecodeException("Insufficient data");
+        }
+
+        String retstr;
+        if (len == 0) {
+            retstr = "";
+        } else {
+            byte[] buf2 = new byte[len];
+
+            System.arraycopy(buf, offset, buf2, 0, len);
+            if (decodeUTF8) {
+                try {
+                    retstr = new String(buf2, "UTF8");
+                } catch (UnsupportedEncodingException e) {
+                    throw new DecodeException("UTF8 not available on platform");
+                }
+            } else {
+                try {
+                    retstr = new String(buf2, "8859_1");
+                } catch (UnsupportedEncodingException e) {
+                    throw new DecodeException("8859_1 not available on platform");
+                }
+            }
+            offset += len;
+        }
+
+        if (rlen != null) {
+            rlen[0] = offset - origOffset;
+        }
+
+        return retstr;
+    }
+
+    /**
+     * Parses an octet string of a given type(tag) from this BER buffer.
+     * <blockquote><pre>
+     * BER Binary Data of type "tag" ::= tag length {byte}*
+     *</pre></blockquote>
+     *
+     * @param tag The tag to look for.
+     * @param rlen An array for returning the relative parsed position. If null,
+     *          the relative parsed position is not returned.
+     * @return A non-null array containing the octet string.
+     * @throws DecodeException If the next byte in the BER buffer is not
+     * {@code tag}, or if length specified in the BER buffer exceeds the
+     * number of bytes left in the buffer.
+     */
+    public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException {
+
+        int origOffset = offset;
+        int st;
+        if ((st = parseByte()) != tag) {
+
+            throw new DecodeException("Encountered ASN.1 tag " +
+                Integer.toString(st) +
+                " (expected tag " + Integer.toString(tag) + ")");
+        }
+
+        int len = parseLength();
+
+        if (len > bufsize - offset) {
+            throw new DecodeException("Insufficient data");
+        }
+
+        byte retarr[] = new byte[len];
+        if (len > 0) {
+            System.arraycopy(buf, offset, retarr, 0, len);
+            offset += len;
+        }
+
+        if (rlen != null) {
+            rlen[0] = offset - origOffset;
+        }
+
+        return retarr;
+    }
+
+    /**
+     * Returns the number of unparsed bytes in this BER buffer.
+     */
+    public int bytesLeft() {
+        return bufsize - offset;
+    }
+}
Index: davmail.git/src/java/davmail/ldap/BerEncoder.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ davmail.git/src/java/davmail/ldap/BerEncoder.java	2019-11-18 22:18:15.462957423 +0100
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * This class was stolen as is from com.sun.jndi.ldap in order to prevent
+ * compilation against private jdk APIs.
+ */
+
+package davmail.ldap;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+  * A BER encoder.
+  *
+  * @author Jagane Sundar
+  * @author Scott Seligman
+  * @author Vincent Ryan
+  */
+public final class BerEncoder extends Ber {
+
+    private int curSeqIndex;
+    private int seqOffset[];
+    private static final int INITIAL_SEQUENCES = 16;
+    private static final int DEFAULT_BUFSIZE = 1024;
+
+    // When buf is full, expand its size by the following factor.
+    private static final int BUF_GROWTH_FACTOR = 8;
+
+    /**
+     * Creates a BER buffer for encoding.
+     */
+    public BerEncoder() {
+        this(DEFAULT_BUFSIZE);
+    }
+
+    /**
+     * Creates a BER buffer of a specified size for encoding.
+     * Specify the initial bufsize.  Buffer will be expanded as needed.
+     * @param bufsize The number of bytes for the buffer.
+     */
+    public BerEncoder(int bufsize) {
+        buf = new byte[bufsize];
+        this.bufsize = bufsize;
+        offset = 0;
+
+        seqOffset = new int[INITIAL_SEQUENCES];
+        curSeqIndex = 0;
+    }
+
+    /**
+     * Resets encoder to state when newly constructed.  Zeros out
+     * internal data structures.
+     */
+    public void reset() {
+        while (offset > 0) {
+            buf[--offset] = 0;
+        }
+        while (curSeqIndex > 0) {
+            seqOffset[--curSeqIndex] = 0;
+        }
+    }
+
+// ------------------ Accessor methods ------------
+
+    /**
+     * Gets the number of encoded bytes in this BER buffer.
+     */
+    public int getDataLen() {
+        return offset;
+    }
+
+    /**
+     * Gets the buffer that contains the BER encoding. Throws an
+     * exception if unmatched beginSeq() and endSeq() pairs were
+     * encountered. Not entire buffer contains encoded bytes.
+     * Use getDataLen() to determine number of encoded bytes.
+     * Use getBuffer(true) to get rid of excess bytes in array.
+     * @throws IllegalStateException If buffer contains unbalanced sequence.
+     */
+    public byte[] getBuf() {
+        if (curSeqIndex != 0) {
+            throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
+        }
+        return buf;     // shared buffer, be careful to use this method.
+    }
+
+    /**
+     * Gets the buffer that contains the BER encoding, trimming unused bytes.
+     *
+     * @throws IllegalStateException If buffer contains unbalanced sequence.
+     */
+    public byte[] getTrimmedBuf() {
+        int len = getDataLen();
+        byte[] trimBuf = new byte[len];
+
+        System.arraycopy(getBuf(), 0, trimBuf, 0, len);
+        return trimBuf;
+    }
+
+// -------------- encoding methods -------------
+
+    /**
+     * Begin encoding a sequence with a tag.
+     */
+    public void beginSeq(int tag) {
+
+        // Double the size of the SEQUENCE array if it overflows
+        if (curSeqIndex >= seqOffset.length) {
+            int[] seqOffsetTmp = new int[seqOffset.length * 2];
+
+            for (int i = 0; i < seqOffset.length; i++) {
+                seqOffsetTmp[i] = seqOffset[i];
+            }
+            seqOffset = seqOffsetTmp;
+        }
+
+        encodeByte(tag);
+        seqOffset[curSeqIndex] = offset;
+
+        // Save space for sequence length.
+        // %%% Currently we save enough space for sequences up to 64k.
+        //     For larger sequences we'll need to shift the data to the right
+        //     in endSeq().  If we could instead pad the length field with
+        //     zeros, it would be a big win.
+        ensureFreeBytes(3);
+        offset += 3;
+
+        curSeqIndex++;
+    }
+
+    /**
+      * Terminate a BER sequence.
+      */
+    public void endSeq() throws EncodeException {
+        curSeqIndex--;
+        if (curSeqIndex < 0) {
+            throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
+        }
+
+        int start = seqOffset[curSeqIndex] + 3; // index beyond length field
+        int len = offset - start;
+
+        if (len <= 0x7f) {
+            shiftSeqData(start, len, -2);
+            buf[seqOffset[curSeqIndex]] = (byte) len;
+        } else if (len <= 0xff) {
+            shiftSeqData(start, len, -1);
+            buf[seqOffset[curSeqIndex]] = (byte) 0x81;
+            buf[seqOffset[curSeqIndex] + 1] = (byte) len;
+        } else if (len <= 0xffff) {
+            buf[seqOffset[curSeqIndex]] = (byte) 0x82;
+            buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 8);
+            buf[seqOffset[curSeqIndex] + 2] = (byte) len;
+        } else if (len <= 0xffffff) {
+            shiftSeqData(start, len, 1);
+            buf[seqOffset[curSeqIndex]] = (byte) 0x83;
+            buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 16);
+            buf[seqOffset[curSeqIndex] + 2] = (byte) (len >> 8);
+            buf[seqOffset[curSeqIndex] + 3] = (byte) len;
+        } else {
+            throw new EncodeException("SEQUENCE too long");
+        }
+    }
+
+    /**
+     * Shifts contents of buf in the range [start,start+len) a specified amount.
+     * Positive shift value means shift to the right.
+     */
+    private void shiftSeqData(int start, int len, int shift) {
+        if (shift > 0) {
+            ensureFreeBytes(shift);
+        }
+        System.arraycopy(buf, start, buf, start + shift, len);
+        offset += shift;
+    }
+
+    /**
+     * Encode a single byte.
+     */
+    public void encodeByte(int b) {
+        ensureFreeBytes(1);
+        buf[offset++] = (byte) b;
+    }
+
+/*
+    private void deleteByte() {
+        offset--;
+    }
+*/
+
+
+    /*
+     * Encodes an int.
+     *<blockquote><pre>
+     * BER integer ::= 0x02 berlength byte {byte}*
+     *</pre></blockquote>
+     */
+    public void encodeInt(int i) {
+        encodeInt(i, 0x02);
+    }
+
+    /**
+     * Encodes an int and a tag.
+     *<blockquote><pre>
+     * BER integer w tag ::= tag berlength byte {byte}*
+     *</pre></blockquote>
+     */
+    public void encodeInt(int i, int tag) {
+        int mask = 0xff800000;
+        int intsize = 4;
+
+        while( (((i & mask) == 0) || ((i & mask) == mask)) && (intsize > 1) ) {
+            intsize--;
+            i <<= 8;
+        }
+
+        encodeInt(i, tag, intsize);
+    }
+
+    //
+    // encodes an int using numbytes for the actual encoding.
+    //
+    private void encodeInt(int i, int tag, int intsize) {
+
+        //
+        // integer ::= 0x02 asnlength byte {byte}*
+        //
+
+        if (intsize > 4) {
+            throw new IllegalArgumentException("BER encode error: INTEGER too long.");
+        }
+
+        ensureFreeBytes(2 + intsize);
+
+        buf[offset++] = (byte) tag;
+        buf[offset++] = (byte) intsize;
+
+        int mask = 0xff000000;
+
+        while (intsize-- > 0) {
+            buf[offset++] = (byte) ((i & mask) >> 24);
+            i <<= 8;
+        }
+    }
+
+    /**
+     * Encodes a boolean.
+     *<blockquote><pre>
+     * BER boolean ::= 0x01 0x01 {0xff|0x00}
+     *</pre></blockquote>
+     */
+    public void encodeBoolean(boolean b) {
+        encodeBoolean(b, ASN_BOOLEAN);
+    }
+
+
+    /**
+     * Encodes a boolean and a tag
+     *<blockquote><pre>
+     * BER boolean w TAG ::= tag 0x01 {0xff|0x00}
+     *</pre></blockquote>
+     */
+    public void encodeBoolean(boolean b, int tag) {
+        ensureFreeBytes(3);
+
+        buf[offset++] = (byte) tag;
+        buf[offset++] = 0x01;
+        buf[offset++] = b ? (byte) 0xff : (byte) 0x00;
+    }
+
+    /**
+     * Encodes a string.
+     *<blockquote><pre>
+     * BER string ::= 0x04 strlen byte1 byte2...
+     *</pre></blockquote>
+     * The string is converted into bytes using UTF-8 or ISO-Latin-1.
+     */
+    public void encodeString(String str, boolean encodeUTF8)
+        throws EncodeException {
+        encodeString(str, ASN_OCTET_STR, encodeUTF8);
+    }
+
+    /**
+     * Encodes a string and a tag.
+     *<blockquote><pre>
+     * BER string w TAG ::= tag strlen byte1 byte2...
+     *</pre></blockquote>
+     */
+    public void encodeString(String str, int tag, boolean encodeUTF8)
+        throws EncodeException {
+
+        encodeByte(tag);
+
+        int i = 0;
+        int count;
+        byte[] bytes = null;
+
+        if (str == null) {
+            count = 0;
+        } else if (encodeUTF8) {
+            try {
+                bytes = str.getBytes("UTF8");
+                count = bytes.length;
+            } catch (UnsupportedEncodingException e) {
+                throw new EncodeException("UTF8 not available on platform");
+            }
+        } else {
+            try {
+                bytes = str.getBytes("8859_1");
+                count = bytes.length;
+            } catch (UnsupportedEncodingException e) {
+                throw new EncodeException("8859_1 not available on platform");
+            }
+        }
+
+        encodeLength(count);
+
+        ensureFreeBytes(count);
+        while (i < count) {
+            buf[offset++] = bytes[i++];
+        }
+    }
+
+    /**
+     * Encodes a portion of an octet string and a tag.
+     */
+    public void encodeOctetString(byte tb[], int tag, int tboffset, int length)
+        throws EncodeException {
+
+        encodeByte(tag);
+        encodeLength(length);
+
+        if (length > 0) {
+            ensureFreeBytes(length);
+            System.arraycopy(tb, tboffset, buf, offset, length);
+            offset += length;
+        }
+    }
+
+    /**
+      * Encodes an octet string and a tag.
+      */
+    public void encodeOctetString(byte tb[], int tag) throws EncodeException {
+        encodeOctetString(tb, tag, 0, tb.length);
+    }
+
+    private void encodeLength(int len) throws EncodeException {
+        ensureFreeBytes(4);     // worst case
+
+        if (len < 128) {
+            buf[offset++] = (byte) len;
+        } else if (len <= 0xff) {
+            buf[offset++] = (byte) 0x81;
+            buf[offset++] = (byte) len;
+        } else if (len <= 0xffff) {
+            buf[offset++] = (byte) 0x82;
+            buf[offset++] = (byte) (len >> 8);
+            buf[offset++] = (byte) (len & 0xff);
+        } else if (len <= 0xffffff) {
+            buf[offset++] = (byte) 0x83;
+            buf[offset++] = (byte) (len >> 16);
+            buf[offset++] = (byte) (len >> 8);
+            buf[offset++] = (byte) (len & 0xff);
+        } else {
+            throw new EncodeException("string too long");
+        }
+    }
+
+    /**
+     * Encodes an array of strings.
+     */
+    public void encodeStringArray(String strs[], boolean encodeUTF8)
+        throws EncodeException {
+        if (strs == null)
+            return;
+        for (int i = 0; i < strs.length; i++) {
+            encodeString(strs[i], encodeUTF8);
+        }
+    }
+/*
+    private void encodeNull() {
+
+        //
+        // NULL ::= 0x05 0x00
+        //
+        encodeByte(0x05);
+        encodeByte(0x00);
+    }
+*/
+
+    /**
+     * Ensures that there are at least "len" unused bytes in "buf".
+     * When more space is needed "buf" is expanded by a factor of
+     * BUF_GROWTH_FACTOR, then "len" bytes are added if "buf" still
+     * isn't large enough.
+     */
+    private void ensureFreeBytes(int len) {
+        if (bufsize - offset < len) {
+            int newsize = bufsize * BUF_GROWTH_FACTOR;
+            if (newsize - offset < len) {
+                newsize += len;
+            }
+            byte newbuf[] = new byte[newsize];
+            // Only copy bytes in the range [0, offset)
+            System.arraycopy(buf, 0, newbuf, 0, offset);
+
+            buf = newbuf;
+            bufsize = newsize;
+        }
+    }
+}
Index: davmail.git/src/java/davmail/ldap/HexDumpEncoder.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ davmail.git/src/java/davmail/ldap/HexDumpEncoder.java	2019-11-18 22:18:15.462957423 +0100
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ * This class was stolen as is from sun.security.util in order to prevent
+ * compilation against private jdk APIs.
+ */
+
+package davmail.ldap;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * This class encodes a buffer into the classic: "Hexadecimal Dump" format of
+ * the past. It is useful for analyzing the contents of binary buffers.
+ * The format produced is as follows:
+ * <pre>
+ * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff ................
+ * </pre>
+ * Where xxxx is the offset into the buffer in 16 byte chunks, followed
+ * by ascii coded hexadecimal bytes followed by the ASCII representation of
+ * the bytes or '.' if they are not valid bytes.
+ *
+ * @author      Chuck McManis
+ */
+
+public class HexDumpEncoder {
+
+    private int offset;
+    private int thisLineLength;
+    private int currentByte;
+    private byte thisLine[] = new byte[16];
+
+    static void hexDigit(PrintStream p, byte x) {
+        char c;
+
+        c = (char) ((x >> 4) & 0xf);
+        if (c > 9)
+            c = (char) ((c-10) + 'A');
+        else
+            c = (char)(c + '0');
+        p.write(c);
+        c = (char) (x & 0xf);
+        if (c > 9)
+            c = (char)((c-10) + 'A');
+        else
+            c = (char)(c + '0');
+        p.write(c);
+    }
+
+    protected int bytesPerAtom() {
+        return (1);
+    }
+
+    protected int bytesPerLine() {
+        return (16);
+    }
+
+    protected void encodeBufferPrefix(OutputStream o) throws IOException {
+        offset = 0;
+        pStream = new PrintStream(o);
+    }
+
+    protected void encodeLinePrefix(OutputStream o, int len) throws IOException {
+        hexDigit(pStream, (byte)((offset >>> 8) & 0xff));
+        hexDigit(pStream, (byte)(offset & 0xff));
+        pStream.print(": ");
+        currentByte = 0;
+        thisLineLength = len;
+    }
+
+    protected void encodeAtom(OutputStream o, byte buf[], int off, int len) throws IOException {
+        thisLine[currentByte] = buf[off];
+        hexDigit(pStream, buf[off]);
+        pStream.print(" ");
+        currentByte++;
+        if (currentByte == 8)
+            pStream.print("  ");
+    }
+
+    protected void encodeLineSuffix(OutputStream o) throws IOException {
+        if (thisLineLength < 16) {
+            for (int i = thisLineLength; i < 16; i++) {
+                pStream.print("   ");
+                if (i == 7)
+                    pStream.print("  ");
+            }
+        }
+        pStream.print(" ");
+        for (int i = 0; i < thisLineLength; i++) {
+            if ((thisLine[i] < ' ') || (thisLine[i] > 'z')) {
+                pStream.print(".");
+            } else {
+                pStream.write(thisLine[i]);
+            }
+        }
+        pStream.println();
+        offset += thisLineLength;
+    }
+
+    /** Stream that understands "printing" */
+    protected PrintStream pStream;
+
+    /**
+     * This method works around the bizarre semantics of BufferedInputStream's
+     * read method.
+     */
+    protected int readFully(InputStream in, byte buffer[])
+            throws java.io.IOException {
+        for (int i = 0; i < buffer.length; i++) {
+            int q = in.read();
+            if (q == -1)
+                return i;
+            buffer[i] = (byte)q;
+        }
+        return buffer.length;
+    }
+
+    /**
+     * Encode bytes from the input stream, and write them as text characters
+     * to the output stream. This method will run until it exhausts the
+     * input stream, but does not print the line suffix for a final
+     * line that is shorter than bytesPerLine().
+     */
+    public void encode(InputStream inStream, OutputStream outStream)
+        throws IOException
+    {
+        int     j;
+        int     numBytes;
+        byte    tmpbuffer[] = new byte[bytesPerLine()];
+
+        encodeBufferPrefix(outStream);
+
+        while (true) {
+            numBytes = readFully(inStream, tmpbuffer);
+            if (numBytes == 0) {
+                break;
+            }
+            encodeLinePrefix(outStream, numBytes);
+            for (j = 0; j < numBytes; j += bytesPerAtom()) {
+
+                if ((j + bytesPerAtom()) <= numBytes) {
+                    encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
+                } else {
+                    encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);
+                }
+            }
+            if (numBytes < bytesPerLine()) {
+                break;
+            } else {
+                encodeLineSuffix(outStream);
+            }
+        }
+    }
+
+    /**
+     * A 'streamless' version of encode that simply takes a buffer of
+     * bytes and returns a string containing the encoded buffer.
+     */
+    public String encode(byte aBuffer[]) {
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        ByteArrayInputStream    inStream = new ByteArrayInputStream(aBuffer);
+        String retVal = null;
+        try {
+            encode(inStream, outStream);
+            // explicit ascii->unicode conversion
+            retVal = outStream.toString("ISO-8859-1");
+        } catch (Exception IOException) {
+            // This should never happen.
+            throw new Error("CharacterEncoder.encode internal error");
+        }
+        return (retVal);
+    }
+
+    /**
+     * Return a byte array from the remaining bytes in this ByteBuffer.
+     * <P>
+     * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+     * <P>
+     * To avoid an extra copy, the implementation will attempt to return the
+     * byte array backing the ByteBuffer.  If this is not possible, a
+     * new byte array will be created.
+     */
+    private byte [] getBytes(ByteBuffer bb) {
+        /*
+         * This should never return a BufferOverflowException, as we're
+         * careful to allocate just the right amount.
+         */
+        byte [] buf = null;
+
+        /*
+         * If it has a usable backing byte buffer, use it.  Use only
+         * if the array exactly represents the current ByteBuffer.
+         */
+        if (bb.hasArray()) {
+            byte [] tmp = bb.array();
+            if ((tmp.length == bb.capacity()) &&
+                    (tmp.length == bb.remaining())) {
+                buf = tmp;
+                bb.position(bb.limit());
+            }
+        }
+
+        if (buf == null) {
+            /*
+             * This class doesn't have a concept of encode(buf, len, off),
+             * so if we have a partial buffer, we must reallocate
+             * space.
+             */
+            buf = new byte[bb.remaining()];
+
+            /*
+             * position() automatically updated
+             */
+            bb.get(buf);
+        }
+
+        return buf;
+    }
+
+    /**
+     * A 'streamless' version of encode that simply takes a ByteBuffer
+     * and returns a string containing the encoded buffer.
+     * <P>
+     * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+     */
+    public String encode(ByteBuffer aBuffer) {
+        byte [] buf = getBytes(aBuffer);
+        return encode(buf);
+    }
+
+    /**
+     * Encode bytes from the input stream, and write them as text characters
+     * to the output stream. This method will run until it exhausts the
+     * input stream. It differs from encode in that it will add the
+     * line at the end of a final line that is shorter than bytesPerLine().
+     */
+    public void encodeBuffer(InputStream inStream, OutputStream outStream)
+        throws IOException
+    {
+        int     j;
+        int     numBytes;
+        byte    tmpbuffer[] = new byte[bytesPerLine()];
+
+        encodeBufferPrefix(outStream);
+
+        while (true) {
+            numBytes = readFully(inStream, tmpbuffer);
+            if (numBytes == 0) {
+                break;
+            }
+            encodeLinePrefix(outStream, numBytes);
+            for (j = 0; j < numBytes; j += bytesPerAtom()) {
+                if ((j + bytesPerAtom()) <= numBytes) {
+                    encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
+                } else {
+                    encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);
+                }
+            }
+            encodeLineSuffix(outStream);
+            if (numBytes < bytesPerLine()) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Encode the buffer in <i>aBuffer</i> and write the encoded
+     * result to the OutputStream <i>aStream</i>.
+     */
+    public void encodeBuffer(byte aBuffer[], OutputStream aStream)
+        throws IOException
+    {
+        ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
+        encodeBuffer(inStream, aStream);
+    }
+
+    /**
+     * A 'streamless' version of encode that simply takes a buffer of
+     * bytes and returns a string containing the encoded buffer.
+     */
+    public String encodeBuffer(byte aBuffer[]) {
+        ByteArrayOutputStream   outStream = new ByteArrayOutputStream();
+        ByteArrayInputStream    inStream = new ByteArrayInputStream(aBuffer);
+        try {
+            encodeBuffer(inStream, outStream);
+        } catch (Exception IOException) {
+            // This should never happen.
+            throw new Error("CharacterEncoder.encodeBuffer internal error");
+        }
+        return (outStream.toString());
+    }
+
+    /**
+     * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
+     * result to the OutputStream <i>aStream</i>.
+     * <P>
+     * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+     */
+    public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)
+        throws IOException
+    {
+        byte [] buf = getBytes(aBuffer);
+        encodeBuffer(buf, aStream);
+    }
+
+}
Index: davmail.git/src/java/davmail/ldap/LdapConnection.java
===================================================================
--- davmail.git.orig/src/java/davmail/ldap/LdapConnection.java	2019-11-18 22:18:15.534958615 +0100
+++ davmail.git/src/java/davmail/ldap/LdapConnection.java	2019-11-18 22:18:15.474957621 +0100
@@ -18,9 +18,6 @@
  */
 package davmail.ldap;
 
-import com.sun.jndi.ldap.Ber;
-import com.sun.jndi.ldap.BerDecoder;
-import com.sun.jndi.ldap.BerEncoder;
 import davmail.AbstractConnection;
 import davmail.BundleMessage;
 import davmail.Settings;
Index: davmail.git/src/java/davmail/ldap/LdapServer.java
===================================================================
--- davmail.git.orig/src/java/davmail/ldap/LdapServer.java	2019-11-18 22:18:15.534958615 +0100
+++ davmail.git/src/java/davmail/ldap/LdapServer.java	2019-11-18 22:18:15.474957621 +0100
@@ -53,4 +53,4 @@
     public AbstractConnection createConnectionHandler(Socket clientSocket) {
         return new LdapConnection(clientSocket);
     }
-}
\ No newline at end of file
+}
Index: davmail.git/build.xml
===================================================================
--- davmail.git.orig/build.xml	2019-11-18 22:18:15.534958615 +0100
+++ davmail.git/build.xml	2019-11-18 22:18:15.474957621 +0100
@@ -43,12 +43,6 @@
         </not>
     </condition>
 
-    <condition property="is.java9">
-        <not>
-            <matches string="${ant.java.version}" pattern="1\.[0-8]"/>
-        </not>
-    </condition>
-
     <condition property="is.utf8">
         <equals arg1="${file.encoding}" arg2="UTF-8"/>
     </condition>
@@ -88,24 +82,7 @@
         <mkdir dir="target/classes"/>
     </target>
 
-    <target name="compile-java9" depends="init">
-        <mkdir dir="target/classes"/>
-        <javac srcdir="src/java" destdir="target/classes" source="9" debug="on" encoding="UTF-8"
-               includeantruntime="false">
-            <exclude name="davmail/exchange/auth/*Interactive*" unless="is.javafx"/>
-            <exclude name="davmail/service/DavService.java" if="nowindowsservice"/>
-            <exclude name="davmail/ui/tray/OSXAwtGatewayTray.java" if="noosxtray"/>
-            <compilerarg value="--add-exports" />
-            <compilerarg value="java.naming/com.sun.jndi.ldap=ALL-UNNAMED" />
-            <compilerarg value="--add-exports" />
-            <compilerarg value="java.base/sun.net.www.protocol.https=ALL-UNNAMED" />
-            <classpath>
-                <path refid="classpath"/>
-            </classpath>
-        </javac>
-    </target>
-
-    <target name="compile-java" depends="init">
+    <target name="compile" depends="init">
         <mkdir dir="target/classes"/>
         <javac srcdir="src/java" destdir="target/classes" source="1.8" target="1.8" debug="on" encoding="UTF-8"
                includeantruntime="false">
@@ -116,9 +93,6 @@
                 <path refid="classpath"/>
             </classpath>
         </javac>
-    </target>
-
-    <target name="compile" depends="compile-java">
         <copy todir="target/classes">
             <fileset dir="src/java">
                 <include name="**/*"/>
