KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > repo > webdav > WebDAV


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.repo.webdav;
18
19 import java.io.Serializable JavaDoc;
20 import java.io.UnsupportedEncodingException JavaDoc;
21 import java.net.URLDecoder JavaDoc;
22 import java.net.URLEncoder JavaDoc;
23 import java.text.SimpleDateFormat JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.Hashtable JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.StringTokenizer JavaDoc;
29
30 import javax.servlet.http.HttpServletRequest JavaDoc;
31
32 import org.alfresco.error.AlfrescoRuntimeException;
33 import org.alfresco.model.ContentModel;
34 import org.alfresco.service.cmr.repository.ContentData;
35 import org.alfresco.service.cmr.repository.NodeRef;
36 import org.alfresco.service.namespace.QName;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40 /**
41  * Helper class used by the WebDAV protocol handling classes
42  *
43  * @author gavinc
44  */

45 public class WebDAV
46 {
47     // Logging
48

49     private static Log logger = LogFactory.getLog("org.alfresco.webdav.protocol");
50     
51     // WebDAV XML namespace
52

53     public static final String JavaDoc DAV_NS = "D";
54     public static final String JavaDoc DAV_NS_PREFIX = DAV_NS + ":";
55     
56     // PROPFIND depth
57

58     public static final int DEPTH_0 = 0;
59     public static final int DEPTH_1 = 1;
60     public static final int DEPTH_INFINITY = -1;
61     public static final short TIMEOUT_INFINITY = -1;
62
63     // WebDAV HTTP response codes
64

65     public static final int WEBDAV_SC_MULTI_STATUS = 207;
66     public static final int WEBDAV_SC_LOCKED = 423;
67
68     // HTTP response code descriptions
69

70     public static final String JavaDoc SC_OK_DESC = "OK";
71     public static final String JavaDoc SC_NOT_FOUND_DESC = "Not Found";
72
73     // HTTP methods
74

75     public static final String JavaDoc METHOD_PUT = "PUT";
76     public static final String JavaDoc METHOD_POST = "POST";
77     public static final String JavaDoc METHOD_GET = "GET";
78     public static final String JavaDoc METHOD_DELETE = "DELETE";
79     public static final String JavaDoc METHOD_HEAD = "HEAD";
80     public static final String JavaDoc METHOD_OPTIONS = "OPTIONS";
81     public static final String JavaDoc METHOD_PROPFIND = "PROPFIND";
82     public static final String JavaDoc METHOD_PROPPATCH = "PROPPATCH";
83     public static final String JavaDoc METHOD_MKCOL = "MKCOL";
84     public static final String JavaDoc METHOD_MOVE = "MOVE";
85     public static final String JavaDoc METHOD_COPY = "COPY";
86     public static final String JavaDoc METHOD_LOCK = "LOCK";
87     public static final String JavaDoc METHOD_UNLOCK = "UNLOCK";
88
89     // HTTP headers
90

91     public static final String JavaDoc HEADER_CONTENT_LENGTH = "Content-Length";
92     public static final String JavaDoc HEADER_CONTENT_TYPE = "Content-Type";
93     public static final String JavaDoc HEADER_DEPTH = "Depth";
94     public static final String JavaDoc HEADER_DESTINATION = "Destination";
95     public static final String JavaDoc HEADER_ETAG = "ETag";
96     public static final String JavaDoc HEADER_EXPECT = "Expect";
97     public static final String JavaDoc HEADER_EXPECT_CONTENT = "100-continue";
98     public static final String JavaDoc HEADER_IF = "If";
99     public static final String JavaDoc HEADER_IF_MATCH = "If-Match";
100     public static final String JavaDoc HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
101     public static final String JavaDoc HEADER_IF_NONE_MATCH = "If-None-Match";
102     public static final String JavaDoc HEADER_IF_RANGE = "If-Range";
103     public static final String JavaDoc HEADER_IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
104     public static final String JavaDoc HEADER_LAST_MODIFIED = "Last-Modified";
105     public static final String JavaDoc HEADER_LOCK_TOKEN = "Lock-Token";
106     public static final String JavaDoc HEADER_OVERWRITE = "Overwrite";
107     public static final String JavaDoc HEADER_RANGE = "Range";
108     public static final String JavaDoc HEADER_TIMEOUT = "Timeout";
109
110     // If-Modified/If-Unmodified date format
111

112     public static final String JavaDoc HEADER_IF_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
113     
114     // General string constants
115

116     public static final String JavaDoc ASTERISK = "*";
117     public static final String JavaDoc DEFAULT_NAMESPACE_URI = "DAV:";
118     public static final String JavaDoc DIR_SEPARATOR = "/";
119     public static final String JavaDoc FAKE_TOKEN = "faketoken";
120     public static final String JavaDoc HTTP1_1 = "HTTP/1.1";
121     public static final String JavaDoc INFINITE = "Infinite";
122     public static final String JavaDoc INFINITY = "infinity";
123     public static final String JavaDoc OPAQUE_LOCK_TOKEN = "opaquelocktoken:";
124     public static final String JavaDoc NAMESPACE_SEPARATOR = ":";
125     public static final String JavaDoc SECOND = "Second-";
126     public static final String JavaDoc HEADER_VALUE_SEPARATOR = ",";
127     public static final String JavaDoc ZERO = "0";
128     public static final String JavaDoc ONE = "1";
129     public static final String JavaDoc T = "T";
130
131     // Strings used in WebDAV XML payload
132

133     public static final String JavaDoc XML_NS = "xmlns";
134     
135     public static final String JavaDoc XML_ACTIVE_LOCK = "activelock";
136     public static final String JavaDoc XML_ALLPROP = "allprop";
137     public static final String JavaDoc XML_COLLECTION = "collection";
138     public static final String JavaDoc XML_CREATION_DATE = "creationdate";
139     public static final String JavaDoc XML_DEPTH = "depth";
140     public static final String JavaDoc XML_DISPLAYNAME = "displayname";
141     public static final String JavaDoc XML_EXCLUSIVE = "exclusive";
142     public static final String JavaDoc XML_GET_CONTENT_LANGUAGE = "getcontentlanguage";
143     public static final String JavaDoc XML_GET_CONTENT_LENGTH = "getcontentlength";
144     public static final String JavaDoc XML_GET_CONTENT_TYPE = "getcontenttype";
145     public static final String JavaDoc XML_GET_ETAG = "getetag";
146     public static final String JavaDoc XML_GET_LAST_MODIFIED = "getlastmodified";
147     public static final String JavaDoc XML_HREF = "href";
148     public static final String JavaDoc XML_LOCK_DISCOVERY = "lockdiscovery";
149     public static final String JavaDoc XML_LOCK_SCOPE = "lockscope";
150     public static final String JavaDoc XML_LOCK_TOKEN = "locktoken";
151     public static final String JavaDoc XML_LOCK_TYPE = "locktype";
152     public static final String JavaDoc XML_MULTI_STATUS = "multistatus";
153     public static final String JavaDoc XML_OWNER = "owner";
154     public static final String JavaDoc XML_PROP = "prop";
155     public static final String JavaDoc XML_PROPNAME = "propname";
156     public static final String JavaDoc XML_PROPSTAT = "propstat";
157     public static final String JavaDoc XML_RESOURCE_TYPE = "resourcetype";
158     public static final String JavaDoc XML_RESPONSE = "response";
159     public static final String JavaDoc XML_SHARED = "shared";
160     public static final String JavaDoc XML_SOURCE = "source";
161     public static final String JavaDoc XML_STATUS = "status";
162     public static final String JavaDoc XML_SUPPORTED_LOCK = "supportedlock";
163     public static final String JavaDoc XML_TIMEOUT = "timeout";
164     public static final String JavaDoc XML_WRITE = "write";
165
166     // Namespaced versions of payload elements
167

168     public static final String JavaDoc XML_NS_ACTIVE_LOCK = DAV_NS_PREFIX + "activelock";
169     public static final String JavaDoc XML_NS_ALLPROP = DAV_NS_PREFIX + "allprop";
170     public static final String JavaDoc XML_NS_COLLECTION = DAV_NS_PREFIX + "collection";
171     public static final String JavaDoc XML_NS_CREATION_DATE = DAV_NS_PREFIX + "creationdate";
172     public static final String JavaDoc XML_NS_DEPTH = DAV_NS_PREFIX + "depth";
173     public static final String JavaDoc XML_NS_DISPLAYNAME = DAV_NS_PREFIX + "displayname";
174     public static final String JavaDoc XML_NS_EXCLUSIVE = DAV_NS_PREFIX + "exclusive";
175     public static final String JavaDoc XML_NS_GET_CONTENT_LANGUAGE = DAV_NS_PREFIX + "getcontentlanguage";
176     public static final String JavaDoc XML_NS_GET_CONTENT_LENGTH = DAV_NS_PREFIX + "getcontentlength";
177     public static final String JavaDoc XML_NS_GET_CONTENT_TYPE = DAV_NS_PREFIX + "getcontenttype";
178     public static final String JavaDoc XML_NS_GET_ETAG = DAV_NS_PREFIX + "getetag";
179     public static final String JavaDoc XML_NS_GET_LAST_MODIFIED = DAV_NS_PREFIX + "getlastmodified";
180     public static final String JavaDoc XML_NS_HREF = DAV_NS_PREFIX + "href";
181     public static final String JavaDoc XML_NS_LOCK_DISCOVERY = DAV_NS_PREFIX + "lockdiscovery";
182     public static final String JavaDoc XML_NS_LOCK_SCOPE = DAV_NS_PREFIX + "lockscope";
183     public static final String JavaDoc XML_NS_LOCK_TOKEN = DAV_NS_PREFIX + "locktoken";
184     public static final String JavaDoc XML_NS_LOCK_TYPE = DAV_NS_PREFIX + "locktype";
185     public static final String JavaDoc XML_NS_MULTI_STATUS = DAV_NS_PREFIX + "multistatus";
186     public static final String JavaDoc XML_NS_OWNER = DAV_NS_PREFIX + "owner";
187     public static final String JavaDoc XML_NS_PROP = DAV_NS_PREFIX + "prop";
188     public static final String JavaDoc XML_NS_PROPNAME = DAV_NS_PREFIX + "propname";
189     public static final String JavaDoc XML_NS_PROPSTAT = DAV_NS_PREFIX + "propstat";
190     public static final String JavaDoc XML_NS_RESOURCE_TYPE = DAV_NS_PREFIX + "resourcetype";
191     public static final String JavaDoc XML_NS_RESPONSE = DAV_NS_PREFIX + "response";
192     public static final String JavaDoc XML_NS_SHARED = DAV_NS_PREFIX + "shared";
193     public static final String JavaDoc XML_NS_SOURCE = DAV_NS_PREFIX + "source";
194     public static final String JavaDoc XML_NS_STATUS = DAV_NS_PREFIX + "status";
195     public static final String JavaDoc XML_NS_SUPPORTED_LOCK = DAV_NS_PREFIX + "supportedlock";
196     public static final String JavaDoc XML_NS_TIMEOUT = DAV_NS_PREFIX + "timeout";
197     public static final String JavaDoc XML_NS_WRITE = DAV_NS_PREFIX + "write";
198     
199     public static final String JavaDoc XML_CONTENT_TYPE = "text/xml; charset=UTF-8";
200     
201     private static HashMap JavaDoc s_codeDescriptions = null;
202
203     // Path seperator
204

205     private static final String JavaDoc DIR_SEPERATOR = "\\";
206
207     // Path seperator
208

209     public static final String JavaDoc PathSeperator = "/";
210     public static final char PathSeperatorChar = '/';
211     
212     // Lock token seperator
213

214     public static final String JavaDoc LOCK_TOKEN_SEPERATOR = ":";
215     
216     // Root path
217

218     private static final String JavaDoc RootPath = PathSeperator;
219     
220     // Map WebDAV property names to Alfresco property names
221

222     private static Hashtable JavaDoc<String JavaDoc, QName> _propertyNameMap;
223     
224     // WebDAV creation date/time formatter
225

226     private static SimpleDateFormat JavaDoc _creationDateFormatter = new SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss'Z'");
227     
228     // HTTP header date/time formatter
229

230     private static SimpleDateFormat JavaDoc _httpDateFormatter = new SimpleDateFormat JavaDoc(HEADER_IF_DATE_FORMAT);
231     
232     /**
233      * Formats the given date so that it conforms with the Last-Modified HTTP header
234      *
235      * @param date The date to format
236      * @return The formatted date string
237      */

238     public static String JavaDoc formatModifiedDate(Date JavaDoc date)
239     {
240         return _httpDateFormatter.format(date);
241     }
242
243     /**
244      * Formats the given date so that it conforms with the Last-Modified HTTP header
245      *
246      * @param date long
247      * @return The formatted date string
248      */

249     public static String JavaDoc formatModifiedDate(long ldate)
250     {
251         return _httpDateFormatter.format(new Date JavaDoc(ldate));
252     }
253
254     /**
255      * Formats the given date so that it conforms with the WebDAV creation date/time format
256      *
257      * @param date The date to format
258      * @return The formatted date string
259      */

260     public static String JavaDoc formatCreationDate(Date JavaDoc date)
261     {
262         return _creationDateFormatter.format(date);
263     }
264
265     /**
266      * Formats the given date so that it conforms with the WebDAV creation date/time format
267      *
268      * @param date long
269      * @return The formatted date string
270      */

271     public static String JavaDoc formatCreationDate(long ldate)
272     {
273         return _creationDateFormatter.format(new Date JavaDoc(ldate));
274     }
275
276     /**
277      * Formats the given date for use in the HTTP header
278      *
279      * @param date Date
280      * @return String
281      */

282     public static String JavaDoc formatHeaderDate(Date JavaDoc date)
283     {
284         return _httpDateFormatter.format( date);
285     }
286     
287     /**
288      * Formats the given date for use in the HTTP header
289      *
290      * @param date long
291      * @return String
292      */

293     public static String JavaDoc formatHeaderDate(long date)
294     {
295         return _httpDateFormatter.format( new Date JavaDoc(date));
296     }
297     
298     /**
299      * Return the Alfresco property value for the specified WebDAV property
300      *
301      * @param props Map<QName, Serializable>
302      * @param davPropName String
303      * @return Object
304      */

305     public static Object JavaDoc getDAVPropertyValue( Map JavaDoc<QName, Serializable JavaDoc> props, String JavaDoc davPropName)
306     {
307         // Convert the WebDAV property name to the corresponding Alfresco property
308

309         QName propName = _propertyNameMap.get( davPropName);
310         if ( propName == null)
311             throw new AlfrescoRuntimeException("No mapping for WebDAV property " + davPropName);
312         
313         // Return the property value
314
Object JavaDoc value = props.get(propName);
315         if (value instanceof ContentData)
316         {
317             ContentData contentData = (ContentData) value;
318             if (davPropName.equals(WebDAV.XML_GET_CONTENT_TYPE))
319             {
320                 value = contentData.getMimetype();
321             }
322             else if (davPropName.equals(WebDAV.XML_GET_CONTENT_LENGTH))
323             {
324                 value = new Long JavaDoc(contentData.getSize());
325             }
326         }
327         return value;
328     }
329     
330     
331     /**
332      * Maps the current HTTP request to a path that can be used to access a content repository
333      *
334      * @param request HTTP request
335      * @return A content repository path
336      */

337     public static String JavaDoc getRepositoryPath(HttpServletRequest JavaDoc request)
338     {
339         // Try and get the path
340

341         String JavaDoc strPath = null;
342         
343         try {
344             strPath = URLDecoder.decode( request.getRequestURI(), "UTF-8");
345         }
346         catch (Exception JavaDoc ex) {
347         }
348
349         // Find the servlet path and trim from the request path
350

351         String JavaDoc servletPath = request.getServletPath();
352         
353         int rootPos = strPath.indexOf(servletPath);
354         if ( rootPos != -1)
355             strPath = strPath.substring( rootPos);
356         
357         // If we failed to get the path from the request try and get the path from the servlet path
358

359         if (strPath == null)
360         {
361             strPath = request.getServletPath();
362         }
363
364         // If we still have not got a path then default to the root directory
365

366         if (strPath == null || strPath.length() == 0)
367         {
368             strPath = RootPath;
369         }
370
371         // Make sure there are no trailing slashes
372

373         else if (strPath.endsWith(DIR_SEPARATOR))
374         {
375             strPath = strPath.substring(0, strPath.length() - 1);
376         }
377         
378         // Check if the path starts with the base servlet path
379

380         if ( strPath.startsWith(request.getServletPath()))
381         {
382             int len = request.getServletPath().length();
383             
384             if ( strPath.length() > len)
385                 strPath = strPath.substring(len);
386             else
387                 strPath = RootPath;
388         }
389
390         // Return the path
391

392         return strPath;
393     }
394
395     /**
396      * Returns a URL that could be used to access the given path.
397      *
398      * @param request HttpServletRequest
399      * @param path String
400      * @param isCollection boolean
401      * @return String
402      */

403     public static String JavaDoc getURLForPath(HttpServletRequest JavaDoc request, String JavaDoc path, boolean isCollection)
404     {
405         StringBuilder JavaDoc urlStr = new StringBuilder JavaDoc(request.getRequestURI());
406         String JavaDoc servletPath = request.getServletPath();
407         
408         int rootPos = urlStr.indexOf(servletPath);
409         if (rootPos != -1)
410         {
411             urlStr.setLength(rootPos + servletPath.length());
412         }
413         
414         if (urlStr.charAt(urlStr.length() - 1) != PathSeperatorChar)
415         {
416             urlStr.append(PathSeperator);
417         }
418         
419         if (path.equals(RootPath) == false)
420         {
421             // split the path and URL encode each path element
422
for (StringTokenizer JavaDoc t = new StringTokenizer JavaDoc(path, PathSeperator); t.hasMoreTokens(); /**/)
423             {
424                 urlStr.append( WebDAVHelper.encodeURL(t.nextToken()) );
425                 if (t.hasMoreTokens())
426                 {
427                     urlStr.append(PathSeperator);
428                 }
429             }
430         }
431         
432         // If the URL is to a collection add a trailing slash
433
if (isCollection && urlStr.charAt( urlStr.length() - 1) != PathSeperatorChar)
434         {
435             urlStr.append( PathSeperator);
436         }
437         
438         // Return the URL string
439
return urlStr.toString();
440     }
441
442     /**
443      * Returns a context-relative path, beginning with a "/", that represents the canonical version
444      * of the specified path after ".." and "." elements are resolved out. If the specified path
445      * attempts to go outside the boundaries of the current context (i.e. too many ".." path
446      * elements are present), return <code>null</code> instead.
447      *
448      * @param strPath The path to be decoded
449      */

450     public static String JavaDoc decodeURL(String JavaDoc strPath)
451     {
452         if (strPath == null)
453             return null;
454
455         // Resolve encoded characters in the normalized path, which also handles encoded
456
// spaces so we can skip that later. Placed at the beginning of the chain so that
457
// encoded bad stuff(tm) can be caught by the later checks
458

459         String JavaDoc strNormalized = null;
460
461         try
462         {
463             strNormalized = URLDecoder.decode(strPath, "UTF-8");
464         }
465         catch (Exception JavaDoc ex)
466         {
467             logger.error("Error in decodeURL, URL = " + strPath, ex);
468         }
469
470         if (strNormalized == null)
471             return (null);
472
473         // Normalize the slashes and add leading slash if necessary
474

475         if (strNormalized.indexOf('\\') >= 0)
476             strNormalized = strNormalized.replace('\\', '/');
477
478         if (!strNormalized.startsWith("/"))
479             strNormalized = "/" + strNormalized;
480
481         // Resolve occurrences of "//" in the normalized path
482

483         while (true)
484         {
485             int index = strNormalized.indexOf("//");
486             if (index < 0)
487                 break;
488             strNormalized = strNormalized.substring(0, index) + strNormalized.substring(index + 1);
489         }
490
491         // Resolve occurrences of "/./" in the normalized path
492

493         while (true)
494         {
495             int index = strNormalized.indexOf("/./");
496             if (index < 0)
497                 break;
498             strNormalized = strNormalized.substring(0, index) + strNormalized.substring(index + 2);
499         }
500
501         // Resolve occurrences of "/../" in the normalized path
502

503         while (true)
504         {
505             int index = strNormalized.indexOf("/../");
506             if (index < 0)
507                 break;
508             if (index == 0)
509                 return (null); // Trying to go outside our context
510

511             int index2 = strNormalized.lastIndexOf('/', index - 1);
512             strNormalized = strNormalized.substring(0, index2) + strNormalized.substring(index + 3);
513         }
514
515         // Return the normalized path that we have completed
516

517         return strNormalized;
518     }
519     
520     /**
521      * Make a unique lock token
522      *
523      * @param lockNode NodeRef
524      * @param owner String
525      * @return String
526      */

527     public static final String JavaDoc makeLockToken(NodeRef lockNode, String JavaDoc owner)
528     {
529         StringBuilder JavaDoc str = new StringBuilder JavaDoc();
530         
531         str.append(WebDAV.OPAQUE_LOCK_TOKEN);
532         str.append(lockNode.getId());
533         str.append(LOCK_TOKEN_SEPERATOR);
534         str.append(owner);
535         
536         return str.toString();
537     }
538     
539     /**
540      * Parse a lock token returning the node if and username
541      *
542      * @param lockToken String
543      * @return String[]
544      */

545     public static final String JavaDoc[] parseLockToken(String JavaDoc lockToken)
546     {
547         // Check if the lock token is valid
548

549         if ( lockToken == null)
550             return null;
551         
552         // Check if the token contains the lock token header
553

554         if ( lockToken.startsWith(WebDAV.OPAQUE_LOCK_TOKEN))
555             lockToken = lockToken.substring(WebDAV.OPAQUE_LOCK_TOKEN.length());
556         
557         // Split the node id and username tokens
558

559         int pos = lockToken.indexOf(LOCK_TOKEN_SEPERATOR);
560         if ( pos == -1)
561             return null;
562         
563         String JavaDoc[] tokens = new String JavaDoc[2];
564         
565         tokens[0] = lockToken.substring(0,pos);
566         tokens[1] = lockToken.substring(pos + 1);
567         
568         return tokens;
569     }
570     
571     /**
572      * Static initializer
573      */

574     static
575     {
576         // Create the WebDAV to Alfresco property mapping table
577

578         _propertyNameMap = new Hashtable JavaDoc<String JavaDoc, QName>();
579         
580         _propertyNameMap.put(XML_DISPLAYNAME, ContentModel.PROP_NAME);
581         _propertyNameMap.put(XML_CREATION_DATE, ContentModel.PROP_CREATED);
582         _propertyNameMap.put(XML_GET_LAST_MODIFIED, ContentModel.PROP_MODIFIED);
583         _propertyNameMap.put(XML_GET_CONTENT_TYPE, ContentModel.PROP_CONTENT);
584     }
585 }
586
Popular Tags