KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > servlets > WebdavServlet


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 JavaDoc;
23 import java.io.StringWriter JavaDoc;
24 import java.io.Writer JavaDoc;
25 import java.security.MessageDigest JavaDoc;
26 import java.security.NoSuchAlgorithmException JavaDoc;
27 import java.text.SimpleDateFormat JavaDoc;
28 import java.util.Date JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.Hashtable JavaDoc;
31 import java.util.Stack JavaDoc;
32 import java.util.TimeZone JavaDoc;
33 import java.util.Vector JavaDoc;
34
35 import javax.naming.NameClassPair JavaDoc;
36 import javax.naming.NamingEnumeration JavaDoc;
37 import javax.naming.NamingException JavaDoc;
38 import javax.naming.directory.DirContext JavaDoc;
39 import javax.servlet.ServletException JavaDoc;
40 import javax.servlet.UnavailableException JavaDoc;
41 import javax.servlet.http.HttpServletRequest JavaDoc;
42 import javax.servlet.http.HttpServletResponse JavaDoc;
43 import javax.xml.parsers.DocumentBuilder JavaDoc;
44 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
45 import javax.xml.parsers.ParserConfigurationException JavaDoc;
46
47 import org.apache.catalina.util.DOMWriter;
48 import org.apache.catalina.util.MD5Encoder;
49 import org.apache.catalina.util.RequestUtil;
50 import org.apache.catalina.util.XMLWriter;
51 import org.apache.naming.resources.CacheEntry;
52 import org.apache.naming.resources.Resource;
53 import org.apache.naming.resources.ResourceAttributes;
54 import org.apache.tomcat.util.http.FastHttpDateFormat;
55 import org.w3c.dom.Document JavaDoc;
56 import org.w3c.dom.Element JavaDoc;
57 import org.w3c.dom.Node JavaDoc;
58 import org.w3c.dom.NodeList JavaDoc;
59 import org.xml.sax.InputSource JavaDoc;
60 import org.xml.sax.SAXException JavaDoc;
61
62
63
64 /**
65  * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
66  * are handled by the DefaultServlet.
67  *
68  * @author Remy Maucherat
69  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
70  */

