1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.webdav.methods;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  import com.liferay.portal.kernel.util.ContentTypes;
20  import com.liferay.portal.kernel.util.StringPool;
21  import com.liferay.portal.kernel.util.Time;
22  import com.liferay.portal.kernel.util.Tuple;
23  import com.liferay.portal.kernel.xml.Document;
24  import com.liferay.portal.kernel.xml.Element;
25  import com.liferay.portal.kernel.xml.Namespace;
26  import com.liferay.portal.kernel.xml.QName;
27  import com.liferay.portal.kernel.xml.SAXReaderUtil;
28  import com.liferay.portal.model.Lock;
29  import com.liferay.portal.model.WebDAVProps;
30  import com.liferay.portal.service.WebDAVPropsLocalServiceUtil;
31  import com.liferay.portal.webdav.Resource;
32  import com.liferay.portal.webdav.WebDAVRequest;
33  import com.liferay.portal.webdav.WebDAVStorage;
34  import com.liferay.portal.webdav.WebDAVUtil;
35  import com.liferay.util.servlet.ServletResponseUtil;
36  import com.liferay.util.xml.DocUtil;
37  
38  import java.util.Arrays;
39  import java.util.HashSet;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Set;
43  
44  import javax.servlet.http.HttpServletResponse;
45  
46  /**
47   * <a href="BasePropMethodImpl.java.html"><b><i>View Source</i></b></a>
48   *
49   * @author Alexander Chow
50   */
51  public abstract class BasePropMethodImpl implements Method {
52  
53      public static final String ALLPROP = "allprop";
54  
55      public static final Tuple ALLPROP_PAIR = new Tuple(
56          ALLPROP, WebDAVUtil.DAV_URI);
57  
58      public static final String CREATIONDATE = "creationdate";
59  
60      public static final Tuple CREATIONDATE_PAIR = new Tuple(
61          CREATIONDATE, WebDAVUtil.DAV_URI);
62  
63      public static final String DISPLAYNAME = "displayname";
64  
65      public static final Tuple DISPLAYNAME_PAIR = new Tuple(
66          DISPLAYNAME, WebDAVUtil.DAV_URI);
67  
68      public static final String GETCONTENTLENGTH = "getcontentlength";
69  
70      public static final Tuple GETCONTENTLENGTH_PAIR = new Tuple(
71          GETCONTENTLENGTH, WebDAVUtil.DAV_URI);
72  
73      public static final String GETCONTENTTYPE = "getcontenttype";
74  
75      public static final Tuple GETCONTENTTYPE_PAIR = new Tuple(
76          GETCONTENTTYPE, WebDAVUtil.DAV_URI);
77  
78      public static final String GETLASTMODIFIED = "getlastmodified";
79  
80      public static final Tuple GETLASTMODIFIED_PAIR = new Tuple(
81          GETCONTENTLENGTH, WebDAVUtil.DAV_URI);
82  
83      public static final String LOCKDISCOVERY = "lockdiscovery";
84  
85      public static final Tuple LOCKDISCOVERY_PAIR = new Tuple(
86          LOCKDISCOVERY, WebDAVUtil.DAV_URI);
87  
88      public static final String RESOURCETYPE = "resourcetype";
89  
90      public static final Tuple RESOURCETYPE_PAIR = new Tuple(
91          RESOURCETYPE, WebDAVUtil.DAV_URI);
92  
93      protected Element addElement(Element element, String name) {
94          QName qName = SAXReaderUtil.createQName(name, WebDAVUtil.DAV_URI);
95  
96          return element.addElement(qName);
97      }
98  
99      protected Element addElement(Element element, String name1, String name2) {
100         Element childElement = addElement(element, name1);
101 
102         return addElement(childElement, name2);
103     }
104 
105     protected void addResponse(String href, Element multistatusElement)
106         throws Exception {
107 
108         Element responseElement = DocUtil.add(
109             multistatusElement, "response", WebDAVUtil.DAV_URI);
110 
111         DocUtil.add(responseElement, "href", WebDAVUtil.DAV_URI, href);
112 
113         Element propstatElement = DocUtil.add(
114             responseElement, "propstat", WebDAVUtil.DAV_URI);
115 
116         DocUtil.add(
117             propstatElement, "status", WebDAVUtil.DAV_URI,
118             "HTTP/1.1 404 Not Found");
119     }
120 
121     protected void addResponse(
122             WebDAVRequest webDavRequest, Resource resource, Set<Tuple> props,
123             Element multistatus)
124         throws Exception {
125 
126         // Make a deep copy of the props
127 
128         props = new HashSet<Tuple>(props);
129 
130         // Start building multistatus response
131 
132         Element responseElement = DocUtil.add(
133             multistatus, "response", WebDAVUtil.DAV_URI);
134 
135         DocUtil.add(
136             responseElement, "href", WebDAVUtil.DAV_URI, resource.getHREF());
137 
138         // Build success and failure propstat elements
139 
140         Element successStatElement = DocUtil.add(
141             responseElement, "propstat", WebDAVUtil.DAV_URI);
142         Element successPropElement = DocUtil.add(
143             successStatElement, "prop", WebDAVUtil.DAV_URI);
144         Element failureStatElement = DocUtil.add(
145             responseElement, "propstat", WebDAVUtil.DAV_URI);
146         Element failurePropElement = DocUtil.add(
147             failureStatElement, "prop", WebDAVUtil.DAV_URI);
148 
149         boolean hasSuccess = false;
150         boolean hasFailure = false;
151 
152         // Check DAV properties
153 
154         if (props.contains(ALLPROP_PAIR)) {
155             props.remove(ALLPROP_PAIR);
156 
157             if (resource.isCollection()) {
158                 props.addAll(_ALL_COLLECTION_PROPS);
159             }
160             else {
161                 props.addAll(_ALL_SIMPLE_PROPS);
162             }
163         }
164 
165         if (props.contains(CREATIONDATE_PAIR)) {
166             props.remove(CREATIONDATE_PAIR);
167 
168             DocUtil.add(
169                 successPropElement, CREATIONDATE, WebDAVUtil.DAV_URI,
170                 resource.getCreateDate());
171 
172             hasSuccess = true;
173         }
174 
175         if (props.contains(DISPLAYNAME_PAIR)) {
176             props.remove(DISPLAYNAME_PAIR);
177 
178             DocUtil.add(
179                 successPropElement, DISPLAYNAME, WebDAVUtil.DAV_URI,
180                 resource.getDisplayName());
181 
182             hasSuccess = true;
183         }
184 
185         if (props.contains(GETLASTMODIFIED_PAIR)) {
186             props.remove(GETLASTMODIFIED_PAIR);
187 
188             DocUtil.add(
189                 successPropElement, GETLASTMODIFIED, WebDAVUtil.DAV_URI,
190                 resource.getModifiedDate());
191 
192             hasSuccess = true;
193         }
194 
195         if (props.contains(GETCONTENTTYPE_PAIR)) {
196             props.remove(GETCONTENTTYPE_PAIR);
197 
198             DocUtil.add(
199                 successPropElement, GETCONTENTTYPE, WebDAVUtil.DAV_URI,
200                 resource.getContentType());
201 
202             hasSuccess = true;
203         }
204 
205         if (props.contains(GETCONTENTLENGTH_PAIR)) {
206             props.remove(GETCONTENTLENGTH_PAIR);
207 
208             if (!resource.isCollection()) {
209                 DocUtil.add(
210                     successPropElement, GETCONTENTLENGTH, WebDAVUtil.DAV_URI,
211                     resource.getSize());
212 
213                 hasSuccess = true;
214             }
215             else {
216                 DocUtil.add(
217                     failurePropElement, GETCONTENTLENGTH, WebDAVUtil.DAV_URI);
218 
219                 hasFailure = true;
220             }
221         }
222 
223         if (props.contains(LOCKDISCOVERY_PAIR)) {
224             props.remove(LOCKDISCOVERY_PAIR);
225 
226             Lock lock = resource.getLock();
227 
228             if (lock != null) {
229                 long now = System.currentTimeMillis();
230 
231                 long timeRemaining =
232                     (lock.getExpirationDate().getTime() - now) / Time.SECOND;
233 
234                 if (timeRemaining <= 0) {
235                     timeRemaining = 1;
236                 }
237 
238                 Element lockDiscoveryElement = addElement(
239                     successPropElement, LOCKDISCOVERY);
240 
241                 Element activeLockElement = addElement(
242                     lockDiscoveryElement, "activelock");
243 
244                 addElement(activeLockElement, "locktype", "write");
245                 addElement(activeLockElement, "lockscope", "exclusive");
246 
247                 if (resource.isCollection()) {
248                     DocUtil.add(
249                         activeLockElement, "depth", WebDAVUtil.DAV_URI,
250                         "Infinity");
251                 }
252 
253                 DocUtil.add(
254                     activeLockElement, "owner", WebDAVUtil.DAV_URI,
255                     lock.getOwner());
256                 DocUtil.add(
257                     activeLockElement, "timeout", WebDAVUtil.DAV_URI,
258                     "Second-" + timeRemaining);
259 
260                 if (webDavRequest.getUserId() == lock.getUserId()) {
261                     Element lockTokenElement = addElement(
262                         activeLockElement, "locktoken", "href");
263 
264                     lockTokenElement.addText(
265                         "opaquelocktoken:" + lock.getUuid());
266                 }
267 
268                 hasSuccess = true;
269             }
270             else {
271                 DocUtil.add(
272                     failurePropElement, LOCKDISCOVERY, WebDAVUtil.DAV_URI);
273 
274                 hasFailure = true;
275             }
276         }
277 
278         if (props.contains(RESOURCETYPE_PAIR)) {
279             props.remove(RESOURCETYPE_PAIR);
280 
281             Element resourceTypeElement = DocUtil.add(
282                 successPropElement, RESOURCETYPE, WebDAVUtil.DAV_URI);
283 
284             if (resource.isCollection()) {
285                 DocUtil.add(
286                     resourceTypeElement, "collection", WebDAVUtil.DAV_URI);
287             }
288 
289             hasSuccess = true;
290         }
291 
292         // Check remaining properties against custom properties
293 
294         WebDAVProps webDavProps = WebDAVPropsLocalServiceUtil.getWebDAVProps(
295             webDavRequest.getCompanyId(), resource.getClassName(),
296             resource.getPrimaryKey());
297 
298         Set<Tuple> customProps = webDavProps.getPropsSet();
299 
300         for (Tuple tuple : props) {
301             String name = (String)tuple.getObject(0);
302             Namespace namespace = (Namespace)tuple.getObject(1);
303 
304             String prefix = namespace.getPrefix();
305             String uri = namespace.getURI();
306 
307             if (customProps.contains(tuple)) {
308                 String text = webDavProps.getText(name, prefix, uri);
309 
310                 DocUtil.add(successPropElement, name, namespace, text);
311 
312                 hasSuccess = true;
313             }
314             else {
315                 DocUtil.add(failurePropElement, name, namespace);
316 
317                 hasFailure = true;
318             }
319         }
320 
321         // Clean up propstats
322 
323         if (hasSuccess) {
324             DocUtil.add(
325                 successStatElement, "status", WebDAVUtil.DAV_URI,
326                 "HTTP/1.1 200 OK");
327         }
328         else {
329             responseElement.remove(successStatElement);
330         }
331 
332         if (!hasSuccess && hasFailure) {
333             DocUtil.add(
334                 failureStatElement, "status", WebDAVUtil.DAV_URI,
335                 "HTTP/1.1 404 Not Found");
336         }
337         else {
338             responseElement.remove(failureStatElement);
339         }
340     }
341 
342     protected void addResponse(
343             WebDAVStorage storage, WebDAVRequest webDavRequest,
344             Resource resource, Set<Tuple> props, Element multistatusElement,
345             long depth)
346         throws Exception {
347 
348         addResponse(webDavRequest, resource, props, multistatusElement);
349 
350         if (resource.isCollection() && (depth != 0)) {
351             Iterator<Resource> itr = storage.getResources(
352                 webDavRequest).iterator();
353 
354             while (itr.hasNext()) {
355                 resource = itr.next();
356 
357                 addResponse(
358                     webDavRequest, resource, props, multistatusElement);
359             }
360         }
361     }
362 
363     protected int writeResponseXML(
364             WebDAVRequest webDavRequest, Set<Tuple> props)
365         throws Exception {
366 
367         WebDAVStorage storage = webDavRequest.getWebDAVStorage();
368 
369         long depth = WebDAVUtil.getDepth(webDavRequest.getHttpServletRequest());
370 
371         Document document = SAXReaderUtil.createDocument();
372 
373         Element multistatusElement = SAXReaderUtil.createElement(
374             SAXReaderUtil.createQName("multistatus", WebDAVUtil.DAV_URI));
375 
376         document.setRootElement(multistatusElement);
377 
378         Resource resource = storage.getResource(webDavRequest);
379 
380         if (resource != null) {
381             addResponse(
382                 storage, webDavRequest, resource, props, multistatusElement,
383                 depth);
384 
385             String xml = document.formattedString(StringPool.FOUR_SPACES);
386 
387             if (_log.isDebugEnabled()) {
388                 _log.debug("Response XML\n" + xml);
389             }
390 
391             // Set the status prior to writing the XML
392 
393             int status = WebDAVUtil.SC_MULTI_STATUS;
394 
395             HttpServletResponse response =
396                 webDavRequest.getHttpServletResponse();
397 
398             response.setContentType(ContentTypes.TEXT_XML_UTF8);
399             response.setStatus(status);
400 
401             try {
402                 ServletResponseUtil.write(response, xml);
403 
404                 response.flushBuffer();
405             }
406             catch (Exception e) {
407                 if (_log.isWarnEnabled()) {
408                     _log.warn(e);
409                 }
410             }
411 
412             return status;
413         }
414         else {
415             if (_log.isDebugEnabled()) {
416                 _log.debug(
417                     "No resource found for " + storage.getRootPath() +
418                         webDavRequest.getPath());
419             }
420 
421             return HttpServletResponse.SC_NOT_FOUND;
422         }
423     }
424 
425     private static final List<Tuple> _ALL_COLLECTION_PROPS = Arrays.asList(
426         new Tuple[] {
427             CREATIONDATE_PAIR, DISPLAYNAME_PAIR, GETLASTMODIFIED_PAIR,
428             GETCONTENTTYPE_PAIR, LOCKDISCOVERY_PAIR, RESOURCETYPE_PAIR
429         });
430 
431     private static final List<Tuple> _ALL_SIMPLE_PROPS = Arrays.asList(
432         new Tuple[] {
433             CREATIONDATE_PAIR, DISPLAYNAME_PAIR, GETLASTMODIFIED_PAIR,
434             GETCONTENTTYPE_PAIR, GETCONTENTLENGTH_PAIR, LOCKDISCOVERY_PAIR,
435             RESOURCETYPE_PAIR
436         });
437 
438     private static Log _log = LogFactoryUtil.getLog(BasePropMethodImpl.class);
439 
440 }