001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.webdav.methods;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.servlet.ServletResponseUtil;
020    import com.liferay.portal.kernel.util.ContentTypes;
021    import com.liferay.portal.kernel.util.StringPool;
022    import com.liferay.portal.kernel.util.Time;
023    import com.liferay.portal.kernel.webdav.Resource;
024    import com.liferay.portal.kernel.webdav.WebDAVRequest;
025    import com.liferay.portal.kernel.webdav.WebDAVStorage;
026    import com.liferay.portal.kernel.webdav.WebDAVUtil;
027    import com.liferay.portal.kernel.xml.Document;
028    import com.liferay.portal.kernel.xml.Element;
029    import com.liferay.portal.kernel.xml.Namespace;
030    import com.liferay.portal.kernel.xml.QName;
031    import com.liferay.portal.kernel.xml.SAXReaderUtil;
032    import com.liferay.portal.model.Lock;
033    import com.liferay.portal.model.WebDAVProps;
034    import com.liferay.portal.service.WebDAVPropsLocalServiceUtil;
035    import com.liferay.util.xml.DocUtil;
036    
037    import java.util.Arrays;
038    import java.util.Date;
039    import java.util.HashSet;
040    import java.util.List;
041    import java.util.Set;
042    
043    import javax.servlet.http.HttpServletResponse;
044    
045    /**
046     * @author Alexander Chow
047     */
048    public abstract class BasePropMethodImpl implements Method {
049    
050            public static final QName ALLPROP = createQName("allprop");
051    
052            public static final QName CREATIONDATE = createQName("creationdate");
053    
054            public static final QName DISPLAYNAME = createQName("displayname");
055    
056            public static final QName GETCONTENTLENGTH = createQName(
057                    "getcontentlength");
058    
059            public static final QName GETCONTENTTYPE = createQName("getcontenttype");
060    
061            public static final QName GETLASTMODIFIED = createQName("getlastmodified");
062    
063            public static final QName ISREADONLY = createQName("isreadonly");
064    
065            public static final QName LOCKDISCOVERY = createQName("lockdiscovery");
066    
067            public static final QName RESOURCETYPE = createQName("resourcetype");
068    
069            protected static QName createQName(String name) {
070                    return SAXReaderUtil.createQName(name, WebDAVUtil.DAV_URI);
071            }
072    
073            protected void addResponse(String href, Element multistatusElement)
074                    throws Exception {
075    
076                    Element responseElement = DocUtil.add(
077                            multistatusElement, createQName("response"));
078    
079                    DocUtil.add(responseElement, createQName("href"), href);
080    
081                    Element propstatElement = DocUtil.add(
082                            responseElement, createQName("propstat"));
083    
084                    DocUtil.add(
085                            propstatElement, createQName("status"), "HTTP/1.1 404 Not Found");
086            }
087    
088            protected void addResponse(
089                            WebDAVRequest webDAVRequest, Resource resource, Set<QName> props,
090                            Element multistatus)
091                    throws Exception {
092    
093                    // Make a deep copy of the props
094    
095                    props = new HashSet<QName>(props);
096    
097                    // Start building multistatus response
098    
099                    Element responseElement = DocUtil.add(
100                            multistatus, createQName("response"));
101    
102                    DocUtil.add(responseElement, createQName("href"), resource.getHREF());
103    
104                    // Build success and failure propstat elements
105    
106                    Element successStatElement = DocUtil.add(
107                            responseElement, createQName("propstat"));
108                    Element successPropElement = DocUtil.add(
109                            successStatElement, createQName("prop"));
110                    Element failureStatElement = DocUtil.add(
111                            responseElement, createQName("propstat"));
112                    Element failurePropElement = DocUtil.add(
113                            failureStatElement, createQName("prop"));
114    
115                    boolean hasSuccess = false;
116                    boolean hasFailure = false;
117    
118                    // Check DAV properties
119    
120                    if (props.contains(ALLPROP)) {
121                            props.remove(ALLPROP);
122    
123                            if (resource.isCollection()) {
124                                    props.addAll(_ALL_COLLECTION_PROPS);
125                            }
126                            else {
127                                    props.addAll(_ALL_SIMPLE_PROPS);
128                            }
129                    }
130    
131                    if (props.contains(CREATIONDATE)) {
132                            props.remove(CREATIONDATE);
133    
134                            DocUtil.add(
135                                    successPropElement, CREATIONDATE, resource.getCreateDate());
136    
137                            hasSuccess = true;
138                    }
139    
140                    if (props.contains(DISPLAYNAME)) {
141                            props.remove(DISPLAYNAME);
142    
143                            DocUtil.add(
144                                    successPropElement, DISPLAYNAME, resource.getDisplayName());
145    
146                            hasSuccess = true;
147                    }
148    
149                    if (props.contains(GETLASTMODIFIED)) {
150                            props.remove(GETLASTMODIFIED);
151    
152                            DocUtil.add(
153                                    successPropElement, GETLASTMODIFIED,
154                                    resource.getModifiedDate());
155    
156                            hasSuccess = true;
157                    }
158    
159                    if (props.contains(GETCONTENTTYPE)) {
160                            props.remove(GETCONTENTTYPE);
161    
162                            DocUtil.add(
163                                    successPropElement, GETCONTENTTYPE, resource.getContentType());
164    
165                            hasSuccess = true;
166                    }
167    
168                    if (props.contains(GETCONTENTLENGTH)) {
169                            props.remove(GETCONTENTLENGTH);
170    
171                            if (!resource.isCollection()) {
172                                    DocUtil.add(
173                                            successPropElement, GETCONTENTLENGTH, resource.getSize());
174    
175                                    hasSuccess = true;
176                            }
177                            else {
178                                    DocUtil.add(failurePropElement, GETCONTENTLENGTH);
179    
180                                    hasFailure = true;
181                            }
182                    }
183    
184                    if (props.contains(ISREADONLY)) {
185                            props.remove(ISREADONLY);
186    
187                            Lock lock = resource.getLock();
188    
189                            if ((lock == null) || resource.isLocked()) {
190                                    DocUtil.add(
191                                            successPropElement, ISREADONLY, Boolean.FALSE.toString());
192                            }
193                            else {
194                                    DocUtil.add(
195                                            successPropElement, ISREADONLY, Boolean.TRUE.toString());
196                            }
197    
198                            hasSuccess = true;
199                    }
200    
201                    if (props.contains(LOCKDISCOVERY)) {
202                            props.remove(LOCKDISCOVERY);
203    
204                            Lock lock = resource.getLock();
205    
206                            if (lock != null) {
207                                    Element lockDiscoveryElement = DocUtil.add(
208                                            successPropElement, LOCKDISCOVERY);
209    
210                                    Element activeLockElement = DocUtil.add(
211                                            lockDiscoveryElement, createQName("activelock"));
212    
213                                    Element lockTypeElement = DocUtil.add(
214                                            activeLockElement, createQName("locktype"));
215    
216                                    DocUtil.add(lockTypeElement, createQName("write"));
217    
218                                    Element lockScopeElement = DocUtil.add(
219                                            activeLockElement, createQName("lockscope"));
220    
221                                    DocUtil.add(lockScopeElement, createQName("exclusive"));
222    
223                                    if (resource.isCollection()) {
224                                            DocUtil.add(
225                                                    activeLockElement, createQName("depth"), "Infinity");
226                                    }
227    
228                                    DocUtil.add(
229                                            activeLockElement, createQName("owner"), lock.getOwner());
230    
231                                    long timeRemaining = 0;
232    
233                                    Date expirationDate = lock.getExpirationDate();
234    
235                                    if (expirationDate != null) {
236                                            long now = System.currentTimeMillis();
237    
238                                            timeRemaining =
239                                                    (expirationDate.getTime() - now) / Time.SECOND;
240    
241                                            if (timeRemaining <= 0) {
242                                                    timeRemaining = 1;
243                                            }
244                                    }
245    
246                                    if (timeRemaining > 0) {
247                                            DocUtil.add(
248                                                    activeLockElement, createQName("timeout"),
249                                                    "Second-" + timeRemaining);
250                                    }
251                                    else {
252                                            DocUtil.add(
253                                                    activeLockElement, createQName("timeout"), "Infinite");
254                                    }
255    
256                                    if (webDAVRequest.getUserId() == lock.getUserId()) {
257                                            Element lockTokenElement = DocUtil.add(
258                                                    activeLockElement, createQName("locktoken"));
259    
260                                            DocUtil.add(
261                                                    lockTokenElement, createQName("href"),
262                                                    "opaquelocktoken:" + lock.getUuid());
263                                    }
264    
265                                    hasSuccess = true;
266                            }
267                            else {
268                                    DocUtil.add(failurePropElement, LOCKDISCOVERY);
269    
270                                    hasFailure = true;
271                            }
272                    }
273    
274                    if (props.contains(RESOURCETYPE)) {
275                            props.remove(RESOURCETYPE);
276    
277                            Element resourceTypeElement = DocUtil.add(
278                                    successPropElement, RESOURCETYPE);
279    
280                            if (resource.isCollection()) {
281                                    DocUtil.add(resourceTypeElement, createQName("collection"));
282                            }
283    
284                            hasSuccess = true;
285                    }
286    
287                    // Check remaining properties against custom properties
288    
289                    WebDAVProps webDavProps = WebDAVPropsLocalServiceUtil.getWebDAVProps(
290                            webDAVRequest.getCompanyId(), resource.getClassName(),
291                            resource.getPrimaryKey());
292    
293                    Set<QName> customProps = webDavProps.getPropsSet();
294    
295                    for (QName qname : props) {
296                            String name = qname.getName();
297                            Namespace namespace = qname.getNamespace();
298    
299                            String prefix = namespace.getPrefix();
300                            String uri = namespace.getURI();
301    
302                            if (customProps.contains(qname)) {
303                                    String text = webDavProps.getText(name, prefix, uri);
304    
305                                    DocUtil.add(successPropElement, qname, text);
306    
307                                    hasSuccess = true;
308                            }
309                            else {
310                                    DocUtil.add(failurePropElement, qname);
311    
312                                    hasFailure = true;
313                            }
314                    }
315    
316                    // Clean up propstats
317    
318                    if (hasSuccess) {
319                            DocUtil.add(
320                                    successStatElement, createQName("status"), "HTTP/1.1 200 OK");
321                    }
322                    else {
323                            responseElement.remove(successStatElement);
324                    }
325    
326                    if (!hasSuccess && hasFailure) {
327                            DocUtil.add(
328                                    failureStatElement, createQName("status"),
329                                    "HTTP/1.1 404 Not Found");
330                    }
331                    else {
332                            responseElement.remove(failureStatElement);
333                    }
334            }
335    
336            protected void addResponse(
337                            WebDAVStorage storage, WebDAVRequest webDAVRequest,
338                            Resource resource, Set<QName> props, Element multistatusElement,
339                            long depth)
340                    throws Exception {
341    
342                    addResponse(webDAVRequest, resource, props, multistatusElement);
343    
344                    if (resource.isCollection() && (depth != 0)) {
345                            List<Resource> storageResources = storage.getResources(
346                                    webDAVRequest);
347    
348                            for (Resource storageResource : storageResources) {
349                                    addResponse(
350                                            webDAVRequest, storageResource, props, multistatusElement);
351                            }
352                    }
353            }
354    
355            protected int writeResponseXML(
356                            WebDAVRequest webDAVRequest, Set<QName> props)
357                    throws Exception {
358    
359                    WebDAVStorage storage = webDAVRequest.getWebDAVStorage();
360    
361                    long depth = WebDAVUtil.getDepth(webDAVRequest.getHttpServletRequest());
362    
363                    Document document = SAXReaderUtil.createDocument();
364    
365                    Element multistatusElement = SAXReaderUtil.createElement(
366                            createQName("multistatus"));
367    
368                    document.setRootElement(multistatusElement);
369    
370                    Resource resource = storage.getResource(webDAVRequest);
371    
372                    if (resource != null) {
373                            addResponse(
374                                    storage, webDAVRequest, resource, props, multistatusElement,
375                                    depth);
376    
377                            String xml = document.formattedString(StringPool.FOUR_SPACES);
378    
379                            if (_log.isDebugEnabled()) {
380                                    _log.debug("Response XML\n" + xml);
381                            }
382    
383                            // Set the status prior to writing the XML
384    
385                            int status = WebDAVUtil.SC_MULTI_STATUS;
386    
387                            HttpServletResponse response =
388                                    webDAVRequest.getHttpServletResponse();
389    
390                            response.setContentType(ContentTypes.TEXT_XML_UTF8);
391                            response.setStatus(status);
392    
393                            try {
394                                    ServletResponseUtil.write(response, xml);
395    
396                                    response.flushBuffer();
397                            }
398                            catch (Exception e) {
399                                    if (_log.isWarnEnabled()) {
400                                            _log.warn(e);
401                                    }
402                            }
403    
404                            return status;
405                    }
406                    else {
407                            if (_log.isDebugEnabled()) {
408                                    _log.debug(
409                                            "No resource found for " + storage.getRootPath() +
410                                                    webDAVRequest.getPath());
411                            }
412    
413                            return HttpServletResponse.SC_NOT_FOUND;
414                    }
415            }
416    
417            private static final List<QName> _ALL_COLLECTION_PROPS = Arrays.asList(
418                    new QName[] {
419                            CREATIONDATE, DISPLAYNAME, GETLASTMODIFIED, GETCONTENTTYPE,
420                            LOCKDISCOVERY, RESOURCETYPE
421                    });
422    
423            private static final List<QName> _ALL_SIMPLE_PROPS = Arrays.asList(
424                    new QName[] {
425                            CREATIONDATE, DISPLAYNAME, GETLASTMODIFIED, GETCONTENTTYPE,
426                            GETCONTENTLENGTH, ISREADONLY, LOCKDISCOVERY, RESOURCETYPE
427                    });
428    
429            private static Log _log = LogFactoryUtil.getLog(BasePropMethodImpl.class);
430    
431    }