// JigsawHttpServletRequest.java
// $Id: JigsawHttpServletRequest.java,v 1.16 1998/01/22 14:09:35 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.servlet;

import java.io.*;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

import org.w3c.util.*;
import org.w3c.jigsaw.http.*;
import org.w3c.jigsaw.forms.URLDecoder;
import org.w3c.jigsaw.forms.URLDecoderException;
import org.w3c.www.http.*;
import org.w3c.www.mime.*;
import org.w3c.jigsaw.auth.AuthFilter; // for auth infos access

import org.w3c.tools.resources.*;

class HeaderNames implements Enumeration {
  // The HeaderDescription enumeration
  Enumeration e = null;

  public boolean hasMoreElements() {
    return e.hasMoreElements();
  }

  public Object nextElement() {
    HeaderDescription d = (HeaderDescription) e.nextElement();
    return d.getName();
  }

  HeaderNames(Enumeration e) {
    this.e = e ;
  }

}

/**
 *  @author Alexandre Rafalovitch <alex@access.com.au>
 *  @author Anselm Baird-Smith <abaird@w3.org>
 *  @author Benoit Mahe <bmahe@sophia.inria.fr>
 */

public class JigsawHttpServletRequest implements HttpServletRequest {

  public final static 
  String STATE_PARAMETERS = "org.w3c.jigsaw.servlet.stateParam";

  private static MimeType type = MimeType.APPLICATION_X_WWW_FORM_URLENCODED ;
  /** 
   * The initial request.
   */
  private Request request = null;
  /**
   * The attached servlet.
   */
  private Servlet servlet = null;
  /**
   * The lazyly computed queryParameters hashtable.
   */
  private Hashtable queryParameters = null;

  private synchronized void prepareQueryParameters()
  {
    if( queryParameters != null )
      return;
    // What kinf of parameters are we expecting ?
    if ( request.getMethod().equals("POST") ) {
      // POSTed parameters, check content type:
      if ((! request.hasContentType())
	  || (type.match(request.getContentType()) < 0) ) 
	return;
      // Get and decode the request entity:
      URLDecoder dec = null;
      try {
	InputStream in = request.getInputStream() ;
	// Notify the client that we are willing to continue
	Client client = request.getClient();
	if ( client != null ) 
	  client.sendContinue();
	dec = new URLDecoder (in, false);
	queryParameters = dec.parse () ;
      } catch (URLDecoderException e) {
	queryParameters = null;
      } catch (IOException ex) {
	queryParameters = null;
      }
    } else {
      // URL encoded parameters:
      String query = request.getQueryString();
      if (query != null) {
	InputStream qis = new StringBufferInputStream(query);
	try {
	  queryParameters = new URLDecoder(qis, false).parse();
	} catch (Exception ex) {
	  throw new RuntimeException("Java implementation bug.");
	}
      }
    }
    // state parameters
    Hashtable param = (Hashtable)request.getState(STATE_PARAMETERS);
    if (param != null) {
      if (queryParameters == null)
	queryParameters = param;
      else {
	Enumeration e= param.keys();
	while (e.hasMoreElements()) {
	  String name = (String)e.nextElement();
	  Object value = queryParameters.get(name);
	  if (value == null)
	    queryParameters.put(name,param.get(name));
	  else  if (value instanceof String[]) {
	    String oldValues [] = (String[])value;
	    String newValues [] = new String[oldValues.length+1];
	    System.arraycopy(oldValues,0,newValues,0,oldValues.length);
	    newValues[oldValues.length] = (String)param.get(name);
	    queryParameters.put(name,newValues);
	  } else {
	    String newValues [] = new String[2];
	    newValues[0] = (String)param.get(name);
	    newValues[1] = (String)value;
	    queryParameters.put(name,newValues);
	  }
	}
      }
    }
  }

  /**
   * ServletRequest implementation - Get the length of request data.
   * @return An int, or <strong>-1</strong>.
   */

  public int getContentLength()
  {
    return request.getContentLength();
  }

