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