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