manifoldcf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kwri...@apache.org
Subject svn commit: r1308632 [2/3] - in /incubator/lcf/branches/CONNECTORS-443: build.xml common-build.xml upstream-diffs/ upstream-diffs/commons-httpclient-3.1-mcf.patch upstream-diffs/xerces2-j-2.9.1-mcf.patch
Date Tue, 03 Apr 2012 00:32:42 GMT
Added: incubator/lcf/branches/CONNECTORS-443/upstream-diffs/commons-httpclient-3.1-mcf.patch
URL: http://svn.apache.org/viewvc/incubator/lcf/branches/CONNECTORS-443/upstream-diffs/commons-httpclient-3.1-mcf.patch?rev=1308632&view=auto
==============================================================================
--- incubator/lcf/branches/CONNECTORS-443/upstream-diffs/commons-httpclient-3.1-mcf.patch (added)
+++ incubator/lcf/branches/CONNECTORS-443/upstream-diffs/commons-httpclient-3.1-mcf.patch Tue Apr  3 00:32:41 2012
@@ -0,0 +1,2167 @@
+Index: src/java/org/apache/commons/httpclient/HostConfiguration.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/HostConfiguration.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 915934)
++++ src/java/org/apache/commons/httpclient/HostConfiguration.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(working copy)
+@@ -31,7 +31,9 @@
+ package org.apache.commons.httpclient;
+ 
+ import org.apache.commons.httpclient.params.HostParams;
++import org.apache.commons.httpclient.params.HttpClientParams;
+ import org.apache.commons.httpclient.protocol.Protocol;
++import org.apache.commons.httpclient.protocol.ProtocolFactory;
+ import org.apache.commons.httpclient.util.LangUtils;
+ 
+ import java.net.InetAddress;
+@@ -250,7 +252,10 @@
+      * @param protocol The protocol.
+      */
+     public synchronized void setHost(final String host, int port, final String protocol) {
+-        this.host = new HttpHost(host, port, Protocol.getProtocol(protocol));
++        if (this.params.getParameter(HttpClientParams.PROTOCOL_FACTORY) != null)
++            this.host = new HttpHost(host, port, ((ProtocolFactory)this.params.getParameter(HttpClientParams.PROTOCOL_FACTORY)).getProtocol(protocol));
++        else
++            this.host = new HttpHost(host, port, Protocol.getProtocol(protocol));
+     }
+     
+     /**
+@@ -293,7 +298,7 @@
+      * @param port The port
+      */
+     public synchronized void setHost(final String host, int port) {
+-        setHost(host, port, Protocol.getProtocol("http"));
++        setHost(host, port, "http");
+     }
+     
+     /**
+@@ -302,7 +307,11 @@
+      * @param host The host(IP or DNS name).
+      */
+     public synchronized void setHost(final String host) {
+-        Protocol defaultProtocol = Protocol.getProtocol("http"); 
++        Protocol defaultProtocol;
++        if (this.params.getParameter(HttpClientParams.PROTOCOL_FACTORY) != null)
++            defaultProtocol = ((ProtocolFactory)this.params.getParameter(HttpClientParams.PROTOCOL_FACTORY)).getProtocol("http");
++        else
++            defaultProtocol = Protocol.getProtocol("http"); 
+         setHost(host, defaultProtocol.getDefaultPort(), defaultProtocol);
+     }
+     
+Index: src/java/org/apache/commons/httpclient/protocol/ProtocolFactory.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/protocol/ProtocolFactory.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 0)
++++ src/java/org/apache/commons/httpclient/protocol/ProtocolFactory.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(revision 1308601)
+@@ -0,0 +1,150 @@
++/*
++ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/protocol/Protocol.java,v 1.10 2004/04/18 23:51:38 jsdever Exp $
++ * $Revision: 157457 $
++ * $Date: 2005-03-14 15:23:16 -0500 (Mon, 14 Mar 2005) $
++ *
++ * ====================================================================
++ *
++ *  Copyright 2002-2004 The Apache Software Foundation
++ *
++ *  Licensed 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.
++ * ====================================================================
++ *
++ * This software consists of voluntary contributions made by many
++ * individuals on behalf of the Apache Software Foundation.  For more
++ * information on the Apache Software Foundation, please see
++ * <http://www.apache.org/>.
++ *
++ */
++package org.apache.commons.httpclient.protocol;
++
++import java.util.Collections;
++import java.util.HashMap;
++import java.util.Map;
++
++import org.apache.commons.httpclient.util.LangUtils;
++
++/**
++ * A class to encapsulate the specifics of a protocol.  This class class also
++ * provides the ability to customize the set and characteristics of the
++ * protocols used.
++ * 
++ * <p>One use case for modifying the default set of protocols would be to set a
++ * custom SSL socket factory.  This would look something like the following:
++ * <pre> 
++ * Protocol myHTTPS = new Protocol( "https", new MySSLSocketFactory(), 443 );
++ * 
++ * Protocol.registerProtocol( "https", myHTTPS );
++ * </pre>
++ *
++ * @author Michael Becke 
++ * @author Jeff Dever
++ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
++ *  
++ * @since 2.0 
++ */
++public class ProtocolFactory {
++
++    /** The available protocols */
++    private Map PROTOCOLS = Collections.synchronizedMap(new HashMap());
++
++    /**
++     * Registers a new protocol with the given identifier.  If a protocol with
++     * the given ID already exists it will be overridden.  This ID is the same
++     * one used to retrieve the protocol from getProtocol(String).
++     * 
++     * @param id the identifier for this protocol
++     * @param protocol the protocol to register
++     * 
++     * @see #getProtocol(String)
++     */
++    public void registerProtocol(String id, Protocol protocol) {
++
++        if (id == null) {
++            throw new IllegalArgumentException("id is null");
++        }
++        if (protocol == null) {
++            throw new IllegalArgumentException("protocol is null");
++        }
++
++        PROTOCOLS.put(id, protocol);
++    }
++
++    /**
++     * Unregisters the protocol with the given ID.
++     * 
++     * @param id the ID of the protocol to remove
++     */
++    public void unregisterProtocol(String id) {
++
++        if (id == null) {
++            throw new IllegalArgumentException("id is null");
++        }
++
++        PROTOCOLS.remove(id);
++    }
++
++    /**
++     * Gets the protocol with the given ID.
++     * 
++     * @param id the protocol ID
++     * 
++     * @return Protocol a protocol
++     * 
++     * @throws IllegalStateException if a protocol with the ID cannot be found
++     */
++    public Protocol getProtocol(String id) 
++        throws IllegalStateException {
++
++        if (id == null) {
++            throw new IllegalArgumentException("id is null");
++        }
++
++        Protocol protocol = (Protocol) PROTOCOLS.get(id);
++
++        if (protocol == null) {
++            protocol = lazyRegisterProtocol(id);
++        }
++
++        return protocol;
++    } 
++
++    /**
++     * Lazily registers the protocol with the given id.
++     * 
++     * @param id the protocol ID
++     * 
++     * @return the lazily registered protocol
++     * 
++     * @throws IllegalStateException if the protocol with id is not recognized
++     */
++    private Protocol lazyRegisterProtocol(String id) 
++        throws IllegalStateException {
++
++        if ("http".equals(id)) {
++            final Protocol http 
++                = new Protocol("http", DefaultProtocolSocketFactory.getSocketFactory(), 80);
++            Protocol.registerProtocol("http", http);
++            return http;
++        }
++
++        if ("https".equals(id)) {
++            final Protocol https 
++                = new Protocol("https", SSLProtocolSocketFactory.getSocketFactory(), 443);
++            Protocol.registerProtocol("https", https);
++            return https;
++        }
++
++        throw new IllegalStateException("unsupported protocol: '" + id + "'");
++    }
++}
+
+Property changes on: src/java/org/apache/commons/httpclient/protocol/ProtocolFactory.java
+___________________________________________________________________
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+Index: src/java/org/apache/commons/httpclient/auth/NTLMScheme.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/auth/NTLMScheme.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 915934)
++++ src/java/org/apache/commons/httpclient/auth/NTLMScheme.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(working copy)
+@@ -336,18 +336,23 @@
+         NTLM ntlm = new NTLM();
+         ntlm.setCredentialCharset(method.getParams().getCredentialCharset());
+         String response = null;
++
+         if (this.state == INITIATED || this.state == FAILED) {
+             response = ntlm.getType1Message(
+                 ntcredentials.getHost(), 
+                 ntcredentials.getDomain());
+             this.state = TYPE1_MSG_GENERATED;
+         } else {
++	    NTLM.Type2Message t2m = new NTLM.Type2Message(this.ntlmchallenge);
+             response = ntlm.getType3Message(
+                 ntcredentials.getUserName(), 
+                 ntcredentials.getPassword(),
+                 ntcredentials.getHost(), 
+                 ntcredentials.getDomain(),
+-                ntlm.parseType2Message(this.ntlmchallenge));
++                t2m.getChallenge(),
++		t2m.getFlags(),
++		t2m.getTarget(),
++		t2m.getTargetInfo());
+             this.state = TYPE3_MSG_GENERATED;
+         }
+         return "NTLM " + response;
+Index: src/java/org/apache/commons/httpclient/auth/NTLM.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/auth/NTLM.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 915934)
++++ src/java/org/apache/commons/httpclient/auth/NTLM.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(working copy)
+@@ -32,6 +32,9 @@
+ 
+ import java.security.InvalidKeyException;
+ import java.security.NoSuchAlgorithmException;
++import java.security.Key;
++import java.security.MessageDigest;
++
+ import java.util.Locale;
+ 
+ import javax.crypto.BadPaddingException;
+@@ -52,9 +55,13 @@
+  * exists for it.  This class is based upon the reverse engineering
+  * efforts of a wide range of people.</p>
+  *
++ * THIS IS A VERY HELPFUL REFERENCE: http://en.wikipedia.org/wiki/NTLM
++ *
+  * <p>Please note that an implementation of JCE must be correctly installed and configured when
+  * using NTLM support.</p>
+  *
++ * NTLMv2 protocol description provided by Michael B Allen <jcifs at samba.org>
++ *
+  * <p>This class should not be used externally to HttpClient as it's API is specifically
+  * designed to work with HttpClient's use case, in particular it's connection management.</p>
+  *
+@@ -67,18 +74,52 @@
+  */
+ final class NTLM {
+ 
++    // Flags we use
++    protected final static int FLAG_UNICODE_ENCODING = 0x00000001;
++    protected final static int FLAG_TARGET_DESIRED = 0x00000004;
++    protected final static int FLAG_NEGOTIATE_SIGN = 0x00000010;
++    protected final static int FLAG_NEGOTIATE_SEAL = 0x00000020;
++    protected final static int FLAG_NEGOTIATE_NTLM = 0x00000200;
++    protected final static int FLAG_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
++    protected final static int FLAG_NEGOTIATE_NTLM2 = 0x00080000;
++    protected final static int FLAG_NEGOTIATE_128 = 0x20000000;
++    protected final static int FLAG_NEGOTIATE_KEY_EXCH = 0x40000000;
++
++    /** Secure random generator */
++    private static java.security.SecureRandom randomGenerator;
++    static
++    {
++	try
++	{
++		randomGenerator = java.security.SecureRandom.getInstance("SHA1PRNG");
++	}
++	catch (Exception e)
++	{
++		// If exception, nothing we can really do about it - can't even count on log being initialized
++		System.err.println("Couldn't initialize random generator: "+e.getMessage());
++		e.printStackTrace(System.err);
++	}
++    }
++
++    /** Signature string for NTLM messages */
++    private static final String signatureString = "NTLMSSP";
++
+     /** Character encoding */
+     public static final String DEFAULT_CHARSET = "ASCII";
+ 
+-    /** The current response */
+-    private byte[] currentResponse;
+-
+-    /** The current position */
+-    private int currentPosition = 0;
+-
+     /** The character set to use for encoding the credentials */
+     private String credentialCharset = DEFAULT_CHARSET;
+     
++    /** The signature string as bytes in the default encoding */
++    private static byte[] signatureBytes;
++    static
++    {
++	byte[] bytesWithoutNull = EncodingUtil.getBytes(signatureString, "ASCII");
++	signatureBytes = new byte[bytesWithoutNull.length + 1];
++	System.arraycopy(bytesWithoutNull,0,signatureBytes,0,bytesWithoutNull.length);
++	signatureBytes[bytesWithoutNull.length] = (byte) 0x00;
++    }
++
+     /**
+      * Returns the response for the given message.
+      *
+@@ -98,469 +139,1268 @@
+         if (message == null || message.trim().equals("")) {
+             response = getType1Message(host, domain);
+         } else {
+-            response = getType3Message(username, password, host, domain,
+-                    parseType2Message(message));
++	    Type2Message t2m = new Type2Message(message);
++	    response = getType3Message(username, password, host, domain,
++                t2m.getChallenge(), t2m.getFlags(), t2m.getTarget(), t2m.getTargetInfo());
+         }
+         return response;
+     }
+ 
+     /**
+-     * Return the cipher for the specified key.
+-     * @param key The key.
+-     * @return Cipher The cipher.
+-     * @throws AuthenticationException If the cipher cannot be retrieved.
++     * Creates the first message (type 1 message) in the NTLM authentication sequence.
++     * This message includes the user name, domain and host for the authentication session.
++     *
++     * @param host the computer name of the host requesting authentication.
++     * @param domain The domain to authenticate with.
++     * @return String the message to add to the HTTP request header.
+      */
+-    private Cipher getCipher(byte[] key) throws AuthenticationException {
+-        try {
+-            final Cipher ecipher = Cipher.getInstance("DES/ECB/NoPadding");
+-            key = setupKey(key);
+-            ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DES"));
+-            return ecipher;
+-        } catch (NoSuchAlgorithmException e) {
+-            throw new AuthenticationException("DES encryption is not available.", e);
+-        } catch (InvalidKeyException e) {
+-            throw new AuthenticationException("Invalid key for DES encryption.", e);
+-        } catch (NoSuchPaddingException e) {
+-            throw new AuthenticationException(
+-                "NoPadding option for DES is not available.", e);
+-        }
++    public String getType1Message(String host, String domain)
++        throws AuthenticationException {
++        return new Type1Message(domain,host).getResponse();
+     }
+ 
+     /** 
+-     * Adds parity bits to the key.
+-     * @param key56 The key
+-     * @return The modified key.
++     * Creates the type 3 message using the given server nonce.  The type 3 message includes all the
++     * information for authentication, host, domain, username and the result of encrypting the
++     * nonce sent by the server using the user's password as the key.
++     *
++     * @param user The user name.  This should not include the domain name.
++     * @param password The password.
++     * @param host The host that is originating the authentication request.
++     * @param domain The domain to authenticate within.
++     * @param nonce the 8 byte array the server sent.
++     * @return The type 3 message.
++     * @throws AuthenticationException If {@encrypt(byte[],byte[])} fails.
+      */
+-    private byte[] setupKey(byte[] key56) {
+-        byte[] key = new byte[8];
+-        key[0] = (byte) ((key56[0] >> 1) & 0xff);
+-        key[1] = (byte) ((((key56[0] & 0x01) << 6) 
+-            | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff);
+-        key[2] = (byte) ((((key56[1] & 0x03) << 5) 
+-            | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff);
+-        key[3] = (byte) ((((key56[2] & 0x07) << 4) 
+-            | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff);
+-        key[4] = (byte) ((((key56[3] & 0x0f) << 3) 
+-            | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff);
+-        key[5] = (byte) ((((key56[4] & 0x1f) << 2) 
+-            | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff);
+-        key[6] = (byte) ((((key56[5] & 0x3f) << 1) 
+-            | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff);
+-        key[7] = (byte) (key56[6] & 0x7f);
+-        
+-        for (int i = 0; i < key.length; i++) {
+-            key[i] = (byte) (key[i] << 1);
+-        }
+-        return key;
++    public String getType3Message(String user, String password,
++            String host, String domain, byte[] nonce, int type2Flags, String target, byte[] targetInformation)
++    throws AuthenticationException {
++        return new Type3Message(domain,host,user,password,nonce,type2Flags,target,targetInformation).getResponse();
+     }
+ 
+     /**
+-     * Encrypt the data.
+-     * @param key The key.
+-     * @param bytes The data
+-     * @return byte[] The encrypted data
+-     * @throws HttpException If {@link Cipher.doFinal(byte[])} fails
++     * @return Returns the credentialCharset.
+      */
+-    private byte[] encrypt(byte[] key, byte[] bytes)
+-        throws AuthenticationException {
+-        Cipher ecipher = getCipher(key);
+-        try {
+-            byte[] enc = ecipher.doFinal(bytes);
+-            return enc;
+-        } catch (IllegalBlockSizeException e) {
+-            throw new AuthenticationException("Invalid block size for DES encryption.", e);
+-        } catch (BadPaddingException e) {
+-            throw new AuthenticationException("Data not padded correctly for DES encryption.", e);
+-        }
++    public String getCredentialCharset() {
++        return credentialCharset;
+     }
+ 
+     /** 
+-     * Prepares the object to create a response of the given length.
+-     * @param length the length of the response to prepare.
++     * @param credentialCharset The credentialCharset to set.
+      */
+-    private void prepareResponse(int length) {
+-        currentResponse = new byte[length];
+-        currentPosition = 0;
++    public void setCredentialCharset(String credentialCharset) {
++        this.credentialCharset = credentialCharset;
+     }
+ 
+-    /** 
+-     * Adds the given byte to the response.
+-     * @param b the byte to add.
+-     */
+-    private void addByte(byte b) {
+-        currentResponse[currentPosition] = b;
+-        currentPosition++;
++    /** Strip dot suffix from a name */
++    private static String stripDotSuffix(String value)
++    {
++	int index = value.indexOf(".");
++	if (index != -1)
++            return value.substring(0,index);
++	return value;
+     }
+ 
+-    /** 
+-     * Adds the given bytes to the response.
+-     * @param bytes the bytes to add.
+-     */
+-    private void addBytes(byte[] bytes) {
+-        for (int i = 0; i < bytes.length; i++) {
+-            currentResponse[currentPosition] = bytes[i];
+-            currentPosition++;
+-        }
++    /** Convert host to standard form */
++    private static String convertHost(String host)
++    {
++	return stripDotSuffix(host);
+     }
+ 
+-    /** 
+-     * Returns the response that has been generated after shrinking the array if
+-     * required and base64 encodes the response.
+-     * @return The response as above.
+-     */
+-    private String getResponse() {
+-        byte[] resp;
+-        if (currentResponse.length > currentPosition) {
+-            byte[] tmp = new byte[currentPosition];
+-            for (int i = 0; i < currentPosition; i++) {
+-                tmp[i] = currentResponse[i];
+-            }
+-            resp = tmp;
+-        } else {
+-            resp = currentResponse;
++    /** Convert domain to standard form */
++    private static String convertDomain(String domain)
++    {
++        return stripDotSuffix(domain);
++    }
++
++    private static int readULong(byte[] src, int index)
++        throws AuthenticationException {
++        if (src.length < index + 4)
++            throw new AuthenticationException("NTLM authentication - buffer too small for DWORD");
++        return (src[index] & 0xff) |
++                ((src[index + 1] & 0xff) << 8) |
++                ((src[index + 2] & 0xff) << 16) |
++                ((src[index + 3] & 0xff) << 24);
++    }
++
++    private static int readUShort(byte[] src, int index)
++        throws AuthenticationException {
++        if (src.length < index + 2)
++            throw new AuthenticationException("NTLM authentication - buffer too small for WORD");
++        return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);
++    }
++
++    private static byte[] readSecurityBuffer(byte[] src, int index) 
++        throws AuthenticationException {
++        int length = readUShort(src, index);
++        int offset = readULong(src, index + 4);
++	if (src.length < offset + length)
++            throw new AuthenticationException("NTLM authentication - buffer too small for data item");
++        byte[] buffer = new byte[length];
++        System.arraycopy(src, offset, buffer, 0, length);
++        return buffer;
++    }
++
++    /** Calculate a challenge block */
++    private static byte[] makeRandomChallenge()
++    {
++        byte[] rval = new byte[8];
++        synchronized (randomGenerator)
++        {
++            randomGenerator.nextBytes(rval);
+         }
+-        return EncodingUtil.getAsciiString(Base64.encodeBase64(resp));
++        return rval;
+     }
++     
++    /** Calculate an NTLM2 challenge block */
++    private static byte[] makeNTLM2RandomChallenge()
++    {
++        byte[] rval = new byte[24];
++        synchronized (randomGenerator)
++        {
++            randomGenerator.nextBytes(rval);
++        }
++        // 8-byte challenge, padded with zeros to 24 bytes.
++        java.util.Arrays.fill(rval,8,24,(byte)0x00);
++        return rval;
++    }
+     
++    
+     /**
+-     * Creates the first message (type 1 message) in the NTLM authentication sequence.
+-     * This message includes the user name, domain and host for the authentication session.
++     * Calculates the LM Response for the given challenge, using the specified
++     * password.
+      *
+-     * @param host the computer name of the host requesting authentication.
+-     * @param domain The domain to authenticate with.
+-     * @return String the message to add to the HTTP request header.
++     * @param password The user's password.
++     * @param challenge The Type 2 challenge from the server.
++     *
++     * @return The LM Response.
+      */
+-    public String getType1Message(String host, String domain) {
+-        host = host.toUpperCase(Locale.ENGLISH);
+-        domain = domain.toUpperCase(Locale.ENGLISH);
+-        byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET);
+-        byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET);
++    public static byte[] getLMResponse(String password, byte[] challenge)
++        throws AuthenticationException {
++        byte[] lmHash = lmHash(password);
++        return lmResponse(lmHash, challenge);
++    }
+ 
+-        int finalLength = 32 + hostBytes.length + domainBytes.length;
+-        prepareResponse(finalLength);
+-        
+-        // The initial id string.
+-        byte[] protocol = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET);
+-        addBytes(protocol);
+-        addByte((byte) 0);
++    /**
++     * Calculates the NTLM Response for the given challenge, using the
++     * specified password.
++     *
++     * @param password The user's password.
++     * @param challenge The Type 2 challenge from the server.
++     *
++     * @return The NTLM Response.
++     */
++    public static byte[] getNTLMResponse(String password, byte[] challenge)
++        throws AuthenticationException {
++        byte[] ntlmHash = ntlmHash(password);
++        return lmResponse(ntlmHash, challenge);
++    }
+ 
+-        // Type
+-        addByte((byte) 1);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++    /**
++     * Calculates the NTLMv2 Response for the given challenge, using the
++     * specified authentication target, username, password, target information
++     * block, and client challenge.
++     *
++     * @param target The authentication target (i.e., domain).
++     * @param user The username. 
++     * @param password The user's password.
++     * @param targetInformation The target information block from the Type 2
++     * message.
++     * @param challenge The Type 2 challenge from the server.
++     * @param clientChallenge The random 8-byte client challenge. 
++     *
++     * @return The NTLMv2 Response.
++     */
++    public static byte[] getNTLMv2Response(String target, String user,
++        String password, byte[] challenge,
++        byte[] clientChallenge, byte[] targetInformation)
++            throws AuthenticationException {
++        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
++        byte[] blob = createBlob(clientChallenge, targetInformation);
++        return lmv2Response(ntlmv2Hash, challenge, blob);
++    }
+ 
+-        // Flags
+-        addByte((byte) 6);
+-        addByte((byte) 82);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++    /**
++     * Calculates the LMv2 Response for the given challenge, using the
++     * specified authentication target, username, password, and client
++     * challenge.
++     *
++     * @param target The authentication target (i.e., domain).
++     * @param user The username.
++     * @param password The user's password.
++     * @param challenge The Type 2 challenge from the server.
++     * @param clientChallenge The random 8-byte client challenge.
++     *
++     * @return The LMv2 Response. 
++     */
++    public static byte[] getLMv2Response(String target, String user,
++        String password, byte[] challenge, byte[] clientChallenge)
++            throws AuthenticationException {
++        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
++        return lmv2Response(ntlmv2Hash, challenge, clientChallenge);
++    }
+ 
+-        // Domain length (first time).
+-        int iDomLen = domainBytes.length;
+-        byte[] domLen = convertShort(iDomLen);
+-        addByte(domLen[0]);
+-        addByte(domLen[1]);
++    /**
++     * Calculates the NTLM2 Session Response for the given challenge, using the
++     * specified password and client challenge.
++     *
++     * @param password The user's password.
++     * @param challenge The Type 2 challenge from the server.
++     * @param clientChallenge The random 8-byte client challenge.
++     *
++     * @return The NTLM2 Session Response.  This is placed in the NTLM
++     * response field of the Type 3 message; the LM response field contains
++     * the client challenge, null-padded to 24 bytes.
++     */
++    public static byte[] getNTLM2SessionResponse(String password,
++        byte[] challenge, byte[] clientChallenge) throws AuthenticationException
++    {
++        try
++        {
++            byte[] ntlmHash = ntlmHash(password);
+ 
+-        // Domain length (second time).
+-        addByte(domLen[0]);
+-        addByte(domLen[1]);
++            // Look up MD5 algorithm (was necessary on jdk 1.4.2)
++            // This used to be needed, but java 1.5.0_07 includes the MD5 algorithm (finally)
++            //Class x = Class.forName("gnu.crypto.hash.MD5");
++            //Method updateMethod = x.getMethod("update",new Class[]{byte[].class});
++            //Method digestMethod = x.getMethod("digest",new Class[0]);
++            //Object mdInstance = x.newInstance();
++            //updateMethod.invoke(mdInstance,new Object[]{challenge});
++            //updateMethod.invoke(mdInstance,new Object[]{clientChallenge});
++            //byte[] digest = (byte[])digestMethod.invoke(mdInstance,new Object[0]);
++	
++            MessageDigest md5 = MessageDigest.getInstance("MD5");
++            md5.update(challenge);
++            md5.update(clientChallenge);
++            byte[] digest = md5.digest();
++	
++            byte[] sessionHash = new byte[8];
++            System.arraycopy(digest, 0, sessionHash, 0, 8);
++            return lmResponse(ntlmHash, sessionHash);
++        }
++        catch (Exception e)
++        {
++            if (e instanceof AuthenticationException)
++                throw (AuthenticationException)e;
++            throw new AuthenticationException(e.getMessage(),e);
++	}
++    }
+ 
+-        // Domain offset.
+-        byte[] domOff = convertShort(hostBytes.length + 32);
+-        addByte(domOff[0]);
+-        addByte(domOff[1]);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++    /**
++     * Creates the LM Hash of the user's password.
++     *
++     * @param password The password.
++     *
++     * @return The LM Hash of the given password, used in the calculation
++     * of the LM Response.
++     */
++    private static byte[] lmHash(String password) throws AuthenticationException
++    {
++        try
++        {
++            byte[] oemPassword = password.toUpperCase().getBytes("US-ASCII");
++            int length = Math.min(oemPassword.length, 14);
++            byte[] keyBytes = new byte[14];
++            System.arraycopy(oemPassword, 0, keyBytes, 0, length);
++            Key lowKey = createDESKey(keyBytes, 0);
++            Key highKey = createDESKey(keyBytes, 7);
++            byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII");
++            Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
++            des.init(Cipher.ENCRYPT_MODE, lowKey);
++            byte[] lowHash = des.doFinal(magicConstant);
++            des.init(Cipher.ENCRYPT_MODE, highKey);
++            byte[] highHash = des.doFinal(magicConstant);
++            byte[] lmHash = new byte[16];
++            System.arraycopy(lowHash, 0, lmHash, 0, 8);
++            System.arraycopy(highHash, 0, lmHash, 8, 8);
++            return lmHash;
++        }
++        catch (Exception e)
++        {
++            throw new AuthenticationException(e.getMessage(),e);
++        }
++    }
+ 
+-        // Host length (first time).
+-        byte[] hostLen = convertShort(hostBytes.length);
+-        addByte(hostLen[0]);
+-        addByte(hostLen[1]);
++    /**
++     * Creates the NTLM Hash of the user's password.
++     *
++     * @param password The password.
++     *
++     * @return The NTLM Hash of the given password, used in the calculation
++     * of the NTLM Response and the NTLMv2 and LMv2 Hashes.
++     */
++    private static byte[] ntlmHash(String password) throws AuthenticationException
++    {
++        try
++        {
++            byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
++            MD4 md4 = new MD4();
++            md4.update(unicodePassword);
++            return md4.getOutput();
++        }
++        catch (java.io.UnsupportedEncodingException e)
++        {
++            throw new AuthenticationException("Unicode not supported: "+e.getMessage(),e);
++        }
++    }
+ 
+-        // Host length (second time).
+-        addByte(hostLen[0]);
+-        addByte(hostLen[1]);
++    /**
++     * Creates the NTLMv2 Hash of the user's password.
++     *
++     * @param target The authentication target (i.e., domain).
++     * @param user The username.
++     * @param password The password.
++     *
++     * @return The NTLMv2 Hash, used in the calculation of the NTLMv2
++     * and LMv2 Responses. 
++     */
++    private static byte[] ntlmv2Hash(String target, String user,
++        String password) throws AuthenticationException
++    {
++        try
++        {
++            byte[] ntlmHash = ntlmHash(password);
++            HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
++            // Upper case username, mixed case target!!
++            hmacMD5.update(user.toUpperCase().getBytes("UnicodeLittleUnmarked"));
++            hmacMD5.update(target.getBytes("UnicodeLittleUnmarked"));
++            return hmacMD5.getOutput();
++        }
++        catch (java.io.UnsupportedEncodingException e)
++        {
++            throw new AuthenticationException("Unicode not supported! "+e.getMessage(),e);
++        }
++    }
+ 
+-        // Host offset (always 32).
+-        byte[] hostOff = convertShort(32);
+-        addByte(hostOff[0]);
+-        addByte(hostOff[1]);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++    /**
++     * Creates the LM Response from the given hash and Type 2 challenge.
++     *
++     * @param hash The LM or NTLM Hash.
++     * @param challenge The server challenge from the Type 2 message.
++     *
++     * @return The response (either LM or NTLM, depending on the provided
++     * hash).
++     */
++    private static byte[] lmResponse(byte[] hash, byte[] challenge)
++        throws AuthenticationException
++    {
++        try
++        {
++            byte[] keyBytes = new byte[21];
++            System.arraycopy(hash, 0, keyBytes, 0, 16);
++            Key lowKey = createDESKey(keyBytes, 0);
++            Key middleKey = createDESKey(keyBytes, 7);
++            Key highKey = createDESKey(keyBytes, 14);
++            Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
++            des.init(Cipher.ENCRYPT_MODE, lowKey);
++            byte[] lowResponse = des.doFinal(challenge);
++            des.init(Cipher.ENCRYPT_MODE, middleKey);
++            byte[] middleResponse = des.doFinal(challenge);
++            des.init(Cipher.ENCRYPT_MODE, highKey);
++            byte[] highResponse = des.doFinal(challenge);
++            byte[] lmResponse = new byte[24];
++            System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
++            System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
++            System.arraycopy(highResponse, 0, lmResponse, 16, 8);
++            return lmResponse;
++        }
++        catch (Exception e)
++        {
++            throw new AuthenticationException(e.getMessage(),e);
++        }
++    }
+ 
+-        // Host String.
+-        addBytes(hostBytes);
+-
+-        // Domain String.
+-        addBytes(domainBytes);
+-
+-        return getResponse();
++    /**
++     * Creates the LMv2 Response from the given hash, client data, and
++     * Type 2 challenge.
++     *
++     * @param hash The NTLMv2 Hash.
++     * @param clientData The client data (blob or client challenge).
++     * @param challenge The server challenge from the Type 2 message.
++     *
++     * @return The response (either NTLMv2 or LMv2, depending on the
++     * client data).
++     */
++    private static byte[] lmv2Response(byte[] hash, byte[] challenge,
++        byte[] clientData) throws AuthenticationException
++    {
++        HMACMD5 hmacMD5 = new HMACMD5(hash);
++        hmacMD5.update(challenge);
++        hmacMD5.update(clientData);
++        byte[] mac = hmacMD5.getOutput();
++        byte[] lmv2Response = new byte[mac.length + clientData.length];
++        System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
++        System.arraycopy(clientData, 0, lmv2Response, mac.length,
++            clientData.length);
++        return lmv2Response;
+     }
+ 
+-    /** 
+-     * Extracts the server nonce out of the given message type 2.
+-     * 
+-     * @param message the String containing the base64 encoded message.
+-     * @return an array of 8 bytes that the server sent to be used when
+-     * hashing the password.
++    /**
++     * Creates the NTLMv2 blob from the given target information block and
++     * client challenge.
++     *
++     * @param targetInformation The target information block from the Type 2
++     * message.
++     * @param clientChallenge The random 8-byte client challenge.
++     *
++     * @return The blob, used in the calculation of the NTLMv2 Response.
+      */
+-    public byte[] parseType2Message(String message) {
+-        // Decode the message first.
+-        byte[] msg = Base64.decodeBase64(EncodingUtil.getBytes(message, DEFAULT_CHARSET));
+-        byte[] nonce = new byte[8];
+-        // The nonce is the 8 bytes starting from the byte in position 24.
++    private static byte[] createBlob(byte[] clientChallenge, byte[] targetInformation) {
++        byte[] blobSignature = new byte[] {
++            (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00
++        };
++        byte[] reserved = new byte[] {
++            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
++        };
++        byte[] unknown1 = new byte[] {
++            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
++        };
++        long time = System.currentTimeMillis();
++        time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
++        time *= 10000; // tenths of a microsecond.
++        // convert to little-endian byte array.
++        byte[] timestamp = new byte[8];
+         for (int i = 0; i < 8; i++) {
+-            nonce[i] = msg[i + 24];
++            timestamp[i] = (byte) time;
++            time >>>= 8;
+         }
+-        return nonce;
++        byte[] blob = new byte[blobSignature.length + reserved.length+ timestamp.length + 8 +
++            unknown1.length + targetInformation.length];
++        int offset = 0;
++        System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
++        offset += blobSignature.length;
++        System.arraycopy(reserved, 0, blob, offset, reserved.length);
++        offset += reserved.length;
++        System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
++        offset += timestamp.length;
++        System.arraycopy(clientChallenge, 0, blob, offset,8);
++        offset += 8;
++        System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
++        offset += unknown1.length;
++        System.arraycopy(targetInformation, 0, blob, offset,
++            targetInformation.length);
++        return blob;
+     }
+ 
+     /** 
+-     * Creates the type 3 message using the given server nonce.  The type 3 message includes all the
+-     * information for authentication, host, domain, username and the result of encrypting the
+-     * nonce sent by the server using the user's password as the key.
++     * Creates a DES encryption key from the given key material.
+      *
+-     * @param user The user name.  This should not include the domain name.
+-     * @param password The password.
+-     * @param host The host that is originating the authentication request.
+-     * @param domain The domain to authenticate within.
+-     * @param nonce the 8 byte array the server sent.
+-     * @return The type 3 message.
+-     * @throws AuthenticationException If {@encrypt(byte[],byte[])} fails.
++     * @param bytes A byte array containing the DES key material.
++     * @param offset The offset in the given byte array at which
++     * the 7-byte key material starts.
++     *
++     * @return A DES encryption key created from the key material
++     * starting at the specified offset in the given byte array.
+      */
+-    public String getType3Message(String user, String password,
+-            String host, String domain, byte[] nonce)
+-    throws AuthenticationException {
++    private static Key createDESKey(byte[] bytes, int offset) {
++        byte[] keyBytes = new byte[7];
++        System.arraycopy(bytes, offset, keyBytes, 0, 7);
++        byte[] material = new byte[8];
++        material[0] = keyBytes[0];
++        material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
++        material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
++        material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
++        material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
++        material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
++        material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
++        material[7] = (byte) (keyBytes[6] << 1);
++        oddParity(material);
++        return new SecretKeySpec(material, "DES");
++    }
+ 
+-        int ntRespLen = 0;
+-        int lmRespLen = 24;
+-        domain = domain.toUpperCase(Locale.ENGLISH);
+-        host = host.toUpperCase(Locale.ENGLISH);
+-        user = user.toUpperCase(Locale.ENGLISH);
+-        byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET);
+-        byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET);
+-        byte[] userBytes = EncodingUtil.getBytes(user, credentialCharset);
+-        int domainLen = domainBytes.length;
+-        int hostLen = hostBytes.length;
+-        int userLen = userBytes.length;
+-        int finalLength = 64 + ntRespLen + lmRespLen + domainLen 
+-            + userLen + hostLen;
+-        prepareResponse(finalLength);
+-        byte[] ntlmssp = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET);
+-        addBytes(ntlmssp);
+-        addByte((byte) 0);
+-        addByte((byte) 3);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++    /**
++     * Applies odd parity to the given byte array.
++     *
++     * @param bytes The data whose parity bits are to be adjusted for
++     * odd parity.
++     */
++    private static void oddParity(byte[] bytes) {
++        for (int i = 0; i < bytes.length; i++) {
++            byte b = bytes[i];
++            boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^
++                                    (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^
++                                    (b >>> 1)) & 0x01) == 0;
++            if (needsParity) {
++                bytes[i] |= (byte) 0x01;
++            } else {
++                bytes[i] &= (byte) 0xfe;
++            }
++        }
++    }
++    
++    /** NTLM message generation, base class */
++    protected static class NTLMMessage
++    {
++        /** The current response */
++        private byte[] messageContents = null;
+ 
+-        // LM Resp Length (twice)
+-        addBytes(convertShort(24));
+-        addBytes(convertShort(24));
++        /** The current output position */
++        private int currentOutputPosition = 0;
+ 
+-        // LM Resp Offset
+-        addBytes(convertShort(finalLength - 24));
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++        /** Constructor to use when message contents are not yet known */
++        public NTLMMessage()
++        {
++        }
++	
++        /** Constructor to use when message contents are known */
++        public NTLMMessage(String messageBody, int expectedType)
++            throws AuthenticationException
++        {
++            messageContents = Base64.decodeBase64(EncodingUtil.getBytes(messageBody, DEFAULT_CHARSET));
++            // Look for NTLM message
++            if (messageContents.length < signatureBytes.length)
++                throw new AuthenticationException("NTLM message decoding error - packet too short");
++            int i = 0;
++            while (i < signatureBytes.length)
++            {
++                if (messageContents[i] != signatureBytes[i])
++                    throw new AuthenticationException("NTLM message expected - instead got unrecognized bytes");
++                i++;
++            }
+ 
+-        // NT Resp Length (twice)
+-        addBytes(convertShort(0));
+-        addBytes(convertShort(0));
++            // Check to be sure there's a type 2 message indicator next
++            int type = readULong(signatureBytes.length);
++            if (type != expectedType)
++                throw new AuthenticationException("NTLM type "+Integer.toString(expectedType)+" message expected - instead got type "+Integer.toString(type));
++		
++            currentOutputPosition = messageContents.length;
++        }
+ 
+-        // NT Resp Offset
+-        addBytes(convertShort(finalLength));
+-        addByte((byte) 0);
+-        addByte((byte) 0);
+-
+-        // Domain length (twice)
+-        addBytes(convertShort(domainLen));
+-        addBytes(convertShort(domainLen));
++        /** Get the length of the signature and flags, so calculations can adjust offsets accordingly.
++        */
++        protected int getPreambleLength()
++        {
++            return signatureBytes.length + 4;
++        }
++	
++        /** Get the message length */
++        protected int getMessageLength()
++        {
++            return currentOutputPosition;
++        }
+         
+-        // Domain offset.
+-        addBytes(convertShort(64));
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++        /** Read a byte from a position within the message buffer */
++        protected byte readByte(int position) throws AuthenticationException
++        {
++            if (messageContents.length < position + 1)
++                throw new AuthenticationException("NTLM: Message too short");
++            return messageContents[position];
++        }
+ 
+-        // User Length (twice)
+-        addBytes(convertShort(userLen));
+-        addBytes(convertShort(userLen));
++        /** Read a bunch of bytes from a position in the message buffer */
++        protected void readBytes(byte[] buffer, int position)
++            throws AuthenticationException
++        {
++            if (messageContents.length < position + buffer.length)
++                throw new AuthenticationException("NTLM: Message too short");
++            System.arraycopy(messageContents,position,buffer,0,buffer.length);
++        }
++	
++        /** Read a ushort from a position within the message buffer */
++        protected int readUShort(int position) throws AuthenticationException
++        {
++            return NTLM.readUShort(messageContents,position);
++        }
++	
++        /** Read a ulong from a position within the message buffer */
++        protected int readULong(int position) throws AuthenticationException
++        {
++            return NTLM.readULong(messageContents,position);
++        }
++	
++        /** Read a security buffer from a position within the message buffer */
++        protected byte[] readSecurityBuffer(int position) throws AuthenticationException
++        {
++            return NTLM.readSecurityBuffer(messageContents,position);
++        }
++	
++        /** 
++        * Prepares the object to create a response of the given length.
++        * @param length the maximum length of the response to prepare, not including the type and the signature (which this method adds).
++        */
++        protected void prepareResponse(int maxlength, int messageType) {
++            messageContents = new byte[maxlength];
++            currentOutputPosition = 0;
++            addBytes(signatureBytes);
++            addULong(messageType);
++        }
+ 
+-        // User offset
+-        addBytes(convertShort(64 + domainLen));
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++        /** 
++        * Adds the given byte to the response.
++        * @param b the byte to add.
++        */
++        protected void addByte(byte b) {
++            messageContents[currentOutputPosition] = b;
++            currentOutputPosition++;
++        }
+ 
+-        // Host length (twice)
+-        addBytes(convertShort(hostLen));
+-        addBytes(convertShort(hostLen));
++        /** 
++        * Adds the given bytes to the response.
++        * @param bytes the bytes to add.
++        */
++        protected void addBytes(byte[] bytes) {
++            for (int i = 0; i < bytes.length; i++) {
++                messageContents[currentOutputPosition] = bytes[i];
++                currentOutputPosition++;
++            }
++        }
+ 
+-        // Host offset
+-        addBytes(convertShort(64 + domainLen + userLen));
++        /** Adds a USHORT to the response */
++        protected void addUShort(int value) {
++            addByte((byte) (value & 0xff));
++            addByte((byte) (value >> 8 & 0xff));
++        }
++	
++        /** Adds a ULong to the response */
++        protected void addULong(int value) {
++            addByte((byte) (value & 0xff));
++            addByte((byte) (value >> 8 & 0xff));
++            addByte((byte) (value >> 16 & 0xff));
++            addByte((byte) (value >> 24 & 0xff));
++        }
+ 
+-        for (int i = 0; i < 6; i++) {
+-            addByte((byte) 0);
++        /** 
++        * Returns the response that has been generated after shrinking the array if
++        * required and base64 encodes the response.
++        * @return The response as above.
++        */
++        public String getResponse() {
++            byte[] resp;
++            if (messageContents.length > currentOutputPosition) {
++                byte[] tmp = new byte[currentOutputPosition];
++                for (int i = 0; i < currentOutputPosition; i++) {
++                    tmp[i] = messageContents[i];
++                }
++                resp = tmp;
++            } else {
++                resp = messageContents;
++            }
++            return EncodingUtil.getAsciiString(Base64.encodeBase64(resp));
+         }
+ 
+-        // Message length
+-        addBytes(convertShort(finalLength));
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++    }
++    
++    /** Type 1 message assembly class */
++    public static class Type1Message extends NTLMMessage
++    {
++	protected byte[] hostBytes;
++	protected byte[] domainBytes;
+ 
+-        // Flags
+-        addByte((byte) 6);
+-        addByte((byte) 82);
+-        addByte((byte) 0);
+-        addByte((byte) 0);
++        /** Constructor.  Include the arguments the message will need */
++        public Type1Message(String domain, String host)
++	    throws AuthenticationException
++        {
++            super();
++            try
++            {
++                // Strip off domain name from the host!
++                host = convertHost(host);
++                // Use only the base domain name!
++                domain = convertDomain(domain);
++	
++                hostBytes = host.getBytes("UnicodeLittleUnmarked");
++                domainBytes = domain.toUpperCase().getBytes("UnicodeLittleUnmarked");
++            }
++            catch (java.io.UnsupportedEncodingException e)
++            {
++                throw new AuthenticationException("Unicode unsupported: "+e.getMessage(),e);
++            }
++        }
++	
++        /** Getting the response involves building the message before returning it */
++        public String getResponse()
++        {
++            // Now, build the message.  Calculate its length first, including signature or type.
++            int finalLength = 32 + hostBytes.length + domainBytes.length;
++            
++            // Set up the response.  This will initialize the signature, message type, and flags.
++            prepareResponse(finalLength,1);
++		
++            // Flags.  These are the complete set of flags we support.
++            addULong(FLAG_NEGOTIATE_NTLM |
++                FLAG_NEGOTIATE_NTLM2 |
++                FLAG_NEGOTIATE_SIGN |
++                FLAG_NEGOTIATE_SEAL |
++                /* FLAG_NEGOTIATE_ALWAYS_SIGN |
++                    FLAG_NEGOTIATE_KEY_EXCH | */
++                FLAG_UNICODE_ENCODING |
++                FLAG_TARGET_DESIRED |
++                FLAG_NEGOTIATE_128);
+ 
+-        addBytes(domainBytes);
+-        addBytes(userBytes);
+-        addBytes(hostBytes);
+-        addBytes(hashPassword(password, nonce));
+-        return getResponse();
+-    }
++            // Domain length (two times).
++            addUShort(domainBytes.length);
++            addUShort(domainBytes.length);
+ 
+-    /** 
+-     * Creates the LANManager and NT response for the given password using the
+-     * given nonce.
+-     * @param password the password to create a hash for.
+-     * @param nonce the nonce sent by the server.
+-     * @return The response.
+-     * @throws HttpException If {@link #encrypt(byte[],byte[])} fails.
+-     */
+-    private byte[] hashPassword(String password, byte[] nonce)
+-        throws AuthenticationException {
+-        byte[] passw = EncodingUtil.getBytes(password.toUpperCase(Locale.ENGLISH), credentialCharset);
+-        byte[] lmPw1 = new byte[7];
+-        byte[] lmPw2 = new byte[7];
++            // Domain offset.
++            addULong(hostBytes.length + 32);
+ 
+-        int len = passw.length;
+-        if (len > 7) {
+-            len = 7;
++            // Host length (two times).
++            addUShort(hostBytes.length);
++            addUShort(hostBytes.length);
++
++            // Host offset (always 32).
++            addULong(32);
++		
++            // Host String.
++            addBytes(hostBytes);
++
++            // Domain String.
++            addBytes(domainBytes);
++
++            return super.getResponse();
+         }
+ 
+-        int idx;
+-        for (idx = 0; idx < len; idx++) {
+-            lmPw1[idx] = passw[idx];
++    }
++
++    /** Type 2 message class */
++    public static class Type2Message extends NTLMMessage
++    {
++        protected byte[] challenge;
++        protected String target;
++        protected byte[] targetInfo;
++        protected int flags;
++
++        public Type2Message(String message)
++	    throws AuthenticationException
++        {
++            super(message,2);
++		
++            // Parse out the rest of the info we need from the message
++            // The nonce is the 8 bytes starting from the byte in position 24.
++            challenge = new byte[8];
++            readBytes(challenge,24);
++
++            flags = readULong(20);
++            if ((flags & FLAG_UNICODE_ENCODING) == 0)
++                throw new AuthenticationException("NTLM type 2 message has flags that make no sense: "+Integer.toString(flags));
++            // Do the target!
++            target = null;
++            // The TARGET_DESIRED flag is said to not have understood semantics in Type2 messages, so use the length of the packet to decide
++            // how to proceed instead
++            if (getMessageLength() >= 12 + 8)
++            {
++                byte[] bytes = readSecurityBuffer(12);
++                if (bytes.length != 0)
++                {
++                    try
++                    {
++                        target = new String(bytes,"UnicodeLittleUnmarked");
++                    }
++                    catch (java.io.UnsupportedEncodingException e)
++                    {
++                        throw new AuthenticationException(e.getMessage(),e);
++                    }
++                }
++            }
++
++            // Do the target info!
++            targetInfo = null;
++            // TARGET_DESIRED flag cannot be relied on, so use packet length
++            if (getMessageLength() >= 40 + 8)
++            {
++                byte[] bytes = readSecurityBuffer(40);
++                if (bytes.length != 0)
++                {
++                    targetInfo = bytes;
++                }
++            }
+         }
+-        for (; idx < 7; idx++) {
+-            lmPw1[idx] = (byte) 0;
++	
++        /** Retrieve the challenge */
++        public byte[] getChallenge()
++        {
++            return challenge;
+         }
+-
+-        len = passw.length;
+-        if (len > 14) {
+-            len = 14;
++	
++        /** Retrieve the target */
++        public String getTarget()
++        {
++            return target;
+         }
+-        for (idx = 7; idx < len; idx++) {
+-            lmPw2[idx - 7] = passw[idx];
++	
++        /** Retrieve the target info */
++        public byte[] getTargetInfo()
++        {
++            return targetInfo;
+         }
+-        for (; idx < 14; idx++) {
+-            lmPw2[idx - 7] = (byte) 0;
++	
++        /** Retrieve the response flags */
++        public int getFlags()
++        {
++            return flags;
+         }
+ 
+-        // Create LanManager hashed Password
+-        byte[] magic = {
+-            (byte) 0x4B, (byte) 0x47, (byte) 0x53, (byte) 0x21, 
+-            (byte) 0x40, (byte) 0x23, (byte) 0x24, (byte) 0x25
+-        };
++    }
+ 
+-        byte[] lmHpw1;
+-        lmHpw1 = encrypt(lmPw1, magic);
++    /** Type 3 message assembly class */
++    public static class Type3Message extends NTLMMessage
++    {
++        // Response flags from the type2 message
++        protected int type2Flags;
+ 
+-        byte[] lmHpw2 = encrypt(lmPw2, magic);
++        protected byte[] domainBytes;
++        protected byte[] hostBytes;
++        protected byte[] userBytes;
++	
++        protected byte[] lmResp;
++        protected byte[] ntResp;
+ 
+-        byte[] lmHpw = new byte[21];
+-        for (int i = 0; i < lmHpw1.length; i++) {
+-            lmHpw[i] = lmHpw1[i];
++        /** Constructor.  Pass the arguments we will need */
++        public Type3Message(String domain, String host, String user, String password,
++            byte[] nonce, int type2Flags, String target, byte[] targetInformation)
++	    throws AuthenticationException
++        {
++            // Save the flags
++            this.type2Flags = type2Flags;
++		
++            // Strip off domain name from the host!
++            host = convertHost(host);
++            // Use only the base domain name!
++            domain = convertDomain(domain);
++
++            // Use the new code to calculate the responses, including v2 if that seems warranted.
++            try
++            {
++                if (targetInformation != null && target != null)
++                {
++                    byte[] clientChallenge = makeRandomChallenge();
++                    ntResp = getNTLMv2Response(target,user,password,nonce,clientChallenge,targetInformation);
++                    lmResp = getLMv2Response(target,user,password,nonce,clientChallenge);
++                }
++                else
++                {
++                    if ((type2Flags & FLAG_NEGOTIATE_NTLM2) != 0)
++                    {
++                        // NTLM2 session stuff is requested
++                        byte[] clientChallenge = makeNTLM2RandomChallenge();
++				
++                        ntResp = getNTLM2SessionResponse(password,nonce,clientChallenge);
++                        lmResp = clientChallenge;
++				
++                        // All the other flags we send (signing, sealing, key exchange) are supported, but they don't do anything at all in an
++                        // NTLM2 context!  So we're done at this point.
++                    }
++                    else
++                    {
++                        ntResp = getNTLMResponse(password,nonce);
++                        lmResp = getLMResponse(password,nonce);
++                    }
++                }
++            }
++            catch (AuthenticationException e)
++            {
++                // This likely means we couldn't find the MD4 hash algorithm - fail back to just using LM
++                ntResp = new byte[0];
++                lmResp = getLMResponse(password,nonce);
++            }
++
++            try
++            {
++                domainBytes = domain.toUpperCase().getBytes("UnicodeLittleUnmarked");
++                hostBytes = host.getBytes("UnicodeLittleUnmarked");
++                userBytes = user.getBytes("UnicodeLittleUnmarked");
++            }
++            catch (java.io.UnsupportedEncodingException e)
++            {
++                throw new AuthenticationException("Unicode not supported: "+e.getMessage(),e);
++            }
+         }
+-        for (int i = 0; i < lmHpw2.length; i++) {
+-            lmHpw[i + 8] = lmHpw2[i];
++	
++        /** Assemble the response */
++        public String getResponse()
++        {
++            int ntRespLen = ntResp.length;
++            int lmRespLen = lmResp.length;
++
++            int domainLen = domainBytes.length;
++            int hostLen = hostBytes.length;
++            int userLen = userBytes.length;
++		    
++            // Calculate the layout within the packet
++            int lmRespOffset = 64;
++            int ntRespOffset = lmRespOffset + lmRespLen;
++            int domainOffset = ntRespOffset + ntRespLen;
++            int userOffset = domainOffset + domainLen;
++            int hostOffset = userOffset + userLen;
++            int sessionKeyOffset = hostOffset + hostLen;
++            int finalLength = sessionKeyOffset + 0;
++		
++            // Start the response.  Length includes signature and type
++            prepareResponse(finalLength,3);
++		
++            // LM Resp Length (twice)
++            addUShort(lmRespLen);
++            addUShort(lmRespLen);
++
++            // LM Resp Offset
++            addULong(lmRespOffset);
++
++            // NT Resp Length (twice)
++            addUShort(ntRespLen);
++            addUShort(ntRespLen);
++
++            // NT Resp Offset
++            addULong(ntRespOffset);
++
++            // Domain length (twice)
++            addUShort(domainLen);
++            addUShort(domainLen);
++		
++            // Domain offset.
++            addULong(domainOffset);
++
++            // User Length (twice)
++            addUShort(userLen);
++            addUShort(userLen);
++
++            // User offset
++            addULong(userOffset);
++
++            // Host length (twice)
++            addUShort(hostLen);
++            addUShort(hostLen);
++
++            // Host offset
++            addULong(hostOffset);
++		
++            // 4 bytes of zeros - not sure what this is
++            addULong(0);
++
++            // Message length
++            addULong(finalLength);
++		
++            // Flags.  Currently:  NEGOTIATE_NTLM + UNICODE_ENCODING + TARGET_DESIRED + NEGOTIATE_128
++            addULong(FLAG_NEGOTIATE_NTLM | FLAG_UNICODE_ENCODING | FLAG_TARGET_DESIRED | FLAG_NEGOTIATE_128 |
++                (type2Flags & FLAG_NEGOTIATE_NTLM2) |
++                (type2Flags & FLAG_NEGOTIATE_SIGN) |
++                (type2Flags & FLAG_NEGOTIATE_SEAL) |
++                (type2Flags & FLAG_NEGOTIATE_KEY_EXCH) |
++                (type2Flags & FLAG_NEGOTIATE_ALWAYS_SIGN));
++
++            // Add the actual data
++            addBytes(lmResp);
++            addBytes(ntResp);
++            addBytes(domainBytes);
++            addBytes(userBytes);
++            addBytes(hostBytes);
++
++            return super.getResponse();
+         }
+-        for (int i = 0; i < 5; i++) {
+-            lmHpw[i + 16] = (byte) 0;
+-        }
++    }
+ 
+-        // Create the responses.
+-        byte[] lmResp = new byte[24];
+-        calcResp(lmHpw, nonce, lmResp);
++    protected static void writeULong(byte[] buffer, int value, int offset)
++    {
++        buffer[offset] = (byte) (value & 0xff);
++        buffer[offset+1] = (byte) (value >> 8 & 0xff);
++        buffer[offset+2] = (byte) (value >> 16 & 0xff);
++        buffer[offset+3] = (byte) (value >> 24 & 0xff);
++    }
++    
++    protected static int F(int x, int y, int z) {
++        return((x & y) | (~x & z));
++    }
++    protected static int G(int x, int y, int z) {
++        return((x & y) | (x & z) | (y & z));
++    }
++    protected static int H(int x, int y, int z) {
++        return(x ^ y ^ z);
++    }
+ 
+-        return lmResp;
++    protected static int rotintlft(int val, int numbits) {
++        return((val << numbits) | (val >>> (32 - numbits)));
+     }
+ 
+-    /** 
+-     * Takes a 21 byte array and treats it as 3 56-bit DES keys.  The 8 byte
+-     * plaintext is encrypted with each key and the resulting 24 bytes are
+-     * stored in the results array.
+-     * 
+-     * @param keys The keys.
+-     * @param plaintext The plain text to encrypt.
+-     * @param results Where the results are stored.
+-     * @throws AuthenticationException If {@link #encrypt(byte[],byte[])} fails.
+-     */
+-    private void calcResp(byte[] keys, byte[] plaintext, byte[] results)
+-        throws AuthenticationException {
+-        byte[] keys1 = new byte[7];
+-        byte[] keys2 = new byte[7];
+-        byte[] keys3 = new byte[7];
+-        for (int i = 0; i < 7; i++) {
+-            keys1[i] = keys[i];
++    /** Cryptography support - MD4.
++    * The following class was based loosely on the RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
++    * Code correctness was verified by looking at MD4.java from the jcifs library (http://jcifs.samba.org).
++    * It was massaged extensively to the final form found here by Karl Wright (kwright@metacarta.com).
++    */
++    protected static class MD4
++    {
++        protected int A = 0x67452301;
++        protected int B = 0xefcdab89;
++        protected int C = 0x98badcfe;
++        protected int D = 0x10325476;
++        protected long count = 0L;
++        protected byte[] dataBuffer = new byte[64];
++	
++        public MD4()
++        {
+         }
++	
++        public void update(byte[] input)
++        {
++            // We always deal with 512 bits at a time.  Correspondingly, there is a buffer 64 bytes long that we write data into until it gets full.
++            int curBufferPos = (int)(count & 63L);
++            int inputIndex = 0;
++            while (input.length - inputIndex + curBufferPos >= dataBuffer.length)
++            {
++                // We have enough data to do the next step.  Do a partial copy and a transform, updating inputIndex and curBufferPos accordingly
++                int transferAmt = dataBuffer.length - curBufferPos;
++                System.arraycopy(input,inputIndex,dataBuffer,curBufferPos,transferAmt);
++                count += transferAmt;
++                curBufferPos = 0;
++                inputIndex += transferAmt;
++                processBuffer();
++            }
++		
++            // If there's anything left, copy it into the buffer and leave it.  We know there's not enough left to process.
++            if (inputIndex < input.length)
++            {
++                int transferAmt = input.length - inputIndex;
++                System.arraycopy(input,inputIndex,dataBuffer,curBufferPos,transferAmt);
++                count += transferAmt;
++                curBufferPos += transferAmt;
++            }
++        }
++	
++        public byte[] getOutput()
++        {
++            // Feed pad/length data into engine.  This must round out the input to a multiple of 512 bits.
++            int bufferIndex = (int)(count & 63L);
++            int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
++            byte[] postBytes = new byte[padLen + 8];
++            // Leading 0x80, specified amount of zero padding, then length in bits.
++            postBytes[0] = (byte)0x80;
++            // Fill out the last 8 bytes with the length
++            for (int i = 0; i < 8; i++)
++            {
++                postBytes[padLen + i] = (byte)((count * 8) >>> (8 * i));
++            }
++		
++            // Update the engine
++            update(postBytes);
+ 
+-        for (int i = 0; i < 7; i++) {
+-            keys2[i] = keys[i + 7];
++            // Calculate final result
++            byte[] result = new byte[16];
++            writeULong(result,A,0);
++            writeULong(result,B,4);
++            writeULong(result,C,8);
++            writeULong(result,D,12);
++            return result;
+         }
+ 
+-        for (int i = 0; i < 7; i++) {
+-            keys3[i] = keys[i + 14];
++        protected void processBuffer()
++        {
++            // Convert current buffer to 16 ulongs
++            int[] d = new int[16];
++
++            for (int i = 0; i < 16; i++)
++            {
++                d[i] = (dataBuffer[i*4] & 0xff) + ((dataBuffer[i*4+1] & 0xff) << 8) + 
++                    ((dataBuffer[i*4+2] & 0xff) << 16) + ((dataBuffer[i*4+3] & 0xff) << 24);
++            }
++		
++            // Do a round of processing
++            int AA = A; int BB = B; int CC = C; int DD = D;
++            round1(d);
++            round2(d);
++            round3(d);
++            A += AA; B+= BB; C+= CC; D+= DD;
++
+         }
+-        byte[] results1 = encrypt(keys1, plaintext);
+ 
+-        byte[] results2 = encrypt(keys2, plaintext);
++        protected void round1(int[] d) {
++            A = rotintlft((A + F(B, C, D) + d[0]), 3);
++            D = rotintlft((D + F(A, B, C) + d[1]), 7);
++            C = rotintlft((C + F(D, A, B) + d[2]), 11);
++            B = rotintlft((B + F(C, D, A) + d[3]), 19);
+ 
+-        byte[] results3 = encrypt(keys3, plaintext);
++            A = rotintlft((A + F(B, C, D) + d[4]), 3);
++            D = rotintlft((D + F(A, B, C) + d[5]), 7);
++            C = rotintlft((C + F(D, A, B) + d[6]), 11);
++            B = rotintlft((B + F(C, D, A) + d[7]), 19);
+ 
+-        for (int i = 0; i < 8; i++) {
+-            results[i] = results1[i];
++            A = rotintlft((A + F(B, C, D) + d[8]), 3);
++            D = rotintlft((D + F(A, B, C) + d[9]), 7);
++            C = rotintlft((C + F(D, A, B) + d[10]), 11);
++            B = rotintlft((B + F(C, D, A) + d[11]), 19);
++
++            A = rotintlft((A + F(B, C, D) + d[12]), 3);
++            D = rotintlft((D + F(A, B, C) + d[13]), 7);
++            C = rotintlft((C + F(D, A, B) + d[14]), 11);
++            B = rotintlft((B + F(C, D, A) + d[15]), 19);
++	}
++	    
++        protected void round2(int[] d) {
++            A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
++            D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
++            C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
++            B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
++
++            A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
++            D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
++            C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
++            B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
++
++            A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
++            D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
++            C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
++            B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
++
++            A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
++            D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
++            C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
++            B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
++
+         }
+-        for (int i = 0; i < 8; i++) {
+-            results[i + 8] = results2[i];
++
++        protected void round3(int[] d) {
++            A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
++            D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
++            C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
++            B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
++
++            A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
++            D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
++            C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
++            B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
++
++            A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
++            D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
++            C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
++            B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
++
++            A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
++            D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
++            C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
++            B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
++
+         }
+-        for (int i = 0; i < 8; i++) {
+-            results[i + 16] = results3[i];
+-        }
++
+     }
+ 
+-    /** 
+-     * Converts a given number to a two byte array in little endian order.
+-     * @param num the number to convert.
+-     * @return The byte representation of <i>num</i> in little endian order.
+-     */
+-    private byte[] convertShort(int num) {
+-        byte[] val = new byte[2];
+-        String hex = Integer.toString(num, 16);
+-        while (hex.length() < 4) {
+-            hex = "0" + hex;
++    /** Cryptography support - HMACMD5 - algorithmically based on various web resources by Karl Wright */
++    protected static class HMACMD5
++    {
++        protected byte[] ipad;
++        protected byte[] opad;
++        protected MessageDigest md5;
++
++        public HMACMD5(byte[] key)
++            throws AuthenticationException
++        {
++            try
++            {
++                md5 = MessageDigest.getInstance("MD5");
++            }
++            catch (Exception ex)
++            {
++                // Umm, the algorithm doesn't exist - throw an AuthenticationException!
++                throw new AuthenticationException("Error getting md5 message digest implementation: "+ex.getMessage(),ex);
++            }
++
++            // Initialize the pad buffers with the key
++            ipad = new byte[64];
++            opad = new byte[64];
++
++            int keyLength = key.length;
++            if (keyLength > 64)
++            {
++                // Use MD5 of the key instead, as described in RFC 2104
++                md5.update(key);
++                key = md5.digest();
++                keyLength = key.length;
++            }
++            int i = 0;
++            while (i < keyLength)
++            {
++                ipad[i] = (byte) (key[i] ^ (byte)0x36);
++                opad[i] = (byte) (key[i] ^ (byte)0x5c);
++                i++;
++            }
++            while (i < 64)
++            {
++                ipad[i] = (byte)0x36;
++                opad[i] = (byte)0x5c;
++                i++;
++            }
++	    
++            // Very important: update the digest with the ipad buffer
++            md5.reset();
++            md5.update(ipad);
++
+         }
+-        String low = hex.substring(2, 4);
+-        String high = hex.substring(0, 2);
+-
+-        val[0] = (byte) Integer.parseInt(low, 16);
+-        val[1] = (byte) Integer.parseInt(high, 16);
+-        return val;
++	
++        /** Grab the current digest.  This is the "answer". */
++        public byte[] getOutput()
++        {
++            byte[] digest = md5.digest();
++            md5.update(opad);
++            return md5.digest(digest);
++        }
++	
++        /** Update by adding a complete array */
++        public void update(byte[] input)
++        {
++            md5.update(input);
++        }
++	
++        /** Update the algorithm */
++        public void update(byte[] input, int offset, int length)
++        {
++            md5.update(input,offset,length);
++        }
++	
+     }
+     
+-    /**
+-     * @return Returns the credentialCharset.
+-     */
+-    public String getCredentialCharset() {
+-        return credentialCharset;
++    /* Run test suite */
++    public static void main(String[] args)
++        throws Exception
++    {
++        // MD4 test suite:
++        checkMD4("","31d6cfe0d16ae931b73c59d7e0c089c0");
++        checkMD4("a","bde52cb31de33e46245e05fbdbd6fb24");
++        checkMD4("abc","a448017aaf21d8525fc10ae87aa6729d");
++        checkMD4("message digest","d9130a8164549fe818874806e1c7014b");
++        checkMD4("abcdefghijklmnopqrstuvwxyz","d79e1c308aa5bbcdeea8ed63df412da9");
++        checkMD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
++            "043f8582f241db351ce627e153e7f0e4");
++        checkMD4("12345678901234567890123456789012345678901234567890123456789012345678901234567890",
++            "e33b4ddc9c38f2199c3e7b164fcc0536");
++	    
++        System.out.println("Tests pass");
+     }
+-
+-    /**
+-     * @param credentialCharset The credentialCharset to set.
+-     */
+-    public void setCredentialCharset(String credentialCharset) {
+-        this.credentialCharset = credentialCharset;
++   
++    /* Test suite helper */
++    protected static byte checkToNibble(char c)
++    {
++        if (c >= 'a' && c <= 'f')
++            return (byte)(c - 'a' + 0x0a);
++        return (byte)(c - '0');
+     }
++    
++    /*Test suite helper */
++    protected static byte[] checkToBytes(String hex)
++    {
++        byte[] rval = new byte[hex.length()/2];
++        int i = 0;
++        while (i < rval.length)
++        {
++            rval[i] = (byte)((checkToNibble(hex.charAt(i*2)) << 4) | (checkToNibble(hex.charAt(i*2+1))));
++            i++;
++        }
++        return rval;
++    }
++    
++    /* Test suite MD4 helper */
++    protected static void checkMD4(String input, String hexOutput)
++        throws Exception
++    {
++        MD4 md4;
++        md4 = new MD4 ();
++        md4.update(input.getBytes("ASCII"));
++        byte[] answer = md4.getOutput();
++        byte[] correctAnswer = checkToBytes(hexOutput);
++        if (answer.length != correctAnswer.length)
++            throw new Exception("Answer length disagrees for MD4('"+input+"')");
++        int i = 0;
++        while (i < answer.length)
++        {
++            if (answer[i] != correctAnswer[i])
++                throw new Exception("Answer value for MD4('"+input+"') disagrees at position "+Integer.toString(i));
++            i++;
++        }
++    }
+ 
+ }
+Index: src/java/org/apache/commons/httpclient/cookie/CookieSpecMediumSecurity.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/cookie/CookieSpecMediumSecurity.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 0)
++++ src/java/org/apache/commons/httpclient/cookie/CookieSpecMediumSecurity.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(revision 1308601)
+@@ -0,0 +1,153 @@
++/*
++ * $HeadURL: https://svn.apache.org/repos/asf/incubator/lcf/upstream/commons-httpclient-3x/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java $
++ * $Revision: 653067 $
++ * $Date: 2008-05-03 08:42:39 -0400 (Sat, 03 May 2008) $
++ *
++ * ====================================================================
++ *
++ *  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.
++ * ====================================================================
++ *
++ * This software consists of voluntary contributions made by many
++ * individuals on behalf of the Apache Software Foundation.  For more
++ * information on the Apache Software Foundation, please see
++ * <http://www.apache.org/>.
++ *
++ */ 
++
++package org.apache.commons.httpclient.cookie;
++
++import java.util.Collection;
++import java.util.Date;
++import java.util.LinkedList;
++import java.util.List;
++import java.util.Locale;
++
++import org.apache.commons.httpclient.Cookie;
++import org.apache.commons.httpclient.Header;
++import org.apache.commons.httpclient.HeaderElement;
++import org.apache.commons.httpclient.NameValuePair;
++import org.apache.commons.httpclient.util.DateParseException;
++import org.apache.commons.httpclient.util.DateUtil;
++import org.apache.commons.logging.Log;
++import org.apache.commons.logging.LogFactory;
++
++/**
++ * 
++ * Cookie management functions shared by all specification.
++ *
++ * @author  B.C. Holmes
++ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
++ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
++ * @author Rod Waldhoff
++ * @author dIon Gillard
++ * @author Sean C. Sullivan
++ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
++ * @author Marc A. Saegesser
++ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
++ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
++ * 
++ * @since 2.0 
++ */
++public class CookieSpecMediumSecurity extends CookieSpecBase {
++    
++    /** Log object */
++    protected static final Log LOG = LogFactory.getLog(CookieSpecMediumSecurity.class);
++
++    /** Default constructor */
++    public CookieSpecMediumSecurity() {
++        super();
++    }
++
++	/**
++      * Performs most common {@link Cookie} validation
++      *
++      * @param host the host from which the {@link Cookie} was received
++      * @param port the port from which the {@link Cookie} was received
++      * @param path the path from which the {@link Cookie} was received
++      * @param secure <tt>true</tt> when the {@link Cookie} was received using a
++      * secure connection
++      * @param cookie The cookie to validate.
++      * @throws MalformedCookieException if an exception occurs during
++      * validation
++      */
++    
++    public void validate(String host, int port, String path, 
++        boolean secure, final Cookie cookie) 
++        throws MalformedCookieException {
++            
++        LOG.trace("enter CookieSpecMediumSecurity.validate("
++            + "String, port, path, boolean, Cookie)");
++        if (host == null) {
++            throw new IllegalArgumentException(
++                "Host of origin may not be null");
++        }
++        if (host.trim().equals("")) {
++            throw new IllegalArgumentException(
++                "Host of origin may not be blank");
++        }
++        if (port < 0) {
++            throw new IllegalArgumentException("Invalid port: " + port);
++        }
++        if (path == null) {
++            throw new IllegalArgumentException(
++                "Path of origin may not be null.");
++        }
++        if (path.trim().equals("")) {
++            path = PATH_DELIM;
++        }
++        host = host.toLowerCase(Locale.ENGLISH);
++        // check version
++        if (cookie.getVersion() < 0) {
++            throw new MalformedCookieException ("Illegal version number " 
++                + cookie.getValue());
++        }
++
++        // security check... we musn't allow the server to give us an
++        // invalid domain scope
++
++        // Validate the cookies domain attribute.  NOTE:  Domains without 
++        // any dots are allowed to support hosts on private LANs that don't 
++        // have DNS names.  Since they have no dots, to domain-match the 
++        // request-host and domain must be identical for the cookie to sent 
++        // back to the origin-server.
++        if (host.indexOf(".") >= 0) {
++            // Not required to have at least two dots.  RFC 2965.
++            // A Set-Cookie2 with Domain=ajax.com will be accepted.
++
++            // domain must match host
++            if (!host.endsWith(cookie.getDomain())) {
++                String s = cookie.getDomain();
++                if (s.startsWith(".")) {
++                    s = s.substring(1, s.length());
++                }
++                if (!host.equals(s)) { 
++                    throw new MalformedCookieException(
++                        "Illegal domain attribute \"" + cookie.getDomain() 
++                        + "\". Domain of origin: \"" + host + "\"");
++                }
++            }
++        } else {
++            if (!host.equals(cookie.getDomain())) {
++                throw new MalformedCookieException(
++                    "Illegal domain attribute \"" + cookie.getDomain() 
++                    + "\". Domain of origin: \"" + host + "\"");
++            }
++        }
++
++    }
++
++}
+
+Property changes on: src/java/org/apache/commons/httpclient/cookie/CookieSpecMediumSecurity.java
+___________________________________________________________________
+Added: svn:keywords
+## -0,0 +1 ##
++Id
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+Index: src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 915934)
++++ src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(working copy)
+@@ -69,6 +69,11 @@
+      */
+     public static final String BROWSER_COMPATIBILITY = "compatibility";
+     
++    /**
++     * Medium-security browser compatibility setting.
++     */
++    public static final String BROWSER_COMPATIBILITY_MEDIUM_SECURITY = "compatibilitymediumsecurity";
++    
+     /** 
+      * The Netscape cookie draft compliant policy. 
+      * 
+@@ -109,6 +114,7 @@
+         CookiePolicy.registerCookieSpec(RFC_2109, RFC2109Spec.class);
+         CookiePolicy.registerCookieSpec(RFC_2965, RFC2965Spec.class);
+         CookiePolicy.registerCookieSpec(BROWSER_COMPATIBILITY, CookieSpecBase.class);
++        CookiePolicy.registerCookieSpec(BROWSER_COMPATIBILITY_MEDIUM_SECURITY, CookieSpecMediumSecurity.class);
+         CookiePolicy.registerCookieSpec(NETSCAPE, NetscapeDraftSpec.class);
+         CookiePolicy.registerCookieSpec(IGNORE_COOKIES, IgnoreCookiesSpec.class);
+     }
+Index: src/java/org/apache/commons/httpclient/params/HttpClientParams.java
+===================================================================
+--- src/java/org/apache/commons/httpclient/params/HttpClientParams.java	(.../httpcomponents/oac.hc3x/trunk)	(revision 915934)
++++ src/java/org/apache/commons/httpclient/params/HttpClientParams.java	(.../incubator/lcf/upstream/commons-httpclient-3.1-mcf/trunk)	(working copy)
+@@ -81,6 +81,14 @@
+      */
+     public static final String REJECT_RELATIVE_REDIRECT = "http.protocol.reject-relative-redirect"; 
+ 
++    /**
++     * Supplies a ProtocolFactory object, for custom protocol support even across redirections.
++     * <p>
++     * This parameter expects a value of type {@link ProtocolFactory}.
++     * </p>
++     */
++    public static final String PROTOCOL_FACTORY = "http.protocol.factory"; 
++
+     /** 
+      * Defines the maximum number of redirects to be followed. 
+      * The limit on number of redirects is intended to prevent infinite loops. 



Mime
View raw message