/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.web.connector.coyote;

import java.util.logging.Logger;
import java.util.logging.Level;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.coyote.tomcat5.CoyoteRequest;
import org.apache.coyote.tomcat5.CoyoteResponse;
import org.apache.coyote.tomcat5.CoyoteConnector;
import org.apache.coyote.tomcat5.CoyoteInputStream;
import org.apache.coyote.tomcat5.CoyoteReader;
import org.apache.coyote.tomcat5.InputBuffer;
import org.apache.tomcat.util.http.Parameters;
// START GlassFish 898
import org.apache.tomcat.util.http.ServerCookie;
// END GlassFish 898
import com.sun.enterprise.web.PwcWebModule;
import com.sun.enterprise.web.session.SessionCookieConfig;
import com.sun.enterprise.web.logging.pwc.LogDomains;

/**
 * Customized version of the Tomcat 5 CoyoteRequest
 * This is required for supporting Web Programmatic Login and setting the
 * request encoding (charset).
 *
 * @author Jeanfrancois Arcand
 * @author Jan Luehe
 */
public class PwcCoyoteRequest extends CoyoteRequest {

    private static Logger logger = LogDomains.getLogger(LogDomains.PWC_LOGGER);

    // Have we already determined request encoding from sun-web.xml?
    private boolean sunWebXmlChecked = false;

    // START SJSAS 6346738
    private byte[] formData = null;
    private int formDataLen = 0;
    // END SJSAS 6346738

    public void setContext(Context ctx) {
        if (ctx == null) {
            // Invalid request. Response will be handled by
            // the StandardEngineValve
            return;
        }
        
        super.setContext(ctx);
        CoyoteResponse response = (CoyoteResponse) getResponse();
        // Assert response!=null
        if (response != null) {
            String[] cacheControls = ((PwcWebModule) ctx).getCacheControls();
            for (int i=0; cacheControls!=null && i<cacheControls.length; i++) {
                response.addHeader("Cache-Control", cacheControls[i]);
            }
        }

        sunWebXmlChecked = false;
    }

    public BufferedReader getReader() throws IOException {
        if (super.getCharacterEncoding() == null) {
            setRequestEncodingFromSunWebXml();
        }
        return super.getReader();
    }


    /**
     * Return the character encoding for this Request.
     *
     * If there is no request charset specified in the request, determines and
     * sets the request charset using the locale-charset-info,
     * locale-charset-map, and parameter-encoding elements provided in the
     * sun-web.xml.
     */
    public String getCharacterEncoding() {
        String enc = super.getCharacterEncoding();
        if (enc != null) {
            return enc;
        }
    
        boolean encodingFound = setRequestEncodingFromSunWebXml();
        if (encodingFound) {
            return super.getCharacterEncoding();
        } else {
            return null;
        }
    }


    /*
     * Configures the given JSESSIONID cookie with the cookie-properties from
     * sun-web.xml.
     *
     * @param cookie The JSESSIONID cookie to be configured
     */
    public void configureSessionCookie(Cookie cookie) {

        super.configureSessionCookie(cookie);

        PwcWebModule wm = (PwcWebModule) getContext();
        SessionCookieConfig cookieConfig = wm.getSessionCookieConfig();

        if (cookieConfig != null) {

            String name = cookieConfig.getName();
            if (name != null && !name.equals(Globals.SESSION_COOKIE_NAME)) {
                logger.log(Level.WARNING,
                           "pe_coyote.request.illegal_cookie_name",
                           new String[] { name, Globals.SESSION_COOKIE_NAME });
            }
     
            if (cookieConfig.getPath() != null) {
                cookie.setPath(cookieConfig.getPath());
            }

            cookie.setMaxAge(cookieConfig.getMaxAge());

            if (cookieConfig.getDomain() != null) {
                cookie.setDomain(cookieConfig.getDomain());
            }

            if (cookieConfig.getComment() != null) {
                cookie.setVersion(1);
                cookie.setComment(cookieConfig.getComment());
            }

            if (!cookieConfig.getSecure().equalsIgnoreCase(SessionCookieConfig.DYNAMIC_SECURE)) {
                cookie.setSecure(Boolean.parseBoolean(cookieConfig.getSecure()));
            }
        }
    }
    

    // START SJSAS 6346738
    public void recycle() {
        super.recycle();
        formDataLen = 0;
        sunWebXmlChecked = false;
    }
    // END SJSAS 6346738
            

