/* * Copyright 1999,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ejie.x38.webdav.methods; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import com.ejie.x38.webdav.IMimeTyper; import com.ejie.x38.webdav.ITransaction; import com.ejie.x38.webdav.IWebdavStore; import com.ejie.x38.webdav.StoredObject; import com.ejie.x38.webdav.WebdavStatus; import com.ejie.x38.webdav.exceptions.AccessDeniedException; import com.ejie.x38.webdav.exceptions.LockFailedException; import com.ejie.x38.webdav.exceptions.WebdavException; import com.ejie.x38.webdav.fromcatalina.XMLHelper; import com.ejie.x38.webdav.fromcatalina.XMLWriter; import com.ejie.x38.webdav.locking.LockedObject; import com.ejie.x38.webdav.locking.IResourceLocks; public class DoPropfind extends AbstractMethod { private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory .getLogger(DoPropfind.class); /** * PROPFIND - Specify a property mask. */ private static final int FIND_BY_PROPERTY = 0; /** * PROPFIND - Display all properties. */ private static final int FIND_ALL_PROP = 1; /** * PROPFIND - Return property names. */ private static final int FIND_PROPERTY_NAMES = 2; private IWebdavStore _store; private IResourceLocks _resourceLocks; private IMimeTyper _mimeTyper; private int _depth; public DoPropfind(IWebdavStore store, IResourceLocks resLocks, IMimeTyper mimeTyper) { _store = store; _resourceLocks = resLocks; _mimeTyper = mimeTyper; } public void execute(ITransaction transaction, HttpServletRequest req, HttpServletResponse resp) throws IOException, LockFailedException { LOG.trace("-- " + this.getClass().getName()); // Retrieve the resources String path = getCleanPath(getRelativePath(req)); String tempLockOwner = "doPropfind" + System.currentTimeMillis() + req.toString(); _depth = getDepth(req); if (_resourceLocks.lock(transaction, path, tempLockOwner, false, _depth, TEMP_TIMEOUT, TEMPORARY)) { StoredObject so = null; try { so = _store.getStoredObject(transaction, path); if (so == null) { resp.setContentType("text/xml; charset=UTF-8"); resp.sendError(HttpServletResponse.SC_NOT_FOUND, req .getRequestURI()); return; } List<String> properties = null; path = getCleanPath(getRelativePath(req)); int propertyFindType = FIND_ALL_PROP; Node propNode = null; // Windows 7 does a propfind with content length 0 if (req.getContentLength() > 0) { DocumentBuilder documentBuilder = getDocumentBuilder(); try { Document document = documentBuilder .parse(new InputSource(req.getInputStream())); // Get the root element of the document Element rootElement = document.getDocumentElement(); propNode = XMLHelper .findSubElement(rootElement, "prop"); if (propNode != null) { propertyFindType = FIND_BY_PROPERTY; } else if (XMLHelper.findSubElement(rootElement, "propname") != null) { propertyFindType = FIND_PROPERTY_NAMES; } else if (XMLHelper.findSubElement(rootElement, "allprop") != null) { propertyFindType = FIND_ALL_PROP; } } catch (Exception e) { resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); return; } } else { // no content, which means it is a allprop request propertyFindType = FIND_ALL_PROP; } HashMap<String, String> namespaces = new HashMap<String, String>(); namespaces.put("DAV:", "D"); if (propertyFindType == FIND_BY_PROPERTY) { propertyFindType = 0; properties = XMLHelper.getPropertiesFromXML(propNode); } resp.setStatus(WebdavStatus.SC_MULTI_STATUS); resp.setContentType("text/xml; charset=UTF-8"); // Create multistatus object XMLWriter generatedXML = new XMLWriter(resp.getWriter(), namespaces); generatedXML.writeXMLHeader(); generatedXML .writeElement("DAV::multistatus", XMLWriter.OPENING); String mimeType; if (_mimeTyper!=null){ mimeType = _mimeTyper.getMimeType(path); }else{ mimeType = req.getSession().getServletContext().getMimeType(path); } if (_depth == 0) { parseProperties(transaction, req, generatedXML, path, propertyFindType, properties, mimeType); } else { recursiveParseProperties(transaction, path, req, generatedXML, propertyFindType, properties, _depth, mimeType); } generatedXML .writeElement("DAV::multistatus", XMLWriter.CLOSING); generatedXML.sendData(); } catch (AccessDeniedException e) { resp.sendError(WebdavStatus.SC_FORBIDDEN); } catch (WebdavException e) { LOG.warn("Sending internal error!"); resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); } catch (ServletException e) { e.printStackTrace(); // To change body of catch statement use // File | Settings | File Templates. } finally { _resourceLocks.unlockTemporaryLockedObjects(transaction, path, tempLockOwner); } } else { Hashtable<String, Integer> errorList = new Hashtable<String, Integer>(); errorList.put(path, WebdavStatus.SC_LOCKED); sendReport(req, resp, errorList); } } /** * goes recursive through all folders. used by propfind * * @param currentPath * the current path * @param req * HttpServletRequest * @param generatedXML * @param propertyFindType * @param properties * @param depth * depth of the propfind * @throws IOException * if an error in the underlying store occurs */ private void recursiveParseProperties(ITransaction transaction, String currentPath, HttpServletRequest req, XMLWriter generatedXML, int propertyFindType, List<String> properties, int depth, String mimeType) throws WebdavException { parseProperties(transaction, req, generatedXML, currentPath, propertyFindType, properties, mimeType); if (depth > 0) { // no need to get name if depth is already zero String[] names = _store.getChildrenNames(transaction, currentPath); names = names == null ? new String[] {} : names; String newPath = null; for (String name : names) { newPath = currentPath; if (!(newPath.endsWith("/"))) { newPath += "/"; } newPath += name; recursiveParseProperties(transaction, newPath, req, generatedXML, propertyFindType, properties, depth - 1, mimeType); } } } /** * Propfind helper method. * * @param req * The servlet request * @param generatedXML * XML response to the Propfind request * @param path * Path of the current resource * @param type * Propfind type * @param propertiesVector * If the propfind type is find properties by name, then this Vector * contains those properties */ private void parseProperties(ITransaction transaction, HttpServletRequest req, XMLWriter generatedXML, String path, int type, List<String> propertiesVector, String mimeType) throws WebdavException { StoredObject so = _store.getStoredObject(transaction, path); boolean isFolder = so.isFolder(); String creationdate = CREATION_DATE_FORMAT.format(so.getCreationDate()); String lastModified = LAST_MODIFIED_DATE_FORMAT.format(so .getLastModified()); String resourceLength = String.valueOf(so.getResourceLength()); // ResourceInfo resourceInfo = new ResourceInfo(path, resources); generatedXML.writeElement("DAV::response", XMLWriter.OPENING); String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK)); // Generating href element generatedXML.writeElement("DAV::href", XMLWriter.OPENING); String href = req.getContextPath(); String servletPath = req.getServletPath(); if (servletPath != null) { if ((href.endsWith("/")) && (servletPath.startsWith("/"))) href += servletPath.substring(1); else href += servletPath; } if ((href.endsWith("/")) && (path.startsWith("/"))) href += path.substring(1); else href += path; if ((isFolder) && (!href.endsWith("/"))) href += "/"; generatedXML.writeText(rewriteUrl(href)); generatedXML.writeElement("DAV::href", XMLWriter.CLOSING); String resourceName = path; int lastSlash = path.lastIndexOf('/'); if (lastSlash != -1) resourceName = resourceName.substring(lastSlash + 1); switch (type) { case FIND_ALL_PROP: generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING); generatedXML.writeElement("DAV::prop", XMLWriter.OPENING); generatedXML.writeProperty("DAV::creationdate", creationdate); generatedXML.writeElement("DAV::displayname", XMLWriter.OPENING); generatedXML.writeData(resourceName); generatedXML.writeElement("DAV::displayname", XMLWriter.CLOSING); if (!isFolder) { generatedXML .writeProperty("DAV::getlastmodified", lastModified); generatedXML.writeProperty("DAV::getcontentlength", resourceLength); String contentType = mimeType; if (contentType != null) { generatedXML.writeProperty("DAV::getcontenttype", contentType); } generatedXML.writeProperty("DAV::getetag", getETag(so)); generatedXML.writeElement("DAV::resourcetype", XMLWriter.NO_CONTENT); } else { generatedXML.writeElement("DAV::resourcetype", XMLWriter.OPENING); generatedXML.writeElement("DAV::collection", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::resourcetype", XMLWriter.CLOSING); } writeSupportedLockElements(transaction, generatedXML, path); writeLockDiscoveryElements(transaction, generatedXML, path); generatedXML.writeProperty("DAV::source", ""); generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING); generatedXML.writeElement("DAV::status", XMLWriter.OPENING); generatedXML.writeText(status); generatedXML.writeElement("DAV::status", XMLWriter.CLOSING); generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING); break; case FIND_PROPERTY_NAMES: generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING); generatedXML.writeElement("DAV::prop", XMLWriter.OPENING); generatedXML .writeElement("DAV::creationdate", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::displayname", XMLWriter.NO_CONTENT); if (!isFolder) { generatedXML.writeElement("DAV::getcontentlanguage", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::getcontentlength", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::getcontenttype", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::getetag", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::getlastmodified", XMLWriter.NO_CONTENT); } generatedXML .writeElement("DAV::resourcetype", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::supportedlock", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::source", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING); generatedXML.writeElement("DAV::status", XMLWriter.OPENING); generatedXML.writeText(status); generatedXML.writeElement("DAV::status", XMLWriter.CLOSING); generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING); break; case FIND_BY_PROPERTY: List<String> propertiesNotFound = new ArrayList<String>(); // Parse the list of properties generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING); generatedXML.writeElement("DAV::prop", XMLWriter.OPENING); Iterator<String> properties = propertiesVector.iterator(); while (properties.hasNext()) { String property = properties.next(); if (property.equals("DAV::creationdate")) { generatedXML.writeProperty("DAV::creationdate", creationdate); } else if (property.equals("DAV::displayname")) { generatedXML.writeElement("DAV::displayname", XMLWriter.OPENING); generatedXML.writeData(resourceName); generatedXML.writeElement("DAV::displayname", XMLWriter.CLOSING); } else if (property.equals("DAV::getcontentlanguage")) { if (isFolder) { propertiesNotFound.add(property); } else { generatedXML.writeElement("DAV::getcontentlanguage", XMLWriter.NO_CONTENT); } } else if (property.equals("DAV::getcontentlength")) { if (isFolder) { propertiesNotFound.add(property); } else { generatedXML.writeProperty("DAV::getcontentlength", resourceLength); } } else if (property.equals("DAV::getcontenttype")) { if (isFolder) { propertiesNotFound.add(property); } else { generatedXML.writeProperty("DAV::getcontenttype", mimeType); } } else if (property.equals("DAV::getetag")) { if (isFolder || so.isNullResource()) { propertiesNotFound.add(property); } else { generatedXML.writeProperty("DAV::getetag", getETag(so)); } } else if (property.equals("DAV::getlastmodified")) { if (isFolder) { propertiesNotFound.add(property); } else { generatedXML.writeProperty("DAV::getlastmodified", lastModified); } } else if (property.equals("DAV::resourcetype")) { if (isFolder) { generatedXML.writeElement("DAV::resourcetype", XMLWriter.OPENING); generatedXML.writeElement("DAV::collection", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::resourcetype", XMLWriter.CLOSING); } else { generatedXML.writeElement("DAV::resourcetype", XMLWriter.NO_CONTENT); } } else if (property.equals("DAV::source")) { generatedXML.writeProperty("DAV::source", ""); } else if (property.equals("DAV::supportedlock")) { writeSupportedLockElements(transaction, generatedXML, path); } else if (property.equals("DAV::lockdiscovery")) { writeLockDiscoveryElements(transaction, generatedXML, path); } else { propertiesNotFound.add(property); } } generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING); generatedXML.writeElement("DAV::status", XMLWriter.OPENING); generatedXML.writeText(status); generatedXML.writeElement("DAV::status", XMLWriter.CLOSING); generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING); Iterator<String> propertiesNotFoundList = propertiesNotFound .iterator(); if (propertiesNotFoundList.hasNext()) { status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " " + WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND)); generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING); generatedXML.writeElement("DAV::prop", XMLWriter.OPENING); while (propertiesNotFoundList.hasNext()) { generatedXML.writeElement((String) propertiesNotFoundList .next(), XMLWriter.NO_CONTENT); } generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING); generatedXML.writeElement("DAV::status", XMLWriter.OPENING); generatedXML.writeText(status); generatedXML.writeElement("DAV::status", XMLWriter.CLOSING); generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING); } break; } generatedXML.writeElement("DAV::response", XMLWriter.CLOSING); so = null; } private void writeSupportedLockElements(ITransaction transaction, XMLWriter generatedXML, String path) { LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction, path); generatedXML.writeElement("DAV::supportedlock", XMLWriter.OPENING); if (lo == null) { // both locks (shared/exclusive) can be granted generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING); generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING); generatedXML.writeElement("DAV::exclusive", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING); generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING); generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING); generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING); generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING); generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING); generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING); generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING); generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING); generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING); } else { // LockObject exists, checking lock state // if an exclusive lock exists, no further lock is possible if (lo.isShared()) { generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING); generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING); generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING); generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING); generatedXML.writeElement("DAV::" + lo.getType(), XMLWriter.NO_CONTENT); generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING); generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING); } } generatedXML.writeElement("DAV::supportedlock", XMLWriter.CLOSING); lo = null; } private void writeLockDiscoveryElements(ITransaction transaction, XMLWriter generatedXML, String path) { LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction, path); if (lo != null && !lo.hasExpired()) { generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING); generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING); generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING); generatedXML.writeProperty("DAV::" + lo.getType()); generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING); generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING); if (lo.isExclusive()) { generatedXML.writeProperty("DAV::exclusive"); } else { generatedXML.writeProperty("DAV::shared"); } generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING); generatedXML.writeElement("DAV::depth", XMLWriter.OPENING); if (_depth == INFINITY) { generatedXML.writeText("Infinity"); } else { generatedXML.writeText(String.valueOf(_depth)); } generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING); String[] owners = lo.getOwner(); if (owners != null) { for (int i = 0; i < owners.length; i++) { generatedXML.writeElement("DAV::owner", XMLWriter.OPENING); generatedXML.writeElement("DAV::href", XMLWriter.OPENING); generatedXML.writeText(owners[i]); generatedXML.writeElement("DAV::href", XMLWriter.CLOSING); generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING); } } else { generatedXML.writeElement("DAV::owner", XMLWriter.NO_CONTENT); } int timeout = (int) (lo.getTimeoutMillis() / 1000); String timeoutStr = new Integer(timeout).toString(); generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING); generatedXML.writeText("Second-" + timeoutStr); generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING); String lockToken = lo.getID(); generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING); generatedXML.writeElement("DAV::href", XMLWriter.OPENING); generatedXML.writeText("opaquelocktoken:" + lockToken); generatedXML.writeElement("DAV::href", XMLWriter.CLOSING); generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING); generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING); generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING); } else { generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.NO_CONTENT); } lo = null; } }