You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@locus.apache.org on 2000/07/31 08:28:03 UTC

cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util XMLWriter.java

remm        00/07/30 23:28:02

  Modified:    proposals/catalina/src/share/org/apache/tomcat/resources
                        FileResources.java
               proposals/catalina/src/share/org/apache/tomcat/servlets
                        DefaultServlet.java LocalStrings.properties
  Added:       proposals/catalina/src/share/org/apache/tomcat/servlets
                        WebdavServlet.java
               proposals/catalina/src/share/org/apache/tomcat/util
                        XMLWriter.java
  Log:
  - New WebdavServlet class in servlets package. Use it instead
    of DefaultServlet to get DAV functionality.
  - Adds limited read only WebDAV support. Only PROPFIND is
    supported right now. I tested with IE 5 and it works :-)
  - More DAV support coming soon. Full DAV level 2 support is planned.
  - The new WebdavServlet is an extension of DefaultServlet. It is
    based on code from the Jakarta Slide project.
  - Fix to the file implementation of
    Resources.getCollectionMembers(). The path needs to be translated a bit.
  - Add an XML elements generation utility class (XMLWriter), which
    was also taken from Slide.
  
  Revision  Changes    Path
  1.4       +15 -7     jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/resources/FileResources.java
  
  Index: FileResources.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/resources/FileResources.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- FileResources.java	2000/07/30 23:20:22	1.3
  +++ FileResources.java	2000/07/31 06:27:59	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/resources/FileResources.java,v 1.3 2000/07/30 23:20:22 remm Exp $
  - * $Revision: 1.3 $
  - * $Date: 2000/07/30 23:20:22 $
  + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/resources/FileResources.java,v 1.4 2000/07/31 06:27:59 remm Exp $
  + * $Revision: 1.4 $
  + * $Date: 2000/07/31 06:27:59 $
    *
    * ====================================================================
    *
  @@ -89,7 +89,7 @@
    * since they were cached.
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.3 $ $Date: 2000/07/30 23:20:22 $
  + * @version $Revision: 1.4 $ $Date: 2000/07/31 06:27:59 $
    */
   
   public final class FileResources extends ResourcesBase {
  @@ -469,10 +469,18 @@
   	validate(path);
   
   	File file = file(normalize(path));
  -	if (file != null)
  -	    return (file.list());
  -	else
  +	if (file != null) {
  +            String normalized = normalize(path);
  +            if (normalized == null)
  +                return (null);
  +	    String[] dirList = file.list();
  +            for (int i=0; i<dirList.length; i++) {
  +                dirList[i] = normalize(normalized + "/" + dirList[i]);
  +            }
  +            return dirList;
  +        } else {
   	    return (null);
  +        }
   
       }
   
  
  
  
  1.15      +198 -219  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/DefaultServlet.java
  
  Index: DefaultServlet.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/DefaultServlet.java,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- DefaultServlet.java	2000/07/30 23:23:34	1.14
  +++ DefaultServlet.java	2000/07/31 06:28:00	1.15
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/DefaultServlet.java,v 1.14 2000/07/30 23:23:34 remm Exp $
  - * $Revision: 1.14 $
  - * $Date: 2000/07/30 23:23:34 $
  + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/DefaultServlet.java,v 1.15 2000/07/31 06:28:00 remm Exp $
  + * $Revision: 1.15 $
  + * $Date: 2000/07/31 06:28:00 $
    *
    * ====================================================================
    *
  @@ -108,77 +108,62 @@
    *
    * @author Craig R. McClanahan
    * @author Remy Maucherat
  - * @version $Revision: 1.14 $ $Date: 2000/07/30 23:23:34 $
  + * @version $Revision: 1.15 $ $Date: 2000/07/31 06:28:00 $
    */
   
  -public final class DefaultServlet
  +public class DefaultServlet
       extends HttpServlet {
   
   
  -    // -------------------------------------------------------------- Constants
  -
  -
  -    /**
  -     * Maximum file size for which content is cached. 32 ko by default.
  -     */
  -    public static final long MAX_SIZE_CACHE = 32768;
  -
  -
  -    /**
  -     * Cache entry timeout. 10s by default.
  -     */
  -    public static final long CACHE_ENTRY_TIMEOUT = 10000;
  -
  -
       // ----------------------------------------------------- Instance Variables
   
   
       /**
        * The debugging detail level for this servlet.
        */
  -    private int debug = 0;
  +    protected int debug = 0;
   
   
       /**
        * The input buffer size to use when serving resources.
        */
  -    private int input = 2048;
  +    protected int input = 2048;
   
   
       /**
        * Should we generate directory listings when no welcome file is present?
        */
  -    private boolean listings = true;
  +    protected boolean listings = true;
   
   
       /**
        * The output buffer size to use when serving resources.
        */
  -    private int output = 2048;
  +    protected int output = 2048;
   
   
       /**
        * The set of welcome files for this web application
        */
  -    private String welcomes[] = new String[0];
  +    protected String welcomes[] = new String[0];
   
   
       /**
        * MD5 message digest provider.
        */
  -    private static MessageDigest md5Helper;
  +    protected static MessageDigest md5Helper;
   
   
       /**
        * The MD5 helper object for this class.
        */
  -    private static final MD5Encoder md5Encoder = new MD5Encoder();
  +    protected static final MD5Encoder md5Encoder = new MD5Encoder();
   
   
       /**
        * The set of SimpleDateFormat formats to use in getDateHeader().
        */
  -    private static final SimpleDateFormat formats[] = {
  +    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)
  @@ -188,19 +173,13 @@
       /**
        * MIME multipart separation string
        */
  -    private static final String mimeSeparation = "THIS_STRING_SEPARATES";
  +    protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
   
   
       /**
  -     * File descriptor cache
  -     */
  -    private Hashtable fileCache = new Hashtable();
  -
  -
  -    /**
        * The string manager for this package.
        */
  -    private static StringManager sm =
  +    protected static StringManager sm =
   	StringManager.getManager(Constants.Package);
   
   
  @@ -337,6 +316,188 @@
   
   
   
  +    // ------------------------------------------------------ Protected Methods
  +
  +
  +    /**
  +     * 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);
  +                    System.out.println("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
   
   
  @@ -474,9 +635,6 @@
               exception = copyRange(istream, ostream, currentRange.start,
                                     currentRange.end);
               
  -            //ostream.println();
  -            //ostream.println();
  -            
               try {
                   istream.close();
               } catch (Throwable t) {
  @@ -819,185 +977,6 @@
   
   
       /**
  -     * 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
  -     */
  -    private 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);
  -                    System.out.println("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
  -     */
  -    private 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
  -     */
  -    private String getETag(ResourceInfo resourceInfo, boolean strong) {
  -        if (strong)
  -            return "\"" + getETagValue(resourceInfo, strong) + "\"";
  -        else
  -            return "W/\"" + getETagValue(resourceInfo, strong) + "\"";
  -    }
  -
  -
  -    /**
        * Parse the range header.
        * 
        * @param request The servlet request we are processing
  @@ -1155,7 +1134,7 @@
       // ----------------------------------------------  ResourceInfo Inner Class
   
   
  -    private class ResourceInfo {
  +    protected class ResourceInfo {
   
   
           /**
  
  
  
  1.2       +1 -0      jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/LocalStrings.properties,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- LocalStrings.properties	2000/06/24 19:48:56	1.1
  +++ LocalStrings.properties	2000/07/31 06:28:01	1.2
  @@ -2,3 +2,4 @@
   defaultservlet.upto=Up to:
   defaultservlet.subdirectories=Subdirectories:
   defaultservlet.files=Files:
  +webdavservlet.jaxpfailed=JAXP initialization failed
  
  
  
  1.1                  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/WebdavServlet.java
  
  Index: WebdavServlet.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/servlets/WebdavServlet.java,v 1.1 2000/07/31 06:28:01 remm Exp $
   * $Revision: 1.1 $
   * $Date: 2000/07/31 06:28:01 $
   *
   * ====================================================================
   *
   * 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.tomcat.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.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.tomcat.Resources;
  import org.apache.tomcat.core.ApplicationContext;
  import org.apache.tomcat.resources.ResourceBean;
  import org.apache.tomcat.resources.DirectoryBean;
  import org.apache.tomcat.util.MD5Encoder;
  import org.apache.tomcat.util.StringManager;
  import org.apache.tomcat.util.XMLWriter;
  import org.apache.tomcat.util.xml.SaxContext;
  import org.apache.tomcat.util.xml.XmlAction;
  import org.apache.tomcat.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/07/31 06:28:01 $
   */
  
  public final class WebdavServlet
      extends DefaultServlet {
  
  
      // -------------------------------------------------------------- Constants
  
  
      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;
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * JAXP Document builder.
       */
      private DocumentBuilder documentBuilder;
  
  
      /**
       * Read only flag. By default, it's set to true.
       */
      private boolean readOnly = true;
  
  
      /**
       * Lock token repository.
       */
      private Hashtable lockStore = new Hashtable();
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Initialize this servlet.
       */
      public void init() 
          throws ServletException {
  
          super.init();
  
          try {
              documentBuilder = 
                  DocumentBuilderFactory.newInstance().newDocumentBuilder();
          } catch(ParserConfigurationException e) {
              throw new ServletException
                  (sm.getString("webdavservlet.jaxpfailed"));
          }
  
  	// Set our properties from the initialization parameters
  	String value = null;
  	try {
  	    value = getServletConfig().getInitParameter("readonly");
  	    readOnly = (new Boolean(value)).booleanValue();
  	} catch (Throwable t) {
  	    ;
  	}
  
      }
  
  
      // ------------------------------------------------------ Protected Methods
  
  
      /**
       * Handles the special WebDAV methods.
       */
      protected void service(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
  	String method = req.getMethod();
          
  	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 {
              // DefailtServlet 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 {
          
  	resp.addHeader("DAV", "1,2");
          String methodsAllowed = "OPTIONS, GET, HEAD, POST, DELETE, TRACE, " 
              + "PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK, 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 servletPath = req.getServletPath();
  	if (servletPath == null)
  	    servletPath = "/";
          
          // 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();
  
  	// Convert the resource path to a URL
  	URL resourceURL = null;
  	try {
  	    resourceURL = context.getResource(servletPath);
  	} catch (MalformedURLException e) {
  	    ;
  	}
  	if (resourceURL == null) {
  	    resp.sendError(HttpServletResponse.SC_NOT_FOUND, servletPath);
  	    return;
  	}
  
          resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
  
          Resources resources = context.getResources();
          
          // Create multistatus object
          XMLWriter generatedXML = new XMLWriter();
          
          generatedXML.writeElement("d", "DAV", "multistatus", 
                                    XMLWriter.OPENING);
          
          if (depth == 0) {
              parseProperties(resources, generatedXML, servletPath, type, 
                              properties);
          } else {
              // The stack always contains the object of the current level
              Stack stack = new Stack();
              stack.push(servletPath);
              
              // Stack of the objects one level below
              Stack stackBelow = new Stack();
              
              while ((!stack.isEmpty()) && (depth >= 0)) {
                  
                  String currentPath = (String) stack.pop();
                  parseProperties(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 (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 {
  
          
  
      }
  
  
      /**
       * MKCOL Method.
       */
      protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      /**
       * DELETE Method.
       */
      protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      /**
       * PUT Method.
       */
      protected void doPut(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      /**
       * COPY Method.
       */
      protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      /**
       * MOVE Method.
       */
      protected void doMove(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      /**
       * LOCK Method.
       */
      protected void doLock(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      /**
       * UNLOCK Method.
       */
      protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
  	throws ServletException, IOException {
  
          
  
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * 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(Resources resources, XMLWriter generatedXML,
                                   String path, int type, 
                                   Vector propertiesVector) {
          
          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);
          generatedXML.writeText(path);
          if ((resourceInfo.collection) && (!path.equals("/")))
              generatedXML.writeText("/");
          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>";
              generatedXML.writeProperty("d", "DAV", "supportedlock", 
                                         supportedLocks);
              
              // TODO : Lock discovery property
              
              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>";
                      generatedXML.writeProperty("d", "DAV", "supportedlock",
                                                 supportedLocks);
                  } else if (property.equals("lockdiscovery")) {
                      // TODO : Implement locks !!!
                      generatedXML.writeProperty("d", "DAV", 
                                                 "lockdiscovery", "");
                  } 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);
          
      }
  
  
      // --------------------------------------------------  LockInfo Inner Class
  
  
      private class LockInfo {
  
  
          /**
           * Constructor.
           * 
           * @param pathname Path name of the file
           */
          public LockInfo() {
  
              
  
          }
  
  
  
  
      }
  
  };
  
  
  // --------------------------------------------------------  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/proposals/catalina/src/share/org/apache/tomcat/util/XMLWriter.java
  
  Index: XMLWriter.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util/XMLWriter.java,v 1.1 2000/07/31 06:28:02 remm Exp $
   * $Revision: 1.1 $
   * $Date: 2000/07/31 06:28:02 $
   *
   * ====================================================================
   *
   * 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.tomcat.util;
  
  /**
   * XMLWriter helper class.
   * 
   * @author Remy Maucherat
   */
  public class XMLWriter {
      
      
      // -------------------------------------------------------------- Constants
      
      
      /**
       * Opening tag.
       */
      public static final int OPENING = 0;
      
      
      /**
       * Closing tag.
       */
      public static final int CLOSING = 1;
      
      
      /**
       * Element with no content.
       */
      public static final int NO_CONTENT = 2;
      
      
      // ----------------------------------------------------- Instance Variables
      
      
      /**
       * Buffer.
       */
      protected StringBuffer buffer;
      
      
      // ----------------------------------------------------------- Constructors
      
      
      /**
       * Constructor.
       */
      public XMLWriter() {
          buffer = new StringBuffer();
      }
      
      
      // --------------------------------------------------------- Public Methods
      
      
      /**
       * Retrieve generated XML.
       * 
       * @return String containing the generated XML
       */
      public String toString() {
          return buffer.toString();
      }
      
      
      /**
       * Write property to the XML.
       * 
       * @param namespace Namespace
       * @param namespaceInfo Namespace info
       * @param name Property name
       * @param value Property value
       */
      public void writeProperty(String namespace, String namespaceInfo, 
                                String name, String value) {
          writeElement(namespace, namespaceInfo, name, OPENING);
          buffer.append(value);
          writeElement(namespace, namespaceInfo, name, CLOSING);
      }
      
      
      /**
       * Write property to the XML.
       * 
       * @param namespace Namespace
       * @param name Property name
       * @param value Property value
       */
      public void writeProperty(String namespace, String name, String value) {
          writeElement(namespace, name, OPENING);
          buffer.append(value);
          writeElement(namespace, name, CLOSING);
      }
      
      
      /**
       * Write property to the XML.
       * 
       * @param namespace Namespace
       * @param name Property name
       */
      public void writeProperty(String namespace, String name) {
          writeElement(namespace, name, NO_CONTENT);
      }
      
      
      /**
       * Write an element.
       * 
       * @param name Element name
       * @param namespace Namespace abbreviation
       * @param type Element type
       */
      public void writeElement(String namespace, String name, int type) {
          writeElement(namespace, null, name, type);
      }
      
      
      /**
       * Write an element.
       * 
       * @param namespace Namespace abbreviation
       * @param namespaceInfo Namespace info
       * @param name Element name
       * @param type Element type
       */
      public void writeElement(String namespace, String namespaceInfo, 
                               String name, int type) {
          if ((namespace != null) && (namespace.length() > 0)) {
              switch (type) {
              case OPENING:
                  if (namespaceInfo != null) {
                      buffer.append("<" + namespace + ":" + name + " xmlns:" 
                                    + namespace + "=\"" 
                                    + namespaceInfo + ":\">");
                  } else {
                      buffer.append("<" + namespace + ":" + name + ">");
                  }
                  break;
              case CLOSING:
                  buffer.append("</" + namespace + ":" + name + ">\n");
                  break;
              case NO_CONTENT:
              default:
                  if (namespaceInfo != null) {
                      buffer.append("<" + namespace + ":" + name + " xmlns:" 
                                    + namespace + "=\"" 
                                    + namespaceInfo + ":\"/>");
                  } else {
                      buffer.append("<" + namespace + ":" + name + "/>");
                  }
                  break;
              }
          } else {
              switch (type) {
              case OPENING:
                  buffer.append("<" + name + ">");
                  break;
              case CLOSING:
                  buffer.append("</" + name + ">\n");
                  break;
              case NO_CONTENT:
              default:
                  buffer.append("<" + name + "/>");
                  break;
              }
          }
      }
      
      
      /**
       * Write text.
       * 
       * @param text Text to append
       */
      public void writeText(String text) {
          buffer.append(text);
      }
      
      
      /**
       * Write XML Header.
       */
      public void writeXMLHeader() {
          buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
      }
      
      
  }