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