KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > slide > webdav > util > WebdavUtils


1 /*
2  * $Header$
3  * $Revision: 208614 $
4  * $Date: 2005-05-27 19:20:19 +0200 (Fri, 27 May 2005) $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2002 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24 package org.apache.slide.webdav.util;
25
26 import org.apache.slide.authenticate.CredentialsToken;
27 import org.apache.slide.common.*;
28 import org.apache.slide.content.Content;
29 import org.apache.slide.content.InsufficientStorageException;
30 import org.apache.slide.content.NodeRevisionDescriptor;
31 import org.apache.slide.content.NodeRevisionDescriptors;
32 import org.apache.slide.event.VetoException;
33 import org.apache.slide.lock.ObjectLockedException;
34 import org.apache.slide.macro.ConflictException;
35 import org.apache.slide.macro.ForbiddenException;
36 import org.apache.slide.security.AccessDeniedException;
37 import org.apache.slide.security.UnauthenticatedException;
38 import org.apache.slide.structure.ObjectAlreadyExistsException;
39 import org.apache.slide.structure.ObjectNotFoundException;
40 import org.apache.slide.util.Configuration;
41 import org.apache.slide.util.logger.Logger;
42 import org.apache.slide.webdav.WebdavException;
43 import org.apache.slide.webdav.WebdavServletConfig;
44 import org.apache.slide.webdav.method.MethodNotAllowedException;
45
46 import javax.servlet.http.HttpServletRequest JavaDoc;
47 import javax.servlet.http.HttpSession JavaDoc;
48 import java.io.UnsupportedEncodingException JavaDoc;
49 import java.net.SocketException JavaDoc;
50 import java.security.Principal JavaDoc;
51
52 /**
53  * A collection of various utility and convenience methods.
54  *
55  *
56  * @version $Revision: 208614 $
57  **/