    /**
     * Determines and sets the request charset using the locale-charset-info,
     * locale-charset-map, and parameter-encoding elements provided in the
     * sun-web.xml.
     *
     * @return true if a request encoding has been determined and set,
     * false otherwise
     */
    private boolean setRequestEncodingFromSunWebXml() {

        if (sunWebXmlChecked) {
            return false;
        }

        sunWebXmlChecked = true;

        PwcWebModule wm = (PwcWebModule) getContext();

        String encoding = getFormHintFieldEncoding(wm);
        if (encoding == null) {
            encoding = wm.getDefaultCharset();
            if (encoding == null && wm.hasLocaleToCharsetMapping()) {
                encoding = wm.mapLocalesToCharset(getLocales());
            }
        }

        if (encoding != null) {
            try {
                setCharacterEncoding(encoding);
            } catch (UnsupportedEncodingException uee) {
                logger.log(Level.WARNING, "pe_coyote.request.encoding", uee);
            }
        }

        return (encoding != null);
    }


    /*
     * Returns the value of the query parameter whose name
     * corresponds to the value of the form-hint-field attribute of the
     * <parameter-encoding> element in sun-web.xml.
     *
     * @return The value of the query parameter whose name corresponds to the
     * value of the form-hint-field attribute in sun-web.xml, or null if the
     * request does not have any query string, or the query string does not
     * contain a query parameter with that name
     */
    private String getFormHintFieldEncoding(PwcWebModule wm) {

        String encoding = null;

        String formHintField = wm.getFormHintField();
        if (formHintField == null){
            return null;
        }

        if ("POST".equalsIgnoreCase(getMethod())) {
            // POST
            encoding = getPostDataEncoding(formHintField);
        } else {
            String query = getQueryString();
            if (query != null) {
                encoding = parseFormHintField(query, formHintField);
            }
        }

        return encoding;
    }
        
    
    private String getPostDataEncoding(String formHintField) {

        if (!getMethod().equalsIgnoreCase("POST")) {
            return null;
        }

        String contentType = getContentType();
        if (contentType == null)
            contentType = "";
        int semicolon = contentType.indexOf(';');
        if (semicolon >= 0) {
            contentType = contentType.substring(0, semicolon).trim();
        } else {
            contentType = contentType.trim();
        }
        if (!("application/x-www-form-urlencoded".equals(contentType))) {
            return null;
        }

        int len = getContentLength();
        if (len <= 0) {
            return null;
        }
        int maxPostSize = ((CoyoteConnector) connector).getMaxPostSize();
        if ((maxPostSize > 0) && (len > maxPostSize)) {
            logger.log(Level.WARNING, "peCoyoteRequest.postTooLarge");
            throw new IllegalStateException("Post too large");
        }

        String encoding = null;

        try {
            formData = null;
            if (len < CACHED_POST_LEN) {
                if (postData == null)
                    postData = new byte[CACHED_POST_LEN];
                formData = postData;
            } else {
                formData = new byte[len];
            }
            int actualLen = readPostBody(formData, len);
            if (actualLen == len) {
                // START SJSAS 6346738
                formDataLen = actualLen;
                // END SJSAS 6346738
                String formDataString = new String(formData).substring(0, len);
                encoding = parseFormHintField(formDataString, formHintField);
            }
        } catch (Throwable t) {
            ; // Ignore
        }

        return encoding;
    }


    /*
     * Parses the value of the specified form-hint-field from the given
     * parameter string.
     *
     * @param paramsString Parameter string
     * @param formHintField From-hint-field
     *
     * @return Value of form-hint-field, or null if not found
     */
    private String parseFormHintField(String paramsString,
                                      String formHintField) {

        String encoding = null;

        formHintField += "=";            
        int index = paramsString.indexOf(formHintField);
        if (index != -1) {
            int endIndex = paramsString.indexOf('&', index);
            if (endIndex != -1) {
                encoding = paramsString.substring(
                    index + formHintField.length(), endIndex);
            } else {
                encoding = paramsString.substring(
                    index + formHintField.length());
            }
        }

        return encoding;
    }


    // START SJSAS 6346738
    /**
     * Gets the POST body of this request.
     *
     * @return The POST body of this request
     */
    protected byte[] getPostBody() throws IOException {

        if (formDataLen > 0) {
            // POST body already read
            return formData;
        } else {
            return super.getPostBody();
        } 
    }
    // END SJSAS 6346738


    // START GlassFish 898
    protected Cookie makeCookie(ServerCookie scookie) {

        PwcWebModule wm = (PwcWebModule) getContext();
        boolean encodeCookies = false;
        if (wm != null && wm.getEncodeCookies()) {
            encodeCookies = true;
        }

        return makeCookie(scookie, encodeCookies);
    }
    // END GlassFish 898

}
