You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@locus.apache.org on 2000/08/12 01:40:04 UTC

cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets Constants.java DefaultServlet.java InvokerServlet.java LocalStrings.properties ManagerServlet.java SnoopAllServlet.java WebdavServlet.java package.html

craigmcc    00/08/11 16:40:03

  Added:       catalina/src/share/org/apache/catalina/servlets
                        Constants.java DefaultServlet.java
                        InvokerServlet.java LocalStrings.properties
                        ManagerServlet.java SnoopAllServlet.java
                        WebdavServlet.java package.html
  Log:
  Migrate to Tomcat 4.0.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/Constants.java
  
  Index: Constants.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/Constants.java,v 1.1 2000/08/11 23:39:51 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:51 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.catalina.servlets;
  
  
  public class Constants {
  
      public static final String Package = "org.apache.catalina.servlets";
  
      public static final String CATALINA_NAME = "Tomcat Catalina";
  
      public static final String CATALINA_VERSION = "4.0";
  
  }
  
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java
  
  Index: DefaultServlet.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/DefaultServlet.java,v 1.1 2000/08/11 23:39:51 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:51 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  
  package org.apache.catalina.servlets;
  
  
  import java.io.BufferedInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.InputStream;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.sql.Timestamp;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.Vector;
  import java.util.StringTokenizer;
  import java.util.Locale;
  import java.util.Hashtable;
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import javax.servlet.RequestDispatcher;
  import javax.servlet.ServletException;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletOutputStream;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import org.apache.catalina.Resources;
  import org.apache.catalina.core.ApplicationContext;
  import org.apache.catalina.resources.ResourceBean;
  import org.apache.catalina.resources.DirectoryBean;
  import org.apache.catalina.util.MD5Encoder;
  import org.apache.catalina.util.StringManager;
  import org.apache.catalina.util.xml.SaxContext;
  import org.apache.catalina.util.xml.XmlAction;
  import org.apache.catalina.util.xml.XmlMapper;
  
  
  /**
   * The default resource-serving servlet for most web applications,
   * used to serve static resources such as HTML pages and images.
   *
   * @author Craig R. McClanahan
   * @author Remy Maucherat
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:51 $
   */
  
  public class DefaultServlet
      extends HttpServlet {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The debugging detail level for this servlet.
       */
      protected int debug = 0;
  
  
      /**
       * The input buffer size to use when serving resources.
       */
      protected int input = 2048;
  
  
      /**
       * Should we generate directory listings when no welcome file is present?
       */
      protected boolean listings = true;
  
  
      /**
       * Read only flag. By default, it's set to true.
       */
      protected boolean readOnly = true;
  
  
      /**
       * The output buffer size to use when serving resources.
       */
      protected int output = 2048;
  
  
      /**
       * The set of welcome files for this web application
       */
      protected String welcomes[] = new String[0];
  
  
      /**
       * MD5 message digest provider.
       */
      protected static MessageDigest md5Helper;
  
  
      /**
       * The MD5 helper object for this class.
       */
      protected static final MD5Encoder md5Encoder = new MD5Encoder();
  
  
      /**
       * The set of SimpleDateFormat formats to use in getDateHeader().
       */
      protected static final SimpleDateFormat formats[] = {
  	new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
  	new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
  	new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
      };
  
  
      /**
       * MIME multipart separation string
       */
      protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
  
  
      /**
       * The string manager for this package.
       */
      protected static StringManager sm =
  	StringManager.getManager(Constants.Package);
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Finalize this servlet.
       */
      public void destroy() {
  
  	;	// No actions necessary
  
      }
  
  
      /**
       * Initialize this servlet.
       */
      public void init() throws ServletException {
  
  	// Set our properties from the initialization parameters
  	String value = null;
  	try {
  	    value = getServletConfig().getInitParameter("debug");
  	    debug = Integer.parseInt(value);
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    value = getServletConfig().getInitParameter("input");
  	    input = Integer.parseInt(value);
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    value = getServletConfig().getInitParameter("listings");
  	    listings = (new Boolean(value)).booleanValue();
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    value = getServletConfig().getInitParameter("readonly");
  	    readOnly = (new Boolean(value)).booleanValue();
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    value = getServletConfig().getInitParameter("output");
  	    output = Integer.parseInt(value);
  	} catch (Throwable t) {
  	    ;
  	}
  
  	// Sanity check on the specified buffer sizes
  	if (input < 256)
  	    input = 256;
  	if (output < 256)
  	    output = 256;
  
  	// Initialize the set of welcome files for this application
  	welcomes = (String[]) getServletContext().getAttribute
  	    ("org.apache.catalina.WELCOME_FILES");
  	if (welcomes == null)
  	    welcomes = new String[0];
  
  	if (debug > 0) {
  	    log("DefaultServlet.init:  input buffer size=" + input +
  		", output buffer size=" + output);
  	    for (int i = 0; i < welcomes.length; i++)
  		log("DefaultServlet.init:  welcome file=" +
  		    welcomes[i]);
  	}
  
          // Load the MD5 helper used to calculate signatures.
          try {
              md5Helper = MessageDigest.getInstance("MD5");
          } catch (NoSuchAlgorithmException e) {
              e.printStackTrace();
              throw new IllegalStateException();
          }
  
      }
  
  
  
      // ------------------------------------------------------ Protected Methods
  
  
      /**
       * Return the relative path associated with this servlet.
       *
       * @param request The servlet request we are processing
       */
      protected String getRelativePath(HttpServletRequest request) {
          
          String result = request.getPathInfo();
          if (result == null) {
              result = request.getServletPath();
          }
          if ((result == null) || (result.equals(""))) {
              result = "/";
          }
          return result;
          
      }
  
  
      /**
       * Process a GET request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      protected void doGet(HttpServletRequest request,
                           HttpServletResponse response)
  	throws IOException, ServletException {
  
  	// Serve the requested resource, including the data content
  	serveResource(request, response, true);
  
      }
  
  
      /**
       * Process a HEAD request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      protected void doHead(HttpServletRequest request,
                            HttpServletResponse response)
  	throws IOException, ServletException {
  
  	// Serve the requested resource, without the data content
          serveResource(request, response, false);
  
      }
  
  
      /**
       * Process a POST request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      protected void doPost(HttpServletRequest request,
                            HttpServletResponse response)
  	throws IOException, ServletException {
  
  	doPut(request, response);
  
      }
  
  
      /**
       * Process a POST request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      protected void doPut(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(HttpServletResponse.SC_FORBIDDEN);
              return;
          }
          
          String path = getRelativePath(req);
          
          // Looking for a Content-Range header
          if (req.getHeader("Content-Range") != null) {
              // No content range header is supported
              resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
          }
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
          Resources resources = context.getResources();
          
          boolean exists = resources.exists(path);
          
          boolean result = resources.setResource(path, req.getInputStream());
          
          if (result) {
              if (exists) {
                  resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
              } else {
                  resp.setStatus(HttpServletResponse.SC_CREATED);
              }
          } else {
              resp.sendError(HttpServletResponse.SC_CONFLICT);
          }
          
      }
  
  
      /**
       * Process a POST request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(HttpServletResponse.SC_FORBIDDEN);
              return;
          }
          
          String path = getRelativePath(req);
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
          Resources resources = context.getResources();
          
          boolean exists = resources.exists(path);
          
          if (exists) {
              boolean result = resources.deleteResource(path);
              if (result) {
                  resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
              } else {
                  resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
              }
          } else {
              resp.sendError(HttpServletResponse.SC_NOT_FOUND);
          }
          
      }
  
  
      /**
       * Check if the conditions specified in the optional If headers are 
       * satisfied.
       * 
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       * @param resourceInfo File object
       * @return boolean true if the resource meets all the specified conditions,
       * and false if any of the conditions is not satisfied, in which case
       * request processing is stopped
       */
      protected boolean checkIfHeaders(HttpServletRequest request,
                                       HttpServletResponse response, 
                                       ResourceInfo resourceInfo)
          throws IOException {
          
          String eTag = getETag(resourceInfo, true);
          long fileLength = resourceInfo.length;
          long lastModified = resourceInfo.date;
          
          StringTokenizer commaTokenizer;
          
          String headerValue;
          
          // Checking If-Match
          headerValue = request.getHeader("If-Match");
          if (headerValue != null) {
              if (headerValue.indexOf("*") == -1) {
                  
                  commaTokenizer = new StringTokenizer(headerValue, ",");
                  boolean conditionSatisfied = false;
                  
                  while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
                      String currentToken = commaTokenizer.nextToken();
                      if (currentToken.trim().equals(eTag))
                          conditionSatisfied = true;
                  }
                  
                  // If none of the given ETags match, 412 Precodition failed is
                  // sent back
                  if (!conditionSatisfied) {
                      response.sendError
                          (HttpServletResponse.SC_PRECONDITION_FAILED);
                      return false;
                  }
                  
              }
          }
          
          // Checking If-Modified-Since
          headerValue = request.getHeader("If-Modified-Since");
          if (headerValue != null) {
              
              // If an If-None-Match header has been specified, if modified since
              // is ignored.
              if (request.getHeader("If-None-Match") == null) {
                  
                  Date date = null;
                  
                  // Parsing the HTTP Date
                  for (int i = 0; (date == null) && (i < formats.length); i++) {
                      try {
                          date = formats[i].parse(headerValue);
                      } catch (ParseException e) {
                          ;
                      }
                  }
                  
                  if ((date != null) 
                      && (lastModified <= (date.getTime() + 1000)) ) {
                      // The entity has not been modified since the date 
                      // specified by the client. This is not an error case.
                      response.sendError
                          (HttpServletResponse.SC_NOT_MODIFIED);
                      return false;
                  }
                  
              }
              
          }
          
          // Checking If-None-Match
          headerValue = request.getHeader("If-None-Match");
          if (headerValue != null) {
              if (headerValue.indexOf("*") == -1) {
                  
                  commaTokenizer = new StringTokenizer(headerValue, ",");
                  boolean conditionSatisfied = false;
                  
                  while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
                      String currentToken = commaTokenizer.nextToken();
                      if (currentToken.trim().equals(eTag))
                          conditionSatisfied = true;
                  }
                  
                  if (conditionSatisfied) {
                      
                      // For GET and HEAD, we should respond with 
                      // 304 Not Modified.
                      // For every other method, 412 Precondition Failed is sent
                      // back.
                      if ( ("GET".equals(request.getMethod()))
                           || ("HEAD".equals(request.getMethod())) ) {
                          response.sendError
                              (HttpServletResponse.SC_NOT_MODIFIED);
                          return false;
                      } else {
                          response.sendError
                              (HttpServletResponse.SC_PRECONDITION_FAILED);
                          return false;
                      }
                  }
                  
              } else {
                  if (resourceInfo.exists()) {
                      
                  }
              }
          }
          
          // Checking If-Unmodified-Since
          headerValue = request.getHeader("If-Unmodified-Since");
          if (headerValue != null) {
              
              Date date = null;
              
              // Parsing the HTTP Date
              for (int i = 0; (date == null) && (i < formats.length); i++) {
                  try {
                      date = formats[i].parse(headerValue);
                  } catch (ParseException e) {
                      ;
                  }
              }
              
              if ( (date != null) && (lastModified > date.getTime()) ) {
                  // The entity has not been modified since the date 
                  // specified by the client. This is not an error case.
                  response.sendError
                      (HttpServletResponse.SC_PRECONDITION_FAILED);
                  return false;
              }
              
          }
          
          return true;
      }
  
  
      /**
       * Get the ETag value associated with a file.
       * 
       * @param resourceInfo File object
       * @param strong True if we want a strong ETag, in which case a checksum
       * of the file has to be calculated
       */
      protected String getETagValue(ResourceInfo resourceInfo, boolean strong) {
          // FIXME : Compute a strong ETag if requested, using an MD5 digest
          // of the file contents
          return resourceInfo.length + "-" + resourceInfo.date;
      }
  
  
      /**
       * Get the ETag associated with a file.
       * 
       * @param resourceInfo File object
       * @param strong True if we want a strong ETag, in which case a checksum
       * of the file has to be calculated
       */
      protected String getETag(ResourceInfo resourceInfo, boolean strong) {
          if (strong)
              return "\"" + getETagValue(resourceInfo, strong) + "\"";
          else
              return "W/\"" + getETagValue(resourceInfo, strong) + "\"";
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Copy the contents of the specified input stream to the specified
       * output stream, and ensure that both streams are closed before returning
       * (even in the face of an exception).
       *
       * @param istream The input stream to read from
       * @param ostream The output stream to write to
       *
       * @exception IOException if an input/output error occurs
       */
      private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream)
  	throws IOException {
  
          IOException exception = null;
              
          // FIXME : i18n ?
          InputStream resourceInputStream = 
              resourceInfo.resources.getResourceAsStream(resourceInfo.path);
          InputStream istream = new BufferedInputStream
              (resourceInputStream, input);
          
          // Copy the input stream to the output stream
          exception = copyRange(istream, ostream);
          
          // Clean up the input and output streams
          try {
              istream.close();
          } catch (Throwable t) {
              ;
          }
          
  	try {
  	    ostream.flush();
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    ostream.close();
  	} catch (Throwable t) {
  	    ;
  	}
  
  	// Rethrow any exception that has occurred
  	if (exception != null)
  	    throw exception;
  
      }
  
  
      /**
       * Copy the contents of the specified input stream to the specified
       * output stream, and ensure that both streams are closed before returning
       * (even in the face of an exception).
       *
       * @param resourceInfo The ResourceInfo object
       * @param ostream The output stream to write to
       * @param range Range the client wanted to retrieve
       * @exception IOException if an input/output error occurs
       */
      private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream, 
                        Range range)
  	throws IOException {
          
          IOException exception = null;
          
          InputStream resourceInputStream = 
              resourceInfo.resources.getResourceAsStream(resourceInfo.path);
          InputStream istream =
              new BufferedInputStream(resourceInputStream, input);
          exception = copyRange(istream, ostream, range.start, range.end);
          
  	// Clean up the input and output streams
  	try {
  	    istream.close();
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    ostream.flush();
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    ostream.close();
  	} catch (Throwable t) {
  	    ;
  	}
  
  	// Rethrow any exception that has occurred
  	if (exception != null)
  	    throw exception;
          
      }
  
  
      /**
       * Copy the contents of the specified input stream to the specified
       * output stream, and ensure that both streams are closed before returning
       * (even in the face of an exception).
       *
       * @param resourceInfo The ResourceInfo object
       * @param ostream The output stream to write to
       * @param ranges Enumeration of the ranges the client wanted to retrieve
       * @param contentType Content type of the resource
       * @exception IOException if an input/output error occurs
       */
      private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
                        Enumeration ranges, String contentType)
  	throws IOException {
          
          IOException exception = null;
          
          while ( (exception == null) && (ranges.hasMoreElements()) ) {
              
              InputStream resourceInputStream = 
                  resourceInfo.resources.getResourceAsStream(resourceInfo.path);
              InputStream istream =	// FIXME: internationalization???????
                  new BufferedInputStream(resourceInputStream, input);
          
              Range currentRange = (Range) ranges.nextElement();
              
              // Writing MIME header.
              ostream.println("--" + mimeSeparation);
              if (contentType != null)
                  ostream.println("Content-Type: " + contentType);
              ostream.println("Content-Range: bytes " + currentRange.start
                             + "-" + currentRange.end + "/" 
                             + currentRange.length);
              ostream.println();
              
              // Printing content
              exception = copyRange(istream, ostream, currentRange.start,
                                    currentRange.end);
              
              try {
                  istream.close();
              } catch (Throwable t) {
                  ;
              }
              
          }
          
          ostream.print("--" + mimeSeparation + "--");
          
  	// Clean up the output streams
  	try {
  	    ostream.flush();
  	} catch (Throwable t) {
  	    ;
  	}
  	try {
  	    ostream.close();
  	} catch (Throwable t) {
  	    ;
  	}
  
  	// Rethrow any exception that has occurred
  	if (exception != null)
  	    throw exception;
          
      }
  
  
      /**
       * Copy the contents of the specified input stream to the specified
       * output stream, and ensure that both streams are closed before returning
       * (even in the face of an exception).
       *
       * @param istream The input stream to read from
       * @param ostream The output stream to write to
       * @return Exception which occured during processing
       */
      private IOException copyRange(InputStream istream, 
                                    ServletOutputStream ostream) {
          
  	// Copy the input stream to the output stream
  	IOException exception = null;
  	byte buffer[] = new byte[input];
  	int len = buffer.length;
  	while (true) {
  	    try {
                  len = istream.read(buffer);
                  if (len == -1)
                      break;
                  ostream.write(buffer, 0, len);
  	    } catch (IOException e) {
  		exception = e;
  		len = -1;
                  break;
  	    }
  	}
          return exception;
          
      }
  
  
      /**
       * Copy the contents of the specified input stream to the specified
       * output stream, and ensure that both streams are closed before returning
       * (even in the face of an exception).
       *
       * @param istream The input stream to read from
       * @param ostream The output stream to write to
       * @param start Start of the range which will be copied
       * @param end End of the range which will be copied
       * @return Exception which occured during processing
       */
      private IOException copyRange(InputStream istream, 
                                    ServletOutputStream ostream,
                                    long start, long end) {
          
          try {
              istream.skip(start);
          } catch (IOException e) {
              return e;
          }
          
  	IOException exception = null;
          long bytesToRead = end - start + 1;
          
  	byte buffer[] = new byte[input];
  	int len = buffer.length;
  	while ( (bytesToRead > 0) && (len >= buffer.length)) {
  	    try {
                  len = istream.read(buffer);
                  if (bytesToRead >= len) {
                      ostream.write(buffer, 0, len);
                      bytesToRead -= len; 
                  } else {
                      ostream.write(buffer, 0, (int) bytesToRead);
                      bytesToRead = 0;
                  }
  	    } catch (IOException e) {
  		exception = e;
  		len = -1;
  	    }
  	    if (len < buffer.length)
  		break;
  	}
          
          return exception;
          
      }
  
  
      /**
       * Display the size of a file.
       */
      private void displaySize(StringBuffer buf, int filesize) {
          
  	int leftside = filesize / 1024;
  	int rightside = (filesize % 1024) / 103;  // makes 1 digit
  	// To avoid 0.0 for non-zero file, we bump to 0.1
  	if (leftside == 0 && rightside == 0 && filesize != 0) 
  	    rightside = 1;
  	buf.append(leftside).append(".").append(rightside);
  	buf.append(" KB");
          
      }
      
      
      /**
       * Check to see if a default page exists.
       * 
       * @param pathname Pathname of the file to be served
       */
      private ResourceInfo checkWelcomeFiles(String pathname, 
                                             Resources resources) {
          
          String collectionName = pathname;
          if (!pathname.endsWith("/")) {
              collectionName += "/";
          }
              
          // Serve a welcome resource or file if one exists
          // FIXME - update the welcome files list?
          for (int i = 0; i < welcomes.length; i++) {
              
              // Does the specified resource exist?
              String resourceName = collectionName + welcomes[i];
              ResourceInfo resourceInfo = 
                  new ResourceInfo(resourceName, resources);
              if (resourceInfo.exists()) {
                  return resourceInfo;
              }
              
          }
          
          return null;
          
      }
  
  
      /**
       * Serve the specified resource, optionally including the data content.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       * @param content Should the content be included?
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      private void serveResource(HttpServletRequest request,
                                 HttpServletResponse response,
                                 boolean content)
  	throws IOException, ServletException {
  
          // Identify the requested resource path
          String path = getRelativePath(request);
  	if (debug > 0) {
  	    if (content)
  		log("DefaultServlet.serveResource:  Serving resource '" +
  		    path + "' headers and data");
  	    else
  		log("DefaultServlet.serveResource:  Serving resource '" +
  		    path + "' headers only");
  	}
  
  	// Exclude any resource in the /WEB-INF and /META-INF subdirectories
  	// (the "toUpperCase()" avoids problems on Windows systems)
  	if (path.toUpperCase().startsWith("/WEB-INF") ||
  	    path.toUpperCase().startsWith("/META-INF")) {
  	    response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
  	    return;
  	}
  
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
  
  	// Convert the resource path to a URL
  	URL resourceURL = null;
  	try {
  	    resourceURL = context.getResource(path);
  	} catch (MalformedURLException e) {
  	    ;
  	}
  	if (resourceURL == null) {
  	    response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
  	    return;
  	}
  	if (debug > 0)
  	    log("DefaultServlet.serveResource:  Corresponding URL is '" +
  		resourceURL.toString() + "'");
          
          Resources resources = context.getResources();
          ResourceInfo resourceInfo = new ResourceInfo(path, resources);
  
          // If the resource is a collection (aka a directory), we check 
          // the welcome files list.
          if (resourceInfo.collection) {
              ResourceInfo welcomeFileInfo = checkWelcomeFiles(path, resources);
              if (welcomeFileInfo != null) {
                  resourceInfo = welcomeFileInfo;
              }
              
              if ((welcomeFileInfo != null) && 
                  (!path.endsWith("/"))) {
                  response.sendRedirect(request.getRequestURI() + "/");
                  return;
              }
              
          }
          
  	if (!resourceInfo.exists()) {
  	    response.sendError(HttpServletResponse.SC_NOT_FOUND, 
                                 resourceInfo.path);
  	    return;
  	}
  
          // Checking If headers
          if ( !checkIfHeaders(request, response, resourceInfo) )
              return;
          
          // Find content type.
          String contentType = 
              getServletContext().getMimeType(resourceInfo.path);
          
          if (resourceInfo.collection) {
              // Skip directory listings if we have been configured to 
              // suppress them
              if (!listings) {
                  response.sendError(HttpServletResponse.SC_NOT_FOUND,
                                     resourceInfo.path);
                  return;
              }
              contentType = "text/html";
          }
  
  
          // Parse range specifier
          Vector ranges = null;
          if (!resourceInfo.collection) {
              parseRange(request, response, resourceInfo);
          
              // Last-Modified header
              if (debug > 0)
                  log("DefaultServlet.serveFile:  lastModified='" +
                      (new Timestamp(resourceInfo.date)).toString() + "'");
              response.setDateHeader("Last-Modified", resourceInfo.date);
              
              // ETag header
              response.setHeader("ETag", getETag(resourceInfo, true));
          }
          
          if ( (ranges == null) && (request.getHeader("Range") == null) ) {
              
              // Set the appropriate output headers
              if (contentType != null) {
                  if (debug > 0)
                      log("DefaultServlet.serveFile:  contentType='" +
                          contentType + "'");
                  response.setContentType(contentType);
              }
              long contentLength = resourceInfo.length;
              if ((!resourceInfo.collection) && (contentLength >= 0)) {
                  if (debug > 0)
                      log("DefaultServlet.serveFile:  contentLength=" +
                          contentLength);
                  response.setContentLength((int) contentLength);
              }
              
              // Copy the input stream to our output stream (if requested)
              if (content) {
                  response.setBufferSize(output);
                  copy(resourceInfo, response.getOutputStream());
              }
              
          } else {
              
              if (ranges == null)
                  return;
              
              // Partial content response.
              
              response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
              
              if (ranges.size() == 1) {
                  
                  Range range = (Range) ranges.elementAt(0);
                  response.addHeader("Content-Range", "bytes " 
                                     + range.start
                                     + "-" + range.end + "/" 
                                     + range.length);
                  
                  if (contentType != null) {
                      if (debug > 0)
                          log("DefaultServlet.serveFile:  contentType='" +
                              contentType + "'");
                      response.setContentType(contentType);
                  }
                  
                  if (content) {
                      response.setBufferSize(output);
                      copy(resourceInfo, response.getOutputStream(), range);
                  }
                  
              } else {
                  
                  response.setContentType("multipart/byteranges; boundary="
                                          + mimeSeparation);
                  
                  if (content) {
                      response.setBufferSize(output);
                      copy(resourceInfo, response.getOutputStream(), 
                           ranges.elements(), contentType);
                  }
                  
              }
              
          }
          
      }
  
  
      /**
       * Parse the range header.
       * 
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       * @return Vector of ranges
       */
      private Vector parseRange(HttpServletRequest request, 
                                HttpServletResponse response, 
                                ResourceInfo resourceInfo) 
          throws IOException {
          
          // Checking If-Range
          String headerValue = request.getHeader("If-Range");
          if (headerValue != null) {
              
              String eTag = getETag(resourceInfo, true);
              long lastModified = resourceInfo.date;
              
              Date date = null;
              
              // Parsing the HTTP Date
              for (int i = 0; (date == null) && (i < formats.length); i++) {
                  try {
                      date = formats[i].parse(headerValue);
                  } catch (ParseException e) {
                      ;
                  }
              }
              
              if (date == null) {
                  
                  // If the ETag the client gave does not match the entity
                  // etag, then the entire entity is returned.
                  if (!eTag.equals(headerValue.trim()))
                      return null;
                  
              } else {
                  
                  // If the timestamp of the entity the client got is older than
                  // the last modification date of the entity, the entire entity
                  // is returned.
                  if (lastModified > (date.getTime() + 1000))
                      return null;
                  
              }
              
          }
          
          long fileLength = resourceInfo.length;
          
          if (fileLength == 0)
              return null;
          
          // Retrieving the range header (if any is specified
          String rangeHeader = request.getHeader("Range");
          
          if (rangeHeader == null)
              return null;
          // bytes is the only range unit supported (and I don't see the point
          // of adding new ones).
          if (!rangeHeader.startsWith("bytes")) {
              response.sendError
                  (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
              return null;
          }
          
          rangeHeader = rangeHeader.substring(6);
          
          // Vector which will contain all the ranges which are successfully
          // parsed.
          Vector result = new Vector();
          StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
          
          // Parsing the range list
          while (commaTokenizer.hasMoreTokens()) {
              String rangeDefinition = commaTokenizer.nextToken();
              
              Range currentRange = new Range();
              currentRange.length = fileLength;
              
              int dashPos = rangeDefinition.indexOf('-');
              
              if (dashPos == -1) {
                  response.sendError
                      (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                  return null;
              }
              
              if (dashPos == 0) {
                  
                  try {
                      long offset = Long.parseLong(rangeDefinition);
                      currentRange.start = fileLength + offset;
                      currentRange.end = fileLength - 1;
                  } catch (NumberFormatException e) {
                      response.sendError
                          (HttpServletResponse
                           .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                      return null;
                  }
                  
              } else {
                  
                  try {
                      currentRange.start = Long.parseLong
                          (rangeDefinition.substring(0, dashPos));
                      if (dashPos < rangeDefinition.length() - 1)
                          currentRange.end = Long.parseLong
                              (rangeDefinition.substring
                               (dashPos + 1, rangeDefinition.length()));
                      else
                          currentRange.end = fileLength - 1;
                  } catch (NumberFormatException e) {
                      response.sendError
                          (HttpServletResponse
                           .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                      return null;
                  }
                  
              }
              
              if (!currentRange.validate()) {
                  response.sendError
                      (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                  return null;
              }
              
              result.addElement(currentRange);
          }
          
          return result;
      }
  
  
      // ------------------------------------------------------ Range Inner Class
  
  
      private class Range {
          
          public long start;
          public long end;
          public long length;
          
          /**
           * Validate range.
           */
          public boolean validate() {
              return ( (start >= 0) && (end >= 0) && (length > 0)
                       && (start <= end) && (end < length) );
          }
          
      }
  
  
      // ----------------------------------------------  ResourceInfo Inner Class
  
  
      protected class ResourceInfo {
  
  
          /**
           * Constructor.
           * 
           * @param pathname Path name of the file
           */
          public ResourceInfo(String path, Resources resources) {
              
              this.path = path;
              this.resources = resources;
              this.exists = resources.exists(path);
              if (exists) {
                  this.creationDate = resources.getResourceCreated(path);
                  this.date = resources.getResourceModified(path);
                  this.httpDate = formats[0].format(new Date(date));
                  this.length = resources.getResourceLength(path);
                  this.collection = resources.isCollection(path);
              }
  
          }
  
  
          public String path;
          public long creationDate;
          public String httpDate;
          public long date;
          public long length;
          public boolean collection;
          public boolean exists;
          public Resources resources;
  
  
          /**
           * Test if the associated resource exists.
           */
          public boolean exists() {
              return exists;
          }
  
  
          /**
           * String representation.
           */
          public String toString() {
              return path;
          }
  
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/InvokerServlet.java
  
  Index: InvokerServlet.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/InvokerServlet.java,v 1.1 2000/08/11 23:39:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:52 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  
  package org.apache.catalina.servlets;
  
  
  import java.io.IOException;
  import javax.servlet.RequestDispatcher;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import org.apache.catalina.Context;
  import org.apache.catalina.HttpRequest;
  import org.apache.catalina.HttpResponse;
  import org.apache.catalina.Wrapper;
  
  
  /**
   * The default servlet-invoking servlet for most web applications,
   * used to serve requests to servlets that have not been registered
   * in the web application deployment descriptor.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:52 $
   */
  
  public final class InvokerServlet
      extends HttpServlet {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The Context container associated with our web application.
       */
      private Context context = null;
  
  
      /**
       * The debugging detail level for this servlet.
       */
      private int debug = 0;
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Finalize this servlet.
       */
      public void destroy() {
  
  	;	// No actions necessary
  
      }
  
  
      /**
       * Process a GET request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void doGet(HttpServletRequest request,
  		      HttpServletResponse response)
  	throws IOException, ServletException {
  
  	serveRequest(request, response);
  
      }
  
  
      /**
       * Process a HEAD request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void doHead(HttpServletRequest request,
  		       HttpServletResponse response)
  	throws IOException, ServletException {
  
  	serveRequest(request, response);
  
      }
  
  
      /**
       * Process a POST request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void doPost(HttpServletRequest request,
  		       HttpServletResponse response)
  	throws IOException, ServletException {
  
  	serveRequest(request, response);
  
      }
  
  
      /**
       * Initialize this servlet.
       */
      public void init() throws ServletException {
  
  	// Set our properties from the initialization parameters
  	String value = null;
  	try {
  	    value = getServletConfig().getInitParameter("debug");
  	    debug = Integer.parseInt(value);
  	} catch (Throwable t) {
  	    ;
  	}
  
  	// Identify the internal container resources we need
  	Wrapper wrapper = (Wrapper) getServletConfig();
  	context = (Context) wrapper.getParent();
  
  	if (debug >= 1)
  	    log("init: Associated with Context '" + context.getPath() + "'");
  
      }
  
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Serve the specified request, creating the corresponding response.
       * After the first time a particular servlet class is requested, it will
       * be served directly (like any registered servlet) because it will have
       * been registered and mapped in our associated Context.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void serveRequest(HttpServletRequest request,
  		             HttpServletResponse response)
  	throws IOException, ServletException {
  
  	// Identify the class name of the servlet we want
  	if (debug >= 1)
  	    log("serveRequest: Serving request " + request.getMethod() +
  		" " + request.getRequestURI());
  	String pathInfo = request.getPathInfo();
  	if (pathInfo == null) {
  	    if (debug >= 1)
  	        log("serveRequest:  Invalid pathInfo '" + pathInfo + "'");
  	    response.sendError(HttpServletResponse.SC_BAD_REQUEST,
  	                       request.getRequestURI());
  	    return;
          }
  	String servletClass = pathInfo.substring(1);
  
  	// Identify the servlet name or class, and related information we will need
  	int slash = servletClass.indexOf("/");
  	if (slash >= 0) {
  	    pathInfo = servletClass.substring(slash);
  	    servletClass = servletClass.substring(0, slash);
          } else {
  	    pathInfo = "";
          }
  	String name = "org.apache.catalina.INVOKER." + servletClass;
  	String pattern = request.getServletPath() + "/" + servletClass + "/*";
  	Wrapper wrapper = null;
  
  	// Are we referencing an existing servlet name?
  	wrapper = (Wrapper) context.findChild(servletClass);
  	if (wrapper != null) {
  	    if (debug >= 1)
  	        log("serveRequest:  Using wrapper for servlet '" +
  		    wrapper.getName() + "' with mapping '" + pattern + "'");
  	    context.addServletMapping(pattern, wrapper.getName());
  	}
  
  	// No, create a new wrapper for the specified servlet class
          else {
  	    if (debug >= 1)
  		log("serveRequest:  Creating wrapper for '" + servletClass +
  		    "' with mapping '" + pattern + "'");
  	    try {
  	        wrapper = context.createWrapper();
  		wrapper.setName(name);
  		wrapper.setLoadOnStartup(1);
  		wrapper.setServletClass(servletClass);
  		context.addChild(wrapper);
  		context.addServletMapping(pattern, name);
              } catch (Throwable t) {
  		log("serveRequest", t);
  		response.sendError(HttpServletResponse.SC_NOT_FOUND,
  				   request.getRequestURI());
  		return;
              }
  	}
  
  	// Pass this request on to the identified or newly created wrapper
  	StringBuffer sb = new StringBuffer(request.getServletPath());
  	sb.append("/");
  	sb.append(servletClass);
  	sb.append(pathInfo);
  	String dispatcherPath = sb.toString();
  	if (debug >= 1)
  	    log("serveRequest:  Forwarding to '" + dispatcherPath + "'");
  	RequestDispatcher rd =
  	    getServletContext().getRequestDispatcher(dispatcherPath);
  	rd.forward(request, response);
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  defaultservlet.directorylistingfor=Directory Listing for:
  defaultservlet.upto=Up to:
  defaultservlet.subdirectories=Subdirectories:
  defaultservlet.files=Files:
  managerServlet.alreadyContext=FAIL - Application already exists at path {0}
  managerServlet.deployed=OK - Deployed application at context path {0}
  managerServlet.exception=FAIL - Encountered exception {0}
  managerServlet.listed=OK - Listed applications for virtual host {0}
  managerServlet.noCommand=FAIL - No command was specified
  managerServlet.noContext=FAIL - No context exists for path {0}
  managerServlet.noPath=FAIL - No context path was specified
  managerServlet.noRole=FAIL - User does not possess role {0}
  managerServlet.reloaded=OK - Reloaded application at context path {0}
  managerServlet.undeployed=OK - Undeployed application at context path {0}
  webdavservlet.jaxpfailed=JAXP initialization failed
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/ManagerServlet.java
  
  Index: ManagerServlet.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/ManagerServlet.java,v 1.1 2000/08/11 23:39:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:52 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  
  package org.apache.catalina.servlets;
  
  
  import java.io.IOException;
  import java.io.PrintWriter;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import org.apache.catalina.Container;
  import org.apache.catalina.Context;
  import org.apache.catalina.Host;
  import org.apache.catalina.HttpRequest;
  import org.apache.catalina.HttpResponse;
  import org.apache.catalina.Wrapper;
  import org.apache.catalina.util.StringManager;
  
  
  /**
   * Servlet that enables remote management of the web applications installed
   * within the same virtual host as this web application is.  Normally, this
   * functionality will be protected by a security constraint in the web
   * application deployment descriptor.  However, this requirement can be
   * relaxed during testing.
   * <p>
   * This servlet examines the value returned by <code>getPathInfo()</code>
   * and related query parameters to determine what action is being requested.
   * The following actions and parameters (starting after the servlet path)
   * are supported:
   * <ul>
   * <li><b>/list</b> - Return a list of the context paths of all currently
   *     running web applications in this virtual host.
   * <li><b>/deploy?path=/xxx&war={war-url}</b> - Deploy a new web application
   *     attached to context path <code>/xxx</code>, based on the contents of
   *     the web application archive found at the specified URL.
   * <li><b>/reload?path=/xxx</b> - Reload the Java classes and resources for
   *     the application at the specified path, but do not reread the web.xml
   *     configuration files.
   * <li><b>/undeploy?path=/xxx</b> - Remove any web application attached to
   *     context path <code>/xxx</code> from this virtual host.
   * </ul>
   * <p>
   * <b>NOTE</b> - Attempting to reload or undeploy the application containing
   * this servlet itself will not succeed.  Therefore, this servlet should
   * generally be deployed as a separate web application within the virtual host
   * to be managed.
   * <p>
   * The following servlet initialization parameters are recognized:
   * <ul>
   * <li><b>debug</b> - The debugging detail level that controls the amount
   *     of information that is logged by this servlet.  Default is zero.
   * </ul>
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:52 $
   */
  
  public final class ManagerServlet
      extends HttpServlet {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The Context container associated with our web application.
       */
      private Context context = null;
  
  
      /**
       * The debugging detail level for this servlet.
       */
      private int debug = 1;
  
  
      /**
       * The Host container that contains our own web application's Context,
       * along with the associated Contexts for web applications that we
       * are managing.
       */
      private Host host = null;
  
  
      /**
       * The string manager for this package.
       */
      private static StringManager sm =
        StringManager.getManager(Constants.Package);
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Finalize this servlet.
       */
      public void destroy() {
  
  	;	// No actions necessary
  
      }
  
  
      /**
       * Process a GET request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void doGet(HttpServletRequest request,
  		      HttpServletResponse response)
  	throws IOException, ServletException {
  
  	// Identify the request parameters that we need
  	String command = request.getPathInfo();
  	if (command == null)
  	    command = request.getServletPath();
  	String path = request.getParameter("path");
  	String war = request.getParameter("war");
  
  	// Prepare our output writer to generate the response message
  	response.setContentType("text/plain");
  	PrintWriter writer = response.getWriter();
  
  	// Process the requested command
  	if (command == null) {
  	    writer.println(sm.getString("managerServlet.noCommand"));
  	} else if (command.equals("/deploy")) {
  	    deploy(writer, path, war);
  	} else if (command.equals("/list")) {
  	    list(writer);
  	} else if (command.equals("/reload")) {
  	    reload(writer, path);
  	} else if (command.equals("/undeploy")) {
  	    undeploy(writer, path);
  	} else {
  	    writer.println(sm.getString("managerServlet.unknownCommand",
  					command));
  	}
  
  	// Finish up the response
  	writer.flush();
  	writer.close();
  
      }
  
  
      /**
       * Initialize this servlet.
       */
      public void init() throws ServletException {
  
  	// Set our properties from the initialization parameters
  	String value = null;
  	try {
  	    value = getServletConfig().getInitParameter("debug");
  	    debug = Integer.parseInt(value);
  	} catch (Throwable t) {
  	    ;
  	}
  
  	// Identify the internal container resources we need
  	Wrapper wrapper = (Wrapper) getServletConfig();
  	context = (Context) wrapper.getParent();
  	host = (Host) context.getParent();
  
  	// Log debugging messages as necessary
  	if (debug >= 1) {
  	    log("init: Associated with Host '" + host.getName() + "'");
  	}
  
      }
  
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Deploy an application for the specified path from the specified
       * web application archive.
       *
       * @param writer Writer to render results to
       * @param path Context path of the application to be deployed
       * @param war URL of the web application archive to be deployed
       */
      private void deploy(PrintWriter writer, String path, String war) {
  
          if (debug >= 1)
  	    log("deploy: Deploying web application at '" + path +
  		"' from '" + war + "'");
  
  	try {
  	  Context context = (Context) host.findChild(path);
  	  if (context != null) {
  	      writer.println(sm.getString("managerServlet.alreadyContext",
  					  path));
  	      return;
  	  }
  	  ; // FIXME - deploy()
  	  writer.println(sm.getString("managerServlet.deployed", path));
  	} catch (Throwable t) {
  	    getServletContext().log("ManagerServlet.deploy[" + path + "]", t);
  	    writer.println(sm.getString("managerServlet.exception",
  					t.toString()));
  	}
  
      }
  
  
      /**
       * Render a list of the currently active Contexts in our virtual host.
       *
       * @param writer Writer to render to
       */
      private void list(PrintWriter writer) {
  
          if (debug >= 1)
  	    log("list: Listing contexts for virtual host '" +
  		host.getName() + "'");
  
          writer.println(sm.getString("managerServlet.listed", host.getName()));
  	Container children[] = host.findChildren();
  	for (int i = 0; i < children.length; i++)
  	    writer.println(children[i].getName());
  
  
      }
  
  
      /**
       * Reload the web application at the specified context path.
       *
       * @param writer Writer to render to
       * @param path Context path of the application to be restarted
       */
      private void reload(PrintWriter writer, String path) {
  
          if (debug >= 1)
  	    log("restart: Reloading web application at '" + path + "'");
  
          try {
  	    Context context = (Context) host.findChild(path);
  	    if (context == null) {
  	        writer.println(sm.getString("managerServlet.noContext", path));
  		return;
  	    }
  	    context.reload();
  	    writer.println(sm.getString("managerServlet.reloaded", path));
  	} catch (Throwable t) {
  	    getServletContext().log("ManagerServlet.reload[" + path + "]", t);
  	    writer.println(sm.getString("managerServlet.exception",
  					t.toString()));
  	}
  
      }
  
  
      /**
       * Undeploy the web application at the specified context path.
       *
       * @param writer Writer to render to
       * @param path Context path of the application to be undeployed
       */
      private void undeploy(PrintWriter writer, String path) {
  
          if (debug >= 1)
  	    log("undeploy: Undeploying web application at '" + path + "'");
  
          try {
  	    Context context = (Context) host.findChild(path);
  	    if (context == null) {
  	        writer.println(sm.getString("managerServlet.noContext", path));
  		return;
  	    }
  	    host.removeChild(context);
  	    writer.println(sm.getString("managerServlet.undeployed", path));
  	} catch (Throwable t) {
  	    getServletContext().log("ManagerServlet.undeploy[" + path + "]",
  				    t);
  	    writer.println(sm.getString("managerServlet.exception",
  					t.toString()));
  	}
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/SnoopAllServlet.java
  
  Index: SnoopAllServlet.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/SnoopAllServlet.java,v 1.1 2000/08/11 23:39:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:52 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  
  package org.apache.catalina.servlets;
  
  
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.util.Enumeration;
  import java.util.Locale;
  import javax.servlet.ServletException;
  import javax.servlet.http.Cookie;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  
  /**
   * Servlet to dump everything that is known in the current request object
   * into an HTML page.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:52 $
   */
  
  public final class SnoopAllServlet
      extends HttpServlet {
  
  
      /**
       * Process a GET request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void doGet(HttpServletRequest request,
  		      HttpServletResponse response)
  	throws IOException, ServletException {
  
  	response.setContentType("text/html");
  	PrintWriter writer = response.getWriter();
  
  	// Generate HTML page header
  	writer.println("<head>");
  	writer.println("<title>SnoopAllServlet Output</title>");
  	writer.println("</head>");
  	writer.println("<body bgcolor=\"white\">");
  
  	// Document the properties from ServletRequest
  	writer.println("<h1>ServletRequest Properties</h1>");
  	writer.println("<ul>");
  	Enumeration attrs = request.getAttributeNames();
  	while (attrs.hasMoreElements()) {
   	    String attr = (String) attrs.nextElement();
  	    writer.println("<li><b>attribute</b> " + attr + " = " +
  			   request.getAttribute(attr));
  	}
  	writer.println("<li><b>characterEncoding</b> = " +
  		       request.getCharacterEncoding());
  	writer.println("<li><b>contentLength</b> = " +
  		       request.getContentLength());
  	writer.println("<li><b>contentType</b> = " +
  		       request.getContentType());
  	Enumeration locales = request.getLocales();
  	while (locales.hasMoreElements()) {
  	    Locale locale = (Locale) locales.nextElement();
  	    writer.println("<li><b>locale</b> = " + locale);
  	}
  	Enumeration params = request.getParameterNames();
  	while (params.hasMoreElements()) {
  	    String param = (String) params.nextElement();
  	    String values[] = request.getParameterValues(param);
  	    for (int i = 0; i < values.length; i++)
  		writer.println("<li><b>parameter</b> " + param + " = " +
  			       values[i]);
  	}
  	writer.println("<li><b>protocol</b> = " + request.getProtocol());
  	writer.println("<li><b>remoteAddr</b> = " + request.getRemoteAddr());
  	writer.println("<li><b>remoteHost</b> = " + request.getRemoteHost());
  	writer.println("<li><b>scheme</b> = " + request.getScheme());
  	writer.println("<li><b>secure</b> = " + request.isSecure());
  	writer.println("<li><b>serverName</b> = " + request.getServerName());
  	writer.println("<li><b>serverPort</b> = " + request.getServerPort());
  	writer.println("</ul>");
  	writer.println("<hr>");
  
  	// Document the properties from HttpServletRequest
  	writer.println("<h1>HttpServletRequest Properties</h1>");
  	writer.println("<ul>");
  	writer.println("<li><b>authType</b> = " + request.getAuthType());
  	writer.println("<li><b>contextPath</b> = " +
  		       request.getContextPath());
  	Cookie cookies[] = request.getCookies();
  	for (int i = 0; i < cookies.length; i++)
  	    writer.println("<li><b>cookie</b> " + cookies[i].getName() +
  			   " = " + cookies[i].getValue());
  	Enumeration headers = request.getHeaderNames();
  	while (headers.hasMoreElements()) {
  	    String header = (String) headers.nextElement();
  	    writer.println("<li><b>header</b> " + header + " = " +
  			   request.getHeader(header));
  	}
  	writer.println("<li><b>method</b> = " + request.getMethod());
  	writer.println("<li><b>pathInfo</b> = " + request.getPathInfo());
  	writer.println("<li><b>pathTranslated</b> = " +
  		       request.getPathTranslated());
  	writer.println("<li><b>queryString</b> = " +
  		       request.getQueryString());
  	writer.println("<li><b>remoteUser</b> = " +
  		       request.getRemoteUser());
  	writer.println("<li><b>requestedSessionId</b> = " +
  		       request.getRequestedSessionId());
  	writer.println("<li><b>requestedSessionIdFromCookie</b> = " +
  		       request.isRequestedSessionIdFromCookie());
  	writer.println("<li><b>requestedSessionIdFromURL</b> = " +
  		       request.isRequestedSessionIdFromURL());
  	writer.println("<li><b>requestedSessionIdValid</b> = " +
  		       request.isRequestedSessionIdValid());
  	writer.println("<li><b>requestURI</b> = " +
  		       request.getRequestURI());
  	writer.println("<li><b>servletPath</b> = " +
  		       request.getServletPath());
  	writer.println("<li><b>userPrincipal</b> = " +
  		       request.getUserPrincipal());
  	writer.println("</ul>");
  	writer.println("<hr>");
  
  	// Document the servlet context attributes
  	writer.println("<h1>ServletContext Attributes</h1>");
  	writer.println("<ul>");
  	attrs = getServletContext().getAttributeNames();
  	while (attrs.hasMoreElements()) {
  	    String attr = (String) attrs.nextElement();
  	    writer.println("<li><b>" + attr + "</b> = " +
  			   getServletContext().getAttribute(attr));
  	}
  	writer.println("</ul>");
  	writer.println("<hr>");
  
  	// Generate HTML page footer
  	writer.println("</body>");
  	writer.println("</html>");
  
      }
  
  
      /**
       * Process a POST request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      public void doPost(HttpServletRequest request,
  		       HttpServletResponse response)
  	throws IOException, ServletException {
  
  	doGet(request, response);
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/WebdavServlet.java
  
  Index: WebdavServlet.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/WebdavServlet.java,v 1.1 2000/08/11 23:39:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:52 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  
  package org.apache.catalina.servlets;
  
  
  import java.io.BufferedInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.InputStream;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.StringWriter;
  import java.io.Writer;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.sql.Timestamp;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.Vector;
  import java.util.Stack;
  import java.util.StringTokenizer;
  import java.util.Locale;
  import java.util.Hashtable;
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import javax.servlet.RequestDispatcher;
  import javax.servlet.ServletException;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletOutputStream;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  import org.w3c.dom.Element;
  import org.w3c.dom.Document;
  import org.xml.sax.InputSource;
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import org.apache.catalina.Resources;
  import org.apache.catalina.core.ApplicationContext;
  import org.apache.catalina.resources.ResourceBean;
  import org.apache.catalina.resources.DirectoryBean;
  import org.apache.catalina.util.MD5Encoder;
  import org.apache.catalina.util.StringManager;
  import org.apache.catalina.util.XMLWriter;
  import org.apache.catalina.util.DOMWriter;
  import org.apache.catalina.util.xml.SaxContext;
  import org.apache.catalina.util.xml.XmlAction;
  import org.apache.catalina.util.xml.XmlMapper;
  
  
  /**
   * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
   * are handled by the DefaultServlet.
   *
   * @author Remy Maucherat
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:52 $
   */
  
  public class WebdavServlet
      extends DefaultServlet {
  
  
      // -------------------------------------------------------------- Constants
  
  
      private static final String METHOD_HEAD = "HEAD";
      private static final String METHOD_PROPFIND = "PROPFIND";
      private static final String METHOD_PROPPATCH = "PROPPATCH";
      private static final String METHOD_MKCOL = "MKCOL";
      private static final String METHOD_COPY = "COPY";
      private static final String METHOD_MOVE = "MOVE";
      private static final String METHOD_LOCK = "LOCK";
      private static final String METHOD_UNLOCK = "UNLOCK";
      
  
      /**
       * Default depth is infite.
       */
      private static final int INFINITY = 3; // To limit tree browsing a bit
      
      
      /**
       * PROPFIND - Specify a property mask.
       */
      private static final int FIND_BY_PROPERTY = 0;
      
      
      /**
       * PROPFIND - Display all properties.
       */
      private static final int FIND_ALL_PROP = 1;
      
      
      /**
       * PROPFIND - Return property names.
       */
      private static final int FIND_PROPERTY_NAMES = 2;
  
  
      /**
       * Create a new lock.
       */
      private static final int LOCK_CREATION = 0;
      
      
      /**
       * Refresh lock.
       */
      private static final int LOCK_REFRESH = 1;
      
      
      /**
       * Default lock timeout value.
       */
      private static final int DEFAULT_TIMEOUT = 3600;
      
      
      /**
       * Maximum lock timeout.
       */
      private static final int MAX_TIMEOUT = 604800;
      
      
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * JAXP Document builder.
       */
      private DocumentBuilder documentBuilder;
  
  
      /**
       * Repository of the locks put on single resources.
       * <p>
       * Key : path <br>
       * Value : LockInfo
       */
      private Hashtable resourceLocks = new Hashtable();
  
  
      /**
       * Repository of the lock-null resources.
       * <p>
       * Key : path of the collection containing the lock-null resource<br>
       * Value : Vector of lock-null resource which are members of the 
       * collection. Each element of the Vector is the path associated with
       * the lock-null resource.
       */
      private Hashtable lockNullResources = new Hashtable();
  
  
      /**
       * Vector of the heritable locks.
       * <p>
       * Key : path <br>
       * Value : LockInfo
       */
      private Vector collectionLocks = new Vector();
  
  
      /**
       * Secret information used to generate reasonably secure lock ids.
       */
      private String secret = "catalina";
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Initialize this servlet.
       */
      public void init() 
          throws ServletException {
  
          super.init();
          
          String value = null;
  	try {
  	    value = getServletConfig().getInitParameter("secret");
              if (value != null)
                  secret = value;
          } catch (Throwable t) {
  	    ;
  	}
          
          try {
              documentBuilder = 
                  DocumentBuilderFactory.newInstance().newDocumentBuilder();
          } catch(ParserConfigurationException e) {
              throw new ServletException
                  (sm.getString("webdavservlet.jaxpfailed"));
          }
  
      }
  
  
      // ------------------------------------------------------ Protected Methods
  
  
      /**
       * Handles the special WebDAV methods.
       */
      protected void service(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
  	String method = req.getMethod();
  
  	String path = getRelativePath(req);
          System.out.println("[" + method + "] " + path);
          
  	if (method.equals(METHOD_PROPFIND)) {
  	    doPropfind(req, resp);
          } else if (method.equals(METHOD_PROPPATCH)) {
              doProppatch(req, resp);
          } else if (method.equals(METHOD_MKCOL)) {
              doMkcol(req, resp);
          } else if (method.equals(METHOD_COPY)) {
              doCopy(req, resp);
          } else if (method.equals(METHOD_MOVE)) {
              doMove(req, resp);
          } else if (method.equals(METHOD_LOCK)) {
              doLock(req, resp);
          } else if (method.equals(METHOD_UNLOCK)) {
              doUnlock(req, resp);
          } else {
              // DefaultServlet processing
              super.service(req, resp);
          }
          
      }
  
  
      /**
       * Check if the conditions specified in the optional If headers are 
       * satisfied.
       * 
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       * @param resourceInfo File object
       * @return boolean true if the resource meets all the specified conditions,
       * and false if any of the conditions is not satisfied, in which case
       * request processing is stopped
       */
      protected boolean checkIfHeaders(HttpServletRequest request,
                                       HttpServletResponse response, 
                                       ResourceInfo resourceInfo)
          throws IOException {
  
          if (!super.checkIfHeaders(request, response, resourceInfo))
              return false;
  
          // TODO : Checking the WebDAV If header
          return true;
  
      }
  
  
      /**
       * OPTIONS Method.
       */
      protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
          
          String path = getRelativePath(req);
          
  	resp.addHeader("DAV", "1,2");
          String methodsAllowed = null;
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
          
          Resources resources = context.getResources();
          
  	if (!resources.exists(path)) {
  	    methodsAllowed = "OPTIONS, MKCOL, PUT, LOCK";
              resp.addHeader("Allow", methodsAllowed);
              return;
  	}
          
          methodsAllowed = "OPTIONS, GET, HEAD, POST, DELETE, TRACE, " 
              + "PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK";
          if (!resources.isCollection(path)) {
              methodsAllowed += ", PUT";
          }
          
          resp.addHeader("Allow", methodsAllowed);
          
      }
  
  
      /**
       * PROPFIND Method.
       */
      protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (!listings) {
              resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
              return;
          }
  
          String path = getRelativePath(req);
          
          // Properties which are to be displayed.
          Vector properties = null;
          // Propfind depth
          int depth = INFINITY;
          // Propfind type
          int type = FIND_ALL_PROP;
          
  	String depthStr = req.getHeader("Depth");
  	
  	if (depthStr == null) {
  	    depth = INFINITY;
  	} else {
  	    if (depthStr.equals("0")) {
                  depth = 0;
  	    } else if (depthStr.equals("1")) {
                  depth = 1;
  	    } else if (depthStr.equals("infinity")) {
                  depth = INFINITY;
  	    }
  	}
  
          Node propNode = null;
          
          try {
              Document document = documentBuilder.parse
                  (new InputSource(req.getInputStream()));
              
              // Get the root element of the document
              Element rootElement = document.getDocumentElement();
              NodeList childList = rootElement.getChildNodes();
              
              for (int i=0; i < childList.getLength(); i++) {
                  Node currentNode = childList.item(i);
                  switch (currentNode.getNodeType()) {
                  case Node.TEXT_NODE:
                      break;
                  case Node.ELEMENT_NODE:
                      if (currentNode.getNodeName().endsWith("prop")) {
                          type = FIND_BY_PROPERTY;
                          propNode = currentNode;
                      }
                      if (currentNode.getNodeName().endsWith("propname")) {
                          type = FIND_PROPERTY_NAMES;
                      }
                      if (currentNode.getNodeName().endsWith("allprop")) {
                          type = FIND_ALL_PROP;
                      }
                      break;
                  }
              }
          } catch(Exception e) {
              // Most likely there was no content : we use the defaults.
              // TODO : Enhance that !
          }
          
          if (type == FIND_BY_PROPERTY) {
              properties = new Vector();
              NodeList childList = propNode.getChildNodes();
              
              for (int i=0; i < childList.getLength(); i++) {
                  Node currentNode = childList.item(i);
                  switch (currentNode.getNodeType()) {
                  case Node.TEXT_NODE:
                      break;
                  case Node.ELEMENT_NODE:
                      String nodeName = currentNode.getNodeName();
                      String propertyName = null;
                      if (nodeName.indexOf(':') != -1) {
                          propertyName = nodeName.substring
                              (nodeName.indexOf(':') + 1);
                      } else {
                          propertyName = nodeName;
                      }
                      // href is a live property which is handled differently
                      properties.addElement(propertyName);
                      break;
                  }
              }
              
          }
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
  
          Resources resources = context.getResources();
          
  	if (!resources.exists(path)) {
  	    resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
  	    return;
  	}
  
          resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
  
          // Create multistatus object
          XMLWriter generatedXML = new XMLWriter();
          
          generatedXML.writeElement("d", "DAV", "multistatus", 
                                    XMLWriter.OPENING);
          
          if (depth == 0) {
              parseProperties(req, resources, generatedXML, path, type, 
                              properties);
          } else {
              // The stack always contains the object of the current level
              Stack stack = new Stack();
              stack.push(path);
              
              // Stack of the objects one level below
              Stack stackBelow = new Stack();
              
              while ((!stack.isEmpty()) && (depth >= 0)) {
                  
                  String currentPath = (String) stack.pop();
                  parseProperties(req, resources, generatedXML, currentPath, type,
                                  properties);
                  
                  if (resources.isCollection(currentPath)) {
                      String[] children = 
                          resources.getCollectionMembers(currentPath);
                      
                      for (int i=0; i<children.length; i++) {
                          stackBelow.push(children[i]);
                      }
                      
                      if (depth > 0) {
                          // Displaying the lock-null resources present in that 
                          // collection
                          Vector currentLockNullResources = 
                              (Vector) lockNullResources.get(currentPath);
                          if (currentLockNullResources != null) {
                              Enumeration lockNullResourcesList =
                                  currentLockNullResources.elements();
                              while (lockNullResourcesList.hasMoreElements()) {
                                  String lockNullPath = (String) 
                                      lockNullResourcesList.nextElement();
                                  parseLockNullProperties
                                      (req, generatedXML, currentPath, type, 
                                       properties);
                              }
                          }
                      }
                  }
                  
                  if (stack.isEmpty()) {
                      depth--;
                      stack = stackBelow;
                      stackBelow = new Stack();
                  }
                  
              }
          }
          
          generatedXML.writeElement("d", "multistatus", 
                                    XMLWriter.CLOSING);
          
          Writer writer = resp.getWriter();
          writer.write(generatedXML.toString());
          writer.flush();
          
      }
  
  
      /**
       * PROPPATCH Method.
       */
      protected void doProppatch(HttpServletRequest req, 
                                 HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return;
          }
  
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          
          
      }
  
  
      /**
       * MKCOL Method.
       */
      protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return;
          }
  
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          String path = getRelativePath(req);
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
  
          Resources resources = context.getResources();
          
  	// Can't create a collection if a resource already exists at the given
          // path
  	if (resources.exists(path)) {
  	    resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
  	    return;
  	}
  
          boolean result = resources.createCollection(path);
          
          if (!result) {
              resp.sendError(WebdavStatus.SC_CONFLICT,
                             WebdavStatus.getStatusText
                             (WebdavStatus.SC_CONFLICT));
          } else {
              resp.setStatus(WebdavStatus.SC_CREATED);
              // Removing any lock-null resource which would be present
              lockNullResources.remove(path);
          }
  
      }
  
  
      /**
       * DELETE Method.
       */
      protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return;
          }
          
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          deleteResource(req, resp);
          
      }
  
  
      /**
       * Process a POST request for the specified resource.
       *
       * @param request The servlet request we are processing
       * @param response The servlet response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet-specified error occurs
       */
      protected void doPut(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
          
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          super.doPut(req, resp);
          
          String path = getRelativePath(req);
          
          // Removing any lock-null resource which would be present
          lockNullResources.remove(path);
          
      }
  
      /**
       * COPY Method.
       */
      protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return;
          }
          
          copyResource(req, resp);
          
      }
  
  
      /**
       * MOVE Method.
       */
      protected void doMove(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          if (readOnly) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return;
          }
          
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          String path = getRelativePath(req);
          
          if (copyResource(req, resp)) {
              deleteResource(path, req, resp);
          }
          
      }
  
  
      /**
       * LOCK Method.
       */
      protected void doLock(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
          
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          LockInfo lock = new LockInfo();
          
          // Parsing lock request
          
          // Parsing depth header
          
  	String depthStr = req.getHeader("Depth");
  	
  	if (depthStr == null) {
  	    lock.depth = INFINITY;
  	} else {
  	    if (depthStr.equals("0")) {
  		lock.depth = 0;
  	    } else {
                  lock.depth = INFINITY;
  	    }
  	}
          
          // Parsing timeout header
          
          int lockDuration = DEFAULT_TIMEOUT;
          String lockDurationStr = req.getHeader("Timeout");
          if (lockDurationStr == null) {
              lockDuration = DEFAULT_TIMEOUT;
          } else {
              if (lockDurationStr.startsWith("Second-")) {
                  lockDuration = 
                      (new Integer(lockDurationStr.substring(7))).intValue();
              } else {
                  if (lockDurationStr.equalsIgnoreCase("infinity")) {
                      lockDuration = MAX_TIMEOUT;
                  } else {
                      try {
                          lockDuration = 
                              (new Integer(lockDurationStr)).intValue();
                      } catch (NumberFormatException e) {
                          lockDuration = MAX_TIMEOUT;
                      }
                  }
              }
              if (lockDuration == 0) {
                  lockDuration = DEFAULT_TIMEOUT;
              }
              if (lockDuration > MAX_TIMEOUT) {
                  lockDuration = MAX_TIMEOUT;
              }
          }
          lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
          
          int lockRequestType = LOCK_CREATION;
          
          Node lockInfoNode = null;
          
          try {
              Document document = documentBuilder.parse(new InputSource
                  (req.getInputStream()));
              
              // Get the root element of the document
              Element rootElement = document.getDocumentElement();
              lockInfoNode = rootElement;
          } catch(Exception e) {
              lockRequestType = LOCK_REFRESH;
          }
          
          if (lockInfoNode != null) {
              
              // Reading lock information
              
              NodeList childList = lockInfoNode.getChildNodes();
              StringWriter strWriter = null;
              DOMWriter domWriter = null;
              
              Node lockScopeNode = null;
              Node lockTypeNode = null;
              Node lockOwnerNode = null;
              
              for (int i=0; i < childList.getLength(); i++) {
                  Node currentNode = childList.item(i);
                  switch (currentNode.getNodeType()) {
                  case Node.TEXT_NODE:
                      break;
                  case Node.ELEMENT_NODE:
                      String nodeName = currentNode.getNodeName();
                      if (nodeName.endsWith("lockscope")) {
                          lockScopeNode = currentNode;
                      }
                      if (nodeName.endsWith("locktype")) {
                          lockTypeNode = currentNode;
                      }
                      if (nodeName.endsWith("owner")) {
                          lockOwnerNode = currentNode;
                      }
                      break;
                  }
              }
              
              if (lockScopeNode != null) {
                  
                  childList = lockScopeNode.getChildNodes();
                  for (int i=0; i < childList.getLength(); i++) {
                      Node currentNode = childList.item(i);
                      switch (currentNode.getNodeType()) {
                      case Node.TEXT_NODE:
                          break;
                      case Node.ELEMENT_NODE:
                          String tempScope = currentNode.getNodeName();
                          if (tempScope.indexOf(':') != -1) {
                              lock.scope = 
                                  tempScope.substring(tempScope.indexOf(':'));
                          } else {
                              lock.scope = tempScope;
                          }
                          break;
                      }
                  }
                  
                  if (lock.scope == null) {
                      // Bad request
                      resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
                  }
                  
              } else {
                  // Bad request
                  resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
              }
              
              if (lockTypeNode != null) {
                  
                  childList = lockTypeNode.getChildNodes();
                  for (int i=0; i < childList.getLength(); i++) {
                      Node currentNode = childList.item(i);
                      switch (currentNode.getNodeType()) {
                      case Node.TEXT_NODE:
                          break;
                      case Node.ELEMENT_NODE:
                          String tempType = currentNode.getNodeName();
                          if (tempType.indexOf(':') != -1) {
                              lock.type = 
                                  tempType.substring(tempType.indexOf(':') + 1);
                          } else {
                              lock.type = tempType;
                          }
                          break;
                      }
                  }
                  
                  if (lock.type == null) {
                      // Bad request
                      resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
                  }
                  
              } else {
                  // Bad request
                  resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
              }
              
              if (lockOwnerNode != null) {
                  
                  childList = lockOwnerNode.getChildNodes();
                  for (int i=0; i < childList.getLength(); i++) {
                      Node currentNode = childList.item(i);
                      switch (currentNode.getNodeType()) {
                      case Node.TEXT_NODE:
                          lock.owner += currentNode.getNodeValue();
                          break;
                      case Node.ELEMENT_NODE:
                          strWriter = new StringWriter();
                          domWriter = new DOMWriter(strWriter, true);
                          domWriter.print(currentNode);
                          lock.owner += strWriter.toString();
                          break;
                      }
                  }
                  
                  if (lock.owner == null) {
                      // Bad request
                      resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
                  }
                  
              } else {
                  lock.owner = new String();
              }
              
          }
          
          String path = getRelativePath(req);
          
          lock.path = path;
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
          
          Resources resources = context.getResources();
          
          Enumeration locksList = null;
          
          if (lockRequestType == LOCK_CREATION) {
              
              // Generating lock id
              String lockTokenStr = req.getServletPath() + "-" + lock.type + "-" 
                  + lock.scope + "-" + req.getUserPrincipal() + "-" 
                  + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-" 
                  + lock.expiresAt + "-" + System.currentTimeMillis() + "-" 
                  + secret;
              String lockToken = 
                  md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));
              
              if ( (resources.exists(path)) && (resources.isCollection(path)) && 
                   (lock.depth == INFINITY) ) {
                  
                  // Locking a collection (and all its member resources)
                  
                  // Checking if a child resource of this collection is 
                  // already locked
                  Vector lockPaths = new Vector();
                  locksList = collectionLocks.elements();
                  while (locksList.hasMoreElements()) {
                      LockInfo currentLock = (LockInfo) locksList.nextElement();
                      if (currentLock.hasExpired()) {
                          resourceLocks.remove(currentLock.path);
                          continue;
                      }
                      if ( (currentLock.path.startsWith(lock.path)) &&
                           ((currentLock.isExclusive()) || 
                            (lock.isExclusive())) ) {
                          // A child collection of this collection is locked
                          lockPaths.addElement(currentLock.path);
                      }
                  }
                  locksList = resourceLocks.elements();
                  while (locksList.hasMoreElements()) {
                      LockInfo currentLock = (LockInfo) locksList.nextElement();
                      if (currentLock.hasExpired()) {
                          resourceLocks.remove(currentLock.path);
                          continue;
                      }
                      if ( (currentLock.path.startsWith(lock.path)) &&
                           ((currentLock.isExclusive()) || 
                            (lock.isExclusive())) ) {
                          // A child resource of this collection is locked
                          lockPaths.addElement(currentLock.path);
                      }
                  }
                  
                  if (!lockPaths.isEmpty()) {
                      
                      // One of the child paths was locked
                      // We generate a multistatus error report
                      
                      Enumeration lockPathsList = lockPaths.elements();
                      
                      resp.setStatus(WebdavStatus.SC_CONFLICT);
                      
                      XMLWriter generatedXML = new XMLWriter();
                      generatedXML.writeXMLHeader();
                      
                      generatedXML.writeElement("d", "DAV", "multistatus", 
                                                XMLWriter.OPENING);
                      
                      while (lockPathsList.hasMoreElements()) {
                          generatedXML.writeElement("d", "DAV", "response", 
                                                    XMLWriter.OPENING);
                          generatedXML.writeElement("d", "DAV", "href", 
                                                    XMLWriter.OPENING);
                          generatedXML
                              .writeText((String) lockPathsList.nextElement());
                          generatedXML.writeElement("d", "href", 
                                                    XMLWriter.CLOSING);
                          generatedXML.writeElement("d", "DAV", "status", 
                                                    XMLWriter.OPENING);
                          generatedXML
                              .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
                                         + " " + WebdavStatus
                                         .getStatusText(WebdavStatus.SC_LOCKED));
                          generatedXML.writeElement("d", "status", 
                                                    XMLWriter.CLOSING);
                          
                          generatedXML.writeElement("d", "response", 
                                                    XMLWriter.CLOSING);
                      }
                      
                      generatedXML.writeElement("d", "multistatus", 
                                            XMLWriter.CLOSING);
                      
                      Writer writer = resp.getWriter();
                      writer.write(generatedXML.toString());
                      writer.close();
                      
                      return;
                      
                  }
                  
                  boolean addLock = true;
                  
                  // Checking if there is already a shared lock on this path
                  locksList = collectionLocks.elements();
                  while (locksList.hasMoreElements()) {
                      
                      LockInfo currentLock = (LockInfo) locksList.nextElement();
                      if (currentLock.path.equals(lock.path)) {
                          
                          if (currentLock.isExclusive()) {
                              resp.sendError(WebdavStatus.SC_LOCKED);
                              return;
                          } else {
                              if (lock.isExclusive()) {
                                  resp.sendError(WebdavStatus.SC_LOCKED);
                                  return;
                              }
                          }
                          
                          currentLock.tokens.addElement(lockToken);
                          lock = currentLock;
                          addLock = false;
                          
                      }
                      
                  }
                  
                  if (addLock) {
                      lock.tokens.addElement(lockToken);
                      collectionLocks.addElement(lock);
                  }
                  
              } else {
                  
                  // Locking a single resource
                  
                  // Retrieving an already existing lock on that resource
                  LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path);
                  if (presentLock != null) {
                      
                      if ( (presentLock.isExclusive()) || (lock.isExclusive()) ) {
                          // If either lock is exclusive, the lock can't be 
                          // granted
                          resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
                          return;
                      } else {
                          presentLock.tokens.addElement(lockToken);
                          lock = presentLock;
                      }
                      
                  } else {
                      
                      lock.tokens.addElement(lockToken);
                      resourceLocks.put(lock.path, lock);
                      
                      // Checking if a resource exists at this path
                      if (!resources.exists(lock.path)) {
                          
                          // "Creating" a lock-null resource
                          int slash = lock.path.lastIndexOf('/');
                          String parentPath = lock.path.substring(0, slash);
                          
                          Vector lockNulls = 
                              (Vector) lockNullResources.get(parentPath);
                          if (lockNulls == null) {
                              lockNulls = new Vector();
                              lockNullResources.put(parentPath, lockNulls);
                          }
                          
                          lockNulls.addElement(lock.path);
                          
                      }
                      
                  }
                  
              }
              
          }
          
          if (lockRequestType == LOCK_REFRESH) {
              
              String ifHeader = req.getHeader("If");
              if (ifHeader == null)
                  ifHeader = "";
              
              // Checking resource locks
              
              LockInfo toRenew = (LockInfo) resourceLocks.get(path);
              Enumeration tokenList = null;
              if (lock != null) {
                  
                  // At least one of the tokens of the locks must have been given
                  
                  tokenList = toRenew.tokens.elements();
                  while (tokenList.hasMoreElements()) {
                      String token = (String) tokenList.nextElement();
                      if (ifHeader.indexOf(token) != -1) {
                          toRenew.expiresAt = lock.expiresAt;
                          lock = toRenew;
                      }
                  }
                  
              }
              
              // Checking inheritable collection locks
              
              Enumeration collectionLocksList = collectionLocks.elements();
              while (collectionLocksList.hasMoreElements()) {
                  toRenew = (LockInfo) collectionLocksList.nextElement();
                  if (path.equals(toRenew.path)) {
                      
                      tokenList = toRenew.tokens.elements();
                      while (tokenList.hasMoreElements()) {
                          String token = (String) tokenList.nextElement();
                          if (ifHeader.indexOf(token) != -1) {
                              toRenew.expiresAt = lock.expiresAt;
                              lock = toRenew;
                          }
                      }
                      
                  }
              }
              
          }
          
          // Set the status, then generate the XML response containing 
          // the lock information
          XMLWriter generatedXML = new XMLWriter();
          generatedXML.writeXMLHeader();
          generatedXML.writeElement("d", "DAV", "prop", XMLWriter.OPENING);
          
          generatedXML.writeElement("d", "DAV", "lockdiscovery", 
                                    XMLWriter.OPENING);
          
          lock.toXML(generatedXML, true);
          
          generatedXML.writeElement("d", "DAV", "lockdiscovery", 
                                    XMLWriter.CLOSING);
          
          generatedXML.writeElement("d", "DAV", "prop", XMLWriter.CLOSING);
          
          resp.setStatus(WebdavStatus.SC_OK);
          Writer writer = resp.getWriter();
          writer.write(generatedXML.toString());
          writer.close();
          
      }
  
  
      /**
       * UNLOCK Method.
       */
      protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
          
          if (isLocked(req)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return;
          }
          
          String path = getRelativePath(req);
          
          String lockTokenHeader = req.getHeader("Lock-Token");
          if (lockTokenHeader == null)
              lockTokenHeader = "";
          
          // Checking resource locks
          
          LockInfo lock = (LockInfo) resourceLocks.get(path);
          Enumeration tokenList = null;
          if (lock != null) {
              
              // At least one of the tokens of the locks must have been given
              
              tokenList = lock.tokens.elements();
              while (tokenList.hasMoreElements()) {
                  String token = (String) tokenList.nextElement();
                  if (lockTokenHeader.indexOf(token) != -1) {
                      lock.tokens.removeElement(token);
                  }
              }
              
              if (lock.tokens.isEmpty()) {
                  resourceLocks.remove(path);
                  // Removing any lock-null resource which would be present
                  lockNullResources.remove(path);
              }
              
          }
          
          // Checking inheritable collection locks
          
          Enumeration collectionLocksList = collectionLocks.elements();
          while (collectionLocksList.hasMoreElements()) {
              lock = (LockInfo) collectionLocksList.nextElement();
              if (path.equals(lock.path)) {
                  
                  tokenList = lock.tokens.elements();
                  while (tokenList.hasMoreElements()) {
                      String token = (String) tokenList.nextElement();
                      if (lockTokenHeader.indexOf(token) != -1) {
                          lock.tokens.removeElement(token);
                          break;
                      }
                  }
                  
                  if (lock.tokens.isEmpty()) {
                      collectionLocks.removeElement(lock);
                      // Removing any lock-null resource which would be present
                      lockNullResources.remove(path);
                  }
                  
              }
          }
          
          resp.setStatus(WebdavStatus.SC_NO_CONTENT);
          
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Check to see if a resource is currently write locked. The method
       * will look at the "If" header to make sure the client
       * has give the appropriate lock tokens.
       * 
       * @param req Servlet request
       * @return boolean true if the resource is locked (and no appropriate
       * lock token has been found for at least one of the non-shared locks which
       * are present on the resource).
       */
      private boolean isLocked(HttpServletRequest req) {
          
          String path = getRelativePath(req);
          
          String ifHeader = req.getHeader("If");
          if (ifHeader == null)
              ifHeader = "";
          
          String lockTokenHeader = req.getHeader("Lock-Token");
          if (lockTokenHeader == null)
              lockTokenHeader = "";
          
          return isLocked(path, ifHeader + lockTokenHeader);
          
      }
  
  
      /**
       * Check to see if a resource is currently write locked.
       * 
       * @param path Path of the resource
       * @param ifHeader "If" HTTP header which was included in the request
       * @return boolean true if the resource is locked (and no appropriate
       * lock token has been found for at least one of the non-shared locks which
       * are present on the resource).
       */
      private boolean isLocked(String path, String ifHeader) {
          
          // Checking resource locks
          
          LockInfo lock = (LockInfo) resourceLocks.get(path);
          Enumeration tokenList = null;
          if ((lock != null) && (lock.hasExpired())) {
              resourceLocks.remove(path);
          } else if (lock != null) {
              
              // At least one of the tokens of the locks must have been given
              
              tokenList = lock.tokens.elements();
              boolean tokenMatch = false;
              while (tokenList.hasMoreElements()) {
                  String token = (String) tokenList.nextElement();
                  if (ifHeader.indexOf(token) != -1)
                      tokenMatch = true;
              }
              if (!tokenMatch)
                  return true;
              
          }
          
          // Checking inheritable collection locks
          
          Enumeration collectionLocksList = collectionLocks.elements();
          while (collectionLocksList.hasMoreElements()) {
              lock = (LockInfo) collectionLocksList.nextElement();
              if (lock.hasExpired()) {
                  collectionLocks.removeElement(lock);
              } else if (path.startsWith(lock.path)) {
                  
                  tokenList = lock.tokens.elements();
                  boolean tokenMatch = false;
                  while (tokenList.hasMoreElements()) {
                      String token = (String) tokenList.nextElement();
                      if (ifHeader.indexOf(token) != -1)
                          tokenMatch = true;
                  }
                  if (!tokenMatch)
                      return true;
                  
              }
          }
          
          return false;
          
      }
  
  
      /**
       * Copy a resource.
       * 
       * @param req Servlet request
       * @param resp Servlet response
       * @return boolean true if the copy is successful
       */
      private boolean copyResource(HttpServletRequest req, 
                                   HttpServletResponse resp)
          throws ServletException, IOException {
          
          // Parsing destination header
          
          String destinationPath = req.getHeader("Destination");
          
          if (destinationPath.startsWith("http://")) {
              destinationPath = destinationPath.substring("http://".length());
          }
          
          String hostName = req.getServerName();
          if ((hostName != null) && (destinationPath.startsWith(hostName))) {
              destinationPath = destinationPath.substring(hostName.length());
          }
          
          if (destinationPath.startsWith(":")) {
              int firstSeparator = destinationPath.indexOf("/");
              if (firstSeparator < 0) {
                  destinationPath = "/";
              } else {
                  destinationPath = destinationPath.substring(firstSeparator);
              }
          }
          
          String contextPath = req.getContextPath();
          if ((contextPath != null) && 
              (destinationPath.startsWith(contextPath))) {
              destinationPath = destinationPath.substring(contextPath.length());
          }
          
          String pathInfo = req.getPathInfo();
          if (pathInfo != null) {
              String servletPath = req.getServletPath();
              if ((servletPath != null) && 
                  (destinationPath.startsWith(servletPath))) {
                  destinationPath = destinationPath
                      .substring(servletPath.length());
              }
          }
          System.out.println("Dest path :" + destinationPath);
          
          String path = getRelativePath(req);
          
          if (destinationPath.equals(path)) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return false;
          }
          
          // Parsing overwrite header
          
          boolean overwrite = true;
          String overwriteHeader = req.getHeader("Overwrite");
          
          if (overwriteHeader != null) {
              if (overwriteHeader.equalsIgnoreCase("T")) {
                  overwrite = true;
              } else {
                  overwrite = false;
              }
          }
          
          // Overwriting the destination
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
          
          Resources resources = context.getResources();
          
          if (overwrite) {
              
              // Delete destination resource, if it exists
              if (resources.exists(destinationPath)) {
                  if (!deleteResource(destinationPath, req, resp)) {
                      return false;
                  } else {
                      resp.setStatus(WebdavStatus.SC_NO_CONTENT);
                  }
              } else {
                  resp.setStatus(WebdavStatus.SC_CREATED);
              }
              
          } else {
              
              // If the destination exists, then it's a conflict
              if (resources.exists(destinationPath)) {
                  resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
                  return false;
              }
              
          }
          
          // Copying source to destination
          
          Hashtable errorList = new Hashtable();
          
          boolean result = copyResource(resources, errorList, 
                                        path, destinationPath);
          
          if ((!result) || (!errorList.isEmpty())) {
              
              sendReport(req, resp, errorList);
              return false;
              
          }
          
          // Removing any lock-null resource which would be present at 
          // the destination path
          lockNullResources.remove(destinationPath);
          
          return true;
          
      }
  
  
      /**
       * Copy a collection.
       * 
       * @param resources Resources implementation to be used
       * @param errorList Hashtable containing the list of errors which occured
       * during the copy operation
       * @param source Path of the resource to be copied
       * @param dest Destination path
       */
      private boolean copyResource(Resources resources, Hashtable errorList,
                                   String source, String dest) {
          
          if (resources.isCollection(source)) {
              
              if (!resources.createCollection(dest)) {
                  errorList.put
                      (dest, 
                       new Integer(WebdavStatus.SC_CONFLICT));
                  return false;
              }
              String[] members = resources.getCollectionMembers(source);
              for (int i=0; i<members.length; i++) {
                  String childDest = dest + 
                      members[i].substring(source.length());
                  copyResource(resources, errorList, members[i], childDest);
              }
              
          } else {
              
              InputStream is = resources.getResourceAsStream(source);
              if (!resources.setResource(dest, is)) {
                  errorList.put
                      (source, 
                       new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                  return false;
              }
              
          }
          
          return true;
          
      }
  
  
      /**
       * Delete a resource.
       * 
       * @param req Servlet request
       * @param resp Servlet response
       * @return boolean true if the copy is successful
       */
      private boolean deleteResource(HttpServletRequest req, 
                                     HttpServletResponse resp)
          throws ServletException, IOException {
          
          String path = getRelativePath(req);
          
          return deleteResource(path, req, resp);
          
      }
  
  
      /**
       * Delete a resource.
       * 
       * @param path Path of the resource which is to be deleted
       * @param req Servlet request
       * @param resp Servlet response
       */
      private boolean deleteResource(String path, HttpServletRequest req, 
                                     HttpServletResponse resp)
          throws ServletException, IOException {
          
          String ifHeader = req.getHeader("If");
          if (ifHeader == null)
              ifHeader = "";
          
          String lockTokenHeader = req.getHeader("Lock-Token");
          if (lockTokenHeader == null)
              lockTokenHeader = "";
          
          if (isLocked(path, ifHeader + lockTokenHeader)) {
              resp.sendError(WebdavStatus.SC_LOCKED);
              return false;
          }
          
          // Retrieve the Catalina context
          ApplicationContext context = (ApplicationContext) getServletContext();
  
          Resources resources = context.getResources();
          
  	if (!resources.exists(path)) {
              resp.sendError(WebdavStatus.SC_NOT_FOUND);
  	    return false;
  	}
          
          boolean collection = resources.isCollection(path);
          
          if (!collection) {
              if (!resources.deleteResource(path)) {
                  resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
                  return false;
              }
          } else {
              
              Hashtable errorList = new Hashtable();
              
              deleteCollection(req, resources, path, errorList);
              resources.deleteResource(path);
              
              if (!errorList.isEmpty()) {
                  
                  sendReport(req, resp, errorList);
                  return false;
                  
              }
              
          }
          
          resp.setStatus(WebdavStatus.SC_NO_CONTENT);
          return true;
          
      }
  
  
      /**
       * Deletes a collection.
       * 
       * @param resources Resources implementation associated with the context
       * @param path Path to the collection to be deleted
       * @param errorList Contains the list of the errors which occured
       */
      private void deleteCollection(HttpServletRequest req, Resources resources, 
                                    String path, Hashtable errorList) {
          
          String ifHeader = req.getHeader("If");
          if (ifHeader == null)
              ifHeader = "";
          
          String lockTokenHeader = req.getHeader("Lock-Token");
          if (lockTokenHeader == null)
              lockTokenHeader = "";
          
          String[] members = resources.getCollectionMembers(path);
          
          for (int i=0; i<members.length; i++) {
              
              if (isLocked(members[i], ifHeader + lockTokenHeader)) {
                  
                  errorList.put(members[i], new Integer(WebdavStatus.SC_LOCKED));
                  
              } else {
                  
                  if (resources.isCollection(members[i])) {
                      deleteCollection(req, resources, members[i], errorList);
                  }
                  
                  boolean result = resources.deleteResource(members[i]);
                  if (!result) {
                      if (!resources.isCollection(members[i])) {
                          // If it's not a collection, then it's an unknown
                          // error
                          errorList.put
                              (members[i], new Integer
                                  (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                      }
                  }
                  
              }
              
          }
          
      }
  
  
      /**
       * Send a multistatus element containing a complete error report to the
       * client.
       * 
       * @param req Servlet request
       * @param resp Servlet response
       * @param errorList List of error to be displayed
       */
      private void sendReport(HttpServletRequest req, HttpServletResponse resp,
                              Hashtable errorList) 
          throws ServletException, IOException {
          
          resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
          
          String absoluteUri = req.getRequestURI();
          String relativePath = getRelativePath(req);
          
          XMLWriter generatedXML = new XMLWriter();
          generatedXML.writeXMLHeader();
          
          generatedXML.writeElement("d", "DAV", "multistatus", 
                                    XMLWriter.OPENING);
          
          Enumeration pathList = errorList.keys();
          while (pathList.hasMoreElements()) {
              
              String errorPath = (String) pathList.nextElement();
              int errorCode = ((Integer) errorList.get(errorPath)).intValue();
              
              generatedXML.writeElement("d", "DAV", "response", 
                                        XMLWriter.OPENING);
              
              generatedXML.writeElement("d", "DAV", "href", 
                                        XMLWriter.OPENING);
              String toAppend = errorPath.substring(relativePath.length());
              if (!toAppend.startsWith("/"))
                  toAppend = "/" + toAppend;
              generatedXML.writeText(absoluteUri + toAppend);
              generatedXML.writeElement("d", "href", 
                                        XMLWriter.CLOSING);
              generatedXML.writeElement("d", "DAV", "status", 
                                        XMLWriter.OPENING);
              generatedXML
                  .writeText("HTTP/1.1 " + errorCode + " " 
                             + WebdavStatus.getStatusText(errorCode));
              generatedXML.writeElement("d", "status", 
                                        XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "response", 
                                        XMLWriter.CLOSING);
              
          }
          
          generatedXML.writeElement("d", "multistatus", 
                                    XMLWriter.CLOSING);
          
          Writer writer = resp.getWriter();
          writer.write(generatedXML.toString());
          writer.close();
          
      }
      
  
      /**
       * Propfind helper method.
       * 
       * @param resources Resources object associated with this context
       * @param generatedXML XML response to the Propfind request
       * @param path Path of the current resource
       * @param type Propfind type
       * @param propertiesVector If the propfind type is find properties by 
       * name, then this Vector contains those properties
       */
      private void parseProperties(HttpServletRequest req,
                                   Resources resources, XMLWriter generatedXML,
                                   String path, int type, 
                                   Vector propertiesVector) {
          
  	// Exclude any resource in the /WEB-INF and /META-INF subdirectories
  	// (the "toUpperCase()" avoids problems on Windows systems)
          if (path.toUpperCase().startsWith("/WEB-INF") ||
              path.toUpperCase().startsWith("/META-INF"))
              return;
          
          ResourceInfo resourceInfo = new ResourceInfo(path, resources);
          
          generatedXML.writeElement("d", "response", XMLWriter.OPENING);
          String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " " 
                                     + WebdavStatus.getStatusText
                                     (WebdavStatus.SC_OK));
          
          // Generating href element
          generatedXML.writeElement("d", "href", XMLWriter.OPENING);
          
          String absoluteUri = req.getRequestURI();
          String relativePath = getRelativePath(req);
          String toAppend = path.substring(relativePath.length());
          if (!toAppend.startsWith("/"))
              toAppend = "/" + toAppend;
          
          generatedXML.writeText(absoluteUri + toAppend);
          
          generatedXML.writeElement("d", "href", XMLWriter.CLOSING);
          
          switch (type) {
              
          case FIND_ALL_PROP :
              
              generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
              generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
              
              generatedXML.writeProperty("d", "DAV", "creationdate", 
                                         formats[0].format(new Date
                                             (resourceInfo.creationDate)));
              generatedXML.writeProperty("d", "DAV", "displayname", 
                                         resourceInfo.path);
              generatedXML.writeProperty("d", "DAV", "getcontentlanguage", 
                                         Locale.getDefault().toString());
              generatedXML.writeProperty("d", "DAV", "getlastmodified", 
                                         resourceInfo.httpDate);
              if (!resourceInfo.collection) {
                  generatedXML.writeProperty
                      ("d", "DAV", "getcontentlength", 
                       String.valueOf(resourceInfo.length));
                  generatedXML.writeProperty
                      ("d", "DAV", "getcontenttype", 
                       getServletContext().getMimeType(resourceInfo.path));
                  generatedXML.writeProperty("d", "DAV", "getetag", 
                                             getETagValue(resourceInfo, true));
                  generatedXML.writeElement("d", "DAV", "resourcetype", 
                                            XMLWriter.NO_CONTENT);
              } else {
                  generatedXML.writeElement("d", "DAV", "resourcetype", 
                                            XMLWriter.OPENING);
                  generatedXML.writeElement("d", "collection", 
                                            XMLWriter.NO_CONTENT);
                  generatedXML.writeElement("d", "resourcetype", 
                                            XMLWriter.CLOSING);
              }
              
              generatedXML.writeProperty("d", "DAV", "source", "");
              
              String supportedLocks = "<d:lockentry>" 
                  + "<d:lockscope><d:exclusive/></d:lockscope>"
                  + "<d:locktype><d:write/></d:locktype>"
                  + "</d:lockentry>" + "<d:lockentry>" 
                  + "<d:lockscope><d:shared/></d:lockscope>"
                  + "<d:locktype><d:write/></d:locktype>"
                  + "</d:lockentry>";
              generatedXML.writeProperty("d", "DAV", "supportedlock", 
                                         supportedLocks);
              
              generateLockDiscovery(path, generatedXML);
              
              generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
              generatedXML.writeProperty("d", "status", status);
              generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
              
              break;
              
  	case FIND_PROPERTY_NAMES :
              
              generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
              generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
              
              generatedXML.writeElement("d", "DAV", "creationdate", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "displayname", 
                                        XMLWriter.NO_CONTENT);
              if (!resourceInfo.collection) {
                  generatedXML.writeElement("d", "DAV", "getcontentlanguage",
                                            XMLWriter.NO_CONTENT);
                  generatedXML.writeElement("d", "DAV", "getcontentlength", 
                                            XMLWriter.NO_CONTENT);
                  generatedXML.writeElement("d", "DAV", "getcontenttype", 
                                            XMLWriter.NO_CONTENT);
                  generatedXML.writeElement("d", "DAV", "getetag", 
                                            XMLWriter.NO_CONTENT);
                  generatedXML.writeElement("d", "DAV", "getlastmodified", 
                                            XMLWriter.NO_CONTENT);
              }
              generatedXML.writeElement("d", "DAV", "resourcetype", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "source", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "lockdiscovery", 
                                        XMLWriter.NO_CONTENT);
              
              generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
              generatedXML.writeProperty("d", "status", status);
              generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
              
              break;
              
          case FIND_BY_PROPERTY :
              
              Vector propertiesNotFound = new Vector();
              
              // Parse the list of properties
              
              generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
              generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
              
              Enumeration properties = propertiesVector.elements();
              
              while (properties.hasMoreElements()) {
                  
                  String property = (String) properties.nextElement();
                  
                  if (property.equals("creationdate")) {
                      generatedXML.writeProperty
                          ("d", "DAV", "creationdate", formats[0].format
                           (new Date(resourceInfo.creationDate)));
                  } else if (property.equals("displayname")) {
                      generatedXML.writeProperty("d", "DAV", "displayname", 
                                                 resourceInfo.path);
                  } else if (property.equals("getcontentlanguage")) {
                      if (resourceInfo.collection) {
                          propertiesNotFound.addElement(property);
                      } else {
                          generatedXML.writeProperty
                              ("d", "DAV", "getcontentlanguage", 
                               Locale.getDefault().toString());
                      }
                  } else if (property.equals("getcontentlength")) {
                      if (resourceInfo.collection) {
                          propertiesNotFound.addElement(property);
                      } else {
                          generatedXML.writeProperty
                              ("d", "DAV", "getcontentlength", 
                               (String.valueOf(resourceInfo.length)));
                      }
                  } else if (property.equals("getcontenttype")) {
                      if (resourceInfo.collection) {
                          propertiesNotFound.addElement(property);
                      } else {
                          generatedXML.writeProperty
                              ("d", "DAV", "getcontenttype", 
                               getServletContext().getMimeType
                               (resourceInfo.path));
                      }
                  } else if (property.equals("getetag")) {
                      if (resourceInfo.collection) {
                          propertiesNotFound.addElement(property);
                      } else {
                          generatedXML.writeProperty
                              ("d", "DAV", "getetag", 
                               getETagValue(resourceInfo, true));
                      }
                  } else if (property.equals("getlastmodified")) {
                      if (resourceInfo.collection) {
                          propertiesNotFound.addElement(property);
                      } else {
                          generatedXML.writeProperty("d", "DAV", 
                                                     "getlastmodified", 
                                                     resourceInfo.httpDate);
                      }
                  } else if (property.equals("resourcetype")) {
                      if (resourceInfo.collection) {
                          generatedXML.writeElement("d", "DAV", "resourcetype", 
                                                    XMLWriter.OPENING);
                          generatedXML.writeElement("d", "collection", 
                                                    XMLWriter.NO_CONTENT);
                          generatedXML.writeElement("d", "resourcetype", 
                                                    XMLWriter.CLOSING);
                      } else {
                          generatedXML.writeElement("d", "DAV", "resourcetype", 
                                                    XMLWriter.NO_CONTENT);
                      }
                  } else if (property.equals("source")) {
                      generatedXML.writeProperty("d", "DAV", "source", "");
                  } else if (property.equals("supportedlock")) {
                      supportedLocks = "<d:lockentry>" 
                          + "<d:lockscope><d:exclusive/></d:lockscope>"
                          + "<d:locktype><d:write/></d:locktype>"
                          + "</d:lockentry>" + "<d:lockentry>" 
                          + "<d:lockscope><d:shared/></d:lockscope>"
                          + "<d:locktype><d:write/></d:locktype>"
                          + "</d:lockentry>";
                      generatedXML.writeProperty("d", "DAV", "supportedlock",
                                                 supportedLocks);
                  } else if (property.equals("lockdiscovery")) {
                      generateLockDiscovery(path, generatedXML);
                  } else {
                      propertiesNotFound.addElement(property);
                  }
                  
              }
              
              generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
              generatedXML.writeProperty("d", "status", status);
              generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
              
              Enumeration propertiesNotFoundList = propertiesNotFound.elements();
              
              if (propertiesNotFoundList.hasMoreElements()) {
                  
                  status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND 
                                      + " " + WebdavStatus.getStatusText
                                      (WebdavStatus.SC_NOT_FOUND));
                  
                  generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
                  generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
                  
                  while (propertiesNotFoundList.hasMoreElements()) {
                      generatedXML.writeElement
                          (null, (String) propertiesNotFoundList.nextElement(), 
                           XMLWriter.NO_CONTENT);
                  }
                  
                  generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
                  generatedXML.writeProperty("d", "status", status);
                  generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
                  
              }
              
              break;
              
          }
          
          generatedXML.writeElement("d", "response", XMLWriter.CLOSING);
          
      }
  
  
      /**
       * Propfind helper method. Dispays the properties of a lock-null resource.
       * 
       * @param resources Resources object associated with this context
       * @param generatedXML XML response to the Propfind request
       * @param path Path of the current resource
       * @param type Propfind type
       * @param propertiesVector If the propfind type is find properties by 
       * name, then this Vector contains those properties
       */
      private void parseLockNullProperties(HttpServletRequest req,
                                           XMLWriter generatedXML,
                                           String path, int type, 
                                           Vector propertiesVector) {
          
  	// Exclude any resource in the /WEB-INF and /META-INF subdirectories
  	// (the "toUpperCase()" avoids problems on Windows systems)
          if (path.toUpperCase().startsWith("/WEB-INF") ||
              path.toUpperCase().startsWith("/META-INF"))
              return;
          
          // Retrieving the lock associated with the lock-null resource
          LockInfo lock = (LockInfo) resourceLocks.get(path);
          
          if (lock == null)
              return;
          
          generatedXML.writeElement("d", "response", XMLWriter.OPENING);
          String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " " 
                                     + WebdavStatus.getStatusText
                                     (WebdavStatus.SC_OK));
          
          // Generating href element
          generatedXML.writeElement("d", "href", XMLWriter.OPENING);
          
          String absoluteUri = req.getRequestURI();
          String relativePath = getRelativePath(req);
          String toAppend = path.substring(relativePath.length());
          if (!toAppend.startsWith("/"))
              toAppend = "/" + toAppend;
          
          generatedXML.writeText(absoluteUri + toAppend);
          
          generatedXML.writeElement("d", "href", XMLWriter.CLOSING);
          
          switch (type) {
              
          case FIND_ALL_PROP :
              
              generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
              generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
              
              generatedXML.writeProperty("d", "DAV", "creationdate", 
                                         formats[0].format(lock.creationDate));
              generatedXML.writeProperty("d", "DAV", "displayname", path);
              generatedXML.writeProperty("d", "DAV", "getcontentlanguage", 
                                         Locale.getDefault().toString());
              generatedXML.writeProperty("d", "DAV", "getlastmodified", 
                                         formats[0].format(lock.creationDate));
              generatedXML.writeProperty
                  ("d", "DAV", "getcontentlength", String.valueOf(0));
              generatedXML.writeProperty("d", "DAV", "getcontenttype", "");
              generatedXML.writeProperty("d", "DAV", "getetag", "");
              generatedXML.writeElement("d", "DAV", "resourcetype", 
                                        XMLWriter.OPENING);
              generatedXML.writeElement("d", "lock-null", XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "resourcetype", 
                                        XMLWriter.CLOSING);
              
              generatedXML.writeProperty("d", "DAV", "source", "");
              
              String supportedLocks = "<d:lockentry>" 
                  + "<d:lockscope><d:exclusive/></d:lockscope>"
                  + "<d:locktype><d:write/></d:locktype>"
                  + "</d:lockentry>" + "<d:lockentry>" 
                  + "<d:lockscope><d:shared/></d:lockscope>"
                  + "<d:locktype><d:write/></d:locktype>"
                  + "</d:lockentry>";
              generatedXML.writeProperty("d", "DAV", "supportedlock", 
                                         supportedLocks);
              
              generateLockDiscovery(path, generatedXML);
              
              generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
              generatedXML.writeProperty("d", "status", status);
              generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
              
              break;
              
  	case FIND_PROPERTY_NAMES :
              
              generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
              generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
              
              generatedXML.writeElement("d", "DAV", "creationdate", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "displayname", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "getcontentlanguage",
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "getcontentlength", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "getcontenttype", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "getetag", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "getlastmodified", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "resourcetype", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "source", 
                                        XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "lockdiscovery", 
                                        XMLWriter.NO_CONTENT);
              
              generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
              generatedXML.writeProperty("d", "status", status);
              generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
              
              break;
              
          case FIND_BY_PROPERTY :
              
              Vector propertiesNotFound = new Vector();
              
              // Parse the list of properties
              
              generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
              generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
              
              Enumeration properties = propertiesVector.elements();
              
              while (properties.hasMoreElements()) {
                  
                  String property = (String) properties.nextElement();
                  
                  if (property.equals("creationdate")) {
                      generatedXML.writeProperty
                          ("d", "DAV", "creationdate", 
                           formats[0].format(lock.creationDate));
                  } else if (property.equals("displayname")) {
                      generatedXML.writeProperty("d", "DAV", "displayname", path);
                  } else if (property.equals("getcontentlanguage")) {
                      generatedXML.writeProperty
                          ("d", "DAV", "getcontentlanguage", 
                           Locale.getDefault().toString());
                  } else if (property.equals("getcontentlength")) {
                      generatedXML.writeProperty
                          ("d", "DAV", "getcontentlength", (String.valueOf(0)));
                  } else if (property.equals("getcontenttype")) {
                      generatedXML.writeProperty
                          ("d", "DAV", "getcontenttype", "");
                  } else if (property.equals("getetag")) {
                      generatedXML.writeProperty("d", "DAV", "getetag", "");
                  } else if (property.equals("getlastmodified")) {
                      generatedXML.writeProperty
                          ("d", "DAV", "getlastmodified", 
                           formats[0].format(lock.creationDate));
                  } else if (property.equals("resourcetype")) {
                      generatedXML.writeElement("d", "DAV", "resourcetype", 
                                                XMLWriter.OPENING);
                      generatedXML.writeElement("d", "lock-null", 
                                                XMLWriter.NO_CONTENT);
                      generatedXML.writeElement("d", "resourcetype", 
                                                XMLWriter.CLOSING);
                  } else if (property.equals("source")) {
                      generatedXML.writeProperty("d", "DAV", "source", "");
                  } else if (property.equals("supportedlock")) {
                      supportedLocks = "<d:lockentry>" 
                          + "<d:lockscope><d:exclusive/></d:lockscope>"
                          + "<d:locktype><d:write/></d:locktype>"
                          + "</d:lockentry>" + "<d:lockentry>" 
                          + "<d:lockscope><d:shared/></d:lockscope>"
                          + "<d:locktype><d:write/></d:locktype>"
                          + "</d:lockentry>";
                      generatedXML.writeProperty("d", "DAV", "supportedlock",
                                                 supportedLocks);
                  } else if (property.equals("lockdiscovery")) {
                      generateLockDiscovery(path, generatedXML);
                  } else {
                      propertiesNotFound.addElement(property);
                  }
                  
              }
              
              generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
              generatedXML.writeProperty("d", "status", status);
              generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
              
              Enumeration propertiesNotFoundList = propertiesNotFound.elements();
              
              if (propertiesNotFoundList.hasMoreElements()) {
                  
                  status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND 
                                      + " " + WebdavStatus.getStatusText
                                      (WebdavStatus.SC_NOT_FOUND));
                  
                  generatedXML.writeElement("d", "propstat", XMLWriter.OPENING);
                  generatedXML.writeElement("d", "prop", XMLWriter.OPENING);
                  
                  while (propertiesNotFoundList.hasMoreElements()) {
                      generatedXML.writeElement
                          (null, (String) propertiesNotFoundList.nextElement(), 
                           XMLWriter.NO_CONTENT);
                  }
                  
                  generatedXML.writeElement("d", "prop", XMLWriter.CLOSING);
                  generatedXML.writeProperty("d", "status", status);
                  generatedXML.writeElement("d", "propstat", XMLWriter.CLOSING);
                  
              }
              
              break;
              
          }
          
          generatedXML.writeElement("d", "response", XMLWriter.CLOSING);
          
      }
  
  
      /**
       * Print the lock discovery information associated with a path.
       * 
       * @param path Path
       * @param generatedXML XML data to which the locks info will be appended
       */
      private void generateLockDiscovery(String path, XMLWriter generatedXML) {
          
          generatedXML.writeElement("d", "DAV", "lockdiscovery", 
                                    XMLWriter.OPENING);
          
          LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
          if (resourceLock != null) {
              resourceLock.toXML(generatedXML);
          }
          
          Enumeration collectionLocksList = collectionLocks.elements();
          while (collectionLocksList.hasMoreElements()) {
              LockInfo currentLock = (LockInfo) collectionLocksList.nextElement();
              if (path.startsWith(currentLock.path)) {
                  currentLock.toXML(generatedXML);
              }
          }
          
          generatedXML.writeElement("d", "DAV", "lockdiscovery", 
                                    XMLWriter.CLOSING);
          
      }
  
  
      // --------------------------------------------------  LockInfo Inner Class
  
  
      /**
       * Holds a lock information.
       */
      private class LockInfo {
  
  
          // --------------------------------------------------------- Constructor
  
  
          /**
           * Constructor.
           * 
           * @param pathname Path name of the file
           */
          public LockInfo() {
  
          }
  
  
          // -------------------------------------------------- Instance Variables
  
  
          String path = "/";
          String type = "write";
          String scope = "exclusive";
          int depth = 0;
          String owner = "";
          Vector tokens = new Vector();
          long expiresAt = 0;
          Date creationDate = new Date();
          
          
          // ------------------------------------------------------ Public Methods
  
  
          /**
           * Get a String representation of this lock token.
           */
          public String toString() {
              
              String result =  "Type:" + type + "\n";
              result += "Scope:" + scope + "\n";
              result += "Depth:" + depth + "\n";
              result += "Owner:" + owner + "\n";
              result += "Expiration:" + 
                  formats[0].format(new Date(expiresAt)) + "\n";
              Enumeration tokensList = tokens.elements();
              while (tokensList.hasMoreElements()) {
                  result += "Token:" + tokensList.nextElement() + "\n";
              }
              return result;
              
          }
          
          
          /**
           * Return true if the lock has expired.
           */
          public boolean hasExpired() {
              return (System.currentTimeMillis() > expiresAt);
          }
          
          
          /**
           * Return true if the lock is exclusive.
           */
          public boolean isExclusive() {
              
              return (scope.equals("exclusive"));
              
          }
          
          
          /**
           * Get an XML representation of this lock token. This method will
           * append an XML fragment to the given XML writer.
           */
          public void toXML(XMLWriter generatedXML) {
              toXML(generatedXML, false);
          }
          
          
          /**
           * Get an XML representation of this lock token. This method will
           * append an XML fragment to the given XML writer.
           */
          public void toXML(XMLWriter generatedXML, boolean showToken) {
              
              generatedXML.writeElement("d", "DAV", "activelock", 
                                        XMLWriter.OPENING);
              
              generatedXML.writeElement("d", "DAV", "locktype", 
                                        XMLWriter.OPENING);
              generatedXML.writeElement("d", "DAV", type, XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "locktype", 
                                        XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "DAV", "lockscope", 
                                        XMLWriter.OPENING);
              generatedXML.writeElement("d", "DAV", scope, XMLWriter.NO_CONTENT);
              generatedXML.writeElement("d", "DAV", "lockscope", 
                                        XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "DAV", "depth", XMLWriter.OPENING);
              if (depth == INFINITY) {
                  generatedXML.writeText("Infinity");
              } else {
                  generatedXML.writeText("0");
              }
              generatedXML.writeElement("d", "DAV", "depth", XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "DAV", "owner", XMLWriter.OPENING);
              generatedXML.writeText(owner);
              generatedXML.writeElement("d", "DAV", "owner", XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "DAV", "timeout", XMLWriter.OPENING);
              long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
              generatedXML.writeText("Second-" + timeout);
              generatedXML.writeElement("d", "DAV", "timeout", XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "DAV", "locktoken", 
                                        XMLWriter.OPENING);
              if (showToken) {
                  Enumeration tokensList = tokens.elements();
                  while (tokensList.hasMoreElements()) {
                      generatedXML.writeElement("d", "DAV", "href", 
                                                XMLWriter.OPENING);
                      generatedXML.writeText("opaquelocktoken:" 
                                             + tokensList.nextElement());
                      generatedXML.writeElement("d", "DAV", "href", 
                                                XMLWriter.CLOSING);
                  }
              } else {
                  generatedXML.writeElement("d", "DAV", "href", 
                                            XMLWriter.OPENING);
                  generatedXML.writeText("opaquelocktoken:dummytoken");
                  generatedXML.writeElement("d", "DAV", "href", 
                                            XMLWriter.CLOSING);
              }
              generatedXML.writeElement("d", "DAV", "locktoken", 
                                        XMLWriter.CLOSING);
              
              generatedXML.writeElement("d", "DAV", "activelock", 
                                        XMLWriter.CLOSING);
              
          }
          
          
      }
  
  
      // --------------------------------------------------- Property Inner Class
      
      
      private class Property {
          
          public String name;
          public String value;
          public String namespace;
          public String namespaceAbbrev;
          public int status = WebdavStatus.SC_OK;
          
      }
  
  
  };
  
  
  // --------------------------------------------------------  WebdavStatus Class
  
  
  /**
   * Wraps the HttpServletResponse class to abstract the
   * specific protocol used.  To support other protocols
   * we would only need to modify this class and the
   * WebDavRetCode classes.
   *
   * @author		Marc Eaddy
   * @version		1.0, 16 Nov 1997
   */
  class WebdavStatus {
  
      
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * This Hashtable contains the mapping of HTTP and WebDAV
       * status codes to descriptive text.  This is a static
       * variable.
       */
      private static Hashtable mapStatusCodes = new Hashtable();
  
      
      // ------------------------------------------------------ HTTP Status Codes
  
  
      /**
       * Status code (200) indicating the request succeeded normally.
       */
      public static final int SC_OK = HttpServletResponse.SC_OK;
  
      
      /**
       * Status code (201) indicating the request succeeded and created
       * a new resource on the server.
       */
      public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
  
  
      /**
       * Status code (202) indicating that a request was accepted for
       * processing, but was not completed.
       */
      public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
  
  
      /**
       * Status code (204) indicating that the request succeeded but that
       * there was no new information to return.
       */
      public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
  
  
      /**
       * Status code (301) indicating that the resource has permanently
       * moved to a new location, and that future references should use a
       * new URI with their requests.
       */
      public static final int SC_MOVED_PERMANENTLY = 
          HttpServletResponse.SC_MOVED_PERMANENTLY;
  
  
      /**
       * Status code (302) indicating that the resource has temporarily
       * moved to another location, but that future references should
       * still use the original URI to access the resource.
       */
      public static final int SC_MOVED_TEMPORARILY = 
          HttpServletResponse.SC_MOVED_TEMPORARILY;
  
  
      /**
       * Status code (304) indicating that a conditional GET operation
       * found that the resource was available and not modified.
       */
      public static final int SC_NOT_MODIFIED = 
          HttpServletResponse.SC_NOT_MODIFIED;
  
  
      /**
       * Status code (400) indicating the request sent by the client was
       * syntactically incorrect.
       */
      public static final int SC_BAD_REQUEST = 
          HttpServletResponse.SC_BAD_REQUEST;
  
  
      /**
       * Status code (401) indicating that the request requires HTTP
       * authentication.
       */
      public static final int SC_UNAUTHORIZED = 
          HttpServletResponse.SC_UNAUTHORIZED;
  
  
      /**
       * Status code (403) indicating the server understood the request
       * but refused to fulfill it.
       */
      public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
  
  
      /**
       * Status code (404) indicating that the requested resource is not
       * available.
       */
      public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
  
  
      /**
       * Status code (500) indicating an error inside the HTTP service
       * which prevented it from fulfilling the request.
       */
      public static final int SC_INTERNAL_SERVER_ERROR = 
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
  
  
      /**
       * Status code (501) indicating the HTTP service does not support
       * the functionality needed to fulfill the request.
       */
      public static final int SC_NOT_IMPLEMENTED = 
          HttpServletResponse.SC_NOT_IMPLEMENTED;
  
  
      /**
       * Status code (502) indicating that the HTTP server received an
       * invalid response from a server it consulted when acting as a
       * proxy or gateway.
       */
      public static final int SC_BAD_GATEWAY = 
          HttpServletResponse.SC_BAD_GATEWAY;
  
  
      /**
       * Status code (503) indicating that the HTTP service is
       * temporarily overloaded, and unable to handle the request.
       */
      public static final int SC_SERVICE_UNAVAILABLE = 
          HttpServletResponse.SC_SERVICE_UNAVAILABLE;
  
  
      /**
       * Status code (100) indicating the client may continue with
       * its request.  This interim response is used to inform the 
       * client that the initial part of the request has been
       * received and has not yet been rejected by the server.
       */
      public static final int SC_CONTINUE = 100;
  
  
      /**
       * Status code (405) indicating the method specified is not
       * allowed for the resource.
       */
      public static final int SC_METHOD_NOT_ALLOWED = 405;
  
      
      /**
       * Status code (409) indicating that the request could not be
       * completed due to a conflict with the current state of the
       * resource.
       */
      public static final int SC_CONFLICT	= 409;
  
  
      /**
       * Status code (412) indicating the precondition given in one
       * or more of the request-header fields evaluated to false
       * when it was tested on the server.
       */
      public static final int SC_PRECONDITION_FAILED = 412;
  
  
      /**
       * Status code (413) indicating the server is refusing to
       * process a request because the request entity is larger
       * than the server is willing or able to process.
       */
      public static final int SC_REQUEST_TOO_LONG	= 413;
  
  
      /**
       * Status code (415) indicating the server is refusing to service
       * the request because the entity of the request is in a format
       * not supported by the requested resource for the requested
       * method.
       */
      public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
      
      
      // -------------------------------------------- Extended WebDav status code
  
  
      /**
       * Status code (207) indicating that the response requires
       * providing status for multiple independent operations.
       */
      public static final int SC_MULTI_STATUS = 207;
      // This one colides with HTTP 1.1
      // "207 Parital Update OK"
  
      
      /**
       * Status code (418) indicating the entity body submitted with
       * the PATCH method was not understood by the resource.
       */
      public static final int SC_UNPROCESSABLE_ENTITY = 418;
      // This one colides with HTTP 1.1
      // "418 Reauthentication Required"
  
      
      /**
       * Status code (419) indicating that the resource does not have
       * sufficient space to record the state of the resource after the
       * execution of this method.
       */
      public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
      // This one colides with HTTP 1.1
      // "419 Proxy Reauthentication Required"
  
  
      /**
       * Status code (420) indicating the method was not executed on
       * a particular resource within its scope because some part of
       * the method's execution failed causing the entire method to be
       * aborted.
       */
      public static final int SC_METHOD_FAILURE = 420;
  
      
      /**
       * Status code (423) indicating the destination resource of a
       * method is locked, and either the request did not contain a
       * valid Lock-Info header, or the Lock-Info header identifies
       * a lock held by another principal.
       */
      public static final int SC_LOCKED = 423;
  
      
      // ------------------------------------------------------------ Initializer
  
  
      static {
  	// HTTP 1.0 tatus Code
  	addStatusCodeMap(SC_OK, "OK");
  	addStatusCodeMap(SC_CREATED, "Created");
  	addStatusCodeMap(SC_ACCEPTED, "Accepted");
  	addStatusCodeMap(SC_NO_CONTENT, "No Content");
  	addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
  	addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
  	addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
  	addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
  	addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
  	addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
  	addStatusCodeMap(SC_NOT_FOUND, "Not Found");
  	addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
  	addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
  	addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
  	addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
  	addStatusCodeMap(SC_CONTINUE, "Continue");
  	addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
  	addStatusCodeMap(SC_CONFLICT, "Conflict");
  	addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
  	addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
  	addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
  	// WebDav Status Codes
  	addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
  	addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
  	addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE, 
                           "Insufficient Space On Resource");
  	addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
  	addStatusCodeMap(SC_LOCKED, "Locked");
      }
      
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Returns the HTTP status text for the HTTP or WebDav status code
       * specified by looking it up in the static mapping.  This is a
       * static function.
       * 
       * @param	nHttpStatusCode	[IN] HTTP or WebDAV status code
       * @return	A string with a short descriptive phrase for the
       *			HTTP status code (e.g., "OK").
       */
      public static String getStatusText(int nHttpStatusCode) {
  	Integer intKey = new Integer(nHttpStatusCode);
  	
  	if (!mapStatusCodes.containsKey(intKey)) {
  	    return "";
  	} else {
  	    return (String) mapStatusCodes.get(intKey);
  	}
      }
  
      
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Adds a new status code -> status text mapping.  This is a static
       * method because the mapping is a static variable.
       * 
       * @param	nKey	[IN] HTTP or WebDAV status code
       * @param	strVal	[IN] HTTP status text
       */
      private static void addStatusCodeMap(int nKey, String strVal) {
  	mapStatusCodes.put(new Integer(nKey), strVal);
      }
  
      
  };
  
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/package.html
  
  Index: package.html
  ===================================================================
  <body>
  
  <p>This package contains <code>Servlets</code> that implement some of the
  standard functionality provided by the Catalina servlet container.  Because
  these servlets are in the <code>org.apache.catalina</code> package hierarchy,
  they are in the privileged position of being able to reference internal server
  data structures, which application level servlets are prevented from
  accessing (by the application class loader implementation).</p>
  
  <p>To the extent that these servlets depend upon internal Catalina data
  structures, they are obviously not portable to other servlet container
  environments.  However, they can be used as models for creating application
  level servlets that provide similar capabilities -- most obviously the
  <a href="DefaultServlet.html">DefaultServlet</a> implementation, which
  serves static resources when Catalina runs stand-alone.</p>
  
  </body>