/*
* $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/util/WebdavUtils.java,v 1.13.2.2 2004/03/14 18:14:56 mholz Exp $
* $Revision: 1.13.2.2 $
* $Date: 2004/03/14 18:14:56 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.slide.webdav.util;
import java.security.Principal;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.slide.authenticate.CredentialsToken;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.SlideTokenImpl;
import org.apache.slide.common.SlideTokenWrapper;
import org.apache.slide.content.Content;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.lock.ObjectLockedException;
import org.apache.slide.macro.ConflictException;
import org.apache.slide.macro.ForbiddenException;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.Configuration;
import org.apache.slide.webdav.WebdavException;
import org.apache.slide.webdav.WebdavServletConfig;
import org.apache.slide.webdav.method.MethodNotAllowedException;
import org.apache.slide.webdav.util.UriHandler;
import org.apache.util.URLUtil;
import org.apache.util.WebdavStatus;
/**
* A collection of various utility and convenience methods.
*
*
* @author Christopher Lenz (cmlenz at apache.org)
* @version $Revision: 1.13.2.2 $
**/
public class WebdavUtils {
private static final String PRINCIPAL_ATTRIBUTE =
"org.apache.slide.webdav.method.principal";
// --------------------------------------------------------- Public Methods
static private void printHexString(String path, String enc) {
try {
byte[] ba;
if (enc == null) ba = path.getBytes();
else ba = path.getBytes("ISO-8859-1");
System.out.print("@@@@ bytes= " + enc + " \t" );
for(int i=0; i < ba.length; i++) {
System.out.print(convertHexDigit(ba[i]));
if( i < ba.length-1 )
System.out.print(" ");
else {
String s;
if (enc == null) s = new String(ba);
else s = new String(ba, enc);
System.out.print(" ( " + s + " )"+ "\n");
}
}
}
catch( Exception x ) {
x.printStackTrace();
}
}
static private void printString(String path) {
System.out.println("");
System.out.println("@@@@ string="+path);
printHexString(path, Configuration.urlEncoding());
printHexString(path, "UTF-8");
// printHexString(path, "SHIFT_JIS");
printHexString(path, null);
}
protected static final String[] hexadecimal =
{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F"};
private static String convertHexDigit( byte toEncode ) {
String result;
int low = (int) (toEncode & 0x0f);
int high = (int) ((toEncode & 0xf0) >> 4);
result = hexadecimal[high] + hexadecimal[low];
return result;
}
/**
* Return a context-relative path, beginning with a "/", that represents
* the canonical version of the specified path after ".." and "." elements
* are resolved out. If the specified path attempts to go outside the
* boundaries of the current context (i.e. too many ".." path elements
* are present), return <code>null</code> instead.
*
* @param path the path to be normalized
**/
public static String decodeURL(String path) {
return decodeURL(path, Configuration.urlEncoding());
}
/**
* Return a context-relative path, beginning with a "/", that represents
* the canonical version of the specified path after ".." and "." elements
* are resolved out. If the specified path attempts to go outside the
* boundaries of the current context (i.e. too many ".." path elements
* are present), return <code>null</code> instead.
*
* @param path the path to be normalized
**/
public static String decodeURL(String path, String enc) {
// @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
//System.out.println("BEFORE ENCODING");
// printString(path);
// @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
// @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
// System.out.println("After ENCODING");
// printString(path);
// System.out.println("");
// System.out.println("");
// @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
if (path == null)
return null;
// Resolve encoded characters in the normalized path,
// which also handles encoded spaces so we can skip that later.
// Placed at the beginning of the chain so that encoded
// bad stuff(tm) can be caught by the later checks
String normalized = URLUtil.URLDecode(path, enc);
if (normalized == null)
return (null);
// Normalize the slashes and add leading slash if necessary
if (normalized.indexOf('\\') >= 0)
normalized = normalized.replace('\\', '/');
if (!normalized.startsWith("/"))
normalized = "/" + normalized;
// Resolve occurrences of "//" in the normalized path
while (true) {
int index = normalized.indexOf("//");
if (index < 0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 1);
}
// Resolve occurrences of "/./" in the normalized path
while (true) {
int index = normalized.indexOf("/./");
if (index < 0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 2);
}
// Resolve occurrences of "/../" in the normalized path
while (true) {
int index = normalized.indexOf("/../");
if (index < 0)
break;
if (index == 0)
return (null); // Trying to go outside our context
int index2 = normalized.lastIndexOf('/', index - 1);
normalized = normalized.substring(0, index2) +
normalized.substring(index + 3);
}
// @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
//printString(normalized);
// @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
// Return the normalized path that we have completed
return (normalized);
}
/**
* URL rewriter.
*
* @param path the path to be rewritten
**/
public static String encodeURL(String path) {
return URLUtil.URLEncode(path, Configuration.urlEncoding());
}
/**
* URL rewriter.
*
* @param path the path to be rewritten
* @param enc the encoding
**/
public static String encodeURL(String path, String enc) {
return URLUtil.URLEncode(path, enc);
}
/**
* Maps the URI of a node in the Slide namespace to an external URI as seen
* by clients. This involves adding the context and servlet path to the
* URI.
*
* @param uri the node's URI
* @param config configuration of the WebdavServlet
*
* @return the node's URI as absolute path
**/
public static String getAbsolutePath
(String uri, HttpServletRequest req, WebdavServletConfig config)
{
return getAbsolutePath (uri, req.getContextPath(), req.getServletPath(), config);
}
/**
* Maps the URI of a node in the Slide namespace to an external URI as seen
* by clients. This involves adding the context and servlet path to the
* URI.
*
* @param uri the node's URI
* @param servletPath a String, the result of HttpRequest.getServletPath()
* @param contextPath a String , the result of HttpRequest.getContextPath()
* @param config configuration of the WebdavServlet
*
* @return a String
*
*/
public static String getAbsolutePath (String uri, String contextPath,
String servletPath,
WebdavServletConfig config)
{
String result = uri;
int scopeLength = config.getScope().length();
if (scopeLength > 0) {
result = uri.substring(scopeLength);
}
if (!config.isDefaultServlet()) {
contextPath += servletPath;
}
result = contextPath + result;
return encodeURL(result);
}
/**
* Maps the request URI of a HTTP request to a URI in the Slide namespace
* (this does not necessarily mean that a node exists at that URI).
*
* @param req the request object
* @param config configuration of the WebdavServlet
*
* @return the request URI mapped into the Slide namespace
**/
public static String getRelativePath
(HttpServletRequest req, WebdavServletConfig config) {
// get the requested path, depending on whether the servlet is mapped
// as default servlet.
String result = null;
if (config.isDefaultServlet()) {
result = req.getServletPath();
} else {
result = req.getPathInfo();
}
// default to the namespace root if no path-info is specified
if ((result == null) || (result.length() == 0)) {
result = "/";
}
// prefix the URI with the configured scope
result = config.getScope() + result;
return decodeURL(fixTomcatURL(result, "UTF-8")); // the request URL is utf-8 encoded
}
/**
* Returns an URL based on input. The input URL is encoded with "fromEncoding".
* The resulting URL is encoded as specified in Configuration.urlEncoding()
*
* @param input the input URL
* @param fromEncoding the used encoding of the input URL
*
* @return a new URL encoded in Configuration.urlEncoding()
**/
public static String fixTomcatURL(String input, String fromEncoding) {
if (input == null) return null;
String result = null;
try {
// printString(input.substring(27,input.length()));
// byte[] a = input.getBytes("UTF-8");
result = encodeURL(new String(input.getBytes(fromEncoding), Configuration.urlEncoding()),
Configuration.urlEncoding());
// printString(result.substring(27,result.length()));
// byte[] b = result.getBytes(Configuration.urlEncoding());
// System.out.println("Length b " + b.length);
} catch (Exception e) { e.printStackTrace(); }
// System.out.println("Length = " + input.length());
// System.out.println("Length = " + result.length());
return result;
}
/**
* Returns an decoded header value. The input header is encoded with "ISO-8859-1".
* The resulting header is encoded as specified in toEncoding
*
* @param header the input header
* @param toEncoding the target encoding of the header
*
* @return a new header value encoded in toEncoding
**/
public static String fixTomcatHeader(String header, String toEncoding) {
if (header == null) return null;
String result = null;
try {
result = URLUtil.URLDecode(new String(header.getBytes("ISO-8859-1"), toEncoding),
toEncoding);
} catch (Exception e) { e.printStackTrace(); }
return result;
}
/**
* Returns a SlideToken using the authentication information of an HTTP
* request.
*
* @param req the HTTP request
*
* @return a new SlideToken instance
**/
public static SlideToken getSlideToken
(HttpServletRequest req) {
Principal principal = req.getUserPrincipal();
HttpSession session = req.getSession();
// store the current principal in the session, to get around a bug in
// IE 5 where the authentication info is not submitted by IE when
// doing a HEAD request.
if (principal == null) {
principal = (Principal) session.getAttribute(PRINCIPAL_ATTRIBUTE);
} else {
session.setAttribute(PRINCIPAL_ATTRIBUTE, principal);
}
CredentialsToken credentials;
if (principal == null) {
credentials = new CredentialsToken("");
} else {
credentials = new CredentialsToken(principal);
}
SlideToken token = new SlideTokenImpl(credentials);
token.setEnforceLockTokens(true);
return token;
}
/**
* Tests whether a given path maps to a URI in the Slide namespace that
* identifies a collection resource.
*
* @param token the namespace access token
* @param slideToken the slide token
* @param path relative path of the resource
*
* @return true if the requested resource is a collection, false otherwise
**/
public static boolean isCollection
(NamespaceAccessToken token, SlideToken slideToken,
String path) {
if (slideToken.isForceStoreEnlistment()) {
slideToken = new SlideTokenWrapper(slideToken, false); // check only, no enlistment
}
// Added for DeltaV --start--
if( Configuration.useVersionControl() ) {
UriHandler uh = UriHandler.getUriHandler( path );
if( uh.isWorkspaceUri() )
return true;
if( uh.isHistoryUri() )
return true;
if( uh.isVersionUri() )
return false;
}
// Added for DeltaV --end--
try {
Content content = token.getContentHelper();
NodeRevisionDescriptors revisionDescriptors =
content.retrieve(slideToken, path);
if (revisionDescriptors.hasRevisions()) {
NodeRevisionDescriptor revisionDescriptor =
content.retrieve(slideToken, revisionDescriptors);
return isCollection(revisionDescriptor);
} else {
return true;
}
} catch(ObjectNotFoundException e) {
// if the Object is not found return false for no 207 is generated
return false;
} catch(SlideException e) {
// this is the default
return true;
}
}
/**
* Tests whether a resource is a collection resource.
*
* @param revisionDescriptor revision descriptor of the resource
*
* @return true if the descriptor represents a collection, false otherwise
**/
public static boolean isCollection
(NodeRevisionDescriptor revisionDescriptor) {
boolean result = false;
if (revisionDescriptor == null)
return true;
if (revisionDescriptor.propertyValueContains(
revisionDescriptor.RESOURCE_TYPE ,"collection")) {
result = true;
}
return result;
}
public static String getSlidePath(String fullpath, String contextPath) {
// strip off the protocol://host:port part
if (fullpath.indexOf("://") >= 0) {
fullpath=fullpath.substring(fullpath.indexOf("://")+3);
fullpath=fullpath.substring(fullpath.indexOf("/"));
}
// strip off the servlet context path
if (fullpath.startsWith(contextPath)) {
fullpath=fullpath.substring(contextPath.length());
}
return fullpath;
}
/**
* Get return status based on exception type.
*/
public static int getErrorCode(Throwable ex) {
if ( !(ex instanceof SlideException) ) {
// if( ex != null ) ex.printStackTrace();
return WebdavStatus.SC_INTERNAL_SERVER_ERROR;
} else {
return getErrorCode((SlideException)ex);
}
}
/**
* Get return status based on exception type.
*/
public static int getErrorCode(SlideException ex) {
try {
throw ex;
} catch(ObjectNotFoundException e) {
return WebdavStatus.SC_NOT_FOUND;
} catch(ConflictException e) {
return WebdavStatus.SC_CONFLICT;
} catch(ForbiddenException e) {
return WebdavStatus.SC_FORBIDDEN;
} catch(AccessDeniedException e) {
return WebdavStatus.SC_FORBIDDEN;
} catch(ObjectAlreadyExistsException e) {
return WebdavStatus.SC_PRECONDITION_FAILED;
} catch(ServiceAccessException e) {
return getErrorCode((ServiceAccessException)ex);
} catch(ObjectLockedException e) {
return WebdavStatus.SC_LOCKED;
} catch(WebdavException e) {
return e.getStatusCode();
} catch(MethodNotAllowedException e) {
return WebdavStatus.SC_METHOD_NOT_ALLOWED;
} catch(SlideException e) {
// e.printStackTrace();
return WebdavStatus.SC_INTERNAL_SERVER_ERROR;
}
}
/**
* Get return status based on exception type.
*/
public static int getErrorCode(ServiceAccessException ex) {
Throwable cause = ex.getCauseException();
if (cause == null || !(cause instanceof SlideException) ) {
// ex.printStackTrace();
if( cause != null ) cause.printStackTrace();
return WebdavStatus.SC_INTERNAL_SERVER_ERROR; // this is the default}
} else {
return getErrorCode((SlideException)cause);
}
}
}