/*  Copyright (c) 2004 The Nutch Organization.  All rights reserved.   */
/*  Use subject to the conditions in http://www.nutch.org/LICENSE.txt. */

package org.apache.nutch.protocol.httpclient;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Base64;

import org.apache.nutch.util.LogFormatter;
import org.apache.nutch.util.NutchConf;

/**
 * Implementation of RFC 2617 Basic Authentication.  Usernames and passwords are stored 
 * in standard Nutch configuration files using the following properties:
 *      http.auth.basic.<realm>.user
 *      http.auth.basic.<realm>.pass
 *
 * @author    Matt Tencati
 */
public class HttpBasicAuthentication implements HttpAuthentication {
	public static final Logger LOG =
		LogFormatter.getLogger("net.nutch.net.HttpBasicAuthentication");

	static {
		if (NutchConf.get().getBoolean("http.auth.verbose", false))
			LOG.setLevel(Level.FINE);
	}

	private static Pattern basic = Pattern.compile("[bB][aA][sS][iI][cC] [rR][eE][aA][lL][mM]=\"(\\w*)\"");
	
    private static Map authMap = new TreeMap();
    
    private String challenge = null;
    private ArrayList credentials = null;
    private String realm = null;

    /**
     *  Construct an HttpBasicAuthentication for the given challenge
     *  parameters. The challenge parameters are returned by the web
     *  server using a WWW-Authenticate header. This will typically be
     *  represented by single line of the form <code>WWW-Authenticate: Basic realm="myrealm"</code>
     *
     * @param  challenge  WWW-Authenticate header from web server
     */
    protected HttpBasicAuthentication(String challenge) throws HttpAuthenticationException {
        this.challenge = challenge;
        LOG.fine("BasicAuthentication challenge is " + challenge);
        credentials = new ArrayList();
        
        String username = NutchConf.get().get("http.auth.basic." + challenge + ".user");
        LOG.fine("BasicAuthentication username=" + username);
        String password = NutchConf.get().get("http.auth.basic." + challenge + ".user");
        LOG.fine("BasicAuthentication password=" + password);
        
        if (username == null) {
        	throw new HttpAuthenticationException("Username for " + challenge + " is null");
        }

        if (password == null) {
        	throw new HttpAuthenticationException("Password for " + challenge + " is null");
        }
        
        byte[] credBytes = (username + ":" + password).getBytes();
        credentials.add("Authorization: Basic " + new String(Base64.encodeBase64(credBytes)));
        LOG.fine("Basic credentials: " + credentials);
    }

    /**
     *  Gets the Basic credentials generated by this
     *  HttpBasicAuthentication object
     *
     * @return    Credentials in the form of <code>Authorization: Basic &lt;Base64 encoded userid:password&gt;
     *
     */
    public List getCredentials() {
        return credentials;
    }


   /**
    * Gets the realm attribute of the HttpBasicAuthentication object.
    * This should have been supplied to the {@link #getAuthentication(String)} static method
    *
    * @return    The realm
    */
    public String getRealm() {
        return realm;
    }

    /**
     * This method is responsible for providing Basic authentication information.  The
     * method caches authentication information for each realm so that the required
     * authentication information does not need to be regenerated for every request.
     *  
     * @param challenge The challenge string provided by the webserver.  This is the
     * text which follows the WWW-Authenticate header, including the Basic tag.
     * @return An HttpBasicAuthentication object or null 
     * if unable to generate appropriate credentials.
     */
    public static HttpBasicAuthentication getAuthentication(String challenge) {
        if (challenge == null) return null;
        Matcher basicMatcher = basic.matcher(challenge);
        if (basicMatcher.matches()) {
        	String realm = basicMatcher.group(1);
	        Object auth = authMap.get(realm);
	        if (auth == null) {
	            HttpBasicAuthentication newAuth = null;
	            try {
	            	newAuth = new HttpBasicAuthentication(realm);
	            } catch (HttpAuthenticationException hae) { 
	            	LOG.fine("HttpBasicAuthentication failed for " + challenge);
	            }
	            authMap.put(realm, newAuth);
	            return newAuth;
	        } else {
	            return (HttpBasicAuthentication) auth;
	        }
        }
        return null;
    }
    
	/**
	 * Provides a pattern which can be used by an outside resource to determine if 
	 * this class can provide credentials based on simple header information.  It does
	 * not calculate any information regarding realms or challenges.
	 * 
	 * @return Returns a Pattern which will match a Basic WWW-Authenticate header.
	 */
	public static final Pattern getBasicPattern() {
		return basic;
	}
}


@@ -2239,36 +2239,40 @@
 + challenge + %22.
-user
+password
 %22);%0A        LOG.

Fix a typo. Spotted by Juho Maekinen (sorry, svn wouldn't accept 'a umlaut' ...)