/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.webdav.methods; import com.liferay.portal.kernel.lock.Lock; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.WebDAVProps; import com.liferay.portal.kernel.service.WebDAVPropsLocalServiceUtil; import com.liferay.portal.kernel.servlet.ServletResponseUtil; import com.liferay.portal.kernel.util.ContentTypes; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.Time; import com.liferay.portal.kernel.webdav.Resource; import com.liferay.portal.kernel.webdav.WebDAVRequest; import com.liferay.portal.kernel.webdav.WebDAVStorage; import com.liferay.portal.kernel.webdav.WebDAVUtil; import com.liferay.portal.kernel.webdav.methods.Method; import com.liferay.portal.kernel.xml.Document; import com.liferay.portal.kernel.xml.Element; import com.liferay.portal.kernel.xml.Namespace; import com.liferay.portal.kernel.xml.QName; import com.liferay.portal.kernel.xml.SAXReaderUtil; import com.liferay.util.xml.DocUtil; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletResponse; /** * @author Alexander Chow */ public abstract class BasePropMethodImpl implements Method { public static final QName ALLPROP = createQName("allprop"); public static final QName CREATIONDATE = createQName("creationdate"); public static final QName DISPLAYNAME = createQName("displayname"); public static final QName GETCONTENTLENGTH = createQName( "getcontentlength"); public static final QName GETCONTENTTYPE = createQName("getcontenttype"); public static final QName GETLASTMODIFIED = createQName("getlastmodified"); public static final QName ISREADONLY = createQName("isreadonly"); public static final QName LOCKDISCOVERY = createQName("lockdiscovery"); public static final QName RESOURCETYPE = createQName("resourcetype"); protected static QName createQName(String name) { return SAXReaderUtil.createQName(name, WebDAVUtil.DAV_URI); } protected void addResponse(String href, Element multistatusElement) throws Exception { Element responseElement = DocUtil.add( multistatusElement, createQName("response")); DocUtil.add(responseElement, createQName("href"), href); Element propstatElement = DocUtil.add( responseElement, createQName("propstat")); DocUtil.add( propstatElement, createQName("status"), "HTTP/1.1 404 Not Found"); } protected void addResponse( WebDAVRequest webDAVRequest, Resource resource, Set<QName> props, Element multistatus) throws Exception { // Make a deep copy of the props props = new HashSet<>(props); // Start building multistatus response Element responseElement = DocUtil.add( multistatus, createQName("response")); DocUtil.add(responseElement, createQName("href"), resource.getHREF()); // Build success and failure propstat elements Element successStatElement = DocUtil.add( responseElement, createQName("propstat")); Element successPropElement = DocUtil.add( successStatElement, createQName("prop")); Element failureStatElement = DocUtil.add( responseElement, createQName("propstat")); Element failurePropElement = DocUtil.add( failureStatElement, createQName("prop")); boolean hasSuccess = false; boolean hasFailure = false; // Check DAV properties if (props.contains(ALLPROP)) { props.remove(ALLPROP); if (resource.isCollection()) { props.addAll(_allCollectionProps); } else { props.addAll(_allSimpleProps); } } if (props.contains(CREATIONDATE)) { props.remove(CREATIONDATE); DocUtil.add( successPropElement, CREATIONDATE, resource.getCreateDate()); hasSuccess = true; } if (props.contains(DISPLAYNAME)) { props.remove(DISPLAYNAME); DocUtil.add( successPropElement, DISPLAYNAME, resource.getDisplayName()); hasSuccess = true; } if (props.contains(GETLASTMODIFIED)) { props.remove(GETLASTMODIFIED); DocUtil.add( successPropElement, GETLASTMODIFIED, resource.getModifiedDate()); hasSuccess = true; } if (props.contains(GETCONTENTTYPE)) { props.remove(GETCONTENTTYPE); DocUtil.add( successPropElement, GETCONTENTTYPE, resource.getContentType()); hasSuccess = true; } if (props.contains(GETCONTENTLENGTH)) { props.remove(GETCONTENTLENGTH); if (!resource.isCollection()) { DocUtil.add( successPropElement, GETCONTENTLENGTH, resource.getSize()); hasSuccess = true; } else { DocUtil.add(failurePropElement, GETCONTENTLENGTH); hasFailure = true; } } if (props.contains(ISREADONLY)) { props.remove(ISREADONLY); Lock lock = resource.getLock(); if ((lock == null) || resource.isLocked()) { DocUtil.add( successPropElement, ISREADONLY, Boolean.FALSE.toString()); } else { DocUtil.add( successPropElement, ISREADONLY, Boolean.TRUE.toString()); } hasSuccess = true; } if (props.contains(LOCKDISCOVERY)) { props.remove(LOCKDISCOVERY); Lock lock = resource.getLock(); if (lock != null) { Element lockDiscoveryElement = DocUtil.add( successPropElement, LOCKDISCOVERY); Element activeLockElement = DocUtil.add( lockDiscoveryElement, createQName("activelock")); Element lockTypeElement = DocUtil.add( activeLockElement, createQName("locktype")); DocUtil.add(lockTypeElement, createQName("write")); Element lockScopeElement = DocUtil.add( activeLockElement, createQName("lockscope")); DocUtil.add(lockScopeElement, createQName("exclusive")); if (resource.isCollection()) { DocUtil.add( activeLockElement, createQName("depth"), "Infinity"); } DocUtil.add( activeLockElement, createQName("owner"), lock.getOwner()); long timeRemaining = 0; Date expirationDate = lock.getExpirationDate(); if (expirationDate != null) { long now = System.currentTimeMillis(); timeRemaining = (expirationDate.getTime() - now) / Time.SECOND; if (timeRemaining <= 0) { timeRemaining = 1; } } if (timeRemaining > 0) { DocUtil.add( activeLockElement, createQName("timeout"), "Second-" + timeRemaining); } else { DocUtil.add( activeLockElement, createQName("timeout"), "Infinite"); } if (webDAVRequest.getUserId() == lock.getUserId()) { Element lockTokenElement = DocUtil.add( activeLockElement, createQName("locktoken")); DocUtil.add( lockTokenElement, createQName("href"), "opaquelocktoken:" + lock.getUuid()); } hasSuccess = true; } else { DocUtil.add(failurePropElement, LOCKDISCOVERY); hasFailure = true; } } if (props.contains(RESOURCETYPE)) { props.remove(RESOURCETYPE); Element resourceTypeElement = DocUtil.add( successPropElement, RESOURCETYPE); if (resource.isCollection()) { DocUtil.add(resourceTypeElement, createQName("collection")); } hasSuccess = true; } // Check remaining properties against custom properties WebDAVProps webDavProps = WebDAVPropsLocalServiceUtil.getWebDAVProps( webDAVRequest.getCompanyId(), resource.getClassName(), resource.getPrimaryKey()); Set<QName> customProps = webDavProps.getPropsSet(); for (QName qname : props) { String name = qname.getName(); Namespace namespace = qname.getNamespace(); String prefix = namespace.getPrefix(); String uri = namespace.getURI(); if (customProps.contains(qname)) { String text = webDavProps.getText(name, prefix, uri); DocUtil.add(successPropElement, qname, text); hasSuccess = true; } else { DocUtil.add(failurePropElement, qname); hasFailure = true; } } // Clean up propstats if (hasSuccess) { DocUtil.add( successStatElement, createQName("status"), "HTTP/1.1 200 OK"); } else { responseElement.remove(successStatElement); } if (!hasSuccess && hasFailure) { DocUtil.add( failureStatElement, createQName("status"), "HTTP/1.1 404 Not Found"); } else { responseElement.remove(failureStatElement); } } protected void addResponse( WebDAVStorage storage, WebDAVRequest webDAVRequest, Resource resource, Set<QName> props, Element multistatusElement, long depth) throws Exception { addResponse(webDAVRequest, resource, props, multistatusElement); if (resource.isCollection() && (depth != 0)) { List<Resource> storageResources = storage.getResources( webDAVRequest); for (Resource storageResource : storageResources) { addResponse( webDAVRequest, storageResource, props, multistatusElement); } } } protected int writeResponseXML( WebDAVRequest webDAVRequest, Set<QName> props) throws Exception { WebDAVStorage storage = webDAVRequest.getWebDAVStorage(); long depth = WebDAVUtil.getDepth(webDAVRequest.getHttpServletRequest()); Document document = SAXReaderUtil.createDocument(); Element multistatusElement = SAXReaderUtil.createElement( createQName("multistatus")); document.setRootElement(multistatusElement); Resource resource = storage.getResource(webDAVRequest); if (resource != null) { addResponse( storage, webDAVRequest, resource, props, multistatusElement, depth); String xml = document.formattedString(StringPool.FOUR_SPACES); if (_log.isDebugEnabled()) { _log.debug("Response XML\n" + xml); } // Set the status prior to writing the XML int status = WebDAVUtil.SC_MULTI_STATUS; HttpServletResponse response = webDAVRequest.getHttpServletResponse(); response.setContentType(ContentTypes.TEXT_XML_UTF8); response.setStatus(status); try { ServletResponseUtil.write(response, xml); response.flushBuffer(); } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e); } } return status; } if (_log.isDebugEnabled()) { _log.debug( "No resource found for " + storage.getRootPath() + webDAVRequest.getPath()); } return HttpServletResponse.SC_NOT_FOUND; } private static final Log _log = LogFactoryUtil.getLog( BasePropMethodImpl.class); private static final List<QName> _allCollectionProps = Arrays.asList( new QName[] { CREATIONDATE, DISPLAYNAME, GETLASTMODIFIED, GETCONTENTTYPE, LOCKDISCOVERY, RESOURCETYPE }); private static final List<QName> _allSimpleProps = Arrays.asList( new QName[] { CREATIONDATE, DISPLAYNAME, GETLASTMODIFIED, GETCONTENTTYPE, GETCONTENTLENGTH, ISREADONLY, LOCKDISCOVERY, RESOURCETYPE }); }