  /**
   * ServletRequest implementation - Get the type of the request's body.
   * @return A String encoded mime type, or <strong>null</strong>.
   */

  public String getContentType()
  {
    org.w3c.www.mime.MimeType t = request.getContentType();
    return (t == null) ? null : t.toString();
  }

  /**
   * ServletRequest implementation - Get the protocol of that request.
   * @return A String encoded version of the protocol.
   */

  public String getProtocol()
  {
    return request.getVersion();
  }
    
  /**
   * ServletRequest implementation - Get the name of queried server.
   * @return Name of server, as a String.
   */

  public String getServerName()
  {
    return request.getClient().getServer().getHost();
  }
    
  /**
   * ServletRequest implementation - Get the port of queried server.
   * @return A port number (int).
   */

  public int getServerPort()
  {
    return request.getClient().getServer().getLocalPort();
  }

  /**
   * ServletRequest implementation - Get the IP address of requests's sender.
   * @return Numeric IP address, as a String.
   */

  public String getRemoteAddr()
  {
    return request.getClient().getInetAddress().getHostAddress();
  }

  /**
   * ServletRequest implementation - FQDN of request's sender.
   * @return Name of client's machine (FQDN).
   */

  public String getRemoteHost()
  {
    return request.getClient().getInetAddress().getHostName();
  }

  /**
   * ServletRequest implementation - Get real path.
   * Jigsaw realy has no notion of <em>translation</em> stricto
   * sensu (it has much better in fact ;-). This is a pain here.
   * @return Always <strong>null</strong>.
   */

  public String getRealPath(String name) {
    return null;
  }

  /**
   * ServletRequest interface - Get the input stream to read data.
   * @return An input stream instance (may be <strong>null</strong>).
   */
    
  protected ServletInputStream is = null;
  public ServletInputStream getInputStream()
    throws IOException
  {
    // If alredy computed return:
    if ( is != null )
      return is;
    // Built it:
    InputStream stream = null;
    if ((stream = request.getInputStream()) == null)
      stream = new ContentLengthInputStream(null, 0);
    return is = new JigsawServletInputStream(stream);
  }
    
  /**
   * ServletRequest implementation - Get a parameter value.
   * @return The String encoded value for the parameter.
   */

  public String getParameter(String name)
  {
    prepareQueryParameters();
    if ( queryParameters != null ) {
      Object value = queryParameters.get(name);
      if (value instanceof String[])
	return ((String[])value)[0];
      else return (String)value;
    }
    else
      return null;
  }

  /**
   * ServletRequest implementation - Get the parameters value.
   * @return The String array encoded value for the parameter.
   */

  public String[] getParameterValues(String parameter) {
    Vector V = new Vector(23);
    prepareQueryParameters();
    if (queryParameters == null) 
      return null;
    Object value = queryParameters.get(parameter);
    if (value == null) return null;
    if (value instanceof String[])
      return (String[])value;
    else {
      String [] parameterValues = new String[1];
      parameterValues[0] = (String)value;
      return parameterValues;
    }
  }

  /**
   * ServletRequest implementation - List available parameters.
   * @return An enumeration of parameter names.
   */

  public Enumeration getParameterNames()
  {
    prepareQueryParameters();
    return ((queryParameters == null)
	    ? new EmptyEnumeration()
	    : queryParameters.keys());
  }
    
  /**
   * ServletRequest implementation - Get an attribute of the request.
   * This closely match Jigsaw's notion of request state.
   * @param name The name of the attribute.
   * @return An object that gives the value of the attribute.
   */

  public Object getAttribute(String name) {
    return request.getState(name);
  }

  /**
   * HttpServletRequest implementation - Get the request's method.
   * @return A String instance.
   */

  public  String getMethod()
  {
    return request.getMethod();
  }
    
  /**
   * HttpServletRequest implementation - Get the request's path info.
   * @return A String instance or <strong>null</strong>.
   */

  public  String getPathInfo()
  {
    return (String) request.getState(ServletWrapperFrame.STATE_EXTRA_PATH);
  }
    
