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