/*
 * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (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.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.kernel.security;

import com.arsdigita.util.ServletUtils;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.servlet.http.Cookie;
import org.apache.log4j.Logger;
import com.arsdigita.util.UncheckedWrapperException;

/**
 * Manages a string value stored in a cookie.
 *
 * @see CookieLoginModule
 *
 * @author Sameer Ajmani
 **/
public class CookieManager extends CredentialManager {

    public static final String versionId = "$Id: //core-platform/dev/src/com/arsdigita/kernel/security/CookieManager.java#8 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";
    private static final Logger s_log =
        Logger.getLogger(CookieManager.class.getName());

    public void initialize(CredentialLoginModule module,
                           Subject subject,
                           CallbackHandler handler,
                           Map shared,
                           Map options) {
        super.initialize(module, subject, handler, shared, options);
    }

    /**
     * Determines whether <code>setValue()</code> should be called.
     *
     * @param value the new value for the credential
     *
     * @return <code>true</code> if the credential is not set or has the
     * wrong value or should be renewed, <code>false</code> otherwise.
     **/
    protected boolean shouldSetValue(String value)
        throws LoginException {
        if (getModule().requestIsExcluded()) {
            return false;
        }
        return !getModule().credentialIsSet()
            || !getModule().credentialHasValue(value)
            || getModule().credentialIsOld();
    }

    /**
     * Returns the value of the cookie named
     * <code>getModule().getCredentialName()</code>.
     *
     * @return the value of the cookie named
     * <code>getModule().getCredentialName()</code>.
     *
     * @throws CredentialNotFoundException if the cookie is not in the
     * current request.
     *
     * @throws LoginException if an error occurs.
     **/
    protected final String getValue()
        throws LoginException {
        s_log.debug("START getValue");
        String value = ServletUtils.getCookieValue
            (getModule().getRequest(),
             getModule().getCredentialName());
        if (value == null) {
            s_log.debug("FAILURE getValue");
            throw new CredentialNotFoundException();
        }
        s_log.debug("SUCCESS getValue: "+value);
        return value;
    }

    /**
     * Sets the cookie named <code>getModule().getCredentialName()</code> to
     * the given value.
     *
     * @throws LoginException if an error occurs.
     **/
    protected final void setValue(String value)
        throws LoginException {
        setCookie(getModule().getCredentialName(), value, getCookieMaxAge());
    }

    /**
     * Deletes the cookie named
     * <code>getModule().getCredentialName()</code>.
     *
     * @throws LoginException if an error occurs.
     **/
    protected final void deleteValue()
        throws LoginException {
        deleteCookie(getModule().getCredentialName());
    }

    /**
     * Deletes the named cookie.
     **/
    private void deleteCookie(String name)
        throws LoginException {
        if (isCookieSet(name)) {
            s_log.debug("deleting existing cookie");
            setCookie(name, "", 0); // maxAge == 0 deletes cookie
        } else {
            s_log.debug("Not deleting cookie since it doesn't exist!");
        }
    }

    private boolean isCookieSet(String name) {
        Cookie cookies[] = null;
        try {
            cookies = getModule().getRequest().getCookies();
        } catch (LoginException ex) {
            throw new UncheckedWrapperException(ex);
        }

        if (cookies == null)
            return false;

        for (int i = 0 ; i < cookies.length ; i++)
            if (cookies[i].getName().equals(name))
                return true;

        return false;
    }

    /**
     * Sets the named cookie to the given value.
     **/
    private void setCookie(String name, String value, int maxAge)
        throws LoginException {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(maxAge);
        cookie.setPath("/");
        cookie.setSecure(getModule().isSecure());
        getModule().getResponse().addCookie(cookie);
    }

    /**
     * Determines the correct max age for the cookie in seconds.  A return
     * value of -1 means the cookie should be deleted when the client's
     * browser quits.
     *
     * @return <code>FOREVER_SECS</code> if the user has requested permanent
     * login; -1 otherwise.
     **/
    protected int getCookieMaxAge()
        throws LoginException {
        return getModule().getForever() ?
            (int)CredentialLoginModule.FOREVER_SECS : -1;
    }
}