  /**
   * HttpServletRequest implementation - Get the request's path translated.
   * @return A String instance or <strong>null</strong>.
   */
    
  public  String getPathTranslated()
  {
    String pathinfo = getPathInfo();
    if ( pathinfo != null ) {
      httpd             server  = request.getClient().getServer();
      ResourceReference rr_root = server.getRootReference();
      try {
	LookupState       ls      = new LookupState(pathinfo);
	LookupResult      lr      = new LookupResult(rr_root);
	ResourceReference path    = null;

	try {
	  FramedResource root = (FramedResource) rr_root.lock();
	  if (root.lookup(ls,lr))
	    path = lr.getTarget();
	} catch (InvalidResourceException ex) {
	  path = null;
	} finally {
	  rr_root.unlock();
	}

	if (path != null) {
	  try {
	    Resource r = path.lock();
	    if (r instanceof FileResource)
	      return ((FileResource)r).getFile().getAbsolutePath();
	    else if (r instanceof DirectoryResource)
	      return ((DirectoryResource)r).getDirectory().getAbsolutePath();
	    else return null;
	  } catch (InvalidResourceException ex) {
	    return null;
	  } finally {
	    path.unlock();
	  }
	}
      } catch (org.w3c.tools.resources.ProtocolException ex) {
	return null;
      }
    }
    return null;
  }

  /**
   * HttpServletRequest implementation - Get the request's query string.
   * @return A String instance or <strong>null</strong>.
   */

  public  String getQueryString()
  {
    return request.getQueryString();
  }
    
  /**
   * HttpServletRequest implementation - Get the request's user (if any).
   * @return A String instance or <strong>null</strong>.
   */

  public String getRemoteUser()
  {
    return (String) request.getState(AuthFilter.STATE_AUTHUSER);
  }
    
  /**
   * HttpServletRequest implementation - Get the request's auth method.
   * @return A String instance or <strong>null</strong>.
   */

  public String getAuthType() {
    return (String) request.getState(AuthFilter.STATE_AUTHTYPE);
  }

  /**
   * HttpServletRequest implementation - Get a request header as a String.
   * @return A String instance or <strong>null</strong>.
   */

  public String getHeader(String name) {
    return request.getValue(name);
  }

  /**
   * HttpServletRequest implementation - Get a request header as an int.
   * @return An int, or <strong>-1</strong>.
   */

  public int getIntHeader(String name) {
    HeaderValue v = request.getHeaderValue(name);
    if ( v != null ) {
      Object o = v.getValue();
      if ((o != null) && (o instanceof Integer))
	return ((Integer) o).intValue();
    }
    return -1;
  }

  /**
   * HttpServletRequest implementation - Get a request header as an date.
   * @return An long (as a number of milliseconds), or <strong>-1</strong>.
   */

  public long getDateHeader(String name) {
    HeaderValue v = request.getHeaderValue(name, null);
    if ( v != null ) {
      Object o = v.getValue();
      if ((o != null) && (o instanceof Long)) 
	return ((Long) o).longValue();
    }
    return (long) -1;
  }

  /**
   * HttpServletRequest implementation - Get a all header names.
   * @return An enumeration.
   */

  public Enumeration getHeaderNames() {
    return new HeaderNames(request.enumerateHeaderDescriptions());
  }

  public  String getRequestURI()
  {
    return request.getURL().toExternalForm();
  }
    
  public  String getRequestPath()
  {
    return request.getURLPath();
  }
    
  public  String getServletPath()
  {
    ResourceReference rr = request.getTargetResource();
    try {
      return rr.lock().getURLPath();
    } catch (InvalidResourceException ex) {
      return null;
    } finally {
      rr.unlock();
    }
  }
    
  JigsawHttpServletRequest(Servlet servlet, Request request) {
    this.servlet = servlet;
    this.request = request;
  }

  // method added 

  public String getScheme() {
    return request.getURL().getProtocol();
  }
  
}