71
72 public class WebdavServlet
73     extends DefaultServlet {
74
75
76     // -------------------------------------------------------------- Constants
77

78
79     private static final String JavaDoc METHOD_HEAD = "HEAD";
80     private static final String JavaDoc METHOD_PROPFIND = "PROPFIND";
81     private static final String JavaDoc METHOD_PROPPATCH = "PROPPATCH";
82     private static final String JavaDoc METHOD_MKCOL = "MKCOL";
83     private static final String JavaDoc METHOD_COPY = "COPY";
84     private static final String JavaDoc METHOD_MOVE = "MOVE";
85     private static final String JavaDoc METHOD_LOCK = "LOCK";
86     private static final String JavaDoc METHOD_UNLOCK = "UNLOCK";
87
88
89     /**
90      * Default depth is infite.
91      */

92     private static final int INFINITY = 3; // To limit tree browsing a bit
93

94
95     /**
96      * PROPFIND - Specify a property mask.
97      */

98     private static final int FIND_BY_PROPERTY = 0;
99
100
101     /**
102      * PROPFIND - Display all properties.
103      */

104     private static final int FIND_ALL_PROP = 1;
105
106
107     /**
108      * PROPFIND - Return property names.
109      */

110     private static final int FIND_PROPERTY_NAMES = 2;
111
112
113     /**
114      * Create a new lock.
115      */

116     private static final int LOCK_CREATION = 0;
117
118
119     /**
120      * Refresh lock.
121      */

122     private static final int LOCK_REFRESH = 1;
123
124
125     /**
126      * Default lock timeout value.
127      */

128     private static final int DEFAULT_TIMEOUT = 3600;
129
130
131     /**
132      * Maximum lock timeout.
133      */

134     private static final int MAX_TIMEOUT = 604800;
135
136
137     /**
138      * Default namespace.
139      */

140     protected static final String JavaDoc DEFAULT_NAMESPACE = "DAV:";
141
142
143     /**
144      * Simple date format for the creation date ISO representation (partial).
145      */

146     protected static final SimpleDateFormat JavaDoc creationDateFormat =
147         new SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss'Z'");
148
149
150      /**
151      * MD5 message digest provider.
152      */

153     protected static MessageDigest JavaDoc md5Helper;
154
155
156     /**
157      * The MD5 helper object for this class.
158      */

159     protected static final MD5Encoder md5Encoder = new MD5Encoder();
160
161
162
163     static {
164         creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
165     }
166
167
168     // ----------------------------------------------------- Instance Variables
169

170
171     /**
172      * Repository of the locks put on single resources.
173      * <p>
174      * Key : path <br>
175      * Value : LockInfo
176      */

177     private Hashtable JavaDoc resourceLocks = new Hashtable JavaDoc();
178
179
180     /**
181      * Repository of the lock-null resources.
182      * <p>
183      * Key : path of the collection containing the lock-null resource<br>
184      * Value : Vector of lock-null resource which are members of the
185      * collection. Each element of the Vector is the path associated with
186      * the lock-null resource.
187      */

188     private Hashtable JavaDoc lockNullResources = new Hashtable JavaDoc();
189
190
191     /**
192      * Vector of the heritable locks.
193      * <p>
194      * Key : path <br>
195      * Value : LockInfo
196      */

197     private Vector JavaDoc collectionLocks = new Vector JavaDoc();
198
199
200     /**
201      * Secret information used to generate reasonably secure lock ids.
202      */

203     private String JavaDoc secret = "catalina";
204
205
206     // --------------------------------------------------------- Public Methods
207

208
209     /**
210      * Initialize this servlet.
211      */

212     public void init()
213         throws ServletException JavaDoc {
214
215         super.init();
216
217         if (getServletConfig().getInitParameter("secret") != null)
218             secret = getServletConfig().getInitParameter("secret");
219
220         // Load the MD5 helper used to calculate signatures.
221
try {
222             md5Helper = MessageDigest.getInstance("MD5");
223         } catch (NoSuchAlgorithmException JavaDoc e) {
224             throw new UnavailableException JavaDoc("No MD5");
225         }
226
227     }
228
229
230     // ------------------------------------------------------ Protected Methods
231

232
233     /**
234      * Return JAXP document builder instance.
235      */

236     protected DocumentBuilder JavaDoc getDocumentBuilder()
237         throws ServletException JavaDoc {
238         DocumentBuilder JavaDoc documentBuilder = null;
239         DocumentBuilderFactory JavaDoc documentBuilderFactory = null;
240         try {
241             documentBuilderFactory = DocumentBuilderFactory.newInstance();
242             documentBuilderFactory.setNamespaceAware(true);
243             documentBuilder = documentBuilderFactory.newDocumentBuilder();
244         } catch(ParserConfigurationException JavaDoc e) {
245             throw new ServletException JavaDoc
246                 (sm.getString("webdavservlet.jaxpfailed"));
247         }
248         return documentBuilder;
249     }
250
251
252     /**
253      * Handles the special WebDAV methods.
254      */

255     protected void service(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
256         throws ServletException JavaDoc, IOException JavaDoc {
257
258         String JavaDoc method = req.getMethod();
259
260         if (debug > 0) {
261             String JavaDoc path = getRelativePath(req);
262             log("[" + method + "] " + path);
263         }
264
265         if (method.equals(METHOD_PROPFIND)) {
266             doPropfind(req, resp);
267         } else if (method.equals(METHOD_PROPPATCH)) {
268             doProppatch(req, resp);
269         } else if (method.equals(METHOD_MKCOL)) {
270             doMkcol(req, resp);
271         } else if (method.equals(METHOD_COPY)) {
272             doCopy(req, resp);
273         } else if (method.equals(METHOD_MOVE)) {
274             doMove(req, resp);
275         } else if (method.equals(METHOD_LOCK)) {
276             doLock(req, resp);
277         } else if (method.equals(METHOD_UNLOCK)) {
278             doUnlock(req, resp);
279         } else {
280             // DefaultServlet processing
281
super.service(req, resp);
282         }
283
284     }
285
286
287     /**
288      * Check if the conditions specified in the optional If headers are
289      * satisfied.
290      *
291      * @param request The servlet request we are processing
292      * @param response The servlet response we are creating
293      * @param resourceAttributes The resource information
294      * @return boolean true if the resource meets all the specified conditions,
295      * and false if any of the conditions is not satisfied, in which case
296      * request processing is stopped
297      */

298     protected boolean checkIfHeaders(HttpServletRequest JavaDoc request,
299                                      HttpServletResponse JavaDoc response,
300                                      ResourceAttributes resourceAttributes)
301         throws IOException JavaDoc {
302
303         if (!super.checkIfHeaders(request, response, resourceAttributes))
304             return false;
305
306         // TODO : Checking the WebDAV If header
307
return true;
308
309     }
310
311
312     /**
313      * OPTIONS Method.
314      *
315      * @param req The request
316      * @param resp The response
317      * @throws ServletException If an error occurs
318      * @throws IOException If an IO error occurs
319      */

320     protected void doOptions(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
321         throws ServletException JavaDoc, IOException JavaDoc {
322
323         resp.addHeader("DAV", "1,2");
324
325         StringBuffer JavaDoc methodsAllowed = determineMethodsAllowed(resources,
326                                                               req);
327
328         resp.addHeader("Allow", methodsAllowed.toString());
329         resp.addHeader("MS-Author-Via", "DAV");
330
331     }
332
333
334     /**
335      * PROPFIND Method.
336      */

337     protected void doPropfind(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
338         throws ServletException JavaDoc, IOException JavaDoc {
339
340         if (!listings) {
341             // Get allowed methods
342
StringBuffer JavaDoc methodsAllowed = determineMethodsAllowed(resources,
343                                                                   req);
344
345             resp.addHeader("Allow", methodsAllowed.toString());
346             resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
347             return;
348         }
349
350         String JavaDoc path = getRelativePath(req);
351         if (path.endsWith("/"))
352             path = path.substring(0, path.length() - 1);
353
354         if ((path.toUpperCase().startsWith("/WEB-INF")) ||
355             (path.toUpperCase().startsWith("/META-INF"))) {
356             resp.sendError(WebdavStatus.SC_FORBIDDEN);
357             return;
358         }
359
360         // Properties which are to be displayed.
361
Vector JavaDoc properties = null;
362         // Propfind depth
363
int depth = INFINITY;
364         // Propfind type
365
int type = FIND_ALL_PROP;
366
367         String JavaDoc depthStr = req.getHeader("Depth");
368
369         if (depthStr == null) {
370             depth = INFINITY;
371         } else {
372             if (depthStr.equals("0")) {
373                 depth = 0;
374             } else if (depthStr.equals("1")) {
375                 depth = 1;
376             } else if (depthStr.equals("infinity")) {
377                 depth = INFINITY;
378             }
379         }
380
381         Node JavaDoc propNode = null;
382
383         DocumentBuilder JavaDoc documentBuilder = getDocumentBuilder();
384
385         try {
386             Document JavaDoc document = documentBuilder.parse
387                 (new InputSource JavaDoc(req.getInputStream()));
388
389             // Get the root element of the document
390
Element JavaDoc rootElement = document.getDocumentElement();
391             NodeList JavaDoc childList = rootElement.getChildNodes();
392
393             for (int i=0; i < childList.getLength(); i++) {
394                 Node JavaDoc currentNode = childList.item(i);
395                 switch (currentNode.getNodeType()) {
396                 case Node.TEXT_NODE:
397                     break;
398                 case Node.ELEMENT_NODE:
399                     if (currentNode.getNodeName().endsWith("prop")) {
400                         type = FIND_BY_PROPERTY;
401                         propNode = currentNode;
402                     }
403                     if (currentNode.getNodeName().endsWith("propname")) {
404                         type = FIND_PROPERTY_NAMES;
405                     }
406                     if (currentNode.getNodeName().endsWith("allprop")) {
407                         type = FIND_ALL_PROP;
408                     }
409                     break;
410                 }
411             }
412         } catch (SAXException JavaDoc e) {
413             // Most likely there was no content : we use the defaults.
414
} catch (IOException JavaDoc e) {
415             // Most likely there was no content : we use the defaults.
416
}
417
418         if (type == FIND_BY_PROPERTY) {
419             properties = new Vector JavaDoc();
420             NodeList JavaDoc childList = propNode.getChildNodes();
421
422             for (int i=0; i < childList.getLength(); i++) {
423                 Node JavaDoc currentNode = childList.item(i);
424                 switch (currentNode.getNodeType()) {
425                 case Node.TEXT_NODE:
426                     break;
427                 case Node.ELEMENT_NODE:
428                     String JavaDoc nodeName = currentNode.getNodeName();
429                     String JavaDoc propertyName = null;
430                     if (nodeName.indexOf(':') != -1) {
431                         propertyName = nodeName.substring
432                             (nodeName.indexOf(':') + 1);
433                     } else {
434                         propertyName = nodeName;
435                     }
436                     // href is a live property which is handled differently
437
properties.addElement(propertyName);
438                     break;
439                 }
440             }
441
442         }
443
444         boolean exists = true;
445         Object JavaDoc object = null;
446         try {
447             object = resources.lookup(path);
448         } catch (NamingException JavaDoc e) {
449             exists = false;
450             int slash = path.lastIndexOf('/');
451             if (slash != -1) {
452                 String JavaDoc parentPath = path.substring(0, slash);
453                 Vector JavaDoc currentLockNullResources =
454                     (Vector JavaDoc) lockNullResources.get(parentPath);
455                 if (currentLockNullResources != null) {
456                     Enumeration JavaDoc lockNullResourcesList =
457                         currentLockNullResources.elements();
458                     while (lockNullResourcesList.hasMoreElements()) {
459                         String JavaDoc lockNullPath = (String JavaDoc)
460                             lockNullResourcesList.nextElement();
461                         if (lockNullPath.equals(path)) {
462                             resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
463                             resp.setContentType("text/xml; charset=UTF-8");
464                             // Create multistatus object
465
XMLWriter generatedXML =
466                                 new XMLWriter(resp.getWriter());
467                             generatedXML.writeXMLHeader();
468                             generatedXML.writeElement
469                                 (null, "multistatus"
470                                  + generateNamespaceDeclarations(),
471                                  XMLWriter.OPENING);
472                             parseLockNullProperties
473                                 (req, generatedXML, lockNullPath, type,
474                                  properties);
475                             generatedXML.writeElement(null, "multistatus",
476                                                       XMLWriter.CLOSING);
477                             generatedXML.sendData();
478                             return;
479                         }
480                     }
481                 }
482             }
483         }
484
485         if (!exists) {
486             resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
487             return;
488         }
489
490         resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
491
492         resp.setContentType("text/xml; charset=UTF-8");
493
494         // Create multistatus object
495
XMLWriter generatedXML = new XMLWriter(resp.getWriter());
496         generatedXML.writeXMLHeader();
497
498         generatedXML.writeElement(null, "multistatus"
499                                   + generateNamespaceDeclarations(),
500                                   XMLWriter.OPENING);
501
502         if (depth == 0) {
503             parseProperties(req, generatedXML, path, type,
504                             properties);
505         } else {
506             // The stack always contains the object of the current level
507
Stack JavaDoc stack = new Stack JavaDoc();
508             stack.push(path);
509
510             // Stack of the objects one level below
511
Stack JavaDoc stackBelow = new Stack JavaDoc();
512
513             while ((!stack.isEmpty()) && (depth >= 0)) {
514
515                 String JavaDoc currentPath = (String JavaDoc) stack.pop();
516                 parseProperties(req, generatedXML, currentPath,
517                                 type, properties);
518
519                 try {
520                     object = resources.lookup(currentPath);
521                 } catch (NamingException JavaDoc e) {
522                     continue;
523                 }
524
525                 if ((object instanceof DirContext JavaDoc) && (depth > 0)) {
526
527                     try {
528                         NamingEnumeration JavaDoc enumeration = resources.list(currentPath);
529                         while (enumeration.hasMoreElements()) {
530                             NameClassPair JavaDoc ncPair =
531                                 (NameClassPair JavaDoc) enumeration.nextElement();
532                             String JavaDoc newPath = currentPath;
533                             if (!(newPath.endsWith("/")))
534                                 newPath += "/";
535                             newPath += ncPair.getName();
536                             stackBelow.push(newPath);
537                         }
538                     } catch (NamingException JavaDoc e) {
539                         resp.sendError
540                             (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
541                              path);
542                         return;
543                     }
544
545                     // Displaying the lock-null resources present in that
546
// collection
547
String JavaDoc lockPath = currentPath;
548                     if (lockPath.endsWith("/"))
549                         lockPath =
550                             lockPath.substring(0, lockPath.length() - 1);
551                     Vector JavaDoc currentLockNullResources =
552                         (Vector JavaDoc) lockNullResources.get(lockPath);
553                     if (currentLockNullResources != null) {
554                         Enumeration JavaDoc lockNullResourcesList =
555                             currentLockNullResources.elements();
556                         while (lockNullResourcesList.hasMoreElements()) {
557                             String JavaDoc lockNullPath = (String JavaDoc)
558                                 lockNullResourcesList.nextElement();
559                             parseLockNullProperties
560                                 (req, generatedXML, lockNullPath, type,
561                                  properties);
562                         }
563                     }
564
565                 }
566
567                 if (stack.isEmpty()) {
568                     depth--;
569                     stack = stackBelow;
570                     stackBelow = new Stack JavaDoc();
571                 }
572
573                 generatedXML.sendData();
574
575             }
576         }
577
578         generatedXML.writeElement(null, "multistatus",
579                                   XMLWriter.CLOSING);
580
581         generatedXML.sendData();
582
583     }
584
585
586     /**
587      * PROPPATCH Method.
588      */

589     protected void doProppatch(HttpServletRequest JavaDoc req,
590                                HttpServletResponse JavaDoc resp)
591         throws ServletException JavaDoc, IOException JavaDoc {
592
593         if (readOnly) {
594             resp.sendError(WebdavStatus.SC_FORBIDDEN);
595             return;
596         }
597
598         if (isLocked(req)) {
599             resp.sendError(WebdavStatus.SC_LOCKED);
600             return;
601         }
602
603         resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
604
605     }
606
607
608     /**
609      * MKCOL Method.
610      */

611     protected void doMkcol(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
612         throws ServletException JavaDoc, IOException JavaDoc {
613
614         if (readOnly) {
615             resp.sendError(WebdavStatus.SC_FORBIDDEN);
616             return;
617         }
618
619         if (isLocked(req)) {
620             resp.sendError(WebdavStatus.SC_LOCKED);
621             return;
622         }
623
624         String JavaDoc path = getRelativePath(req);
625
626         if ((path.toUpperCase().startsWith("/WEB-INF")) ||
627             (path.toUpperCase().startsWith("/META-INF"))) {
628             resp.sendError(WebdavStatus.SC_FORBIDDEN);
629             return;
630         }
631
632         boolean exists = true;
633         Object JavaDoc object = null;
634         try {
635             object = resources.lookup(path);
636         } catch (NamingException JavaDoc e) {
637             exists = false;
638         }
639
640         // Can't create a collection if a resource already exists at the given
641
// path
642
if (exists) {
643             // Get allowed methods
644
StringBuffer JavaDoc methodsAllowed = determineMethodsAllowed(resources,
645                                                                   req);
646
647             resp.addHeader("Allow", methodsAllowed.toString());
648
649             resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
650             return;
651         }
652
653         if (req.getInputStream().available() > 0) {
654             DocumentBuilder JavaDoc documentBuilder = getDocumentBuilder();
655             try {
656                 Document JavaDoc document = documentBuilder.parse
657                     (new InputSource JavaDoc(req.getInputStream()));
658                 // TODO : Process this request body
659
resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
660                 return;
661
662             } catch(SAXException JavaDoc saxe) {
663                 // Parse error - assume invalid content
664
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
665                 return;
666             }
667         }
668
669         boolean result = true;
670         try {
671             resources.createSubcontext(path);
672         } catch (NamingException JavaDoc e) {
673             result = false;
674         }
675
676         if (!result) {
677             resp.sendError(WebdavStatus.SC_CONFLICT,
678                            WebdavStatus.getStatusText
679                            (WebdavStatus.SC_CONFLICT));
680         } else {
681             resp.setStatus(WebdavStatus.SC_CREATED);
682             // Removing any lock-null resource which would be present
683
lockNullResources.remove(path);
684         }
685
686     }
687
688
689     /**
690      * DELETE Method.
691      */

692     protected void doDelete(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
693         throws ServletException JavaDoc, IOException JavaDoc {
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         deleteResource(req, resp);
706
707     }
708
709
710     /**
711      * Process a POST request for the specified resource.
712      *
713      * @param req The servlet request we are processing
714      * @param resp The servlet response we are creating
715      *
716      * @exception IOException if an input/output error occurs
717      * @exception ServletException if a servlet-specified error occurs
718      */

719     protected void doPut(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
720         throws ServletException JavaDoc, IOException JavaDoc {
721
722         if (isLocked(req)) {
723             resp.sendError(WebdavStatus.SC_LOCKED);
724             return;
725         }
726
727         super.doPut(req, resp);
728
729         String JavaDoc path = getRelativePath(req);
730
731         // Removing any lock-null resource which would be present
732
lockNullResources.remove(path);
733
734     }
735
736     /**
737      * COPY Method.
738      */

739     protected void doCopy(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
740         throws ServletException JavaDoc, IOException JavaDoc {
741
742         if (readOnly) {
743             resp.sendError(WebdavStatus.SC_FORBIDDEN);
744             return;
745         }
746
747         copyResource(req, resp);
748
749     }
750
751
752     /**
753      * MOVE Method.
754      */

755     protected void doMove(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
756         throws ServletException JavaDoc, IOException JavaDoc {
757
758         if (readOnly) {
759             resp.sendError(WebdavStatus.SC_FORBIDDEN);
760             return;
761         }
762
763         if (isLocked(req)) {
764             resp.sendError(WebdavStatus.SC_LOCKED);
765             return;
766         }
767
768         String JavaDoc path = getRelativePath(req);
769
770         if (copyResource(req, resp)) {
771             deleteResource(path, req, resp, false);
772         }
773
774     }
775
776
777     /**
778      * LOCK Method.
779      */

780     protected void doLock(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
781         throws ServletException JavaDoc, IOException JavaDoc {
782
783         if (readOnly) {
784             resp.sendError(WebdavStatus.SC_FORBIDDEN);
785             return;
786         }
787
788         if (isLocked(req)) {
789             resp.sendError(WebdavStatus.SC_LOCKED);
790             return;
791         }
792
793         LockInfo lock = new LockInfo();
794
795         // Parsing lock request
796

797         // Parsing depth header
798

799         String JavaDoc depthStr = req.getHeader("Depth");
800
801         if (depthStr == null) {
802             lock.depth = INFINITY;
803         } else {
804             if (depthStr.equals("0")) {
805                 lock.depth = 0;
806             } else {
807                 lock.depth = INFINITY;
808             }
809         }
810
811         // Parsing timeout header
812

813         int lockDuration = DEFAULT_TIMEOUT;
814         String JavaDoc lockDurationStr = req.getHeader("Timeout");
815         if (lockDurationStr == null) {
816             lockDuration = DEFAULT_TIMEOUT;
817         } else {
818             int commaPos = lockDurationStr.indexOf(",");
819             // If multiple timeouts, just use the first
820
if (commaPos != -1) {
821                 lockDurationStr = lockDurationStr.substring(0,commaPos);
822             }
823             if (lockDurationStr.startsWith("Second-")) {
824                 lockDuration =
825                     (new Integer JavaDoc(lockDurationStr.substring(7))).intValue();
826             } else {
827                 if (lockDurationStr.equalsIgnoreCase("infinity")) {
828                     lockDuration = MAX_TIMEOUT;
829                 } else {
830                     try {
831                         lockDuration =
832                             (new Integer JavaDoc(lockDurationStr)).intValue();
833                     } catch (NumberFormatException JavaDoc e) {
834                         lockDuration = MAX_TIMEOUT;
835                     }
836                 }
837             }
838             if (lockDuration == 0) {
839                 lockDuration = DEFAULT_TIMEOUT;
840             }
841             if (lockDuration > MAX_TIMEOUT) {
842                 lockDuration = MAX_TIMEOUT;
843             }
844         }
845         lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
846
847         int lockRequestType = LOCK_CREATION;
848
849         Node JavaDoc lockInfoNode = null;
850
851         DocumentBuilder JavaDoc documentBuilder = getDocumentBuilder();
852
853         try {
854             Document JavaDoc document = documentBuilder.parse(new InputSource JavaDoc
855                 (req.getInputStream()));
856
857             // Get the root element of the document
858
Element JavaDoc rootElement = document.getDocumentElement();
859             lockInfoNode = rootElement;
860         } catch (IOException JavaDoc e) {
861             lockRequestType = LOCK_REFRESH;
862         } catch (SAXException JavaDoc e) {
863             lockRequestType = LOCK_REFRESH;
864         }
865
866         if (lockInfoNode != null) {
867
868             // Reading lock information
869

870             NodeList JavaDoc childList = lockInfoNode.getChildNodes();
871             StringWriter JavaDoc strWriter = null;
872             DOMWriter domWriter = null;
873
874             Node JavaDoc lockScopeNode = null;
875             Node JavaDoc lockTypeNode = null;
876             Node JavaDoc lockOwnerNode = null;
877
878             for (int i=0; i < childList.getLength(); i++) {
879                 Node JavaDoc currentNode = childList.item(i);
880                 switch (currentNode.getNodeType()) {
881                 case Node.TEXT_NODE:
882                     break;
883                 case Node.ELEMENT_NODE:
884                     String JavaDoc nodeName = currentNode.getNodeName();
885                     if (nodeName.endsWith("lockscope")) {
886                         lockScopeNode = currentNode;
887                     }
888                     if (nodeName.endsWith("locktype")) {
889                         lockTypeNode = currentNode;
890                     }
891                     if (nodeName.endsWith("owner")) {
892                         lockOwnerNode = currentNode;
893                     }
894                     break;
895                 }
896             }
897
898             if (lockScopeNode != null) {
899
900                 childList = lockScopeNode.getChildNodes();
901                 for (int i=0; i < childList.getLength(); i++) {
902                     Node JavaDoc currentNode = childList.item(i);
903                     switch (currentNode.getNodeType()) {
904                     case Node.TEXT_NODE:
905                         break;
906                     case Node.ELEMENT_NODE:
907                         String JavaDoc tempScope = currentNode.getNodeName();
908                         if (tempScope.indexOf(':') != -1) {
909                             lock.scope = tempScope.substring
910                                 (tempScope.indexOf(':') + 1);
911                         } else {
912                             lock.scope = tempScope;
913                         }
914                         break;
915                     }
916                 }
917
918                 if (lock.scope == null) {
919                     // Bad request
920
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
921                 }
922
923             } else {
924                 // Bad request
925
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
926             }
927
928             if (lockTypeNode != null) {
929
930                 childList = lockTypeNode.getChildNodes();
931                 for (int i=0; i < childList.getLength(); i++) {
932                     Node JavaDoc currentNode = childList.item(i);
933                     switch (currentNode.getNodeType()) {
934                     case Node.TEXT_NODE:
935                         break;
936                     case Node.ELEMENT_NODE:
937                         String JavaDoc tempType = currentNode.getNodeName();
938                         if (tempType.indexOf(':') != -1) {
939                             lock.type =
940                                 tempType.substring(tempType.indexOf(':') + 1);
941                         } else {
942                             lock.type = tempType;
943                         }
944                         break;
945                     }
946                 }
947
948                 if (lock.type == null) {
949                     // Bad request
950
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
951                 }
952
953             } else {
954                 // Bad request
955
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
956             }
957
958             if (lockOwnerNode != null) {
959
960                 childList = lockOwnerNode.getChildNodes();
961                 for (int i=0; i < childList.getLength(); i++) {
962                     Node JavaDoc currentNode = childList.item(i);
963                     switch (currentNode.getNodeType()) {
964                     case Node.TEXT_NODE:
965                         lock.owner += currentNode.getNodeValue();
966                         break;
967                     case Node.ELEMENT_NODE:
968                         strWriter = new StringWriter JavaDoc();
969                         domWriter = new DOMWriter(strWriter, true);
970                         domWriter.setQualifiedNames(false);
971                         domWriter.print(currentNode);
972                         lock.owner += strWriter.toString();
973                         break;
974                     }
975                 }
976
977                 if (lock.owner == null) {
978                     // Bad request
979
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
980                 }
981
982             } else {
983                 lock.owner = new String JavaDoc();
984             }
985
986         }
987
988         String JavaDoc path = getRelativePath(req);
989
990         lock.path = path;
991
992         boolean exists = true;
993         Object JavaDoc object = null;
994         try {
995             object = resources.lookup(path);
996         } catch (NamingException JavaDoc e) {
997             exists = false;
998         }
999
1000        Enumeration JavaDoc locksList = null;
1001
1002        if (lockRequestType == LOCK_CREATION) {
1003
1004            // Generating lock id
1005
String JavaDoc lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
1006                + lock.scope + "-" + req.getUserPrincipal() + "-"
1007                + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
1008                + lock.expiresAt + "-" + System.currentTimeMillis() + "-"
1009                + secret;
1010            String JavaDoc lockToken =
1011                md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));
1012
1013            if ( (exists) && (object instanceof DirContext JavaDoc) &&
1014                 (lock.depth == INFINITY) ) {
1015
1016                // Locking a collection (and all its member resources)
1017

1018                // Checking if a child resource of this collection is
1019
// already locked
1020
Vector JavaDoc lockPaths = new Vector JavaDoc();
1021                locksList = collectionLocks.elements();
1022                while (locksList.hasMoreElements()) {
1023                    LockInfo currentLock = (LockInfo) locksList.nextElement();
1024                    if (currentLock.hasExpired()) {
1025                        resourceLocks.remove(currentLock.path);
1026                        continue;
1027                    }
1028                    if ( (currentLock.path.startsWith(lock.path)) &&
1029                         ((currentLock.isExclusive()) ||
1030                          (lock.isExclusive())) ) {
1031                        // A child collection of this collection is locked
1032
lockPaths.addElement(currentLock.path);
1033                    }
1034                }
1035                locksList = resourceLocks.elements();
1036                while (locksList.hasMoreElements()) {
1037                    LockInfo currentLock = (LockInfo) locksList.nextElement();
1038                    if (currentLock.hasExpired()) {
1039                        resourceLocks.remove(currentLock.path);
1040                        continue;
1041                    }
1042                    if ( (currentLock.path.startsWith(lock.path)) &&
1043                         ((currentLock.isExclusive()) ||
1044                          (lock.isExclusive())) ) {
1045                        // A child resource of this collection is locked
1046
lockPaths.addElement(currentLock.path);
1047                    }
1048                }
1049
1050                if (!lockPaths.isEmpty()) {
1051
1052                    // One of the child paths was locked
1053
// We generate a multistatus error report
1054

1055                    Enumeration JavaDoc lockPathsList = lockPaths.elements();
1056
1057                    resp.setStatus(WebdavStatus.SC_CONFLICT);
1058
1059                    XMLWriter generatedXML = new XMLWriter();
1060                    generatedXML.writeXMLHeader();
1061
1062                    generatedXML.writeElement
1063                        (null, "multistatus" + generateNamespaceDeclarations(),
1064                         XMLWriter.OPENING);
1065
1066                    while (lockPathsList.hasMoreElements()) {
1067                        generatedXML.writeElement(null, "response",
1068                                                  XMLWriter.OPENING);
1069                        generatedXML.writeElement(null, "href",
1070                                                  XMLWriter.OPENING);
1071                        generatedXML
1072                            .writeText((String JavaDoc) lockPathsList.nextElement());
1073                        generatedXML.writeElement(null, "href",
1074                                                  XMLWriter.CLOSING);
1075                        generatedXML.writeElement(null, "status",
1076                                                  XMLWriter.OPENING);
1077                        generatedXML
1078                            .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
1079                                       + " " + WebdavStatus
1080                                       .getStatusText(WebdavStatus.SC_LOCKED));
1081                        generatedXML.writeElement(null, "status",
1082                                                  XMLWriter.CLOSING);
1083
1084                        generatedXML.writeElement(null, "response",
1085                                                  XMLWriter.CLOSING);
1086                    }
1087
1088                    generatedXML.writeElement(null, "multistatus",
1089                                          XMLWriter.CLOSING);
1090
1091                    Writer JavaDoc writer = resp.getWriter();
1092                    writer.write(generatedXML.toString());
1093                    writer.close();
1094
1095                    return;
1096
1097                }
1098
1099                boolean addLock = true;
1100
1101                // Checking if there is already a shared lock on this path
1102
locksList = collectionLocks.elements();
1103                while (locksList.hasMoreElements()) {
1104
1105                    LockInfo currentLock = (LockInfo) locksList.nextElement();
1106                    if (currentLock.path.equals(lock.path)) {
1107
1108                        if (currentLock.isExclusive()) {
1109                            resp.sendError(WebdavStatus.SC_LOCKED);
1110                            return;
1111                        } else {
1112                            if (lock.isExclusive()) {
1113                                resp.sendError(WebdavStatus.SC_LOCKED);
1114                                return;
1115                            }
1116                        }
1117
1118                        currentLock.tokens.addElement(lockToken);
1119                        lock = currentLock;
1120                        addLock = false;
1121
1122                    }
1123
1124                }
1125
1126                if (addLock) {
1127                    lock.tokens.addElement(lockToken);
1128                    collectionLocks.addElement(lock);
1129                }
1130
1131            } else {
1132
1133                // Locking a single resource
1134

1135                // Retrieving an already existing lock on that resource
1136
LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path);
1137                if (presentLock != null) {
1138
1139                    if ((presentLock.isExclusive()) || (lock.isExclusive())) {
1140                        // If either lock is exclusive, the lock can't be
1141
// granted
1142
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
1143                        return;
1144                    } else {
1145                        presentLock.tokens.addElement(lockToken);
1146                        lock = presentLock;
1147                    }
1148
1149                } else {
1150
1151                    lock.tokens.addElement(lockToken);
1152                    resourceLocks.put(lock.path, lock);
1153
1154                    // Checking if a resource exists at this path
1155
exists = true;
1156                    try {
1157                        object = resources.lookup(path);
1158                    } catch (NamingException JavaDoc e) {
1159                        exists = false;
1160                    }
1161                    if (!exists) {
1162
1163                        // "Creating" a lock-null resource
1164
int slash = lock.path.lastIndexOf('/');
1165                        String JavaDoc parentPath = lock.path.substring(0, slash);
1166
1167                        Vector JavaDoc lockNulls =
1168                            (Vector JavaDoc) lockNullResources.get(parentPath);
1169                        if (lockNulls == null) {
1170                            lockNulls = new Vector JavaDoc();
1171                            lockNullResources.put(parentPath, lockNulls);
1172                        }
1173
1174                        lockNulls.addElement(lock.path);
1175
1176                    }
1177                    // Add the Lock-Token header as by RFC 2518 8.10.1
1178
// - only do this for newly created locks
1179
resp.addHeader("Lock-Token", "<opaquelocktoken:"
1180                                   + lockToken + ">");
1181                }
1182
1183            }
1184
1185        }
1186
1187        if (lockRequestType == LOCK_REFRESH) {
1188
1189            String JavaDoc ifHeader = req.getHeader("If");
1190            if (ifHeader == null)
1191                ifHeader = "";
1192
1193            // Checking resource locks
1194

1195            LockInfo toRenew = (LockInfo) resourceLocks.get(path);
1196            Enumeration JavaDoc tokenList = null;
1197            if (lock != null) {
1198
1199                // At least one of the tokens of the locks must have been given
1200

1201                tokenList = toRenew.tokens.elements();
1202                while (tokenList.hasMoreElements()) {
1203                    String JavaDoc token = (String JavaDoc) tokenList.nextElement();
1204                    if (ifHeader.indexOf(token) != -1) {
1205                        toRenew.expiresAt = lock.expiresAt;
1206                        lock = toRenew;
1207                    }
1208                }
1209
1210            }
1211
1212            // Checking inheritable collection locks
1213

1214            Enumeration JavaDoc collectionLocksList = collectionLocks.elements();
1215            while (collectionLocksList.hasMoreElements()) {
1216                toRenew = (LockInfo) collectionLocksList.nextElement();
1217                if (path.equals(toRenew.path)) {
1218
1219                    tokenList = toRenew.tokens.elements();
1220                    while (tokenList.hasMoreElements()) {
1221                        String JavaDoc token = (String JavaDoc) tokenList.nextElement();
1222                        if (ifHeader.indexOf(token) != -1) {
1223                            toRenew.expiresAt = lock.expiresAt;
1224                            lock = toRenew;
1225                        }
1226                    }
1227
1228                }
1229            }
1230
1231        }
1232
1233        // Set the status, then generate the XML response containing
1234
// the lock information
1235
XMLWriter generatedXML = new XMLWriter();
1236        generatedXML.writeXMLHeader();
1237        generatedXML.writeElement(null, "prop"
1238                                  + generateNamespaceDeclarations(),
1239                                  XMLWriter.OPENING);
1240
1241        generatedXML.writeElement(null, "lockdiscovery",
1242                                  XMLWriter.OPENING);
1243
1244        lock.toXML(generatedXML);
1245
1246        generatedXML.writeElement(null, "lockdiscovery",
1247                                  XMLWriter.CLOSING);
1248
1249        generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
1250
1251        resp.setStatus(WebdavStatus.SC_OK);
1252        resp.setContentType("text/xml; charset=UTF-8");
1253        Writer JavaDoc writer = resp.getWriter();
1254        writer.write(generatedXML.toString());
1255        writer.close();
1256
1257    }
1258
1259
1260    /**
1261     * UNLOCK Method.
1262     */

1263    protected void doUnlock(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
1264        throws ServletException JavaDoc, IOException JavaDoc {
1265
1266        if (readOnly) {
1267            resp.sendError(WebdavStatus.SC_FORBIDDEN);
1268            return;
1269        }
1270
1271        if (isLocked(req)) {
1272            resp.sendError(WebdavStatus.SC_LOCKED);
1273            return;
1274        }
1275
1276        String JavaDoc path = getRelativePath(req);
1277
1278        String JavaDoc lockTokenHeader = req.getHeader("Lock-Token");
1279        if (lockTokenHeader == null)
1280            lockTokenHeader = "";
1281
1282        // Checking resource locks
1283

1284        LockInfo lock = (LockInfo) resourceLocks.get(path);
1285        Enumeration JavaDoc tokenList = null;
1286        if (lock != null) {
1287
1288            // At least one of the tokens of the locks must have been given
1289

1290            tokenList = lock.tokens.elements();
1291            while (tokenList.hasMoreElements()) {
1292                String JavaDoc token = (String JavaDoc) tokenList.nextElement();
1293                if (lockTokenHeader.indexOf(token) != -1) {
1294                    lock.tokens.removeElement(token);
1295                }
1296            }
1297
1298            if (lock.tokens.isEmpty()) {
1299                resourceLocks.remove(path);
1300                // Removing any lock-null resource which would be present
1301
lockNullResources.remove(path);
1302            }
1303
1304        }
1305
1306        // Checking inheritable collection locks
1307

1308        Enumeration JavaDoc collectionLocksList = collectionLocks.elements();
1309        while (collectionLocksList.hasMoreElements()) {
1310            lock = (LockInfo) collectionLocksList.nextElement();
1311            if (path.equals(lock.path)) {
1312
1313                tokenList = lock.tokens.elements();
1314                while (tokenList.hasMoreElements()) {
1315                    String JavaDoc token = (String JavaDoc) tokenList.nextElement();
1316                    if (lockTokenHeader.indexOf(token) != -1) {
1317                        lock.tokens.removeElement(token);
1318                        break;
1319                    }
1320                }
1321
1322                if (lock.tokens.isEmpty()) {
1323                    collectionLocks.removeElement(lock);
1324                    // Removing any lock-null resource which would be present
1325
lockNullResources.remove(path);
1326                }
1327
1328            }
1329        }
1330
1331        resp.setStatus(WebdavStatus.SC_NO_CONTENT);
1332
1333    }
1334
1335    /**
1336     * Return a context-relative path, beginning with a "/", that represents
1337     * the canonical version of the specified path after ".." and "." elements
1338     * are resolved out. If the specified path attempts to go outside the
1339     * boundaries of the current context (i.e. too many ".." path elements
1340     * are present), return <code>null</code> instead.
1341     *
1342     * @param path Path to be normalized
1343     */

1344    protected String JavaDoc normalize(String JavaDoc path) {
1345
1346        if (path == null)
1347            return null;
1348
1349        // Create a place for the normalized path
1350
String JavaDoc normalized = path;
1351
1352        if (normalized == null)
1353            return (null);
1354
1355        if (normalized.equals("/."))
1356            return "/";
1357
1358        // Normalize the slashes and add leading slash if necessary
1359
if (normalized.indexOf('\\') >= 0)
1360            normalized = normalized.replace('\\', '/');
1361        if (!normalized.startsWith("/"))
1362            normalized = "/" + normalized;
1363
1364        // Resolve occurrences of "//" in the normalized path
1365
while (true) {
1366            int index = normalized.indexOf("//");
1367            if (index < 0)
1368                break;
1369            normalized = normalized.substring(0, index) +
1370                normalized.substring(index + 1);
1371        }
1372
1373        // Resolve occurrences of "/./" in the normalized path
1374
while (true) {
1375            int index = normalized.indexOf("/./");
1376            if (index < 0)
1377                break;
1378            normalized = normalized.substring(0, index) +
1379                normalized.substring(index + 2);
1380        }
1381
1382        // Resolve occurrences of "/../" in the normalized path
1383
while (true) {
1384            int index = normalized.indexOf("/../");
1385            if (index < 0)
1386                break;
1387            if (index == 0)
1388                return (null); // Trying to go outside our context
1389
int index2 = normalized.lastIndexOf('/', index - 1);
1390            normalized = normalized.substring(0, index2) +
1391                normalized.substring(index + 3);
1392        }
1393
1394        // Return the normalized path that we have completed
1395
return (normalized);
1396
1397    }
1398
1399
1400    // -------------------------------------------------------- Private Methods
1401

1402    /**
1403     * Generate the namespace declarations.
1404     */

1405    private String JavaDoc generateNamespaceDeclarations() {
1406        return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
1407    }
1408
1409
1410    /**
1411     * Check to see if a resource is currently write locked. The method
1412     * will look at the "If" header to make sure the client
1413     * has give the appropriate lock tokens.
1414     *
1415     * @param req Servlet request
1416     * @return boolean true if the resource is locked (and no appropriate
1417     * lock token has been found for at least one of the non-shared locks which
1418     * are present on the resource).
1419     */

1420    private boolean isLocked(HttpServletRequest JavaDoc req) {
1421
1422        String JavaDoc path = getRelativePath(req);
1423
1424        String JavaDoc ifHeader = req.getHeader("If");
1425        if (ifHeader == null)
1426            ifHeader = "";
1427
1428        String JavaDoc lockTokenHeader = req.getHeader("Lock-Token");
1429        if (lockTokenHeader == null)
1430            lockTokenHeader = "";
1431
1432        return isLocked(path, ifHeader + lockTokenHeader);
1433
1434    }
1435
1436
1437    /**
1438     * Check to see if a resource is currently write locked.
1439     *
1440     * @param path Path of the resource
1441     * @param ifHeader "If" HTTP header which was included in the request
1442     * @return boolean true if the resource is locked (and no appropriate
1443     * lock token has been found for at least one of the non-shared locks which
1444     * are present on the resource).
1445     */

1446    private boolean isLocked(String JavaDoc path, String JavaDoc ifHeader) {
1447
1448        // Checking resource locks
1449

1450        LockInfo lock = (LockInfo) resourceLocks.get(path);
1451        Enumeration JavaDoc tokenList = null;
1452        if ((lock != null) && (lock.hasExpired())) {
1453            resourceLocks.remove(path);
1454        } else if (lock != null) {
1455
1456            // At least one of the tokens of the locks must have been given
1457

1458            tokenList = lock.tokens.elements();
1459            boolean tokenMatch = false;
1460            while (tokenList.hasMoreElements()) {
1461                String JavaDoc token = (String JavaDoc) tokenList.nextElement();
1462                if (ifHeader.indexOf(token) != -1)
1463                    tokenMatch = true;
1464            }
1465            if (!tokenMatch)
1466                return true;
1467
1468        }
1469
1470        // Checking inheritable collection locks
1471

1472        Enumeration JavaDoc collectionLocksList = collectionLocks.elements();
1473        while (collectionLocksList.hasMoreElements()) {
1474            lock = (LockInfo) collectionLocksList.nextElement();
1475            if (lock.hasExpired()) {
1476                collectionLocks.removeElement(lock);
1477            } else if (path.startsWith(lock.path)) {
1478
1479                tokenList = lock.tokens.elements();
1480                boolean tokenMatch = false;
1481                while (tokenList.hasMoreElements()) {
1482                    String JavaDoc token = (String JavaDoc) tokenList.nextElement();
1483                    if (ifHeader.indexOf(token) != -1)
1484                        tokenMatch = true;
1485                }
1486                if (!tokenMatch)
1487                    return true;
1488
1489            }
1490        }
1491
1492        return false;
1493
1494    }
1495
1496
1497    /**
1498     * Copy a resource.
1499     *
1500     * @param req Servlet request
1501     * @param resp Servlet response
1502     * @return boolean true if the copy is successful
1503     */

1504    private boolean copyResource(HttpServletRequest JavaDoc req,
1505                                 HttpServletResponse JavaDoc resp)
1506        throws ServletException JavaDoc, IOException JavaDoc {
1507
1508        // Parsing destination header
1509

1510        String JavaDoc destinationPath = req.getHeader("Destination");
1511
1512        if (destinationPath == null) {
1513            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
1514            return false;
1515        }
1516
1517        // Remove url encoding from destination
1518
destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
1519
1520        int protocolIndex = destinationPath.indexOf("://");
1521        if (protocolIndex >= 0) {
1522            // if the Destination URL contains the protocol, we can safely
1523
// trim everything upto the first "/" character after "://"
1524
int firstSeparator =
1525                destinationPath.indexOf("/", protocolIndex + 4);
1526            if (firstSeparator < 0) {
1527                destinationPath = "/";
1528            } else {
1529                destinationPath = destinationPath.substring(firstSeparator);
1530            }
1531        } else {
1532            String JavaDoc hostName = req.getServerName();
1533            if ((hostName != null) && (destinationPath.startsWith(hostName))) {
1534                destinationPath = destinationPath.substring(hostName.length());
1535            }
1536
1537            int portIndex = destinationPath.indexOf(":");
1538            if (portIndex >= 0) {
1539                destinationPath = destinationPath.substring(portIndex);
1540            }
1541
1542            if (destinationPath.startsWith(":")) {
1543                int firstSeparator = destinationPath.indexOf("/");
1544                if (firstSeparator < 0) {
1545                    destinationPath = "/";
1546                } else {
1547                    destinationPath =
1548                        destinationPath.substring(firstSeparator);
1549                }
1550            }
1551        }
1552
1553        // Normalise destination path (remove '.' and '..')
1554
destinationPath = normalize(destinationPath);
1555
1556        String JavaDoc contextPath = req.getContextPath();
1557        if ((contextPath != null) &&
1558            (destinationPath.startsWith(contextPath))) {
1559            destinationPath = destinationPath.substring(contextPath.length());
1560        }
1561
1562        String JavaDoc pathInfo = req.getPathInfo();
1563        if (pathInfo != null) {
1564            String JavaDoc servletPath = req.getServletPath();
1565            if ((servletPath != null) &&
1566                (destinationPath.startsWith(servletPath))) {
1567                destinationPath = destinationPath
1568                    .substring(servletPath.length());
1569            }
1570        }
1571
1572        if (debug > 0)
1573            log("Dest path :" + destinationPath);
1574
1575        if ((destinationPath.toUpperCase().startsWith("/WEB-INF")) ||
1576            (destinationPath.toUpperCase().startsWith("/META-INF"))) {
1577            resp.sendError(WebdavStatus.SC_FORBIDDEN);
1578            return false;
1579        }
1580
1581        String JavaDoc path = getRelativePath(req);
1582
1583        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
1584            (path.toUpperCase().startsWith("/META-INF"))) {
1585            resp.sendError(WebdavStatus.SC_FORBIDDEN);
1586            return false;
1587        }
1588
1589        if (destinationPath.equals(path)) {
1590            resp.sendError(WebdavStatus.SC_FORBIDDEN);
1591            return false;
1592        }
1593
1594        // Parsing overwrite header
1595

1596        boolean overwrite = true;
1597        String JavaDoc overwriteHeader = req.getHeader("Overwrite");
1598
1599        if (overwriteHeader != null) {
1600            if (overwriteHeader.equalsIgnoreCase("T")) {
1601                overwrite = true;
1602            } else {
1603                overwrite = false;
1604            }
1605        }
1606
1607        // Overwriting the destination
1608

1609        boolean exists = true;
1610        try {
1611            resources.lookup(destinationPath);
1612        } catch (NamingException JavaDoc e) {
1613            exists = false;
1614        }
1615
1616        if (overwrite) {
1617
1618            // Delete destination resource, if it exists
1619
if (exists) {
1620                if (!deleteResource(destinationPath, req, resp, true)) {
1621                    return false;
1622                }
1623            } else {
1624                resp.setStatus(WebdavStatus.SC_CREATED);
1625            }
1626
1627        } else {
1628
1629            // If the destination exists, then it's a conflict
1630
if (exists) {
1631                resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
1632                return false;
1633            }
1634
1635        }
1636
1637        // Copying source to destination
1638

1639        Hashtable JavaDoc errorList = new Hashtable JavaDoc();
1640
1641        boolean result = copyResource(resources, errorList,
1642                                      path, destinationPath);
1643
1644        if ((!result) || (!errorList.isEmpty())) {
1645
1646            sendReport(req, resp, errorList);
1647            return false;
1648
1649        }
1650
1651        // Removing any lock-null resource which would be present at
1652
// the destination path
1653
lockNullResources.remove(destinationPath);
1654
1655        return true;
1656
1657    }
1658
1659
1660    /**
1661     * Copy a collection.
1662     *
1663     * @param resources Resources implementation to be used
1664     * @param errorList Hashtable containing the list of errors which occurred
1665     * during the copy operation
1666     * @param source Path of the resource to be copied
1667     * @param dest Destination path
1668     */

1669    private boolean copyResource(DirContext JavaDoc resources, Hashtable JavaDoc errorList,
1670                                 String JavaDoc source, String JavaDoc dest) {
1671
1672        if (debug > 1)
1673            log("Copy: " + source + " To: " + dest);
1674
1675        Object JavaDoc object = null;
1676        try {
1677            object = resources.lookup(source);
1678        } catch (NamingException JavaDoc e) {
1679        }
1680
1681        if (object instanceof DirContext JavaDoc) {
1682
1683            try {
1684                resources.createSubcontext(dest);
1685            } catch (NamingException JavaDoc e) {
1686                errorList.put
1687                    (dest, new Integer JavaDoc(WebdavStatus.SC_CONFLICT));
1688                return false;
1689            }
1690
1691            try {
1692                NamingEnumeration JavaDoc enumeration = resources.list(source);
1693                while (enumeration.hasMoreElements()) {
1694                    NameClassPair JavaDoc ncPair = (NameClassPair JavaDoc) enumeration.nextElement();
1695                    String JavaDoc childDest = dest;
1696                    if (!childDest.equals("/"))
1697                        childDest += "/";
1698                    childDest += ncPair.getName();
1699                    String JavaDoc childSrc = source;
1700                    if (!childSrc.equals("/"))
1701                        childSrc += "/";
1702                    childSrc += ncPair.getName();
1703                    copyResource(resources, errorList, childSrc, childDest);
1704                }
1705            } catch (NamingException JavaDoc e) {
1706                errorList.put
1707                    (dest, new Integer JavaDoc(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1708                return false;
1709            }
1710
1711        } else {
1712
1713            if (object instanceof Resource) {
1714                try {
1715                    resources.bind(dest, object);
1716                } catch (NamingException JavaDoc e) {
1717                    errorList.put
1718                        (source,
1719                         new Integer JavaDoc(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1720                    return false;
1721                }
1722            } else {
1723                errorList.put
1724                    (source,
1725                     new Integer JavaDoc(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1726                return false;
1727            }
1728
1729        }
1730
1731        return true;
1732
1733    }
1734
1735
1736    /**
1737     * Delete a resource.
1738     *
1739     * @param req Servlet request
1740     * @param resp Servlet response
1741     * @return boolean true if the copy is successful
1742     */

1743    private boolean deleteResource(HttpServletRequest JavaDoc req,
1744                                   HttpServletResponse JavaDoc resp)
1745        throws ServletException JavaDoc, IOException JavaDoc {
1746
1747        String JavaDoc path = getRelativePath(req);
1748
1749        return deleteResource(path, req, resp, true);
1750
1751    }
1752
1753
1754    /**
1755     * Delete a resource.
1756     *
1757     * @param path Path of the resource which is to be deleted
1758     * @param req Servlet request
1759     * @param resp Servlet response
1760     * @param setStatus Should the response status be set on successful
1761     * completion
1762     */

1763    private boolean deleteResource(String JavaDoc path, HttpServletRequest JavaDoc req,
1764                                   HttpServletResponse JavaDoc resp, boolean setStatus)
1765        throws ServletException JavaDoc, IOException JavaDoc {
1766
1767        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
1768            (path.toUpperCase().startsWith("/META-INF"))) {
1769            resp.sendError(WebdavStatus.SC_FORBIDDEN);
1770            return false;
1771        }
1772
1773        String JavaDoc ifHeader = req.getHeader("If");
1774        if (ifHeader == null)
1775            ifHeader = "";
1776
1777        String JavaDoc lockTokenHeader = req.getHeader("Lock-Token");
1778        if (lockTokenHeader == null)
1779            lockTokenHeader = "";
1780
1781        if (isLocked(path, ifHeader + lockTokenHeader)) {
1782            resp.sendError(WebdavStatus.SC_LOCKED);
1783            return false;
1784        }
1785
1786        boolean exists = true;
1787        Object JavaDoc object = null;
1788        try {
1789            object = resources.lookup(path);
1790        } catch (NamingException JavaDoc e) {
1791            exists = false;
1792        }
1793
1794        if (!exists) {
1795            resp.sendError(WebdavStatus.SC_NOT_FOUND);
1796            return false;
1797        }
1798
1799        boolean collection = (object instanceof DirContext JavaDoc);
1800
1801        if (!collection) {
1802            try {
1803                resources.unbind(path);
1804            } catch (NamingException JavaDoc e) {
1805                resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
1806                return false;
1807            }
1808        } else {
1809
1810            Hashtable JavaDoc errorList = new Hashtable JavaDoc();
1811
1812            deleteCollection(req, resources, path, errorList);
1813            try {
1814                resources.unbind(path);
1815            } catch (NamingException JavaDoc e) {
1816                errorList.put(path, new Integer JavaDoc
1817                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1818            }
1819
1820            if (!errorList.isEmpty()) {
1821
1822                sendReport(req, resp, errorList);
1823                return false;
1824
1825            }
1826
1827        }
1828        if (setStatus) {
1829            resp.setStatus(WebdavStatus.SC_NO_CONTENT);
1830        }
1831        return true;
1832
1833    }
1834
1835
1836    /**
1837     * Deletes a collection.
1838     *
1839     * @param resources Resources implementation associated with the context
1840     * @param path Path to the collection to be deleted
1841     * @param errorList Contains the list of the errors which occurred
1842     */

1843    private void deleteCollection(HttpServletRequest JavaDoc req,
1844                                  DirContext JavaDoc resources,
1845                                  String JavaDoc path, Hashtable JavaDoc errorList) {
1846
1847        if (debug > 1)
1848            log("Delete:" + path);
1849
1850        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
1851            (path.toUpperCase().startsWith("/META-INF"))) {
1852            errorList.put(path, new Integer JavaDoc(WebdavStatus.SC_FORBIDDEN));
1853            return;
1854        }
1855
1856        String JavaDoc ifHeader = req.getHeader("If");
1857        if (ifHeader == null)
1858            ifHeader = "";
1859
1860        String JavaDoc lockTokenHeader = req.getHeader("Lock-Token");
1861        if (lockTokenHeader == null)
1862            lockTokenHeader = "";
1863
1864        Enumeration JavaDoc enumeration = null;
1865        try {
1866            enumeration = resources.list(path);
1867        } catch (NamingException JavaDoc e) {
1868            errorList.put(path, new Integer JavaDoc
1869                (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1870            return;
1871        }
1872
1873        while (enumeration.hasMoreElements()) {
1874            NameClassPair JavaDoc ncPair = (NameClassPair JavaDoc) enumeration.nextElement();
1875            String JavaDoc childName = path;
1876            if (!childName.equals("/"))
1877                childName += "/";
1878            childName += ncPair.getName();
1879
1880            if (isLocked(childName, ifHeader + lockTokenHeader)) {
1881
1882                errorList.put(childName, new Integer JavaDoc(WebdavStatus.SC_LOCKED));
1883
1884            } else {
1885
1886                try {
1887                    Object JavaDoc object = resources.lookup(childName);
1888                    if (object instanceof DirContext JavaDoc) {
1889                        deleteCollection(req, resources, childName, errorList);
1890                    }
1891
1892                    try {
1893                        resources.unbind(childName);
1894                    } catch (NamingException JavaDoc e) {
1895                        if (!(object instanceof DirContext JavaDoc)) {
1896                            // If it's not a collection, then it's an unknown
1897
// error
1898
errorList.put
1899                                (childName, new Integer JavaDoc
1900                                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1901                        }
1902                    }
1903                } catch (NamingException JavaDoc e) {
1904                    errorList.put
1905                        (childName, new Integer JavaDoc
1906                            (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1907                }
1908            }
1909
1910        }
1911
1912    }
1913
1914
1915    /**
1916     * Send a multistatus element containing a complete error report to the
1917     * client.
1918     *
1919     * @param req Servlet request
1920     * @param resp Servlet response
1921     * @param errorList List of error to be displayed
1922     */

1923    private void sendReport(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp,
1924                            Hashtable JavaDoc errorList)
1925        throws ServletException JavaDoc, IOException JavaDoc {
1926
1927        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
1928
1929        String JavaDoc absoluteUri = req.getRequestURI();
1930        String JavaDoc relativePath = getRelativePath(req);
1931
1932        XMLWriter generatedXML = new XMLWriter();
1933        generatedXML.writeXMLHeader();
1934
1935        generatedXML.writeElement(null, "multistatus"
1936                                  + generateNamespaceDeclarations(),
1937                                  XMLWriter.OPENING);
1938
1939        Enumeration JavaDoc pathList = errorList.keys();
1940        while (pathList.hasMoreElements()) {
1941
1942            String JavaDoc errorPath = (String JavaDoc) pathList.nextElement();
1943            int errorCode = ((Integer JavaDoc) errorList.get(errorPath)).intValue();
1944
1945            generatedXML.writeElement(null, "response", XMLWriter.OPENING);
1946
1947            generatedXML.writeElement(null, "href", XMLWriter.OPENING);
1948            String JavaDoc toAppend = errorPath.substring(relativePath.length());
1949            if (!toAppend.startsWith("/"))
1950                toAppend = "/" + toAppend;
1951            generatedXML.writeText(absoluteUri + toAppend);
1952            generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
1953            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
1954            generatedXML
1955                .writeText("HTTP/1.1 " + errorCode + " "
1956                           + WebdavStatus.getStatusText(errorCode));
1957            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
1958
1959            generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
1960
1961        }
1962
1963        generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);
1964
1965        Writer JavaDoc writer = resp.getWriter();
1966        writer.write(generatedXML.toString());
1967        writer.close();
1968
1969    }
1970
1971
1972    /**
1973     * Propfind helper method.
1974     *
1975     * @param req The servlet request
1976     * @param resources Resources object associated with this context
1977     * @param generatedXML XML response to the Propfind request
1978     * @param path Path of the current resource
1979     * @param type Propfind type
1980     * @param propertiesVector If the propfind type is find properties by
1981     * name, then this Vector contains those properties
1982     */

1983    private void parseProperties(HttpServletRequest JavaDoc req,
1984                                 XMLWriter generatedXML,
1985                                 String JavaDoc path, int type,
1986                                 Vector JavaDoc propertiesVector) {
1987
1988        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
1989
// (the "toUpperCase()" avoids problems on Windows systems)
1990
if (path.toUpperCase().startsWith("/WEB-INF") ||
1991            path.toUpperCase().startsWith("/META-INF"))
1992            return;
1993
1994        CacheEntry cacheEntry = resources.lookupCache(path);
1995
1996        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
1997        String JavaDoc status = new String JavaDoc("HTTP/1.1 " + WebdavStatus.SC_OK + " "
1998                                   + WebdavStatus.getStatusText
1999                                   (WebdavStatus.SC_OK));
2000
2001        // Generating href element
2002
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
2003
2004        String JavaDoc href = req.getContextPath() + req.getServletPath();
2005        if ((href.endsWith("/")) && (path.startsWith("/")))
2006            href += path.substring(1);
2007        else
2008            href += path;
2009        if ((cacheEntry.context != null) && (!href.endsWith("/")))
2010            href += "/";
2011
2012        generatedXML.writeText(rewriteUrl(href));
2013
2014        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
2015
2016        String JavaDoc resourceName = path;
2017        int lastSlash = path.lastIndexOf('/');
2018        if (lastSlash != -1)
2019            resourceName = resourceName.substring(lastSlash + 1);
2020
2021        switch (type) {
2022
2023        case FIND_ALL_PROP :
2024
2025            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2026            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2027
2028            generatedXML.writeProperty
2029                (null, "creationdate",
2030                 getISOCreationDate(cacheEntry.attributes.getCreation()));
2031            generatedXML.writeElement(null, "displayname", XMLWriter.OPENING);
2032            generatedXML.writeData(resourceName);
2033            generatedXML.writeElement(null, "displayname", XMLWriter.CLOSING);
2034            if (cacheEntry.resource != null) {
2035                generatedXML.writeProperty
2036                    (null, "getlastmodified", FastHttpDateFormat.formatDate
2037                           (cacheEntry.attributes.getLastModified(), null));
2038                generatedXML.writeProperty
2039                    (null, "getcontentlength",
2040                     String.valueOf(cacheEntry.attributes.getContentLength()));
2041                String JavaDoc contentType = getServletContext().getMimeType
2042                    (cacheEntry.name);
2043                if (contentType != null) {
2044                    generatedXML.writeProperty(null, "getcontenttype",
2045                                               contentType);
2046                }
2047                generatedXML.writeProperty(null, "getetag",
2048                                           getETag(cacheEntry.attributes));
2049                generatedXML.writeElement(null, "resourcetype",
2050                                          XMLWriter.NO_CONTENT);
2051            } else {
2052                generatedXML.writeElement(null, "resourcetype",
2053                                          XMLWriter.OPENING);
2054                generatedXML.writeElement(null, "collection",
2055                                          XMLWriter.NO_CONTENT);
2056                generatedXML.writeElement(null, "resourcetype",
2057                                          XMLWriter.CLOSING);
2058            }
2059
2060            generatedXML.writeProperty(null, "source", "");
2061
2062            String JavaDoc supportedLocks = "<lockentry>"
2063                + "<lockscope><exclusive/></lockscope>"
2064                + "<locktype><write/></locktype>"
2065                + "</lockentry>" + "<lockentry>"
2066                + "<lockscope><shared/></lockscope>"
2067                + "<locktype><write/></locktype>"
2068                + "</lockentry>";
2069            generatedXML.writeElement(null, "supportedlock",
2070                                      XMLWriter.OPENING);
2071            generatedXML.writeText(supportedLocks);
2072            generatedXML.writeElement(null, "supportedlock",
2073                                      XMLWriter.CLOSING);
2074
2075            generateLockDiscovery(path, generatedXML);
2076
2077            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2078            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2079            generatedXML.writeText(status);
2080            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2081            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2082
2083            break;
2084
2085        case FIND_PROPERTY_NAMES :
2086
2087            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2088            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2089
2090            generatedXML.writeElement(null, "creationdate",
2091                                      XMLWriter.NO_CONTENT);
2092            generatedXML.writeElement(null, "displayname",
2093                                      XMLWriter.NO_CONTENT);
2094            if (cacheEntry.resource != null) {
2095                generatedXML.writeElement(null, "getcontentlanguage",
2096                                          XMLWriter.NO_CONTENT);
2097                generatedXML.writeElement(null, "getcontentlength",
2098                                          XMLWriter.NO_CONTENT);
2099                generatedXML.writeElement(null, "getcontenttype",
2100                                          XMLWriter.NO_CONTENT);
2101                generatedXML.writeElement(null, "getetag",
2102                                          XMLWriter.NO_CONTENT);
2103                generatedXML.writeElement(null, "getlastmodified",
2104                                          XMLWriter.NO_CONTENT);
2105            }
2106            generatedXML.writeElement(null, "resourcetype",
2107                                      XMLWriter.NO_CONTENT);
2108            generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT);
2109            generatedXML.writeElement(null, "lockdiscovery",
2110                                      XMLWriter.NO_CONTENT);
2111
2112            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2113            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2114            generatedXML.writeText(status);
2115            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2116            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2117
2118            break;
2119
2120        case FIND_BY_PROPERTY :
2121
2122            Vector JavaDoc propertiesNotFound = new Vector JavaDoc();
2123
2124            // Parse the list of properties
2125

2126            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2127            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2128
2129            Enumeration JavaDoc properties = propertiesVector.elements();
2130
2131            while (properties.hasMoreElements()) {
2132
2133                String JavaDoc property = (String JavaDoc) properties.nextElement();
2134
2135                if (property.equals("creationdate")) {
2136                    generatedXML.writeProperty
2137                        (null, "creationdate",
2138                         getISOCreationDate(cacheEntry.attributes.getCreation()));
2139                } else if (property.equals("displayname")) {
2140                    generatedXML.writeElement
2141                        (null, "displayname", XMLWriter.OPENING);
2142                    generatedXML.writeData(resourceName);
2143                    generatedXML.writeElement
2144                        (null, "displayname", XMLWriter.CLOSING);
2145                } else if (property.equals("getcontentlanguage")) {
2146                    if (cacheEntry.context != null) {
2147                        propertiesNotFound.addElement(property);
2148                    } else {
2149                        generatedXML.writeElement(null, "getcontentlanguage",
2150                                                  XMLWriter.NO_CONTENT);
2151                    }
2152                } else if (property.equals("getcontentlength")) {
2153                    if (cacheEntry.context != null) {
2154                        propertiesNotFound.addElement(property);
2155                    } else {
2156                        generatedXML.writeProperty
2157                            (null, "getcontentlength",
2158                             (String.valueOf(cacheEntry.attributes.getContentLength())));
2159                    }
2160                } else if (property.equals("getcontenttype")) {
2161                    if (cacheEntry.context != null) {
2162                        propertiesNotFound.addElement(property);
2163                    } else {
2164                        generatedXML.writeProperty
2165                            (null, "getcontenttype",
2166                             getServletContext().getMimeType
2167                             (cacheEntry.name));
2168                    }
2169                } else if (property.equals("getetag")) {
2170                    if (cacheEntry.context != null) {
2171                        propertiesNotFound.addElement(property);
2172                    } else {
2173                        generatedXML.writeProperty
2174                            (null, "getetag", getETag(cacheEntry.attributes));
2175                    }
2176                } else if (property.equals("getlastmodified")) {
2177                    if (cacheEntry.context != null) {
2178                        propertiesNotFound.addElement(property);
2179                    } else {
2180                        generatedXML.writeProperty
2181                            (null, "getlastmodified", FastHttpDateFormat.formatDate
2182                                    (cacheEntry.attributes.getLastModified(), null));
2183                    }
2184                } else if (property.equals("resourcetype")) {
2185                    if (cacheEntry.context != null) {
2186                        generatedXML.writeElement(null, "resourcetype",
2187                                                  XMLWriter.OPENING);
2188                        generatedXML.writeElement(null, "collection",
2189                                                  XMLWriter.NO_CONTENT);
2190                        generatedXML.writeElement(null, "resourcetype",
2191                                                  XMLWriter.CLOSING);
2192                    } else {
2193                        generatedXML.writeElement(null, "resourcetype",
2194                                                  XMLWriter.NO_CONTENT);
2195                    }
2196                } else if (property.equals("source")) {
2197                    generatedXML.writeProperty(null, "source", "");
2198                } else if (property.equals("supportedlock")) {
2199                    supportedLocks = "<lockentry>"
2200                        + "<lockscope><exclusive/></lockscope>"
2201                        + "<locktype><write/></locktype>"
2202                        + "</lockentry>" + "<lockentry>"
2203                        + "<lockscope><shared/></lockscope>"
2204                        + "<locktype><write/></locktype>"
2205                        + "</lockentry>";
2206                    generatedXML.writeElement(null, "supportedlock",
2207                                              XMLWriter.OPENING);
2208                    generatedXML.writeText(supportedLocks);
2209                    generatedXML.writeElement(null, "supportedlock",
2210                                              XMLWriter.CLOSING);
2211                } else if (property.equals("lockdiscovery")) {
2212                    if (!generateLockDiscovery(path, generatedXML))
2213                        propertiesNotFound.addElement(property);
2214                } else {
2215                    propertiesNotFound.addElement(property);
2216                }
2217
2218            }
2219
2220            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2221            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2222            generatedXML.writeText(status);
2223            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2224            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2225
2226            Enumeration JavaDoc propertiesNotFoundList = propertiesNotFound.elements();
2227
2228            if (propertiesNotFoundList.hasMoreElements()) {
2229
2230                status = new String JavaDoc("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
2231                                    + " " + WebdavStatus.getStatusText
2232                                    (WebdavStatus.SC_NOT_FOUND));
2233
2234                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2235                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2236
2237                while (propertiesNotFoundList.hasMoreElements()) {
2238                    generatedXML.writeElement
2239                        (null, (String JavaDoc) propertiesNotFoundList.nextElement(),
2240                         XMLWriter.NO_CONTENT);
2241                }
2242
2243                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2244                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2245                generatedXML.writeText(status);
2246                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2247                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2248
2249            }
2250
2251            break;
2252
2253        }
2254
2255        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
2256
2257    }
2258
2259
2260    /**
2261     * Propfind helper method. Dispays the properties of a lock-null resource.
2262     *
2263     * @param resources Resources object associated with this context
2264     * @param generatedXML XML response to the Propfind request
2265     * @param path Path of the current resource
2266     * @param type Propfind type
2267     * @param propertiesVector If the propfind type is find properties by
2268     * name, then this Vector contains those properties
2269     */

2270    private void parseLockNullProperties(HttpServletRequest JavaDoc req,
2271                                         XMLWriter generatedXML,
2272                                         String JavaDoc path, int type,
2273                                         Vector JavaDoc propertiesVector) {
2274
2275        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
2276
// (the "toUpperCase()" avoids problems on Windows systems)
2277
if (path.toUpperCase().startsWith("/WEB-INF") ||
2278            path.toUpperCase().startsWith("/META-INF"))
2279            return;
2280
2281        // Retrieving the lock associated with the lock-null resource
2282
LockInfo lock = (LockInfo) resourceLocks.get(path);
2283
2284        if (lock == null)
2285            return;
2286
2287        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
2288        String JavaDoc status = new String JavaDoc("HTTP/1.1 " + WebdavStatus.SC_OK + " "
2289                                   + WebdavStatus.getStatusText
2290                                   (WebdavStatus.SC_OK));
2291
2292        // Generating href element
2293
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
2294
2295        String JavaDoc absoluteUri = req.getRequestURI();
2296        String JavaDoc relativePath = getRelativePath(req);
2297        String JavaDoc toAppend = path.substring(relativePath.length());
2298        if (!toAppend.startsWith("/"))
2299            toAppend = "/" + toAppend;
2300
2301        generatedXML.writeText(rewriteUrl(normalize(absoluteUri + toAppend)));
2302
2303        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
2304
2305        String JavaDoc resourceName = path;
2306        int lastSlash = path.lastIndexOf('/');
2307        if (lastSlash != -1)
2308            resourceName = resourceName.substring(lastSlash + 1);
2309
2310        switch (type) {
2311
2312        case FIND_ALL_PROP :
2313
2314            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2315            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2316
2317            generatedXML.writeProperty
2318                (null, "creationdate",
2319                 getISOCreationDate(lock.creationDate.getTime()));
2320            generatedXML.writeElement
2321                (null, "displayname", XMLWriter.OPENING);
2322            generatedXML.writeData(resourceName);
2323            generatedXML.writeElement
2324                (null, "displayname", XMLWriter.CLOSING);
2325            generatedXML.writeProperty(null, "getlastmodified",
2326                                       FastHttpDateFormat.formatDate
2327                                       (lock.creationDate.getTime(), null));
2328            generatedXML.writeProperty
2329                (null, "getcontentlength", String.valueOf(0));
2330            generatedXML.writeProperty(null, "getcontenttype", "");
2331            generatedXML.writeProperty(null, "getetag", "");
2332            generatedXML.writeElement(null, "resourcetype",
2333                                      XMLWriter.OPENING);
2334            generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT);
2335            generatedXML.writeElement(null, "resourcetype",
2336                                      XMLWriter.CLOSING);
2337
2338            generatedXML.writeProperty(null, "source", "");
2339
2340            String JavaDoc supportedLocks = "<lockentry>"
2341                + "<lockscope><exclusive/></lockscope>"
2342                + "<locktype><write/></locktype>"
2343                + "</lockentry>" + "<lockentry>"
2344                + "<lockscope><shared/></lockscope>"
2345                + "<locktype><write/></locktype>"
2346                + "</lockentry>";
2347            generatedXML.writeElement(null, "supportedlock",
2348                                      XMLWriter.OPENING);
2349            generatedXML.writeText(supportedLocks);
2350            generatedXML.writeElement(null, "supportedlock",
2351                                      XMLWriter.CLOSING);
2352
2353            generateLockDiscovery(path, generatedXML);
2354
2355            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2356            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2357            generatedXML.writeText(status);
2358            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2359            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2360
2361            break;
2362
2363        case FIND_PROPERTY_NAMES :
2364
2365            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2366            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2367
2368            generatedXML.writeElement(null, "creationdate",
2369                                      XMLWriter.NO_CONTENT);
2370            generatedXML.writeElement(null, "displayname",
2371                                      XMLWriter.NO_CONTENT);
2372            generatedXML.writeElement(null, "getcontentlanguage",
2373                                      XMLWriter.NO_CONTENT);
2374            generatedXML.writeElement(null, "getcontentlength",
2375                                      XMLWriter.NO_CONTENT);
2376            generatedXML.writeElement(null, "getcontenttype",
2377                                      XMLWriter.NO_CONTENT);
2378            generatedXML.writeElement(null, "getetag",
2379                                      XMLWriter.NO_CONTENT);
2380            generatedXML.writeElement(null, "getlastmodified",
2381                                      XMLWriter.NO_CONTENT);
2382            generatedXML.writeElement(null, "resourcetype",
2383                                      XMLWriter.NO_CONTENT);
2384            generatedXML.writeElement(null, "source",
2385                                      XMLWriter.NO_CONTENT);
2386            generatedXML.writeElement(null, "lockdiscovery",
2387                                      XMLWriter.NO_CONTENT);
2388
2389            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2390            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2391            generatedXML.writeText(status);
2392            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2393            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2394
2395            break;
2396
2397        case FIND_BY_PROPERTY :
2398
2399            Vector JavaDoc propertiesNotFound = new Vector JavaDoc();
2400
2401            // Parse the list of properties
2402

2403            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2404            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2405
2406            Enumeration JavaDoc properties = propertiesVector.elements();
2407
2408            while (properties.hasMoreElements()) {
2409
2410                String JavaDoc property = (String JavaDoc) properties.nextElement();
2411
2412                if (property.equals("creationdate")) {
2413                    generatedXML.writeProperty
2414                        (null, "creationdate",
2415                         getISOCreationDate(lock.creationDate.getTime()));
2416                } else if (property.equals("displayname")) {
2417                    generatedXML.writeElement
2418                        (null, "displayname", XMLWriter.OPENING);
2419                    generatedXML.writeData(resourceName);
2420                    generatedXML.writeElement
2421                        (null, "displayname", XMLWriter.CLOSING);
2422                } else if (property.equals("getcontentlanguage")) {
2423                    generatedXML.writeElement(null, "getcontentlanguage",
2424                                              XMLWriter.NO_CONTENT);
2425                } else if (property.equals("getcontentlength")) {
2426                    generatedXML.writeProperty
2427                        (null, "getcontentlength", (String.valueOf(0)));
2428                } else if (property.equals("getcontenttype")) {
2429                    generatedXML.writeProperty
2430                        (null, "getcontenttype", "");
2431                } else if (property.equals("getetag")) {
2432                    generatedXML.writeProperty(null, "getetag", "");
2433                } else if (property.equals("getlastmodified")) {
2434                    generatedXML.writeProperty
2435                        (null, "getlastmodified",
2436                          FastHttpDateFormat.formatDate
2437                         (lock.creationDate.getTime(), null));
2438                } else if (property.equals("resourcetype")) {
2439                    generatedXML.writeElement(null, "resourcetype",
2440                                              XMLWriter.OPENING);
2441                    generatedXML.writeElement(null, "lock-null",
2442                                              XMLWriter.NO_CONTENT);
2443                    generatedXML.writeElement(null, "resourcetype",
2444                                              XMLWriter.CLOSING);
2445                } else if (property.equals("source")) {
2446                    generatedXML.writeProperty(null, "source", "");
2447                } else if (property.equals("supportedlock")) {
2448                    supportedLocks = "<lockentry>"
2449                        + "<lockscope><exclusive/></lockscope>"
2450                        + "<locktype><write/></locktype>"
2451                        + "</lockentry>" + "<lockentry>"
2452                        + "<lockscope><shared/></lockscope>"
2453                        + "<locktype><write/></locktype>"
2454                        + "</lockentry>";
2455                    generatedXML.writeElement(null, "supportedlock",
2456                                              XMLWriter.OPENING);
2457                    generatedXML.writeText(supportedLocks);
2458                    generatedXML.writeElement(null, "supportedlock",
2459                                              XMLWriter.CLOSING);
2460                } else if (property.equals("lockdiscovery")) {
2461                    if (!generateLockDiscovery(path, generatedXML))
2462                        propertiesNotFound.addElement(property);
2463                } else {
2464                    propertiesNotFound.addElement(property);
2465                }
2466
2467            }
2468
2469            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2470            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2471            generatedXML.writeText(status);
2472            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2473            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2474
2475            Enumeration JavaDoc propertiesNotFoundList = propertiesNotFound.elements();
2476
2477            if (propertiesNotFoundList.hasMoreElements()) {
2478
2479                status = new String JavaDoc("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
2480                                    + " " + WebdavStatus.getStatusText
2481                                    (WebdavStatus.SC_NOT_FOUND));
2482
2483                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
2484                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2485
2486                while (propertiesNotFoundList.hasMoreElements()) {
2487                    generatedXML.writeElement
2488                        (null, (String JavaDoc) propertiesNotFoundList.nextElement(),
2489                         XMLWriter.NO_CONTENT);
2490                }
2491
2492                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2493                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
2494                generatedXML.writeText(status);
2495                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
2496                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
2497
2498            }
2499
2500            break;
2501
2502        }
2503
2504        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
2505
2506    }
2507
2508
2509    /**
2510     * Print the lock discovery information associated with a path.
2511     *
2512     * @param path Path
2513     * @param generatedXML XML data to which the locks info will be appended
2514     * @return true if at least one lock was displayed
2515     */

2516    private boolean generateLockDiscovery
2517        (String JavaDoc path, XMLWriter generatedXML) {
2518
2519        LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
2520        Enumeration JavaDoc collectionLocksList = collectionLocks.elements();
2521
2522        boolean wroteStart = false;
2523
2524        if (resourceLock != null) {
2525            wroteStart = true;
2526            generatedXML.writeElement(null, "lockdiscovery",
2527                                      XMLWriter.OPENING);
2528            resourceLock.toXML(generatedXML);
2529        }
2530
2531        while (collectionLocksList.hasMoreElements()) {
2532            LockInfo currentLock =
2533                (LockInfo) collectionLocksList.nextElement();
2534            if (path.startsWith(currentLock.path)) {
2535                if (!wroteStart) {
2536                    wroteStart = true;
2537                    generatedXML.writeElement(null, "lockdiscovery",
2538                                              XMLWriter.OPENING);
2539                }
2540                currentLock.toXML(generatedXML);
2541            }
2542        }
2543
2544        if (wroteStart) {
2545            generatedXML.writeElement(null, "lockdiscovery",
2546                                      XMLWriter.CLOSING);
2547        } else {
2548            return false;
2549        }
2550
2551        return true;
2552
2553    }
2554
2555
2556    /**
2557     * Get creation date in ISO format.
2558     */

2559    private String JavaDoc getISOCreationDate(long creationDate) {
2560        StringBuffer JavaDoc creationDateValue = new StringBuffer JavaDoc
2561            (creationDateFormat.format
2562             (new Date JavaDoc(creationDate)));
2563        /*
2564        int offset = Calendar.getInstance().getTimeZone().getRawOffset()
2565            / 3600000; // FIXME ?
2566        if (offset < 0) {
2567            creationDateValue.append("-");
2568            offset = -offset;
2569        } else if (offset > 0) {
2570            creationDateValue.append("+");
2571        }
2572        if (offset != 0) {
2573            if (offset < 10)
2574                creationDateValue.append("0");
2575            creationDateValue.append(offset + ":00");
2576        } else {
2577            creationDateValue.append("Z");
2578        }
2579        */

2580        return creationDateValue.toString();
2581    }
2582
2583    /**
2584     * Determines the methods normally allowed for the resource.
2585     *
2586     */

2587    private StringBuffer JavaDoc determineMethodsAllowed(DirContext JavaDoc resources,
2588                                                 HttpServletRequest JavaDoc req) {
2589
2590        StringBuffer JavaDoc methodsAllowed = new StringBuffer JavaDoc();
2591        boolean exists = true;
2592        Object JavaDoc object = null;
2593        try {
2594            String JavaDoc path = getRelativePath(req);
2595
2596            object = resources.lookup(path);
2597        } catch (NamingException JavaDoc e) {
2598            exists = false;
2599        }
2600
2601        if (!exists) {
2602            methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK");
2603            return methodsAllowed;
2604        }
2605
2606        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
2607        methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");
2608
2609        if (listings) {
2610            methodsAllowed.append(", PROPFIND");
2611        }
2612
2613        if (!(object instanceof DirContext JavaDoc)) {
2614            methodsAllowed.append(", PUT");
2615        }
2616
2617        return methodsAllowed;
2618    }
2619
2620    // -------------------------------------------------- LockInfo Inner Class
2621

2622
2623    /**
2624     * Holds a lock information.
2625     */

2626    private class LockInfo {
2627
2628
2629        // -------------------------------------------------------- Constructor
2630

2631
2632        /**
2633         * Constructor.
2634         */

2635        public LockInfo() {
2636
2637        }
2638
2639
2640        // ------------------------------------------------- Instance Variables
2641

2642
2643        String JavaDoc path = "/";
2644        String JavaDoc type = "write";
2645        String JavaDoc scope = "exclusive";
2646        int depth = 0;
2647        String JavaDoc owner = "";
2648        Vector JavaDoc tokens = new Vector JavaDoc();
2649        long expiresAt = 0;
2650        Date JavaDoc creationDate = new Date JavaDoc();
2651
2652
2653        // ----------------------------------------------------- Public Methods
2654

2655
2656        /**
2657         * Get a String representation of this lock token.
2658         */

2659        public String JavaDoc toString() {
2660
2661            String JavaDoc result = "Type:" + type + "\n";
2662            result += "Scope:" + scope + "\n";
2663            result += "Depth:" + depth + "\n";
2664            result += "Owner:" + owner + "\n";
2665            result += "Expiration:"
2666                + FastHttpDateFormat.formatDate(expiresAt, null) + "\n";
2667            Enumeration JavaDoc tokensList = tokens.elements();
2668            while (tokensList.hasMoreElements()) {
2669                result += "Token:" + tokensList.nextElement() + "\n";
2670            }
2671            return result;
2672
2673        }
2674
2675
2676        /**
2677         * Return true if the lock has expired.
2678         */

2679        public boolean hasExpired() {
2680            return (System.currentTimeMillis() > expiresAt);
2681        }
2682
2683
2684        /**
2685         * Return true if the lock is exclusive.
2686         */

2687        public boolean isExclusive() {
2688
2689            return (scope.equals("exclusive"));
2690
2691        }
2692
2693
2694        /**
2695         * Get an XML representation of this lock token. This method will
2696         * append an XML fragment to the given XML writer.
2697         */

2698        public void toXML(XMLWriter generatedXML) {
2699
2700            generatedXML.writeElement(null, "activelock", XMLWriter.OPENING);
2701
2702            generatedXML.writeElement(null, "locktype", XMLWriter.OPENING);
2703            generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT);
2704            generatedXML.writeElement(null, "locktype", XMLWriter.CLOSING);
2705
2706            generatedXML.writeElement(null, "lockscope", XMLWriter.OPENING);
2707            generatedXML.writeElement(null, scope, XMLWriter.NO_CONTENT);
2708            generatedXML.writeElement(null, "lockscope", XMLWriter.CLOSING);
2709
2710            generatedXML.writeElement(null, "depth", XMLWriter.OPENING);
2711            if (depth == INFINITY) {
2712                generatedXML.writeText("Infinity");
2713            } else {
2714                generatedXML.writeText("0");
2715            }
2716            generatedXML.writeElement(null, "depth", XMLWriter.CLOSING);
2717
2718            generatedXML.writeElement(null, "owner", XMLWriter.OPENING);
2719            generatedXML.writeText(owner);
2720            generatedXML.writeElement(null, "owner", XMLWriter.CLOSING);
2721
2722            generatedXML.writeElement(null, "timeout", XMLWriter.OPENING);
2723            long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
2724            generatedXML.writeText("Second-" + timeout);
2725            generatedXML.writeElement(null, "timeout", XMLWriter.CLOSING);
2726
2727            generatedXML.writeElement(null, "locktoken", XMLWriter.OPENING);
2728            Enumeration JavaDoc tokensList = tokens.elements();
2729            while (tokensList.hasMoreElements()) {
2730                generatedXML.writeElement(null, "href", XMLWriter.OPENING);
2731                generatedXML.writeText("opaquelocktoken:"
2732                                       + tokensList.nextElement());
2733                generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
2734            }
2735            generatedXML.writeElement(null, "locktoken", XMLWriter.CLOSING);
2736
2737            generatedXML.writeElement(null, "activelock", XMLWriter.CLOSING);
2738
2739        }
2740
2741
2742    }
2743
2744
2745    // --------------------------------------------------- Property Inner Class
2746

2747
2748    private class Property {
2749
2750        public String JavaDoc name;
2751        public String JavaDoc value;
2752        public String JavaDoc namespace;
2753        public String JavaDoc namespaceAbbrev;
2754        public int status = WebdavStatus.SC_OK;
2755
2756    }
2757
2758
2759};
2760
2761
2762// -------------------------------------------------------- WebdavStatus Class
2763

2764
2765/**
2766 * Wraps the HttpServletResponse class to abstract the
2767 * specific protocol used. To support other protocols
2768 * we would only need to modify this class and the
2769 * WebDavRetCode classes.
2770 *
2771 * @author Marc Eaddy
2772 * @version 1.0, 16 Nov 1997
2773 */

2774class WebdavStatus {
2775
2776
2777    // ----------------------------------------------------- Instance Variables
2778

2779
2780    /**
2781     * This Hashtable contains the mapping of HTTP and WebDAV
2782     * status codes to descriptive text. This is a static
2783     * variable.
2784     */

2785    private static Hashtable JavaDoc mapStatusCodes = new Hashtable JavaDoc();
2786
2787
2788    // ------------------------------------------------------ HTTP Status Codes
2789

2790
2791    /**
2792     * Status code (200) indicating the request succeeded normally.
2793     */

2794    public static final int SC_OK = HttpServletResponse.SC_OK;
2795
2796
2797    /**
2798     * Status code (201) indicating the request succeeded and created
2799     * a new resource on the server.
2800     */

2801    public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
2802
2803
2804    /**
2805     * Status code (202) indicating that a request was accepted for
2806     * processing, but was not completed.
2807     */

2808    public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
2809
2810
2811    /**
2812     * Status code (204) indicating that the request succeeded but that
2813     * there was no new information to return.
2814     */

2815    public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
2816
2817
2818    /**
2819     * Status code (301) indicating that the resource has permanently
2820     * moved to a new location, and that future references should use a
2821     * new URI with their requests.
2822     */

2823    public static final int SC_MOVED_PERMANENTLY =
2824        HttpServletResponse.SC_MOVED_PERMANENTLY;
2825
2826
2827    /**
2828     * Status code (302) indicating that the resource has temporarily
2829     * moved to another location, but that future references should
2830     * still use the original URI to access the resource.
2831     */

2832    public static final int SC_MOVED_TEMPORARILY =
2833        HttpServletResponse.SC_MOVED_TEMPORARILY;
2834
2835
2836    /**
2837     * Status code (304) indicating that a conditional GET operation
2838     * found that the resource was available and not modified.
2839     */

2840    public static final int SC_NOT_MODIFIED =
2841        HttpServletResponse.SC_NOT_MODIFIED;
2842
2843
2844    /**
2845     * Status code (400) indicating the request sent by the client was
2846     * syntactically incorrect.
2847     */

2848    public static final int SC_BAD_REQUEST =
2849        HttpServletResponse.SC_BAD_REQUEST;
2850
2851
2852    /**
2853     * Status code (401) indicating that the request requires HTTP
2854     * authentication.
2855     */

2856    public static final int SC_UNAUTHORIZED =
2857        HttpServletResponse.SC_UNAUTHORIZED;
2858
2859
2860    /**
2861     * Status code (403) indicating the server understood the request
2862     * but refused to fulfill it.
2863     */

2864    public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
2865
2866
2867    /**
2868     * Status code (404) indicating that the requested resource is not
2869     * available.
2870     */

2871    public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
2872
2873
2874    /**
2875     * Status code (500) indicating an error inside the HTTP service
2876     * which prevented it from fulfilling the request.
2877     */

2878    public static final int SC_INTERNAL_SERVER_ERROR =
2879        HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
2880
2881
2882    /**
2883     * Status code (501) indicating the HTTP service does not support
2884     * the functionality needed to fulfill the request.
2885     */

2886    public static final int SC_NOT_IMPLEMENTED =
2887        HttpServletResponse.SC_NOT_IMPLEMENTED;
2888
2889
2890    /**
2891     * Status code (502) indicating that the HTTP server received an
2892     * invalid response from a server it consulted when acting as a
2893     * proxy or gateway.
2894     */

2895    public static final int SC_BAD_GATEWAY =
2896        HttpServletResponse.SC_BAD_GATEWAY;
2897
2898
2899    /**
2900     * Status code (503) indicating that the HTTP service is
2901     * temporarily overloaded, and unable to handle the request.
2902     */

2903    public static final int SC_SERVICE_UNAVAILABLE =
2904        HttpServletResponse.SC_SERVICE_UNAVAILABLE;
2905
2906
2907    /**
2908     * Status code (100) indicating the client may continue with
2909     * its request. This interim response is used to inform the
2910     * client that the initial part of the request has been
2911     * received and has not yet been rejected by the server.
2912     */

2913    public static final int SC_CONTINUE = 100;
2914
2915
2916    /**
2917     * Status code (405) indicating the method specified is not
2918     * allowed for the resource.
2919     */

2920    public static final int SC_METHOD_NOT_ALLOWED = 405;
2921
2922
2923    /**
2924     * Status code (409) indicating that the request could not be
2925     * completed due to a conflict with the current state of the
2926     * resource.
2927     */

2928    public static final int SC_CONFLICT = 409;
2929
2930
2931    /**
2932     * Status code (412) indicating the precondition given in one
2933     * or more of the request-header fields evaluated to false
2934     * when it was tested on the server.
2935     */

2936    public static final int SC_PRECONDITION_FAILED = 412;
2937
2938
2939    /**
2940     * Status code (413) indicating the server is refusing to
2941     * process a request because the request entity is larger
2942     * than the server is willing or able to process.
2943     */

2944    public static final int SC_REQUEST_TOO_LONG = 413;
2945
2946
2947    /**
2948     * Status code (415) indicating the server is refusing to service
2949     * the request because the entity of the request is in a format
2950     * not supported by the requested resource for the requested
2951     * method.
2952     */

2953    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
2954
2955
2956    // -------------------------------------------- Extended WebDav status code
2957

2958
2959    /**
2960     * Status code (207) indicating that the response requires
2961     * providing status for multiple independent operations.
2962     */

2963    public static final int SC_MULTI_STATUS = 207;
2964    // This one colides with HTTP 1.1
2965
// "207 Parital Update OK"
2966

2967
2968    /**
2969     * Status code (418) indicating the entity body submitted with
2970     * the PATCH method was not understood by the resource.
2971     */

2972    public static final int SC_UNPROCESSABLE_ENTITY = 418;
2973    // This one colides with HTTP 1.1
2974
// "418 Reauthentication Required"
2975

2976
2977    /**
2978     * Status code (419) indicating that the resource does not have
2979     * sufficient space to record the state of the resource after the
2980     * execution of this method.
2981     */

2982    public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
2983    // This one colides with HTTP 1.1
2984
// "419 Proxy Reauthentication Required"
2985

2986
2987    /**
2988     * Status code (420) indicating the method was not executed on
2989     * a particular resource within its scope because some part of
2990     * the method's execution failed causing the entire method to be
2991     * aborted.
2992     */

2993    public static final int SC_METHOD_FAILURE = 420;
2994
2995
2996    /**
2997     * Status code (423) indicating the destination resource of a
2998     * method is locked, and either the request did not contain a
2999     * valid Lock-Info header, or the Lock-Info header identifies
3000     * a lock held by another principal.
3001     */

3002    public static final int SC_LOCKED = 423;
3003
3004
3005    // ------------------------------------------------------------ Initializer
3006

3007
3008    static {
3009        // HTTP 1.0 tatus Code
3010
addStatusCodeMap(SC_OK, "OK");
3011        addStatusCodeMap(SC_CREATED, "Created");
3012        addStatusCodeMap(SC_ACCEPTED, "Accepted");
3013        addStatusCodeMap(SC_NO_CONTENT, "No Content");
3014        addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
3015        addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
3016        addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
3017        addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
3018        addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
3019        addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
3020        addStatusCodeMap(SC_NOT_FOUND, "Not Found");
3021        addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
3022        addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
3023        addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
3024        addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
3025        addStatusCodeMap(SC_CONTINUE, "Continue");
3026        addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
3027        addStatusCodeMap(SC_CONFLICT, "Conflict");
3028        addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
3029        addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
3030        addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
3031        // WebDav Status Codes
3032
addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
3033        addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
3034        addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
3035                         "Insufficient Space On Resource");
3036        addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
3037        addStatusCodeMap(SC_LOCKED, "Locked");
3038    }
3039
3040
3041    // --------------------------------------------------------- Public Methods
3042

3043
3044    /**
3045     * Returns the HTTP status text for the HTTP or WebDav status code
3046     * specified by looking it up in the static mapping. This is a
3047     * static function.
3048     *
3049     * @param nHttpStatusCode [IN] HTTP or WebDAV status code
3050     * @return A string with a short descriptive phrase for the
3051     * HTTP status code (e.g., "OK").
3052     */

3053    public static String JavaDoc getStatusText(int nHttpStatusCode) {
3054        Integer JavaDoc intKey = new Integer JavaDoc(nHttpStatusCode);
3055
3056        if (!mapStatusCodes.containsKey(intKey)) {
3057            return "";
3058        } else {
3059            return (String JavaDoc) mapStatusCodes.get(intKey);
3060        }
3061    }
3062
3063
3064    // -------------------------------------------------------- Private Methods
3065

3066
3067    /**
3068     * Adds a new status code -> status text mapping. This is a static
3069     * method because the mapping is a static variable.
3070     *
3071     * @param nKey [IN] HTTP or WebDAV status code
3072     * @param strVal [IN] HTTP status text
3073     */

3074    private static void addStatusCodeMap(int nKey, String JavaDoc strVal) {
3075        mapStatusCodes.put(new Integer JavaDoc(nKey), strVal);
3076    }
3077
3078};
3079
3080
Popular Tags