58 public class WebdavUtils {
59
60
61     private static final String JavaDoc CREDENTIALS_ATTRIBUTE =
62         "org.apache.slide.webdav.method.credentials";
63
64     private static final String JavaDoc LOG_CHANNEL =
65         WebdavUtils.class.getName();
66
67     /**
68      * Constructs a new String based on bytes sequence contained
69      * in the
70      * Non-ASCII parts of the sequence will be decoded with UTF-8
71      * if possible, or with the specified encoding.
72      *
73      * @param string A "String" returned by tomcat getPathInfo()
74      * @param enc Encoding to use if non UTF-8
75      * @return A properly encoded String object
76      */

77     public static String JavaDoc decodeString(String JavaDoc string, String JavaDoc enc)
78         throws UnsupportedEncodingException JavaDoc {
79         if(string == null)
80             return null;
81         StringBuffer JavaDoc sb = null;
82         int j = 0;
83         int sf = 0;
84         sb = new StringBuffer JavaDoc();
85         byte utf8buffer[] = new byte[5];
86         // Get bytes without any decoding
87
byte bytes[] = string.getBytes("ISO-8859-1");
88         for (int i = 0; i < bytes.length; i+=1) {
89             byte b = bytes[i];
90             int bb = (b >= 0) ? b : b+256;
91             utf8buffer[j++] = b;
92             boolean ok = false;
93             // First test if non-ascii
94
if (bb >= 128) {
95                 // No ongoing UTF-8 decoding ?
96
if (sf==0) {
97                     // Now test if this can be a UTF-8 first byte
98
if (bb >= 192 && i < 252) {
99                         ok = true;
100                         // Determine UTF-8 size
101
if (bb >= 224)
102                             if (bb >= 240)
103                                 if (bb >= 248)
104                                     sf = 4;
105                                 else
106                                     sf = 3;
107                             else
108                                 sf = 2;
109                         else
110                             sf = 1;
111                     }
112                 } else if (bb >= 128 && bb < 192) {
113                     // This is a UTF-8 part
114
sf--;
115                     if (sf == 0) {
116                         sb.append(new String JavaDoc(utf8buffer,0,j,"UTF-8"));
117                         j = 0;
118                     }
119                     ok = true;
120                 }
121             }
122             // If there was an error during UTF-8 decoding, decode all remaining chars with default encoding
123
if (!ok) {
124                 sb.append(new String JavaDoc(utf8buffer,0,j, enc));
125                 j = 0;
126                 sf = 0;
127             }
128         }
129         // Remaining chars
130
if (j > 0) {
131             sb.append(new String JavaDoc(utf8buffer,0,j, enc));
132         }
133         return sb.toString();
134     }
135
136
137     /**
138      * Return a context-relative path, beginning with a "/", that represents
139      * the canonical version of the specified path after ".." and "." elements
140      * are resolved out. If the specified path attempts to go outside the
141      * boundaries of the current context (i.e. too many ".." path elements
142      * are present), return <code>null</code> instead.
143      *
144      * @param path the path to be normalized
145      **/

146     public static String JavaDoc normalizeURL(String JavaDoc path) {
147
148        if (path == null)
149             return null;
150
151         String JavaDoc normalized = path;
152
153         if (normalized == null)
154             return (null);
155
156         // Normalize the slashes and add leading slash if necessary
157
if (normalized.indexOf('\\') >= 0)
158             normalized = normalized.replace('\\', '/');
159         if (!normalized.startsWith("/"))
160             normalized = "/" + normalized;
161
162         // Resolve occurrences of "//" in the normalized path
163
while (true) {
164             int index = normalized.indexOf("//");
165             if (index < 0)
166                 break;
167             normalized = normalized.substring(0, index) +
168                 normalized.substring(index + 1);
169         }
170
171         // Resolve occurrences of "/./" in the normalized path
172
while (true) {
173             int index = normalized.indexOf("/./");
174             if (index < 0)
175                 break;
176             normalized = normalized.substring(0, index) +
177                 normalized.substring(index + 2);
178         }
179
180         // Resolve occurrences of "/../" in the normalized path
181
while (true) {
182             int index = normalized.indexOf("/../");
183             if (index < 0)
184                 break;
185             if (index == 0)
186                 return (null); // Trying to go outside our context
187
int index2 = normalized.lastIndexOf('/', index - 1);
188             normalized = normalized.substring(0, index2) +
189                 normalized.substring(index + 3);
190         }
191         normalized = normalized.replace('?','_');
192         // Return the normalized path that we have completed
193
return (normalized);
194     }
195
196
197     /**
198      * URL rewriter.
199      *
200      * @param path the path to be rewritten
201      **/

202     public static String JavaDoc encodeURL(String JavaDoc path) {
203         return URLUtil.URLEncode(path, Configuration.realUrlEncoding());
204     }
205
206
207     /**
208      * URL rewriter.
209      *
210      * @param path the path to be rewritten
211      * @param enc the encoding
212      **/

213     public static String JavaDoc encodeURL(String JavaDoc path, String JavaDoc enc) {
214         return URLUtil.URLEncode(path, enc);
215     }
216
217     /**
218      * Maps the URI of a node in the Slide namespace to an external URI as seen
219      * by clients. This involves adding the context and servlet path to the
220      * URI.
221      *
222      * @param uri the node's URI
223      * @param config configuration of the WebdavServlet
224      *
225      * @return the node's URI as absolute path
226      **/

227     public static String JavaDoc getAbsolutePath
228         (String JavaDoc uri, HttpServletRequest JavaDoc req, WebdavServletConfig config)
229     {
230         return getAbsolutePath (uri, req.getContextPath(), req.getServletPath(), config);
231     }
232
233     /**
234      * Maps the URI of a node in the Slide namespace to an external URI as seen
235      * by clients. This involves adding the context and servlet path to the
236      * URI.
237      *
238      * @param uri the node's URI
239      * @param servletPath a String, the result of HttpRequest.getServletPath()
240      * @param contextPath a String , the result of HttpRequest.getContextPath()
241      * @param config configuration of the WebdavServlet
242      *
243      * @return a String
244      *
245      */

246     public static String JavaDoc getAbsolutePath (String JavaDoc uri, String JavaDoc contextPath,
247                                           String JavaDoc servletPath,
248                                           WebdavServletConfig config)
249     {
250         String JavaDoc result = uri;
251
252         String JavaDoc scope = config.getScope();
253         int scopeLength = scope.length();
254         if (scopeLength > 0 && uri.startsWith(scope)) {
255             result = uri.substring(scopeLength);
256         }
257
258         if (!config.isDefaultServlet()) {
259             contextPath += servletPath;
260         }
261         result = contextPath + result;
262
263         return encodeURL(result);
264     }
265
266     public static String JavaDoc getAbsolutePath (String JavaDoc uri, String JavaDoc slideContextPath,
267             WebdavServletConfig config)
268     {
269         String JavaDoc result = uri;
270
271         String JavaDoc scope = config.getScope();
272         int scopeLength = scope.length();
273         if (scopeLength > 0 && uri.startsWith(scope)) {
274             result = uri.substring(scopeLength);
275         }
276
277         result = slideContextPath + result;
278
279         return encodeURL(result);
280     }
281
282     public static String JavaDoc getAbsolutePath (String JavaDoc uri, WebdavContext context)
283     {
284         String JavaDoc result = uri;
285
286         String JavaDoc scope = context.getServletConfig().getScope();
287         int scopeLength = scope.length();
288         if (scopeLength > 0 && uri.startsWith(scope)) {
289             result = uri.substring(scopeLength);
290         }
291
292         result = context.getContextPath() + result;
293
294         return encodeURL(result);
295     }
296
297     /**
298      * Maps the request URI of a HTTP request to a URI in the Slide namespace
299      * (this does not necessarily mean that a node exists at that URI).
300      *
301      * @param req the request object
302      * @param config configuration of the WebdavServlet
303      *
304      * @return the request URI mapped into the Slide namespace
305      **/

306     public static String JavaDoc getRelativePath
307         (HttpServletRequest JavaDoc req, WebdavServletConfig config) {
308
309         // get the requested path, depending on whether the servlet is mapped
310
// as default servlet.
311
String JavaDoc result = null;
312
313         String JavaDoc pathMapperClassName = config.getInitParameter("path-mapper-hook");
314         if (pathMapperClassName != null) {
315             try {
316                 Class JavaDoc storeClass = Class.forName(pathMapperClassName);
317                 PathMapper mapper = (PathMapper) storeClass.newInstance();
318                 result = mapper.map(req, config);
319             } catch (Exception JavaDoc e) {
320                 Domain.log(e, LOG_CHANNEL, Logger.WARNING);
321             }
322         }
323
324         if (result == null) {
325             PathMapper mapper = (PathMapper) config.getServletContext().getAttribute(
326                     "org.apache.slide.webdav.util.PathMapper");
327             if (mapper != null) {
328                 result = mapper.map(req, config);
329             }
330         }
331
332         if (result == null) {
333             if (config.isDefaultServlet()) {
334                 result = req.getServletPath();
335             } else {
336                 result = req.getRequestURI();
337                 result = result.substring(req.getContextPath().length()+ req.getServletPath().length());
338             }
339         }
340
341         // default to the namespace root if no path-info is specified
342
if ((result == null) || (result.length() == 0)) {
343             result = "/";
344         }
345
346         // prefix the URI with the configured scope
347
result = config.getScope() + result;
348         result = normalizeURL(fixTomcatURL(result)); // the request URL is utf-8 encoded
349
return result;
350
351     }
352
353     /**
354      * Returns an URL based on input. The input URL is encoded with "fromEncoding".
355      * The resulting URL is encoded as specified in Configuration.urlEncoding()
356      *
357      * @param input the input URL
358      *
359      * @return a new URL encoded in Configuration.urlEncoding()
360      **/

361     public static String JavaDoc fixTomcatURL(String JavaDoc input) {
362         return fixTomcatHeader(input, "UTF-8");
363     }
364
365
366
367     /**
368      * Returns an decoded header value. The input header is encoded with "ISO-8859-1".
369      * The resulting header is encoded as specified in toEncoding
370      *
371      * @param header the input header
372      * @param toEncoding the target encoding of the header
373      *
374      * @return a new header value encoded in toEncoding
375      **/

376     public static String JavaDoc fixTomcatHeader(String JavaDoc header, String JavaDoc toEncoding) {
377         // todo: toEncoding parameter not used anymore
378
if (header == null) return null;
379
380         String JavaDoc result = null;
381         try {
382             result = URLUtil.URLDecode(header.getBytes("ISO-8859-1"),"ISO-8859-1");
383             result = decodeString(result,Configuration.urlEncoding());
384             if (!Configuration.useUTF8()) {
385                 // Remove unsupported characters
386
result = new String JavaDoc(result.getBytes(Configuration.urlEncoding()),Configuration.urlEncoding());
387             }
388         } catch (Exception JavaDoc e) { e.printStackTrace(); }
389         return result;
390     }
391
392     /**
393      * Returns a SlideToken using the authentication information of an HTTP
394      * request.
395      *
396      * @param req the HTTP request
397      *
398      * @return a new SlideToken instance
399      **/

400     public static SlideToken getSlideToken(HttpServletRequest JavaDoc req) {
401
402         CredentialsToken credentialsToken;
403         Principal JavaDoc principal = req.getUserPrincipal();
404         HttpSession JavaDoc session = req.getSession();
405
406         // store the current principal in the session, to get around a bug in
407
// IE 5 where the authentication info is not submitted by IE when
408
// doing a HEAD request.
409
if (principal == null) {
410             final String JavaDoc credentials = (String JavaDoc) session.getAttribute(CREDENTIALS_ATTRIBUTE);
411             credentialsToken = new CredentialsToken(credentials == null ? "" : credentials);
412         } else {
413             // because the principal is not guaranteed to be serializable
414
// and could thus create problems in a distributed deployment
415
// we store the principal name instead of the principal itself
416
session.setAttribute(CREDENTIALS_ATTRIBUTE, principal.getName());
417             credentialsToken = new CredentialsToken(principal);
418         }
419
420         SlideToken token = new SlideTokenImpl(credentialsToken);
421         token.setEnforceLockTokens(true);
422
423         // store session attributes in token parameters to pass them through
424
// to the stores
425
String JavaDoc[] attr = session.getValueNames();
426         for (int i = 0; i < attr.length; i++) {
427             String JavaDoc name = attr[i];
428             token.addParameter(name, session.getAttribute(name));
429         }
430
431         return token;
432     }
433
434
435     /**
436      * Tests whether a given path maps to a URI in the Slide namespace that
437      * identifies a collection resource.
438      *
439      * @param token the namespace access token
440      * @param slideToken the slide token
441      * @param path relative path of the resource
442      *
443      * @return true if the requested resource is a collection, false otherwise
444      **/

445     public static boolean isCollection
446         (NamespaceAccessToken token, SlideToken slideToken,
447          String JavaDoc path) {
448
449         // Added for DeltaV --start--
450
if( Configuration.useVersionControl() ) {
451             UriHandler uh = UriHandler.getUriHandler( path );
452             if( uh.isWorkspaceUri() )
453                 return true;
454             if( uh.isHistoryUri() )
455                 return true;
456             if( uh.isVersionUri() )
457                 return false;
458         }
459         // Added for DeltaV --end--
460

461         try {
462             Content content = token.getContentHelper();
463             NodeRevisionDescriptors revisionDescriptors =
464                 content.retrieve(slideToken, path);
465             if (revisionDescriptors.hasRevisions()) {
466                 NodeRevisionDescriptor revisionDescriptor =
467                     content.retrieve(slideToken, revisionDescriptors);
468                 return isCollection(revisionDescriptor);
469             } else {
470                 return true;
471             }
472         } catch(ObjectNotFoundException e) {
473             // if the Object is not found return false for no 207 is generated
474
return false;
475         } catch(SlideException e) {
476             // this is the default
477
return true;
478         }
479     }
480
481
482     /**
483      * Tests whether a resource is a collection resource.
484      *
485      * @param revisionDescriptor revision descriptor of the resource
486      *
487      * @return true if the descriptor represents a collection, false otherwise
488      **/

489     public static boolean isCollection
490         (NodeRevisionDescriptor revisionDescriptor) {
491
492         boolean result = false;
493
494         if (revisionDescriptor == null)
495             return true;
496
497         if (revisionDescriptor.propertyValueContains(
498                 NodeRevisionDescriptor.RESOURCE_TYPE ,"collection")) {
499             result = true;
500         }
501
502         return result;
503     }
504
505
506     /**
507      * Tests whether a resource is a redirect reference.
508      *
509      * @param revisionDescriptor revision descriptor of the resource
510      *
511      * @return <code>true</code> if the descriptor represents a redirect
512      * reference, <code>false</code> otherwise.
513      */

514     public static boolean isRedirectref
515         (NodeRevisionDescriptor revisionDescriptor) {
516         if (revisionDescriptor == null)
517             return false;
518
519         return revisionDescriptor.propertyValueContains(
520                 NodeRevisionDescriptor.RESOURCE_TYPE, "redirectref");
521     }
522
523
524     public static String JavaDoc getSlidePath(String JavaDoc fullpath, String JavaDoc slideContextPath) {
525         // strip off the protocol://host:port part
526
if (fullpath.indexOf("://") >= 0) {
527             fullpath=fullpath.substring(fullpath.indexOf("://")+3);
528             fullpath=fullpath.substring(fullpath.indexOf("/"));
529         }
530
531         // strip off the servlet context path
532
if (fullpath.startsWith(slideContextPath)) {
533             fullpath=fullpath.substring(slideContextPath.length());
534         }
535         return fullpath;
536     }
537
538     /**
539      * Get return status based on exception type.
540      */

541     public static int getErrorCode(Throwable JavaDoc ex) {
542         if ( !(ex instanceof SlideException) ) {
543             // if( ex != null ) ex.printStackTrace();
544
return WebdavStatus.SC_INTERNAL_SERVER_ERROR;
545         } else {
546             return getErrorCode((SlideException)ex);
547         }
548     }
549
550     /**
551      * Get return status based on exception type.
552      */

553     public static int getErrorCode(SlideException ex) {
554         try {
555             throw ex;
556         } catch(ObjectNotFoundException e) {
557             return WebdavStatus.SC_NOT_FOUND;
558         } catch(ConflictException e) {
559             return WebdavStatus.SC_CONFLICT;
560         } catch(ForbiddenException e) {
561             return WebdavStatus.SC_FORBIDDEN;
562         } catch(AccessDeniedException e) {
563             return WebdavStatus.SC_FORBIDDEN;
564         } catch(UnauthenticatedException e) {
565             return WebdavStatus.SC_UNAUTHORIZED;
566         } catch(ObjectAlreadyExistsException e) {
567             return WebdavStatus.SC_PRECONDITION_FAILED;
568         } catch(ServiceAccessException e) {
569             return getErrorCode((ServiceAccessException)ex);
570         } catch(ObjectLockedException e) {
571             return WebdavStatus.SC_LOCKED;
572         } catch(WebdavException e) {
573             return e.getStatusCode();
574         } catch(MethodNotAllowedException e) {
575             return WebdavStatus.SC_METHOD_NOT_ALLOWED;
576         } catch(InsufficientStorageException e) {
577             return WebdavStatus.SC_INSUFFICIENT_STORAGE;
578         } catch(VetoException e) {
579             if (e != e.getCause()) {
580                 return getErrorCode(e.getCause());
581             }
582             return WebdavStatus.SC_NOT_ACCEPTABLE;
583         } catch(SlideException e) {
584             return WebdavStatus.SC_INTERNAL_SERVER_ERROR;
585         }
586     }
587
588     /**
589      * Get return status based on exception type.
590      */

591     public static int getErrorCode(ServiceAccessException ex) {
592         Throwable JavaDoc cause = ex.getCauseException();
593         // XXX SocketException pops up when the client closes the connection in the middle of transferring
594
// the HTTP body, so this is no internal error, really!
595
if (cause != null && cause instanceof SocketException JavaDoc) {
596             // XXX is this a reasonable code?
597
return WebdavStatus.SC_PRECONDITION_FAILED;
598         } else if (cause == null || !(cause instanceof SlideException)) {
599             // ex.printStackTrace();
600
if( cause != null ) cause.printStackTrace();
601             return WebdavStatus.SC_INTERNAL_SERVER_ERROR; // this is the default}
602

603         } else {
604             return getErrorCode((SlideException)cause);
605         }
606     }
607 }
608
609
Popular Tags