0001: /*
0002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/WebdavServlet.java,v 1.28 2002/04/01 18:12:38 remm Exp $
0003: * $Revision: 1.28 $
0004: * $Date: 2002/04/01 18:12:38 $
0005: *
0006: * ====================================================================
0007: *
0008: * The Apache Software License, Version 1.1
0009: *
0010: * Copyright (c) 1999 The Apache Software Foundation. All rights
0011: * reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. Redistributions in binary form must reproduce the above copyright
0021: * notice, this list of conditions and the following disclaimer in
0022: * the documentation and/or other materials provided with the
0023: * distribution.
0024: *
0025: * 3. The end-user documentation included with the redistribution, if
0026: * any, must include the following acknowlegement:
0027: * "This product includes software developed by the
0028: * Apache Software Foundation (http://www.apache.org/)."
0029: * Alternately, this acknowlegement may appear in the software itself,
0030: * if and wherever such third-party acknowlegements normally appear.
0031: *
0032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0033: * Foundation" must not be used to endorse or promote products derived
0034: * from this software without prior written permission. For written
0035: * permission, please contact apache@apache.org.
0036: *
0037: * 5. Products derived from this software may not be called "Apache"
0038: * nor may "Apache" appear in their names without prior written
0039: * permission of the Apache Group.
0040: *
0041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0052: * SUCH DAMAGE.
0053: * ====================================================================
0054: *
0055: * This software consists of voluntary contributions made by many
0056: * individuals on behalf of the Apache Software Foundation. For more
0057: * information on the Apache Software Foundation, please see
0058: * <http://www.apache.org/>.
0059: *
0060: * [Additional notices, if required by prior licensing conditions]
0061: *
0062: */
0063:
0064: package org.apache.catalina.servlets;
0065:
0066: import java.io.BufferedInputStream;
0067: import java.io.File;
0068: import java.io.FileInputStream;
0069: import java.io.InputStream;
0070: import java.io.IOException;
0071: import java.io.PrintWriter;
0072: import java.io.StringWriter;
0073: import java.io.Writer;
0074: import java.net.MalformedURLException;
0075: import java.net.URL;
0076: import java.sql.Timestamp;
0077: import java.util.Date;
0078: import java.util.Enumeration;
0079: import java.util.Vector;
0080: import java.util.Stack;
0081: import java.util.StringTokenizer;
0082: import java.util.Locale;
0083: import java.util.Hashtable;
0084: import java.util.Calendar;
0085: import java.util.TimeZone;
0086: import java.text.ParseException;
0087: import java.text.SimpleDateFormat;
0088: import java.security.MessageDigest;
0089: import java.security.NoSuchAlgorithmException;
0090: import org.w3c.dom.Node;
0091: import org.w3c.dom.NodeList;
0092: import org.w3c.dom.Element;
0093: import org.w3c.dom.Document;
0094: import org.xml.sax.InputSource;
0095: import javax.servlet.RequestDispatcher;
0096: import javax.servlet.ServletException;
0097: import javax.servlet.ServletContext;
0098: import javax.servlet.ServletOutputStream;
0099: import javax.servlet.http.HttpServlet;
0100: import javax.servlet.http.HttpServletRequest;
0101: import javax.servlet.http.HttpServletResponse;
0102: import javax.xml.parsers.DocumentBuilder;
0103: import javax.xml.parsers.DocumentBuilderFactory;
0104: import javax.xml.parsers.ParserConfigurationException;
0105: import javax.naming.NamingException;
0106: import javax.naming.InitialContext;
0107: import javax.naming.Context;
0108: import javax.naming.NamingEnumeration;
0109: import javax.naming.NameClassPair;
0110: import javax.naming.directory.DirContext;
0111: import javax.naming.directory.Attribute;
0112: import javax.naming.directory.Attributes;
0113: import org.apache.naming.resources.Resource;
0114: import org.apache.naming.resources.ResourceAttributes;
0115: import org.apache.catalina.util.MD5Encoder;
0116: import org.apache.catalina.util.StringManager;
0117: import org.apache.catalina.util.XMLWriter;
0118: import org.apache.catalina.util.DOMWriter;
0119: import org.apache.catalina.util.RequestUtil;
0120:
0121: /**
0122: * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
0123: * are handled by the DefaultServlet.
0124: *
0125: * @author Remy Maucherat
0126: * @version $Revision: 1.28 $ $Date: 2002/04/01 18:12:38 $
0127: */
0128:
0129: public class WebdavServlet extends DefaultServlet {
0130:
0131: // -------------------------------------------------------------- Constants
0132:
0133: private static final String METHOD_HEAD = "HEAD";
0134: private static final String METHOD_PROPFIND = "PROPFIND";
0135: private static final String METHOD_PROPPATCH = "PROPPATCH";
0136: private static final String METHOD_MKCOL = "MKCOL";
0137: private static final String METHOD_COPY = "COPY";
0138: private static final String METHOD_MOVE = "MOVE";
0139: private static final String METHOD_LOCK = "LOCK";
0140: private static final String METHOD_UNLOCK = "UNLOCK";
0141:
0142: /**
0143: * Default depth is infite.
0144: */
0145: private static final int INFINITY = 3; // To limit tree browsing a bit
0146:
0147: /**
0148: * PROPFIND - Specify a property mask.
0149: */
0150: private static final int FIND_BY_PROPERTY = 0;
0151:
0152: /**
0153: * PROPFIND - Display all properties.
0154: */
0155: private static final int FIND_ALL_PROP = 1;
0156:
0157: /**
0158: * PROPFIND - Return property names.
0159: */
0160: private static final int FIND_PROPERTY_NAMES = 2;
0161:
0162: /**
0163: * Create a new lock.
0164: */
0165: private static final int LOCK_CREATION = 0;
0166:
0167: /**
0168: * Refresh lock.
0169: */
0170: private static final int LOCK_REFRESH = 1;
0171:
0172: /**
0173: * Default lock timeout value.
0174: */
0175: private static final int DEFAULT_TIMEOUT = 3600;
0176:
0177: /**
0178: * Maximum lock timeout.
0179: */
0180: private static final int MAX_TIMEOUT = 604800;
0181:
0182: /**
0183: * Default namespace.
0184: */
0185: protected static final String DEFAULT_NAMESPACE = "DAV:";
0186:
0187: /**
0188: * Simple date format for the creation date ISO representation (partial).
0189: */
0190: protected static final SimpleDateFormat creationDateFormat = new SimpleDateFormat(
0191: "yyyy-MM-dd'T'HH:mm:ss'Z'");
0192:
0193: static {
0194: creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
0195: }
0196:
0197: // ----------------------------------------------------- Instance Variables
0198:
0199: /**
0200: * Repository of the locks put on single resources.
0201: * <p>
0202: * Key : path <br>
0203: * Value : LockInfo
0204: */
0205: private Hashtable resourceLocks = new Hashtable();
0206:
0207: /**
0208: * Repository of the lock-null resources.
0209: * <p>
0210: * Key : path of the collection containing the lock-null resource<br>
0211: * Value : Vector of lock-null resource which are members of the
0212: * collection. Each element of the Vector is the path associated with
0213: * the lock-null resource.
0214: */
0215: private Hashtable lockNullResources = new Hashtable();
0216:
0217: /**
0218: * Vector of the heritable locks.
0219: * <p>
0220: * Key : path <br>
0221: * Value : LockInfo
0222: */
0223: private Vector collectionLocks = new Vector();
0224:
0225: /**
0226: * Secret information used to generate reasonably secure lock ids.
0227: */
0228: private String secret = "catalina";
0229:
0230: // --------------------------------------------------------- Public Methods
0231:
0232: /**
0233: * Initialize this servlet.
0234: */
0235: public void init() throws ServletException {
0236:
0237: super .init();
0238:
0239: String value = null;
0240: try {
0241: value = getServletConfig().getInitParameter("secret");
0242: if (value != null)
0243: secret = value;
0244: } catch (Throwable t) {
0245: ;
0246: }
0247:
0248: }
0249:
0250: // ------------------------------------------------------ Protected Methods
0251:
0252: /**
0253: * Return JAXP document builder instance.
0254: */
0255: protected DocumentBuilder getDocumentBuilder()
0256: throws ServletException {
0257: DocumentBuilder documentBuilder = null;
0258: try {
0259: documentBuilder = DocumentBuilderFactory.newInstance()
0260: .newDocumentBuilder();
0261: } catch (ParserConfigurationException e) {
0262: throw new ServletException(sm
0263: .getString("webdavservlet.jaxpfailed"));
0264: }
0265: return documentBuilder;
0266: }
0267:
0268: /**
0269: * Handles the special WebDAV methods.
0270: */
0271: protected void service(HttpServletRequest req,
0272: HttpServletResponse resp) throws ServletException,
0273: IOException {
0274:
0275: String method = req.getMethod();
0276:
0277: if (debug > 0) {
0278: String path = getRelativePath(req);
0279: System.out.println("[" + method + "] " + path);
0280: }
0281:
0282: if (method.equals(METHOD_PROPFIND)) {
0283: doPropfind(req, resp);
0284: } else if (method.equals(METHOD_PROPPATCH)) {
0285: doProppatch(req, resp);
0286: } else if (method.equals(METHOD_MKCOL)) {
0287: doMkcol(req, resp);
0288: } else if (method.equals(METHOD_COPY)) {
0289: doCopy(req, resp);
0290: } else if (method.equals(METHOD_MOVE)) {
0291: doMove(req, resp);
0292: } else if (method.equals(METHOD_LOCK)) {
0293: doLock(req, resp);
0294: } else if (method.equals(METHOD_UNLOCK)) {
0295: doUnlock(req, resp);
0296: } else {
0297: // DefaultServlet processing
0298: super .service(req, resp);
0299: }
0300:
0301: }
0302:
0303: /**
0304: * Check if the conditions specified in the optional If headers are
0305: * satisfied.
0306: *
0307: * @param request The servlet request we are processing
0308: * @param response The servlet response we are creating
0309: * @param resourceInfo File object
0310: * @return boolean true if the resource meets all the specified conditions,
0311: * and false if any of the conditions is not satisfied, in which case
0312: * request processing is stopped
0313: */
0314: protected boolean checkIfHeaders(HttpServletRequest request,
0315: HttpServletResponse response, ResourceInfo resourceInfo)
0316: throws IOException {
0317:
0318: if (!super .checkIfHeaders(request, response, resourceInfo))
0319: return false;
0320:
0321: // TODO : Checking the WebDAV If header
0322: return true;
0323:
0324: }
0325:
0326: /**
0327: * OPTIONS Method.
0328: */
0329: protected void doOptions(HttpServletRequest req,
0330: HttpServletResponse resp) throws ServletException,
0331: IOException {
0332:
0333: String path = getRelativePath(req);
0334:
0335: resp.addHeader("DAV", "1,2");
0336: String methodsAllowed = null;
0337:
0338: // Retrieve the resources
0339: DirContext resources = getResources();
0340:
0341: if (resources == null) {
0342: resp
0343: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0344: return;
0345: }
0346:
0347: boolean exists = true;
0348: Object object = null;
0349: try {
0350: object = resources.lookup(path);
0351: } catch (NamingException e) {
0352: exists = false;
0353: }
0354:
0355: if (!exists) {
0356: methodsAllowed = "OPTIONS, MKCOL, PUT, LOCK";
0357: resp.addHeader("Allow", methodsAllowed);
0358: return;
0359: }
0360:
0361: methodsAllowed = "OPTIONS, GET, HEAD, POST, DELETE, TRACE, "
0362: + "PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK";
0363: if (!(object instanceof DirContext)) {
0364: methodsAllowed += ", PUT";
0365: }
0366:
0367: resp.addHeader("Allow", methodsAllowed);
0368:
0369: resp.addHeader("MS-Author-Via", "DAV");
0370:
0371: }
0372:
0373: /**
0374: * PROPFIND Method.
0375: */
0376: protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
0377: throws ServletException, IOException {
0378:
0379: if (!listings) {
0380: resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
0381: return;
0382: }
0383:
0384: String path = getRelativePath(req);
0385: if (path.endsWith("/"))
0386: path = path.substring(0, path.length() - 1);
0387:
0388: if ((path.toUpperCase().startsWith("/WEB-INF")) ||
0389: (path.toUpperCase().startsWith("/META-INF"))) {
0390: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0391: return;
0392: }
0393:
0394: // Properties which are to be displayed.
0395: Vector properties = null;
0396: // Propfind depth
0397: int depth = INFINITY;
0398: // Propfind type
0399: int type = FIND_ALL_PROP;
0400:
0401: String depthStr = req.getHeader("Depth");
0402:
0403: if (depthStr == null) {
0404: depth = INFINITY;
0405: } else {
0406: if (depthStr.equals("0")) {
0407: depth = 0;
0408: } else if (depthStr.equals("1")) {
0409: depth = 1;
0410: } else if (depthStr.equals("infinity")) {
0411: depth = INFINITY;
0412: }
0413: }
0414:
0415: Node propNode = null;
0416:
0417: DocumentBuilder documentBuilder = getDocumentBuilder();
0418:
0419: try {
0420: Document document = documentBuilder.parse
0421: (new InputSource(req.getInputStream()));
0422:
0423: // Get the root element of the document
0424: Element rootElement = document.getDocumentElement();
0425: NodeList childList = rootElement.getChildNodes();
0426:
0427: for (int i=0; i < childList.getLength(); i++) {
0428: Node currentNode = childList.item(i);
0429: switch (currentNode.getNodeType()) {
0430: case Node.TEXT_NODE:
0431: break;
0432: case Node.ELEMENT_NODE:
0433: if (currentNode.getNodeName().endsWith("prop")) {
0434: type = FIND_BY_PROPERTY;
0435: propNode = currentNode;
0436: }
0437: if (currentNode.getNodeName().endsWith("propname")) {
0438: type = FIND_PROPERTY_NAMES;
0439: }
0440: if (currentNode.getNodeName().endsWith("allprop")) {
0441: type = FIND_ALL_PROP;
0442: }
0443: break;
0444: }
0445: }
0446: } catch(Exception e) {
0447: // Most likely there was no content : we use the defaults.
0448: // TODO : Enhance that !
0449: }
0450:
0451: if (type == FIND_BY_PROPERTY) {
0452: properties = new Vector();
0453: NodeList childList = propNode.getChildNodes();
0454:
0455: for (int i=0; i < childList.getLength(); i++) {
0456: Node currentNode = childList.item(i);
0457: switch (currentNode.getNodeType()) {
0458: case Node.TEXT_NODE:
0459: break;
0460: case Node.ELEMENT_NODE:
0461: String nodeName = currentNode.getNodeName();
0462: String propertyName = null;
0463: if (nodeName.indexOf(':') != -1) {
0464: propertyName = nodeName.substring
0465: (nodeName.indexOf(':') + 1);
0466: } else {
0467: propertyName = nodeName;
0468: }
0469: // href is a live property which is handled differently
0470: properties.addElement(propertyName);
0471: break;
0472: }
0473: }
0474:
0475: }
0476:
0477: // Retrieve the resources
0478: DirContext resources = getResources();
0479:
0480: if (resources == null) {
0481: resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0482: return;
0483: }
0484:
0485: boolean exists = true;
0486: Object object = null;
0487: try {
0488: object = resources.lookup(path);
0489: } catch (NamingException e) {
0490: exists = false;
0491: int slash = path.lastIndexOf('/');
0492: if (slash != -1) {
0493: String parentPath = path.substring(0, slash);
0494: Vector currentLockNullResources =
0495: (Vector) lockNullResources.get(parentPath);
0496: if (currentLockNullResources != null) {
0497: Enumeration lockNullResourcesList =
0498: currentLockNullResources.elements();
0499: while (lockNullResourcesList.hasMoreElements()) {
0500: String lockNullPath = (String)
0501: lockNullResourcesList.nextElement();
0502: if (lockNullPath.equals(path)) {
0503: resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
0504: resp.setContentType("text/xml; charset=UTF-8");
0505: // Create multistatus object
0506: XMLWriter generatedXML =
0507: new XMLWriter(resp.getWriter());
0508: generatedXML.writeXMLHeader();
0509: generatedXML.writeElement
0510: (null, "multistatus"
0511: + generateNamespaceDeclarations(),
0512: XMLWriter.OPENING);
0513: parseLockNullProperties
0514: (req, generatedXML, lockNullPath, type,
0515: properties);
0516: generatedXML.writeElement(null, "multistatus",
0517: XMLWriter.CLOSING);
0518: generatedXML.sendData();
0519: return;
0520: }
0521: }
0522: }
0523: }
0524: }
0525:
0526: if (!exists) {
0527: resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
0528: return;
0529: }
0530:
0531: resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
0532:
0533: resp.setContentType("text/xml; charset=UTF-8");
0534:
0535: // Create multistatus object
0536: XMLWriter generatedXML = new XMLWriter(resp.getWriter());
0537: generatedXML.writeXMLHeader();
0538:
0539: generatedXML.writeElement(null, "multistatus"
0540: + generateNamespaceDeclarations(),
0541: XMLWriter.OPENING);
0542:
0543: if (depth == 0) {
0544: parseProperties(req, resources, generatedXML, path, type,
0545: properties);
0546: } else {
0547: // The stack always contains the object of the current level
0548: Stack stack = new Stack();
0549: stack.push(path);
0550:
0551: // Stack of the objects one level below
0552: Stack stackBelow = new Stack();
0553:
0554: while ((!stack.isEmpty()) && (depth >= 0)) {
0555:
0556: String currentPath = (String) stack.pop();
0557: parseProperties(req, resources, generatedXML, currentPath,
0558: type, properties);
0559:
0560: try {
0561: object = resources.lookup(currentPath);
0562: } catch (NamingException e) {
0563: continue;
0564: }
0565:
0566: if ((object instanceof DirContext) && (depth > 0)) {
0567:
0568: try {
0569: NamingEnumeration enum = resources.list(currentPath);
0570: while (enum.hasMoreElements()) {
0571: NameClassPair ncPair =
0572: (NameClassPair) enum.nextElement();
0573: String newPath = currentPath;
0574: if (!(newPath.endsWith("/")))
0575: newPath += "/";
0576: newPath += ncPair.getName();
0577: stackBelow.push(newPath);
0578: }
0579: } catch (NamingException e) {
0580: resp.sendError
0581: (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0582: path);
0583: return;
0584: }
0585:
0586: // Displaying the lock-null resources present in that
0587: // collection
0588: String lockPath = currentPath;
0589: if (lockPath.endsWith("/"))
0590: lockPath =
0591: lockPath.substring(0, lockPath.length() - 1);
0592: Vector currentLockNullResources =
0593: (Vector) lockNullResources.get(lockPath);
0594: if (currentLockNullResources != null) {
0595: Enumeration lockNullResourcesList =
0596: currentLockNullResources.elements();
0597: while (lockNullResourcesList.hasMoreElements()) {
0598: String lockNullPath = (String)
0599: lockNullResourcesList.nextElement();
0600: parseLockNullProperties
0601: (req, generatedXML, lockNullPath, type,
0602: properties);
0603: }
0604: }
0605:
0606: }
0607:
0608: if (stack.isEmpty()) {
0609: depth--;
0610: stack = stackBelow;
0611: stackBelow = new Stack();
0612: }
0613:
0614: generatedXML.sendData();
0615:
0616: }
0617: }
0618:
0619: generatedXML.writeElement(null, "multistatus",
0620: XMLWriter.CLOSING);
0621:
0622: generatedXML.sendData();
0623:
0624: }
0625:
0626: /**
0627: * PROPPATCH Method.
0628: */
0629: protected void doProppatch(HttpServletRequest req,
0630: HttpServletResponse resp) throws ServletException,
0631: IOException {
0632:
0633: if (readOnly) {
0634: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0635: return;
0636: }
0637:
0638: if (isLocked(req)) {
0639: resp.sendError(WebdavStatus.SC_LOCKED);
0640: return;
0641: }
0642:
0643: resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
0644:
0645: }
0646:
0647: /**
0648: * MKCOL Method.
0649: */
0650: protected void doMkcol(HttpServletRequest req,
0651: HttpServletResponse resp) throws ServletException,
0652: IOException {
0653:
0654: if (readOnly) {
0655: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0656: return;
0657: }
0658:
0659: if (isLocked(req)) {
0660: resp.sendError(WebdavStatus.SC_LOCKED);
0661: return;
0662: }
0663:
0664: String path = getRelativePath(req);
0665:
0666: if ((path.toUpperCase().startsWith("/WEB-INF"))
0667: || (path.toUpperCase().startsWith("/META-INF"))) {
0668: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0669: return;
0670: }
0671:
0672: // Retrieve the resources
0673: DirContext resources = getResources();
0674:
0675: if (resources == null) {
0676: resp
0677: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0678: return;
0679: }
0680:
0681: boolean exists = true;
0682: Object object = null;
0683: try {
0684: object = resources.lookup(path);
0685: } catch (NamingException e) {
0686: exists = false;
0687: }
0688:
0689: // Can't create a collection if a resource already exists at the given
0690: // path
0691: if (exists) {
0692: resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
0693: return;
0694: }
0695:
0696: boolean result = true;
0697: try {
0698: resources.createSubcontext(path);
0699: } catch (NamingException e) {
0700: result = false;
0701: }
0702:
0703: if (!result) {
0704: resp.sendError(WebdavStatus.SC_CONFLICT, WebdavStatus
0705: .getStatusText(WebdavStatus.SC_CONFLICT));
0706: } else {
0707: resp.setStatus(WebdavStatus.SC_CREATED);
0708: // Removing any lock-null resource which would be present
0709: lockNullResources.remove(path);
0710: }
0711:
0712: }
0713:
0714: /**
0715: * DELETE Method.
0716: */
0717: protected void doDelete(HttpServletRequest req,
0718: HttpServletResponse resp) throws ServletException,
0719: IOException {
0720:
0721: if (readOnly) {
0722: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0723: return;
0724: }
0725:
0726: if (isLocked(req)) {
0727: resp.sendError(WebdavStatus.SC_LOCKED);
0728: return;
0729: }
0730:
0731: deleteResource(req, resp);
0732:
0733: }
0734:
0735: /**
0736: * Process a POST request for the specified resource.
0737: *
0738: * @param request The servlet request we are processing
0739: * @param response The servlet response we are creating
0740: *
0741: * @exception IOException if an input/output error occurs
0742: * @exception ServletException if a servlet-specified error occurs
0743: */
0744: protected void doPut(HttpServletRequest req,
0745: HttpServletResponse resp) throws ServletException,
0746: IOException {
0747:
0748: if (isLocked(req)) {
0749: resp.sendError(WebdavStatus.SC_LOCKED);
0750: return;
0751: }
0752:
0753: super .doPut(req, resp);
0754:
0755: String path = getRelativePath(req);
0756:
0757: // Removing any lock-null resource which would be present
0758: lockNullResources.remove(path);
0759:
0760: }
0761:
0762: /**
0763: * COPY Method.
0764: */
0765: protected void doCopy(HttpServletRequest req,
0766: HttpServletResponse resp) throws ServletException,
0767: IOException {
0768:
0769: if (readOnly) {
0770: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0771: return;
0772: }
0773:
0774: copyResource(req, resp);
0775:
0776: }
0777:
0778: /**
0779: * MOVE Method.
0780: */
0781: protected void doMove(HttpServletRequest req,
0782: HttpServletResponse resp) throws ServletException,
0783: IOException {
0784:
0785: if (readOnly) {
0786: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0787: return;
0788: }
0789:
0790: if (isLocked(req)) {
0791: resp.sendError(WebdavStatus.SC_LOCKED);
0792: return;
0793: }
0794:
0795: String path = getRelativePath(req);
0796:
0797: if (copyResource(req, resp)) {
0798: deleteResource(path, req, resp);
0799: }
0800:
0801: }
0802:
0803: /**
0804: * LOCK Method.
0805: */
0806: protected void doLock(HttpServletRequest req,
0807: HttpServletResponse resp) throws ServletException,
0808: IOException {
0809:
0810: if (readOnly) {
0811: resp.sendError(WebdavStatus.SC_FORBIDDEN);
0812: return;
0813: }
0814:
0815: if (isLocked(req)) {
0816: resp.sendError(WebdavStatus.SC_LOCKED);
0817: return;
0818: }
0819:
0820: LockInfo lock = new LockInfo();
0821:
0822: // Parsing lock request
0823:
0824: // Parsing depth header
0825:
0826: String depthStr = req.getHeader("Depth");
0827:
0828: if (depthStr == null) {
0829: lock.depth = INFINITY;
0830: } else {
0831: if (depthStr.equals("0")) {
0832: lock.depth = 0;
0833: } else {
0834: lock.depth = INFINITY;
0835: }
0836: }
0837:
0838: // Parsing timeout header
0839:
0840: int lockDuration = DEFAULT_TIMEOUT;
0841: String lockDurationStr = req.getHeader("Timeout");
0842: if (lockDurationStr == null) {
0843: lockDuration = DEFAULT_TIMEOUT;
0844: } else {
0845: if (lockDurationStr.startsWith("Second-")) {
0846: lockDuration = (new Integer(lockDurationStr
0847: .substring(7))).intValue();
0848: } else {
0849: if (lockDurationStr.equalsIgnoreCase("infinity")) {
0850: lockDuration = MAX_TIMEOUT;
0851: } else {
0852: try {
0853: lockDuration = (new Integer(lockDurationStr))
0854: .intValue();
0855: } catch (NumberFormatException e) {
0856: lockDuration = MAX_TIMEOUT;
0857: }
0858: }
0859: }
0860: if (lockDuration == 0) {
0861: lockDuration = DEFAULT_TIMEOUT;
0862: }
0863: if (lockDuration > MAX_TIMEOUT) {
0864: lockDuration = MAX_TIMEOUT;
0865: }
0866: }
0867: lock.expiresAt = System.currentTimeMillis()
0868: + (lockDuration * 1000);
0869:
0870: int lockRequestType = LOCK_CREATION;
0871:
0872: Node lockInfoNode = null;
0873:
0874: DocumentBuilder documentBuilder = getDocumentBuilder();
0875:
0876: try {
0877: Document document = documentBuilder.parse(new InputSource(
0878: req.getInputStream()));
0879:
0880: // Get the root element of the document
0881: Element rootElement = document.getDocumentElement();
0882: lockInfoNode = rootElement;
0883: } catch (Exception e) {
0884: lockRequestType = LOCK_REFRESH;
0885: }
0886:
0887: if (lockInfoNode != null) {
0888:
0889: // Reading lock information
0890:
0891: NodeList childList = lockInfoNode.getChildNodes();
0892: StringWriter strWriter = null;
0893: DOMWriter domWriter = null;
0894:
0895: Node lockScopeNode = null;
0896: Node lockTypeNode = null;
0897: Node lockOwnerNode = null;
0898:
0899: for (int i = 0; i < childList.getLength(); i++) {
0900: Node currentNode = childList.item(i);
0901: switch (currentNode.getNodeType()) {
0902: case Node.TEXT_NODE:
0903: break;
0904: case Node.ELEMENT_NODE:
0905: String nodeName = currentNode.getNodeName();
0906: if (nodeName.endsWith("lockscope")) {
0907: lockScopeNode = currentNode;
0908: }
0909: if (nodeName.endsWith("locktype")) {
0910: lockTypeNode = currentNode;
0911: }
0912: if (nodeName.endsWith("owner")) {
0913: lockOwnerNode = currentNode;
0914: }
0915: break;
0916: }
0917: }
0918:
0919: if (lockScopeNode != null) {
0920:
0921: childList = lockScopeNode.getChildNodes();
0922: for (int i = 0; i < childList.getLength(); i++) {
0923: Node currentNode = childList.item(i);
0924: switch (currentNode.getNodeType()) {
0925: case Node.TEXT_NODE:
0926: break;
0927: case Node.ELEMENT_NODE:
0928: String tempScope = currentNode.getNodeName();
0929: if (tempScope.indexOf(':') != -1) {
0930: lock.scope = tempScope.substring(tempScope
0931: .indexOf(':') + 1);
0932: } else {
0933: lock.scope = tempScope;
0934: }
0935: break;
0936: }
0937: }
0938:
0939: if (lock.scope == null) {
0940: // Bad request
0941: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
0942: }
0943:
0944: } else {
0945: // Bad request
0946: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
0947: }
0948:
0949: if (lockTypeNode != null) {
0950:
0951: childList = lockTypeNode.getChildNodes();
0952: for (int i = 0; i < childList.getLength(); i++) {
0953: Node currentNode = childList.item(i);
0954: switch (currentNode.getNodeType()) {
0955: case Node.TEXT_NODE:
0956: break;
0957: case Node.ELEMENT_NODE:
0958: String tempType = currentNode.getNodeName();
0959: if (tempType.indexOf(':') != -1) {
0960: lock.type = tempType.substring(tempType
0961: .indexOf(':') + 1);
0962: } else {
0963: lock.type = tempType;
0964: }
0965: break;
0966: }
0967: }
0968:
0969: if (lock.type == null) {
0970: // Bad request
0971: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
0972: }
0973:
0974: } else {
0975: // Bad request
0976: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
0977: }
0978:
0979: if (lockOwnerNode != null) {
0980:
0981: childList = lockOwnerNode.getChildNodes();
0982: for (int i = 0; i < childList.getLength(); i++) {
0983: Node currentNode = childList.item(i);
0984: switch (currentNode.getNodeType()) {
0985: case Node.TEXT_NODE:
0986: lock.owner += currentNode.getNodeValue();
0987: break;
0988: case Node.ELEMENT_NODE:
0989: strWriter = new StringWriter();
0990: domWriter = new DOMWriter(strWriter, true);
0991: domWriter.print(currentNode);
0992: lock.owner += strWriter.toString();
0993: break;
0994: }
0995: }
0996:
0997: if (lock.owner == null) {
0998: // Bad request
0999: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
1000: }
1001:
1002: } else {
1003: lock.owner = new String();
1004: }
1005:
1006: }
1007:
1008: String path = getRelativePath(req);
1009:
1010: lock.path = path;
1011:
1012: // Retrieve the resources
1013: DirContext resources = getResources();
1014:
1015: if (resources == null) {
1016: resp
1017: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1018: return;
1019: }
1020:
1021: boolean exists = true;
1022: Object object = null;
1023: try {
1024: object = resources.lookup(path);
1025: } catch (NamingException e) {
1026: exists = false;
1027: }
1028:
1029: Enumeration locksList = null;
1030:
1031: if (lockRequestType == LOCK_CREATION) {
1032:
1033: // Generating lock id
1034: String lockTokenStr = req.getServletPath() + "-"
1035: + lock.type + "-" + lock.scope + "-"
1036: + req.getUserPrincipal() + "-" + lock.depth + "-"
1037: + lock.owner + "-" + lock.tokens + "-"
1038: + lock.expiresAt + "-" + System.currentTimeMillis()
1039: + "-" + secret;
1040: String lockToken = md5Encoder.encode(md5Helper
1041: .digest(lockTokenStr.getBytes()));
1042:
1043: if ((exists) && (object instanceof DirContext)
1044: && (lock.depth == INFINITY)) {
1045:
1046: // Locking a collection (and all its member resources)
1047:
1048: // Checking if a child resource of this collection is
1049: // already locked
1050: Vector lockPaths = new Vector();
1051: locksList = collectionLocks.elements();
1052: while (locksList.hasMoreElements()) {
1053: LockInfo currentLock = (LockInfo) locksList
1054: .nextElement();
1055: if (currentLock.hasExpired()) {
1056: resourceLocks.remove(currentLock.path);
1057: continue;
1058: }
1059: if ((currentLock.path.startsWith(lock.path))
1060: && ((currentLock.isExclusive()) || (lock
1061: .isExclusive()))) {
1062: // A child collection of this collection is locked
1063: lockPaths.addElement(currentLock.path);
1064: }
1065: }
1066: locksList = resourceLocks.elements();
1067: while (locksList.hasMoreElements()) {
1068: LockInfo currentLock = (LockInfo) locksList
1069: .nextElement();
1070: if (currentLock.hasExpired()) {
1071: resourceLocks.remove(currentLock.path);
1072: continue;
1073: }
1074: if ((currentLock.path.startsWith(lock.path))
1075: && ((currentLock.isExclusive()) || (lock
1076: .isExclusive()))) {
1077: // A child resource of this collection is locked
1078: lockPaths.addElement(currentLock.path);
1079: }
1080: }
1081:
1082: if (!lockPaths.isEmpty()) {
1083:
1084: // One of the child paths was locked
1085: // We generate a multistatus error report
1086:
1087: Enumeration lockPathsList = lockPaths.elements();
1088:
1089: resp.setStatus(WebdavStatus.SC_CONFLICT);
1090:
1091: XMLWriter generatedXML = new XMLWriter();
1092: generatedXML.writeXMLHeader();
1093:
1094: generatedXML.writeElement(null, "multistatus"
1095: + generateNamespaceDeclarations(),
1096: XMLWriter.OPENING);
1097:
1098: while (lockPathsList.hasMoreElements()) {
1099: generatedXML.writeElement(null, "response",
1100: XMLWriter.OPENING);
1101: generatedXML.writeElement(null, "href",
1102: XMLWriter.OPENING);
1103: generatedXML.writeText((String) lockPathsList
1104: .nextElement());
1105: generatedXML.writeElement(null, "href",
1106: XMLWriter.CLOSING);
1107: generatedXML.writeElement(null, "status",
1108: XMLWriter.OPENING);
1109: generatedXML
1110: .writeText("HTTP/1.1 "
1111: + WebdavStatus.SC_LOCKED
1112: + " "
1113: + WebdavStatus
1114: .getStatusText(WebdavStatus.SC_LOCKED));
1115: generatedXML.writeElement(null, "status",
1116: XMLWriter.CLOSING);
1117:
1118: generatedXML.writeElement(null, "response",
1119: XMLWriter.CLOSING);
1120: }
1121:
1122: generatedXML.writeElement(null, "multistatus",
1123: XMLWriter.CLOSING);
1124:
1125: Writer writer = resp.getWriter();
1126: writer.write(generatedXML.toString());
1127: writer.close();
1128:
1129: return;
1130:
1131: }
1132:
1133: boolean addLock = true;
1134:
1135: // Checking if there is already a shared lock on this path
1136: locksList = collectionLocks.elements();
1137: while (locksList.hasMoreElements()) {
1138:
1139: LockInfo currentLock = (LockInfo) locksList
1140: .nextElement();
1141: if (currentLock.path.equals(lock.path)) {
1142:
1143: if (currentLock.isExclusive()) {
1144: resp.sendError(WebdavStatus.SC_LOCKED);
1145: return;
1146: } else {
1147: if (lock.isExclusive()) {
1148: resp.sendError(WebdavStatus.SC_LOCKED);
1149: return;
1150: }
1151: }
1152:
1153: currentLock.tokens.addElement(lockToken);
1154: lock = currentLock;
1155: addLock = false;
1156:
1157: }
1158:
1159: }
1160:
1161: if (addLock) {
1162: lock.tokens.addElement(lockToken);
1163: collectionLocks.addElement(lock);
1164: }
1165:
1166: } else {
1167:
1168: // Locking a single resource
1169:
1170: // Retrieving an already existing lock on that resource
1171: LockInfo presentLock = (LockInfo) resourceLocks
1172: .get(lock.path);
1173: if (presentLock != null) {
1174:
1175: if ((presentLock.isExclusive())
1176: || (lock.isExclusive())) {
1177: // If either lock is exclusive, the lock can't be
1178: // granted
1179: resp
1180: .sendError(WebdavStatus.SC_PRECONDITION_FAILED);
1181: return;
1182: } else {
1183: presentLock.tokens.addElement(lockToken);
1184: lock = presentLock;
1185: }
1186:
1187: } else {
1188:
1189: lock.tokens.addElement(lockToken);
1190: resourceLocks.put(lock.path, lock);
1191:
1192: // Checking if a resource exists at this path
1193: exists = true;
1194: try {
1195: object = resources.lookup(path);
1196: } catch (NamingException e) {
1197: exists = false;
1198: }
1199: if (!exists) {
1200:
1201: // "Creating" a lock-null resource
1202: int slash = lock.path.lastIndexOf('/');
1203: String parentPath = lock.path.substring(0,
1204: slash);
1205:
1206: Vector lockNulls = (Vector) lockNullResources
1207: .get(parentPath);
1208: if (lockNulls == null) {
1209: lockNulls = new Vector();
1210: lockNullResources
1211: .put(parentPath, lockNulls);
1212: }
1213:
1214: lockNulls.addElement(lock.path);
1215:
1216: }
1217:
1218: }
1219:
1220: }
1221:
1222: }
1223:
1224: if (lockRequestType == LOCK_REFRESH) {
1225:
1226: String ifHeader = req.getHeader("If");
1227: if (ifHeader == null)
1228: ifHeader = "";
1229:
1230: // Checking resource locks
1231:
1232: LockInfo toRenew = (LockInfo) resourceLocks.get(path);
1233: Enumeration tokenList = null;
1234: if (lock != null) {
1235:
1236: // At least one of the tokens of the locks must have been given
1237:
1238: tokenList = toRenew.tokens.elements();
1239: while (tokenList.hasMoreElements()) {
1240: String token = (String) tokenList.nextElement();
1241: if (ifHeader.indexOf(token) != -1) {
1242: toRenew.expiresAt = lock.expiresAt;
1243: lock = toRenew;
1244: }
1245: }
1246:
1247: }
1248:
1249: // Checking inheritable collection locks
1250:
1251: Enumeration collectionLocksList = collectionLocks
1252: .elements();
1253: while (collectionLocksList.hasMoreElements()) {
1254: toRenew = (LockInfo) collectionLocksList.nextElement();
1255: if (path.equals(toRenew.path)) {
1256:
1257: tokenList = toRenew.tokens.elements();
1258: while (tokenList.hasMoreElements()) {
1259: String token = (String) tokenList.nextElement();
1260: if (ifHeader.indexOf(token) != -1) {
1261: toRenew.expiresAt = lock.expiresAt;
1262: lock = toRenew;
1263: }
1264: }
1265:
1266: }
1267: }
1268:
1269: }
1270:
1271: // Set the status, then generate the XML response containing
1272: // the lock information
1273: XMLWriter generatedXML = new XMLWriter();
1274: generatedXML.writeXMLHeader();
1275: generatedXML.writeElement(null, "prop"
1276: + generateNamespaceDeclarations(), XMLWriter.OPENING);
1277:
1278: generatedXML.writeElement(null, "lockdiscovery",
1279: XMLWriter.OPENING);
1280:
1281: lock.toXML(generatedXML, true);
1282:
1283: generatedXML.writeElement(null, "lockdiscovery",
1284: XMLWriter.CLOSING);
1285:
1286: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
1287:
1288: resp.setStatus(WebdavStatus.SC_OK);
1289: resp.setContentType("text/xml; charset=UTF-8");
1290: Writer writer = resp.getWriter();
1291: writer.write(generatedXML.toString());
1292: writer.close();
1293:
1294: }
1295:
1296: /**
1297: * UNLOCK Method.
1298: */
1299: protected void doUnlock(HttpServletRequest req,
1300: HttpServletResponse resp) throws ServletException,
1301: IOException {
1302:
1303: if (readOnly) {
1304: resp.sendError(WebdavStatus.SC_FORBIDDEN);
1305: return;
1306: }
1307:
1308: if (isLocked(req)) {
1309: resp.sendError(WebdavStatus.SC_LOCKED);
1310: return;
1311: }
1312:
1313: String path = getRelativePath(req);
1314:
1315: String lockTokenHeader = req.getHeader("Lock-Token");
1316: if (lockTokenHeader == null)
1317: lockTokenHeader = "";
1318:
1319: // Checking resource locks
1320:
1321: LockInfo lock = (LockInfo) resourceLocks.get(path);
1322: Enumeration tokenList = null;
1323: if (lock != null) {
1324:
1325: // At least one of the tokens of the locks must have been given
1326:
1327: tokenList = lock.tokens.elements();
1328: while (tokenList.hasMoreElements()) {
1329: String token = (String) tokenList.nextElement();
1330: if (lockTokenHeader.indexOf(token) != -1) {
1331: lock.tokens.removeElement(token);
1332: }
1333: }
1334:
1335: if (lock.tokens.isEmpty()) {
1336: resourceLocks.remove(path);
1337: // Removing any lock-null resource which would be present
1338: lockNullResources.remove(path);
1339: }
1340:
1341: }
1342:
1343: // Checking inheritable collection locks
1344:
1345: Enumeration collectionLocksList = collectionLocks.elements();
1346: while (collectionLocksList.hasMoreElements()) {
1347: lock = (LockInfo) collectionLocksList.nextElement();
1348: if (path.equals(lock.path)) {
1349:
1350: tokenList = lock.tokens.elements();
1351: while (tokenList.hasMoreElements()) {
1352: String token = (String) tokenList.nextElement();
1353: if (lockTokenHeader.indexOf(token) != -1) {
1354: lock.tokens.removeElement(token);
1355: break;
1356: }
1357: }
1358:
1359: if (lock.tokens.isEmpty()) {
1360: collectionLocks.removeElement(lock);
1361: // Removing any lock-null resource which would be present
1362: lockNullResources.remove(path);
1363: }
1364:
1365: }
1366: }
1367:
1368: resp.setStatus(WebdavStatus.SC_NO_CONTENT);
1369:
1370: }
1371:
1372: // -------------------------------------------------------- Private Methods
1373:
1374: protected String getETagValue(ResourceInfo resourceInfo) {
1375: return resourceInfo.length + "-" + resourceInfo.date;
1376: }
1377:
1378: /**
1379: * Generate the namespace declarations.
1380: */
1381: private String generateNamespaceDeclarations() {
1382: return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
1383: }
1384:
1385: /**
1386: * Check to see if a resource is currently write locked. The method
1387: * will look at the "If" header to make sure the client
1388: * has give the appropriate lock tokens.
1389: *
1390: * @param req Servlet request
1391: * @return boolean true if the resource is locked (and no appropriate
1392: * lock token has been found for at least one of the non-shared locks which
1393: * are present on the resource).
1394: */
1395: private boolean isLocked(HttpServletRequest req) {
1396:
1397: String path = getRelativePath(req);
1398:
1399: String ifHeader = req.getHeader("If");
1400: if (ifHeader == null)
1401: ifHeader = "";
1402:
1403: String lockTokenHeader = req.getHeader("Lock-Token");
1404: if (lockTokenHeader == null)
1405: lockTokenHeader = "";
1406:
1407: return isLocked(path, ifHeader + lockTokenHeader);
1408:
1409: }
1410:
1411: /**
1412: * Check to see if a resource is currently write locked.
1413: *
1414: * @param path Path of the resource
1415: * @param ifHeader "If" HTTP header which was included in the 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(String path, String ifHeader) {
1421:
1422: // Checking resource locks
1423:
1424: LockInfo lock = (LockInfo) resourceLocks.get(path);
1425: Enumeration tokenList = null;
1426: if ((lock != null) && (lock.hasExpired())) {
1427: resourceLocks.remove(path);
1428: } else if (lock != null) {
1429:
1430: // At least one of the tokens of the locks must have been given
1431:
1432: tokenList = lock.tokens.elements();
1433: boolean tokenMatch = false;
1434: while (tokenList.hasMoreElements()) {
1435: String token = (String) tokenList.nextElement();
1436: if (ifHeader.indexOf(token) != -1)
1437: tokenMatch = true;
1438: }
1439: if (!tokenMatch)
1440: return true;
1441:
1442: }
1443:
1444: // Checking inheritable collection locks
1445:
1446: Enumeration collectionLocksList = collectionLocks.elements();
1447: while (collectionLocksList.hasMoreElements()) {
1448: lock = (LockInfo) collectionLocksList.nextElement();
1449: if (lock.hasExpired()) {
1450: collectionLocks.removeElement(lock);
1451: } else if (path.startsWith(lock.path)) {
1452:
1453: tokenList = lock.tokens.elements();
1454: boolean tokenMatch = false;
1455: while (tokenList.hasMoreElements()) {
1456: String token = (String) tokenList.nextElement();
1457: if (ifHeader.indexOf(token) != -1)
1458: tokenMatch = true;
1459: }
1460: if (!tokenMatch)
1461: return true;
1462:
1463: }
1464: }
1465:
1466: return false;
1467:
1468: }
1469:
1470: /**
1471: * Copy a resource.
1472: *
1473: * @param req Servlet request
1474: * @param resp Servlet response
1475: * @return boolean true if the copy is successful
1476: */
1477: private boolean copyResource(HttpServletRequest req,
1478: HttpServletResponse resp) throws ServletException,
1479: IOException {
1480:
1481: // Parsing destination header
1482:
1483: String destinationPath = req.getHeader("Destination");
1484:
1485: if (destinationPath == null) {
1486: resp.sendError(WebdavStatus.SC_BAD_REQUEST);
1487: return false;
1488: }
1489:
1490: int protocolIndex = destinationPath.indexOf("://");
1491: if (protocolIndex >= 0) {
1492: // if the Destination URL contains the protocol, we can safely
1493: // trim everything upto the first "/" character after "://"
1494: int firstSeparator = destinationPath.indexOf("/",
1495: protocolIndex + 4);
1496: if (firstSeparator < 0) {
1497: destinationPath = "/";
1498: } else {
1499: destinationPath = destinationPath
1500: .substring(firstSeparator);
1501: }
1502: } else {
1503: String hostName = req.getServerName();
1504: if ((hostName != null)
1505: && (destinationPath.startsWith(hostName))) {
1506: destinationPath = destinationPath.substring(hostName
1507: .length());
1508: }
1509:
1510: int portIndex = destinationPath.indexOf(":");
1511: if (portIndex >= 0) {
1512: destinationPath = destinationPath.substring(portIndex);
1513: }
1514:
1515: if (destinationPath.startsWith(":")) {
1516: int firstSeparator = destinationPath.indexOf("/");
1517: if (firstSeparator < 0) {
1518: destinationPath = "/";
1519: } else {
1520: destinationPath = destinationPath
1521: .substring(firstSeparator);
1522: }
1523: }
1524: }
1525:
1526: String contextPath = req.getContextPath();
1527: if ((contextPath != null)
1528: && (destinationPath.startsWith(contextPath))) {
1529: destinationPath = destinationPath.substring(contextPath
1530: .length());
1531: }
1532:
1533: String pathInfo = req.getPathInfo();
1534: if (pathInfo != null) {
1535: String servletPath = req.getServletPath();
1536: if ((servletPath != null)
1537: && (destinationPath.startsWith(servletPath))) {
1538: destinationPath = destinationPath.substring(servletPath
1539: .length());
1540: }
1541: }
1542:
1543: destinationPath = RequestUtil.URLDecode(
1544: normalize(destinationPath), "UTF8");
1545:
1546: if (debug > 0)
1547: System.out.println("Dest path :" + destinationPath);
1548:
1549: if ((destinationPath.toUpperCase().startsWith("/WEB-INF"))
1550: || (destinationPath.toUpperCase()
1551: .startsWith("/META-INF"))) {
1552: resp.sendError(WebdavStatus.SC_FORBIDDEN);
1553: return false;
1554: }
1555:
1556: String path = getRelativePath(req);
1557:
1558: if ((path.toUpperCase().startsWith("/WEB-INF"))
1559: || (path.toUpperCase().startsWith("/META-INF"))) {
1560: resp.sendError(WebdavStatus.SC_FORBIDDEN);
1561: return false;
1562: }
1563:
1564: if (destinationPath.equals(path)) {
1565: resp.sendError(WebdavStatus.SC_FORBIDDEN);
1566: return false;
1567: }
1568:
1569: // Parsing overwrite header
1570:
1571: boolean overwrite = true;
1572: String overwriteHeader = req.getHeader("Overwrite");
1573:
1574: if (overwriteHeader != null) {
1575: if (overwriteHeader.equalsIgnoreCase("T")) {
1576: overwrite = true;
1577: } else {
1578: overwrite = false;
1579: }
1580: }
1581:
1582: // Overwriting the destination
1583:
1584: // Retrieve the resources
1585: DirContext resources = getResources();
1586:
1587: if (resources == null) {
1588: resp
1589: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1590: return false;
1591: }
1592:
1593: boolean exists = true;
1594: try {
1595: resources.lookup(destinationPath);
1596: } catch (NamingException e) {
1597: exists = false;
1598: }
1599:
1600: if (overwrite) {
1601:
1602: // Delete destination resource, if it exists
1603: if (exists) {
1604: if (!deleteResource(destinationPath, req, resp)) {
1605: return false;
1606: } else {
1607: resp.setStatus(WebdavStatus.SC_NO_CONTENT);
1608: }
1609: } else {
1610: resp.setStatus(WebdavStatus.SC_CREATED);
1611: }
1612:
1613: } else {
1614:
1615: // If the destination exists, then it's a conflict
1616: if (exists) {
1617: resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
1618: return false;
1619: }
1620:
1621: }
1622:
1623: // Copying source to destination
1624:
1625: Hashtable errorList = new Hashtable();
1626:
1627: boolean result = copyResource(resources, errorList, path,
1628: destinationPath);
1629:
1630: if ((!result) || (!errorList.isEmpty())) {
1631:
1632: sendReport(req, resp, errorList);
1633: return false;
1634:
1635: }
1636:
1637: // Removing any lock-null resource which would be present at
1638: // the destination path
1639: lockNullResources.remove(destinationPath);
1640:
1641: return true;
1642:
1643: }
1644:
1645: /**
1646: * Copy a collection.
1647: *
1648: * @param resources Resources implementation to be used
1649: * @param errorList Hashtable containing the list of errors which occurred
1650: * during the copy operation
1651: * @param source Path of the resource to be copied
1652: * @param dest Destination path
1653: */
1654: private boolean copyResource(DirContext resources, Hashtable errorList,
1655: String source, String dest) {
1656:
1657: if (debug > 1)
1658: System.out.println("Copy: " + source + " To: " + dest);
1659:
1660: Object object = null;
1661: try {
1662: object = resources.lookup(source);
1663: } catch (NamingException e) {
1664: }
1665:
1666: if (object instanceof DirContext) {
1667:
1668: try {
1669: resources.createSubcontext(dest);
1670: } catch (NamingException e) {
1671: errorList.put
1672: (dest, new Integer(WebdavStatus.SC_CONFLICT));
1673: return false;
1674: }
1675:
1676: try {
1677: NamingEnumeration enum = resources.list(source);
1678: while (enum.hasMoreElements()) {
1679: NameClassPair ncPair = (NameClassPair) enum.nextElement();
1680: String childDest = dest;
1681: if (!childDest.equals("/"))
1682: childDest += "/";
1683: childDest += ncPair.getName();
1684: String childSrc = source;
1685: if (!childSrc.equals("/"))
1686: childSrc += "/";
1687: childSrc += ncPair.getName();
1688: copyResource(resources, errorList, childSrc, childDest);
1689: }
1690: } catch (NamingException e) {
1691: errorList.put
1692: (dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1693: return false;
1694: }
1695:
1696: } else {
1697:
1698: if (object instanceof Resource) {
1699: try {
1700: resources.bind(dest, object);
1701: } catch (NamingException e) {
1702: errorList.put
1703: (source,
1704: new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1705: return false;
1706: }
1707: } else {
1708: errorList.put
1709: (source,
1710: new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1711: return false;
1712: }
1713:
1714: }
1715:
1716: return true;
1717:
1718: }
1719:
1720: /**
1721: * Delete a resource.
1722: *
1723: * @param req Servlet request
1724: * @param resp Servlet response
1725: * @return boolean true if the copy is successful
1726: */
1727: private boolean deleteResource(HttpServletRequest req,
1728: HttpServletResponse resp) throws ServletException,
1729: IOException {
1730:
1731: String path = getRelativePath(req);
1732:
1733: return deleteResource(path, req, resp);
1734:
1735: }
1736:
1737: /**
1738: * Delete a resource.
1739: *
1740: * @param path Path of the resource which is to be deleted
1741: * @param req Servlet request
1742: * @param resp Servlet response
1743: */
1744: private boolean deleteResource(String path, HttpServletRequest req,
1745: HttpServletResponse resp) throws ServletException,
1746: IOException {
1747:
1748: if ((path.toUpperCase().startsWith("/WEB-INF"))
1749: || (path.toUpperCase().startsWith("/META-INF"))) {
1750: resp.sendError(WebdavStatus.SC_FORBIDDEN);
1751: return false;
1752: }
1753:
1754: String ifHeader = req.getHeader("If");
1755: if (ifHeader == null)
1756: ifHeader = "";
1757:
1758: String lockTokenHeader = req.getHeader("Lock-Token");
1759: if (lockTokenHeader == null)
1760: lockTokenHeader = "";
1761:
1762: if (isLocked(path, ifHeader + lockTokenHeader)) {
1763: resp.sendError(WebdavStatus.SC_LOCKED);
1764: return false;
1765: }
1766:
1767: // Retrieve the resources
1768: DirContext resources = getResources();
1769:
1770: if (resources == null) {
1771: resp
1772: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1773: return false;
1774: }
1775:
1776: boolean exists = true;
1777: Object object = null;
1778: try {
1779: object = resources.lookup(path);
1780: } catch (NamingException e) {
1781: exists = false;
1782: }
1783:
1784: if (!exists) {
1785: resp.sendError(WebdavStatus.SC_NOT_FOUND);
1786: return false;
1787: }
1788:
1789: boolean collection = (object instanceof DirContext);
1790:
1791: if (!collection) {
1792: try {
1793: resources.unbind(path);
1794: } catch (NamingException e) {
1795: resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
1796: return false;
1797: }
1798: } else {
1799:
1800: Hashtable errorList = new Hashtable();
1801:
1802: deleteCollection(req, resources, path, errorList);
1803: try {
1804: resources.unbind(path);
1805: } catch (NamingException e) {
1806: errorList.put(path, new Integer(
1807: WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1808: }
1809:
1810: if (!errorList.isEmpty()) {
1811:
1812: sendReport(req, resp, errorList);
1813: return false;
1814:
1815: }
1816:
1817: }
1818:
1819: resp.setStatus(WebdavStatus.SC_NO_CONTENT);
1820: return true;
1821:
1822: }
1823:
1824: /**
1825: * Deletes a collection.
1826: *
1827: * @param resources Resources implementation associated with the context
1828: * @param path Path to the collection to be deleted
1829: * @param errorList Contains the list of the errors which occurred
1830: */
1831: private void deleteCollection(HttpServletRequest req,
1832: DirContext resources,
1833: String path, Hashtable errorList) {
1834:
1835: if (debug > 1)
1836: System.out.println("Delete:" + path);
1837:
1838: if ((path.toUpperCase().startsWith("/WEB-INF")) ||
1839: (path.toUpperCase().startsWith("/META-INF"))) {
1840: errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN));
1841: return;
1842: }
1843:
1844: String ifHeader = req.getHeader("If");
1845: if (ifHeader == null)
1846: ifHeader = "";
1847:
1848: String lockTokenHeader = req.getHeader("Lock-Token");
1849: if (lockTokenHeader == null)
1850: lockTokenHeader = "";
1851:
1852: Enumeration enum = null;
1853: try {
1854: enum = resources.list(path);
1855: } catch (NamingException e) {
1856: errorList.put(path, new Integer
1857: (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1858: return;
1859: }
1860:
1861: while (enum.hasMoreElements()) {
1862: NameClassPair ncPair = (NameClassPair) enum.nextElement();
1863: String childName = path;
1864: if (!childName.equals("/"))
1865: childName += "/";
1866: childName += ncPair.getName();
1867:
1868: if (isLocked(childName, ifHeader + lockTokenHeader)) {
1869:
1870: errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED));
1871:
1872: } else {
1873:
1874: try {
1875: Object object = resources.lookup(childName);
1876: if (object instanceof DirContext) {
1877: deleteCollection(req, resources, childName, errorList);
1878: }
1879:
1880: try {
1881: resources.unbind(childName);
1882: } catch (NamingException e) {
1883: if (!(object instanceof DirContext)) {
1884: // If it's not a collection, then it's an unknown
1885: // error
1886: errorList.put
1887: (childName, new Integer
1888: (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1889: }
1890: }
1891: } catch (NamingException e) {
1892: errorList.put
1893: (childName, new Integer
1894: (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
1895: }
1896: }
1897:
1898: }
1899:
1900: }
1901:
1902: /**
1903: * Send a multistatus element containing a complete error report to the
1904: * client.
1905: *
1906: * @param req Servlet request
1907: * @param resp Servlet response
1908: * @param errorList List of error to be displayed
1909: */
1910: private void sendReport(HttpServletRequest req,
1911: HttpServletResponse resp, Hashtable errorList)
1912: throws ServletException, IOException {
1913:
1914: resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
1915:
1916: String absoluteUri = req.getRequestURI();
1917: String relativePath = getRelativePath(req);
1918:
1919: XMLWriter generatedXML = new XMLWriter();
1920: generatedXML.writeXMLHeader();
1921:
1922: generatedXML.writeElement(null, "multistatus"
1923: + generateNamespaceDeclarations(), XMLWriter.OPENING);
1924:
1925: Enumeration pathList = errorList.keys();
1926: while (pathList.hasMoreElements()) {
1927:
1928: String errorPath = (String) pathList.nextElement();
1929: int errorCode = ((Integer) errorList.get(errorPath))
1930: .intValue();
1931:
1932: generatedXML.writeElement(null, "response",
1933: XMLWriter.OPENING);
1934:
1935: generatedXML.writeElement(null, "href", XMLWriter.OPENING);
1936: String toAppend = errorPath
1937: .substring(relativePath.length());
1938: if (!toAppend.startsWith("/"))
1939: toAppend = "/" + toAppend;
1940: generatedXML.writeText(absoluteUri + toAppend);
1941: generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
1942: generatedXML
1943: .writeElement(null, "status", XMLWriter.OPENING);
1944: generatedXML.writeText("HTTP/1.1 " + errorCode + " "
1945: + WebdavStatus.getStatusText(errorCode));
1946: generatedXML
1947: .writeElement(null, "status", XMLWriter.CLOSING);
1948:
1949: generatedXML.writeElement(null, "response",
1950: XMLWriter.CLOSING);
1951:
1952: }
1953:
1954: generatedXML.writeElement(null, "multistatus",
1955: XMLWriter.CLOSING);
1956:
1957: Writer writer = resp.getWriter();
1958: writer.write(generatedXML.toString());
1959: writer.close();
1960:
1961: }
1962:
1963: /**
1964: * Propfind helper method.
1965: *
1966: * @param resources Resources object associated with this context
1967: * @param generatedXML XML response to the Propfind request
1968: * @param path Path of the current resource
1969: * @param type Propfind type
1970: * @param propertiesVector If the propfind type is find properties by
1971: * name, then this Vector contains those properties
1972: */
1973: private void parseProperties(HttpServletRequest req,
1974: DirContext resources, XMLWriter generatedXML, String path,
1975: int type, Vector propertiesVector) {
1976:
1977: // Exclude any resource in the /WEB-INF and /META-INF subdirectories
1978: // (the "toUpperCase()" avoids problems on Windows systems)
1979: if (path.toUpperCase().startsWith("/WEB-INF")
1980: || path.toUpperCase().startsWith("/META-INF"))
1981: return;
1982:
1983: ResourceInfo resourceInfo = new ResourceInfo(path, resources);
1984:
1985: generatedXML.writeElement(null, "response", XMLWriter.OPENING);
1986: String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
1987: + " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK));
1988:
1989: // Generating href element
1990: generatedXML.writeElement(null, "href", XMLWriter.OPENING);
1991:
1992: String href = req.getContextPath();
1993: if ((href.endsWith("/")) && (path.startsWith("/")))
1994: href += path.substring(1);
1995: else
1996: href += path;
1997: if ((resourceInfo.collection) && (!href.endsWith("/")))
1998: href += "/";
1999:
2000: generatedXML.writeText(rewriteUrl(href));
2001:
2002: generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
2003:
2004: String resourceName = path;
2005: int lastSlash = path.lastIndexOf('/');
2006: if (lastSlash != -1)
2007: resourceName = resourceName.substring(lastSlash + 1);
2008:
2009: switch (type) {
2010:
2011: case FIND_ALL_PROP:
2012:
2013: generatedXML.writeElement(null, "propstat",
2014: XMLWriter.OPENING);
2015: generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2016:
2017: generatedXML.writeProperty(null, "creationdate",
2018: getISOCreationDate(resourceInfo.creationDate));
2019: generatedXML.writeElement(null, "displayname",
2020: XMLWriter.OPENING);
2021: generatedXML.writeData(resourceName);
2022: generatedXML.writeElement(null, "displayname",
2023: XMLWriter.CLOSING);
2024: generatedXML.writeProperty(null, "getcontentlanguage",
2025: Locale.getDefault().toString());
2026: if (!resourceInfo.collection) {
2027: generatedXML.writeProperty(null, "getlastmodified",
2028: resourceInfo.httpDate);
2029: generatedXML.writeProperty(null, "getcontentlength",
2030: String.valueOf(resourceInfo.length));
2031: generatedXML.writeProperty(null, "getcontenttype",
2032: getServletContext().getMimeType(
2033: resourceInfo.path));
2034: generatedXML.writeProperty(null, "getetag",
2035: getETagValue(resourceInfo));
2036: generatedXML.writeElement(null, "resourcetype",
2037: XMLWriter.NO_CONTENT);
2038: } else {
2039: generatedXML.writeElement(null, "resourcetype",
2040: XMLWriter.OPENING);
2041: generatedXML.writeElement(null, "collection",
2042: XMLWriter.NO_CONTENT);
2043: generatedXML.writeElement(null, "resourcetype",
2044: XMLWriter.CLOSING);
2045: }
2046:
2047: generatedXML.writeProperty(null, "source", "");
2048:
2049: String supportedLocks = "<lockentry>"
2050: + "<lockscope><exclusive/></lockscope>"
2051: + "<locktype><write/></locktype>" + "</lockentry>"
2052: + "<lockentry>"
2053: + "<lockscope><shared/></lockscope>"
2054: + "<locktype><write/></locktype>" + "</lockentry>";
2055: generatedXML.writeElement(null, "supportedlock",
2056: XMLWriter.OPENING);
2057: generatedXML.writeText(supportedLocks);
2058: generatedXML.writeElement(null, "supportedlock",
2059: XMLWriter.CLOSING);
2060:
2061: generateLockDiscovery(path, generatedXML);
2062:
2063: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2064: generatedXML
2065: .writeElement(null, "status", XMLWriter.OPENING);
2066: generatedXML.writeText(status);
2067: generatedXML
2068: .writeElement(null, "status", XMLWriter.CLOSING);
2069: generatedXML.writeElement(null, "propstat",
2070: XMLWriter.CLOSING);
2071:
2072: break;
2073:
2074: case FIND_PROPERTY_NAMES:
2075:
2076: generatedXML.writeElement(null, "propstat",
2077: XMLWriter.OPENING);
2078: generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2079:
2080: generatedXML.writeElement(null, "creationdate",
2081: XMLWriter.NO_CONTENT);
2082: generatedXML.writeElement(null, "displayname",
2083: XMLWriter.NO_CONTENT);
2084: if (!resourceInfo.collection) {
2085: generatedXML.writeElement(null, "getcontentlanguage",
2086: XMLWriter.NO_CONTENT);
2087: generatedXML.writeElement(null, "getcontentlength",
2088: XMLWriter.NO_CONTENT);
2089: generatedXML.writeElement(null, "getcontenttype",
2090: XMLWriter.NO_CONTENT);
2091: generatedXML.writeElement(null, "getetag",
2092: XMLWriter.NO_CONTENT);
2093: generatedXML.writeElement(null, "getlastmodified",
2094: XMLWriter.NO_CONTENT);
2095: }
2096: generatedXML.writeElement(null, "resourcetype",
2097: XMLWriter.NO_CONTENT);
2098: generatedXML.writeElement(null, "source",
2099: XMLWriter.NO_CONTENT);
2100: generatedXML.writeElement(null, "lockdiscovery",
2101: XMLWriter.NO_CONTENT);
2102:
2103: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2104: generatedXML
2105: .writeElement(null, "status", XMLWriter.OPENING);
2106: generatedXML.writeText(status);
2107: generatedXML
2108: .writeElement(null, "status", XMLWriter.CLOSING);
2109: generatedXML.writeElement(null, "propstat",
2110: XMLWriter.CLOSING);
2111:
2112: break;
2113:
2114: case FIND_BY_PROPERTY:
2115:
2116: Vector propertiesNotFound = new Vector();
2117:
2118: // Parse the list of properties
2119:
2120: generatedXML.writeElement(null, "propstat",
2121: XMLWriter.OPENING);
2122: generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2123:
2124: Enumeration properties = propertiesVector.elements();
2125:
2126: while (properties.hasMoreElements()) {
2127:
2128: String property = (String) properties.nextElement();
2129:
2130: if (property.equals("creationdate")) {
2131: generatedXML
2132: .writeProperty(
2133: null,
2134: "creationdate",
2135: getISOCreationDate(resourceInfo.creationDate));
2136: } else if (property.equals("displayname")) {
2137: generatedXML.writeElement(null, "displayname",
2138: XMLWriter.OPENING);
2139: generatedXML.writeData(resourceName);
2140: generatedXML.writeElement(null, "displayname",
2141: XMLWriter.CLOSING);
2142: } else if (property.equals("getcontentlanguage")) {
2143: if (resourceInfo.collection) {
2144: propertiesNotFound.addElement(property);
2145: } else {
2146: generatedXML.writeProperty(null,
2147: "getcontentlanguage", Locale
2148: .getDefault().toString());
2149: }
2150: } else if (property.equals("getcontentlength")) {
2151: if (resourceInfo.collection) {
2152: propertiesNotFound.addElement(property);
2153: } else {
2154: generatedXML.writeProperty(null,
2155: "getcontentlength", (String
2156: .valueOf(resourceInfo.length)));
2157: }
2158: } else if (property.equals("getcontenttype")) {
2159: if (resourceInfo.collection) {
2160: propertiesNotFound.addElement(property);
2161: } else {
2162: generatedXML
2163: .writeProperty(
2164: null,
2165: "getcontenttype",
2166: getServletContext()
2167: .getMimeType(
2168: resourceInfo.path));
2169: }
2170: } else if (property.equals("getetag")) {
2171: if (resourceInfo.collection) {
2172: propertiesNotFound.addElement(property);
2173: } else {
2174: generatedXML.writeProperty(null, "getetag",
2175: getETagValue(resourceInfo));
2176: }
2177: } else if (property.equals("getlastmodified")) {
2178: if (resourceInfo.collection) {
2179: propertiesNotFound.addElement(property);
2180: } else {
2181: generatedXML.writeProperty(null,
2182: "getlastmodified",
2183: resourceInfo.httpDate);
2184: }
2185: } else if (property.equals("resourcetype")) {
2186: if (resourceInfo.collection) {
2187: generatedXML.writeElement(null, "resourcetype",
2188: XMLWriter.OPENING);
2189: generatedXML.writeElement(null, "collection",
2190: XMLWriter.NO_CONTENT);
2191: generatedXML.writeElement(null, "resourcetype",
2192: XMLWriter.CLOSING);
2193: } else {
2194: generatedXML.writeElement(null, "resourcetype",
2195: XMLWriter.NO_CONTENT);
2196: }
2197: } else if (property.equals("source")) {
2198: generatedXML.writeProperty(null, "source", "");
2199: } else if (property.equals("supportedlock")) {
2200: supportedLocks = "<lockentry>"
2201: + "<lockscope><exclusive/></lockscope>"
2202: + "<locktype><write/></locktype>"
2203: + "</lockentry>" + "<lockentry>"
2204: + "<lockscope><shared/></lockscope>"
2205: + "<locktype><write/></locktype>"
2206: + "</lockentry>";
2207: generatedXML.writeElement(null, "supportedlock",
2208: XMLWriter.OPENING);
2209: generatedXML.writeText(supportedLocks);
2210: generatedXML.writeElement(null, "supportedlock",
2211: XMLWriter.CLOSING);
2212: } else if (property.equals("lockdiscovery")) {
2213: if (!generateLockDiscovery(path, generatedXML))
2214: propertiesNotFound.addElement(property);
2215: } else {
2216: propertiesNotFound.addElement(property);
2217: }
2218:
2219: }
2220:
2221: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2222: generatedXML
2223: .writeElement(null, "status", XMLWriter.OPENING);
2224: generatedXML.writeText(status);
2225: generatedXML
2226: .writeElement(null, "status", XMLWriter.CLOSING);
2227: generatedXML.writeElement(null, "propstat",
2228: XMLWriter.CLOSING);
2229:
2230: Enumeration propertiesNotFoundList = propertiesNotFound
2231: .elements();
2232:
2233: if (propertiesNotFoundList.hasMoreElements()) {
2234:
2235: status = new String(
2236: "HTTP/1.1 "
2237: + WebdavStatus.SC_NOT_FOUND
2238: + " "
2239: + WebdavStatus
2240: .getStatusText(WebdavStatus.SC_NOT_FOUND));
2241:
2242: generatedXML.writeElement(null, "propstat",
2243: XMLWriter.OPENING);
2244: generatedXML.writeElement(null, "prop",
2245: XMLWriter.OPENING);
2246:
2247: while (propertiesNotFoundList.hasMoreElements()) {
2248: generatedXML.writeElement(null,
2249: (String) propertiesNotFoundList
2250: .nextElement(),
2251: XMLWriter.NO_CONTENT);
2252: }
2253:
2254: generatedXML.writeElement(null, "prop",
2255: XMLWriter.CLOSING);
2256: generatedXML.writeElement(null, "status",
2257: XMLWriter.OPENING);
2258: generatedXML.writeText(status);
2259: generatedXML.writeElement(null, "status",
2260: XMLWriter.CLOSING);
2261: generatedXML.writeElement(null, "propstat",
2262: XMLWriter.CLOSING);
2263:
2264: }
2265:
2266: break;
2267:
2268: }
2269:
2270: generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
2271:
2272: }
2273:
2274: /**
2275: * Propfind helper method. Dispays the properties of a lock-null resource.
2276: *
2277: * @param resources Resources object associated with this context
2278: * @param generatedXML XML response to the Propfind request
2279: * @param path Path of the current resource
2280: * @param type Propfind type
2281: * @param propertiesVector If the propfind type is find properties by
2282: * name, then this Vector contains those properties
2283: */
2284: private void parseLockNullProperties(HttpServletRequest req,
2285: XMLWriter generatedXML, String path, int type,
2286: Vector propertiesVector) {
2287:
2288: // Exclude any resource in the /WEB-INF and /META-INF subdirectories
2289: // (the "toUpperCase()" avoids problems on Windows systems)
2290: if (path.toUpperCase().startsWith("/WEB-INF")
2291: || path.toUpperCase().startsWith("/META-INF"))
2292: return;
2293:
2294: // Retrieving the lock associated with the lock-null resource
2295: LockInfo lock = (LockInfo) resourceLocks.get(path);
2296:
2297: if (lock == null)
2298: return;
2299:
2300: generatedXML.writeElement(null, "response", XMLWriter.OPENING);
2301: String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
2302: + " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK));
2303:
2304: // Generating href element
2305: generatedXML.writeElement(null, "href", XMLWriter.OPENING);
2306:
2307: String absoluteUri = req.getRequestURI();
2308: String relativePath = getRelativePath(req);
2309: String toAppend = path.substring(relativePath.length());
2310: if (!toAppend.startsWith("/"))
2311: toAppend = "/" + toAppend;
2312:
2313: generatedXML.writeText(rewriteUrl(normalize(absoluteUri
2314: + toAppend)));
2315:
2316: generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
2317:
2318: String resourceName = path;
2319: int lastSlash = path.lastIndexOf('/');
2320: if (lastSlash != -1)
2321: resourceName = resourceName.substring(lastSlash + 1);
2322:
2323: switch (type) {
2324:
2325: case FIND_ALL_PROP:
2326:
2327: generatedXML.writeElement(null, "propstat",
2328: XMLWriter.OPENING);
2329: generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2330:
2331: generatedXML.writeProperty(null, "creationdate",
2332: getISOCreationDate(lock.creationDate.getTime()));
2333: generatedXML.writeElement(null, "displayname",
2334: XMLWriter.OPENING);
2335: generatedXML.writeData(resourceName);
2336: generatedXML.writeElement(null, "displayname",
2337: XMLWriter.CLOSING);
2338: generatedXML.writeProperty(null, "getcontentlanguage",
2339: Locale.getDefault().toString());
2340: generatedXML.writeProperty(null, "getlastmodified",
2341: formats[0].format(lock.creationDate));
2342: generatedXML.writeProperty(null, "getcontentlength", String
2343: .valueOf(0));
2344: generatedXML.writeProperty(null, "getcontenttype", "");
2345: generatedXML.writeProperty(null, "getetag", "");
2346: generatedXML.writeElement(null, "resourcetype",
2347: XMLWriter.OPENING);
2348: generatedXML.writeElement(null, "lock-null",
2349: XMLWriter.NO_CONTENT);
2350: generatedXML.writeElement(null, "resourcetype",
2351: XMLWriter.CLOSING);
2352:
2353: generatedXML.writeProperty(null, "source", "");
2354:
2355: String supportedLocks = "<lockentry>"
2356: + "<lockscope><exclusive/></lockscope>"
2357: + "<locktype><write/></locktype>" + "</lockentry>"
2358: + "<lockentry>"
2359: + "<lockscope><shared/></lockscope>"
2360: + "<locktype><write/></locktype>" + "</lockentry>";
2361: generatedXML.writeElement(null, "supportedlock",
2362: XMLWriter.OPENING);
2363: generatedXML.writeText(supportedLocks);
2364: generatedXML.writeElement(null, "supportedlock",
2365: XMLWriter.CLOSING);
2366:
2367: generateLockDiscovery(path, generatedXML);
2368:
2369: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2370: generatedXML
2371: .writeElement(null, "status", XMLWriter.OPENING);
2372: generatedXML.writeText(status);
2373: generatedXML
2374: .writeElement(null, "status", XMLWriter.CLOSING);
2375: generatedXML.writeElement(null, "propstat",
2376: XMLWriter.CLOSING);
2377:
2378: break;
2379:
2380: case FIND_PROPERTY_NAMES:
2381:
2382: generatedXML.writeElement(null, "propstat",
2383: XMLWriter.OPENING);
2384: generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2385:
2386: generatedXML.writeElement(null, "creationdate",
2387: XMLWriter.NO_CONTENT);
2388: generatedXML.writeElement(null, "displayname",
2389: XMLWriter.NO_CONTENT);
2390: generatedXML.writeElement(null, "getcontentlanguage",
2391: XMLWriter.NO_CONTENT);
2392: generatedXML.writeElement(null, "getcontentlength",
2393: XMLWriter.NO_CONTENT);
2394: generatedXML.writeElement(null, "getcontenttype",
2395: XMLWriter.NO_CONTENT);
2396: generatedXML.writeElement(null, "getetag",
2397: XMLWriter.NO_CONTENT);
2398: generatedXML.writeElement(null, "getlastmodified",
2399: XMLWriter.NO_CONTENT);
2400: generatedXML.writeElement(null, "resourcetype",
2401: XMLWriter.NO_CONTENT);
2402: generatedXML.writeElement(null, "source",
2403: XMLWriter.NO_CONTENT);
2404: generatedXML.writeElement(null, "lockdiscovery",
2405: XMLWriter.NO_CONTENT);
2406:
2407: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2408: generatedXML
2409: .writeElement(null, "status", XMLWriter.OPENING);
2410: generatedXML.writeText(status);
2411: generatedXML
2412: .writeElement(null, "status", XMLWriter.CLOSING);
2413: generatedXML.writeElement(null, "propstat",
2414: XMLWriter.CLOSING);
2415:
2416: break;
2417:
2418: case FIND_BY_PROPERTY:
2419:
2420: Vector propertiesNotFound = new Vector();
2421:
2422: // Parse the list of properties
2423:
2424: generatedXML.writeElement(null, "propstat",
2425: XMLWriter.OPENING);
2426: generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
2427:
2428: Enumeration properties = propertiesVector.elements();
2429:
2430: while (properties.hasMoreElements()) {
2431:
2432: String property = (String) properties.nextElement();
2433:
2434: if (property.equals("creationdate")) {
2435: generatedXML.writeProperty(null, "creationdate",
2436: getISOCreationDate(lock.creationDate
2437: .getTime()));
2438: } else if (property.equals("displayname")) {
2439: generatedXML.writeElement(null, "displayname",
2440: XMLWriter.OPENING);
2441: generatedXML.writeData(resourceName);
2442: generatedXML.writeElement(null, "displayname",
2443: XMLWriter.CLOSING);
2444: } else if (property.equals("getcontentlanguage")) {
2445: generatedXML.writeProperty(null,
2446: "getcontentlanguage", Locale.getDefault()
2447: .toString());
2448: } else if (property.equals("getcontentlength")) {
2449: generatedXML.writeProperty(null,
2450: "getcontentlength", (String.valueOf(0)));
2451: } else if (property.equals("getcontenttype")) {
2452: generatedXML.writeProperty(null, "getcontenttype",
2453: "");
2454: } else if (property.equals("getetag")) {
2455: generatedXML.writeProperty(null, "getetag", "");
2456: } else if (property.equals("getlastmodified")) {
2457: generatedXML.writeProperty(null, "getlastmodified",
2458: formats[0].format(lock.creationDate));
2459: } else if (property.equals("resourcetype")) {
2460: generatedXML.writeElement(null, "resourcetype",
2461: XMLWriter.OPENING);
2462: generatedXML.writeElement(null, "lock-null",
2463: XMLWriter.NO_CONTENT);
2464: generatedXML.writeElement(null, "resourcetype",
2465: XMLWriter.CLOSING);
2466: } else if (property.equals("source")) {
2467: generatedXML.writeProperty(null, "source", "");
2468: } else if (property.equals("supportedlock")) {
2469: supportedLocks = "<lockentry>"
2470: + "<lockscope><exclusive/></lockscope>"
2471: + "<locktype><write/></locktype>"
2472: + "</lockentry>" + "<lockentry>"
2473: + "<lockscope><shared/></lockscope>"
2474: + "<locktype><write/></locktype>"
2475: + "</lockentry>";
2476: generatedXML.writeElement(null, "supportedlock",
2477: XMLWriter.OPENING);
2478: generatedXML.writeText(supportedLocks);
2479: generatedXML.writeElement(null, "supportedlock",
2480: XMLWriter.CLOSING);
2481: } else if (property.equals("lockdiscovery")) {
2482: if (!generateLockDiscovery(path, generatedXML))
2483: propertiesNotFound.addElement(property);
2484: } else {
2485: propertiesNotFound.addElement(property);
2486: }
2487:
2488: }
2489:
2490: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
2491: generatedXML
2492: .writeElement(null, "status", XMLWriter.OPENING);
2493: generatedXML.writeText(status);
2494: generatedXML
2495: .writeElement(null, "status", XMLWriter.CLOSING);
2496: generatedXML.writeElement(null, "propstat",
2497: XMLWriter.CLOSING);
2498:
2499: Enumeration propertiesNotFoundList = propertiesNotFound
2500: .elements();
2501:
2502: if (propertiesNotFoundList.hasMoreElements()) {
2503:
2504: status = new String(
2505: "HTTP/1.1 "
2506: + WebdavStatus.SC_NOT_FOUND
2507: + " "
2508: + WebdavStatus
2509: .getStatusText(WebdavStatus.SC_NOT_FOUND));
2510:
2511: generatedXML.writeElement(null, "propstat",
2512: XMLWriter.OPENING);
2513: generatedXML.writeElement(null, "prop",
2514: XMLWriter.OPENING);
2515:
2516: while (propertiesNotFoundList.hasMoreElements()) {
2517: generatedXML.writeElement(null,
2518: (String) propertiesNotFoundList
2519: .nextElement(),
2520: XMLWriter.NO_CONTENT);
2521: }
2522:
2523: generatedXML.writeElement(null, "prop",
2524: XMLWriter.CLOSING);
2525: generatedXML.writeElement(null, "status",
2526: XMLWriter.OPENING);
2527: generatedXML.writeText(status);
2528: generatedXML.writeElement(null, "status",
2529: XMLWriter.CLOSING);
2530: generatedXML.writeElement(null, "propstat",
2531: XMLWriter.CLOSING);
2532:
2533: }
2534:
2535: break;
2536:
2537: }
2538:
2539: generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
2540:
2541: }
2542:
2543: /**
2544: * Print the lock discovery information associated with a path.
2545: *
2546: * @param path Path
2547: * @param generatedXML XML data to which the locks info will be appended
2548: * @return true if at least one lock was displayed
2549: */
2550: private boolean generateLockDiscovery(String path,
2551: XMLWriter generatedXML) {
2552:
2553: LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
2554: Enumeration collectionLocksList = collectionLocks.elements();
2555:
2556: boolean wroteStart = false;
2557:
2558: if (resourceLock != null) {
2559: wroteStart = true;
2560: generatedXML.writeElement(null, "lockdiscovery",
2561: XMLWriter.OPENING);
2562: resourceLock.toXML(generatedXML);
2563: }
2564:
2565: while (collectionLocksList.hasMoreElements()) {
2566: LockInfo currentLock = (LockInfo) collectionLocksList
2567: .nextElement();
2568: if (path.startsWith(currentLock.path)) {
2569: if (!wroteStart) {
2570: wroteStart = true;
2571: generatedXML.writeElement(null, "lockdiscovery",
2572: XMLWriter.OPENING);
2573: }
2574: currentLock.toXML(generatedXML);
2575: }
2576: }
2577:
2578: if (wroteStart) {
2579: generatedXML.writeElement(null, "lockdiscovery",
2580: XMLWriter.CLOSING);
2581: } else {
2582: return false;
2583: }
2584:
2585: return true;
2586:
2587: }
2588:
2589: /**
2590: * Get creation date in ISO format.
2591: */
2592: private String getISOCreationDate(long creationDate) {
2593: StringBuffer creationDateValue = new StringBuffer(
2594: creationDateFormat.format(new Date(creationDate)));
2595: /*
2596: int offset = Calendar.getInstance().getTimeZone().getRawOffset()
2597: / 3600000; // FIXME ?
2598: if (offset < 0) {
2599: creationDateValue.append("-");
2600: offset = -offset;
2601: } else if (offset > 0) {
2602: creationDateValue.append("+");
2603: }
2604: if (offset != 0) {
2605: if (offset < 10)
2606: creationDateValue.append("0");
2607: creationDateValue.append(offset + ":00");
2608: } else {
2609: creationDateValue.append("Z");
2610: }
2611: */
2612: return creationDateValue.toString();
2613: }
2614:
2615: // -------------------------------------------------- LockInfo Inner Class
2616:
2617: /**
2618: * Holds a lock information.
2619: */
2620: private class LockInfo {
2621:
2622: // -------------------------------------------------------- Constructor
2623:
2624: /**
2625: * Constructor.
2626: *
2627: * @param pathname Path name of the file
2628: */
2629: public LockInfo() {
2630:
2631: }
2632:
2633: // ------------------------------------------------- Instance Variables
2634:
2635: String path = "/";
2636: String type = "write";
2637: String scope = "exclusive";
2638: int depth = 0;
2639: String owner = "";
2640: Vector tokens = new Vector();
2641: long expiresAt = 0;
2642: Date creationDate = new Date();
2643:
2644: // ----------------------------------------------------- Public Methods
2645:
2646: /**
2647: * Get a String representation of this lock token.
2648: */
2649: public String toString() {
2650:
2651: String result = "Type:" + type + "\n";
2652: result += "Scope:" + scope + "\n";
2653: result += "Depth:" + depth + "\n";
2654: result += "Owner:" + owner + "\n";
2655: result += "Expiration:"
2656: + formats[0].format(new Date(expiresAt)) + "\n";
2657: Enumeration tokensList = tokens.elements();
2658: while (tokensList.hasMoreElements()) {
2659: result += "Token:" + tokensList.nextElement() + "\n";
2660: }
2661: return result;
2662:
2663: }
2664:
2665: /**
2666: * Return true if the lock has expired.
2667: */
2668: public boolean hasExpired() {
2669: return (System.currentTimeMillis() > expiresAt);
2670: }
2671:
2672: /**
2673: * Return true if the lock is exclusive.
2674: */
2675: public boolean isExclusive() {
2676:
2677: return (scope.equals("exclusive"));
2678:
2679: }
2680:
2681: /**
2682: * Get an XML representation of this lock token. This method will
2683: * append an XML fragment to the given XML writer.
2684: */
2685: public void toXML(XMLWriter generatedXML) {
2686: toXML(generatedXML, false);
2687: }
2688:
2689: /**
2690: * Get an XML representation of this lock token. This method will
2691: * append an XML fragment to the given XML writer.
2692: */
2693: public void toXML(XMLWriter generatedXML, boolean showToken) {
2694:
2695: generatedXML.writeElement(null, "activelock",
2696: XMLWriter.OPENING);
2697:
2698: generatedXML.writeElement(null, "locktype",
2699: XMLWriter.OPENING);
2700: generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT);
2701: generatedXML.writeElement(null, "locktype",
2702: XMLWriter.CLOSING);
2703:
2704: generatedXML.writeElement(null, "lockscope",
2705: XMLWriter.OPENING);
2706: generatedXML
2707: .writeElement(null, scope, XMLWriter.NO_CONTENT);
2708: generatedXML.writeElement(null, "lockscope",
2709: XMLWriter.CLOSING);
2710:
2711: generatedXML.writeElement(null, "depth", XMLWriter.OPENING);
2712: if (depth == INFINITY) {
2713: generatedXML.writeText("Infinity");
2714: } else {
2715: generatedXML.writeText("0");
2716: }
2717: generatedXML.writeElement(null, "depth", XMLWriter.CLOSING);
2718:
2719: generatedXML.writeElement(null, "owner", XMLWriter.OPENING);
2720: generatedXML.writeText(owner);
2721: generatedXML.writeElement(null, "owner", XMLWriter.CLOSING);
2722:
2723: generatedXML.writeElement(null, "timeout",
2724: XMLWriter.OPENING);
2725: long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
2726: generatedXML.writeText("Second-" + timeout);
2727: generatedXML.writeElement(null, "timeout",
2728: XMLWriter.CLOSING);
2729:
2730: generatedXML.writeElement(null, "locktoken",
2731: XMLWriter.OPENING);
2732: if (showToken) {
2733: Enumeration tokensList = tokens.elements();
2734: while (tokensList.hasMoreElements()) {
2735: generatedXML.writeElement(null, "href",
2736: XMLWriter.OPENING);
2737: generatedXML.writeText("opaquelocktoken:"
2738: + tokensList.nextElement());
2739: generatedXML.writeElement(null, "href",
2740: XMLWriter.CLOSING);
2741: }
2742: } else {
2743: generatedXML.writeElement(null, "href",
2744: XMLWriter.OPENING);
2745: generatedXML.writeText("opaquelocktoken:dummytoken");
2746: generatedXML.writeElement(null, "href",
2747: XMLWriter.CLOSING);
2748: }
2749: generatedXML.writeElement(null, "locktoken",
2750: XMLWriter.CLOSING);
2751:
2752: generatedXML.writeElement(null, "activelock",
2753: XMLWriter.CLOSING);
2754:
2755: }
2756:
2757: }
2758:
2759: // --------------------------------------------------- Property Inner Class
2760:
2761: private class Property {
2762:
2763: public String name;
2764: public String value;
2765: public String namespace;
2766: public String namespaceAbbrev;
2767: public int status = WebdavStatus.SC_OK;
2768:
2769: }
2770:
2771: };
2772:
2773: // -------------------------------------------------------- WebdavStatus Class
2774:
2775: /**
2776: * Wraps the HttpServletResponse class to abstract the
2777: * specific protocol used. To support other protocols
2778: * we would only need to modify this class and the
2779: * WebDavRetCode classes.
2780: *
2781: * @author Marc Eaddy
2782: * @version 1.0, 16 Nov 1997
2783: */
2784: class WebdavStatus {
2785:
2786: // ----------------------------------------------------- Instance Variables
2787:
2788: /**
2789: * This Hashtable contains the mapping of HTTP and WebDAV
2790: * status codes to descriptive text. This is a static
2791: * variable.
2792: */
2793: private static Hashtable mapStatusCodes = new Hashtable();
2794:
2795: // ------------------------------------------------------ HTTP Status Codes
2796:
2797: /**
2798: * Status code (200) indicating the request succeeded normally.
2799: */
2800: public static final int SC_OK = HttpServletResponse.SC_OK;
2801:
2802: /**
2803: * Status code (201) indicating the request succeeded and created
2804: * a new resource on the server.
2805: */
2806: public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
2807:
2808: /**
2809: * Status code (202) indicating that a request was accepted for
2810: * processing, but was not completed.
2811: */
2812: public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
2813:
2814: /**
2815: * Status code (204) indicating that the request succeeded but that
2816: * there was no new information to return.
2817: */
2818: public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
2819:
2820: /**
2821: * Status code (301) indicating that the resource has permanently
2822: * moved to a new location, and that future references should use a
2823: * new URI with their requests.
2824: */
2825: public static final int SC_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY;
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 = HttpServletResponse.SC_MOVED_TEMPORARILY;
2833:
2834: /**
2835: * Status code (304) indicating that a conditional GET operation
2836: * found that the resource was available and not modified.
2837: */
2838: public static final int SC_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED;
2839:
2840: /**
2841: * Status code (400) indicating the request sent by the client was
2842: * syntactically incorrect.
2843: */
2844: public static final int SC_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST;
2845:
2846: /**
2847: * Status code (401) indicating that the request requires HTTP
2848: * authentication.
2849: */
2850: public static final int SC_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED;
2851:
2852: /**
2853: * Status code (403) indicating the server understood the request
2854: * but refused to fulfill it.
2855: */
2856: public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
2857:
2858: /**
2859: * Status code (404) indicating that the requested resource is not
2860: * available.
2861: */
2862: public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
2863:
2864: /**
2865: * Status code (500) indicating an error inside the HTTP service
2866: * which prevented it from fulfilling the request.
2867: */
2868: public static final int SC_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
2869:
2870: /**
2871: * Status code (501) indicating the HTTP service does not support
2872: * the functionality needed to fulfill the request.
2873: */
2874: public static final int SC_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED;
2875:
2876: /**
2877: * Status code (502) indicating that the HTTP server received an
2878: * invalid response from a server it consulted when acting as a
2879: * proxy or gateway.
2880: */
2881: public static final int SC_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY;
2882:
2883: /**
2884: * Status code (503) indicating that the HTTP service is
2885: * temporarily overloaded, and unable to handle the request.
2886: */
2887: public static final int SC_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE;
2888:
2889: /**
2890: * Status code (100) indicating the client may continue with
2891: * its request. This interim response is used to inform the
2892: * client that the initial part of the request has been
2893: * received and has not yet been rejected by the server.
2894: */
2895: public static final int SC_CONTINUE = 100;
2896:
2897: /**
2898: * Status code (405) indicating the method specified is not
2899: * allowed for the resource.
2900: */
2901: public static final int SC_METHOD_NOT_ALLOWED = 405;
2902:
2903: /**
2904: * Status code (409) indicating that the request could not be
2905: * completed due to a conflict with the current state of the
2906: * resource.
2907: */
2908: public static final int SC_CONFLICT = 409;
2909:
2910: /**
2911: * Status code (412) indicating the precondition given in one
2912: * or more of the request-header fields evaluated to false
2913: * when it was tested on the server.
2914: */
2915: public static final int SC_PRECONDITION_FAILED = 412;
2916:
2917: /**
2918: * Status code (413) indicating the server is refusing to
2919: * process a request because the request entity is larger
2920: * than the server is willing or able to process.
2921: */
2922: public static final int SC_REQUEST_TOO_LONG = 413;
2923:
2924: /**
2925: * Status code (415) indicating the server is refusing to service
2926: * the request because the entity of the request is in a format
2927: * not supported by the requested resource for the requested
2928: * method.
2929: */
2930: public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
2931:
2932: // -------------------------------------------- Extended WebDav status code
2933:
2934: /**
2935: * Status code (207) indicating that the response requires
2936: * providing status for multiple independent operations.
2937: */
2938: public static final int SC_MULTI_STATUS = 207;
2939: // This one colides with HTTP 1.1
2940: // "207 Parital Update OK"
2941:
2942: /**
2943: * Status code (418) indicating the entity body submitted with
2944: * the PATCH method was not understood by the resource.
2945: */
2946: public static final int SC_UNPROCESSABLE_ENTITY = 418;
2947: // This one colides with HTTP 1.1
2948: // "418 Reauthentication Required"
2949:
2950: /**
2951: * Status code (419) indicating that the resource does not have
2952: * sufficient space to record the state of the resource after the
2953: * execution of this method.
2954: */
2955: public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
2956: // This one colides with HTTP 1.1
2957: // "419 Proxy Reauthentication Required"
2958:
2959: /**
2960: * Status code (420) indicating the method was not executed on
2961: * a particular resource within its scope because some part of
2962: * the method's execution failed causing the entire method to be
2963: * aborted.
2964: */
2965: public static final int SC_METHOD_FAILURE = 420;
2966:
2967: /**
2968: * Status code (423) indicating the destination resource of a
2969: * method is locked, and either the request did not contain a
2970: * valid Lock-Info header, or the Lock-Info header identifies
2971: * a lock held by another principal.
2972: */
2973: public static final int SC_LOCKED = 423;
2974:
2975: // ------------------------------------------------------------ Initializer
2976:
2977: static {
2978: // HTTP 1.0 tatus Code
2979: addStatusCodeMap(SC_OK, "OK");
2980: addStatusCodeMap(SC_CREATED, "Created");
2981: addStatusCodeMap(SC_ACCEPTED, "Accepted");
2982: addStatusCodeMap(SC_NO_CONTENT, "No Content");
2983: addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
2984: addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
2985: addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
2986: addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
2987: addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
2988: addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
2989: addStatusCodeMap(SC_NOT_FOUND, "Not Found");
2990: addStatusCodeMap(SC_INTERNAL_SERVER_ERROR,
2991: "Internal Server Error");
2992: addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
2993: addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
2994: addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
2995: addStatusCodeMap(SC_CONTINUE, "Continue");
2996: addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
2997: addStatusCodeMap(SC_CONFLICT, "Conflict");
2998: addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
2999: addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
3000: addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE,
3001: "Unsupported Media Type");
3002: // WebDav Status Codes
3003: addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
3004: addStatusCodeMap(SC_UNPROCESSABLE_ENTITY,
3005: "Unprocessable Entity");
3006: addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
3007: "Insufficient Space On Resource");
3008: addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
3009: addStatusCodeMap(SC_LOCKED, "Locked");
3010: }
3011:
3012: // --------------------------------------------------------- Public Methods
3013:
3014: /**
3015: * Returns the HTTP status text for the HTTP or WebDav status code
3016: * specified by looking it up in the static mapping. This is a
3017: * static function.
3018: *
3019: * @param nHttpStatusCode [IN] HTTP or WebDAV status code
3020: * @return A string with a short descriptive phrase for the
3021: * HTTP status code (e.g., "OK").
3022: */
3023: public static String getStatusText(int nHttpStatusCode) {
3024: Integer intKey = new Integer(nHttpStatusCode);
3025:
3026: if (!mapStatusCodes.containsKey(intKey)) {
3027: return "";
3028: } else {
3029: return (String) mapStatusCodes.get(intKey);
3030: }
3031: }
3032:
3033: // -------------------------------------------------------- Private Methods
3034:
3035: /**
3036: * Adds a new status code -> status text mapping. This is a static
3037: * method because the mapping is a static variable.
3038: *
3039: * @param nKey [IN] HTTP or WebDAV status code
3040: * @param strVal [IN] HTTP status text
3041: */
3042: private static void addStatusCodeMap(int nKey, String strVal) {
3043: mapStatusCodes.put(new Integer(nKey), strVal);
3044: }
3045:
3046: };
|