Index: examples/BasicAuthenticationExample.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/examples/BasicAuthenticationExample.java,v retrieving revision 1.1 diff -u -r1.1 BasicAuthenticationExample.java --- examples/BasicAuthenticationExample.java 13 Aug 2003 20:30:40 -0000 1.1 +++ examples/BasicAuthenticationExample.java 22 Jan 2004 13:21:28 -0000 @@ -59,7 +59,8 @@ * [Additional notices, if required by prior licensing conditions] * */ - import org.apache.commons.httpclient.HttpClient; + +import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.methods.GetMethod; @@ -77,6 +78,7 @@ * Constructor for BasicAuthenticatonExample. */ public BasicAuthenticationExample() { + super(); } public static void main(String[] args) throws Exception { @@ -103,13 +105,16 @@ // It will then be up to the client to handle the authentication. get.setDoAuthentication( true ); - // execute the GET - int status = client.executeMethod( get ); - - // print the status and response - System.out.println(status + "\n" + get.getResponseBodyAsString()); - - // release any connection resources used by the method - get.releaseConnection(); + try { + // execute the GET + int status = client.executeMethod( get ); + + // print the status and response + System.out.println(status + "\n" + get.getResponseBodyAsString()); + + } finally { + // release any connection resources used by the method + get.releaseConnection(); + } } } Index: examples/InteractiveAuthenticationExample.java =================================================================== RCS file: examples/InteractiveAuthenticationExample.java diff -N examples/InteractiveAuthenticationExample.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ examples/InteractiveAuthenticationExample.java 22 Jan 2004 13:21:28 -0000 @@ -0,0 +1,164 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "HttpClient", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.NTCredentials; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScheme; +import org.apache.commons.httpclient.auth.CredentialsCallback; +import org.apache.commons.httpclient.auth.CredentialsNotAvailableException; +import org.apache.commons.httpclient.auth.CredentialsPrompter; +import org.apache.commons.httpclient.auth.NTLMScheme; +import org.apache.commons.httpclient.auth.RFC2617Scheme; +import org.apache.commons.httpclient.methods.GetMethod; + +/** + * A simple example that uses HttpClient to perform interactive + * authentication. + * + * @author Oleg Kalnichevski + */ +public class InteractiveAuthenticationExample { + + /** + * Constructor for InteractiveAuthenticationExample. + */ + public InteractiveAuthenticationExample() { + super(); + } + + public static void main(String[] args) throws Exception { + + InteractiveAuthenticationExample demo = new InteractiveAuthenticationExample(); + demo.doDemo(); + } + + private void doDemo() throws IOException { + + CredentialsPrompter.registerCredentialsCallback(new ConsoleAuthPrompter()); + + HttpClient client = new HttpClient(); + GetMethod httpget = new GetMethod("http://target-host/requires-auth.html"); + httpget.setDoAuthentication(true); + try { + // execute the GET + int status = client.executeMethod(httpget); + // print the status and response + System.out.println(httpget.getStatusLine().toString()); + System.out.println(httpget.getResponseBodyAsString()); + } finally { + // release any connection resources used by the method + httpget.releaseConnection(); + } + } + + public class ConsoleAuthPrompter implements CredentialsCallback { + + private BufferedReader in = null; + public ConsoleAuthPrompter() { + super(); + this.in = new BufferedReader(new InputStreamReader(System.in)); + } + + private String readConsole() throws IOException { + return this.in.readLine(); + } + + public Credentials getCredentials(final AuthScheme authscheme, final String host) + throws CredentialsNotAvailableException { + if (authscheme == null) { + return null; + } + try{ + if (authscheme instanceof NTLMScheme) { + System.out.println(host + " requires Windows authentication"); + System.out.print("Enter domain: "); + String domain = readConsole(); + System.out.print("Enter username: "); + String user = readConsole(); + System.out.print("Enter password: "); + String password = readConsole(); + return new NTCredentials(user, password, host, domain); + } else + if (authscheme instanceof RFC2617Scheme) { + System.out.println(host + " requires authentication with the realm '" + + authscheme.getRealm() + "'"); + System.out.print("Enter username: "); + String user = readConsole(); + System.out.print("Enter password: "); + String password = readConsole(); + return new UsernamePasswordCredentials(user, password); + } else { + throw new CredentialsNotAvailableException("Unsupported authentication scheme: " + + authscheme.getSchemeName()); + } + } catch (IOException e) { + throw new CredentialsNotAvailableException(e.getMessage(), e); + } + } + } +} Index: java/org/apache/commons/httpclient/HttpMethodDirector.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodDirector.java,v retrieving revision 1.14 diff -u -r1.14 HttpMethodDirector.java --- java/org/apache/commons/httpclient/HttpMethodDirector.java 14 Jan 2004 18:59:27 -0000 1.14 +++ java/org/apache/commons/httpclient/HttpMethodDirector.java 22 Jan 2004 13:21:29 -0000 @@ -73,6 +73,8 @@ import org.apache.commons.httpclient.auth.AuthPolicy; import org.apache.commons.httpclient.auth.AuthScheme; import org.apache.commons.httpclient.auth.AuthenticationException; +import org.apache.commons.httpclient.auth.CredentialsNotAvailableException; +import org.apache.commons.httpclient.auth.CredentialsPrompter; import org.apache.commons.httpclient.auth.MalformedChallengeException; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.httpclient.params.HttpParams; @@ -645,20 +647,25 @@ if ((this.authState == AUTH_WWW_REQUIRED) && (this.authScheme.isComplete())) { // Already tried and failed - if (LOG.isInfoEnabled()) { - StringBuffer buffer = new StringBuffer(); - buffer.append("Attempt to authenticate with '"); - buffer.append(this.authScheme.getRealm()); - buffer.append("' authentication realm at "); - String host = this.conn.getVirtualHost(); - if (host == null) { - host = this.conn.getHost(); + Credentials credentials = promptForCredentials(); + if (credentials == null) { + if (LOG.isInfoEnabled()) { + StringBuffer buffer = new StringBuffer(); + buffer.append("Attempt to authenticate with '"); + buffer.append(this.authScheme.getRealm()); + buffer.append("' authentication realm at "); + String host = this.conn.getVirtualHost(); + if (host == null) { + host = this.conn.getHost(); + } + buffer.append(host); + buffer.append(" failed"); + LOG.info(buffer.toString()); } - buffer.append(host); - buffer.append(" failed"); - LOG.info(buffer.toString()); + return false; + } else { + return true; } - return false; } else { this.authState = AUTH_WWW_REQUIRED; @@ -669,6 +676,9 @@ String realm = this.authScheme.getRealm(); Credentials credentials = this.state.getCredentials(realm, host); if (credentials == null) { + credentials = promptForCredentials(); + } + if (credentials == null) { if (LOG.isInfoEnabled()) { StringBuffer buffer = new StringBuffer(); buffer.append("No credentials available for the "); @@ -712,16 +722,21 @@ if ((this.authState == AUTH_PROXY_REQUIRED) && (this.proxyAuthScheme.isComplete())) { // Already tried and failed - if (LOG.isInfoEnabled()) { - StringBuffer buffer = new StringBuffer(); - buffer.append("Attempt to authenticate with '"); - buffer.append(this.authScheme.getRealm()); - buffer.append("' proxy authentication realm at "); - buffer.append(this.conn.getProxyHost()); - buffer.append(" failed"); - LOG.info(buffer.toString()); + Credentials credentials = promptForProxyCredentials(); + if (credentials == null) { + if (LOG.isInfoEnabled()) { + StringBuffer buffer = new StringBuffer(); + buffer.append("Attempt to authenticate with '"); + buffer.append(this.proxyAuthScheme.getRealm()); + buffer.append("' proxy authentication realm at "); + buffer.append(this.conn.getProxyHost()); + buffer.append(" failed"); + LOG.info(buffer.toString()); + } + return false; + } else { + return true; } - return false; } else { this.authState = AUTH_PROXY_REQUIRED; @@ -729,6 +744,9 @@ String realm = this.proxyAuthScheme.getRealm(); Credentials credentials = this.state.getProxyCredentials(realm, host); if (credentials == null) { + credentials = promptForProxyCredentials(); + } + if (credentials == null) { if (LOG.isInfoEnabled()) { StringBuffer buffer = new StringBuffer(); buffer.append("No credentials available for the "); @@ -855,6 +873,46 @@ default: return false; } //end of switch + } + + private Credentials promptForCredentials() { + String host = this.conn.getVirtualHost(); + if (host == null) { + host = this.conn.getHost(); + } + Credentials creds = null; + try { + creds = CredentialsPrompter.promptForCredentials(this.authScheme, host); + } catch (CredentialsNotAvailableException e) { + LOG.warn(e.getMessage()); + } + if (creds != null) { + String realm = this.authScheme.getRealm(); + this.state.setCredentials(realm, host, creds); + if (LOG.isDebugEnabled()) { + LOG.debug("New credentials for realm '" + realm + "' at " + host); + } + } + return creds; + } + + + private Credentials promptForProxyCredentials() { + String host = this.conn.getProxyHost(); + Credentials creds = null; + try { + creds = CredentialsPrompter.promptForCredentials(this.proxyAuthScheme, host); + } catch (CredentialsNotAvailableException e) { + LOG.warn(e.getMessage()); + } + if (creds != null) { + String realm = this.proxyAuthScheme.getRealm(); + this.state.setProxyCredentials(realm, host, creds); + if (LOG.isDebugEnabled()) { + LOG.debug("New proxy credentials for realm '" + realm + "' at " + host); + } + } + return creds; } /** Index: java/org/apache/commons/httpclient/HttpState.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v retrieving revision 1.29 diff -u -r1.29 HttpState.java --- java/org/apache/commons/httpclient/HttpState.java 29 Oct 2003 03:12:27 -0000 1.29 +++ java/org/apache/commons/httpclient/HttpState.java 22 Jan 2004 13:21:30 -0000 @@ -128,7 +128,8 @@ private boolean preemptive = false; private int cookiePolicy = 0; - // -------------------------------------------------------- Class Variables + + // -------------------------------------------------------- Class Variables /** Log object for this class. */ private static final Log LOG = LogFactory.getLog(HttpState.class); @@ -477,6 +478,9 @@ LOG.trace("enter HttpState.getCredentials(String, String"); return matchCredentials(this.proxyCred, realm, proxyHost); } + + + /** * Returns a string representation of this HTTP state. Index: java/org/apache/commons/httpclient/auth/CredentialsCallback.java =================================================================== RCS file: java/org/apache/commons/httpclient/auth/CredentialsCallback.java diff -N java/org/apache/commons/httpclient/auth/CredentialsCallback.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/auth/CredentialsCallback.java 22 Jan 2004 13:21:30 -0000 @@ -0,0 +1,10 @@ +package org.apache.commons.httpclient.auth; + +import org.apache.commons.httpclient.Credentials; + +public interface CredentialsCallback { + + public Credentials getCredentials(final AuthScheme authscheme, final String host) + throws CredentialsNotAvailableException; + +} Index: java/org/apache/commons/httpclient/auth/CredentialsPrompter.java =================================================================== RCS file: java/org/apache/commons/httpclient/auth/CredentialsPrompter.java diff -N java/org/apache/commons/httpclient/auth/CredentialsPrompter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/auth/CredentialsPrompter.java 22 Jan 2004 13:21:30 -0000 @@ -0,0 +1,62 @@ +package org.apache.commons.httpclient.auth; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public final class CredentialsPrompter { + + /** Log object for this class. */ + private static final Log LOG = LogFactory.getLog(CredentialsPrompter.class); + + private static List CALLBACKS; + + private CredentialsPrompter() { + super(); + } + + public static synchronized void registerCredentialsCallback(final CredentialsCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("Callback interface may not be null"); + } + if (CALLBACKS == null) { + CALLBACKS = new ArrayList(); + } + CALLBACKS.add(callback); + } + + public static synchronized void unregisterCredentialsCallback(final CredentialsCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("Callback interface may not be null"); + } + CALLBACKS.remove(callback); + if (CALLBACKS.isEmpty()) { + CALLBACKS = null; + } + } + + public static synchronized Credentials promptForCredentials( + final AuthScheme authscheme, final String host) throws CredentialsNotAvailableException + { + if (authscheme == null) { + throw new IllegalArgumentException("Authentication scheme may not be null"); + } + if (CALLBACKS != null) { + LOG.debug("Prompting for credentials"); + Iterator items = CALLBACKS.iterator(); + while (items.hasNext()) { + CredentialsCallback callback = (CredentialsCallback)items.next(); + Credentials creds = callback.getCredentials(authscheme, host); + if (creds != null) { + return creds; + } + } + } + return null; + } + +} Index: java/org/apache/commons/httpclient/auth/NTLMScheme.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/NTLMScheme.java,v retrieving revision 1.17 diff -u -r1.17 NTLMScheme.java --- java/org/apache/commons/httpclient/auth/NTLMScheme.java 21 Jan 2004 21:10:44 -0000 1.17 +++ java/org/apache/commons/httpclient/auth/NTLMScheme.java 22 Jan 2004 13:21:30 -0000 @@ -356,7 +356,7 @@ } NTLM ntlm = new NTLM(); String response = null; - if (this.state == INITIATED) { + if (this.state == INITIATED || this.state == FAILED) { response = ntlm.getType1Message( ntcredentials.getHost(), ntcredentials.getDomain());