You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ed...@apache.org on 2008/08/13 02:05:43 UTC

svn commit: r685389 [2/4] - in /mina/trunk/core/src/main/java/org/apache/mina/proxy: ./ event/ filter/ handlers/ handlers/http/ handlers/http/basic/ handlers/http/digest/ handlers/http/ntlm/ handlers/socks/ session/ utils/

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,219 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http;
+
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.mina.proxy.ProxyAuthException;
+import org.apache.mina.proxy.handlers.ProxyRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HttpProxyRequest.java - Wrapper class for HTTP requests.
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @author James Furness <a href="mailto:james.furness@lehman.com">james.furness@lehman.com</a> 
+ * @version $Id: $
+ */
+public class HttpProxyRequest extends ProxyRequest {
+    private final static Logger logger = LoggerFactory
+            .getLogger(HttpProxyRequest.class);
+
+    public final String httpVerb;
+
+    public final String httpURI;
+
+    private String httpVersion;
+
+    private String host;
+
+    private Map<String, List<String>> headers;
+
+    private transient Map<String, String> properties;
+
+    public HttpProxyRequest(final InetSocketAddress endpointAddress) {
+        this(HttpProxyConstants.CONNECT, endpointAddress.getHostName() + ":"
+                + endpointAddress.getPort(), HttpProxyConstants.HTTP_1_0, null);
+    }
+
+    public HttpProxyRequest(final InetSocketAddress endpointAddress,
+            final String httpVersion) {
+        this(HttpProxyConstants.CONNECT, endpointAddress.getHostName() + ":"
+                + endpointAddress.getPort(), httpVersion, null);
+    }
+
+    public HttpProxyRequest(final InetSocketAddress endpointAddress,
+            final String httpVersion, final Map<String, List<String>> headers) {
+        this(HttpProxyConstants.CONNECT, endpointAddress.getHostName() + ":"
+                + endpointAddress.getPort(), httpVersion, headers);
+    }
+
+    public HttpProxyRequest(final String httpURI) {
+        this(HttpProxyConstants.GET, httpURI, HttpProxyConstants.HTTP_1_0, null);
+    }
+
+    public HttpProxyRequest(final String httpURI, final String httpVersion) {
+        this(HttpProxyConstants.GET, httpURI, httpVersion, null);
+    }
+
+    public HttpProxyRequest(final String httpVerb, final String httpURI,
+            final String httpVersion) {
+        this(httpVerb, httpURI, httpVersion, null);
+    }
+
+    public HttpProxyRequest(final String httpVerb, final String httpURI,
+            final String httpVersion, final Map<String, List<String>> headers) {
+        this.httpVerb = httpVerb;
+        this.httpURI = httpURI;
+        this.httpVersion = httpVersion;
+        this.headers = headers;
+    }
+
+    /**
+     * The request verb.
+     */
+    public final String getHttpVerb() {
+        return httpVerb;
+    }
+
+    /**
+     * The HTTP version.
+     */
+    public String getHttpVersion() {
+        return httpVersion;
+    }
+
+    /**
+     * Sets the HTTP version.
+     */
+    public void setHttpVersion(String httpVersion) {
+        this.httpVersion = httpVersion;
+    }
+
+    /**
+     * Returns the host to which we are connecting.
+     */
+    public synchronized final String getHost() {
+        if (host == null) {
+            if (getEndpointAddress() != null) {
+                host = getEndpointAddress().getHostName();
+            }
+
+            if (host == null && httpURI != null) {
+                try {
+                    host = (new URL(httpURI)).getHost();
+                } catch (MalformedURLException e) {
+                    logger.debug("Malformed URL", e);
+                }
+            }
+        }
+
+        return host;
+    }
+
+    /**
+     * The request URI.
+     */
+    public final String getHttpURI() {
+        return httpURI;
+    }
+
+    /**
+     * HTTP headers.
+     */
+    public final Map<String, List<String>> getHeaders() {
+        return headers;
+    }
+
+    /**
+     * Set the HTTP headers.
+     */
+    public final void setHeaders(Map<String, List<String>> headers) {
+        this.headers = headers;
+    }
+
+    /**
+     * Get the additional properties.
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * Set the additional properties.
+     */
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
+
+    /**
+     * Check if the property is set otherwise throw a{@link ProxyAuthException}.
+     */
+    public void checkRequiredProperty(String propertyName)
+            throws ProxyAuthException {
+        if (properties.get(propertyName) == null) {
+            StringBuilder sb = new StringBuilder("'");
+            sb.append(propertyName).append(
+                    "' property not provided in the request properties");
+            throw new ProxyAuthException(sb.toString());
+        }
+    }
+
+    /**
+     * Returns the string representation of the HTTP request .
+     */
+    public String toHttpString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(getHttpVerb()).append(' ').append(getHttpURI()).append(' ')
+                .append(getHttpVersion()).append(HttpProxyConstants.CRLF);
+
+        boolean hostHeaderFound = false;
+
+        if (getHeaders() != null) {
+            for (Map.Entry<String, List<String>> header : getHeaders()
+                    .entrySet()) {
+                if (!hostHeaderFound) {
+                    hostHeaderFound = header.getKey().equalsIgnoreCase("host");
+                }
+
+                for (String value : header.getValue()) {
+                    sb.append(header.getKey()).append(": ").append(value)
+                            .append(HttpProxyConstants.CRLF);
+                }
+            }
+
+            if (!hostHeaderFound
+                    && getHttpVersion() == HttpProxyConstants.HTTP_1_1) {
+                sb.append("Host: ").append(getHost()).append(
+                        HttpProxyConstants.CRLF);
+            }
+        }
+
+        sb.append(HttpProxyConstants.CRLF);
+
+        return sb.toString();
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,96 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * HttpProxyResponse.java - Wrapper class for HTTP requests.
+ * 
+ * @author James Furness <a href="mailto:james.furness@lehman.com">james.furness@lehman.com</a>
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class HttpProxyResponse {
+    public final String httpVersion;
+
+    public final String statusLine;
+
+    public final int statusCode;
+
+    public final Map<String, List<String>> headers;
+
+    public String body;
+
+    protected HttpProxyResponse(final String httpVersion,
+            final String statusLine, final Map<String, List<String>> headers) {
+        this.httpVersion = httpVersion;
+        this.statusLine = statusLine;
+
+        this.statusCode = statusLine.charAt(0) == ' ' ? Integer
+                .parseInt(statusLine.substring(1, 4)) : Integer
+                .parseInt(statusLine.substring(0, 3));
+
+        this.headers = headers;
+    }
+
+    /**
+     * The HTTP version.
+     */
+    public final String getHttpVersion() {
+        return httpVersion;
+    }
+
+    /**
+     * The HTTP status code.
+     */
+    public final int getStatusCode() {
+        return statusCode;
+    }
+
+    /**
+     * The HTTP status line.
+     */
+    public final String getStatusLine() {
+        return statusLine;
+    }
+
+    /**
+     * The HTTP entity body.
+     */
+    public String getBody() {
+        return body;
+    }
+
+    /**
+     * Sets the HTTP entity body.
+     */
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+    /**
+     * HTTP headers.
+     */
+    public final Map<String, List<String>> getHeaders() {
+        return headers;
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,212 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.ProxyAuthException;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.StringUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HttpSmartProxyHandler.java - HTTP proxy handler that automatically handles forwarding request 
+ * to the appropriate authentication mechanism logic handler.
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class HttpSmartProxyHandler extends AbstractHttpLogicHandler {
+    private final static Logger logger = LoggerFactory
+            .getLogger(HttpSmartProxyHandler.class);
+
+    /**
+     * Has the HTTP proxy request been sent ?
+     */
+    private boolean requestSent = false;
+
+    /**
+     * The automatically selected http authentication logic handler. 
+     */
+    private AbstractAuthLogicHandler authHandler;
+
+    public HttpSmartProxyHandler(final ProxyIoSession proxyIoSession) {
+        super(proxyIoSession);
+    }
+
+    /**
+     * Perform any handshaking processing.
+     */
+    public void doHandshake(final NextFilter nextFilter)
+            throws ProxyAuthException {
+        logger.debug(" doHandshake()");
+
+        if (authHandler != null) {
+            authHandler.doHandshake(nextFilter);
+        } else {
+            if (requestSent) {
+                throw new ProxyAuthException(
+                        "Authentication request already sent");
+            }
+
+            logger.debug("  sending HTTP request");
+
+            // Send request
+            HttpProxyRequest req = (HttpProxyRequest) getProxyIoSession()
+                    .getRequest();
+            Map<String, List<String>> headers = req.getHeaders() != null ? req
+                    .getHeaders() : new HashMap<String, List<String>>();
+
+            StringUtilities.addValueToHeader(headers, "Keep-Alive",
+                    HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
+            StringUtilities.addValueToHeader(headers, "Proxy-Connection",
+                    "keep-Alive", true);
+            req.setHeaders(headers);
+
+            writeRequest(nextFilter, req);
+            requestSent = true;
+        }
+    }
+
+    /**
+     * Automatic selection of the authentication algorithm. If <code>preferedOrder</code> is set then
+     * algorithms are selected from the list order otherwise the algorithm tries to select the most 
+     * secured algorithm available first.
+     */
+    private void autoSelectAuthHandler(final HttpProxyResponse response)
+            throws ProxyAuthException {
+        // Get the Proxy-Authenticate header
+        List<String> values = response.getHeaders().get("Proxy-Authenticate");
+
+        if (values == null || values.size() == 0) {
+            authHandler = HttpAuthenticationMethods.NO_AUTH
+                    .getNewHandler(getProxyIoSession());
+
+        } else if (getProxyIoSession().getPreferedOrder() == null) {
+            for (String proxyAuthHeader : values) {
+                proxyAuthHeader = proxyAuthHeader.toLowerCase();
+
+                try {
+                    // Test which auth mechanism to use. First found is the first used that's why we test
+                    // in a decreasing security quality order.
+                    if (proxyAuthHeader.contains("ntlm")) {
+                        authHandler = HttpAuthenticationMethods.NTLM
+                                .getNewHandler(getProxyIoSession());
+                        break;
+                    } else if (proxyAuthHeader.contains("digest")) {
+                        authHandler = HttpAuthenticationMethods.DIGEST
+                                .getNewHandler(getProxyIoSession());
+                        break;
+                    } else if (proxyAuthHeader.contains("basic")) {
+                        authHandler = HttpAuthenticationMethods.BASIC
+                                .getNewHandler(getProxyIoSession());
+                        break;
+                    }
+                } catch (Exception ex) {
+                    logger.debug("Following exception occured:", ex);
+                }
+            }
+
+            if (authHandler == null) {
+                authHandler = HttpAuthenticationMethods.NO_AUTH
+                        .getNewHandler(getProxyIoSession());
+            }
+
+        } else {
+            for (HttpAuthenticationMethods method : getProxyIoSession()
+                    .getPreferedOrder()) {
+                if (authHandler != null) {
+                    break;
+                }
+
+                if (method == HttpAuthenticationMethods.NO_AUTH) {
+                    authHandler = HttpAuthenticationMethods.NO_AUTH
+                            .getNewHandler(getProxyIoSession());
+                    break;
+                }
+
+                for (String proxyAuthHeader : values) {
+                    proxyAuthHeader = proxyAuthHeader.toLowerCase();
+
+                    try {
+                        // test which auth mechanism to use
+                        if (proxyAuthHeader.contains("basic")
+                                && method == HttpAuthenticationMethods.BASIC) {
+                            authHandler = HttpAuthenticationMethods.BASIC
+                                    .getNewHandler(getProxyIoSession());
+                            break;
+                        } else if (proxyAuthHeader.contains("digest")
+                                && method == HttpAuthenticationMethods.DIGEST) {
+                            authHandler = HttpAuthenticationMethods.DIGEST
+                                    .getNewHandler(getProxyIoSession());
+                            break;
+                        } else if (proxyAuthHeader.contains("ntlm")
+                                && method == HttpAuthenticationMethods.NTLM) {
+                            authHandler = HttpAuthenticationMethods.NTLM
+                                    .getNewHandler(getProxyIoSession());
+                            break;
+                        }
+                    } catch (Exception ex) {
+                        logger.debug("Following exception occured:", ex);
+                    }
+                }
+            }
+
+        }
+
+        if (authHandler == null) {
+            throw new ProxyAuthException(
+                    "Unknown authentication mechanism(s): " + values);
+        }
+    }
+
+    /**
+     * Handle a HTTP response from the proxy server.
+     * 
+     * @param response The response.
+     */
+    @Override
+    public void handleResponse(final HttpProxyResponse response)
+            throws ProxyAuthException {
+        if (!isHandshakeComplete()
+                && ("close".equalsIgnoreCase(StringUtilities
+                        .getSingleValuedHeader(response.getHeaders(),
+                                "Proxy-Connection")) || "close"
+                        .equalsIgnoreCase(StringUtilities
+                                .getSingleValuedHeader(response.getHeaders(),
+                                        "Connection")))) {
+            getProxyIoSession().setReconnectionNeeded(true);
+        }
+
+        if (response.getStatusCode() == 407) {
+            if (authHandler == null) {
+                autoSelectAuthHandler(response);
+            }
+            authHandler.handleResponse(response);
+        } else {
+            throw new ProxyAuthException("Received error response code ("
+                    + response.getStatusLine() + ").");
+        }
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,111 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.basic;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.ProxyAuthException;
+import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
+import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
+import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
+import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.StringUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HttpBasicAuthLogicHandler.java - HTTP Basic authentication mechanism logic handler.
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class HttpBasicAuthLogicHandler extends AbstractAuthLogicHandler {
+    private final static Logger logger = LoggerFactory
+            .getLogger(HttpBasicAuthLogicHandler.class);
+
+    public HttpBasicAuthLogicHandler(final ProxyIoSession proxyIoSession)
+            throws ProxyAuthException {
+        super(proxyIoSession);
+
+        if (request == null || !(request instanceof HttpProxyRequest)) {
+            throw new IllegalArgumentException(
+                    "request parameter should be a non null HttpProxyRequest instance");
+        }
+
+        HttpProxyRequest req = (HttpProxyRequest) request;
+        req.checkRequiredProperty(HttpProxyConstants.USER_PROPERTY);
+        req.checkRequiredProperty(HttpProxyConstants.PWD_PROPERTY);
+    }
+
+    @Override
+    public void doHandshake(final NextFilter nextFilter)
+            throws ProxyAuthException {
+        logger.debug(" doHandshake()");
+
+        if (step > 0) {
+            throw new ProxyAuthException("Authentication request already sent");
+        }
+
+        // Send request
+        HttpProxyRequest req = (HttpProxyRequest) request;
+        Map<String, List<String>> headers = req.getHeaders() != null ? req
+                .getHeaders() : new HashMap<String, List<String>>();
+
+        String username = req.getProperties().get(
+                HttpProxyConstants.USER_PROPERTY);
+        String password = req.getProperties().get(
+                HttpProxyConstants.PWD_PROPERTY);
+
+        StringUtilities.addValueToHeader(headers, "Proxy-Authorization",
+                "Basic " + createAuthorization(username, password), true);
+
+        StringUtilities.addValueToHeader(headers, "Keep-Alive",
+                HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
+        StringUtilities.addValueToHeader(headers, "Proxy-Connection",
+                "keep-Alive", true);
+        req.setHeaders(headers);
+
+        writeRequest(nextFilter, req);
+        step++;
+    }
+
+    /**
+     * Computes authorization header value.
+     */
+    public static String createAuthorization(final String username,
+            final String password) {
+        return new String(Base64.encodeBase64((username + ":" + password)
+                .getBytes()));
+    }
+
+    @Override
+    public void handleResponse(final HttpProxyResponse response)
+            throws ProxyAuthException {
+        if (response.getStatusCode() != 407) {
+            throw new ProxyAuthException("Received error response code ("
+                    + response.getStatusLine() + ").");
+        }
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,63 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.basic;
+
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.ProxyAuthException;
+import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
+import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
+import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HttpNoAuthLogicHandler.java - HTTP 'no auth' mechanism logic handler.
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class HttpNoAuthLogicHandler extends AbstractAuthLogicHandler {
+    private final static Logger logger = LoggerFactory
+            .getLogger(HttpNoAuthLogicHandler.class);
+
+    public HttpNoAuthLogicHandler(final ProxyIoSession proxyIoSession)
+            throws ProxyAuthException {
+        super(proxyIoSession);
+    }
+
+    @Override
+    public void doHandshake(final NextFilter nextFilter)
+            throws ProxyAuthException {
+        logger.debug(" doHandshake()");
+
+        // Just send the request, no authentication needed
+        writeRequest(nextFilter, (HttpProxyRequest) request);
+        step++;
+    }
+
+    @Override
+    public void handleResponse(final HttpProxyResponse response)
+            throws ProxyAuthException {
+        // Should never get here !
+        throw new ProxyAuthException("Received error response code ("
+                + response.getStatusLine() + ").");
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,164 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.digest;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+
+import javax.security.sasl.AuthenticationException;
+
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.ByteUtilities;
+import org.apache.mina.proxy.utils.StringUtilities;
+
+/**
+ * DigestUtilities.java - A class supporting the HTTP DIGEST authentication (see RFC 2617).
+ * 
+ * Note: Some code has been borrowed from <a href="http://tedorg.free.fr/en/projects.php">Mailster</a>  
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class DigestUtilities {
+
+    public final static String SESSION_HA1 = DigestUtilities.class
+            + ".SessionHA1";
+
+    private static MessageDigest md5;
+
+    static {
+        // Initialize secure random generator 
+        try {
+            md5 = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public final static String[] SUPPORTED_QOPS = new String[] { "auth",
+            "auth-int" };
+
+    /**
+     * Computes the response to the DIGEST challenge.
+     */
+    public static String computeResponseValue(IoSession session,
+            HashMap<String, String> map, String method, String pwd,
+            String charsetName, String body) throws AuthenticationException,
+            UnsupportedEncodingException {
+
+        byte[] hA1;
+        StringBuilder sb;
+        boolean isMD5Sess = "md5-sess".equalsIgnoreCase(StringUtilities
+                .getDirectiveValue(map, "algorithm", false));
+
+        if (!!isMD5Sess || session.getAttribute(SESSION_HA1) == null) {
+            // Build A1
+            sb = new StringBuilder();
+            sb.append(
+                    StringUtilities.stringTo8859_1(StringUtilities
+                            .getDirectiveValue(map, "username", true))).append(
+                    ':');
+
+            String realm = StringUtilities.stringTo8859_1(StringUtilities
+                    .getDirectiveValue(map, "realm", false));
+            if (realm != null) {
+                sb.append(realm);
+            }
+
+            sb.append(':').append(pwd);
+
+            if (isMD5Sess) {
+                byte[] prehA1;
+                synchronized (md5) {
+                    md5.reset();
+                    prehA1 = md5.digest(sb.toString().getBytes(charsetName));
+                }
+
+                sb = new StringBuilder();
+                sb.append(ByteUtilities.asHex(prehA1));
+                sb.append(':').append(
+                        StringUtilities.stringTo8859_1(StringUtilities
+                                .getDirectiveValue(map, "nonce", true)));
+                sb.append(':').append(
+                        StringUtilities.stringTo8859_1(StringUtilities
+                                .getDirectiveValue(map, "cnonce", true)));
+
+                synchronized (md5) {
+                    md5.reset();
+                    hA1 = md5.digest(sb.toString().getBytes(charsetName));
+                }
+
+                session.setAttribute(SESSION_HA1, hA1);
+            } else {
+                synchronized (md5) {
+                    md5.reset();
+                    hA1 = md5.digest(sb.toString().getBytes(charsetName));
+                }
+            }
+        } else {
+            hA1 = (byte[]) session.getAttribute(SESSION_HA1);
+        }
+
+        sb = new StringBuilder(method);
+        sb.append(':');
+        sb.append(StringUtilities.getDirectiveValue(map, "uri", false));
+
+        String qop = StringUtilities.getDirectiveValue(map, "qop", false);
+        if ("auth-int".equalsIgnoreCase(qop)) {
+            ProxyIoSession proxyIoSession = (ProxyIoSession) session
+                    .getAttribute(ProxyIoSession.PROXY_SESSION);
+            byte[] hEntity;
+
+            synchronized (md5) {
+                md5.reset();
+                hEntity = md5.digest(body.getBytes(proxyIoSession
+                        .getCharsetName()));
+            }
+            sb.append(':').append(hEntity);
+        }
+
+        byte[] hA2;
+        synchronized (md5) {
+            md5.reset();
+            hA2 = md5.digest(sb.toString().getBytes(charsetName));
+        }
+
+        sb = new StringBuilder();
+        sb.append(ByteUtilities.asHex(hA1));
+        sb.append(':').append(
+                StringUtilities.getDirectiveValue(map, "nonce", true));
+        sb.append(":00000001:");
+
+        sb.append(StringUtilities.getDirectiveValue(map, "cnonce", true));
+        sb.append(':').append(qop).append(':');
+        sb.append(ByteUtilities.asHex(hA2));
+
+        byte[] hFinal;
+        synchronized (md5) {
+            md5.reset();
+            hFinal = md5.digest(sb.toString().getBytes(charsetName));
+        }
+
+        return ByteUtilities.asHex(hFinal);
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,260 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.digest;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.ProxyAuthException;
+import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
+import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
+import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
+import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.StringUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HttpDigestAuthLogicHandler.java - HTTP Digest authentication mechanism logic handler. 
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class HttpDigestAuthLogicHandler extends AbstractAuthLogicHandler {
+
+    private final static Logger logger = LoggerFactory
+            .getLogger(HttpDigestAuthLogicHandler.class);
+
+    /**
+     * The challenge directives provided by the server.
+     */
+    private HashMap<String, String> directives = null;
+
+    /**
+     * The response received to the last request.
+     */
+    private HttpProxyResponse response;
+
+    private static SecureRandom rnd;
+
+    static {
+        // Initialize secure random generator 
+        try {
+            rnd = SecureRandom.getInstance("SHA1PRNG");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public HttpDigestAuthLogicHandler(final ProxyIoSession proxyIoSession)
+            throws ProxyAuthException {
+        super(proxyIoSession);
+
+        if (request == null || !(request instanceof HttpProxyRequest)) {
+            throw new IllegalArgumentException(
+                    "request parameter should be a non null HttpProxyRequest instance");
+        }
+
+        HttpProxyRequest req = (HttpProxyRequest) request;
+        req.checkRequiredProperty(HttpProxyConstants.USER_PROPERTY);
+        req.checkRequiredProperty(HttpProxyConstants.PWD_PROPERTY);
+    }
+
+    @Override
+    public void doHandshake(NextFilter nextFilter) throws ProxyAuthException {
+        logger.debug(" doHandshake()");
+
+        if (step > 0 && directives == null) {
+            throw new ProxyAuthException(
+                    "Authentication challenge not received");
+        } else {
+            HttpProxyRequest req = (HttpProxyRequest) request;
+            Map<String, List<String>> headers = req.getHeaders() != null ? req
+                    .getHeaders() : new HashMap<String, List<String>>();
+
+            if (step > 0) {
+                logger.debug("  sending DIGEST challenge response");
+
+                HashMap<String, String> map = new HashMap<String, String>();
+                map.put("username", req.getProperties().get(
+                        HttpProxyConstants.USER_PROPERTY));
+                StringUtilities.copyDirective(directives, map, "realm");
+                StringUtilities.copyDirective(directives, map, "uri");
+                StringUtilities.copyDirective(directives, map, "opaque");
+                StringUtilities.copyDirective(directives, map, "nonce");
+                String algorithm = StringUtilities.copyDirective(directives,
+                        map, "algorithm");
+
+                // Check for a supported algorithm
+                if (algorithm != null && !"md5".equalsIgnoreCase(algorithm)
+                        && !"md5-sess".equalsIgnoreCase(algorithm)) {
+                    throw new ProxyAuthException(
+                            "Unknown algorithm required by server");
+                }
+
+                // Check for a supported qop
+                String qop = directives.get("qop");
+                if (qop != null) {
+                    StringTokenizer st = new StringTokenizer(qop, ",");
+                    String token = null;
+
+                    while (st.hasMoreTokens()) {
+                        String tk = st.nextToken();
+                        if ("auth".equalsIgnoreCase(token)) {
+                            break;
+                        } else {
+                            int pos = Arrays.binarySearch(
+                                    DigestUtilities.SUPPORTED_QOPS, tk);
+                            if (pos > -1) {
+                                token = tk;
+                            }
+                        }
+                    }
+
+                    if (token != null) {
+                        map.put("qop", token);
+
+                        byte[] nonce = new byte[8];
+                        rnd.nextBytes(nonce);
+
+                        try {
+                            String cnonce = new String(Base64
+                                    .encodeBase64(nonce), proxyIoSession
+                                    .getCharsetName());
+                            map.put("cnonce", cnonce);
+                        } catch (UnsupportedEncodingException e) {
+                            throw new ProxyAuthException(
+                                    "Unable to encode cnonce", e);
+                        }
+                    } else {
+                        throw new ProxyAuthException(
+                                "No supported qop option available");
+                    }
+                }
+
+                map.put("nc", "00000001");
+                map.put("uri", req.getHttpURI());
+
+                // Compute the response
+                try {
+                    map.put("response", DigestUtilities
+                            .computeResponseValue(proxyIoSession.getSession(),
+                                    map, req.getHttpVerb().toUpperCase(),
+                                    req.getProperties().get(
+                                            HttpProxyConstants.PWD_PROPERTY),
+                                    proxyIoSession.getCharsetName(), response
+                                            .getBody()));
+
+                } catch (Exception e) {
+                    throw new ProxyAuthException(
+                            "Digest response computing failed", e);
+                }
+
+                // Prepare the challenge response header and add it to the request we will send
+                StringBuilder sb = new StringBuilder("Digest ");
+                boolean addSeparator = false;
+
+                for (String key : map.keySet()) {
+
+                    if (addSeparator) {
+                        sb.append(", ");
+                    } else {
+                        addSeparator = true;
+                    }
+
+                    boolean quotedValue = !"qop".equals(key)
+                            && !"nc".equals(key);
+                    sb.append(key);
+                    if (quotedValue) {
+                        sb.append("=\"").append(map.get(key)).append('\"');
+                    } else {
+                        sb.append('=').append(map.get(key));
+                    }
+                }
+
+                StringUtilities.addValueToHeader(headers,
+                        "Proxy-Authorization", sb.toString(), true);
+            }
+
+            StringUtilities.addValueToHeader(headers, "Keep-Alive",
+                    HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
+            StringUtilities.addValueToHeader(headers, "Proxy-Connection",
+                    "keep-Alive", true);
+            req.setHeaders(headers);
+
+            writeRequest(nextFilter, req);
+            step++;
+        }
+    }
+
+    @Override
+    public void handleResponse(final HttpProxyResponse response)
+            throws ProxyAuthException {
+        this.response = response;
+
+        if (step == 0) {
+            if (response.getStatusCode() != 401
+                    && response.getStatusCode() != 407) {
+                throw new ProxyAuthException(
+                        "Received unexpected response code ("
+                                + response.getStatusLine() + ").");
+            }
+
+            // Header should be like this
+            // Proxy-Authenticate: Digest still_some_more_stuff
+            List<String> values = response.getHeaders().get(
+                    "Proxy-Authenticate");
+            String challengeResponse = null;
+
+            for (String s : values) {
+                if (s.startsWith("Digest")) {
+                    challengeResponse = s;
+                    break;
+                }
+            }
+
+            if (challengeResponse == null) {
+                throw new ProxyAuthException(
+                        "Server doesn't support digest authentication method !");
+            }
+
+            try {
+                directives = StringUtilities.parseDirectives(challengeResponse
+                        .substring(7).getBytes(proxyIoSession.getCharsetName()));
+            } catch (Exception e) {
+                throw new ProxyAuthException(
+                        "Parsing of server digest directives failed", e);
+            }
+            step = 1;
+        } else {
+            throw new ProxyAuthException("Received unexpected response code ("
+                    + response.getStatusLine() + ").");
+        }
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,191 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.ntlm;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.proxy.ProxyAuthException;
+import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
+import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
+import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
+import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
+import org.apache.mina.proxy.session.ProxyIoSession;
+import org.apache.mina.proxy.utils.StringUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HttpNTLMAuthLogicHandler.java - HTTP NTLM authentication mechanism logic handler.
+ * 
+ * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
+ * @version $Id: $
+ */
+public class HttpNTLMAuthLogicHandler extends AbstractAuthLogicHandler {
+
+    private final static Logger logger = LoggerFactory
+            .getLogger(HttpNTLMAuthLogicHandler.class);
+
+    /**
+     * The challenge provided by the server.
+     */
+    private byte[] challengePacket = null;
+
+    public HttpNTLMAuthLogicHandler(final ProxyIoSession proxyIoSession)
+            throws ProxyAuthException {
+        super(proxyIoSession);
+
+        if (request == null || !(request instanceof HttpProxyRequest)) {
+            throw new IllegalArgumentException(
+                    "request parameter should be a non null HttpProxyRequest instance");
+        }
+
+        HttpProxyRequest req = (HttpProxyRequest) request;
+        req.checkRequiredProperty(HttpProxyConstants.USER_PROPERTY);
+        req.checkRequiredProperty(HttpProxyConstants.PWD_PROPERTY);
+        req.checkRequiredProperty(HttpProxyConstants.DOMAIN_PROPERTY);
+        req.checkRequiredProperty(HttpProxyConstants.WORKSTATION_PROPERTY);
+    }
+
+    @Override
+    public void doHandshake(NextFilter nextFilter) throws ProxyAuthException {
+        logger.debug(" doHandshake()");
+
+        if (step > 0 && challengePacket == null) {
+            throw new IllegalStateException("Challenge packet not received");
+        } else {
+            HttpProxyRequest req = (HttpProxyRequest) request;
+            Map<String, List<String>> headers = req.getHeaders() != null ? req
+                    .getHeaders() : new HashMap<String, List<String>>();
+
+            String domain = req.getProperties().get(
+                    HttpProxyConstants.DOMAIN_PROPERTY);
+            String workstation = req.getProperties().get(
+                    HttpProxyConstants.WORKSTATION_PROPERTY);
+
+            if (step > 0) {
+                logger.debug("  sending NTLM challenge response");
+
+                byte[] challenge = NTLMUtilities
+                        .extractChallengeFromType2Message(challengePacket);
+                int serverFlags = NTLMUtilities
+                        .extractFlagsFromType2Message(challengePacket);
+
+                String username = req.getProperties().get(
+                        HttpProxyConstants.USER_PROPERTY);
+                String password = req.getProperties().get(
+                        HttpProxyConstants.PWD_PROPERTY);
+
+                byte[] authenticationPacket = NTLMUtilities.createType3Message(
+                        username, password, challenge, domain, workstation,
+                        serverFlags, null);
+
+                StringUtilities.addValueToHeader(headers,
+                        "Proxy-Authorization", "NTLM "
+                                + new String(Base64
+                                        .encodeBase64(authenticationPacket)),
+                        true);
+
+            } else {
+                logger.debug("  sending HTTP request");
+
+                byte[] negotiationPacket = NTLMUtilities.createType1Message(
+                        workstation, domain, null, null);
+                StringUtilities
+                        .addValueToHeader(
+                                headers,
+                                "Proxy-Authorization",
+                                "NTLM "
+                                        + new String(
+                                                Base64
+                                                        .encodeBase64(negotiationPacket)),
+                                true);
+            }
+
+            StringUtilities.addValueToHeader(headers, "Keep-Alive",
+                    HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
+            StringUtilities.addValueToHeader(headers, "Proxy-Connection",
+                    "keep-Alive", true);
+            req.setHeaders(headers);
+
+            writeRequest(nextFilter, req);
+            step++;
+        }
+    }
+
+    /**
+     * Returns the value of the NTLM Proxy-Authenticate header.
+     */
+    private String getNTLMHeader(final HttpProxyResponse response) {
+        List<String> values = response.getHeaders().get("Proxy-Authenticate");
+
+        for (String s : values) {
+            if (s.startsWith("NTLM")) {
+                return s;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public void handleResponse(final HttpProxyResponse response)
+            throws ProxyAuthException {
+        if (step == 0) {
+            String challengeResponse = getNTLMHeader(response);
+            step = 1;
+
+            if (challengeResponse == null || challengeResponse.length() < 5) {
+                // Nothing to handle at this step. Just need to send a reply type 1 message in doHandshake().
+                return;
+            }
+
+            // else there was no step 0 so continue to step 1.
+        }
+
+        if (step == 1) {
+            // Header should be like this
+            // Proxy-Authenticate: NTLM still_some_more_stuff
+            String challengeResponse = getNTLMHeader(response);
+
+            if (challengeResponse == null || challengeResponse.length() < 5) {
+                throw new ProxyAuthException(
+                        "Unexpected error while reading server challenge !");
+            }
+
+            try {
+                challengePacket = Base64
+                        .decodeBase64(challengeResponse.substring(5).getBytes(
+                                proxyIoSession.getCharsetName()));
+            } catch (IOException e) {
+                throw new ProxyAuthException(
+                        "Unable to decode the base64 encoded NTLM challenge", e);
+            }
+            step = 2;
+        } else {
+            throw new ProxyAuthException("Received unexpected response code ("
+                    + response.getStatusLine() + ").");
+        }
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,184 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.ntlm;
+
+/**
+ * NTLMConstants.java - All NTLM constants.
+ * 
+ * @author <a href="mailto:doe_wanted@yahoo.fr">Edouard De Oliveira</a>
+ * @version $Revision:$, $Date:$
+ */
+public interface NTLMConstants {
+    // Signature "NTLMSSP"+{0}
+    public final static byte[] NTLM_SIGNATURE = new byte[] { 0x4E, 0x54, 0x4C,
+            0x4D, 0x53, 0x53, 0x50, 0 };
+
+    // Version 5.1.2600 a Windows XP version (ex: Build 2600.xpsp_sp2_gdr.050301-1519 : Service Pack 2)
+    public final static byte[] DEFAULT_OS_VERSION = new byte[] { 0x05, 0x01,
+            0x28, 0x0A, 0, 0, 0, 0x0F };
+
+    /**
+     * Message types
+     */
+
+    public final static int MESSAGE_TYPE_1 = 1;
+
+    public final static int MESSAGE_TYPE_2 = 2;
+
+    public final static int MESSAGE_TYPE_3 = 3;
+
+    /**
+     * Message flags
+     */
+
+    // Indicates that Unicode strings are supported for use in security buffer data
+    public final static int FLAG_NEGOTIATE_UNICODE = 0x00000001;
+
+    // Indicates that OEM strings are supported for use in security buffer data
+    public final static int FLAG_NEGOTIATE_OEM = 0x00000002;
+
+    // Requests that the server's authentication realm be included in the Type 2 message
+    public final static int FLAG_REQUEST_SERVER_AUTH_REALM = 0x00000004;
+
+    // Specifies that authenticated communication between the client 
+    // and server should carry a digital signature (message integrity)
+    public final static int FLAG_NEGOTIATE_SIGN = 0x00000010;
+
+    // Specifies that authenticated communication between the client 
+    // and server should be encrypted (message confidentiality)
+    public final static int FLAG_NEGOTIATE_SEAL = 0x00000020;
+
+    // Indicates that datagram authentication is being used
+    public final static int FLAG_NEGOTIATE_DATAGRAM_STYLE = 0x00000040;
+
+    // Indicates that the Lan Manager Session Key should be used for signing and 
+    // sealing authenticated communications
+    public final static int FLAG_NEGOTIATE_LAN_MANAGER_KEY = 0x00000080;
+
+    // Indicates that NTLM authentication is being used
+    public final static int FLAG_NEGOTIATE_NTLM = 0x00000200;
+
+    // Sent by the client in the Type 3 message to indicate that an anonymous context 
+    // has been established. This also affects the response fields
+    public final static int FLAG_NEGOTIATE_ANONYMOUS = 0x00000800;
+
+    // Sent by the client in the Type 1 message to indicate that the name of the domain in which 
+    // the client workstation has membership is included in the message. This is used by the 
+    // server to determine whether the client is eligible for local authentication
+    public final static int FLAG_NEGOTIATE_DOMAIN_SUPPLIED = 0x00001000;
+
+    // Sent by the client in the Type 1 message to indicate that the client workstation's name 
+    // is included in the message. This is used by the server to determine whether the client 
+    // is eligible for local authentication
+    public final static int FLAG_NEGOTIATE_WORKSTATION_SUPPLIED = 0x00002000;
+
+    // Sent by the server to indicate that the server and client are on the same machine.
+    // Implies that the client may use the established local credentials for authentication 
+    // instead of calculating a response to the challenge
+    public final static int FLAG_NEGOTIATE_LOCAL_CALL = 0x00004000;
+
+    // Indicates that authenticated communication between the client and server should 
+    // be signed with a "dummy" signature
+    public final static int FLAG_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
+
+    // Sent by the server in the Type 2 message to indicate that the target authentication 
+    // realm is a domain
+    public final static int FLAG_TARGET_TYPE_DOMAIN = 0x00010000;
+
+    // Sent by the server in the Type 2 message to indicate that the target authentication 
+    // realm is a server
+    public final static int FLAG_TARGET_TYPE_SERVER = 0x00020000;
+
+    // Sent by the server in the Type 2 message to indicate that the target authentication 
+    // realm is a share. Presumably, this is for share-level authentication. Usage is unclear
+    public final static int FLAG_TARGET_TYPE_SHARE = 0x00040000;
+
+    // Indicates that the NTLM2 signing and sealing scheme should be used for protecting 
+    // authenticated communications. Note that this refers to a particular session security 
+    // scheme, and is not related to the use of NTLMv2 authentication. This flag can, however, 
+    // have an effect on the response calculations
+    public final static int FLAG_NEGOTIATE_NTLM2 = 0x00080000;
+
+    // Sent by the server in the Type 2 message to indicate that it is including a Target 
+    // Information block in the message. The Target Information block is used in the 
+    // calculation of the NTLMv2 response
+    public final static int FLAG_NEGOTIATE_TARGET_INFO = 0x00800000;
+
+    // Indicates that 128-bit encryption is supported
+    public final static int FLAG_NEGOTIATE_128_BIT_ENCRYPTION = 0x20000000;
+
+    // Indicates that the client will provide an encrypted master key in the "Session Key" 
+    // field of the Type 3 message
+    public final static int FLAG_NEGOTIATE_KEY_EXCHANGE = 0x40000000;
+
+    // Indicates that 56-bit encryption is supported
+    public final static int FLAG_NEGOTIATE_56_BIT_ENCRYPTION = 0x80000000;
+
+    // WARN : These flags usage has not been identified
+    public final static int FLAG_UNIDENTIFIED_1 = 0x00000008;
+
+    public final static int FLAG_UNIDENTIFIED_2 = 0x00000100; // Negotiate Netware ??!
+
+    public final static int FLAG_UNIDENTIFIED_3 = 0x00000400;
+
+    public final static int FLAG_UNIDENTIFIED_4 = 0x00100000; // Request Init Response ??!
+
+    public final static int FLAG_UNIDENTIFIED_5 = 0x00200000; // Request Accept Response ??!
+
+    public final static int FLAG_UNIDENTIFIED_6 = 0x00400000; // Request Non-NT Session Key ??!
+
+    public final static int FLAG_UNIDENTIFIED_7 = 0x01000000;
+
+    public final static int FLAG_UNIDENTIFIED_8 = 0x02000000;
+
+    public final static int FLAG_UNIDENTIFIED_9 = 0x04000000;
+
+    public final static int FLAG_UNIDENTIFIED_10 = 0x08000000;
+
+    public final static int FLAG_UNIDENTIFIED_11 = 0x10000000;
+
+    // Default minimal flag set
+    public final static int DEFAULT_FLAGS = FLAG_NEGOTIATE_OEM
+            | FLAG_NEGOTIATE_UNICODE | FLAG_NEGOTIATE_WORKSTATION_SUPPLIED
+            | FLAG_NEGOTIATE_DOMAIN_SUPPLIED;
+
+    /** 
+     * Target Information sub blocks types. It may be that there are other 
+     * as-yet-unidentified sub block types as well.
+     */
+
+    // Sub block terminator
+    public final static short TARGET_INFORMATION_SUBBLOCK_TERMINATOR_TYPE = 0x0000;
+
+    // Server name
+    public final static short TARGET_INFORMATION_SUBBLOCK_SERVER_TYPE = 0x0100;
+
+    // Domain name
+    public final static short TARGET_INFORMATION_SUBBLOCK_DOMAIN_TYPE = 0x0200;
+
+    // Fully-qualified DNS host name (i.e., server.domain.com)
+    public final static short TARGET_INFORMATION_SUBBLOCK_FQDNS_HOSTNAME_TYPE = 0x0300;
+
+    // DNS domain name (i.e., domain.com)
+    public final static short TARGET_INFORMATION_SUBBLOCK_DNS_DOMAIN_NAME_TYPE = 0x0400;
+
+    // Apparently the "parent" DNS domain for servers in sub domains
+    public final static short TARGET_INFORMATION_SUBBLOCK_PARENT_DNS_DOMAIN_NAME_TYPE = 0x0500;
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java?rev=685389&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java Tue Aug 12 17:05:41 2008
@@ -0,0 +1,413 @@
+/*
+ *  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.
+ *
+ */
+package org.apache.mina.proxy.handlers.http.ntlm;
+
+import java.io.UnsupportedEncodingException;
+import java.security.Key;
+import java.security.MessageDigest;
+
+import javax.crypto.Cipher;
+
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * NTLMResponses.java - Calculates the various Type 3 responses. Needs an MD4, MD5 and DES
+ * crypto provider (Please note that default provider doesn't provide MD4).
+ * 
+ * Base code borrowed from : http://curl.haxx.se/rfc/ntlm.html
+ * 
+ * @author <a href="mailto:doe_wanted@yahoo.fr">Edouard De Oliveira</a>
+ * @version $Revision:$, $Date:$
+ */
+public class NTLMResponses {
+
+    // LAN Manager magic constant used in LM Response calculation
+    public static byte[] LM_HASH_MAGIC_CONSTANT = null;
+
+    static {
+        try {
+            LM_HASH_MAGIC_CONSTANT = "KGS!@#$%".getBytes("US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Calculates the LM 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 LM Response.
+     */
+    public static byte[] getLMResponse(String password, byte[] challenge)
+            throws Exception {
+        byte[] lmHash = lmHash(password);
+        return lmResponse(lmHash, challenge);
+    }
+
+    /**
+     * 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 Exception {
+        byte[] ntlmHash = ntlmHash(password);
+        return lmResponse(ntlmHash, challenge);
+    }
+
+    /**
+     * Calculates the NTLMv2 Response for the given challenge, using the
+     * specified authentication target, username, password, target information
+     * block, and client nonce.
+     *
+     * @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 clientNonce The random 8-byte client nonce.
+     *
+     * @return The NTLMv2 Response.
+     */
+    public static byte[] getNTLMv2Response(String target, String user,
+            String password, byte[] targetInformation, byte[] challenge,
+            byte[] clientNonce) throws Exception {
+
+        return getNTLMv2Response(target, user, password, targetInformation,
+                challenge, clientNonce, System.currentTimeMillis());
+    }
+
+    /**
+     * Calculates the NTLMv2 Response for the given challenge, using the
+     * specified authentication target, username, password, target information
+     * block, and client nonce.
+     *
+     * @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 clientNonce The random 8-byte client nonce.
+     * @param time The time stamp. 
+     *
+     * @return The NTLMv2 Response.
+     */
+    public static byte[] getNTLMv2Response(String target, String user,
+            String password, byte[] targetInformation, byte[] challenge,
+            byte[] clientNonce, long time) throws Exception {
+        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
+        byte[] blob = createBlob(targetInformation, clientNonce, time);
+        return lmv2Response(ntlmv2Hash, blob, challenge);
+    }
+
+    /**
+     * 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 clientNonce The random 8-byte client nonce.
+     *
+     * @return The LMv2 Response. 
+     */
+    public static byte[] getLMv2Response(String target, String user,
+            String password, byte[] challenge, byte[] clientNonce)
+            throws Exception {
+        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
+        return lmv2Response(ntlmv2Hash, clientNonce, challenge);
+    }
+
+    /**
+     * Calculates the NTLM2 Session Response for the given challenge, using the
+     * specified password and client nonce.
+     *
+     * @param password The user's password.
+     * @param challenge The Type 2 challenge from the server.
+     * @param clientNonce The random 8-byte client nonce.
+     *
+     * @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 nonce, null-padded to 24 bytes.
+     */
+    public static byte[] getNTLM2SessionResponse(String password,
+            byte[] challenge, byte[] clientNonce) throws Exception {
+        byte[] ntlmHash = ntlmHash(password);
+        MessageDigest md5 = MessageDigest.getInstance("MD5");
+        md5.update(challenge);
+        md5.update(clientNonce);
+        byte[] sessionHash = new byte[8];
+        System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
+        return lmResponse(ntlmHash, sessionHash);
+    }
+
+    /**
+     * 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 Exception {
+        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);
+        Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
+        des.init(Cipher.ENCRYPT_MODE, lowKey);
+        byte[] lowHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
+        des.init(Cipher.ENCRYPT_MODE, highKey);
+        byte[] highHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
+        byte[] lmHash = new byte[16];
+        System.arraycopy(lowHash, 0, lmHash, 0, 8);
+        System.arraycopy(highHash, 0, lmHash, 8, 8);
+        return lmHash;
+    }
+
+    /**
+     * 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 Exception {
+        byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
+        MessageDigest md4 = MessageDigest.getInstance("MD4");
+        return md4.digest(unicodePassword);
+    }
+
+    /**
+     * 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 Exception {
+        byte[] ntlmHash = ntlmHash(password);
+        String identity = user.toUpperCase() + target;
+        return hmacMD5(identity.getBytes("UnicodeLittleUnmarked"), ntlmHash);
+    }
+
+    /**
+     * 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 Exception {
+        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;
+    }
+
+    /**
+     * 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 nonce).
+     * @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[] clientData,
+            byte[] challenge) throws Exception {
+        byte[] data = new byte[challenge.length + clientData.length];
+        System.arraycopy(challenge, 0, data, 0, challenge.length);
+        System.arraycopy(clientData, 0, data, challenge.length,
+                clientData.length);
+        byte[] mac = hmacMD5(data, hash);
+        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;
+    }
+
+    /**
+     * Creates the NTLMv2 blob from the given target information block and
+     * client nonce.
+     *
+     * @param targetInformation The target information block from the Type 2
+     * message.
+     * @param clientNonce The random 8-byte client nonce.
+     * @param time the time stamp.
+     *
+     * @return The blob, used in the calculation of the NTLMv2 Response.
+     */
+    private static byte[] createBlob(byte[] targetInformation,
+            byte[] clientNonce, long time) {
+        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 };
+        byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00 };
+        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++) {
+            timestamp[i] = (byte) time;
+            time >>>= 8;
+        }
+        byte[] blob = new byte[blobSignature.length + reserved.length
+                + timestamp.length + clientNonce.length + unknown1.length
+                + targetInformation.length + unknown2.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(clientNonce, 0, blob, offset, clientNonce.length);
+        offset += clientNonce.length;
+        System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
+        offset += unknown1.length;
+        System.arraycopy(targetInformation, 0, blob, offset,
+                targetInformation.length);
+        offset += targetInformation.length;
+        System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
+        return blob;
+    }
+
+    /**
+     * Calculates the HMAC-MD5 hash of the given data using the specified
+     * hashing key.
+     *
+     * @param data The data for which the hash will be calculated. 
+     * @param key The hashing key.
+     *
+     * @return The HMAC-MD5 hash of the given data.
+     */
+    public static byte[] hmacMD5(byte[] data, byte[] key) throws Exception {
+        byte[] ipad = new byte[64];
+        byte[] opad = new byte[64];
+
+        // Stores key in pads and XOR it with ipad and opad values
+        for (int i = 0; i < 64; i++) {
+            if (i < key.length) {
+                ipad[i] = (byte) (key[i] ^ 0x36);
+                opad[i] = (byte) (key[i] ^ 0x5c);
+            } else {
+                ipad[i] = 0x36;
+                opad[i] = 0x5c;
+            }
+        }
+
+        byte[] content = new byte[data.length + 64];
+        System.arraycopy(ipad, 0, content, 0, 64);
+        System.arraycopy(data, 0, content, 64, data.length);
+        MessageDigest md5 = MessageDigest.getInstance("MD5");
+        data = md5.digest(content);
+        content = new byte[data.length + 64];
+        System.arraycopy(opad, 0, content, 0, 64);
+        System.arraycopy(data, 0, content, 64, data.length);
+        return md5.digest(content);
+    }
+
+    /**
+     * Creates a DES encryption key from the given key material.
+     *
+     * @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.
+     */
+    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");
+    }
+
+    /**
+     * 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;
+            }
+        }
+    }
+}
\ No newline at end of file

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Re: svn commit: r685389 [2/4] - in /mina/trunk/core/src/main/java/org/apache/mina/proxy: ./ event/ filter/ handlers/ handlers/http/ handlers/http/basic/ handlers/http/digest/ handlers/http/ntlm/ handlers/socks/ session/ utils/

Posted by Julien Vermillard <jv...@gmail.com>.
Javadoc on public method & authors tag here too

2008/8/13  <ed...@apache.org>:
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,219 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http;
> +
> +import java.net.InetSocketAddress;
> +import java.net.MalformedURLException;
> +import java.net.URL;
> +import java.util.List;
> +import java.util.Map;
> +
> +import org.apache.mina.proxy.ProxyAuthException;
> +import org.apache.mina.proxy.handlers.ProxyRequest;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +/**
> + * HttpProxyRequest.java - Wrapper class for HTTP requests.
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @author James Furness <a href="mailto:james.furness@lehman.com">james.furness@lehman.com</a>
> + * @version $Id: $
> + */
> +public class HttpProxyRequest extends ProxyRequest {
> +    private final static Logger logger = LoggerFactory
> +            .getLogger(HttpProxyRequest.class);
> +
> +    public final String httpVerb;
> +
> +    public final String httpURI;
> +
> +    private String httpVersion;
> +
> +    private String host;
> +
> +    private Map<String, List<String>> headers;
> +
> +    private transient Map<String, String> properties;
> +
> +    public HttpProxyRequest(final InetSocketAddress endpointAddress) {
> +        this(HttpProxyConstants.CONNECT, endpointAddress.getHostName() + ":"
> +                + endpointAddress.getPort(), HttpProxyConstants.HTTP_1_0, null);
> +    }
> +
> +    public HttpProxyRequest(final InetSocketAddress endpointAddress,
> +            final String httpVersion) {
> +        this(HttpProxyConstants.CONNECT, endpointAddress.getHostName() + ":"
> +                + endpointAddress.getPort(), httpVersion, null);
> +    }
> +
> +    public HttpProxyRequest(final InetSocketAddress endpointAddress,
> +            final String httpVersion, final Map<String, List<String>> headers) {
> +        this(HttpProxyConstants.CONNECT, endpointAddress.getHostName() + ":"
> +                + endpointAddress.getPort(), httpVersion, headers);
> +    }
> +
> +    public HttpProxyRequest(final String httpURI) {
> +        this(HttpProxyConstants.GET, httpURI, HttpProxyConstants.HTTP_1_0, null);
> +    }
> +
> +    public HttpProxyRequest(final String httpURI, final String httpVersion) {
> +        this(HttpProxyConstants.GET, httpURI, httpVersion, null);
> +    }
> +
> +    public HttpProxyRequest(final String httpVerb, final String httpURI,
> +            final String httpVersion) {
> +        this(httpVerb, httpURI, httpVersion, null);
> +    }
> +
> +    public HttpProxyRequest(final String httpVerb, final String httpURI,
> +            final String httpVersion, final Map<String, List<String>> headers) {
> +        this.httpVerb = httpVerb;
> +        this.httpURI = httpURI;
> +        this.httpVersion = httpVersion;
> +        this.headers = headers;
> +    }
> +
> +    /**
> +     * The request verb.
> +     */
> +    public final String getHttpVerb() {
> +        return httpVerb;
> +    }
> +
> +    /**
> +     * The HTTP version.
> +     */
> +    public String getHttpVersion() {
> +        return httpVersion;
> +    }
> +
> +    /**
> +     * Sets the HTTP version.
> +     */
> +    public void setHttpVersion(String httpVersion) {
> +        this.httpVersion = httpVersion;
> +    }
> +
> +    /**
> +     * Returns the host to which we are connecting.
> +     */
> +    public synchronized final String getHost() {
> +        if (host == null) {
> +            if (getEndpointAddress() != null) {
> +                host = getEndpointAddress().getHostName();
> +            }
> +
> +            if (host == null && httpURI != null) {
> +                try {
> +                    host = (new URL(httpURI)).getHost();
> +                } catch (MalformedURLException e) {
> +                    logger.debug("Malformed URL", e);
> +                }
> +            }
> +        }
> +
> +        return host;
> +    }
> +
> +    /**
> +     * The request URI.
> +     */
> +    public final String getHttpURI() {
> +        return httpURI;
> +    }
> +
> +    /**
> +     * HTTP headers.
> +     */
> +    public final Map<String, List<String>> getHeaders() {
> +        return headers;
> +    }
> +
> +    /**
> +     * Set the HTTP headers.
> +     */
> +    public final void setHeaders(Map<String, List<String>> headers) {
> +        this.headers = headers;
> +    }
> +
> +    /**
> +     * Get the additional properties.
> +     */
> +    public Map<String, String> getProperties() {
> +        return properties;
> +    }
> +
> +    /**
> +     * Set the additional properties.
> +     */
> +    public void setProperties(Map<String, String> properties) {
> +        this.properties = properties;
> +    }
> +
> +    /**
> +     * Check if the property is set otherwise throw a{@link ProxyAuthException}.
> +     */
> +    public void checkRequiredProperty(String propertyName)
> +            throws ProxyAuthException {
> +        if (properties.get(propertyName) == null) {
> +            StringBuilder sb = new StringBuilder("'");
> +            sb.append(propertyName).append(
> +                    "' property not provided in the request properties");
> +            throw new ProxyAuthException(sb.toString());
> +        }
> +    }
> +
> +    /**
> +     * Returns the string representation of the HTTP request .
> +     */
> +    public String toHttpString() {
> +        StringBuilder sb = new StringBuilder();
> +
> +        sb.append(getHttpVerb()).append(' ').append(getHttpURI()).append(' ')
> +                .append(getHttpVersion()).append(HttpProxyConstants.CRLF);
> +
> +        boolean hostHeaderFound = false;
> +
> +        if (getHeaders() != null) {
> +            for (Map.Entry<String, List<String>> header : getHeaders()
> +                    .entrySet()) {
> +                if (!hostHeaderFound) {
> +                    hostHeaderFound = header.getKey().equalsIgnoreCase("host");
> +                }
> +
> +                for (String value : header.getValue()) {
> +                    sb.append(header.getKey()).append(": ").append(value)
> +                            .append(HttpProxyConstants.CRLF);
> +                }
> +            }
> +
> +            if (!hostHeaderFound
> +                    && getHttpVersion() == HttpProxyConstants.HTTP_1_1) {
> +                sb.append("Host: ").append(getHost()).append(
> +                        HttpProxyConstants.CRLF);
> +            }
> +        }
> +
> +        sb.append(HttpProxyConstants.CRLF);
> +
> +        return sb.toString();
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyRequest.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,96 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http;
> +
> +import java.util.List;
> +import java.util.Map;
> +
> +/**
> + * HttpProxyResponse.java - Wrapper class for HTTP requests.
> + *
> + * @author James Furness <a href="mailto:james.furness@lehman.com">james.furness@lehman.com</a>
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class HttpProxyResponse {
> +    public final String httpVersion;
> +
> +    public final String statusLine;
> +
> +    public final int statusCode;
> +
> +    public final Map<String, List<String>> headers;
> +
> +    public String body;
> +
> +    protected HttpProxyResponse(final String httpVersion,
> +            final String statusLine, final Map<String, List<String>> headers) {
> +        this.httpVersion = httpVersion;
> +        this.statusLine = statusLine;
> +
> +        this.statusCode = statusLine.charAt(0) == ' ' ? Integer
> +                .parseInt(statusLine.substring(1, 4)) : Integer
> +                .parseInt(statusLine.substring(0, 3));
> +
> +        this.headers = headers;
> +    }
> +
> +    /**
> +     * The HTTP version.
> +     */
> +    public final String getHttpVersion() {
> +        return httpVersion;
> +    }
> +
> +    /**
> +     * The HTTP status code.
> +     */
> +    public final int getStatusCode() {
> +        return statusCode;
> +    }
> +
> +    /**
> +     * The HTTP status line.
> +     */
> +    public final String getStatusLine() {
> +        return statusLine;
> +    }
> +
> +    /**
> +     * The HTTP entity body.
> +     */
> +    public String getBody() {
> +        return body;
> +    }
> +
> +    /**
> +     * Sets the HTTP entity body.
> +     */
> +    public void setBody(String body) {
> +        this.body = body;
> +    }
> +
> +    /**
> +     * HTTP headers.
> +     */
> +    public final Map<String, List<String>> getHeaders() {
> +        return headers;
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpProxyResponse.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,212 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http;
> +
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +
> +import org.apache.mina.core.filterchain.IoFilter.NextFilter;
> +import org.apache.mina.proxy.ProxyAuthException;
> +import org.apache.mina.proxy.session.ProxyIoSession;
> +import org.apache.mina.proxy.utils.StringUtilities;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +/**
> + * HttpSmartProxyHandler.java - HTTP proxy handler that automatically handles forwarding request
> + * to the appropriate authentication mechanism logic handler.
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class HttpSmartProxyHandler extends AbstractHttpLogicHandler {
> +    private final static Logger logger = LoggerFactory
> +            .getLogger(HttpSmartProxyHandler.class);
> +
> +    /**
> +     * Has the HTTP proxy request been sent ?
> +     */
> +    private boolean requestSent = false;
> +
> +    /**
> +     * The automatically selected http authentication logic handler.
> +     */
> +    private AbstractAuthLogicHandler authHandler;
> +
> +    public HttpSmartProxyHandler(final ProxyIoSession proxyIoSession) {
> +        super(proxyIoSession);
> +    }
> +
> +    /**
> +     * Perform any handshaking processing.
> +     */
> +    public void doHandshake(final NextFilter nextFilter)
> +            throws ProxyAuthException {
> +        logger.debug(" doHandshake()");
> +
> +        if (authHandler != null) {
> +            authHandler.doHandshake(nextFilter);
> +        } else {
> +            if (requestSent) {
> +                throw new ProxyAuthException(
> +                        "Authentication request already sent");
> +            }
> +
> +            logger.debug("  sending HTTP request");
> +
> +            // Send request
> +            HttpProxyRequest req = (HttpProxyRequest) getProxyIoSession()
> +                    .getRequest();
> +            Map<String, List<String>> headers = req.getHeaders() != null ? req
> +                    .getHeaders() : new HashMap<String, List<String>>();
> +
> +            StringUtilities.addValueToHeader(headers, "Keep-Alive",
> +                    HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
> +            StringUtilities.addValueToHeader(headers, "Proxy-Connection",
> +                    "keep-Alive", true);
> +            req.setHeaders(headers);
> +
> +            writeRequest(nextFilter, req);
> +            requestSent = true;
> +        }
> +    }
> +
> +    /**
> +     * Automatic selection of the authentication algorithm. If <code>preferedOrder</code> is set then
> +     * algorithms are selected from the list order otherwise the algorithm tries to select the most
> +     * secured algorithm available first.
> +     */
> +    private void autoSelectAuthHandler(final HttpProxyResponse response)
> +            throws ProxyAuthException {
> +        // Get the Proxy-Authenticate header
> +        List<String> values = response.getHeaders().get("Proxy-Authenticate");
> +
> +        if (values == null || values.size() == 0) {
> +            authHandler = HttpAuthenticationMethods.NO_AUTH
> +                    .getNewHandler(getProxyIoSession());
> +
> +        } else if (getProxyIoSession().getPreferedOrder() == null) {
> +            for (String proxyAuthHeader : values) {
> +                proxyAuthHeader = proxyAuthHeader.toLowerCase();
> +
> +                try {
> +                    // Test which auth mechanism to use. First found is the first used that's why we test
> +                    // in a decreasing security quality order.
> +                    if (proxyAuthHeader.contains("ntlm")) {
> +                        authHandler = HttpAuthenticationMethods.NTLM
> +                                .getNewHandler(getProxyIoSession());
> +                        break;
> +                    } else if (proxyAuthHeader.contains("digest")) {
> +                        authHandler = HttpAuthenticationMethods.DIGEST
> +                                .getNewHandler(getProxyIoSession());
> +                        break;
> +                    } else if (proxyAuthHeader.contains("basic")) {
> +                        authHandler = HttpAuthenticationMethods.BASIC
> +                                .getNewHandler(getProxyIoSession());
> +                        break;
> +                    }
> +                } catch (Exception ex) {
> +                    logger.debug("Following exception occured:", ex);
> +                }
> +            }
> +
> +            if (authHandler == null) {
> +                authHandler = HttpAuthenticationMethods.NO_AUTH
> +                        .getNewHandler(getProxyIoSession());
> +            }
> +
> +        } else {
> +            for (HttpAuthenticationMethods method : getProxyIoSession()
> +                    .getPreferedOrder()) {
> +                if (authHandler != null) {
> +                    break;
> +                }
> +
> +                if (method == HttpAuthenticationMethods.NO_AUTH) {
> +                    authHandler = HttpAuthenticationMethods.NO_AUTH
> +                            .getNewHandler(getProxyIoSession());
> +                    break;
> +                }
> +
> +                for (String proxyAuthHeader : values) {
> +                    proxyAuthHeader = proxyAuthHeader.toLowerCase();
> +
> +                    try {
> +                        // test which auth mechanism to use
> +                        if (proxyAuthHeader.contains("basic")
> +                                && method == HttpAuthenticationMethods.BASIC) {
> +                            authHandler = HttpAuthenticationMethods.BASIC
> +                                    .getNewHandler(getProxyIoSession());
> +                            break;
> +                        } else if (proxyAuthHeader.contains("digest")
> +                                && method == HttpAuthenticationMethods.DIGEST) {
> +                            authHandler = HttpAuthenticationMethods.DIGEST
> +                                    .getNewHandler(getProxyIoSession());
> +                            break;
> +                        } else if (proxyAuthHeader.contains("ntlm")
> +                                && method == HttpAuthenticationMethods.NTLM) {
> +                            authHandler = HttpAuthenticationMethods.NTLM
> +                                    .getNewHandler(getProxyIoSession());
> +                            break;
> +                        }
> +                    } catch (Exception ex) {
> +                        logger.debug("Following exception occured:", ex);
> +                    }
> +                }
> +            }
> +
> +        }
> +
> +        if (authHandler == null) {
> +            throw new ProxyAuthException(
> +                    "Unknown authentication mechanism(s): " + values);
> +        }
> +    }
> +
> +    /**
> +     * Handle a HTTP response from the proxy server.
> +     *
> +     * @param response The response.
> +     */
> +    @Override
> +    public void handleResponse(final HttpProxyResponse response)
> +            throws ProxyAuthException {
> +        if (!isHandshakeComplete()
> +                && ("close".equalsIgnoreCase(StringUtilities
> +                        .getSingleValuedHeader(response.getHeaders(),
> +                                "Proxy-Connection")) || "close"
> +                        .equalsIgnoreCase(StringUtilities
> +                                .getSingleValuedHeader(response.getHeaders(),
> +                                        "Connection")))) {
> +            getProxyIoSession().setReconnectionNeeded(true);
> +        }
> +
> +        if (response.getStatusCode() == 407) {
> +            if (authHandler == null) {
> +                autoSelectAuthHandler(response);
> +            }
> +            authHandler.handleResponse(response);
> +        } else {
> +            throw new ProxyAuthException("Received error response code ("
> +                    + response.getStatusLine() + ").");
> +        }
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/HttpSmartProxyHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,111 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.basic;
> +
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +
> +import org.apache.commons.codec.binary.Base64;
> +import org.apache.mina.core.filterchain.IoFilter.NextFilter;
> +import org.apache.mina.proxy.ProxyAuthException;
> +import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
> +import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
> +import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
> +import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
> +import org.apache.mina.proxy.session.ProxyIoSession;
> +import org.apache.mina.proxy.utils.StringUtilities;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +/**
> + * HttpBasicAuthLogicHandler.java - HTTP Basic authentication mechanism logic handler.
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class HttpBasicAuthLogicHandler extends AbstractAuthLogicHandler {
> +    private final static Logger logger = LoggerFactory
> +            .getLogger(HttpBasicAuthLogicHandler.class);
> +
> +    public HttpBasicAuthLogicHandler(final ProxyIoSession proxyIoSession)
> +            throws ProxyAuthException {
> +        super(proxyIoSession);
> +
> +        if (request == null || !(request instanceof HttpProxyRequest)) {
> +            throw new IllegalArgumentException(
> +                    "request parameter should be a non null HttpProxyRequest instance");
> +        }
> +
> +        HttpProxyRequest req = (HttpProxyRequest) request;
> +        req.checkRequiredProperty(HttpProxyConstants.USER_PROPERTY);
> +        req.checkRequiredProperty(HttpProxyConstants.PWD_PROPERTY);
> +    }
> +
> +    @Override
> +    public void doHandshake(final NextFilter nextFilter)
> +            throws ProxyAuthException {
> +        logger.debug(" doHandshake()");
> +
> +        if (step > 0) {
> +            throw new ProxyAuthException("Authentication request already sent");
> +        }
> +
> +        // Send request
> +        HttpProxyRequest req = (HttpProxyRequest) request;
> +        Map<String, List<String>> headers = req.getHeaders() != null ? req
> +                .getHeaders() : new HashMap<String, List<String>>();
> +
> +        String username = req.getProperties().get(
> +                HttpProxyConstants.USER_PROPERTY);
> +        String password = req.getProperties().get(
> +                HttpProxyConstants.PWD_PROPERTY);
> +
> +        StringUtilities.addValueToHeader(headers, "Proxy-Authorization",
> +                "Basic " + createAuthorization(username, password), true);
> +
> +        StringUtilities.addValueToHeader(headers, "Keep-Alive",
> +                HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
> +        StringUtilities.addValueToHeader(headers, "Proxy-Connection",
> +                "keep-Alive", true);
> +        req.setHeaders(headers);
> +
> +        writeRequest(nextFilter, req);
> +        step++;
> +    }
> +
> +    /**
> +     * Computes authorization header value.
> +     */
> +    public static String createAuthorization(final String username,
> +            final String password) {
> +        return new String(Base64.encodeBase64((username + ":" + password)
> +                .getBytes()));
> +    }
> +
> +    @Override
> +    public void handleResponse(final HttpProxyResponse response)
> +            throws ProxyAuthException {
> +        if (response.getStatusCode() != 407) {
> +            throw new ProxyAuthException("Received error response code ("
> +                    + response.getStatusLine() + ").");
> +        }
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpBasicAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,63 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.basic;
> +
> +import org.apache.mina.core.filterchain.IoFilter.NextFilter;
> +import org.apache.mina.proxy.ProxyAuthException;
> +import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
> +import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
> +import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
> +import org.apache.mina.proxy.session.ProxyIoSession;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +/**
> + * HttpNoAuthLogicHandler.java - HTTP 'no auth' mechanism logic handler.
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class HttpNoAuthLogicHandler extends AbstractAuthLogicHandler {
> +    private final static Logger logger = LoggerFactory
> +            .getLogger(HttpNoAuthLogicHandler.class);
> +
> +    public HttpNoAuthLogicHandler(final ProxyIoSession proxyIoSession)
> +            throws ProxyAuthException {
> +        super(proxyIoSession);
> +    }
> +
> +    @Override
> +    public void doHandshake(final NextFilter nextFilter)
> +            throws ProxyAuthException {
> +        logger.debug(" doHandshake()");
> +
> +        // Just send the request, no authentication needed
> +        writeRequest(nextFilter, (HttpProxyRequest) request);
> +        step++;
> +    }
> +
> +    @Override
> +    public void handleResponse(final HttpProxyResponse response)
> +            throws ProxyAuthException {
> +        // Should never get here !
> +        throw new ProxyAuthException("Received error response code ("
> +                + response.getStatusLine() + ").");
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/basic/HttpNoAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,164 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.digest;
> +
> +import java.io.UnsupportedEncodingException;
> +import java.security.MessageDigest;
> +import java.security.NoSuchAlgorithmException;
> +import java.util.HashMap;
> +
> +import javax.security.sasl.AuthenticationException;
> +
> +import org.apache.mina.core.session.IoSession;
> +import org.apache.mina.proxy.session.ProxyIoSession;
> +import org.apache.mina.proxy.utils.ByteUtilities;
> +import org.apache.mina.proxy.utils.StringUtilities;
> +
> +/**
> + * DigestUtilities.java - A class supporting the HTTP DIGEST authentication (see RFC 2617).
> + *
> + * Note: Some code has been borrowed from <a href="http://tedorg.free.fr/en/projects.php">Mailster</a>
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class DigestUtilities {
> +
> +    public final static String SESSION_HA1 = DigestUtilities.class
> +            + ".SessionHA1";
> +
> +    private static MessageDigest md5;
> +
> +    static {
> +        // Initialize secure random generator
> +        try {
> +            md5 = MessageDigest.getInstance("MD5");
> +        } catch (NoSuchAlgorithmException e) {
> +            throw new RuntimeException(e);
> +        }
> +    }
> +
> +    public final static String[] SUPPORTED_QOPS = new String[] { "auth",
> +            "auth-int" };
> +
> +    /**
> +     * Computes the response to the DIGEST challenge.
> +     */
> +    public static String computeResponseValue(IoSession session,
> +            HashMap<String, String> map, String method, String pwd,
> +            String charsetName, String body) throws AuthenticationException,
> +            UnsupportedEncodingException {
> +
> +        byte[] hA1;
> +        StringBuilder sb;
> +        boolean isMD5Sess = "md5-sess".equalsIgnoreCase(StringUtilities
> +                .getDirectiveValue(map, "algorithm", false));
> +
> +        if (!!isMD5Sess || session.getAttribute(SESSION_HA1) == null) {
> +            // Build A1
> +            sb = new StringBuilder();
> +            sb.append(
> +                    StringUtilities.stringTo8859_1(StringUtilities
> +                            .getDirectiveValue(map, "username", true))).append(
> +                    ':');
> +
> +            String realm = StringUtilities.stringTo8859_1(StringUtilities
> +                    .getDirectiveValue(map, "realm", false));
> +            if (realm != null) {
> +                sb.append(realm);
> +            }
> +
> +            sb.append(':').append(pwd);
> +
> +            if (isMD5Sess) {
> +                byte[] prehA1;
> +                synchronized (md5) {
> +                    md5.reset();
> +                    prehA1 = md5.digest(sb.toString().getBytes(charsetName));
> +                }
> +
> +                sb = new StringBuilder();
> +                sb.append(ByteUtilities.asHex(prehA1));
> +                sb.append(':').append(
> +                        StringUtilities.stringTo8859_1(StringUtilities
> +                                .getDirectiveValue(map, "nonce", true)));
> +                sb.append(':').append(
> +                        StringUtilities.stringTo8859_1(StringUtilities
> +                                .getDirectiveValue(map, "cnonce", true)));
> +
> +                synchronized (md5) {
> +                    md5.reset();
> +                    hA1 = md5.digest(sb.toString().getBytes(charsetName));
> +                }
> +
> +                session.setAttribute(SESSION_HA1, hA1);
> +            } else {
> +                synchronized (md5) {
> +                    md5.reset();
> +                    hA1 = md5.digest(sb.toString().getBytes(charsetName));
> +                }
> +            }
> +        } else {
> +            hA1 = (byte[]) session.getAttribute(SESSION_HA1);
> +        }
> +
> +        sb = new StringBuilder(method);
> +        sb.append(':');
> +        sb.append(StringUtilities.getDirectiveValue(map, "uri", false));
> +
> +        String qop = StringUtilities.getDirectiveValue(map, "qop", false);
> +        if ("auth-int".equalsIgnoreCase(qop)) {
> +            ProxyIoSession proxyIoSession = (ProxyIoSession) session
> +                    .getAttribute(ProxyIoSession.PROXY_SESSION);
> +            byte[] hEntity;
> +
> +            synchronized (md5) {
> +                md5.reset();
> +                hEntity = md5.digest(body.getBytes(proxyIoSession
> +                        .getCharsetName()));
> +            }
> +            sb.append(':').append(hEntity);
> +        }
> +
> +        byte[] hA2;
> +        synchronized (md5) {
> +            md5.reset();
> +            hA2 = md5.digest(sb.toString().getBytes(charsetName));
> +        }
> +
> +        sb = new StringBuilder();
> +        sb.append(ByteUtilities.asHex(hA1));
> +        sb.append(':').append(
> +                StringUtilities.getDirectiveValue(map, "nonce", true));
> +        sb.append(":00000001:");
> +
> +        sb.append(StringUtilities.getDirectiveValue(map, "cnonce", true));
> +        sb.append(':').append(qop).append(':');
> +        sb.append(ByteUtilities.asHex(hA2));
> +
> +        byte[] hFinal;
> +        synchronized (md5) {
> +            md5.reset();
> +            hFinal = md5.digest(sb.toString().getBytes(charsetName));
> +        }
> +
> +        return ByteUtilities.asHex(hFinal);
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/DigestUtilities.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,260 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.digest;
> +
> +import java.io.UnsupportedEncodingException;
> +import java.security.NoSuchAlgorithmException;
> +import java.security.SecureRandom;
> +import java.util.Arrays;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.StringTokenizer;
> +
> +import org.apache.commons.codec.binary.Base64;
> +import org.apache.mina.core.filterchain.IoFilter.NextFilter;
> +import org.apache.mina.proxy.ProxyAuthException;
> +import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
> +import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
> +import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
> +import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
> +import org.apache.mina.proxy.session.ProxyIoSession;
> +import org.apache.mina.proxy.utils.StringUtilities;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +/**
> + * HttpDigestAuthLogicHandler.java - HTTP Digest authentication mechanism logic handler.
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class HttpDigestAuthLogicHandler extends AbstractAuthLogicHandler {
> +
> +    private final static Logger logger = LoggerFactory
> +            .getLogger(HttpDigestAuthLogicHandler.class);
> +
> +    /**
> +     * The challenge directives provided by the server.
> +     */
> +    private HashMap<String, String> directives = null;
> +
> +    /**
> +     * The response received to the last request.
> +     */
> +    private HttpProxyResponse response;
> +
> +    private static SecureRandom rnd;
> +
> +    static {
> +        // Initialize secure random generator
> +        try {
> +            rnd = SecureRandom.getInstance("SHA1PRNG");
> +        } catch (NoSuchAlgorithmException e) {
> +            throw new RuntimeException(e);
> +        }
> +    }
> +
> +    public HttpDigestAuthLogicHandler(final ProxyIoSession proxyIoSession)
> +            throws ProxyAuthException {
> +        super(proxyIoSession);
> +
> +        if (request == null || !(request instanceof HttpProxyRequest)) {
> +            throw new IllegalArgumentException(
> +                    "request parameter should be a non null HttpProxyRequest instance");
> +        }
> +
> +        HttpProxyRequest req = (HttpProxyRequest) request;
> +        req.checkRequiredProperty(HttpProxyConstants.USER_PROPERTY);
> +        req.checkRequiredProperty(HttpProxyConstants.PWD_PROPERTY);
> +    }
> +
> +    @Override
> +    public void doHandshake(NextFilter nextFilter) throws ProxyAuthException {
> +        logger.debug(" doHandshake()");
> +
> +        if (step > 0 && directives == null) {
> +            throw new ProxyAuthException(
> +                    "Authentication challenge not received");
> +        } else {
> +            HttpProxyRequest req = (HttpProxyRequest) request;
> +            Map<String, List<String>> headers = req.getHeaders() != null ? req
> +                    .getHeaders() : new HashMap<String, List<String>>();
> +
> +            if (step > 0) {
> +                logger.debug("  sending DIGEST challenge response");
> +
> +                HashMap<String, String> map = new HashMap<String, String>();
> +                map.put("username", req.getProperties().get(
> +                        HttpProxyConstants.USER_PROPERTY));
> +                StringUtilities.copyDirective(directives, map, "realm");
> +                StringUtilities.copyDirective(directives, map, "uri");
> +                StringUtilities.copyDirective(directives, map, "opaque");
> +                StringUtilities.copyDirective(directives, map, "nonce");
> +                String algorithm = StringUtilities.copyDirective(directives,
> +                        map, "algorithm");
> +
> +                // Check for a supported algorithm
> +                if (algorithm != null && !"md5".equalsIgnoreCase(algorithm)
> +                        && !"md5-sess".equalsIgnoreCase(algorithm)) {
> +                    throw new ProxyAuthException(
> +                            "Unknown algorithm required by server");
> +                }
> +
> +                // Check for a supported qop
> +                String qop = directives.get("qop");
> +                if (qop != null) {
> +                    StringTokenizer st = new StringTokenizer(qop, ",");
> +                    String token = null;
> +
> +                    while (st.hasMoreTokens()) {
> +                        String tk = st.nextToken();
> +                        if ("auth".equalsIgnoreCase(token)) {
> +                            break;
> +                        } else {
> +                            int pos = Arrays.binarySearch(
> +                                    DigestUtilities.SUPPORTED_QOPS, tk);
> +                            if (pos > -1) {
> +                                token = tk;
> +                            }
> +                        }
> +                    }
> +
> +                    if (token != null) {
> +                        map.put("qop", token);
> +
> +                        byte[] nonce = new byte[8];
> +                        rnd.nextBytes(nonce);
> +
> +                        try {
> +                            String cnonce = new String(Base64
> +                                    .encodeBase64(nonce), proxyIoSession
> +                                    .getCharsetName());
> +                            map.put("cnonce", cnonce);
> +                        } catch (UnsupportedEncodingException e) {
> +                            throw new ProxyAuthException(
> +                                    "Unable to encode cnonce", e);
> +                        }
> +                    } else {
> +                        throw new ProxyAuthException(
> +                                "No supported qop option available");
> +                    }
> +                }
> +
> +                map.put("nc", "00000001");
> +                map.put("uri", req.getHttpURI());
> +
> +                // Compute the response
> +                try {
> +                    map.put("response", DigestUtilities
> +                            .computeResponseValue(proxyIoSession.getSession(),
> +                                    map, req.getHttpVerb().toUpperCase(),
> +                                    req.getProperties().get(
> +                                            HttpProxyConstants.PWD_PROPERTY),
> +                                    proxyIoSession.getCharsetName(), response
> +                                            .getBody()));
> +
> +                } catch (Exception e) {
> +                    throw new ProxyAuthException(
> +                            "Digest response computing failed", e);
> +                }
> +
> +                // Prepare the challenge response header and add it to the request we will send
> +                StringBuilder sb = new StringBuilder("Digest ");
> +                boolean addSeparator = false;
> +
> +                for (String key : map.keySet()) {
> +
> +                    if (addSeparator) {
> +                        sb.append(", ");
> +                    } else {
> +                        addSeparator = true;
> +                    }
> +
> +                    boolean quotedValue = !"qop".equals(key)
> +                            && !"nc".equals(key);
> +                    sb.append(key);
> +                    if (quotedValue) {
> +                        sb.append("=\"").append(map.get(key)).append('\"');
> +                    } else {
> +                        sb.append('=').append(map.get(key));
> +                    }
> +                }
> +
> +                StringUtilities.addValueToHeader(headers,
> +                        "Proxy-Authorization", sb.toString(), true);
> +            }
> +
> +            StringUtilities.addValueToHeader(headers, "Keep-Alive",
> +                    HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
> +            StringUtilities.addValueToHeader(headers, "Proxy-Connection",
> +                    "keep-Alive", true);
> +            req.setHeaders(headers);
> +
> +            writeRequest(nextFilter, req);
> +            step++;
> +        }
> +    }
> +
> +    @Override
> +    public void handleResponse(final HttpProxyResponse response)
> +            throws ProxyAuthException {
> +        this.response = response;
> +
> +        if (step == 0) {
> +            if (response.getStatusCode() != 401
> +                    && response.getStatusCode() != 407) {
> +                throw new ProxyAuthException(
> +                        "Received unexpected response code ("
> +                                + response.getStatusLine() + ").");
> +            }
> +
> +            // Header should be like this
> +            // Proxy-Authenticate: Digest still_some_more_stuff
> +            List<String> values = response.getHeaders().get(
> +                    "Proxy-Authenticate");
> +            String challengeResponse = null;
> +
> +            for (String s : values) {
> +                if (s.startsWith("Digest")) {
> +                    challengeResponse = s;
> +                    break;
> +                }
> +            }
> +
> +            if (challengeResponse == null) {
> +                throw new ProxyAuthException(
> +                        "Server doesn't support digest authentication method !");
> +            }
> +
> +            try {
> +                directives = StringUtilities.parseDirectives(challengeResponse
> +                        .substring(7).getBytes(proxyIoSession.getCharsetName()));
> +            } catch (Exception e) {
> +                throw new ProxyAuthException(
> +                        "Parsing of server digest directives failed", e);
> +            }
> +            step = 1;
> +        } else {
> +            throw new ProxyAuthException("Received unexpected response code ("
> +                    + response.getStatusLine() + ").");
> +        }
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/digest/HttpDigestAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,191 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.ntlm;
> +
> +import java.io.IOException;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +
> +import org.apache.commons.codec.binary.Base64;
> +import org.apache.mina.core.filterchain.IoFilter.NextFilter;
> +import org.apache.mina.proxy.ProxyAuthException;
> +import org.apache.mina.proxy.handlers.http.AbstractAuthLogicHandler;
> +import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
> +import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
> +import org.apache.mina.proxy.handlers.http.HttpProxyResponse;
> +import org.apache.mina.proxy.session.ProxyIoSession;
> +import org.apache.mina.proxy.utils.StringUtilities;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +/**
> + * HttpNTLMAuthLogicHandler.java - HTTP NTLM authentication mechanism logic handler.
> + *
> + * @author Edouard De Oliveira <a href="mailto:doe_wanted@yahoo.fr">doe_wanted@yahoo.fr</a>
> + * @version $Id: $
> + */
> +public class HttpNTLMAuthLogicHandler extends AbstractAuthLogicHandler {
> +
> +    private final static Logger logger = LoggerFactory
> +            .getLogger(HttpNTLMAuthLogicHandler.class);
> +
> +    /**
> +     * The challenge provided by the server.
> +     */
> +    private byte[] challengePacket = null;
> +
> +    public HttpNTLMAuthLogicHandler(final ProxyIoSession proxyIoSession)
> +            throws ProxyAuthException {
> +        super(proxyIoSession);
> +
> +        if (request == null || !(request instanceof HttpProxyRequest)) {
> +            throw new IllegalArgumentException(
> +                    "request parameter should be a non null HttpProxyRequest instance");
> +        }
> +
> +        HttpProxyRequest req = (HttpProxyRequest) request;
> +        req.checkRequiredProperty(HttpProxyConstants.USER_PROPERTY);
> +        req.checkRequiredProperty(HttpProxyConstants.PWD_PROPERTY);
> +        req.checkRequiredProperty(HttpProxyConstants.DOMAIN_PROPERTY);
> +        req.checkRequiredProperty(HttpProxyConstants.WORKSTATION_PROPERTY);
> +    }
> +
> +    @Override
> +    public void doHandshake(NextFilter nextFilter) throws ProxyAuthException {
> +        logger.debug(" doHandshake()");
> +
> +        if (step > 0 && challengePacket == null) {
> +            throw new IllegalStateException("Challenge packet not received");
> +        } else {
> +            HttpProxyRequest req = (HttpProxyRequest) request;
> +            Map<String, List<String>> headers = req.getHeaders() != null ? req
> +                    .getHeaders() : new HashMap<String, List<String>>();
> +
> +            String domain = req.getProperties().get(
> +                    HttpProxyConstants.DOMAIN_PROPERTY);
> +            String workstation = req.getProperties().get(
> +                    HttpProxyConstants.WORKSTATION_PROPERTY);
> +
> +            if (step > 0) {
> +                logger.debug("  sending NTLM challenge response");
> +
> +                byte[] challenge = NTLMUtilities
> +                        .extractChallengeFromType2Message(challengePacket);
> +                int serverFlags = NTLMUtilities
> +                        .extractFlagsFromType2Message(challengePacket);
> +
> +                String username = req.getProperties().get(
> +                        HttpProxyConstants.USER_PROPERTY);
> +                String password = req.getProperties().get(
> +                        HttpProxyConstants.PWD_PROPERTY);
> +
> +                byte[] authenticationPacket = NTLMUtilities.createType3Message(
> +                        username, password, challenge, domain, workstation,
> +                        serverFlags, null);
> +
> +                StringUtilities.addValueToHeader(headers,
> +                        "Proxy-Authorization", "NTLM "
> +                                + new String(Base64
> +                                        .encodeBase64(authenticationPacket)),
> +                        true);
> +
> +            } else {
> +                logger.debug("  sending HTTP request");
> +
> +                byte[] negotiationPacket = NTLMUtilities.createType1Message(
> +                        workstation, domain, null, null);
> +                StringUtilities
> +                        .addValueToHeader(
> +                                headers,
> +                                "Proxy-Authorization",
> +                                "NTLM "
> +                                        + new String(
> +                                                Base64
> +                                                        .encodeBase64(negotiationPacket)),
> +                                true);
> +            }
> +
> +            StringUtilities.addValueToHeader(headers, "Keep-Alive",
> +                    HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
> +            StringUtilities.addValueToHeader(headers, "Proxy-Connection",
> +                    "keep-Alive", true);
> +            req.setHeaders(headers);
> +
> +            writeRequest(nextFilter, req);
> +            step++;
> +        }
> +    }
> +
> +    /**
> +     * Returns the value of the NTLM Proxy-Authenticate header.
> +     */
> +    private String getNTLMHeader(final HttpProxyResponse response) {
> +        List<String> values = response.getHeaders().get("Proxy-Authenticate");
> +
> +        for (String s : values) {
> +            if (s.startsWith("NTLM")) {
> +                return s;
> +            }
> +        }
> +
> +        return null;
> +    }
> +
> +    @Override
> +    public void handleResponse(final HttpProxyResponse response)
> +            throws ProxyAuthException {
> +        if (step == 0) {
> +            String challengeResponse = getNTLMHeader(response);
> +            step = 1;
> +
> +            if (challengeResponse == null || challengeResponse.length() < 5) {
> +                // Nothing to handle at this step. Just need to send a reply type 1 message in doHandshake().
> +                return;
> +            }
> +
> +            // else there was no step 0 so continue to step 1.
> +        }
> +
> +        if (step == 1) {
> +            // Header should be like this
> +            // Proxy-Authenticate: NTLM still_some_more_stuff
> +            String challengeResponse = getNTLMHeader(response);
> +
> +            if (challengeResponse == null || challengeResponse.length() < 5) {
> +                throw new ProxyAuthException(
> +                        "Unexpected error while reading server challenge !");
> +            }
> +
> +            try {
> +                challengePacket = Base64
> +                        .decodeBase64(challengeResponse.substring(5).getBytes(
> +                                proxyIoSession.getCharsetName()));
> +            } catch (IOException e) {
> +                throw new ProxyAuthException(
> +                        "Unable to decode the base64 encoded NTLM challenge", e);
> +            }
> +            step = 2;
> +        } else {
> +            throw new ProxyAuthException("Received unexpected response code ("
> +                    + response.getStatusLine() + ").");
> +        }
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/HttpNTLMAuthLogicHandler.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,184 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.ntlm;
> +
> +/**
> + * NTLMConstants.java - All NTLM constants.
> + *
> + * @author <a href="mailto:doe_wanted@yahoo.fr">Edouard De Oliveira</a>
> + * @version $Revision:$, $Date:$
> + */
> +public interface NTLMConstants {
> +    // Signature "NTLMSSP"+{0}
> +    public final static byte[] NTLM_SIGNATURE = new byte[] { 0x4E, 0x54, 0x4C,
> +            0x4D, 0x53, 0x53, 0x50, 0 };
> +
> +    // Version 5.1.2600 a Windows XP version (ex: Build 2600.xpsp_sp2_gdr.050301-1519 : Service Pack 2)
> +    public final static byte[] DEFAULT_OS_VERSION = new byte[] { 0x05, 0x01,
> +            0x28, 0x0A, 0, 0, 0, 0x0F };
> +
> +    /**
> +     * Message types
> +     */
> +
> +    public final static int MESSAGE_TYPE_1 = 1;
> +
> +    public final static int MESSAGE_TYPE_2 = 2;
> +
> +    public final static int MESSAGE_TYPE_3 = 3;
> +
> +    /**
> +     * Message flags
> +     */
> +
> +    // Indicates that Unicode strings are supported for use in security buffer data
> +    public final static int FLAG_NEGOTIATE_UNICODE = 0x00000001;
> +
> +    // Indicates that OEM strings are supported for use in security buffer data
> +    public final static int FLAG_NEGOTIATE_OEM = 0x00000002;
> +
> +    // Requests that the server's authentication realm be included in the Type 2 message
> +    public final static int FLAG_REQUEST_SERVER_AUTH_REALM = 0x00000004;
> +
> +    // Specifies that authenticated communication between the client
> +    // and server should carry a digital signature (message integrity)
> +    public final static int FLAG_NEGOTIATE_SIGN = 0x00000010;
> +
> +    // Specifies that authenticated communication between the client
> +    // and server should be encrypted (message confidentiality)
> +    public final static int FLAG_NEGOTIATE_SEAL = 0x00000020;
> +
> +    // Indicates that datagram authentication is being used
> +    public final static int FLAG_NEGOTIATE_DATAGRAM_STYLE = 0x00000040;
> +
> +    // Indicates that the Lan Manager Session Key should be used for signing and
> +    // sealing authenticated communications
> +    public final static int FLAG_NEGOTIATE_LAN_MANAGER_KEY = 0x00000080;
> +
> +    // Indicates that NTLM authentication is being used
> +    public final static int FLAG_NEGOTIATE_NTLM = 0x00000200;
> +
> +    // Sent by the client in the Type 3 message to indicate that an anonymous context
> +    // has been established. This also affects the response fields
> +    public final static int FLAG_NEGOTIATE_ANONYMOUS = 0x00000800;
> +
> +    // Sent by the client in the Type 1 message to indicate that the name of the domain in which
> +    // the client workstation has membership is included in the message. This is used by the
> +    // server to determine whether the client is eligible for local authentication
> +    public final static int FLAG_NEGOTIATE_DOMAIN_SUPPLIED = 0x00001000;
> +
> +    // Sent by the client in the Type 1 message to indicate that the client workstation's name
> +    // is included in the message. This is used by the server to determine whether the client
> +    // is eligible for local authentication
> +    public final static int FLAG_NEGOTIATE_WORKSTATION_SUPPLIED = 0x00002000;
> +
> +    // Sent by the server to indicate that the server and client are on the same machine.
> +    // Implies that the client may use the established local credentials for authentication
> +    // instead of calculating a response to the challenge
> +    public final static int FLAG_NEGOTIATE_LOCAL_CALL = 0x00004000;
> +
> +    // Indicates that authenticated communication between the client and server should
> +    // be signed with a "dummy" signature
> +    public final static int FLAG_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
> +
> +    // Sent by the server in the Type 2 message to indicate that the target authentication
> +    // realm is a domain
> +    public final static int FLAG_TARGET_TYPE_DOMAIN = 0x00010000;
> +
> +    // Sent by the server in the Type 2 message to indicate that the target authentication
> +    // realm is a server
> +    public final static int FLAG_TARGET_TYPE_SERVER = 0x00020000;
> +
> +    // Sent by the server in the Type 2 message to indicate that the target authentication
> +    // realm is a share. Presumably, this is for share-level authentication. Usage is unclear
> +    public final static int FLAG_TARGET_TYPE_SHARE = 0x00040000;
> +
> +    // Indicates that the NTLM2 signing and sealing scheme should be used for protecting
> +    // authenticated communications. Note that this refers to a particular session security
> +    // scheme, and is not related to the use of NTLMv2 authentication. This flag can, however,
> +    // have an effect on the response calculations
> +    public final static int FLAG_NEGOTIATE_NTLM2 = 0x00080000;
> +
> +    // Sent by the server in the Type 2 message to indicate that it is including a Target
> +    // Information block in the message. The Target Information block is used in the
> +    // calculation of the NTLMv2 response
> +    public final static int FLAG_NEGOTIATE_TARGET_INFO = 0x00800000;
> +
> +    // Indicates that 128-bit encryption is supported
> +    public final static int FLAG_NEGOTIATE_128_BIT_ENCRYPTION = 0x20000000;
> +
> +    // Indicates that the client will provide an encrypted master key in the "Session Key"
> +    // field of the Type 3 message
> +    public final static int FLAG_NEGOTIATE_KEY_EXCHANGE = 0x40000000;
> +
> +    // Indicates that 56-bit encryption is supported
> +    public final static int FLAG_NEGOTIATE_56_BIT_ENCRYPTION = 0x80000000;
> +
> +    // WARN : These flags usage has not been identified
> +    public final static int FLAG_UNIDENTIFIED_1 = 0x00000008;
> +
> +    public final static int FLAG_UNIDENTIFIED_2 = 0x00000100; // Negotiate Netware ??!
> +
> +    public final static int FLAG_UNIDENTIFIED_3 = 0x00000400;
> +
> +    public final static int FLAG_UNIDENTIFIED_4 = 0x00100000; // Request Init Response ??!
> +
> +    public final static int FLAG_UNIDENTIFIED_5 = 0x00200000; // Request Accept Response ??!
> +
> +    public final static int FLAG_UNIDENTIFIED_6 = 0x00400000; // Request Non-NT Session Key ??!
> +
> +    public final static int FLAG_UNIDENTIFIED_7 = 0x01000000;
> +
> +    public final static int FLAG_UNIDENTIFIED_8 = 0x02000000;
> +
> +    public final static int FLAG_UNIDENTIFIED_9 = 0x04000000;
> +
> +    public final static int FLAG_UNIDENTIFIED_10 = 0x08000000;
> +
> +    public final static int FLAG_UNIDENTIFIED_11 = 0x10000000;
> +
> +    // Default minimal flag set
> +    public final static int DEFAULT_FLAGS = FLAG_NEGOTIATE_OEM
> +            | FLAG_NEGOTIATE_UNICODE | FLAG_NEGOTIATE_WORKSTATION_SUPPLIED
> +            | FLAG_NEGOTIATE_DOMAIN_SUPPLIED;
> +
> +    /**
> +     * Target Information sub blocks types. It may be that there are other
> +     * as-yet-unidentified sub block types as well.
> +     */
> +
> +    // Sub block terminator
> +    public final static short TARGET_INFORMATION_SUBBLOCK_TERMINATOR_TYPE = 0x0000;
> +
> +    // Server name
> +    public final static short TARGET_INFORMATION_SUBBLOCK_SERVER_TYPE = 0x0100;
> +
> +    // Domain name
> +    public final static short TARGET_INFORMATION_SUBBLOCK_DOMAIN_TYPE = 0x0200;
> +
> +    // Fully-qualified DNS host name (i.e., server.domain.com)
> +    public final static short TARGET_INFORMATION_SUBBLOCK_FQDNS_HOSTNAME_TYPE = 0x0300;
> +
> +    // DNS domain name (i.e., domain.com)
> +    public final static short TARGET_INFORMATION_SUBBLOCK_DNS_DOMAIN_NAME_TYPE = 0x0400;
> +
> +    // Apparently the "parent" DNS domain for servers in sub domains
> +    public final static short TARGET_INFORMATION_SUBBLOCK_PARENT_DNS_DOMAIN_NAME_TYPE = 0x0500;
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMConstants.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
> Added: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java
> URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java?rev=685389&view=auto
> ==============================================================================
> --- mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java (added)
> +++ mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java Tue Aug 12 17:05:41 2008
> @@ -0,0 +1,413 @@
> +/*
> + *  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.
> + *
> + */
> +package org.apache.mina.proxy.handlers.http.ntlm;
> +
> +import java.io.UnsupportedEncodingException;
> +import java.security.Key;
> +import java.security.MessageDigest;
> +
> +import javax.crypto.Cipher;
> +
> +import javax.crypto.spec.SecretKeySpec;
> +
> +/**
> + * NTLMResponses.java - Calculates the various Type 3 responses. Needs an MD4, MD5 and DES
> + * crypto provider (Please note that default provider doesn't provide MD4).
> + *
> + * Base code borrowed from : http://curl.haxx.se/rfc/ntlm.html
> + *
> + * @author <a href="mailto:doe_wanted@yahoo.fr">Edouard De Oliveira</a>
> + * @version $Revision:$, $Date:$
> + */
> +public class NTLMResponses {
> +
> +    // LAN Manager magic constant used in LM Response calculation
> +    public static byte[] LM_HASH_MAGIC_CONSTANT = null;
> +
> +    static {
> +        try {
> +            LM_HASH_MAGIC_CONSTANT = "KGS!@#$%".getBytes("US-ASCII");
> +        } catch (UnsupportedEncodingException e) {
> +            e.printStackTrace();
> +        }
> +    }
> +
> +    /**
> +     * Calculates the LM 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 LM Response.
> +     */
> +    public static byte[] getLMResponse(String password, byte[] challenge)
> +            throws Exception {
> +        byte[] lmHash = lmHash(password);
> +        return lmResponse(lmHash, challenge);
> +    }
> +
> +    /**
> +     * 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 Exception {
> +        byte[] ntlmHash = ntlmHash(password);
> +        return lmResponse(ntlmHash, challenge);
> +    }
> +
> +    /**
> +     * Calculates the NTLMv2 Response for the given challenge, using the
> +     * specified authentication target, username, password, target information
> +     * block, and client nonce.
> +     *
> +     * @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 clientNonce The random 8-byte client nonce.
> +     *
> +     * @return The NTLMv2 Response.
> +     */
> +    public static byte[] getNTLMv2Response(String target, String user,
> +            String password, byte[] targetInformation, byte[] challenge,
> +            byte[] clientNonce) throws Exception {
> +
> +        return getNTLMv2Response(target, user, password, targetInformation,
> +                challenge, clientNonce, System.currentTimeMillis());
> +    }
> +
> +    /**
> +     * Calculates the NTLMv2 Response for the given challenge, using the
> +     * specified authentication target, username, password, target information
> +     * block, and client nonce.
> +     *
> +     * @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 clientNonce The random 8-byte client nonce.
> +     * @param time The time stamp.
> +     *
> +     * @return The NTLMv2 Response.
> +     */
> +    public static byte[] getNTLMv2Response(String target, String user,
> +            String password, byte[] targetInformation, byte[] challenge,
> +            byte[] clientNonce, long time) throws Exception {
> +        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
> +        byte[] blob = createBlob(targetInformation, clientNonce, time);
> +        return lmv2Response(ntlmv2Hash, blob, challenge);
> +    }
> +
> +    /**
> +     * 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 clientNonce The random 8-byte client nonce.
> +     *
> +     * @return The LMv2 Response.
> +     */
> +    public static byte[] getLMv2Response(String target, String user,
> +            String password, byte[] challenge, byte[] clientNonce)
> +            throws Exception {
> +        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
> +        return lmv2Response(ntlmv2Hash, clientNonce, challenge);
> +    }
> +
> +    /**
> +     * Calculates the NTLM2 Session Response for the given challenge, using the
> +     * specified password and client nonce.
> +     *
> +     * @param password The user's password.
> +     * @param challenge The Type 2 challenge from the server.
> +     * @param clientNonce The random 8-byte client nonce.
> +     *
> +     * @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 nonce, null-padded to 24 bytes.
> +     */
> +    public static byte[] getNTLM2SessionResponse(String password,
> +            byte[] challenge, byte[] clientNonce) throws Exception {
> +        byte[] ntlmHash = ntlmHash(password);
> +        MessageDigest md5 = MessageDigest.getInstance("MD5");
> +        md5.update(challenge);
> +        md5.update(clientNonce);
> +        byte[] sessionHash = new byte[8];
> +        System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
> +        return lmResponse(ntlmHash, sessionHash);
> +    }
> +
> +    /**
> +     * 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 Exception {
> +        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);
> +        Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
> +        des.init(Cipher.ENCRYPT_MODE, lowKey);
> +        byte[] lowHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
> +        des.init(Cipher.ENCRYPT_MODE, highKey);
> +        byte[] highHash = des.doFinal(LM_HASH_MAGIC_CONSTANT);
> +        byte[] lmHash = new byte[16];
> +        System.arraycopy(lowHash, 0, lmHash, 0, 8);
> +        System.arraycopy(highHash, 0, lmHash, 8, 8);
> +        return lmHash;
> +    }
> +
> +    /**
> +     * 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 Exception {
> +        byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
> +        MessageDigest md4 = MessageDigest.getInstance("MD4");
> +        return md4.digest(unicodePassword);
> +    }
> +
> +    /**
> +     * 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 Exception {
> +        byte[] ntlmHash = ntlmHash(password);
> +        String identity = user.toUpperCase() + target;
> +        return hmacMD5(identity.getBytes("UnicodeLittleUnmarked"), ntlmHash);
> +    }
> +
> +    /**
> +     * 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 Exception {
> +        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;
> +    }
> +
> +    /**
> +     * 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 nonce).
> +     * @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[] clientData,
> +            byte[] challenge) throws Exception {
> +        byte[] data = new byte[challenge.length + clientData.length];
> +        System.arraycopy(challenge, 0, data, 0, challenge.length);
> +        System.arraycopy(clientData, 0, data, challenge.length,
> +                clientData.length);
> +        byte[] mac = hmacMD5(data, hash);
> +        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;
> +    }
> +
> +    /**
> +     * Creates the NTLMv2 blob from the given target information block and
> +     * client nonce.
> +     *
> +     * @param targetInformation The target information block from the Type 2
> +     * message.
> +     * @param clientNonce The random 8-byte client nonce.
> +     * @param time the time stamp.
> +     *
> +     * @return The blob, used in the calculation of the NTLMv2 Response.
> +     */
> +    private static byte[] createBlob(byte[] targetInformation,
> +            byte[] clientNonce, long time) {
> +        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 };
> +        byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00,
> +                (byte) 0x00 };
> +        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++) {
> +            timestamp[i] = (byte) time;
> +            time >>>= 8;
> +        }
> +        byte[] blob = new byte[blobSignature.length + reserved.length
> +                + timestamp.length + clientNonce.length + unknown1.length
> +                + targetInformation.length + unknown2.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(clientNonce, 0, blob, offset, clientNonce.length);
> +        offset += clientNonce.length;
> +        System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
> +        offset += unknown1.length;
> +        System.arraycopy(targetInformation, 0, blob, offset,
> +                targetInformation.length);
> +        offset += targetInformation.length;
> +        System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
> +        return blob;
> +    }
> +
> +    /**
> +     * Calculates the HMAC-MD5 hash of the given data using the specified
> +     * hashing key.
> +     *
> +     * @param data The data for which the hash will be calculated.
> +     * @param key The hashing key.
> +     *
> +     * @return The HMAC-MD5 hash of the given data.
> +     */
> +    public static byte[] hmacMD5(byte[] data, byte[] key) throws Exception {
> +        byte[] ipad = new byte[64];
> +        byte[] opad = new byte[64];
> +
> +        // Stores key in pads and XOR it with ipad and opad values
> +        for (int i = 0; i < 64; i++) {
> +            if (i < key.length) {
> +                ipad[i] = (byte) (key[i] ^ 0x36);
> +                opad[i] = (byte) (key[i] ^ 0x5c);
> +            } else {
> +                ipad[i] = 0x36;
> +                opad[i] = 0x5c;
> +            }
> +        }
> +
> +        byte[] content = new byte[data.length + 64];
> +        System.arraycopy(ipad, 0, content, 0, 64);
> +        System.arraycopy(data, 0, content, 64, data.length);
> +        MessageDigest md5 = MessageDigest.getInstance("MD5");
> +        data = md5.digest(content);
> +        content = new byte[data.length + 64];
> +        System.arraycopy(opad, 0, content, 0, 64);
> +        System.arraycopy(data, 0, content, 64, data.length);
> +        return md5.digest(content);
> +    }
> +
> +    /**
> +     * Creates a DES encryption key from the given key material.
> +     *
> +     * @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.
> +     */
> +    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");
> +    }
> +
> +    /**
> +     * 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;
> +            }
> +        }
> +    }
> +}
> \ No newline at end of file
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: mina/trunk/core/src/main/java/org/apache/mina/proxy/handlers/http/ntlm/NTLMResponses.java
> ------------------------------------------------------------------------------
>    svn:mime-type = text/plain
>
>
>