Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » servlets » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.servlets;
   20   
   21   
   22   import java.io.IOException;
   23   import java.io.StringReader;
   24   import java.io.StringWriter;
   25   import java.io.Writer;
   26   import java.security.MessageDigest;
   27   import java.security.NoSuchAlgorithmException;
   28   import java.text.SimpleDateFormat;
   29   import java.util.Date;
   30   import java.util.Enumeration;
   31   import java.util.Hashtable;
   32   import java.util.Stack;
   33   import java.util.TimeZone;
   34   import java.util.Vector;
   35   
   36   import javax.naming.NameClassPair;
   37   import javax.naming.NamingEnumeration;
   38   import javax.naming.NamingException;
   39   import javax.naming.directory.DirContext;
   40   import javax.servlet.ServletContext;
   41   import javax.servlet.ServletException;
   42   import javax.servlet.UnavailableException;
   43   import javax.servlet.http.HttpServletRequest;
   44   import javax.servlet.http.HttpServletResponse;
   45   import javax.xml.parsers.DocumentBuilder;
   46   import javax.xml.parsers.DocumentBuilderFactory;
   47   import javax.xml.parsers.ParserConfigurationException;
   48   
   49   import org.apache.catalina.Globals;
   50   import org.apache.catalina.util.DOMWriter;
   51   import org.apache.catalina.util.MD5Encoder;
   52   import org.apache.catalina.util.RequestUtil;
   53   import org.apache.catalina.util.XMLWriter;
   54   import org.apache.naming.resources.CacheEntry;
   55   import org.apache.naming.resources.Resource;
   56   import org.apache.naming.resources.ResourceAttributes;
   57   import org.apache.tomcat.util.http.FastHttpDateFormat;
   58   import org.w3c.dom.Document;
   59   import org.w3c.dom.Element;
   60   import org.w3c.dom.Node;
   61   import org.w3c.dom.NodeList;
   62   import org.xml.sax.EntityResolver;
   63   import org.xml.sax.InputSource;
   64   import org.xml.sax.SAXException;
   65   
   66   
   67   
   68   /**
   69    * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
   70    * are handled by the DefaultServlet. The WebDAVServlet must not be used as the
   71    * default servlet (ie mapped to '/') as it will not work in this configuration.
   72    * To enable WebDAV for a context add the following to web.xml:<br/><code>
   73    * &lt;servlet&gt;<br/>
   74    *  &lt;servlet-name&gt;webdav&lt;/servlet-name&gt;<br/>
   75    *  &lt;servlet-class&gt;org.apache.catalina.servlets.WebdavServlet&lt;/servlet-class&gt;<br/>
   76    *    &lt;init-param&gt;<br/>
   77    *      &lt;param-name&gt;debug&lt;/param-name&gt;<br/>
   78    *      &lt;param-value&gt;0&lt;/param-value&gt;<br/>
   79    *    &lt;/init-param&gt;<br/>
   80    *    &lt;init-param&gt;<br/>
   81    *      &lt;param-name&gt;listings&lt;/param-name&gt;<br/>
   82    *      &lt;param-value&gt;true&lt;/param-value&gt;<br/>
   83    *    &lt;/init-param&gt;<br/>
   84    *  &lt;/servlet&gt;<br/>
   85    *  &lt;servlet-mapping&gt;<br/>
   86    *    &lt;servlet-name&gt;webdav&lt;/servlet-name&gt;<br/>
   87    *    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br/>
   88    *  &lt;/servlet-mapping&gt;
   89    * </code>
   90    * <p/>
   91    * This will enable read only access. To enable read-write access add:<br/>
   92    * <code>
   93    *    &lt;init-param&gt;<br/>
   94    *      &lt;param-name&gt;readonly&lt;/param-name&gt;<br/>
   95    *      &lt;param-value&gt;false&lt;/param-value&gt;<br/>
   96    *    &lt;/init-param&gt;<br/>
   97    * </code>
   98    * <p/>
   99    * To make the content editable via a different URL, using the following
  100    * mapping:<br/>
  101    * <code>
  102    *  &lt;servlet-mapping&gt;<br/>
  103    *    &lt;servlet-name&gt;webdav&lt;/servlet-name&gt;<br/>
  104    *    &lt;url-pattern&gt;/webdavedit/*&lt;/url-pattern&gt;<br/>
  105    *  &lt;/servlet-mapping&gt;
  106    * </code>
  107    * <p/>
  108    * Don't forget to secure access appropriately to the editing URLs. With this
  109    * configuration the context will be accessible to normal users as before. Those
  110    * users with the necessary access will be able to edit content available via
  111    * http://host:port/context/content using
  112    * http://host:port/context/webdavedit/content
  113    *
  114    * @author Remy Maucherat
  115    * @version $Revision: 600268 $ $Date: 2007-12-02 12:09:55 +0100 (dim., 02 déc. 2007) $
  116    */
  117   
  118   public class WebdavServlet
  119       extends DefaultServlet {
  120   
  121   
  122       // -------------------------------------------------------------- Constants
  123   
  124   
  125       private static final String METHOD_HEAD = "HEAD";
  126       private static final String METHOD_PROPFIND = "PROPFIND";
  127       private static final String METHOD_PROPPATCH = "PROPPATCH";
  128       private static final String METHOD_MKCOL = "MKCOL";
  129       private static final String METHOD_COPY = "COPY";
  130       private static final String METHOD_MOVE = "MOVE";
  131       private static final String METHOD_LOCK = "LOCK";
  132       private static final String METHOD_UNLOCK = "UNLOCK";
  133   
  134   
  135       /**
  136        * Default depth is infite.
  137        */
  138       private static final int INFINITY = 3; // To limit tree browsing a bit
  139   
  140   
  141       /**
  142        * PROPFIND - Specify a property mask.
  143        */
  144       private static final int FIND_BY_PROPERTY = 0;
  145   
  146   
  147       /**
  148        * PROPFIND - Display all properties.
  149        */
  150       private static final int FIND_ALL_PROP = 1;
  151   
  152   
  153       /**
  154        * PROPFIND - Return property names.
  155        */
  156       private static final int FIND_PROPERTY_NAMES = 2;
  157   
  158   
  159       /**
  160        * Create a new lock.
  161        */
  162       private static final int LOCK_CREATION = 0;
  163   
  164   
  165       /**
  166        * Refresh lock.
  167        */
  168       private static final int LOCK_REFRESH = 1;
  169   
  170   
  171       /**
  172        * Default lock timeout value.
  173        */
  174       private static final int DEFAULT_TIMEOUT = 3600;
  175   
  176   
  177       /**
  178        * Maximum lock timeout.
  179        */
  180       private static final int MAX_TIMEOUT = 604800;
  181   
  182   
  183       /**
  184        * Default namespace.
  185        */
  186       protected static final String DEFAULT_NAMESPACE = "DAV:";
  187   
  188   
  189       /**
  190        * Simple date format for the creation date ISO representation (partial).
  191        */
  192       protected static final SimpleDateFormat creationDateFormat =
  193           new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
  194   
  195   
  196        /**
  197        * MD5 message digest provider.
  198        */
  199       protected static MessageDigest md5Helper;
  200   
  201   
  202       /**
  203        * The MD5 helper object for this class.
  204        */
  205       protected static final MD5Encoder md5Encoder = new MD5Encoder();
  206   
  207   
  208   
  209       static {
  210           creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
  211       }
  212   
  213   
  214       // ----------------------------------------------------- Instance Variables
  215   
  216   
  217       /**
  218        * Repository of the locks put on single resources.
  219        * <p>
  220        * Key : path <br>
  221        * Value : LockInfo
  222        */
  223       private Hashtable<String,LockInfo> resourceLocks =
  224           new Hashtable<String,LockInfo>();
  225   
  226   
  227       /**
  228        * Repository of the lock-null resources.
  229        * <p>
  230        * Key : path of the collection containing the lock-null resource<br>
  231        * Value : Vector of lock-null resource which are members of the
  232        * collection. Each element of the Vector is the path associated with
  233        * the lock-null resource.
  234        */
  235       private Hashtable<String,Vector<String>> lockNullResources =
  236           new Hashtable<String,Vector<String>>();
  237   
  238   
  239       /**
  240        * Vector of the heritable locks.
  241        * <p>
  242        * Key : path <br>
  243        * Value : LockInfo
  244        */
  245       private Vector<LockInfo> collectionLocks = new Vector<LockInfo>();
  246   
  247   
  248       /**
  249        * Secret information used to generate reasonably secure lock ids.
  250        */
  251       private String secret = "catalina";
  252   
  253   
  254       // --------------------------------------------------------- Public Methods
  255   
  256   
  257       /**
  258        * Initialize this servlet.
  259        */
  260       public void init()
  261           throws ServletException {
  262   
  263           super.init();
  264   
  265           if (getServletConfig().getInitParameter("secret") != null)
  266               secret = getServletConfig().getInitParameter("secret");
  267   
  268           // Load the MD5 helper used to calculate signatures.
  269           try {
  270               md5Helper = MessageDigest.getInstance("MD5");
  271           } catch (NoSuchAlgorithmException e) {
  272               throw new UnavailableException("No MD5");
  273           }
  274   
  275       }
  276   
  277   
  278       // ------------------------------------------------------ Protected Methods
  279   
  280   
  281       /**
  282        * Return JAXP document builder instance.
  283        */
  284       protected DocumentBuilder getDocumentBuilder()
  285           throws ServletException {
  286           DocumentBuilder documentBuilder = null;
  287           DocumentBuilderFactory documentBuilderFactory = null;
  288           try {
  289               documentBuilderFactory = DocumentBuilderFactory.newInstance();
  290               documentBuilderFactory.setNamespaceAware(true);
  291               documentBuilderFactory.setExpandEntityReferences(false);
  292               documentBuilder = documentBuilderFactory.newDocumentBuilder();
  293               documentBuilder.setEntityResolver(
  294                       new WebdavResolver(this.getServletContext()));
  295           } catch(ParserConfigurationException e) {
  296               throw new ServletException
  297                   (sm.getString("webdavservlet.jaxpfailed"));
  298           }
  299           return documentBuilder;
  300       }
  301   
  302   
  303       /**
  304        * Handles the special WebDAV methods.
  305        */
  306       protected void service(HttpServletRequest req, HttpServletResponse resp)
  307           throws ServletException, IOException {
  308   
  309           String method = req.getMethod();
  310   
  311           if (debug > 0) {
  312               String path = getRelativePath(req);
  313               log("[" + method + "] " + path);
  314           }
  315   
  316           if (method.equals(METHOD_PROPFIND)) {
  317               doPropfind(req, resp);
  318           } else if (method.equals(METHOD_PROPPATCH)) {
  319               doProppatch(req, resp);
  320           } else if (method.equals(METHOD_MKCOL)) {
  321               doMkcol(req, resp);
  322           } else if (method.equals(METHOD_COPY)) {
  323               doCopy(req, resp);
  324           } else if (method.equals(METHOD_MOVE)) {
  325               doMove(req, resp);
  326           } else if (method.equals(METHOD_LOCK)) {
  327               doLock(req, resp);
  328           } else if (method.equals(METHOD_UNLOCK)) {
  329               doUnlock(req, resp);
  330           } else {
  331               // DefaultServlet processing
  332               super.service(req, resp);
  333           }
  334   
  335       }
  336   
  337   
  338       /**
  339        * Check if the conditions specified in the optional If headers are
  340        * satisfied.
  341        *
  342        * @param request The servlet request we are processing
  343        * @param response The servlet response we are creating
  344        * @param resourceAttributes The resource information
  345        * @return boolean true if the resource meets all the specified conditions,
  346        * and false if any of the conditions is not satisfied, in which case
  347        * request processing is stopped
  348        */
  349       protected boolean checkIfHeaders(HttpServletRequest request,
  350                                        HttpServletResponse response,
  351                                        ResourceAttributes resourceAttributes)
  352           throws IOException {
  353   
  354           if (!super.checkIfHeaders(request, response, resourceAttributes))
  355               return false;
  356   
  357           // TODO : Checking the WebDAV If header
  358           return true;
  359   
  360       }
  361   
  362   
  363       /**
  364        * Override the DefaultServlet implementation and only use the PathInfo. If
  365        * the ServletPath is non-null, it will be because the WebDAV servlet has
  366        * been mapped to a url other than /* to configure editing at different url
  367        * than normal viewing.
  368        *
  369        * @param request The servlet request we are processing
  370        */
  371       protected String getRelativePath(HttpServletRequest request) {
  372           // Are we being processed by a RequestDispatcher.include()?
  373           if (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) {
  374               String result = (String) request.getAttribute(
  375                                               Globals.INCLUDE_PATH_INFO_ATTR);
  376               if ((result == null) || (result.equals("")))
  377                   result = "/";
  378               return (result);
  379           }
  380   
  381           // No, extract the desired path directly from the request
  382           String result = request.getPathInfo();
  383           if ((result == null) || (result.equals(""))) {
  384               result = "/";
  385           }
  386           return (result);
  387   
  388       }
  389   
  390   
  391       /**
  392        * OPTIONS Method.
  393        *
  394        * @param req The request
  395        * @param resp The response
  396        * @throws ServletException If an error occurs
  397        * @throws IOException If an IO error occurs
  398        */
  399       protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
  400           throws ServletException, IOException {
  401   
  402           resp.addHeader("DAV", "1,2");
  403   
  404           StringBuffer methodsAllowed = determineMethodsAllowed(resources,
  405                                                                 req);
  406   
  407           resp.addHeader("Allow", methodsAllowed.toString());
  408           resp.addHeader("MS-Author-Via", "DAV");
  409   
  410       }
  411   
  412   
  413       /**
  414        * PROPFIND Method.
  415        */
  416       protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
  417           throws ServletException, IOException {
  418   
  419           if (!listings) {
  420               // Get allowed methods
  421               StringBuffer methodsAllowed = determineMethodsAllowed(resources,
  422                                                                     req);
  423   
  424               resp.addHeader("Allow", methodsAllowed.toString());
  425               resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
  426               return;
  427           }
  428   
  429           String path = getRelativePath(req);
  430           if (path.endsWith("/"))
  431               path = path.substring(0, path.length() - 1);
  432   
  433           if ((path.toUpperCase().startsWith("/WEB-INF")) ||
  434               (path.toUpperCase().startsWith("/META-INF"))) {
  435               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  436               return;
  437           }
  438   
  439           // Properties which are to be displayed.
  440           Vector<String> properties = null;
  441           // Propfind depth
  442           int depth = INFINITY;
  443           // Propfind type
  444           int type = FIND_ALL_PROP;
  445   
  446           String depthStr = req.getHeader("Depth");
  447   
  448           if (depthStr == null) {
  449               depth = INFINITY;
  450           } else {
  451               if (depthStr.equals("0")) {
  452                   depth = 0;
  453               } else if (depthStr.equals("1")) {
  454                   depth = 1;
  455               } else if (depthStr.equals("infinity")) {
  456                   depth = INFINITY;
  457               }
  458           }
  459   
  460           Node propNode = null;
  461           
  462           if (req.getInputStream().available() > 0) {
  463               DocumentBuilder documentBuilder = getDocumentBuilder();
  464       
  465               try {
  466                   Document document = documentBuilder.parse
  467                       (new InputSource(req.getInputStream()));
  468       
  469                   // Get the root element of the document
  470                   Element rootElement = document.getDocumentElement();
  471                   NodeList childList = rootElement.getChildNodes();
  472       
  473                   for (int i=0; i < childList.getLength(); i++) {
  474                       Node currentNode = childList.item(i);
  475                       switch (currentNode.getNodeType()) {
  476                       case Node.TEXT_NODE:
  477                           break;
  478                       case Node.ELEMENT_NODE:
  479                           if (currentNode.getNodeName().endsWith("prop")) {
  480                               type = FIND_BY_PROPERTY;
  481                               propNode = currentNode;
  482                           }
  483                           if (currentNode.getNodeName().endsWith("propname")) {
  484                               type = FIND_PROPERTY_NAMES;
  485                           }
  486                           if (currentNode.getNodeName().endsWith("allprop")) {
  487                               type = FIND_ALL_PROP;
  488                           }
  489                           break;
  490                       }
  491                   }
  492               } catch (SAXException e) {
  493                   // Something went wrong - use the defaults.
  494               } catch (IOException e) {
  495                   // Something went wrong - use the defaults.
  496               }
  497           }
  498   
  499           if (type == FIND_BY_PROPERTY) {
  500               properties = new Vector<String>();
  501               NodeList childList = propNode.getChildNodes();
  502   
  503               for (int i=0; i < childList.getLength(); i++) {
  504                   Node currentNode = childList.item(i);
  505                   switch (currentNode.getNodeType()) {
  506                   case Node.TEXT_NODE:
  507                       break;
  508                   case Node.ELEMENT_NODE:
  509                       String nodeName = currentNode.getNodeName();
  510                       String propertyName = null;
  511                       if (nodeName.indexOf(':') != -1) {
  512                           propertyName = nodeName.substring
  513                               (nodeName.indexOf(':') + 1);
  514                       } else {
  515                           propertyName = nodeName;
  516                       }
  517                       // href is a live property which is handled differently
  518                       properties.addElement(propertyName);
  519                       break;
  520                   }
  521               }
  522   
  523           }
  524   
  525           boolean exists = true;
  526           Object object = null;
  527           try {
  528               object = resources.lookup(path);
  529           } catch (NamingException e) {
  530               exists = false;
  531               int slash = path.lastIndexOf('/');
  532               if (slash != -1) {
  533                   String parentPath = path.substring(0, slash);
  534                   Vector currentLockNullResources =
  535                       (Vector) lockNullResources.get(parentPath);
  536                   if (currentLockNullResources != null) {
  537                       Enumeration lockNullResourcesList =
  538                           currentLockNullResources.elements();
  539                       while (lockNullResourcesList.hasMoreElements()) {
  540                           String lockNullPath = (String)
  541                               lockNullResourcesList.nextElement();
  542                           if (lockNullPath.equals(path)) {
  543                               resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
  544                               resp.setContentType("text/xml; charset=UTF-8");
  545                               // Create multistatus object
  546                               XMLWriter generatedXML =
  547                                   new XMLWriter(resp.getWriter());
  548                               generatedXML.writeXMLHeader();
  549                               generatedXML.writeElement
  550                                   (null, "multistatus"
  551                                    + generateNamespaceDeclarations(),
  552                                    XMLWriter.OPENING);
  553                               parseLockNullProperties
  554                                   (req, generatedXML, lockNullPath, type,
  555                                    properties);
  556                               generatedXML.writeElement(null, "multistatus",
  557                                                         XMLWriter.CLOSING);
  558                               generatedXML.sendData();
  559                               return;
  560                           }
  561                       }
  562                   }
  563               }
  564           }
  565   
  566           if (!exists) {
  567               resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
  568               return;
  569           }
  570   
  571           resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
  572   
  573           resp.setContentType("text/xml; charset=UTF-8");
  574   
  575           // Create multistatus object
  576           XMLWriter generatedXML = new XMLWriter(resp.getWriter());
  577           generatedXML.writeXMLHeader();
  578   
  579           generatedXML.writeElement(null, "multistatus"
  580                                     + generateNamespaceDeclarations(),
  581                                     XMLWriter.OPENING);
  582   
  583           if (depth == 0) {
  584               parseProperties(req, generatedXML, path, type,
  585                               properties);
  586           } else {
  587               // The stack always contains the object of the current level
  588               Stack<String> stack = new Stack<String>();
  589               stack.push(path);
  590   
  591               // Stack of the objects one level below
  592               Stack<String> stackBelow = new Stack<String>();
  593   
  594               while ((!stack.isEmpty()) && (depth >= 0)) {
  595   
  596                   String currentPath = (String) stack.pop();
  597                   parseProperties(req, generatedXML, currentPath,
  598                                   type, properties);
  599   
  600                   try {
  601                       object = resources.lookup(currentPath);
  602                   } catch (NamingException e) {
  603                       continue;
  604                   }
  605   
  606                   if ((object instanceof DirContext) && (depth > 0)) {
  607   
  608                       try {
  609                           NamingEnumeration enumeration = resources.list(currentPath);
  610                           while (enumeration.hasMoreElements()) {
  611                               NameClassPair ncPair =
  612                                   (NameClassPair) enumeration.nextElement();
  613                               String newPath = currentPath;
  614                               if (!(newPath.endsWith("/")))
  615                                   newPath += "/";
  616                               newPath += ncPair.getName();
  617                               stackBelow.push(newPath);
  618                           }
  619                       } catch (NamingException e) {
  620                           resp.sendError
  621                               (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  622                                path);
  623                           return;
  624                       }
  625   
  626                       // Displaying the lock-null resources present in that
  627                       // collection
  628                       String lockPath = currentPath;
  629                       if (lockPath.endsWith("/"))
  630                           lockPath =
  631                               lockPath.substring(0, lockPath.length() - 1);
  632                       Vector currentLockNullResources =
  633                           (Vector) lockNullResources.get(lockPath);
  634                       if (currentLockNullResources != null) {
  635                           Enumeration lockNullResourcesList =
  636                               currentLockNullResources.elements();
  637                           while (lockNullResourcesList.hasMoreElements()) {
  638                               String lockNullPath = (String)
  639                                   lockNullResourcesList.nextElement();
  640                               parseLockNullProperties
  641                                   (req, generatedXML, lockNullPath, type,
  642                                    properties);
  643                           }
  644                       }
  645   
  646                   }
  647   
  648                   if (stack.isEmpty()) {
  649                       depth--;
  650                       stack = stackBelow;
  651                       stackBelow = new Stack<String>();
  652                   }
  653   
  654                   generatedXML.sendData();
  655   
  656               }
  657           }
  658   
  659           generatedXML.writeElement(null, "multistatus",
  660                                     XMLWriter.CLOSING);
  661   
  662           generatedXML.sendData();
  663   
  664       }
  665   
  666   
  667       /**
  668        * PROPPATCH Method.
  669        */
  670       protected void doProppatch(HttpServletRequest req,
  671                                  HttpServletResponse resp)
  672           throws ServletException, IOException {
  673   
  674           if (readOnly) {
  675               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  676               return;
  677           }
  678   
  679           if (isLocked(req)) {
  680               resp.sendError(WebdavStatus.SC_LOCKED);
  681               return;
  682           }
  683   
  684           resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  685   
  686       }
  687   
  688   
  689       /**
  690        * MKCOL Method.
  691        */
  692       protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
  693           throws ServletException, IOException {
  694   
  695           if (readOnly) {
  696               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  697               return;
  698           }
  699   
  700           if (isLocked(req)) {
  701               resp.sendError(WebdavStatus.SC_LOCKED);
  702               return;
  703           }
  704   
  705           String path = getRelativePath(req);
  706   
  707           if ((path.toUpperCase().startsWith("/WEB-INF")) ||
  708               (path.toUpperCase().startsWith("/META-INF"))) {
  709               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  710               return;
  711           }
  712   
  713           boolean exists = true;
  714           Object object = null;
  715           try {
  716               object = resources.lookup(path);
  717           } catch (NamingException e) {
  718               exists = false;
  719           }
  720   
  721           // Can't create a collection if a resource already exists at the given
  722           // path
  723           if (exists) {
  724               // Get allowed methods
  725               StringBuffer methodsAllowed = determineMethodsAllowed(resources,
  726                                                                     req);
  727   
  728               resp.addHeader("Allow", methodsAllowed.toString());
  729   
  730               resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
  731               return;
  732           }
  733   
  734           if (req.getInputStream().available() > 0) {
  735               DocumentBuilder documentBuilder = getDocumentBuilder();
  736               try {
  737                   Document document = documentBuilder.parse
  738                       (new InputSource(req.getInputStream()));
  739                   // TODO : Process this request body
  740                   resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
  741                   return;
  742   
  743               } catch(SAXException saxe) {
  744                   // Parse error - assume invalid content
  745                   resp.sendError(WebdavStatus.SC_BAD_REQUEST);
  746                   return;
  747               }
  748           }
  749   
  750           boolean result = true;
  751           try {
  752               resources.createSubcontext(path);
  753           } catch (NamingException e) {
  754               result = false;
  755           }
  756   
  757           if (!result) {
  758               resp.sendError(WebdavStatus.SC_CONFLICT,
  759                              WebdavStatus.getStatusText
  760                              (WebdavStatus.SC_CONFLICT));
  761           } else {
  762               resp.setStatus(WebdavStatus.SC_CREATED);
  763               // Removing any lock-null resource which would be present
  764               lockNullResources.remove(path);
  765           }
  766   
  767       }
  768   
  769   
  770       /**
  771        * DELETE Method.
  772        */
  773       protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
  774           throws ServletException, IOException {
  775   
  776           if (readOnly) {
  777               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  778               return;
  779           }
  780   
  781           if (isLocked(req)) {
  782               resp.sendError(WebdavStatus.SC_LOCKED);
  783               return;
  784           }
  785   
  786           deleteResource(req, resp);
  787   
  788       }
  789   
  790   
  791       /**
  792        * Process a POST request for the specified resource.
  793        *
  794        * @param req The servlet request we are processing
  795        * @param resp The servlet response we are creating
  796        *
  797        * @exception IOException if an input/output error occurs
  798        * @exception ServletException if a servlet-specified error occurs
  799        */
  800       protected void doPut(HttpServletRequest req, HttpServletResponse resp)
  801           throws ServletException, IOException {
  802   
  803           if (isLocked(req)) {
  804               resp.sendError(WebdavStatus.SC_LOCKED);
  805               return;
  806           }
  807   
  808           super.doPut(req, resp);
  809   
  810           String path = getRelativePath(req);
  811   
  812           // Removing any lock-null resource which would be present
  813           lockNullResources.remove(path);
  814   
  815       }
  816   
  817       /**
  818        * COPY Method.
  819        */
  820       protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
  821           throws ServletException, IOException {
  822   
  823           if (readOnly) {
  824               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  825               return;
  826           }
  827   
  828           copyResource(req, resp);
  829   
  830       }
  831   
  832   
  833       /**
  834        * MOVE Method.
  835        */
  836       protected void doMove(HttpServletRequest req, HttpServletResponse resp)
  837           throws ServletException, IOException {
  838   
  839           if (readOnly) {
  840               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  841               return;
  842           }
  843   
  844           if (isLocked(req)) {
  845               resp.sendError(WebdavStatus.SC_LOCKED);
  846               return;
  847           }
  848   
  849           String path = getRelativePath(req);
  850   
  851           if (copyResource(req, resp)) {
  852               deleteResource(path, req, resp, false);
  853           }
  854   
  855       }
  856   
  857   
  858       /**
  859        * LOCK Method.
  860        */
  861       protected void doLock(HttpServletRequest req, HttpServletResponse resp)
  862           throws ServletException, IOException {
  863   
  864           if (readOnly) {
  865               resp.sendError(WebdavStatus.SC_FORBIDDEN);
  866               return;
  867           }
  868   
  869           if (isLocked(req)) {
  870               resp.sendError(WebdavStatus.SC_LOCKED);
  871               return;
  872           }
  873   
  874           LockInfo lock = new LockInfo();
  875   
  876           // Parsing lock request
  877   
  878           // Parsing depth header
  879   
  880           String depthStr = req.getHeader("Depth");
  881   
  882           if (depthStr == null) {
  883               lock.depth = INFINITY;
  884           } else {
  885               if (depthStr.equals("0")) {
  886                   lock.depth = 0;
  887               } else {
  888                   lock.depth = INFINITY;
  889               }
  890           }
  891   
  892           // Parsing timeout header
  893   
  894           int lockDuration = DEFAULT_TIMEOUT;
  895           String lockDurationStr = req.getHeader("Timeout");
  896           if (lockDurationStr == null) {
  897               lockDuration = DEFAULT_TIMEOUT;
  898           } else {
  899               int commaPos = lockDurationStr.indexOf(",");
  900               // If multiple timeouts, just use the first
  901               if (commaPos != -1) {
  902                   lockDurationStr = lockDurationStr.substring(0,commaPos);
  903               }
  904               if (lockDurationStr.startsWith("Second-")) {
  905                   lockDuration =
  906                       (new Integer(lockDurationStr.substring(7))).intValue();
  907               } else {
  908                   if (lockDurationStr.equalsIgnoreCase("infinity")) {
  909                       lockDuration = MAX_TIMEOUT;
  910                   } else {
  911                       try {
  912                           lockDuration =
  913                               (new Integer(lockDurationStr)).intValue();
  914                       } catch (NumberFormatException e) {
  915                           lockDuration = MAX_TIMEOUT;
  916                       }
  917                   }
  918               }
  919               if (lockDuration == 0) {
  920                   lockDuration = DEFAULT_TIMEOUT;
  921               }
  922               if (lockDuration > MAX_TIMEOUT) {
  923                   lockDuration = MAX_TIMEOUT;
  924               }
  925           }
  926           lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
  927   
  928           int lockRequestType = LOCK_CREATION;
  929   
  930           Node lockInfoNode = null;
  931   
  932           DocumentBuilder documentBuilder = getDocumentBuilder();
  933   
  934           try {
  935               Document document = documentBuilder.parse(new InputSource
  936                   (req.getInputStream()));
  937   
  938               // Get the root element of the document
  939               Element rootElement = document.getDocumentElement();
  940               lockInfoNode = rootElement;
  941           } catch (IOException e) {
  942               lockRequestType = LOCK_REFRESH;
  943           } catch (SAXException e) {
  944               lockRequestType = LOCK_REFRESH;
  945           }
  946   
  947           if (lockInfoNode != null) {
  948   
  949               // Reading lock information
  950   
  951               NodeList childList = lockInfoNode.getChildNodes();
  952               StringWriter strWriter = null;
  953               DOMWriter domWriter = null;
  954   
  955               Node lockScopeNode = null;
  956               Node lockTypeNode = null;
  957               Node lockOwnerNode = null;
  958   
  959               for (int i=0; i < childList.getLength(); i++) {
  960                   Node currentNode = childList.item(i);
  961                   switch (currentNode.getNodeType()) {
  962                   case Node.TEXT_NODE:
  963                       break;
  964                   case Node.ELEMENT_NODE:
  965                       String nodeName = currentNode.getNodeName();
  966                       if (nodeName.endsWith("lockscope")) {
  967                           lockScopeNode = currentNode;
  968                       }
  969                       if (nodeName.endsWith("locktype")) {
  970                           lockTypeNode = currentNode;
  971                       }
  972                       if (nodeName.endsWith("owner")) {
  973                           lockOwnerNode = currentNode;
  974                       }
  975                       break;
  976                   }
  977               }
  978   
  979               if (lockScopeNode != null) {
  980   
  981                   childList = lockScopeNode.getChildNodes();
  982                   for (int i=0; i < childList.getLength(); i++) {
  983                       Node currentNode = childList.item(i);
  984                       switch (currentNode.getNodeType()) {
  985                       case Node.TEXT_NODE:
  986                           break;
  987                       case Node.ELEMENT_NODE:
  988                           String tempScope = currentNode.getNodeName();
  989                           if (tempScope.indexOf(':') != -1) {
  990                               lock.scope = tempScope.substring
  991                                   (tempScope.indexOf(':') + 1);
  992                           } else {
  993                               lock.scope = tempScope;
  994                           }
  995                           break;
  996                       }
  997                   }
  998   
  999                   if (lock.scope == null) {
 1000                       // Bad request
 1001                       resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
 1002                   }
 1003   
 1004               } else {
 1005                   // Bad request
 1006                   resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
 1007               }
 1008   
 1009               if (lockTypeNode != null) {
 1010   
 1011                   childList = lockTypeNode.getChildNodes();
 1012                   for (int i=0; i < childList.getLength(); i++) {
 1013                       Node currentNode = childList.item(i);
 1014                       switch (currentNode.getNodeType()) {
 1015                       case Node.TEXT_NODE:
 1016                           break;
 1017                       case Node.ELEMENT_NODE:
 1018                           String tempType = currentNode.getNodeName();
 1019                           if (tempType.indexOf(':') != -1) {
 1020                               lock.type =
 1021                                   tempType.substring(tempType.indexOf(':') + 1);
 1022                           } else {
 1023                               lock.type = tempType;
 1024                           }
 1025                           break;
 1026                       }
 1027                   }
 1028   
 1029                   if (lock.type == null) {
 1030                       // Bad request
 1031                       resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
 1032                   }
 1033   
 1034               } else {
 1035                   // Bad request
 1036                   resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
 1037               }
 1038   
 1039               if (lockOwnerNode != null) {
 1040   
 1041                   childList = lockOwnerNode.getChildNodes();
 1042                   for (int i=0; i < childList.getLength(); i++) {
 1043                       Node currentNode = childList.item(i);
 1044                       switch (currentNode.getNodeType()) {
 1045                       case Node.TEXT_NODE:
 1046                           lock.owner += currentNode.getNodeValue();
 1047                           break;
 1048                       case Node.ELEMENT_NODE:
 1049                           strWriter = new StringWriter();
 1050                           domWriter = new DOMWriter(strWriter, true);
 1051                           domWriter.setQualifiedNames(false);
 1052                           domWriter.print(currentNode);
 1053                           lock.owner += strWriter.toString();
 1054                           break;
 1055                       }
 1056                   }
 1057   
 1058                   if (lock.owner == null) {
 1059                       // Bad request
 1060                       resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
 1061                   }
 1062   
 1063               } else {
 1064                   lock.owner = new String();
 1065               }
 1066   
 1067           }
 1068   
 1069           String path = getRelativePath(req);
 1070   
 1071           lock.path = path;
 1072   
 1073           boolean exists = true;
 1074           Object object = null;
 1075           try {
 1076               object = resources.lookup(path);
 1077           } catch (NamingException e) {
 1078               exists = false;
 1079           }
 1080   
 1081           Enumeration locksList = null;
 1082   
 1083           if (lockRequestType == LOCK_CREATION) {
 1084   
 1085               // Generating lock id
 1086               String lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
 1087                   + lock.scope + "-" + req.getUserPrincipal() + "-"
 1088                   + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
 1089                   + lock.expiresAt + "-" + System.currentTimeMillis() + "-"
 1090                   + secret;
 1091               String lockToken =
 1092                   md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));
 1093   
 1094               if ( (exists) && (object instanceof DirContext) &&
 1095                    (lock.depth == INFINITY) ) {
 1096   
 1097                   // Locking a collection (and all its member resources)
 1098   
 1099                   // Checking if a child resource of this collection is
 1100                   // already locked
 1101                   Vector<String> lockPaths = new Vector<String>();
 1102                   locksList = collectionLocks.elements();
 1103                   while (locksList.hasMoreElements()) {
 1104                       LockInfo currentLock = (LockInfo) locksList.nextElement();
 1105                       if (currentLock.hasExpired()) {
 1106                           resourceLocks.remove(currentLock.path);
 1107                           continue;
 1108                       }
 1109                       if ( (currentLock.path.startsWith(lock.path)) &&
 1110                            ((currentLock.isExclusive()) ||
 1111                             (lock.isExclusive())) ) {
 1112                           // A child collection of this collection is locked
 1113                           lockPaths.addElement(currentLock.path);
 1114                       }
 1115                   }
 1116                   locksList = resourceLocks.elements();
 1117                   while (locksList.hasMoreElements()) {
 1118                       LockInfo currentLock = (LockInfo) locksList.nextElement();
 1119                       if (currentLock.hasExpired()) {
 1120                           resourceLocks.remove(currentLock.path);
 1121                           continue;
 1122                       }
 1123                       if ( (currentLock.path.startsWith(lock.path)) &&
 1124                            ((currentLock.isExclusive()) ||
 1125                             (lock.isExclusive())) ) {
 1126                           // A child resource of this collection is locked
 1127                           lockPaths.addElement(currentLock.path);
 1128                       }
 1129                   }
 1130   
 1131                   if (!lockPaths.isEmpty()) {
 1132   
 1133                       // One of the child paths was locked
 1134                       // We generate a multistatus error report
 1135   
 1136                       Enumeration lockPathsList = lockPaths.elements();
 1137   
 1138                       resp.setStatus(WebdavStatus.SC_CONFLICT);
 1139   
 1140                       XMLWriter generatedXML = new XMLWriter();
 1141                       generatedXML.writeXMLHeader();
 1142   
 1143                       generatedXML.writeElement
 1144                           (null, "multistatus" + generateNamespaceDeclarations(),
 1145                            XMLWriter.OPENING);
 1146   
 1147                       while (lockPathsList.hasMoreElements()) {
 1148                           generatedXML.writeElement(null, "response",
 1149                                                     XMLWriter.OPENING);
 1150                           generatedXML.writeElement(null, "href",
 1151                                                     XMLWriter.OPENING);
 1152                           generatedXML
 1153                               .writeText((String) lockPathsList.nextElement());
 1154                           generatedXML.writeElement(null, "href",
 1155                                                     XMLWriter.CLOSING);
 1156                           generatedXML.writeElement(null, "status",
 1157                                                     XMLWriter.OPENING);
 1158                           generatedXML
 1159                               .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
 1160                                          + " " + WebdavStatus
 1161                                          .getStatusText(WebdavStatus.SC_LOCKED));
 1162                           generatedXML.writeElement(null, "status",
 1163                                                     XMLWriter.CLOSING);
 1164   
 1165                           generatedXML.writeElement(null, "response",
 1166                                                     XMLWriter.CLOSING);
 1167                       }
 1168   
 1169                       generatedXML.writeElement(null, "multistatus",
 1170                                             XMLWriter.CLOSING);
 1171   
 1172                       Writer writer = resp.getWriter();
 1173                       writer.write(generatedXML.toString());
 1174                       writer.close();
 1175   
 1176                       return;
 1177   
 1178                   }
 1179   
 1180                   boolean addLock = true;
 1181   
 1182                   // Checking if there is already a shared lock on this path
 1183                   locksList = collectionLocks.elements();
 1184                   while (locksList.hasMoreElements()) {
 1185   
 1186                       LockInfo currentLock = (LockInfo) locksList.nextElement();
 1187                       if (currentLock.path.equals(lock.path)) {
 1188   
 1189                           if (currentLock.isExclusive()) {
 1190                               resp.sendError(WebdavStatus.SC_LOCKED);
 1191                               return;
 1192                           } else {
 1193                               if (lock.isExclusive()) {
 1194                                   resp.sendError(WebdavStatus.SC_LOCKED);
 1195                                   return;
 1196                               }
 1197                           }
 1198   
 1199                           currentLock.tokens.addElement(lockToken);
 1200                           lock = currentLock;
 1201                           addLock = false;
 1202   
 1203                       }
 1204   
 1205                   }
 1206   
 1207                   if (addLock) {
 1208                       lock.tokens.addElement(lockToken);
 1209                       collectionLocks.addElement(lock);
 1210                   }
 1211   
 1212               } else {
 1213   
 1214                   // Locking a single resource
 1215   
 1216                   // Retrieving an already existing lock on that resource
 1217                   LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path);
 1218                   if (presentLock != null) {
 1219   
 1220                       if ((presentLock.isExclusive()) || (lock.isExclusive())) {
 1221                           // If either lock is exclusive, the lock can't be
 1222                           // granted
 1223                           resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
 1224                           return;
 1225                       } else {
 1226                           presentLock.tokens.addElement(lockToken);
 1227                           lock = presentLock;
 1228                       }
 1229   
 1230                   } else {
 1231   
 1232                       lock.tokens.addElement(lockToken);
 1233                       resourceLocks.put(lock.path, lock);
 1234   
 1235                       // Checking if a resource exists at this path
 1236                       exists = true;
 1237                       try {
 1238                           object = resources.lookup(path);
 1239                       } catch (NamingException e) {
 1240                           exists = false;
 1241                       }
 1242                       if (!exists) {
 1243   
 1244                           // "Creating" a lock-null resource
 1245                           int slash = lock.path.lastIndexOf('/');
 1246                           String parentPath = lock.path.substring(0, slash);
 1247   
 1248                           Vector<String> lockNulls =
 1249                               lockNullResources.get(parentPath);
 1250                           if (lockNulls == null) {
 1251                               lockNulls = new Vector<String>();
 1252                               lockNullResources.put(parentPath, lockNulls);
 1253                           }
 1254   
 1255                           lockNulls.addElement(lock.path);
 1256   
 1257                       }
 1258                       // Add the Lock-Token header as by RFC 2518 8.10.1
 1259                       // - only do this for newly created locks
 1260                       resp.addHeader("Lock-Token", "<opaquelocktoken:"
 1261                                      + lockToken + ">");
 1262                   }
 1263   
 1264               }
 1265   
 1266           }
 1267   
 1268           if (lockRequestType == LOCK_REFRESH) {
 1269   
 1270               String ifHeader = req.getHeader("If");
 1271               if (ifHeader == null)
 1272                   ifHeader = "";
 1273   
 1274               // Checking resource locks
 1275   
 1276               LockInfo toRenew = (LockInfo) resourceLocks.get(path);
 1277               Enumeration tokenList = null;
 1278               if (lock != null) {
 1279   
 1280                   // At least one of the tokens of the locks must have been given
 1281   
 1282                   tokenList = toRenew.tokens.elements();
 1283                   while (tokenList.hasMoreElements()) {
 1284                       String token = (String) tokenList.nextElement();
 1285                       if (ifHeader.indexOf(token) != -1) {
 1286                           toRenew.expiresAt = lock.expiresAt;
 1287                           lock = toRenew;
 1288                       }
 1289                   }
 1290   
 1291               }
 1292   
 1293               // Checking inheritable collection locks
 1294   
 1295               Enumeration collectionLocksList = collectionLocks.elements();
 1296               while (collectionLocksList.hasMoreElements()) {
 1297                   toRenew = (LockInfo) collectionLocksList.nextElement();
 1298                   if (path.equals(toRenew.path)) {
 1299   
 1300                       tokenList = toRenew.tokens.elements();
 1301                       while (tokenList.hasMoreElements()) {
 1302                           String token = (String) tokenList.nextElement();
 1303                           if (ifHeader.indexOf(token) != -1) {
 1304                               toRenew.expiresAt = lock.expiresAt;
 1305                               lock = toRenew;
 1306                           }
 1307                       }
 1308   
 1309                   }
 1310               }
 1311   
 1312           }
 1313   
 1314           // Set the status, then generate the XML response containing
 1315           // the lock information
 1316           XMLWriter generatedXML = new XMLWriter();
 1317           generatedXML.writeXMLHeader();
 1318           generatedXML.writeElement(null, "prop"
 1319                                     + generateNamespaceDeclarations(),
 1320                                     XMLWriter.OPENING);
 1321   
 1322           generatedXML.writeElement(null, "lockdiscovery",
 1323                                     XMLWriter.OPENING);
 1324   
 1325           lock.toXML(generatedXML);
 1326   
 1327           generatedXML.writeElement(null, "lockdiscovery",
 1328                                     XMLWriter.CLOSING);
 1329   
 1330           generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 1331   
 1332           resp.setStatus(WebdavStatus.SC_OK);
 1333           resp.setContentType("text/xml; charset=UTF-8");
 1334           Writer writer = resp.getWriter();
 1335           writer.write(generatedXML.toString());
 1336           writer.close();
 1337   
 1338       }
 1339   
 1340   
 1341       /**
 1342        * UNLOCK Method.
 1343        */
 1344       protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
 1345           throws ServletException, IOException {
 1346   
 1347           if (readOnly) {
 1348               resp.sendError(WebdavStatus.SC_FORBIDDEN);
 1349               return;
 1350           }
 1351   
 1352           if (isLocked(req)) {
 1353               resp.sendError(WebdavStatus.SC_LOCKED);
 1354               return;
 1355           }
 1356   
 1357           String path = getRelativePath(req);
 1358   
 1359           String lockTokenHeader = req.getHeader("Lock-Token");
 1360           if (lockTokenHeader == null)
 1361               lockTokenHeader = "";
 1362   
 1363           // Checking resource locks
 1364   
 1365           LockInfo lock = (LockInfo) resourceLocks.get(path);
 1366           Enumeration tokenList = null;
 1367           if (lock != null) {
 1368   
 1369               // At least one of the tokens of the locks must have been given
 1370   
 1371               tokenList = lock.tokens.elements();
 1372               while (tokenList.hasMoreElements()) {
 1373                   String token = (String) tokenList.nextElement();
 1374                   if (lockTokenHeader.indexOf(token) != -1) {
 1375                       lock.tokens.removeElement(token);
 1376                   }
 1377               }
 1378   
 1379               if (lock.tokens.isEmpty()) {
 1380                   resourceLocks.remove(path);
 1381                   // Removing any lock-null resource which would be present
 1382                   lockNullResources.remove(path);
 1383               }
 1384   
 1385           }
 1386   
 1387           // Checking inheritable collection locks
 1388   
 1389           Enumeration collectionLocksList = collectionLocks.elements();
 1390           while (collectionLocksList.hasMoreElements()) {
 1391               lock = (LockInfo) collectionLocksList.nextElement();
 1392               if (path.equals(lock.path)) {
 1393   
 1394                   tokenList = lock.tokens.elements();
 1395                   while (tokenList.hasMoreElements()) {
 1396                       String token = (String) tokenList.nextElement();
 1397                       if (lockTokenHeader.indexOf(token) != -1) {
 1398                           lock.tokens.removeElement(token);
 1399                           break;
 1400                       }
 1401                   }
 1402   
 1403                   if (lock.tokens.isEmpty()) {
 1404                       collectionLocks.removeElement(lock);
 1405                       // Removing any lock-null resource which would be present
 1406                       lockNullResources.remove(path);
 1407                   }
 1408   
 1409               }
 1410           }
 1411   
 1412           resp.setStatus(WebdavStatus.SC_NO_CONTENT);
 1413   
 1414       }
 1415   
 1416       /**
 1417        * Return a context-relative path, beginning with a "/", that represents
 1418        * the canonical version of the specified path after ".." and "." elements
 1419        * are resolved out.  If the specified path attempts to go outside the
 1420        * boundaries of the current context (i.e. too many ".." path elements
 1421        * are present), return <code>null</code> instead.
 1422        *
 1423        * @param path Path to be normalized
 1424        */
 1425       protected String normalize(String path) {
 1426   
 1427           if (path == null)
 1428               return null;
 1429   
 1430           // Create a place for the normalized path
 1431           String normalized = path;
 1432   
 1433           if (normalized == null)
 1434               return (null);
 1435   
 1436           if (normalized.equals("/."))
 1437               return "/";
 1438   
 1439           // Normalize the slashes and add leading slash if necessary
 1440           if (normalized.indexOf('\\') >= 0)
 1441               normalized = normalized.replace('\\', '/');
 1442           if (!normalized.startsWith("/"))
 1443               normalized = "/" + normalized;
 1444   
 1445           // Resolve occurrences of "//" in the normalized path
 1446           while (true) {
 1447               int index = normalized.indexOf("//");
 1448               if (index < 0)
 1449                   break;
 1450               normalized = normalized.substring(0, index) +
 1451                   normalized.substring(index + 1);
 1452           }
 1453   
 1454           // Resolve occurrences of "/./" in the normalized path
 1455           while (true) {
 1456               int index = normalized.indexOf("/./");
 1457               if (index < 0)
 1458                   break;
 1459               normalized = normalized.substring(0, index) +
 1460                   normalized.substring(index + 2);
 1461           }
 1462   
 1463           // Resolve occurrences of "/../" in the normalized path
 1464           while (true) {
 1465               int index = normalized.indexOf("/../");
 1466               if (index < 0)
 1467                   break;
 1468               if (index == 0)
 1469                   return (null);  // Trying to go outside our context
 1470               int index2 = normalized.lastIndexOf('/', index - 1);
 1471               normalized = normalized.substring(0, index2) +
 1472                   normalized.substring(index + 3);
 1473           }
 1474   
 1475           // Return the normalized path that we have completed
 1476           return (normalized);
 1477   
 1478       }
 1479   
 1480   
 1481       // -------------------------------------------------------- Private Methods
 1482   
 1483       /**
 1484        * Generate the namespace declarations.
 1485        */
 1486       private String generateNamespaceDeclarations() {
 1487           return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
 1488       }
 1489   
 1490   
 1491       /**
 1492        * Check to see if a resource is currently write locked. The method
 1493        * will look at the "If" header to make sure the client
 1494        * has give the appropriate lock tokens.
 1495        *
 1496        * @param req Servlet request
 1497        * @return boolean true if the resource is locked (and no appropriate
 1498        * lock token has been found for at least one of the non-shared locks which
 1499        * are present on the resource).
 1500        */
 1501       private boolean isLocked(HttpServletRequest req) {
 1502   
 1503           String path = getRelativePath(req);
 1504   
 1505           String ifHeader = req.getHeader("If");
 1506           if (ifHeader == null)
 1507               ifHeader = "";
 1508   
 1509           String lockTokenHeader = req.getHeader("Lock-Token");
 1510           if (lockTokenHeader == null)
 1511               lockTokenHeader = "";
 1512   
 1513           return isLocked(path, ifHeader + lockTokenHeader);
 1514   
 1515       }
 1516   
 1517   
 1518       /**
 1519        * Check to see if a resource is currently write locked.
 1520        *
 1521        * @param path Path of the resource
 1522        * @param ifHeader "If" HTTP header which was included in the request
 1523        * @return boolean true if the resource is locked (and no appropriate
 1524        * lock token has been found for at least one of the non-shared locks which
 1525        * are present on the resource).
 1526        */
 1527       private boolean isLocked(String path, String ifHeader) {
 1528   
 1529           // Checking resource locks
 1530   
 1531           LockInfo lock = (LockInfo) resourceLocks.get(path);
 1532           Enumeration tokenList = null;
 1533           if ((lock != null) && (lock.hasExpired())) {
 1534               resourceLocks.remove(path);
 1535           } else if (lock != null) {
 1536   
 1537               // At least one of the tokens of the locks must have been given
 1538   
 1539               tokenList = lock.tokens.elements();
 1540               boolean tokenMatch = false;
 1541               while (tokenList.hasMoreElements()) {
 1542                   String token = (String) tokenList.nextElement();
 1543                   if (ifHeader.indexOf(token) != -1)
 1544                       tokenMatch = true;
 1545               }
 1546               if (!tokenMatch)
 1547                   return true;
 1548   
 1549           }
 1550   
 1551           // Checking inheritable collection locks
 1552   
 1553           Enumeration collectionLocksList = collectionLocks.elements();
 1554           while (collectionLocksList.hasMoreElements()) {
 1555               lock = (LockInfo) collectionLocksList.nextElement();
 1556               if (lock.hasExpired()) {
 1557                   collectionLocks.removeElement(lock);
 1558               } else if (path.startsWith(lock.path)) {
 1559   
 1560                   tokenList = lock.tokens.elements();
 1561                   boolean tokenMatch = false;
 1562                   while (tokenList.hasMoreElements()) {
 1563                       String token = (String) tokenList.nextElement();
 1564                       if (ifHeader.indexOf(token) != -1)
 1565                           tokenMatch = true;
 1566                   }
 1567                   if (!tokenMatch)
 1568                       return true;
 1569   
 1570               }
 1571           }
 1572   
 1573           return false;
 1574   
 1575       }
 1576   
 1577   
 1578       /**
 1579        * Copy a resource.
 1580        *
 1581        * @param req Servlet request
 1582        * @param resp Servlet response
 1583        * @return boolean true if the copy is successful
 1584        */
 1585       private boolean copyResource(HttpServletRequest req,
 1586                                    HttpServletResponse resp)
 1587           throws ServletException, IOException {
 1588   
 1589           // Parsing destination header
 1590   
 1591           String destinationPath = req.getHeader("Destination");
 1592   
 1593           if (destinationPath == null) {
 1594               resp.sendError(WebdavStatus.SC_BAD_REQUEST);
 1595               return false;
 1596           }
 1597   
 1598           // Remove url encoding from destination
 1599           destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
 1600   
 1601           int protocolIndex = destinationPath.indexOf("://");
 1602           if (protocolIndex >= 0) {
 1603               // if the Destination URL contains the protocol, we can safely
 1604               // trim everything upto the first "/" character after "://"
 1605               int firstSeparator =
 1606                   destinationPath.indexOf("/", protocolIndex + 4);
 1607               if (firstSeparator < 0) {
 1608                   destinationPath = "/";
 1609               } else {
 1610                   destinationPath = destinationPath.substring(firstSeparator);
 1611               }
 1612           } else {
 1613               String hostName = req.getServerName();
 1614               if ((hostName != null) && (destinationPath.startsWith(hostName))) {
 1615                   destinationPath = destinationPath.substring(hostName.length());
 1616               }
 1617   
 1618               int portIndex = destinationPath.indexOf(":");
 1619               if (portIndex >= 0) {
 1620                   destinationPath = destinationPath.substring(portIndex);
 1621               }
 1622   
 1623               if (destinationPath.startsWith(":")) {
 1624                   int firstSeparator = destinationPath.indexOf("/");
 1625                   if (firstSeparator < 0) {
 1626                       destinationPath = "/";
 1627                   } else {
 1628                       destinationPath =
 1629                           destinationPath.substring(firstSeparator);
 1630                   }
 1631               }
 1632           }
 1633   
 1634           // Normalise destination path (remove '.' and '..')
 1635           destinationPath = normalize(destinationPath);
 1636   
 1637           String contextPath = req.getContextPath();
 1638           if ((contextPath != null) &&
 1639               (destinationPath.startsWith(contextPath))) {
 1640               destinationPath = destinationPath.substring(contextPath.length());
 1641           }
 1642   
 1643           String pathInfo = req.getPathInfo();
 1644           if (pathInfo != null) {
 1645               String servletPath = req.getServletPath();
 1646               if ((servletPath != null) &&
 1647                   (destinationPath.startsWith(servletPath))) {
 1648                   destinationPath = destinationPath
 1649                       .substring(servletPath.length());
 1650               }
 1651           }
 1652   
 1653           if (debug > 0)
 1654               log("Dest path :" + destinationPath);
 1655   
 1656           if ((destinationPath.toUpperCase().startsWith("/WEB-INF")) ||
 1657               (destinationPath.toUpperCase().startsWith("/META-INF"))) {
 1658               resp.sendError(WebdavStatus.SC_FORBIDDEN);
 1659               return false;
 1660           }
 1661   
 1662           String path = getRelativePath(req);
 1663   
 1664           if ((path.toUpperCase().startsWith("/WEB-INF")) ||
 1665               (path.toUpperCase().startsWith("/META-INF"))) {
 1666               resp.sendError(WebdavStatus.SC_FORBIDDEN);
 1667               return false;
 1668           }
 1669   
 1670           if (destinationPath.equals(path)) {
 1671               resp.sendError(WebdavStatus.SC_FORBIDDEN);
 1672               return false;
 1673           }
 1674   
 1675           // Parsing overwrite header
 1676   
 1677           boolean overwrite = true;
 1678           String overwriteHeader = req.getHeader("Overwrite");
 1679   
 1680           if (overwriteHeader != null) {
 1681               if (overwriteHeader.equalsIgnoreCase("T")) {
 1682                   overwrite = true;
 1683               } else {
 1684                   overwrite = false;
 1685               }
 1686           }
 1687   
 1688           // Overwriting the destination
 1689   
 1690           boolean exists = true;
 1691           try {
 1692               resources.lookup(destinationPath);
 1693           } catch (NamingException e) {
 1694               exists = false;
 1695           }
 1696   
 1697           if (overwrite) {
 1698   
 1699               // Delete destination resource, if it exists
 1700               if (exists) {
 1701                   if (!deleteResource(destinationPath, req, resp, true)) {
 1702                       return false;
 1703                   }
 1704               } else {
 1705                   resp.setStatus(WebdavStatus.SC_CREATED);
 1706               }
 1707   
 1708           } else {
 1709   
 1710               // If the destination exists, then it's a conflict
 1711               if (exists) {
 1712                   resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
 1713                   return false;
 1714               }
 1715   
 1716           }
 1717   
 1718           // Copying source to destination
 1719   
 1720           Hashtable<String,Integer> errorList = new Hashtable<String,Integer>();
 1721   
 1722           boolean result = copyResource(resources, errorList,
 1723                                         path, destinationPath);
 1724   
 1725           if ((!result) || (!errorList.isEmpty())) {
 1726   
 1727               sendReport(req, resp, errorList);
 1728               return false;
 1729   
 1730           }
 1731   
 1732           // Copy was successful
 1733           resp.setStatus(WebdavStatus.SC_CREATED);
 1734           
 1735           // Removing any lock-null resource which would be present at
 1736           // the destination path
 1737           lockNullResources.remove(destinationPath);
 1738   
 1739           return true;
 1740   
 1741       }
 1742   
 1743   
 1744       /**
 1745        * Copy a collection.
 1746        *
 1747        * @param resources Resources implementation to be used
 1748        * @param errorList Hashtable containing the list of errors which occurred
 1749        * during the copy operation
 1750        * @param source Path of the resource to be copied
 1751        * @param dest Destination path
 1752        */
 1753       private boolean copyResource(DirContext resources,
 1754               Hashtable<String,Integer> errorList, String source, String dest) {
 1755   
 1756           if (debug > 1)
 1757               log("Copy: " + source + " To: " + dest);
 1758   
 1759           Object object = null;
 1760           try {
 1761               object = resources.lookup(source);
 1762           } catch (NamingException e) {
 1763           }
 1764   
 1765           if (object instanceof DirContext) {
 1766   
 1767               try {
 1768                   resources.createSubcontext(dest);
 1769               } catch (NamingException e) {
 1770                   errorList.put
 1771                       (dest, new Integer(WebdavStatus.SC_CONFLICT));
 1772                   return false;
 1773               }
 1774   
 1775               try {
 1776                   NamingEnumeration enumeration = resources.list(source);
 1777                   while (enumeration.hasMoreElements()) {
 1778                       NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
 1779                       String childDest = dest;
 1780                       if (!childDest.equals("/"))
 1781                           childDest += "/";
 1782                       childDest += ncPair.getName();
 1783                       String childSrc = source;
 1784                       if (!childSrc.equals("/"))
 1785                           childSrc += "/";
 1786                       childSrc += ncPair.getName();
 1787                       copyResource(resources, errorList, childSrc, childDest);
 1788                   }
 1789               } catch (NamingException e) {
 1790                   errorList.put
 1791                       (dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1792                   return false;
 1793               }
 1794   
 1795           } else {
 1796   
 1797               if (object instanceof Resource) {
 1798                   try {
 1799                       resources.bind(dest, object);
 1800                   } catch (NamingException e) {
 1801                       errorList.put
 1802                           (source,
 1803                            new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1804                       return false;
 1805                   }
 1806               } else {
 1807                   errorList.put
 1808                       (source,
 1809                        new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1810                   return false;
 1811               }
 1812   
 1813           }
 1814   
 1815           return true;
 1816   
 1817       }
 1818   
 1819   
 1820       /**
 1821        * Delete a resource.
 1822        *
 1823        * @param req Servlet request
 1824        * @param resp Servlet response
 1825        * @return boolean true if the copy is successful
 1826        */
 1827       private boolean deleteResource(HttpServletRequest req,
 1828                                      HttpServletResponse resp)
 1829           throws ServletException, IOException {
 1830   
 1831           String path = getRelativePath(req);
 1832   
 1833           return deleteResource(path, req, resp, true);
 1834   
 1835       }
 1836   
 1837   
 1838       /**
 1839        * Delete a resource.
 1840        *
 1841        * @param path Path of the resource which is to be deleted
 1842        * @param req Servlet request
 1843        * @param resp Servlet response
 1844        * @param setStatus Should the response status be set on successful
 1845        *                  completion
 1846        */
 1847       private boolean deleteResource(String path, HttpServletRequest req,
 1848                                      HttpServletResponse resp, boolean setStatus)
 1849           throws ServletException, IOException {
 1850   
 1851           if ((path.toUpperCase().startsWith("/WEB-INF")) ||
 1852               (path.toUpperCase().startsWith("/META-INF"))) {
 1853               resp.sendError(WebdavStatus.SC_FORBIDDEN);
 1854               return false;
 1855           }
 1856   
 1857           String ifHeader = req.getHeader("If");
 1858           if (ifHeader == null)
 1859               ifHeader = "";
 1860   
 1861           String lockTokenHeader = req.getHeader("Lock-Token");
 1862           if (lockTokenHeader == null)
 1863               lockTokenHeader = "";
 1864   
 1865           if (isLocked(path, ifHeader + lockTokenHeader)) {
 1866               resp.sendError(WebdavStatus.SC_LOCKED);
 1867               return false;
 1868           }
 1869   
 1870           boolean exists = true;
 1871           Object object = null;
 1872           try {
 1873               object = resources.lookup(path);
 1874           } catch (NamingException e) {
 1875               exists = false;
 1876           }
 1877   
 1878           if (!exists) {
 1879               resp.sendError(WebdavStatus.SC_NOT_FOUND);
 1880               return false;
 1881           }
 1882   
 1883           boolean collection = (object instanceof DirContext);
 1884   
 1885           if (!collection) {
 1886               try {
 1887                   resources.unbind(path);
 1888               } catch (NamingException e) {
 1889                   resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
 1890                   return false;
 1891               }
 1892           } else {
 1893   
 1894               Hashtable<String,Integer> errorList =
 1895                   new Hashtable<String,Integer>();
 1896   
 1897               deleteCollection(req, resources, path, errorList);
 1898               try {
 1899                   resources.unbind(path);
 1900               } catch (NamingException e) {
 1901                   errorList.put(path, new Integer
 1902                       (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1903               }
 1904   
 1905               if (!errorList.isEmpty()) {
 1906   
 1907                   sendReport(req, resp, errorList);
 1908                   return false;
 1909   
 1910               }
 1911   
 1912           }
 1913           if (setStatus) {
 1914               resp.setStatus(WebdavStatus.SC_NO_CONTENT);
 1915           }
 1916           return true;
 1917   
 1918       }
 1919   
 1920   
 1921       /**
 1922        * Deletes a collection.
 1923        *
 1924        * @param resources Resources implementation associated with the context
 1925        * @param path Path to the collection to be deleted
 1926        * @param errorList Contains the list of the errors which occurred
 1927        */
 1928       private void deleteCollection(HttpServletRequest req,
 1929                                     DirContext resources,
 1930                                     String path,
 1931                                     Hashtable<String,Integer> errorList) {
 1932   
 1933           if (debug > 1)
 1934               log("Delete:" + path);
 1935   
 1936           if ((path.toUpperCase().startsWith("/WEB-INF")) ||
 1937               (path.toUpperCase().startsWith("/META-INF"))) {
 1938               errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN));
 1939               return;
 1940           }
 1941   
 1942           String ifHeader = req.getHeader("If");
 1943           if (ifHeader == null)
 1944               ifHeader = "";
 1945   
 1946           String lockTokenHeader = req.getHeader("Lock-Token");
 1947           if (lockTokenHeader == null)
 1948               lockTokenHeader = "";
 1949   
 1950           Enumeration enumeration = null;
 1951           try {
 1952               enumeration = resources.list(path);
 1953           } catch (NamingException e) {
 1954               errorList.put(path, new Integer
 1955                   (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1956               return;
 1957           }
 1958   
 1959           while (enumeration.hasMoreElements()) {
 1960               NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
 1961               String childName = path;
 1962               if (!childName.equals("/"))
 1963                   childName += "/";
 1964               childName += ncPair.getName();
 1965   
 1966               if (isLocked(childName, ifHeader + lockTokenHeader)) {
 1967   
 1968                   errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED));
 1969   
 1970               } else {
 1971   
 1972                   try {
 1973                       Object object = resources.lookup(childName);
 1974                       if (object instanceof DirContext) {
 1975                           deleteCollection(req, resources, childName, errorList);
 1976                       }
 1977   
 1978                       try {
 1979                           resources.unbind(childName);
 1980                       } catch (NamingException e) {
 1981                           if (!(object instanceof DirContext)) {
 1982                               // If it's not a collection, then it's an unknown
 1983                               // error
 1984                               errorList.put
 1985                                   (childName, new Integer
 1986                                       (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1987                           }
 1988                       }
 1989                   } catch (NamingException e) {
 1990                       errorList.put
 1991                           (childName, new Integer
 1992                               (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
 1993                   }
 1994               }
 1995   
 1996           }
 1997   
 1998       }
 1999   
 2000   
 2001       /**
 2002        * Send a multistatus element containing a complete error report to the
 2003        * client.
 2004        *
 2005        * @param req Servlet request
 2006        * @param resp Servlet response
 2007        * @param errorList List of error to be displayed
 2008        */
 2009       private void sendReport(HttpServletRequest req, HttpServletResponse resp,
 2010                               Hashtable errorList)
 2011           throws ServletException, IOException {
 2012   
 2013           resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
 2014   
 2015           String absoluteUri = req.getRequestURI();
 2016           String relativePath = getRelativePath(req);
 2017   
 2018           XMLWriter generatedXML = new XMLWriter();
 2019           generatedXML.writeXMLHeader();
 2020   
 2021           generatedXML.writeElement(null, "multistatus"
 2022                                     + generateNamespaceDeclarations(),
 2023                                     XMLWriter.OPENING);
 2024   
 2025           Enumeration pathList = errorList.keys();
 2026           while (pathList.hasMoreElements()) {
 2027   
 2028               String errorPath = (String) pathList.nextElement();
 2029               int errorCode = ((Integer) errorList.get(errorPath)).intValue();
 2030   
 2031               generatedXML.writeElement(null, "response", XMLWriter.OPENING);
 2032   
 2033               generatedXML.writeElement(null, "href", XMLWriter.OPENING);
 2034               String toAppend = errorPath.substring(relativePath.length());
 2035               if (!toAppend.startsWith("/"))
 2036                   toAppend = "/" + toAppend;
 2037               generatedXML.writeText(absoluteUri + toAppend);
 2038               generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
 2039               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2040               generatedXML
 2041                   .writeText("HTTP/1.1 " + errorCode + " "
 2042                              + WebdavStatus.getStatusText(errorCode));
 2043               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2044   
 2045               generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
 2046   
 2047           }
 2048   
 2049           generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);
 2050   
 2051           Writer writer = resp.getWriter();
 2052           writer.write(generatedXML.toString());
 2053           writer.close();
 2054   
 2055       }
 2056   
 2057   
 2058       /**
 2059        * Propfind helper method.
 2060        *
 2061        * @param req The servlet request
 2062        * @param resources Resources object associated with this context
 2063        * @param generatedXML XML response to the Propfind request
 2064        * @param path Path of the current resource
 2065        * @param type Propfind type
 2066        * @param propertiesVector If the propfind type is find properties by
 2067        * name, then this Vector contains those properties
 2068        */
 2069       private void parseProperties(HttpServletRequest req,
 2070                                    XMLWriter generatedXML,
 2071                                    String path, int type,
 2072                                    Vector<String> propertiesVector) {
 2073   
 2074           // Exclude any resource in the /WEB-INF and /META-INF subdirectories
 2075           // (the "toUpperCase()" avoids problems on Windows systems)
 2076           if (path.toUpperCase().startsWith("/WEB-INF") ||
 2077               path.toUpperCase().startsWith("/META-INF"))
 2078               return;
 2079   
 2080           CacheEntry cacheEntry = resources.lookupCache(path);
 2081   
 2082           generatedXML.writeElement(null, "response", XMLWriter.OPENING);
 2083           String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
 2084                                      + WebdavStatus.getStatusText
 2085                                      (WebdavStatus.SC_OK));
 2086   
 2087           // Generating href element
 2088           generatedXML.writeElement(null, "href", XMLWriter.OPENING);
 2089   
 2090           String href = req.getContextPath() + req.getServletPath();
 2091           if ((href.endsWith("/")) && (path.startsWith("/")))
 2092               href += path.substring(1);
 2093           else
 2094               href += path;
 2095           if ((cacheEntry.context != null) && (!href.endsWith("/")))
 2096               href += "/";
 2097   
 2098           generatedXML.writeText(rewriteUrl(href));
 2099   
 2100           generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
 2101   
 2102           String resourceName = path;
 2103           int lastSlash = path.lastIndexOf('/');
 2104           if (lastSlash != -1)
 2105               resourceName = resourceName.substring(lastSlash + 1);
 2106   
 2107           switch (type) {
 2108   
 2109           case FIND_ALL_PROP :
 2110   
 2111               generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2112               generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2113   
 2114               generatedXML.writeProperty
 2115                   (null, "creationdate",
 2116                    getISOCreationDate(cacheEntry.attributes.getCreation()));
 2117               generatedXML.writeElement(null, "displayname", XMLWriter.OPENING);
 2118               generatedXML.writeData(resourceName);
 2119               generatedXML.writeElement(null, "displayname", XMLWriter.CLOSING);
 2120               if (cacheEntry.resource != null) {
 2121                   generatedXML.writeProperty
 2122                       (null, "getlastmodified", FastHttpDateFormat.formatDate
 2123                              (cacheEntry.attributes.getLastModified(), null));
 2124                   generatedXML.writeProperty
 2125                       (null, "getcontentlength",
 2126                        String.valueOf(cacheEntry.attributes.getContentLength()));
 2127                   String contentType = getServletContext().getMimeType
 2128                       (cacheEntry.name);
 2129                   if (contentType != null) {
 2130                       generatedXML.writeProperty(null, "getcontenttype",
 2131                                                  contentType);
 2132                   }
 2133                   generatedXML.writeProperty(null, "getetag",
 2134                                              getETag(cacheEntry.attributes));
 2135                   generatedXML.writeElement(null, "resourcetype",
 2136                                             XMLWriter.NO_CONTENT);
 2137               } else {
 2138                   generatedXML.writeElement(null, "resourcetype",
 2139                                             XMLWriter.OPENING);
 2140                   generatedXML.writeElement(null, "collection",
 2141                                             XMLWriter.NO_CONTENT);
 2142                   generatedXML.writeElement(null, "resourcetype",
 2143                                             XMLWriter.CLOSING);
 2144               }
 2145   
 2146               generatedXML.writeProperty(null, "source", "");
 2147   
 2148               String supportedLocks = "<lockentry>"
 2149                   + "<lockscope><exclusive/></lockscope>"
 2150                   + "<locktype><write/></locktype>"
 2151                   + "</lockentry>" + "<lockentry>"
 2152                   + "<lockscope><shared/></lockscope>"
 2153                   + "<locktype><write/></locktype>"
 2154                   + "</lockentry>";
 2155               generatedXML.writeElement(null, "supportedlock",
 2156                                         XMLWriter.OPENING);
 2157               generatedXML.writeText(supportedLocks);
 2158               generatedXML.writeElement(null, "supportedlock",
 2159                                         XMLWriter.CLOSING);
 2160   
 2161               generateLockDiscovery(path, generatedXML);
 2162   
 2163               generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2164               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2165               generatedXML.writeText(status);
 2166               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2167               generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2168   
 2169               break;
 2170   
 2171           case FIND_PROPERTY_NAMES :
 2172   
 2173               generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2174               generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2175   
 2176               generatedXML.writeElement(null, "creationdate",
 2177                                         XMLWriter.NO_CONTENT);
 2178               generatedXML.writeElement(null, "displayname",
 2179                                         XMLWriter.NO_CONTENT);
 2180               if (cacheEntry.resource != null) {
 2181                   generatedXML.writeElement(null, "getcontentlanguage",
 2182                                             XMLWriter.NO_CONTENT);
 2183                   generatedXML.writeElement(null, "getcontentlength",
 2184                                             XMLWriter.NO_CONTENT);
 2185                   generatedXML.writeElement(null, "getcontenttype",
 2186                                             XMLWriter.NO_CONTENT);
 2187                   generatedXML.writeElement(null, "getetag",
 2188                                             XMLWriter.NO_CONTENT);
 2189                   generatedXML.writeElement(null, "getlastmodified",
 2190                                             XMLWriter.NO_CONTENT);
 2191               }
 2192               generatedXML.writeElement(null, "resourcetype",
 2193                                         XMLWriter.NO_CONTENT);
 2194               generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT);
 2195               generatedXML.writeElement(null, "lockdiscovery",
 2196                                         XMLWriter.NO_CONTENT);
 2197   
 2198               generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2199               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2200               generatedXML.writeText(status);
 2201               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2202               generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2203   
 2204               break;
 2205   
 2206           case FIND_BY_PROPERTY :
 2207   
 2208               Vector<String> propertiesNotFound = new Vector<String>();
 2209   
 2210               // Parse the list of properties
 2211   
 2212               generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2213               generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2214   
 2215               Enumeration<String> properties = propertiesVector.elements();
 2216   
 2217               while (properties.hasMoreElements()) {
 2218   
 2219                   String property = (String) properties.nextElement();
 2220   
 2221                   if (property.equals("creationdate")) {
 2222                       generatedXML.writeProperty
 2223                           (null, "creationdate",
 2224                            getISOCreationDate(cacheEntry.attributes.getCreation()));
 2225                   } else if (property.equals("displayname")) {
 2226                       generatedXML.writeElement
 2227                           (null, "displayname", XMLWriter.OPENING);
 2228                       generatedXML.writeData(resourceName);
 2229                       generatedXML.writeElement
 2230                           (null, "displayname", XMLWriter.CLOSING);
 2231                   } else if (property.equals("getcontentlanguage")) {
 2232                       if (cacheEntry.context != null) {
 2233                           propertiesNotFound.addElement(property);
 2234                       } else {
 2235                           generatedXML.writeElement(null, "getcontentlanguage",
 2236                                                     XMLWriter.NO_CONTENT);
 2237                       }
 2238                   } else if (property.equals("getcontentlength")) {
 2239                       if (cacheEntry.context != null) {
 2240                           propertiesNotFound.addElement(property);
 2241                       } else {
 2242                           generatedXML.writeProperty
 2243                               (null, "getcontentlength",
 2244                                (String.valueOf(cacheEntry.attributes.getContentLength())));
 2245                       }
 2246                   } else if (property.equals("getcontenttype")) {
 2247                       if (cacheEntry.context != null) {
 2248                           propertiesNotFound.addElement(property);
 2249                       } else {
 2250                           generatedXML.writeProperty
 2251                               (null, "getcontenttype",
 2252                                getServletContext().getMimeType
 2253                                (cacheEntry.name));
 2254                       }
 2255                   } else if (property.equals("getetag")) {
 2256                       if (cacheEntry.context != null) {
 2257                           propertiesNotFound.addElement(property);
 2258                       } else {
 2259                           generatedXML.writeProperty
 2260                               (null, "getetag", getETag(cacheEntry.attributes));
 2261                       }
 2262                   } else if (property.equals("getlastmodified")) {
 2263                       if (cacheEntry.context != null) {
 2264                           propertiesNotFound.addElement(property);
 2265                       } else {
 2266                           generatedXML.writeProperty
 2267                               (null, "getlastmodified", FastHttpDateFormat.formatDate
 2268                                       (cacheEntry.attributes.getLastModified(), null));
 2269                       }
 2270                   } else if (property.equals("resourcetype")) {
 2271                       if (cacheEntry.context != null) {
 2272                           generatedXML.writeElement(null, "resourcetype",
 2273                                                     XMLWriter.OPENING);
 2274                           generatedXML.writeElement(null, "collection",
 2275                                                     XMLWriter.NO_CONTENT);
 2276                           generatedXML.writeElement(null, "resourcetype",
 2277                                                     XMLWriter.CLOSING);
 2278                       } else {
 2279                           generatedXML.writeElement(null, "resourcetype",
 2280                                                     XMLWriter.NO_CONTENT);
 2281                       }
 2282                   } else if (property.equals("source")) {
 2283                       generatedXML.writeProperty(null, "source", "");
 2284                   } else if (property.equals("supportedlock")) {
 2285                       supportedLocks = "<lockentry>"
 2286                           + "<lockscope><exclusive/></lockscope>"
 2287                           + "<locktype><write/></locktype>"
 2288                           + "</lockentry>" + "<lockentry>"
 2289                           + "<lockscope><shared/></lockscope>"
 2290                           + "<locktype><write/></locktype>"
 2291                           + "</lockentry>";
 2292                       generatedXML.writeElement(null, "supportedlock",
 2293                                                 XMLWriter.OPENING);
 2294                       generatedXML.writeText(supportedLocks);
 2295                       generatedXML.writeElement(null, "supportedlock",
 2296                                                 XMLWriter.CLOSING);
 2297                   } else if (property.equals("lockdiscovery")) {
 2298                       if (!generateLockDiscovery(path, generatedXML))
 2299                           propertiesNotFound.addElement(property);
 2300                   } else {
 2301                       propertiesNotFound.addElement(property);
 2302                   }
 2303   
 2304               }
 2305   
 2306               generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2307               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2308               generatedXML.writeText(status);
 2309               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2310               generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2311   
 2312               Enumeration propertiesNotFoundList = propertiesNotFound.elements();
 2313   
 2314               if (propertiesNotFoundList.hasMoreElements()) {
 2315   
 2316                   status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
 2317                                       + " " + WebdavStatus.getStatusText
 2318                                       (WebdavStatus.SC_NOT_FOUND));
 2319   
 2320                   generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2321                   generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2322   
 2323                   while (propertiesNotFoundList.hasMoreElements()) {
 2324                       generatedXML.writeElement
 2325                           (null, (String) propertiesNotFoundList.nextElement(),
 2326                            XMLWriter.NO_CONTENT);
 2327                   }
 2328   
 2329                   generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2330                   generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2331                   generatedXML.writeText(status);
 2332                   generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2333                   generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2334   
 2335               }
 2336   
 2337               break;
 2338   
 2339           }
 2340   
 2341           generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
 2342   
 2343       }
 2344   
 2345   
 2346       /**
 2347        * Propfind helper method. Dispays the properties of a lock-null resource.
 2348        *
 2349        * @param resources Resources object associated with this context
 2350        * @param generatedXML XML response to the Propfind request
 2351        * @param path Path of the current resource
 2352        * @param type Propfind type
 2353        * @param propertiesVector If the propfind type is find properties by
 2354        * name, then this Vector contains those properties
 2355        */
 2356       private void parseLockNullProperties(HttpServletRequest req,
 2357                                            XMLWriter generatedXML,
 2358                                            String path, int type,
 2359                                            Vector propertiesVector) {
 2360   
 2361           // Exclude any resource in the /WEB-INF and /META-INF subdirectories
 2362           // (the "toUpperCase()" avoids problems on Windows systems)
 2363           if (path.toUpperCase().startsWith("/WEB-INF") ||
 2364               path.toUpperCase().startsWith("/META-INF"))
 2365               return;
 2366   
 2367           // Retrieving the lock associated with the lock-null resource
 2368           LockInfo lock = (LockInfo) resourceLocks.get(path);
 2369   
 2370           if (lock == null)
 2371               return;
 2372   
 2373           generatedXML.writeElement(null, "response", XMLWriter.OPENING);
 2374           String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
 2375                                      + WebdavStatus.getStatusText
 2376                                      (WebdavStatus.SC_OK));
 2377   
 2378           // Generating href element
 2379           generatedXML.writeElement(null, "href", XMLWriter.OPENING);
 2380   
 2381           String absoluteUri = req.getRequestURI();
 2382           String relativePath = getRelativePath(req);
 2383           String toAppend = path.substring(relativePath.length());
 2384           if (!toAppend.startsWith("/"))
 2385               toAppend = "/" + toAppend;
 2386   
 2387           generatedXML.writeText(rewriteUrl(normalize(absoluteUri + toAppend)));
 2388   
 2389           generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
 2390   
 2391           String resourceName = path;
 2392           int lastSlash = path.lastIndexOf('/');
 2393           if (lastSlash != -1)
 2394               resourceName = resourceName.substring(lastSlash + 1);
 2395   
 2396           switch (type) {
 2397   
 2398           case FIND_ALL_PROP :
 2399   
 2400               generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2401               generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2402   
 2403               generatedXML.writeProperty
 2404                   (null, "creationdate",
 2405                    getISOCreationDate(lock.creationDate.getTime()));
 2406               generatedXML.writeElement
 2407                   (null, "displayname", XMLWriter.OPENING);
 2408               generatedXML.writeData(resourceName);
 2409               generatedXML.writeElement
 2410                   (null, "displayname", XMLWriter.CLOSING);
 2411               generatedXML.writeProperty(null, "getlastmodified",
 2412                                          FastHttpDateFormat.formatDate
 2413                                          (lock.creationDate.getTime(), null));
 2414               generatedXML.writeProperty
 2415                   (null, "getcontentlength", String.valueOf(0));
 2416               generatedXML.writeProperty(null, "getcontenttype", "");
 2417               generatedXML.writeProperty(null, "getetag", "");
 2418               generatedXML.writeElement(null, "resourcetype",
 2419                                         XMLWriter.OPENING);
 2420               generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT);
 2421               generatedXML.writeElement(null, "resourcetype",
 2422                                         XMLWriter.CLOSING);
 2423   
 2424               generatedXML.writeProperty(null, "source", "");
 2425   
 2426               String supportedLocks = "<lockentry>"
 2427                   + "<lockscope><exclusive/></lockscope>"
 2428                   + "<locktype><write/></locktype>"
 2429                   + "</lockentry>" + "<lockentry>"
 2430                   + "<lockscope><shared/></lockscope>"
 2431                   + "<locktype><write/></locktype>"
 2432                   + "</lockentry>";
 2433               generatedXML.writeElement(null, "supportedlock",
 2434                                         XMLWriter.OPENING);
 2435               generatedXML.writeText(supportedLocks);
 2436               generatedXML.writeElement(null, "supportedlock",
 2437                                         XMLWriter.CLOSING);
 2438   
 2439               generateLockDiscovery(path, generatedXML);
 2440   
 2441               generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2442               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2443               generatedXML.writeText(status);
 2444               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2445               generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2446   
 2447               break;
 2448   
 2449           case FIND_PROPERTY_NAMES :
 2450   
 2451               generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2452               generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2453   
 2454               generatedXML.writeElement(null, "creationdate",
 2455                                         XMLWriter.NO_CONTENT);
 2456               generatedXML.writeElement(null, "displayname",
 2457                                         XMLWriter.NO_CONTENT);
 2458               generatedXML.writeElement(null, "getcontentlanguage",
 2459                                         XMLWriter.NO_CONTENT);
 2460               generatedXML.writeElement(null, "getcontentlength",
 2461                                         XMLWriter.NO_CONTENT);
 2462               generatedXML.writeElement(null, "getcontenttype",
 2463                                         XMLWriter.NO_CONTENT);
 2464               generatedXML.writeElement(null, "getetag",
 2465                                         XMLWriter.NO_CONTENT);
 2466               generatedXML.writeElement(null, "getlastmodified",
 2467                                         XMLWriter.NO_CONTENT);
 2468               generatedXML.writeElement(null, "resourcetype",
 2469                                         XMLWriter.NO_CONTENT);
 2470               generatedXML.writeElement(null, "source",
 2471                                         XMLWriter.NO_CONTENT);
 2472               generatedXML.writeElement(null, "lockdiscovery",
 2473                                         XMLWriter.NO_CONTENT);
 2474   
 2475               generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2476               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2477               generatedXML.writeText(status);
 2478               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2479               generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2480   
 2481               break;
 2482   
 2483           case FIND_BY_PROPERTY :
 2484   
 2485               Vector<String> propertiesNotFound = new Vector<String>();
 2486   
 2487               // Parse the list of properties
 2488   
 2489               generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2490               generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2491   
 2492               Enumeration properties = propertiesVector.elements();
 2493   
 2494               while (properties.hasMoreElements()) {
 2495   
 2496                   String property = (String) properties.nextElement();
 2497   
 2498                   if (property.equals("creationdate")) {
 2499                       generatedXML.writeProperty
 2500                           (null, "creationdate",
 2501                            getISOCreationDate(lock.creationDate.getTime()));
 2502                   } else if (property.equals("displayname")) {
 2503                       generatedXML.writeElement
 2504                           (null, "displayname", XMLWriter.OPENING);
 2505                       generatedXML.writeData(resourceName);
 2506                       generatedXML.writeElement
 2507                           (null, "displayname", XMLWriter.CLOSING);
 2508                   } else if (property.equals("getcontentlanguage")) {
 2509                       generatedXML.writeElement(null, "getcontentlanguage",
 2510                                                 XMLWriter.NO_CONTENT);
 2511                   } else if (property.equals("getcontentlength")) {
 2512                       generatedXML.writeProperty
 2513                           (null, "getcontentlength", (String.valueOf(0)));
 2514                   } else if (property.equals("getcontenttype")) {
 2515                       generatedXML.writeProperty
 2516                           (null, "getcontenttype", "");
 2517                   } else if (property.equals("getetag")) {
 2518                       generatedXML.writeProperty(null, "getetag", "");
 2519                   } else if (property.equals("getlastmodified")) {
 2520                       generatedXML.writeProperty
 2521                           (null, "getlastmodified",
 2522                             FastHttpDateFormat.formatDate
 2523                            (lock.creationDate.getTime(), null));
 2524                   } else if (property.equals("resourcetype")) {
 2525                       generatedXML.writeElement(null, "resourcetype",
 2526                                                 XMLWriter.OPENING);
 2527                       generatedXML.writeElement(null, "lock-null",
 2528                                                 XMLWriter.NO_CONTENT);
 2529                       generatedXML.writeElement(null, "resourcetype",
 2530                                                 XMLWriter.CLOSING);
 2531                   } else if (property.equals("source")) {
 2532                       generatedXML.writeProperty(null, "source", "");
 2533                   } else if (property.equals("supportedlock")) {
 2534                       supportedLocks = "<lockentry>"
 2535                           + "<lockscope><exclusive/></lockscope>"
 2536                           + "<locktype><write/></locktype>"
 2537                           + "</lockentry>" + "<lockentry>"
 2538                           + "<lockscope><shared/></lockscope>"
 2539                           + "<locktype><write/></locktype>"
 2540                           + "</lockentry>";
 2541                       generatedXML.writeElement(null, "supportedlock",
 2542                                                 XMLWriter.OPENING);
 2543                       generatedXML.writeText(supportedLocks);
 2544                       generatedXML.writeElement(null, "supportedlock",
 2545                                                 XMLWriter.CLOSING);
 2546                   } else if (property.equals("lockdiscovery")) {
 2547                       if (!generateLockDiscovery(path, generatedXML))
 2548                           propertiesNotFound.addElement(property);
 2549                   } else {
 2550                       propertiesNotFound.addElement(property);
 2551                   }
 2552   
 2553               }
 2554   
 2555               generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2556               generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2557               generatedXML.writeText(status);
 2558               generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2559               generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2560   
 2561               Enumeration propertiesNotFoundList = propertiesNotFound.elements();
 2562   
 2563               if (propertiesNotFoundList.hasMoreElements()) {
 2564   
 2565                   status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
 2566                                       + " " + WebdavStatus.getStatusText
 2567                                       (WebdavStatus.SC_NOT_FOUND));
 2568   
 2569                   generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
 2570                   generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
 2571   
 2572                   while (propertiesNotFoundList.hasMoreElements()) {
 2573                       generatedXML.writeElement
 2574                           (null, (String) propertiesNotFoundList.nextElement(),
 2575                            XMLWriter.NO_CONTENT);
 2576                   }
 2577   
 2578                   generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
 2579                   generatedXML.writeElement(null, "status", XMLWriter.OPENING);
 2580                   generatedXML.writeText(status);
 2581                   generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
 2582                   generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
 2583   
 2584               }
 2585   
 2586               break;
 2587   
 2588           }
 2589   
 2590           generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
 2591   
 2592       }
 2593   
 2594   
 2595       /**
 2596        * Print the lock discovery information associated with a path.
 2597        *
 2598        * @param path Path
 2599        * @param generatedXML XML data to which the locks info will be appended
 2600        * @return true if at least one lock was displayed
 2601        */
 2602       private boolean generateLockDiscovery
 2603           (String path, XMLWriter generatedXML) {
 2604   
 2605           LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
 2606           Enumeration collectionLocksList = collectionLocks.elements();
 2607   
 2608           boolean wroteStart = false;
 2609   
 2610           if (resourceLock != null) {
 2611               wroteStart = true;
 2612               generatedXML.writeElement(null, "lockdiscovery",
 2613                                         XMLWriter.OPENING);
 2614               resourceLock.toXML(generatedXML);
 2615           }
 2616   
 2617           while (collectionLocksList.hasMoreElements()) {
 2618               LockInfo currentLock =
 2619                   (LockInfo) collectionLocksList.nextElement();
 2620               if (path.startsWith(currentLock.path)) {
 2621                   if (!wroteStart) {
 2622                       wroteStart = true;
 2623                       generatedXML.writeElement(null, "lockdiscovery",
 2624                                                 XMLWriter.OPENING);
 2625                   }
 2626                   currentLock.toXML(generatedXML);
 2627               }
 2628           }
 2629   
 2630           if (wroteStart) {
 2631               generatedXML.writeElement(null, "lockdiscovery",
 2632                                         XMLWriter.CLOSING);
 2633           } else {
 2634               return false;
 2635           }
 2636   
 2637           return true;
 2638   
 2639       }
 2640   
 2641   
 2642       /**
 2643        * Get creation date in ISO format.
 2644        */
 2645       private String getISOCreationDate(long creationDate) {
 2646           StringBuffer creationDateValue = new StringBuffer
 2647               (creationDateFormat.format
 2648                (new Date(creationDate)));
 2649           /*
 2650           int offset = Calendar.getInstance().getTimeZone().getRawOffset()
 2651               / 3600000; // FIXME ?
 2652           if (offset < 0) {
 2653               creationDateValue.append("-");
 2654               offset = -offset;
 2655           } else if (offset > 0) {
 2656               creationDateValue.append("+");
 2657           }
 2658           if (offset != 0) {
 2659               if (offset < 10)
 2660                   creationDateValue.append("0");
 2661               creationDateValue.append(offset + ":00");
 2662           } else {
 2663               creationDateValue.append("Z");
 2664           }
 2665           */
 2666           return creationDateValue.toString();
 2667       }
 2668   
 2669       /**
 2670        * Determines the methods normally allowed for the resource.
 2671        *
 2672        */
 2673       private StringBuffer determineMethodsAllowed(DirContext resources,
 2674                                                    HttpServletRequest req) {
 2675   
 2676           StringBuffer methodsAllowed = new StringBuffer();
 2677           boolean exists = true;
 2678           Object object = null;
 2679           try {
 2680               String path = getRelativePath(req);
 2681   
 2682               object = resources.lookup(path);
 2683           } catch (NamingException e) {
 2684               exists = false;
 2685           }
 2686   
 2687           if (!exists) {
 2688               methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK");
 2689               return methodsAllowed;
 2690           }
 2691   
 2692           methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
 2693           methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");
 2694   
 2695           if (listings) {
 2696               methodsAllowed.append(", PROPFIND");
 2697           }
 2698   
 2699           if (!(object instanceof DirContext)) {
 2700               methodsAllowed.append(", PUT");
 2701           }
 2702   
 2703           return methodsAllowed;
 2704       }
 2705   
 2706       // --------------------------------------------------  LockInfo Inner Class
 2707   
 2708   
 2709       /**
 2710        * Holds a lock information.
 2711        */
 2712       private class LockInfo {
 2713   
 2714   
 2715           // -------------------------------------------------------- Constructor
 2716   
 2717   
 2718           /**
 2719            * Constructor.
 2720            */
 2721           public LockInfo() {
 2722   
 2723           }
 2724   
 2725   
 2726           // ------------------------------------------------- Instance Variables
 2727   
 2728   
 2729           String path = "/";
 2730           String type = "write";
 2731           String scope = "exclusive";
 2732           int depth = 0;
 2733           String owner = "";
 2734           Vector<String> tokens = new Vector<String>();
 2735           long expiresAt = 0;
 2736           Date creationDate = new Date();
 2737   
 2738   
 2739           // ----------------------------------------------------- Public Methods
 2740   
 2741   
 2742           /**
 2743            * Get a String representation of this lock token.
 2744            */
 2745           public String toString() {
 2746   
 2747               String result =  "Type:" + type + "\n";
 2748               result += "Scope:" + scope + "\n";
 2749               result += "Depth:" + depth + "\n";
 2750               result += "Owner:" + owner + "\n";
 2751               result += "Expiration:"
 2752                   + FastHttpDateFormat.formatDate(expiresAt, null) + "\n";
 2753               Enumeration tokensList = tokens.elements();
 2754               while (tokensList.hasMoreElements()) {
 2755                   result += "Token:" + tokensList.nextElement() + "\n";
 2756               }
 2757               return result;
 2758   
 2759           }
 2760   
 2761   
 2762           /**
 2763            * Return true if the lock has expired.
 2764            */
 2765           public boolean hasExpired() {
 2766               return (System.currentTimeMillis() > expiresAt);
 2767           }
 2768   
 2769   
 2770           /**
 2771            * Return true if the lock is exclusive.
 2772            */
 2773           public boolean isExclusive() {
 2774   
 2775               return (scope.equals("exclusive"));
 2776   
 2777           }
 2778   
 2779   
 2780           /**
 2781            * Get an XML representation of this lock token. This method will
 2782            * append an XML fragment to the given XML writer.
 2783            */
 2784           public void toXML(XMLWriter generatedXML) {
 2785   
 2786               generatedXML.writeElement(null, "activelock", XMLWriter.OPENING);
 2787   
 2788               generatedXML.writeElement(null, "locktype", XMLWriter.OPENING);
 2789               generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT);
 2790               generatedXML.writeElement(null, "locktype", XMLWriter.CLOSING);
 2791   
 2792               generatedXML.writeElement(null, "lockscope", XMLWriter.OPENING);
 2793               generatedXML.writeElement(null, scope, XMLWriter.NO_CONTENT);
 2794               generatedXML.writeElement(null, "lockscope", XMLWriter.CLOSING);
 2795   
 2796               generatedXML.writeElement(null, "depth", XMLWriter.OPENING);
 2797               if (depth == INFINITY) {
 2798                   generatedXML.writeText("Infinity");
 2799               } else {
 2800                   generatedXML.writeText("0");
 2801               }
 2802               generatedXML.writeElement(null, "depth", XMLWriter.CLOSING);
 2803   
 2804               generatedXML.writeElement(null, "owner", XMLWriter.OPENING);
 2805               generatedXML.writeText(owner);
 2806               generatedXML.writeElement(null, "owner", XMLWriter.CLOSING);
 2807   
 2808               generatedXML.writeElement(null, "timeout", XMLWriter.OPENING);
 2809               long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
 2810               generatedXML.writeText("Second-" + timeout);
 2811               generatedXML.writeElement(null, "timeout", XMLWriter.CLOSING);
 2812   
 2813               generatedXML.writeElement(null, "locktoken", XMLWriter.OPENING);
 2814               Enumeration tokensList = tokens.elements();
 2815               while (tokensList.hasMoreElements()) {
 2816                   generatedXML.writeElement(null, "href", XMLWriter.OPENING);
 2817                   generatedXML.writeText("opaquelocktoken:"
 2818                                          + tokensList.nextElement());
 2819                   generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
 2820               }
 2821               generatedXML.writeElement(null, "locktoken", XMLWriter.CLOSING);
 2822   
 2823               generatedXML.writeElement(null, "activelock", XMLWriter.CLOSING);
 2824   
 2825           }
 2826   
 2827   
 2828       }
 2829   
 2830   
 2831       // --------------------------------------------- WebdavResolver Inner Class
 2832       /**
 2833        * Work around for XML parsers that don't fully respect
 2834        * {@link DocumentBuilderFactory#setExpandEntityReferences(false)}. External
 2835        * references are filtered out for security reasons. See CVE-2007-5461.
 2836        */
 2837       private class WebdavResolver implements EntityResolver {
 2838           private ServletContext context;
 2839           
 2840           public WebdavResolver(ServletContext theContext) {
 2841               context = theContext;
 2842           }
 2843        
 2844           public InputSource resolveEntity (String publicId, String systemId) {
 2845               context.log(sm.getString("webdavservlet.enternalEntityIgnored",
 2846                       publicId, systemId));
 2847               return new InputSource(
 2848                       new StringReader("Ignored external entity"));
 2849           }
 2850       }
 2851   };
 2852   
 2853   
 2854   // --------------------------------------------------------  WebdavStatus Class
 2855   
 2856   
 2857   /**
 2858    * Wraps the HttpServletResponse class to abstract the
 2859    * specific protocol used.  To support other protocols
 2860    * we would only need to modify this class and the
 2861    * WebDavRetCode classes.
 2862    *
 2863    * @author              Marc Eaddy
 2864    * @version             1.0, 16 Nov 1997
 2865    */
 2866   class WebdavStatus {
 2867   
 2868   
 2869       // ----------------------------------------------------- Instance Variables
 2870   
 2871   
 2872       /**
 2873        * This Hashtable contains the mapping of HTTP and WebDAV
 2874        * status codes to descriptive text.  This is a static
 2875        * variable.
 2876        */
 2877       private static Hashtable<Integer,String> mapStatusCodes =
 2878           new Hashtable<Integer,String>();
 2879   
 2880   
 2881       // ------------------------------------------------------ HTTP Status Codes
 2882   
 2883   
 2884       /**
 2885        * Status code (200) indicating the request succeeded normally.
 2886        */
 2887       public static final int SC_OK = HttpServletResponse.SC_OK;
 2888   
 2889   
 2890       /**
 2891        * Status code (201) indicating the request succeeded and created
 2892        * a new resource on the server.
 2893        */
 2894       public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
 2895   
 2896   
 2897       /**
 2898        * Status code (202) indicating that a request was accepted for
 2899        * processing, but was not completed.
 2900        */
 2901       public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
 2902   
 2903   
 2904       /**
 2905        * Status code (204) indicating that the request succeeded but that
 2906        * there was no new information to return.
 2907        */
 2908       public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
 2909   
 2910   
 2911       /**
 2912        * Status code (301) indicating that the resource has permanently
 2913        * moved to a new location, and that future references should use a
 2914        * new URI with their requests.
 2915        */
 2916       public static final int SC_MOVED_PERMANENTLY =
 2917           HttpServletResponse.SC_MOVED_PERMANENTLY;
 2918   
 2919   
 2920       /**
 2921        * Status code (302) indicating that the resource has temporarily
 2922        * moved to another location, but that future references should
 2923        * still use the original URI to access the resource.
 2924        */
 2925       public static final int SC_MOVED_TEMPORARILY =
 2926           HttpServletResponse.SC_MOVED_TEMPORARILY;
 2927   
 2928   
 2929       /**
 2930        * Status code (304) indicating that a conditional GET operation
 2931        * found that the resource was available and not modified.
 2932        */
 2933       public static final int SC_NOT_MODIFIED =
 2934           HttpServletResponse.SC_NOT_MODIFIED;
 2935   
 2936   
 2937       /**
 2938        * Status code (400) indicating the request sent by the client was
 2939        * syntactically incorrect.
 2940        */
 2941       public static final int SC_BAD_REQUEST =
 2942           HttpServletResponse.SC_BAD_REQUEST;
 2943   
 2944   
 2945       /**
 2946        * Status code (401) indicating that the request requires HTTP
 2947        * authentication.
 2948        */
 2949       public static final int SC_UNAUTHORIZED =
 2950           HttpServletResponse.SC_UNAUTHORIZED;
 2951   
 2952   
 2953       /**
 2954        * Status code (403) indicating the server understood the request
 2955        * but refused to fulfill it.
 2956        */
 2957       public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
 2958   
 2959   
 2960       /**
 2961        * Status code (404) indicating that the requested resource is not
 2962        * available.
 2963        */
 2964       public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
 2965   
 2966   
 2967       /**
 2968        * Status code (500) indicating an error inside the HTTP service
 2969        * which prevented it from fulfilling the request.
 2970        */
 2971       public static final int SC_INTERNAL_SERVER_ERROR =
 2972           HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
 2973   
 2974   
 2975       /**
 2976        * Status code (501) indicating the HTTP service does not support
 2977        * the functionality needed to fulfill the request.
 2978        */
 2979       public static final int SC_NOT_IMPLEMENTED =
 2980           HttpServletResponse.SC_NOT_IMPLEMENTED;
 2981   
 2982   
 2983       /**
 2984        * Status code (502) indicating that the HTTP server received an
 2985        * invalid response from a server it consulted when acting as a
 2986        * proxy or gateway.
 2987        */
 2988       public static final int SC_BAD_GATEWAY =
 2989           HttpServletResponse.SC_BAD_GATEWAY;
 2990   
 2991   
 2992       /**
 2993        * Status code (503) indicating that the HTTP service is
 2994        * temporarily overloaded, and unable to handle the request.
 2995        */
 2996       public static final int SC_SERVICE_UNAVAILABLE =
 2997           HttpServletResponse.SC_SERVICE_UNAVAILABLE;
 2998   
 2999   
 3000       /**
 3001        * Status code (100) indicating the client may continue with
 3002        * its request.  This interim response is used to inform the
 3003        * client that the initial part of the request has been
 3004        * received and has not yet been rejected by the server.
 3005        */
 3006       public static final int SC_CONTINUE = 100;
 3007   
 3008   
 3009       /**
 3010        * Status code (405) indicating the method specified is not
 3011        * allowed for the resource.
 3012        */
 3013       public static final int SC_METHOD_NOT_ALLOWED = 405;
 3014   
 3015   
 3016       /**
 3017        * Status code (409) indicating that the request could not be
 3018        * completed due to a conflict with the current state of the
 3019        * resource.
 3020        */
 3021       public static final int SC_CONFLICT = 409;
 3022   
 3023   
 3024       /**
 3025        * Status code (412) indicating the precondition given in one
 3026        * or more of the request-header fields evaluated to false
 3027        * when it was tested on the server.
 3028        */
 3029       public static final int SC_PRECONDITION_FAILED = 412;
 3030   
 3031   
 3032       /**
 3033        * Status code (413) indicating the server is refusing to
 3034        * process a request because the request entity is larger
 3035        * than the server is willing or able to process.
 3036        */
 3037       public static final int SC_REQUEST_TOO_LONG = 413;
 3038   
 3039   
 3040       /**
 3041        * Status code (415) indicating the server is refusing to service
 3042        * the request because the entity of the request is in a format
 3043        * not supported by the requested resource for the requested
 3044        * method.
 3045        */
 3046       public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
 3047   
 3048   
 3049       // -------------------------------------------- Extended WebDav status code
 3050   
 3051   
 3052       /**
 3053        * Status code (207) indicating that the response requires
 3054        * providing status for multiple independent operations.
 3055        */
 3056       public static final int SC_MULTI_STATUS = 207;
 3057       // This one colides with HTTP 1.1
 3058       // "207 Parital Update OK"
 3059   
 3060   
 3061       /**
 3062        * Status code (418) indicating the entity body submitted with
 3063        * the PATCH method was not understood by the resource.
 3064        */
 3065       public static final int SC_UNPROCESSABLE_ENTITY = 418;
 3066       // This one colides with HTTP 1.1
 3067       // "418 Reauthentication Required"
 3068   
 3069   
 3070       /**
 3071        * Status code (419) indicating that the resource does not have
 3072        * sufficient space to record the state of the resource after the
 3073        * execution of this method.
 3074        */
 3075       public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
 3076       // This one colides with HTTP 1.1
 3077       // "419 Proxy Reauthentication Required"
 3078   
 3079   
 3080       /**
 3081        * Status code (420) indicating the method was not executed on
 3082        * a particular resource within its scope because some part of
 3083        * the method's execution failed causing the entire method to be
 3084        * aborted.
 3085        */
 3086       public static final int SC_METHOD_FAILURE = 420;
 3087   
 3088   
 3089       /**
 3090        * Status code (423) indicating the destination resource of a
 3091        * method is locked, and either the request did not contain a
 3092        * valid Lock-Info header, or the Lock-Info header identifies
 3093        * a lock held by another principal.
 3094        */
 3095       public static final int SC_LOCKED = 423;
 3096   
 3097   
 3098       // ------------------------------------------------------------ Initializer
 3099   
 3100   
 3101       static {
 3102           // HTTP 1.0 tatus Code
 3103           addStatusCodeMap(SC_OK, "OK");
 3104           addStatusCodeMap(SC_CREATED, "Created");
 3105           addStatusCodeMap(SC_ACCEPTED, "Accepted");
 3106           addStatusCodeMap(SC_NO_CONTENT, "No Content");
 3107           addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
 3108           addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
 3109           addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
 3110           addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
 3111           addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
 3112           addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
 3113           addStatusCodeMap(SC_NOT_FOUND, "Not Found");
 3114           addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
 3115           addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
 3116           addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
 3117           addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
 3118           addStatusCodeMap(SC_CONTINUE, "Continue");
 3119           addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
 3120           addStatusCodeMap(SC_CONFLICT, "Conflict");
 3121           addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
 3122           addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
 3123           addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
 3124           // WebDav Status Codes
 3125           addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
 3126           addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
 3127           addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
 3128                            "Insufficient Space On Resource");
 3129           addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
 3130           addStatusCodeMap(SC_LOCKED, "Locked");
 3131       }
 3132   
 3133   
 3134       // --------------------------------------------------------- Public Methods
 3135   
 3136   
 3137       /**
 3138        * Returns the HTTP status text for the HTTP or WebDav status code
 3139        * specified by looking it up in the static mapping.  This is a
 3140        * static function.
 3141        *
 3142        * @param   nHttpStatusCode [IN] HTTP or WebDAV status code
 3143        * @return  A string with a short descriptive phrase for the
 3144        *                  HTTP status code (e.g., "OK").
 3145        */
 3146       public static String getStatusText(int nHttpStatusCode) {
 3147           Integer intKey = new Integer(nHttpStatusCode);
 3148   
 3149           if (!mapStatusCodes.containsKey(intKey)) {
 3150               return "";
 3151           } else {
 3152               return (String) mapStatusCodes.get(intKey);
 3153           }
 3154       }
 3155   
 3156   
 3157       // -------------------------------------------------------- Private Methods
 3158   
 3159   
 3160       /**
 3161        * Adds a new status code -> status text mapping.  This is a static
 3162        * method because the mapping is a static variable.
 3163        *
 3164        * @param   nKey    [IN] HTTP or WebDAV status code
 3165        * @param   strVal  [IN] HTTP status text
 3166        */
 3167       private static void addStatusCodeMap(int nKey, String strVal) {
 3168           mapStatusCodes.put(new Integer(nKey), strVal);
 3169       }
 3170   
 3171   };
 3172   
 3173   

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » servlets » [javadoc | source]