/*  Copyright (c) 2002 by Oracle Corporation. All Rights Reserved. */

package oracle.portal.provider.v1.http;

import java.io.IOException;
import java.util.Hashtable;
import oracle.webdb.utils.SimpleStringBuffer;
import HTTPClient.AuthorizationHandler;
import HTTPClient.AuthorizationInfo;
import HTTPClient.RoRequest;
import HTTPClient.RoResponse;
import HTTPClient.AuthSchemeNotImplException;
import HTTPClient.NVPair;
import HTTPClient.Response;
import HTTPClient.Codecs;

/**
 * URLAuthHandler serves the purpose of authorization handler for specific
 * URL Based portlet contexts.
 *
 * As an extension to the DefaultAuthHandler, it provides couple of more 
 * methods to add and remove context specific authorizations. Currently 
 * implementation is limited to 'basic' mode of authentication.
 */
public class URLAuthHandler implements AuthorizationHandler
{
    private static Hashtable sPasswordStore = new Hashtable(100);
    private static Hashtable sAuthInfoStore = new Hashtable(100);

    /**
     * Adds login user/password object to the password store in a thread 
     * safe manner.
     *
     * @param context Object acting as a context for the login information.
     * @param login Name value pair containing user name/password.
     */
    public static void addAuthorization(Object context, NVPair login)
    {
        synchronized (sPasswordStore)
        {
            sPasswordStore.put(context, login);
        }
    } 

    /**
     * Removes Login information specific to the give context, from password
     * store and removes authorization information from HTTPClient's
     * AuthorizationModule as well.
     *
     * @param context Object acting as a context for the login information.
     */
    public static void removeAuthorization(Object context)
    {
        synchronized (sPasswordStore)
        {
            sPasswordStore.remove(context);

            // Make sure the authentication details are completely removed
            // for this context
            AuthorizationInfo authInfo = 
                (AuthorizationInfo) sAuthInfoStore.remove(context);
            if (authInfo != null)
            {
                AuthorizationInfo.removeAuthorization(authInfo, context);
            }
        }
    }
    /**
     * This method is called whenever a 401 or 407 response is received and no 
     * candidate info is found in the list of known auth info. Usually this 
     * method will query the user for the necessary info. 

     * If the returned info is not null it will be added to the list of known 
     * info. If the info is valid for more than one (host, port, realm, scheme) 
     * tuple then this method must add the corresponding auth infos itself.   
     *
     * @param challenge - the parsed challenge from the server; 
     *                    the host, port, scheme, realm
     * @param req - the request which provoked this response.
     * @param resp - the full response.
     * @return the authorization info to use when retrying the request, 
     *         or null if the request is not to be retried. The necessary 
     *         info includes the host, port, scheme and realm as given in the 
     *         challenge parameter, plus either the basic cookie or any 
     *         necessary params.
     */
    public AuthorizationInfo getAuthorization(AuthorizationInfo challenge,
                                              RoRequest req, RoResponse resp)
        throws AuthSchemeNotImplException
    {

        if (!challenge.getScheme().equalsIgnoreCase("basic"))
        {
            return null;
        }

        // Get hold of the object representing our context
        Object context = req.getConnection().getContext();

        // Look up the username / password info and create an appropriate
        // AuthorizationInfo
        synchronized (sPasswordStore)
        {
            NVPair login = (NVPair)sPasswordStore.get(context);
            AuthorizationInfo authInfo = null;
            if (login != null)
            {
                authInfo = new AuthorizationInfo(challenge.getHost(),
                                                 challenge.getPort(),
                                                 challenge.getScheme(),
                                                 challenge.getRealm(),
                                                 Codecs.base64Encode(
                                                     new SimpleStringBuffer(30)
                                                     .append(login.getName())
                                                     .append(':')
                                                     .append(login.getValue())
                                                     .toString()));
                // Store the AuthorizationInfo against this context, so
                // that we can delete it later
                sAuthInfoStore.put(context, authInfo);
            }
            return authInfo;
        }
    }
    //Default implementation for the interface methods.
    
    public AuthorizationInfo fixupAuthInfo(AuthorizationInfo authorizationinfo, 
        RoRequest rorequest, 
        AuthorizationInfo authorizationinfo1, 
        RoResponse roresponse)
        throws AuthSchemeNotImplException
    {
        return null;
    }

    //Default implementation for the interface methods.
    public void handleAuthHeaders(Response response, 
        RoRequest rorequest, 
        AuthorizationInfo authorizationinfo, 
        AuthorizationInfo authorizationinfo1)
        throws IOException
    {
    }

    //Default implementation for the interface methods.
    public void handleAuthTrailers(Response response, 
        RoRequest rorequest, 
        AuthorizationInfo authorizationinfo, 
        AuthorizationInfo authorizationinfo1)
        throws IOException
    {
    }
}
