You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/11/03 22:27:53 UTC
cvs commit: jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/util DOMWriter.java MD5Encoder.java MIME2Java.java StringManager.java XMLWriter.java
costin 00/11/03 13:27:48
Modified: . build.xml
Added: src/webdav/org/apache/tomcat/webdav DefaultServlet.java
LocalStrings.properties WebdavServlet.java
src/webdav/org/apache/tomcat/webdav/resources
DirectoryBean.java FileResources.java
JarResources.java LocalStrings.properties
ResourceBean.java ResourceUtils.java Resources.java
ResourcesBase.java
src/webdav/org/apache/tomcat/webdav/util DOMWriter.java
MD5Encoder.java MIME2Java.java StringManager.java
XMLWriter.java
Log:
Initial commit for the webdav module from catalina.
Most of the junk is removed, but a bit more refactoring is still needed -
the cache generates a lot of garbage and the number of threads need to
be reduced ( and integrated with the server's thread management )
The new tomcat3 module has no external dependency ( except servlet22.jar ).
It doesn't work yet, but compiles fine ( it's very close to working :-)
Probably the resources should go to tomcat.util.resources, as we may reuse
them ( or at least the cache ) for static files ( well, that's just for
benchmark purpose, I don't think any decent admin would turn this on,
using a large cache in java will kill everything else - of course,
if only one file is cached, like in a "ab" benchmark, it looks ok ... )
The only missing part is the ServerLiaison, that will allow it to plug
into any servlet container.
Revision Changes Path
1.90 +19 -1 jakarta-tomcat/build.xml
Index: build.xml
===================================================================
RCS file: /home/cvs/jakarta-tomcat/build.xml,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -r1.89 -r1.90
--- build.xml 2000/11/02 21:56:58 1.89
+++ build.xml 2000/11/03 21:27:31 1.90
@@ -237,6 +237,24 @@
</jar>
</target>
+ <!-- ==================== Webdav ========== -->
+ <target name="dav" depends="init" >
+ <javac destdir="${tomcat.build}/classes"
+ debug="${debug}"
+ optimize="${optimize}"
+ deprecation="off"
+ srcdir="src/webdav" >
+ <classpath>
+ <pathelement location="${servlet22.jar}" />
+ </classpath>
+ <include name="org/apache/tomcat/webdav/**" />
+ </javac>
+ <jar jarfile="${tomcat.build}/lib/webdav.jar"
+ basedir="${tomcat.build}/classes" >
+ <include name="org/apache/tomcat/webdav/**" />
+ </jar>
+ </target>
+
<!-- ==================== Servlet 23 (default) implementation ========== -->
<target name="facade23" depends="init" >
<javac destdir="${tomcat.build}/classes"
@@ -315,7 +333,7 @@
/>
</target>
- <target name="tomcat-jars-new" depends="tomcat_util,tomcat.jar,tomcat_core,jasper,tomcat_modules,facade22,facade23,tomcat_config">
+ <target name="tomcat-jars-new" depends="tomcat_util,tomcat.jar,tomcat_core,jasper,tomcat_modules,facade22,facade23,tomcat_config,dav">
</target>
<!-- ==================== J2EE integration ========== -->
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/DefaultServlet.java
Index: DefaultServlet.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Locale;
import java.util.Hashtable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.webdav.util.*;
import org.apache.tomcat.webdav.resources.*;
// import org.apache.catalina.Globals;
// import org.apache.catalina.Resources;
// import org.apache.catalina.core.ApplicationContext;
// import org.apache.catalina.resources.ResourceBean;
// import org.apache.catalina.resources.DirectoryBean;
// import org.apache.catalina.util.MD5Encoder;
// import org.apache.catalina.util.StringManager;
// import org.apache.catalina.util.xml.SaxContext;
// import org.apache.catalina.util.xml.XmlAction;
// import org.apache.catalina.util.xml.XmlMapper;
/**
* The default resource-serving servlet for most web applications,
* used to serve static resources such as HTML pages and images.
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:37 $
*/
public class DefaultServlet
extends HttpServlet {
/**
* The servlet context attribute under which we record the set of
* welcome files (as an object of type String[]) for this application.
*/
public static final String WELCOME_FILES_ATTR =
"org.apache.catalina.WELCOME_FILES";
// ----------------------------------------------------- Instance Variables
/**
* The debugging detail level for this servlet.
*/
protected int debug = 0;
/**
* The input buffer size to use when serving resources.
*/
protected int input = 2048;
/**
* Should we generate directory listings when no welcome file is present?
*/
protected boolean listings = true;
/**
* Read only flag. By default, it's set to true.
*/
protected boolean readOnly = true;
/**
* The output buffer size to use when serving resources.
*/
protected int output = 2048;
/**
* The set of welcome files for this web application
*/
protected String welcomes[] = new String[0];
/**
* MD5 message digest provider.
*/
protected static MessageDigest md5Helper;
/**
* The MD5 helper object for this class.
*/
protected static final MD5Encoder md5Encoder = new MD5Encoder();
/**
* The set of SimpleDateFormat formats to use in getDateHeader().
*/
protected static final SimpleDateFormat formats[] = {
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
};
/**
* MIME multipart separation string
*/
protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
/**
* The string manager for this package.
*/
protected static StringManager sm =
StringManager.getManager("org.apache.tomcat.webdav");
// --------------------------------------------------------- Public Methods
/**
* Finalize this servlet.
*/
public void destroy() {
; // No actions necessary
}
/**
* Initialize this servlet.
*/
public void init() throws ServletException {
// Set our properties from the initialization parameters
String value = null;
try {
value = getServletConfig().getInitParameter("debug");
debug = Integer.parseInt(value);
} catch (Throwable t) {
;
}
try {
value = getServletConfig().getInitParameter("input");
input = Integer.parseInt(value);
} catch (Throwable t) {
;
}
try {
value = getServletConfig().getInitParameter("listings");
listings = (new Boolean(value)).booleanValue();
} catch (Throwable t) {
;
}
try {
value = getServletConfig().getInitParameter("readonly");
readOnly = (new Boolean(value)).booleanValue();
} catch (Throwable t) {
;
}
try {
value = getServletConfig().getInitParameter("output");
output = Integer.parseInt(value);
} catch (Throwable t) {
;
}
// Sanity check on the specified buffer sizes
if (input < 256)
input = 256;
if (output < 256)
output = 256;
// Initialize the set of welcome files for this application
welcomes = (String[]) getServletContext().getAttribute
(WELCOME_FILES_ATTR);
if (welcomes == null)
welcomes = new String[0];
if (debug > 0) {
log("DefaultServlet.init: input buffer size=" + input +
", output buffer size=" + output);
for (int i = 0; i < welcomes.length; i++)
log("DefaultServlet.init: welcome file=" +
welcomes[i]);
}
// Load the MD5 helper used to calculate signatures.
try {
md5Helper = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new IllegalStateException();
}
}
// ------------------------------------------------------ Protected Methods
/**
* Return the relative path associated with this servlet.
*
* @param request The servlet request we are processing
*/
protected String getRelativePath(HttpServletRequest request) {
// Are we being processed by a RequestDispatcher.include()?
if (request.getAttribute("javax.servlet.include.request_uri")!=null) {
String result = (String)
request.getAttribute("javax.servlet.include.path_info");
if (result == null)
result = (String)
request.getAttribute("javax.servlet.include.servlet_path");
if ((result == null) || (result.equals("")))
result = "/";
return (result);
}
// No, extract the desired path directly from the request
String result = request.getPathInfo();
if (result == null) {
result = request.getServletPath();
}
if ((result == null) || (result.equals(""))) {
result = "/";
}
return result;
}
/**
* Process a GET request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Serve the requested resource, including the data content
serveResource(request, response, true);
}
/**
* Process a HEAD request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void doHead(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Serve the requested resource, without the data content
serveResource(request, response, false);
}
/**
* Process a POST request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
doPut(request, response);
}
protected Resources getResources() {
// XXX get context resources
return null;//new Container( getServletContext() );
}
/**
* Process a POST request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String path = getRelativePath(req);
// Looking for a Content-Range header
if (req.getHeader("Content-Range") != null) {
// No content range header is supported
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
}
Resources resources = getResources();
boolean exists = resources.exists(path);
boolean result = resources.setResource(path, req.getInputStream());
if (result) {
if (exists) {
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
} else {
resp.setStatus(HttpServletResponse.SC_CREATED);
}
} else {
resp.sendError(HttpServletResponse.SC_CONFLICT);
}
}
/**
* Process a POST request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String path = getRelativePath(req);
Resources resources = getResources();
boolean exists = resources.exists(path);
if (exists) {
boolean result = resources.deleteResource(path);
if (result) {
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
} else {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Check if the conditions specified in the optional If headers are
* satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceInfo File object
* @return boolean true if the resource meets all the specified conditions,
* and false if any of the conditions is not satisfied, in which case
* request processing is stopped
*/
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,
ResourceInfo resourceInfo)
throws IOException {
String eTag = getETag(resourceInfo, true);
long fileLength = resourceInfo.length;
long lastModified = resourceInfo.date;
StringTokenizer commaTokenizer;
String headerValue;
// Checking If-Match
headerValue = request.getHeader("If-Match");
if (headerValue != null) {
if (headerValue.indexOf("*") == -1) {
commaTokenizer = new StringTokenizer(headerValue, ",");
boolean conditionSatisfied = false;
while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
String currentToken = commaTokenizer.nextToken();
if (currentToken.trim().equals(eTag))
conditionSatisfied = true;
}
// If none of the given ETags match, 412 Precodition failed is
// sent back
if (!conditionSatisfied) {
response.sendError
(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
}
// Checking If-Modified-Since
headerValue = request.getHeader("If-Modified-Since");
if (headerValue != null) {
// If an If-None-Match header has been specified, if modified since
// is ignored.
if (request.getHeader("If-None-Match") == null) {
Date date = null;
// Parsing the HTTP Date
for (int i = 0; (date == null) && (i < formats.length); i++) {
try {
date = formats[i].parse(headerValue);
} catch (ParseException e) {
;
}
}
if ((date != null)
&& (lastModified <= (date.getTime() + 1000)) ) {
// The entity has not been modified since the date
// specified by the client. This is not an error case.
response.sendError
(HttpServletResponse.SC_NOT_MODIFIED);
return false;
}
}
}
// Checking If-None-Match
headerValue = request.getHeader("If-None-Match");
if (headerValue != null) {
if (headerValue.indexOf("*") == -1) {
commaTokenizer = new StringTokenizer(headerValue, ",");
boolean conditionSatisfied = false;
while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
String currentToken = commaTokenizer.nextToken();
if (currentToken.trim().equals(eTag))
conditionSatisfied = true;
}
if (conditionSatisfied) {
// For GET and HEAD, we should respond with
// 304 Not Modified.
// For every other method, 412 Precondition Failed is sent
// back.
if ( ("GET".equals(request.getMethod()))
|| ("HEAD".equals(request.getMethod())) ) {
response.sendError
(HttpServletResponse.SC_NOT_MODIFIED);
return false;
} else {
response.sendError
(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
} else {
if (resourceInfo.exists()) {
}
}
}
// Checking If-Unmodified-Since
headerValue = request.getHeader("If-Unmodified-Since");
if (headerValue != null) {
Date date = null;
// Parsing the HTTP Date
for (int i = 0; (date == null) && (i < formats.length); i++) {
try {
date = formats[i].parse(headerValue);
} catch (ParseException e) {
;
}
}
if ( (date != null) && (lastModified > date.getTime()) ) {
// The entity has not been modified since the date
// specified by the client. This is not an error case.
response.sendError
(HttpServletResponse.SC_PRECONDITION_FAILED);
return false;
}
}
return true;
}
/**
* Get the ETag value associated with a file.
*
* @param resourceInfo File object
* @param strong True if we want a strong ETag, in which case a checksum
* of the file has to be calculated
*/
protected String getETagValue(ResourceInfo resourceInfo, boolean strong) {
// FIXME : Compute a strong ETag if requested, using an MD5 digest
// of the file contents
return resourceInfo.length + "-" + resourceInfo.date;
}
/**
* Get the ETag associated with a file.
*
* @param resourceInfo File object
* @param strong True if we want a strong ETag, in which case a checksum
* of the file has to be calculated
*/
protected String getETag(ResourceInfo resourceInfo, boolean strong) {
if (strong)
return "\"" + getETagValue(resourceInfo, strong) + "\"";
else
return "W/\"" + getETagValue(resourceInfo, strong) + "\"";
}
// -------------------------------------------------------- Private Methods
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param istream The input stream to read from
* @param ostream The output stream to write to
*
* @exception IOException if an input/output error occurs
*/
private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream)
throws IOException {
IOException exception = null;
// FIXME : i18n ?
InputStream resourceInputStream =
resourceInfo.resources.getResourceAsStream(resourceInfo.path);
InputStream istream = new BufferedInputStream
(resourceInputStream, input);
// Copy the input stream to the output stream
exception = copyRange(istream, ostream);
// Clean up the input stream
try {
istream.close();
} catch (Throwable t) {
;
}
// Rethrow any exception that has occurred
if (exception != null)
throw exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param istream The input stream to read from
* @param writer The writer to write to
*
* @exception IOException if an input/output error occurs
*/
private void copy(ResourceInfo resourceInfo, PrintWriter writer)
throws IOException {
IOException exception = null;
InputStream resourceInputStream =
resourceInfo.resources.getResourceAsStream(resourceInfo.path);
// FIXME : i18n ?
Reader reader = new InputStreamReader(resourceInputStream);
// Copy the input stream to the output stream
exception = copyRange(reader, writer);
// Clean up the reader
try {
reader.close();
} catch (Throwable t) {
;
}
// Rethrow any exception that has occurred
if (exception != null)
throw exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param resourceInfo The ResourceInfo object
* @param ostream The output stream to write to
* @param range Range the client wanted to retrieve
* @exception IOException if an input/output error occurs
*/
private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
Range range)
throws IOException {
IOException exception = null;
InputStream resourceInputStream =
resourceInfo.resources.getResourceAsStream(resourceInfo.path);
InputStream istream =
new BufferedInputStream(resourceInputStream, input);
exception = copyRange(istream, ostream, range.start, range.end);
// Clean up the input stream
try {
istream.close();
} catch (Throwable t) {
;
}
// Rethrow any exception that has occurred
if (exception != null)
throw exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param resourceInfo The ResourceInfo object
* @param writer The writer to write to
* @param range Range the client wanted to retrieve
* @exception IOException if an input/output error occurs
*/
private void copy(ResourceInfo resourceInfo, PrintWriter writer,
Range range)
throws IOException {
IOException exception = null;
InputStream resourceInputStream =
resourceInfo.resources.getResourceAsStream(resourceInfo.path);
Reader reader = new InputStreamReader(resourceInputStream);
exception = copyRange(reader, writer, range.start, range.end);
// Clean up the input stream
try {
reader.close();
} catch (Throwable t) {
;
}
// Rethrow any exception that has occurred
if (exception != null)
throw exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param resourceInfo The ResourceInfo object
* @param ostream The output stream to write to
* @param ranges Enumeration of the ranges the client wanted to retrieve
* @param contentType Content type of the resource
* @exception IOException if an input/output error occurs
*/
private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream,
Enumeration ranges, String contentType)
throws IOException {
IOException exception = null;
while ( (exception == null) && (ranges.hasMoreElements()) ) {
InputStream resourceInputStream =
resourceInfo.resources.getResourceAsStream(resourceInfo.path);
InputStream istream = // FIXME: internationalization???????
new BufferedInputStream(resourceInputStream, input);
Range currentRange = (Range) ranges.nextElement();
// Writing MIME header.
ostream.println("--" + mimeSeparation);
if (contentType != null)
ostream.println("Content-Type: " + contentType);
ostream.println("Content-Range: bytes " + currentRange.start
+ "-" + currentRange.end + "/"
+ currentRange.length);
ostream.println();
// Printing content
exception = copyRange(istream, ostream, currentRange.start,
currentRange.end);
try {
istream.close();
} catch (Throwable t) {
;
}
}
ostream.print("--" + mimeSeparation + "--");
// Rethrow any exception that has occurred
if (exception != null)
throw exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param resourceInfo The ResourceInfo object
* @param writer The writer to write to
* @param ranges Enumeration of the ranges the client wanted to retrieve
* @param contentType Content type of the resource
* @exception IOException if an input/output error occurs
*/
private void copy(ResourceInfo resourceInfo, PrintWriter writer,
Enumeration ranges, String contentType)
throws IOException {
IOException exception = null;
while ( (exception == null) && (ranges.hasMoreElements()) ) {
InputStream resourceInputStream =
resourceInfo.resources.getResourceAsStream(resourceInfo.path);
Reader reader = new InputStreamReader(resourceInputStream);
Range currentRange = (Range) ranges.nextElement();
// Writing MIME header.
writer.println("--" + mimeSeparation);
if (contentType != null)
writer.println("Content-Type: " + contentType);
writer.println("Content-Range: bytes " + currentRange.start
+ "-" + currentRange.end + "/"
+ currentRange.length);
writer.println();
// Printing content
exception = copyRange(reader, writer, currentRange.start,
currentRange.end);
try {
reader.close();
} catch (Throwable t) {
;
}
}
writer.print("--" + mimeSeparation + "--");
// Rethrow any exception that has occurred
if (exception != null)
throw exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param istream The input stream to read from
* @param ostream The output stream to write to
* @return Exception which occured during processing
*/
private IOException copyRange(InputStream istream,
ServletOutputStream ostream) {
// Copy the input stream to the output stream
IOException exception = null;
byte buffer[] = new byte[input];
int len = buffer.length;
while (true) {
try {
len = istream.read(buffer);
if (len == -1)
break;
ostream.write(buffer, 0, len);
} catch (IOException e) {
exception = e;
len = -1;
break;
}
}
return exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param reader The reader to read from
* @param writer The writer to write to
* @return Exception which occured during processing
*/
private IOException copyRange(Reader reader, PrintWriter writer) {
// Copy the input stream to the output stream
IOException exception = null;
char buffer[] = new char[input];
int len = buffer.length;
while (true) {
try {
len = reader.read(buffer);
if (len == -1)
break;
writer.write(buffer, 0, len);
} catch (IOException e) {
exception = e;
len = -1;
break;
}
}
return exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param istream The input stream to read from
* @param ostream The output stream to write to
* @param start Start of the range which will be copied
* @param end End of the range which will be copied
* @return Exception which occured during processing
*/
private IOException copyRange(InputStream istream,
ServletOutputStream ostream,
long start, long end) {
try {
istream.skip(start);
} catch (IOException e) {
return e;
}
IOException exception = null;
long bytesToRead = end - start + 1;
byte buffer[] = new byte[input];
int len = buffer.length;
while ( (bytesToRead > 0) && (len >= buffer.length)) {
try {
len = istream.read(buffer);
if (bytesToRead >= len) {
ostream.write(buffer, 0, len);
bytesToRead -= len;
} else {
ostream.write(buffer, 0, (int) bytesToRead);
bytesToRead = 0;
}
} catch (IOException e) {
exception = e;
len = -1;
}
if (len < buffer.length)
break;
}
return exception;
}
/**
* Copy the contents of the specified input stream to the specified
* output stream, and ensure that both streams are closed before returning
* (even in the face of an exception).
*
* @param reader The reader to read from
* @param writer The writer to write to
* @param start Start of the range which will be copied
* @param end End of the range which will be copied
* @return Exception which occured during processing
*/
private IOException copyRange(Reader reader, PrintWriter writer,
long start, long end) {
try {
reader.skip(start);
} catch (IOException e) {
return e;
}
IOException exception = null;
long bytesToRead = end - start + 1;
char buffer[] = new char[input];
int len = buffer.length;
while ( (bytesToRead > 0) && (len >= buffer.length)) {
try {
len = reader.read(buffer);
if (bytesToRead >= len) {
writer.write(buffer, 0, len);
bytesToRead -= len;
} else {
writer.write(buffer, 0, (int) bytesToRead);
bytesToRead = 0;
}
} catch (IOException e) {
exception = e;
len = -1;
}
if (len < buffer.length)
break;
}
return exception;
}
/**
* Display the size of a file.
*/
private void displaySize(StringBuffer buf, int filesize) {
int leftside = filesize / 1024;
int rightside = (filesize % 1024) / 103; // makes 1 digit
// To avoid 0.0 for non-zero file, we bump to 0.1
if (leftside == 0 && rightside == 0 && filesize != 0)
rightside = 1;
buf.append(leftside).append(".").append(rightside);
buf.append(" KB");
}
/**
* Check to see if a default page exists.
*
* @param pathname Pathname of the file to be served
*/
private ResourceInfo checkWelcomeFiles(String pathname,
Resources resources) {
String collectionName = pathname;
if (!pathname.endsWith("/")) {
collectionName += "/";
}
// Refresh our currently defined set of welcome files
synchronized (welcomes) {
welcomes = (String[]) getServletContext().getAttribute
(WELCOME_FILES_ATTR);
if (welcomes == null)
welcomes = new String[0];
}
// Serve a welcome resource or file if one exists
for (int i = 0; i < welcomes.length; i++) {
// Does the specified resource exist?
String resourceName = collectionName + welcomes[i];
ResourceInfo resourceInfo =
new ResourceInfo(resourceName, resources);
if (resourceInfo.exists()) {
return resourceInfo;
}
}
return null;
}
/**
* Serve the specified resource, optionally including the data content.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param content Should the content be included?
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
private void serveResource(HttpServletRequest request,
HttpServletResponse response,
boolean content)
throws IOException, ServletException {
// Identify the requested resource path
String path = getRelativePath(request);
if (debug > 0) {
if (content)
log("DefaultServlet.serveResource: Serving resource '" +
path + "' headers and data");
else
log("DefaultServlet.serveResource: Serving resource '" +
path + "' headers only");
}
// Exclude any resource in the /WEB-INF and /META-INF subdirectories
// (the "toUpperCase()" avoids problems on Windows systems)
if (path.toUpperCase().startsWith("/WEB-INF") ||
path.toUpperCase().startsWith("/META-INF")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
return;
}
Resources resources = getResources();
ResourceInfo resourceInfo = new ResourceInfo(path, resources);
if (!resourceInfo.exists) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
return;
}
// If the resource is not a collection, and the resource path
// ends with "/" or "\", return NOT FOUND
if (!resourceInfo.collection) {
if (path.endsWith("/") || (path.endsWith("\\"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
return;
}
}
// If the resource is a collection (aka a directory), we check
// the welcome files list.
if (resourceInfo.collection) {
if (!request.getRequestURI().endsWith("/")) {
response.sendRedirect(request.getRequestURI() + "/");
return;
}
ResourceInfo welcomeFileInfo = checkWelcomeFiles(path, resources);
if (welcomeFileInfo != null) {
String redirectPath = welcomeFileInfo.path;
String contextPath = request.getContextPath();
if ((contextPath != null) && (!contextPath.equals("/"))) {
redirectPath = contextPath + redirectPath;
}
response.sendRedirect(redirectPath);
return;
}
}
if (!resourceInfo.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
resourceInfo.path);
return;
}
// Checking If headers
if ( !checkIfHeaders(request, response, resourceInfo) )
return;
// Find content type.
String contentType =
getServletContext().getMimeType(resourceInfo.path);
if (resourceInfo.collection) {
// Skip directory listings if we have been configured to
// suppress them
if (!listings) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
resourceInfo.path);
return;
}
contentType = "text/html";
}
// Parse range specifier
Vector ranges = null;
if (!resourceInfo.collection) {
ranges = parseRange(request, response, resourceInfo);
// Last-Modified header
if (debug > 0)
log("DefaultServlet.serveFile: lastModified='" +
(new Timestamp(resourceInfo.date)).toString() + "'");
response.setDateHeader("Last-Modified", resourceInfo.date);
// ETag header
response.setHeader("ETag", getETag(resourceInfo, true));
}
ServletOutputStream ostream = null;
PrintWriter writer = null;
if (content) {
// Trying to retrieve the servlet output stream
try {
ostream = response.getOutputStream();
} catch (IllegalStateException e) {
// If it fails, we try to get a Writer instead if we're
// trying to serve a text file
if ( (contentType != null)
&& (contentType.startsWith("text")) ) {
writer = response.getWriter();
} else {
throw e;
}
}
}
if ( ((ranges == null) || (ranges.isEmpty()))
&& (request.getHeader("Range") == null) ) {
// Set the appropriate output headers
if (contentType != null) {
if (debug > 0)
log("DefaultServlet.serveFile: contentType='" +
contentType + "'");
response.setContentType(contentType);
}
long contentLength = resourceInfo.length;
if ((!resourceInfo.collection) && (contentLength >= 0)) {
if (debug > 0)
log("DefaultServlet.serveFile: contentLength=" +
contentLength);
response.setContentLength((int) contentLength);
}
// Copy the input stream to our output stream (if requested)
if (content) {
response.setBufferSize(output);
if (ostream != null) {
copy(resourceInfo, ostream);
} else {
copy(resourceInfo, writer);
}
}
} else {
if ((ranges == null) || (ranges.isEmpty()))
return;
// Partial content response.
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (ranges.size() == 1) {
Range range = (Range) ranges.elementAt(0);
response.addHeader("Content-Range", "bytes "
+ range.start
+ "-" + range.end + "/"
+ range.length);
if (contentType != null) {
if (debug > 0)
log("DefaultServlet.serveFile: contentType='" +
contentType + "'");
response.setContentType(contentType);
}
if (content) {
response.setBufferSize(output);
if (ostream != null) {
copy(resourceInfo, ostream, range);
} else {
copy(resourceInfo, writer, range);
}
}
} else {
response.setContentType("multipart/byteranges; boundary="
+ mimeSeparation);
if (content) {
response.setBufferSize(output);
if (ostream != null) {
copy(resourceInfo, ostream, ranges.elements(),
contentType);
} else {
copy(resourceInfo, writer, ranges.elements(),
contentType);
}
}
}
}
}
/**
* Parse the range header.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @return Vector of ranges
*/
private Vector parseRange(HttpServletRequest request,
HttpServletResponse response,
ResourceInfo resourceInfo)
throws IOException {
// Checking If-Range
String headerValue = request.getHeader("If-Range");
if (headerValue != null) {
String eTag = getETag(resourceInfo, true);
long lastModified = resourceInfo.date;
Date date = null;
// Parsing the HTTP Date
for (int i = 0; (date == null) && (i < formats.length); i++) {
try {
date = formats[i].parse(headerValue);
} catch (ParseException e) {
;
}
}
if (date == null) {
// If the ETag the client gave does not match the entity
// etag, then the entire entity is returned.
if (!eTag.equals(headerValue.trim()))
return null;
} else {
// If the timestamp of the entity the client got is older than
// the last modification date of the entity, the entire entity
// is returned.
if (lastModified > (date.getTime() + 1000))
return null;
}
}
long fileLength = resourceInfo.length;
if (fileLength == 0)
return null;
// Retrieving the range header (if any is specified
String rangeHeader = request.getHeader("Range");
if (rangeHeader == null)
return null;
// bytes is the only range unit supported (and I don't see the point
// of adding new ones).
if (!rangeHeader.startsWith("bytes")) {
response.sendError
(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
rangeHeader = rangeHeader.substring(6);
// Vector which will contain all the ranges which are successfully
// parsed.
Vector result = new Vector();
StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
// Parsing the range list
while (commaTokenizer.hasMoreTokens()) {
String rangeDefinition = commaTokenizer.nextToken();
Range currentRange = new Range();
currentRange.length = fileLength;
int dashPos = rangeDefinition.indexOf('-');
if (dashPos == -1) {
response.sendError
(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
if (dashPos == 0) {
try {
long offset = Long.parseLong(rangeDefinition);
currentRange.start = fileLength + offset;
currentRange.end = fileLength - 1;
} catch (NumberFormatException e) {
response.sendError
(HttpServletResponse
.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
} else {
try {
currentRange.start = Long.parseLong
(rangeDefinition.substring(0, dashPos));
if (dashPos < rangeDefinition.length() - 1)
currentRange.end = Long.parseLong
(rangeDefinition.substring
(dashPos + 1, rangeDefinition.length()));
else
currentRange.end = fileLength - 1;
} catch (NumberFormatException e) {
response.sendError
(HttpServletResponse
.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
}
if (!currentRange.validate()) {
response.sendError
(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return null;
}
result.addElement(currentRange);
}
return result;
}
// ------------------------------------------------------ Range Inner Class
private class Range {
public long start;
public long end;
public long length;
/**
* Validate range.
*/
public boolean validate() {
return ( (start >= 0) && (end >= 0) && (length > 0)
&& (start <= end) && (end < length) );
}
}
// ---------------------------------------------- ResourceInfo Inner Class
protected class ResourceInfo {
/**
* Constructor.
*
* @param pathname Path name of the file
*/
public ResourceInfo(String path, Resources resources) {
this.path = path;
this.resources = resources;
this.exists = resources.exists(path);
if (exists) {
this.creationDate = resources.getResourceCreated(path);
this.date = resources.getResourceModified(path);
this.httpDate = formats[0].format(new Date(date));
this.length = resources.getResourceLength(path);
this.collection = resources.isCollection(path);
}
}
public String path;
public long creationDate;
public String httpDate;
public long date;
public long length;
public boolean collection;
public boolean exists;
public Resources resources;
/**
* Test if the associated resource exists.
*/
public boolean exists() {
return exists;
}
/**
* String representation.
*/
public String toString() {
return path;
}
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/LocalStrings.properties
Index: LocalStrings.properties
===================================================================
defaultservlet.directorylistingfor=Directory Listing for:
defaultservlet.upto=Up to:
defaultservlet.subdirectories=Subdirectories:
defaultservlet.files=Files:
webdavservlet.jaxpfailed=JAXP initialization failed
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/WebdavServlet.java
Index: WebdavServlet.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Locale;
import java.util.Hashtable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.tomcat.webdav.util.*;
import org.apache.tomcat.webdav.resources.*;
// import org.apache.catalina.Resources;
// import org.apache.catalina.resources.ResourceBean;
// import org.apache.catalina.resources.DirectoryBean;
// import org.apache.catalina.util.MD5Encoder;
// import org.apache.catalina.util.StringManager;
// import org.apache.catalina.util.XMLWriter;
// import org.apache.catalina.util.DOMWriter;
// import org.apache.catalina.util.xml.SaxContext;
// import org.apache.catalina.util.xml.XmlAction;
// import org.apache.catalina.util.xml.XmlMapper;
/**
* Servlet which adds support for WebDAV level 2. All the basic HTTP requests
* are handled by the DefaultServlet.
*
* @author Remy Maucherat
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:37 $
*/
public class WebdavServlet
extends DefaultServlet {
// -------------------------------------------------------------- Constants
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_PROPFIND = "PROPFIND";
private static final String METHOD_PROPPATCH = "PROPPATCH";
private static final String METHOD_MKCOL = "MKCOL";
private static final String METHOD_COPY = "COPY";
private static final String METHOD_MOVE = "MOVE";
private static final String METHOD_LOCK = "LOCK";
private static final String METHOD_UNLOCK = "UNLOCK";
/**
* Default depth is infite.
*/
private static final int INFINITY = 3; // To limit tree browsing a bit
/**
* PROPFIND - Specify a property mask.
*/
private static final int FIND_BY_PROPERTY = 0;
/**
* PROPFIND - Display all properties.
*/
private static final int FIND_ALL_PROP = 1;
/**
* PROPFIND - Return property names.
*/
private static final int FIND_PROPERTY_NAMES = 2;
/**
* Create a new lock.
*/
private static final int LOCK_CREATION = 0;
/**
* Refresh lock.
*/
private static final int LOCK_REFRESH = 1;
/**
* Default lock timeout value.
*/
private static final int DEFAULT_TIMEOUT = 3600;
/**
* Maximum lock timeout.
*/
private static final int MAX_TIMEOUT = 604800;
/**
* Default namespace.
*/
protected static final String DEFAULT_NAMESPACE = "DAV:";
// ----------------------------------------------------- Instance Variables
/**
* JAXP Document builder.
*/
private DocumentBuilder documentBuilder;
/**
* Repository of the locks put on single resources.
* <p>
* Key : path <br>
* Value : LockInfo
*/
private Hashtable resourceLocks = new Hashtable();
/**
* Repository of the lock-null resources.
* <p>
* Key : path of the collection containing the lock-null resource<br>
* Value : Vector of lock-null resource which are members of the
* collection. Each element of the Vector is the path associated with
* the lock-null resource.
*/
private Hashtable lockNullResources = new Hashtable();
/**
* Vector of the heritable locks.
* <p>
* Key : path <br>
* Value : LockInfo
*/
private Vector collectionLocks = new Vector();
/**
* Secret information used to generate reasonably secure lock ids.
*/
private String secret = "catalina";
// --------------------------------------------------------- Public Methods
/**
* Return the relative path associated with this servlet.
*
* @param request The servlet request we are processing
*/
protected String getRelativePath(HttpServletRequest request) {
// Are we being processed by a RequestDispatcher.include()?
if (request.getAttribute("javax.servlet.include.request_uri")!=null) {
String result = (String)
request.getAttribute("javax.servlet.include.path_info");
if (result == null)
result = (String)
request.getAttribute("javax.servlet.include.servlet_path");
if ((result == null) || (result.equals("")))
result = "/";
return (result);
}
// No, extract the desired path directly from the request
String result = request.getPathInfo();
if (result == null) {
result = request.getServletPath();
}
if ((result == null) || (result.equals(""))) {
result = "/";
}
return result;
}
/**
* Initialize this servlet.
*/
public void init()
throws ServletException {
super.init();
String value = null;
try {
value = getServletConfig().getInitParameter("secret");
if (value != null)
secret = value;
} catch (Throwable t) {
;
}
try {
documentBuilder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch(ParserConfigurationException e) {
throw new ServletException
(sm.getString("webdavservlet.jaxpfailed"));
}
}
// ------------------------------------------------------ Protected Methods
/**
* Handles the special WebDAV methods.
*/
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (debug > 0) {
String path = getRelativePath(req);
System.out.println("[" + method + "] " + path);
}
if (method.equals(METHOD_PROPFIND)) {
doPropfind(req, resp);
} else if (method.equals(METHOD_PROPPATCH)) {
doProppatch(req, resp);
} else if (method.equals(METHOD_MKCOL)) {
doMkcol(req, resp);
} else if (method.equals(METHOD_COPY)) {
doCopy(req, resp);
} else if (method.equals(METHOD_MOVE)) {
doMove(req, resp);
} else if (method.equals(METHOD_LOCK)) {
doLock(req, resp);
} else if (method.equals(METHOD_UNLOCK)) {
doUnlock(req, resp);
} else {
// DefaultServlet processing
super.service(req, resp);
}
}
/**
* Check if the conditions specified in the optional If headers are
* satisfied.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param resourceInfo File object
* @return boolean true if the resource meets all the specified conditions,
* and false if any of the conditions is not satisfied, in which case
* request processing is stopped
*/
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,
ResourceInfo resourceInfo)
throws IOException {
if (!super.checkIfHeaders(request, response, resourceInfo))
return false;
// TODO : Checking the WebDAV If header
return true;
}
/**
* OPTIONS Method.
*/
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String path = getRelativePath(req);
resp.addHeader("DAV", "1,2");
String methodsAllowed = null;
Resources resources = getResources();
if (!resources.exists(path)) {
methodsAllowed = "OPTIONS, MKCOL, PUT, LOCK";
resp.addHeader("Allow", methodsAllowed);
return;
}
methodsAllowed = "OPTIONS, GET, HEAD, POST, DELETE, TRACE, "
+ "PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK";
if (!resources.isCollection(path)) {
methodsAllowed += ", PUT";
}
resp.addHeader("Allow", methodsAllowed);
}
/**
* PROPFIND Method.
*/
protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (!listings) {
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
return;
}
String path = getRelativePath(req);
// Properties which are to be displayed.
Vector properties = null;
// Propfind depth
int depth = INFINITY;
// Propfind type
int type = FIND_ALL_PROP;
String depthStr = req.getHeader("Depth");
if (depthStr == null) {
depth = INFINITY;
} else {
if (depthStr.equals("0")) {
depth = 0;
} else if (depthStr.equals("1")) {
depth = 1;
} else if (depthStr.equals("infinity")) {
depth = INFINITY;
}
}
Node propNode = null;
try {
Document document = documentBuilder.parse
(new InputSource(req.getInputStream()));
// Get the root element of the document
Element rootElement = document.getDocumentElement();
NodeList childList = rootElement.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
if (currentNode.getNodeName().endsWith("prop")) {
type = FIND_BY_PROPERTY;
propNode = currentNode;
}
if (currentNode.getNodeName().endsWith("propname")) {
type = FIND_PROPERTY_NAMES;
}
if (currentNode.getNodeName().endsWith("allprop")) {
type = FIND_ALL_PROP;
}
break;
}
}
} catch(Exception e) {
// Most likely there was no content : we use the defaults.
// TODO : Enhance that !
}
if (type == FIND_BY_PROPERTY) {
properties = new Vector();
NodeList childList = propNode.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
String nodeName = currentNode.getNodeName();
String propertyName = null;
if (nodeName.indexOf(':') != -1) {
propertyName = nodeName.substring
(nodeName.indexOf(':') + 1);
} else {
propertyName = nodeName;
}
// href is a live property which is handled differently
properties.addElement(propertyName);
break;
}
}
}
Resources resources = getResources();
if (!resources.exists(path)) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
return;
}
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
// Create multistatus object
XMLWriter generatedXML = new XMLWriter();
generatedXML.writeElement(null, "multistatus"
+ generateNamespaceDeclarations(),
XMLWriter.OPENING);
if (depth == 0) {
parseProperties(req, resources, generatedXML, path, type,
properties);
} else {
// The stack always contains the object of the current level
Stack stack = new Stack();
stack.push(path);
// Stack of the objects one level below
Stack stackBelow = new Stack();
while ((!stack.isEmpty()) && (depth >= 0)) {
String currentPath = (String) stack.pop();
parseProperties(req, resources, generatedXML, currentPath,
type, properties);
if (resources.isCollection(currentPath)) {
String[] children =
resources.getCollectionMembers(currentPath);
for (int i=0; i<children.length; i++) {
stackBelow.push(children[i]);
}
if (depth > 0) {
// Displaying the lock-null resources present in that
// collection
Vector currentLockNullResources =
(Vector) lockNullResources.get(currentPath);
if (currentLockNullResources != null) {
Enumeration lockNullResourcesList =
currentLockNullResources.elements();
while (lockNullResourcesList.hasMoreElements()) {
String lockNullPath = (String)
lockNullResourcesList.nextElement();
parseLockNullProperties
(req, generatedXML, currentPath, type,
properties);
}
}
}
}
if (stack.isEmpty()) {
depth--;
stack = stackBelow;
stackBelow = new Stack();
}
}
}
generatedXML.writeElement(null, "multistatus",
XMLWriter.CLOSING);
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.flush();
}
/**
* PROPPATCH Method.
*/
protected void doProppatch(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
}
/**
* MKCOL Method.
*/
protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
String path = getRelativePath(req);
Resources resources = getResources();
// Can't create a collection if a resource already exists at the given
// path
if (resources.exists(path)) {
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
return;
}
boolean result = resources.createCollection(path);
if (!result) {
resp.sendError(WebdavStatus.SC_CONFLICT,
WebdavStatus.getStatusText
(WebdavStatus.SC_CONFLICT));
} else {
resp.setStatus(WebdavStatus.SC_CREATED);
// Removing any lock-null resource which would be present
lockNullResources.remove(path);
}
}
/**
* DELETE Method.
*/
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
deleteResource(req, resp);
}
/**
* Process a POST request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
super.doPut(req, resp);
String path = getRelativePath(req);
// Removing any lock-null resource which would be present
lockNullResources.remove(path);
}
/**
* COPY Method.
*/
protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
copyResource(req, resp);
}
/**
* MOVE Method.
*/
protected void doMove(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
String path = getRelativePath(req);
if (copyResource(req, resp)) {
deleteResource(path, req, resp);
}
}
/**
* LOCK Method.
*/
protected void doLock(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
LockInfo lock = new LockInfo();
// Parsing lock request
// Parsing depth header
String depthStr = req.getHeader("Depth");
if (depthStr == null) {
lock.depth = INFINITY;
} else {
if (depthStr.equals("0")) {
lock.depth = 0;
} else {
lock.depth = INFINITY;
}
}
// Parsing timeout header
int lockDuration = DEFAULT_TIMEOUT;
String lockDurationStr = req.getHeader("Timeout");
if (lockDurationStr == null) {
lockDuration = DEFAULT_TIMEOUT;
} else {
if (lockDurationStr.startsWith("Second-")) {
lockDuration =
(new Integer(lockDurationStr.substring(7))).intValue();
} else {
if (lockDurationStr.equalsIgnoreCase("infinity")) {
lockDuration = MAX_TIMEOUT;
} else {
try {
lockDuration =
(new Integer(lockDurationStr)).intValue();
} catch (NumberFormatException e) {
lockDuration = MAX_TIMEOUT;
}
}
}
if (lockDuration == 0) {
lockDuration = DEFAULT_TIMEOUT;
}
if (lockDuration > MAX_TIMEOUT) {
lockDuration = MAX_TIMEOUT;
}
}
lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
int lockRequestType = LOCK_CREATION;
Node lockInfoNode = null;
try {
Document document = documentBuilder.parse(new InputSource
(req.getInputStream()));
// Get the root element of the document
Element rootElement = document.getDocumentElement();
lockInfoNode = rootElement;
} catch(Exception e) {
lockRequestType = LOCK_REFRESH;
}
if (lockInfoNode != null) {
// Reading lock information
NodeList childList = lockInfoNode.getChildNodes();
StringWriter strWriter = null;
DOMWriter domWriter = null;
Node lockScopeNode = null;
Node lockTypeNode = null;
Node lockOwnerNode = null;
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
String nodeName = currentNode.getNodeName();
if (nodeName.endsWith("lockscope")) {
lockScopeNode = currentNode;
}
if (nodeName.endsWith("locktype")) {
lockTypeNode = currentNode;
}
if (nodeName.endsWith("owner")) {
lockOwnerNode = currentNode;
}
break;
}
}
if (lockScopeNode != null) {
childList = lockScopeNode.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
String tempScope = currentNode.getNodeName();
if (tempScope.indexOf(':') != -1) {
lock.scope =
tempScope.substring(tempScope.indexOf(':'));
} else {
lock.scope = tempScope;
}
break;
}
}
if (lock.scope == null) {
// Bad request
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
}
} else {
// Bad request
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
}
if (lockTypeNode != null) {
childList = lockTypeNode.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
String tempType = currentNode.getNodeName();
if (tempType.indexOf(':') != -1) {
lock.type =
tempType.substring(tempType.indexOf(':') + 1);
} else {
lock.type = tempType;
}
break;
}
}
if (lock.type == null) {
// Bad request
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
}
} else {
// Bad request
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
}
if (lockOwnerNode != null) {
childList = lockOwnerNode.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
lock.owner += currentNode.getNodeValue();
break;
case Node.ELEMENT_NODE:
strWriter = new StringWriter();
domWriter = new DOMWriter(strWriter, true);
domWriter.print(currentNode);
lock.owner += strWriter.toString();
break;
}
}
if (lock.owner == null) {
// Bad request
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
}
} else {
lock.owner = new String();
}
}
String path = getRelativePath(req);
lock.path = path;
Resources resources = getResources();
Enumeration locksList = null;
if (lockRequestType == LOCK_CREATION) {
// Generating lock id
String lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
+ lock.scope + "-" + req.getUserPrincipal() + "-"
+ lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
+ lock.expiresAt + "-" + System.currentTimeMillis() + "-"
+ secret;
String lockToken =
md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));
if ( (resources.exists(path)) && (resources.isCollection(path)) &&
(lock.depth == INFINITY) ) {
// Locking a collection (and all its member resources)
// Checking if a child resource of this collection is
// already locked
Vector lockPaths = new Vector();
locksList = collectionLocks.elements();
while (locksList.hasMoreElements()) {
LockInfo currentLock = (LockInfo) locksList.nextElement();
if (currentLock.hasExpired()) {
resourceLocks.remove(currentLock.path);
continue;
}
if ( (currentLock.path.startsWith(lock.path)) &&
((currentLock.isExclusive()) ||
(lock.isExclusive())) ) {
// A child collection of this collection is locked
lockPaths.addElement(currentLock.path);
}
}
locksList = resourceLocks.elements();
while (locksList.hasMoreElements()) {
LockInfo currentLock = (LockInfo) locksList.nextElement();
if (currentLock.hasExpired()) {
resourceLocks.remove(currentLock.path);
continue;
}
if ( (currentLock.path.startsWith(lock.path)) &&
((currentLock.isExclusive()) ||
(lock.isExclusive())) ) {
// A child resource of this collection is locked
lockPaths.addElement(currentLock.path);
}
}
if (!lockPaths.isEmpty()) {
// One of the child paths was locked
// We generate a multistatus error report
Enumeration lockPathsList = lockPaths.elements();
resp.setStatus(WebdavStatus.SC_CONFLICT);
XMLWriter generatedXML = new XMLWriter();
generatedXML.writeXMLHeader();
generatedXML.writeElement
(null, "multistatus" + generateNamespaceDeclarations(),
XMLWriter.OPENING);
while (lockPathsList.hasMoreElements()) {
generatedXML.writeElement(null, "response",
XMLWriter.OPENING);
generatedXML.writeElement(null, "href",
XMLWriter.OPENING);
generatedXML
.writeText((String) lockPathsList.nextElement());
generatedXML.writeElement(null, "href",
XMLWriter.CLOSING);
generatedXML.writeElement(null, "status",
XMLWriter.OPENING);
generatedXML
.writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
+ " " + WebdavStatus
.getStatusText(WebdavStatus.SC_LOCKED));
generatedXML.writeElement(null, "status",
XMLWriter.CLOSING);
generatedXML.writeElement(null, "response",
XMLWriter.CLOSING);
}
generatedXML.writeElement(null, "multistatus",
XMLWriter.CLOSING);
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.close();
return;
}
boolean addLock = true;
// Checking if there is already a shared lock on this path
locksList = collectionLocks.elements();
while (locksList.hasMoreElements()) {
LockInfo currentLock = (LockInfo) locksList.nextElement();
if (currentLock.path.equals(lock.path)) {
if (currentLock.isExclusive()) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
} else {
if (lock.isExclusive()) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
}
currentLock.tokens.addElement(lockToken);
lock = currentLock;
addLock = false;
}
}
if (addLock) {
lock.tokens.addElement(lockToken);
collectionLocks.addElement(lock);
}
} else {
// Locking a single resource
// Retrieving an already existing lock on that resource
LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path);
if (presentLock != null) {
if ( (presentLock.isExclusive()) || (lock.isExclusive()) ) {
// If either lock is exclusive, the lock can't be
// granted
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
return;
} else {
presentLock.tokens.addElement(lockToken);
lock = presentLock;
}
} else {
lock.tokens.addElement(lockToken);
resourceLocks.put(lock.path, lock);
// Checking if a resource exists at this path
if (!resources.exists(lock.path)) {
// "Creating" a lock-null resource
int slash = lock.path.lastIndexOf('/');
String parentPath = lock.path.substring(0, slash);
Vector lockNulls =
(Vector) lockNullResources.get(parentPath);
if (lockNulls == null) {
lockNulls = new Vector();
lockNullResources.put(parentPath, lockNulls);
}
lockNulls.addElement(lock.path);
}
}
}
}
if (lockRequestType == LOCK_REFRESH) {
String ifHeader = req.getHeader("If");
if (ifHeader == null)
ifHeader = "";
// Checking resource locks
LockInfo toRenew = (LockInfo) resourceLocks.get(path);
Enumeration tokenList = null;
if (lock != null) {
// At least one of the tokens of the locks must have been given
tokenList = toRenew.tokens.elements();
while (tokenList.hasMoreElements()) {
String token = (String) tokenList.nextElement();
if (ifHeader.indexOf(token) != -1) {
toRenew.expiresAt = lock.expiresAt;
lock = toRenew;
}
}
}
// Checking inheritable collection locks
Enumeration collectionLocksList = collectionLocks.elements();
while (collectionLocksList.hasMoreElements()) {
toRenew = (LockInfo) collectionLocksList.nextElement();
if (path.equals(toRenew.path)) {
tokenList = toRenew.tokens.elements();
while (tokenList.hasMoreElements()) {
String token = (String) tokenList.nextElement();
if (ifHeader.indexOf(token) != -1) {
toRenew.expiresAt = lock.expiresAt;
lock = toRenew;
}
}
}
}
}
// Set the status, then generate the XML response containing
// the lock information
XMLWriter generatedXML = new XMLWriter();
generatedXML.writeXMLHeader();
generatedXML.writeElement(null, "prop"
+ generateNamespaceDeclarations(),
XMLWriter.OPENING);
generatedXML.writeElement(null, "lockdiscovery",
XMLWriter.OPENING);
lock.toXML(generatedXML, true);
generatedXML.writeElement(null, "lockdiscovery",
XMLWriter.CLOSING);
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
resp.setStatus(WebdavStatus.SC_OK);
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.close();
}
/**
* UNLOCK Method.
*/
protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
if (isLocked(req)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return;
}
String path = getRelativePath(req);
String lockTokenHeader = req.getHeader("Lock-Token");
if (lockTokenHeader == null)
lockTokenHeader = "";
// Checking resource locks
LockInfo lock = (LockInfo) resourceLocks.get(path);
Enumeration tokenList = null;
if (lock != null) {
// At least one of the tokens of the locks must have been given
tokenList = lock.tokens.elements();
while (tokenList.hasMoreElements()) {
String token = (String) tokenList.nextElement();
if (lockTokenHeader.indexOf(token) != -1) {
lock.tokens.removeElement(token);
}
}
if (lock.tokens.isEmpty()) {
resourceLocks.remove(path);
// Removing any lock-null resource which would be present
lockNullResources.remove(path);
}
}
// Checking inheritable collection locks
Enumeration collectionLocksList = collectionLocks.elements();
while (collectionLocksList.hasMoreElements()) {
lock = (LockInfo) collectionLocksList.nextElement();
if (path.equals(lock.path)) {
tokenList = lock.tokens.elements();
while (tokenList.hasMoreElements()) {
String token = (String) tokenList.nextElement();
if (lockTokenHeader.indexOf(token) != -1) {
lock.tokens.removeElement(token);
break;
}
}
if (lock.tokens.isEmpty()) {
collectionLocks.removeElement(lock);
// Removing any lock-null resource which would be present
lockNullResources.remove(path);
}
}
}
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
}
// -------------------------------------------------------- Private Methods
/**
* Generate the namespace declarations.
*/
private String generateNamespaceDeclarations() {
return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
}
/**
* Check to see if a resource is currently write locked. The method
* will look at the "If" header to make sure the client
* has give the appropriate lock tokens.
*
* @param req Servlet request
* @return boolean true if the resource is locked (and no appropriate
* lock token has been found for at least one of the non-shared locks which
* are present on the resource).
*/
private boolean isLocked(HttpServletRequest req) {
String path = getRelativePath(req);
String ifHeader = req.getHeader("If");
if (ifHeader == null)
ifHeader = "";
String lockTokenHeader = req.getHeader("Lock-Token");
if (lockTokenHeader == null)
lockTokenHeader = "";
return isLocked(path, ifHeader + lockTokenHeader);
}
/**
* Check to see if a resource is currently write locked.
*
* @param path Path of the resource
* @param ifHeader "If" HTTP header which was included in the request
* @return boolean true if the resource is locked (and no appropriate
* lock token has been found for at least one of the non-shared locks which
* are present on the resource).
*/
private boolean isLocked(String path, String ifHeader) {
// Checking resource locks
LockInfo lock = (LockInfo) resourceLocks.get(path);
Enumeration tokenList = null;
if ((lock != null) && (lock.hasExpired())) {
resourceLocks.remove(path);
} else if (lock != null) {
// At least one of the tokens of the locks must have been given
tokenList = lock.tokens.elements();
boolean tokenMatch = false;
while (tokenList.hasMoreElements()) {
String token = (String) tokenList.nextElement();
if (ifHeader.indexOf(token) != -1)
tokenMatch = true;
}
if (!tokenMatch)
return true;
}
// Checking inheritable collection locks
Enumeration collectionLocksList = collectionLocks.elements();
while (collectionLocksList.hasMoreElements()) {
lock = (LockInfo) collectionLocksList.nextElement();
if (lock.hasExpired()) {
collectionLocks.removeElement(lock);
} else if (path.startsWith(lock.path)) {
tokenList = lock.tokens.elements();
boolean tokenMatch = false;
while (tokenList.hasMoreElements()) {
String token = (String) tokenList.nextElement();
if (ifHeader.indexOf(token) != -1)
tokenMatch = true;
}
if (!tokenMatch)
return true;
}
}
return false;
}
/**
* Copy a resource.
*
* @param req Servlet request
* @param resp Servlet response
* @return boolean true if the copy is successful
*/
private boolean copyResource(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
// Parsing destination header
String destinationPath = req.getHeader("Destination");
if (destinationPath.startsWith("http://")) {
destinationPath = destinationPath.substring("http://".length());
}
String hostName = req.getServerName();
if ((hostName != null) && (destinationPath.startsWith(hostName))) {
destinationPath = destinationPath.substring(hostName.length());
}
if (destinationPath.startsWith(":")) {
int firstSeparator = destinationPath.indexOf("/");
if (firstSeparator < 0) {
destinationPath = "/";
} else {
destinationPath = destinationPath.substring(firstSeparator);
}
}
String contextPath = req.getContextPath();
if ((contextPath != null) &&
(destinationPath.startsWith(contextPath))) {
destinationPath = destinationPath.substring(contextPath.length());
}
String pathInfo = req.getPathInfo();
if (pathInfo != null) {
String servletPath = req.getServletPath();
if ((servletPath != null) &&
(destinationPath.startsWith(servletPath))) {
destinationPath = destinationPath
.substring(servletPath.length());
}
}
if (debug > 0)
System.out.println("Dest path :" + destinationPath);
String path = getRelativePath(req);
if (destinationPath.equals(path)) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return false;
}
// Parsing overwrite header
boolean overwrite = true;
String overwriteHeader = req.getHeader("Overwrite");
if (overwriteHeader != null) {
if (overwriteHeader.equalsIgnoreCase("T")) {
overwrite = true;
} else {
overwrite = false;
}
}
// Overwriting the destination
Resources resources = getResources();
if (overwrite) {
// Delete destination resource, if it exists
if (resources.exists(destinationPath)) {
if (!deleteResource(destinationPath, req, resp)) {
return false;
} else {
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
}
} else {
resp.setStatus(WebdavStatus.SC_CREATED);
}
} else {
// If the destination exists, then it's a conflict
if (resources.exists(destinationPath)) {
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
return false;
}
}
// Copying source to destination
Hashtable errorList = new Hashtable();
boolean result = copyResource(resources, errorList,
path, destinationPath);
if ((!result) || (!errorList.isEmpty())) {
sendReport(req, resp, errorList);
return false;
}
// Removing any lock-null resource which would be present at
// the destination path
lockNullResources.remove(destinationPath);
return true;
}
/**
* Copy a collection.
*
* @param resources Resources implementation to be used
* @param errorList Hashtable containing the list of errors which occured
* during the copy operation
* @param source Path of the resource to be copied
* @param dest Destination path
*/
private boolean copyResource(Resources resources, Hashtable errorList,
String source, String dest) {
if (resources.isCollection(source)) {
if (!resources.createCollection(dest)) {
errorList.put
(dest,
new Integer(WebdavStatus.SC_CONFLICT));
return false;
}
String[] members = resources.getCollectionMembers(source);
for (int i=0; i<members.length; i++) {
String childDest = dest +
members[i].substring(source.length());
copyResource(resources, errorList, members[i], childDest);
}
} else {
InputStream is = resources.getResourceAsStream(source);
if (!resources.setResource(dest, is)) {
errorList.put
(source,
new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
return false;
}
}
return true;
}
/**
* Delete a resource.
*
* @param req Servlet request
* @param resp Servlet response
* @return boolean true if the copy is successful
*/
private boolean deleteResource(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
String path = getRelativePath(req);
return deleteResource(path, req, resp);
}
/**
* Delete a resource.
*
* @param path Path of the resource which is to be deleted
* @param req Servlet request
* @param resp Servlet response
*/
private boolean deleteResource(String path, HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
String ifHeader = req.getHeader("If");
if (ifHeader == null)
ifHeader = "";
String lockTokenHeader = req.getHeader("Lock-Token");
if (lockTokenHeader == null)
lockTokenHeader = "";
if (isLocked(path, ifHeader + lockTokenHeader)) {
resp.sendError(WebdavStatus.SC_LOCKED);
return false;
}
Resources resources = getResources();
if (!resources.exists(path)) {
resp.sendError(WebdavStatus.SC_NOT_FOUND);
return false;
}
boolean collection = resources.isCollection(path);
if (!collection) {
if (!resources.deleteResource(path)) {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
return false;
}
} else {
Hashtable errorList = new Hashtable();
deleteCollection(req, resources, path, errorList);
resources.deleteResource(path);
if (!errorList.isEmpty()) {
sendReport(req, resp, errorList);
return false;
}
}
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
return true;
}
/**
* Deletes a collection.
*
* @param resources Resources implementation associated with the context
* @param path Path to the collection to be deleted
* @param errorList Contains the list of the errors which occured
*/
private void deleteCollection(HttpServletRequest req, Resources resources,
String path, Hashtable errorList) {
String ifHeader = req.getHeader("If");
if (ifHeader == null)
ifHeader = "";
String lockTokenHeader = req.getHeader("Lock-Token");
if (lockTokenHeader == null)
lockTokenHeader = "";
String[] members = resources.getCollectionMembers(path);
for (int i=0; i<members.length; i++) {
if (isLocked(members[i], ifHeader + lockTokenHeader)) {
errorList.put(members[i], new Integer(WebdavStatus.SC_LOCKED));
} else {
if (resources.isCollection(members[i])) {
deleteCollection(req, resources, members[i], errorList);
}
boolean result = resources.deleteResource(members[i]);
if (!result) {
if (!resources.isCollection(members[i])) {
// If it's not a collection, then it's an unknown
// error
errorList.put
(members[i], new Integer
(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
}
}
}
}
}
/**
* Send a multistatus element containing a complete error report to the
* client.
*
* @param req Servlet request
* @param resp Servlet response
* @param errorList List of error to be displayed
*/
private void sendReport(HttpServletRequest req, HttpServletResponse resp,
Hashtable errorList)
throws ServletException, IOException {
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
String absoluteUri = req.getRequestURI();
String relativePath = getRelativePath(req);
XMLWriter generatedXML = new XMLWriter();
generatedXML.writeXMLHeader();
generatedXML.writeElement(null, "multistatus"
+ generateNamespaceDeclarations(),
XMLWriter.OPENING);
Enumeration pathList = errorList.keys();
while (pathList.hasMoreElements()) {
String errorPath = (String) pathList.nextElement();
int errorCode = ((Integer) errorList.get(errorPath)).intValue();
generatedXML.writeElement(null, "response", XMLWriter.OPENING);
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
String toAppend = errorPath.substring(relativePath.length());
if (!toAppend.startsWith("/"))
toAppend = "/" + toAppend;
generatedXML.writeText(absoluteUri + toAppend);
generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
generatedXML.writeElement(null, "status", XMLWriter.OPENING);
generatedXML
.writeText("HTTP/1.1 " + errorCode + " "
+ WebdavStatus.getStatusText(errorCode));
generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
}
generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.close();
}
/**
* Propfind helper method.
*
* @param resources Resources object associated with this context
* @param generatedXML XML response to the Propfind request
* @param path Path of the current resource
* @param type Propfind type
* @param propertiesVector If the propfind type is find properties by
* name, then this Vector contains those properties
*/
private void parseProperties(HttpServletRequest req,
Resources resources, XMLWriter generatedXML,
String path, int type,
Vector propertiesVector) {
// Exclude any resource in the /WEB-INF and /META-INF subdirectories
// (the "toUpperCase()" avoids problems on Windows systems)
if (path.toUpperCase().startsWith("/WEB-INF") ||
path.toUpperCase().startsWith("/META-INF"))
return;
ResourceInfo resourceInfo = new ResourceInfo(path, resources);
generatedXML.writeElement(null, "response", XMLWriter.OPENING);
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
+ WebdavStatus.getStatusText
(WebdavStatus.SC_OK));
// Generating href element
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
String absoluteUri = req.getRequestURI();
String relativePath = getRelativePath(req);
String toAppend = path.substring(relativePath.length());
if ((!toAppend.startsWith("/")) && (!absoluteUri.endsWith("/")))
toAppend = "/" + toAppend;
generatedXML.writeText(absoluteUri + toAppend);
generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
switch (type) {
case FIND_ALL_PROP :
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
generatedXML.writeProperty(null, "creationdate",
formats[0].format(new Date
(resourceInfo.creationDate)));
generatedXML.writeProperty(null, "displayname", resourceInfo.path);
generatedXML.writeProperty(null, "getcontentlanguage",
Locale.getDefault().toString());
generatedXML.writeProperty(null, "getlastmodified",
resourceInfo.httpDate);
if (!resourceInfo.collection) {
generatedXML.writeProperty
(null, "getcontentlength",
String.valueOf(resourceInfo.length));
generatedXML.writeProperty
(null, "getcontenttype",
getServletContext().getMimeType(resourceInfo.path));
generatedXML.writeProperty(null, "getetag",
getETagValue(resourceInfo, true));
generatedXML.writeElement(null, "resourcetype",
XMLWriter.NO_CONTENT);
} else {
generatedXML.writeElement(null, "resourcetype",
XMLWriter.OPENING);
generatedXML.writeElement(null, "collection",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "resourcetype",
XMLWriter.CLOSING);
}
generatedXML.writeProperty(null, "source", "");
String supportedLocks = "<lockentry>"
+ "<lockscope><exclusive/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>" + "<lockentry>"
+ "<lockscope><shared/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>";
generatedXML.writeProperty(null, "supportedlock", supportedLocks);
generateLockDiscovery(path, generatedXML);
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
break;
case FIND_PROPERTY_NAMES :
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
generatedXML.writeElement(null, "creationdate",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "displayname",
XMLWriter.NO_CONTENT);
if (!resourceInfo.collection) {
generatedXML.writeElement(null, "getcontentlanguage",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getcontentlength",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getcontenttype",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getetag",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getlastmodified",
XMLWriter.NO_CONTENT);
}
generatedXML.writeElement(null, "resourcetype",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "lockdiscovery",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
break;
case FIND_BY_PROPERTY :
Vector propertiesNotFound = new Vector();
// Parse the list of properties
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
Enumeration properties = propertiesVector.elements();
while (properties.hasMoreElements()) {
String property = (String) properties.nextElement();
if (property.equals("creationdate")) {
generatedXML.writeProperty
(null, "creationdate", formats[0].format
(new Date(resourceInfo.creationDate)));
} else if (property.equals("displayname")) {
generatedXML.writeProperty(null, "displayname",
resourceInfo.path);
} else if (property.equals("getcontentlanguage")) {
if (resourceInfo.collection) {
propertiesNotFound.addElement(property);
} else {
generatedXML.writeProperty
(null, "getcontentlanguage",
Locale.getDefault().toString());
}
} else if (property.equals("getcontentlength")) {
if (resourceInfo.collection) {
propertiesNotFound.addElement(property);
} else {
generatedXML.writeProperty
(null, "getcontentlength",
(String.valueOf(resourceInfo.length)));
}
} else if (property.equals("getcontenttype")) {
if (resourceInfo.collection) {
propertiesNotFound.addElement(property);
} else {
generatedXML.writeProperty
(null, "getcontenttype",
getServletContext().getMimeType
(resourceInfo.path));
}
} else if (property.equals("getetag")) {
if (resourceInfo.collection) {
propertiesNotFound.addElement(property);
} else {
generatedXML.writeProperty
(null, "getetag",
getETagValue(resourceInfo, true));
}
} else if (property.equals("getlastmodified")) {
if (resourceInfo.collection) {
propertiesNotFound.addElement(property);
} else {
generatedXML.writeProperty(null,
"getlastmodified",
resourceInfo.httpDate);
}
} else if (property.equals("resourcetype")) {
if (resourceInfo.collection) {
generatedXML.writeElement(null, "resourcetype",
XMLWriter.OPENING);
generatedXML.writeElement(null, "collection",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "resourcetype",
XMLWriter.CLOSING);
} else {
generatedXML.writeElement(null, "resourcetype",
XMLWriter.NO_CONTENT);
}
} else if (property.equals("source")) {
generatedXML.writeProperty(null, "source", "");
} else if (property.equals("supportedlock")) {
supportedLocks = "<lockentry>"
+ "<lockscope><exclusive/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>" + "<lockentry>"
+ "<lockscope><shared/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>";
generatedXML.writeProperty(null, "supportedlock",
supportedLocks);
} else if (property.equals("lockdiscovery")) {
generateLockDiscovery(path, generatedXML);
} else {
propertiesNotFound.addElement(property);
}
}
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
Enumeration propertiesNotFoundList = propertiesNotFound.elements();
if (propertiesNotFoundList.hasMoreElements()) {
status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+ " " + WebdavStatus.getStatusText
(WebdavStatus.SC_NOT_FOUND));
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
while (propertiesNotFoundList.hasMoreElements()) {
generatedXML.writeElement
(null, (String) propertiesNotFoundList.nextElement(),
XMLWriter.NO_CONTENT);
}
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
}
break;
}
generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
}
/**
* Propfind helper method. Dispays the properties of a lock-null resource.
*
* @param resources Resources object associated with this context
* @param generatedXML XML response to the Propfind request
* @param path Path of the current resource
* @param type Propfind type
* @param propertiesVector If the propfind type is find properties by
* name, then this Vector contains those properties
*/
private void parseLockNullProperties(HttpServletRequest req,
XMLWriter generatedXML,
String path, int type,
Vector propertiesVector) {
// Exclude any resource in the /WEB-INF and /META-INF subdirectories
// (the "toUpperCase()" avoids problems on Windows systems)
if (path.toUpperCase().startsWith("/WEB-INF") ||
path.toUpperCase().startsWith("/META-INF"))
return;
// Retrieving the lock associated with the lock-null resource
LockInfo lock = (LockInfo) resourceLocks.get(path);
if (lock == null)
return;
generatedXML.writeElement(null, "response", XMLWriter.OPENING);
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
+ WebdavStatus.getStatusText
(WebdavStatus.SC_OK));
// Generating href element
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
String absoluteUri = req.getRequestURI();
String relativePath = getRelativePath(req);
String toAppend = path.substring(relativePath.length());
if (!toAppend.startsWith("/"))
toAppend = "/" + toAppend;
generatedXML.writeText(absoluteUri + toAppend);
generatedXML.writeElement("d", "href", XMLWriter.CLOSING);
switch (type) {
case FIND_ALL_PROP :
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
generatedXML.writeProperty(null, "creationdate",
formats[0].format(lock.creationDate));
generatedXML.writeProperty(null, "displayname", path);
generatedXML.writeProperty(null, "getcontentlanguage",
Locale.getDefault().toString());
generatedXML.writeProperty(null, "getlastmodified",
formats[0].format(lock.creationDate));
generatedXML.writeProperty
(null, "getcontentlength", String.valueOf(0));
generatedXML.writeProperty(null, "getcontenttype", "");
generatedXML.writeProperty(null, "getetag", "");
generatedXML.writeElement(null, "resourcetype",
XMLWriter.OPENING);
generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "resourcetype",
XMLWriter.CLOSING);
generatedXML.writeProperty(null, "source", "");
String supportedLocks = "<lockentry>"
+ "<lockscope><exclusive/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>" + "<lockentry>"
+ "<lockscope><shared/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>";
generatedXML.writeProperty(null, "supportedlock",
supportedLocks);
generateLockDiscovery(path, generatedXML);
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
break;
case FIND_PROPERTY_NAMES :
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
generatedXML.writeElement(null, "creationdate",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "displayname",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getcontentlanguage",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getcontentlength",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getcontenttype",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getetag",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "getlastmodified",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "resourcetype",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "source",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "lockdiscovery",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
break;
case FIND_BY_PROPERTY :
Vector propertiesNotFound = new Vector();
// Parse the list of properties
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
Enumeration properties = propertiesVector.elements();
while (properties.hasMoreElements()) {
String property = (String) properties.nextElement();
if (property.equals("creationdate")) {
generatedXML.writeProperty
(null, "creationdate",
formats[0].format(lock.creationDate));
} else if (property.equals("displayname")) {
generatedXML.writeProperty(null, "displayname", path);
} else if (property.equals("getcontentlanguage")) {
generatedXML.writeProperty
(null, "getcontentlanguage",
Locale.getDefault().toString());
} else if (property.equals("getcontentlength")) {
generatedXML.writeProperty
(null, "getcontentlength", (String.valueOf(0)));
} else if (property.equals("getcontenttype")) {
generatedXML.writeProperty
(null, "getcontenttype", "");
} else if (property.equals("getetag")) {
generatedXML.writeProperty(null, "getetag", "");
} else if (property.equals("getlastmodified")) {
generatedXML.writeProperty
(null, "getlastmodified",
formats[0].format(lock.creationDate));
} else if (property.equals("resourcetype")) {
generatedXML.writeElement(null, "resourcetype",
XMLWriter.OPENING);
generatedXML.writeElement(null, "lock-null",
XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "resourcetype",
XMLWriter.CLOSING);
} else if (property.equals("source")) {
generatedXML.writeProperty(null, "source", "");
} else if (property.equals("supportedlock")) {
supportedLocks = "<lockentry>"
+ "<lockscope><exclusive/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>" + "<lockentry>"
+ "<lockscope><shared/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>";
generatedXML.writeProperty(null, "supportedlock",
supportedLocks);
} else if (property.equals("lockdiscovery")) {
generateLockDiscovery(path, generatedXML);
} else {
propertiesNotFound.addElement(property);
}
}
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
Enumeration propertiesNotFoundList = propertiesNotFound.elements();
if (propertiesNotFoundList.hasMoreElements()) {
status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+ " " + WebdavStatus.getStatusText
(WebdavStatus.SC_NOT_FOUND));
generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
generatedXML.writeElement(null, "prop", XMLWriter.OPENING);
while (propertiesNotFoundList.hasMoreElements()) {
generatedXML.writeElement
(null, (String) propertiesNotFoundList.nextElement(),
XMLWriter.NO_CONTENT);
}
generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);
}
break;
}
generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
}
/**
* Print the lock discovery information associated with a path.
*
* @param path Path
* @param generatedXML XML data to which the locks info will be appended
*/
private void generateLockDiscovery(String path, XMLWriter generatedXML) {
generatedXML.writeElement(null, "lockdiscovery",
XMLWriter.OPENING);
LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
if (resourceLock != null) {
resourceLock.toXML(generatedXML);
}
Enumeration collectionLocksList = collectionLocks.elements();
while (collectionLocksList.hasMoreElements()) {
LockInfo currentLock = (LockInfo) collectionLocksList.nextElement();
if (path.startsWith(currentLock.path)) {
currentLock.toXML(generatedXML);
}
}
generatedXML.writeElement(null, "lockdiscovery",
XMLWriter.CLOSING);
}
// -------------------------------------------------- LockInfo Inner Class
/**
* Holds a lock information.
*/
private class LockInfo {
// --------------------------------------------------------- Constructor
/**
* Constructor.
*
* @param pathname Path name of the file
*/
public LockInfo() {
}
// -------------------------------------------------- Instance Variables
String path = "/";
String type = "write";
String scope = "exclusive";
int depth = 0;
String owner = "";
Vector tokens = new Vector();
long expiresAt = 0;
Date creationDate = new Date();
// ------------------------------------------------------ Public Methods
/**
* Get a String representation of this lock token.
*/
public String toString() {
String result = "Type:" + type + "\n";
result += "Scope:" + scope + "\n";
result += "Depth:" + depth + "\n";
result += "Owner:" + owner + "\n";
result += "Expiration:" +
formats[0].format(new Date(expiresAt)) + "\n";
Enumeration tokensList = tokens.elements();
while (tokensList.hasMoreElements()) {
result += "Token:" + tokensList.nextElement() + "\n";
}
return result;
}
/**
* Return true if the lock has expired.
*/
public boolean hasExpired() {
return (System.currentTimeMillis() > expiresAt);
}
/**
* Return true if the lock is exclusive.
*/
public boolean isExclusive() {
return (scope.equals("exclusive"));
}
/**
* Get an XML representation of this lock token. This method will
* append an XML fragment to the given XML writer.
*/
public void toXML(XMLWriter generatedXML) {
toXML(generatedXML, false);
}
/**
* Get an XML representation of this lock token. This method will
* append an XML fragment to the given XML writer.
*/
public void toXML(XMLWriter generatedXML, boolean showToken) {
generatedXML.writeElement(null, "activelock", XMLWriter.OPENING);
generatedXML.writeElement(null, "locktype", XMLWriter.OPENING);
generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "locktype", XMLWriter.CLOSING);
generatedXML.writeElement(null, "lockscope", XMLWriter.OPENING);
generatedXML.writeElement(null, scope, XMLWriter.NO_CONTENT);
generatedXML.writeElement(null, "lockscope", XMLWriter.CLOSING);
generatedXML.writeElement(null, "depth", XMLWriter.OPENING);
if (depth == INFINITY) {
generatedXML.writeText("Infinity");
} else {
generatedXML.writeText("0");
}
generatedXML.writeElement(null, "depth", XMLWriter.CLOSING);
generatedXML.writeElement(null, "owner", XMLWriter.OPENING);
generatedXML.writeText(owner);
generatedXML.writeElement(null, "owner", XMLWriter.CLOSING);
generatedXML.writeElement(null, "timeout", XMLWriter.OPENING);
long timeout = (expiresAt - System.currentTimeMillis()) / 1000;
generatedXML.writeText("Second-" + timeout);
generatedXML.writeElement(null, "timeout", XMLWriter.CLOSING);
generatedXML.writeElement(null, "locktoken", XMLWriter.OPENING);
if (showToken) {
Enumeration tokensList = tokens.elements();
while (tokensList.hasMoreElements()) {
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
generatedXML.writeText("opaquelocktoken:"
+ tokensList.nextElement());
generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
}
} else {
generatedXML.writeElement(null, "href", XMLWriter.OPENING);
generatedXML.writeText("opaquelocktoken:dummytoken");
generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
}
generatedXML.writeElement(null, "locktoken", XMLWriter.CLOSING);
generatedXML.writeElement(null, "activelock", XMLWriter.CLOSING);
}
}
// --------------------------------------------------- Property Inner Class
private class Property {
public String name;
public String value;
public String namespace;
public String namespaceAbbrev;
public int status = WebdavStatus.SC_OK;
}
};
// -------------------------------------------------------- WebdavStatus Class
/**
* Wraps the HttpServletResponse class to abstract the
* specific protocol used. To support other protocols
* we would only need to modify this class and the
* WebDavRetCode classes.
*
* @author Marc Eaddy
* @version 1.0, 16 Nov 1997
*/
class WebdavStatus {
// ----------------------------------------------------- Instance Variables
/**
* This Hashtable contains the mapping of HTTP and WebDAV
* status codes to descriptive text. This is a static
* variable.
*/
private static Hashtable mapStatusCodes = new Hashtable();
// ------------------------------------------------------ HTTP Status Codes
/**
* Status code (200) indicating the request succeeded normally.
*/
public static final int SC_OK = HttpServletResponse.SC_OK;
/**
* Status code (201) indicating the request succeeded and created
* a new resource on the server.
*/
public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
/**
* Status code (202) indicating that a request was accepted for
* processing, but was not completed.
*/
public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
/**
* Status code (204) indicating that the request succeeded but that
* there was no new information to return.
*/
public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
/**
* Status code (301) indicating that the resource has permanently
* moved to a new location, and that future references should use a
* new URI with their requests.
*/
public static final int SC_MOVED_PERMANENTLY =
HttpServletResponse.SC_MOVED_PERMANENTLY;
/**
* Status code (302) indicating that the resource has temporarily
* moved to another location, but that future references should
* still use the original URI to access the resource.
*/
public static final int SC_MOVED_TEMPORARILY =
HttpServletResponse.SC_MOVED_TEMPORARILY;
/**
* Status code (304) indicating that a conditional GET operation
* found that the resource was available and not modified.
*/
public static final int SC_NOT_MODIFIED =
HttpServletResponse.SC_NOT_MODIFIED;
/**
* Status code (400) indicating the request sent by the client was
* syntactically incorrect.
*/
public static final int SC_BAD_REQUEST =
HttpServletResponse.SC_BAD_REQUEST;
/**
* Status code (401) indicating that the request requires HTTP
* authentication.
*/
public static final int SC_UNAUTHORIZED =
HttpServletResponse.SC_UNAUTHORIZED;
/**
* Status code (403) indicating the server understood the request
* but refused to fulfill it.
*/
public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
/**
* Status code (404) indicating that the requested resource is not
* available.
*/
public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
/**
* Status code (500) indicating an error inside the HTTP service
* which prevented it from fulfilling the request.
*/
public static final int SC_INTERNAL_SERVER_ERROR =
HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
/**
* Status code (501) indicating the HTTP service does not support
* the functionality needed to fulfill the request.
*/
public static final int SC_NOT_IMPLEMENTED =
HttpServletResponse.SC_NOT_IMPLEMENTED;
/**
* Status code (502) indicating that the HTTP server received an
* invalid response from a server it consulted when acting as a
* proxy or gateway.
*/
public static final int SC_BAD_GATEWAY =
HttpServletResponse.SC_BAD_GATEWAY;
/**
* Status code (503) indicating that the HTTP service is
* temporarily overloaded, and unable to handle the request.
*/
public static final int SC_SERVICE_UNAVAILABLE =
HttpServletResponse.SC_SERVICE_UNAVAILABLE;
/**
* Status code (100) indicating the client may continue with
* its request. This interim response is used to inform the
* client that the initial part of the request has been
* received and has not yet been rejected by the server.
*/
public static final int SC_CONTINUE = 100;
/**
* Status code (405) indicating the method specified is not
* allowed for the resource.
*/
public static final int SC_METHOD_NOT_ALLOWED = 405;
/**
* Status code (409) indicating that the request could not be
* completed due to a conflict with the current state of the
* resource.
*/
public static final int SC_CONFLICT = 409;
/**
* Status code (412) indicating the precondition given in one
* or more of the request-header fields evaluated to false
* when it was tested on the server.
*/
public static final int SC_PRECONDITION_FAILED = 412;
/**
* Status code (413) indicating the server is refusing to
* process a request because the request entity is larger
* than the server is willing or able to process.
*/
public static final int SC_REQUEST_TOO_LONG = 413;
/**
* Status code (415) indicating the server is refusing to service
* the request because the entity of the request is in a format
* not supported by the requested resource for the requested
* method.
*/
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
// -------------------------------------------- Extended WebDav status code
/**
* Status code (207) indicating that the response requires
* providing status for multiple independent operations.
*/
public static final int SC_MULTI_STATUS = 207;
// This one colides with HTTP 1.1
// "207 Parital Update OK"
/**
* Status code (418) indicating the entity body submitted with
* the PATCH method was not understood by the resource.
*/
public static final int SC_UNPROCESSABLE_ENTITY = 418;
// This one colides with HTTP 1.1
// "418 Reauthentication Required"
/**
* Status code (419) indicating that the resource does not have
* sufficient space to record the state of the resource after the
* execution of this method.
*/
public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
// This one colides with HTTP 1.1
// "419 Proxy Reauthentication Required"
/**
* Status code (420) indicating the method was not executed on
* a particular resource within its scope because some part of
* the method's execution failed causing the entire method to be
* aborted.
*/
public static final int SC_METHOD_FAILURE = 420;
/**
* Status code (423) indicating the destination resource of a
* method is locked, and either the request did not contain a
* valid Lock-Info header, or the Lock-Info header identifies
* a lock held by another principal.
*/
public static final int SC_LOCKED = 423;
// ------------------------------------------------------------ Initializer
static {
// HTTP 1.0 tatus Code
addStatusCodeMap(SC_OK, "OK");
addStatusCodeMap(SC_CREATED, "Created");
addStatusCodeMap(SC_ACCEPTED, "Accepted");
addStatusCodeMap(SC_NO_CONTENT, "No Content");
addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
addStatusCodeMap(SC_NOT_FOUND, "Not Found");
addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
addStatusCodeMap(SC_CONTINUE, "Continue");
addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
addStatusCodeMap(SC_CONFLICT, "Conflict");
addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
// WebDav Status Codes
addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
"Insufficient Space On Resource");
addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
addStatusCodeMap(SC_LOCKED, "Locked");
}
// --------------------------------------------------------- Public Methods
/**
* Returns the HTTP status text for the HTTP or WebDav status code
* specified by looking it up in the static mapping. This is a
* static function.
*
* @param nHttpStatusCode [IN] HTTP or WebDAV status code
* @return A string with a short descriptive phrase for the
* HTTP status code (e.g., "OK").
*/
public static String getStatusText(int nHttpStatusCode) {
Integer intKey = new Integer(nHttpStatusCode);
if (!mapStatusCodes.containsKey(intKey)) {
return "";
} else {
return (String) mapStatusCodes.get(intKey);
}
}
// -------------------------------------------------------- Private Methods
/**
* Adds a new status code -> status text mapping. This is a static
* method because the mapping is a static variable.
*
* @param nKey [IN] HTTP or WebDAV status code
* @param strVal [IN] HTTP status text
*/
private static void addStatusCodeMap(int nKey, String strVal) {
mapStatusCodes.put(new Integer(nKey), strVal);
}
};
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/DirectoryBean.java
Index: DirectoryBean.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import org.apache.tomcat.webdav.util.StringManager;
/**
* Abstraction bean that represents the properties of a "resource" that is
* actually a "directory", in a fashion that independent of the actual
* underlying medium used to represent those entries. Convenient constructors
* are provided to populate our properties from common sources, but it is
* feasible to do everything with property setters if necessary.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that access to the
* set of resources associated with this directory are done in a thread safe
* manner. No internal synchronization is performed.
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:40 $
*/
public final class DirectoryBean extends ResourceBean {
// ----------------------------------------------------------- Constructors
/**
* Construct a new directory bean for the named resource, with default
* properties.
*
* @param name Normalized context-relative name of this resource
*/
public DirectoryBean(String name) {
super(name);
}
/**
* Construct a new directory bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param file File representing this resource entry
*/
public DirectoryBean(String name, File file) {
super(name, file);
}
/**
* Construct a new directory bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param entry JAR entry representing this resource entry
*/
public DirectoryBean(String name, JarEntry entry) {
super(name, entry);
}
// ----------------------------------------------------- Instance Variables
/**
* The system default Locale.
*/
private static Locale defaultLocale = Locale.getDefault();
/**
* The date format pattern for rendering last modified date and time.
*/
private static String pattern = "EEE, dd MMM yyyy HH:mm z";
/**
* The collection of resources for this directory, sorted by name.
*/
private TreeMap resources = new TreeMap();
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager("org.apache.tomcat.webdav.resources");
/**
* The date format for rendering last modified date and time.
*/
private static DateFormat timestamp =
new SimpleDateFormat(pattern, defaultLocale);
// --------------------------------------------------------- Public Methods
/**
* Add a new resource to this directory.
*
* @param entry ResourceBean for the resource to be added
*/
public void addResource(ResourceBean resource) {
resources.put(resource.getName(), resource);
}
/**
* Return the set of resources that belong to this directory,
* in alphabetical order based on their names.
*/
public ResourceBean[] findResources() {
ResourceBean results[] = new ResourceBean[resources.size()];
return ((ResourceBean[]) resources.values().toArray(results));
}
/**
* Remove an existing resource from this directory.
*
* @param entry ResourceBean for the resource to be removed
*/
public void removeResource(ResourceBean resource) {
resources.remove(resource.getName());
}
/**
* Return an InputStream to an HTML representation of the contents
* of this directory.
*
* @param contextPath Context path to which our internal paths are
* relative
*/
public InputStream render(String contextPath, String serverInfo) {
// Number of characters to trim from the beginnings of filenames
int trim = name.length();
if (!name.endsWith("/"))
trim += 1;
if (name.equals("/"))
trim = 1;
// Prepare a writer to a buffered area
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(stream);
// FIXME - Currently pays no attention to the user's Locale
// Render the page header
writer.print("<html>\r\n");
writer.print("<head>\r\n");
writer.print("<title>");
writer.print(sm.getString("directory.title", name));
writer.print("</title>\r\n</head>\r\n");
writer.print("<body bgcolor=\"white\">\r\n");
writer.print("<table width=\"90%\" cellspacing=\"0\"" +
" cellpadding=\"5\" align=\"center\">\r\n");
// Render the in-page title
writer.print("<tr><td colspan=\"3\"><font size=\"+2\">\r\n<strong>");
writer.print(sm.getString("directory.title", name));
writer.print("</strong>\r\n</font></td></tr>\r\n");
// Render the link to our parent (if required)
String parentDirectory = name;
if (parentDirectory.endsWith("/")) {
parentDirectory =
parentDirectory.substring(0, parentDirectory.length() - 1);
}
int slash = parentDirectory.lastIndexOf("/");
if (slash >= 0) {
String parent = name.substring(0, slash);
writer.print("<tr><td colspan=\"3\" bgcolor=\"#ffffff\">\r\n");
writer.print("<a href=\"");
writer.print(rewriteUrl(contextPath));
if (parent.equals(""))
parent = "/";
writer.print(parent);
writer.print("\">");
writer.print(sm.getString("directory.parent", parent));
writer.print("</a>\r\n");
writer.print("</td></tr>\r\n");
}
// Render the column headings
writer.print("<tr bgcolor=\"#cccccc\">\r\n");
writer.print("<td align=\"left\"><font size=\"+1\"><strong>");
writer.print(sm.getString("directory.filename"));
writer.print("</strong></font></td>\r\n");
writer.print("<td align=\"center\"><font size=\"+1\"><strong>");
writer.print(sm.getString("directory.size"));
writer.print("</strong></font></td>\r\n");
writer.print("<td align=\"right\"><font size=\"+1\"><strong>");
writer.print(sm.getString("directory.lastModified"));
writer.print("</strong></font></td>\r\n");
writer.print("</tr>\r\n");
// Render the directory entries within this directory
ResourceBean resources[] = findResources();
boolean shade = false;
for (int i = 0;i < resources.length; i++) {
String trimmed = resources[i].getName().substring(trim);
if (trimmed.equalsIgnoreCase("WEB-INF") ||
trimmed.equalsIgnoreCase("META-INF"))
continue;
writer.print("<tr");
if (shade)
writer.print(" bgcolor=\"eeeeee\"");
writer.print(">\r\n");
shade = !shade;
writer.print("<td align=\"left\"> \r\n");
writer.print("<a href=\"");
writer.print(rewriteUrl(contextPath));
writer.print(rewriteUrl(resources[i].getName()));
writer.print("\"><tt>");
writer.print(trimmed);
if (resources[i] instanceof DirectoryBean)
writer.print("/");
writer.print("</tt></a></td>\r\n");
writer.print("<td align=\"right\"><tt>");
if (resources[i] instanceof DirectoryBean)
writer.print(" ");
else
writer.print(renderSize(resources[i].getSize()));
writer.print("</tt></td>\r\n");
writer.print("<td align=\"right\"><tt>");
writer.print(renderLastModified(resources[i].getLastModified()));
writer.print("</tt></td>\r\n");
writer.print("</tr>\r\n");
}
// Render the page footer
writer.print("<tr><td colspan=\"3\"> </td></tr>\r\n");
writer.print("<tr><td colspan=\"3\" bgcolor=\"#cccccc\">");
writer.print("<font size=\"-1\">");
writer.print(serverInfo);
writer.print("</font></td></tr>\r\n");
writer.print("</table>\r\n");
writer.print("</body>\r\n");
writer.print("</html>\r\n");
// Return an input stream to the underlying bytes
writer.flush();
return (new ByteArrayInputStream(stream.toByteArray()));
}
/**
* Render the last modified date and time for the specified timestamp.
*
* @param lastModified Last modified date and time, in milliseconds since
* the epoch
*/
private String renderLastModified(long lastModified) {
return (timestamp.format(new Date(lastModified)));
}
/**
* Render the specified file size (in bytes).
*
* @param size File size (in bytes)
*/
private String renderSize(long size) {
long leftSide = size / 1024;
long rightSide = (size % 1024) / 103; // Makes 1 digit
if ((leftSide == 0) && (rightSide == 0) && (size > 0))
rightSide = 1;
return ("" + leftSide + "." + rightSide + " kb");
}
/**
* URL rewriter.
*
* @param path Path which has to be rewiten
*/
private String rewriteUrl(String path) {
String normalized = path;
// Replace " " with "%20"
while (true) {
int index = normalized.indexOf(" ");
if (index < 0)
break;
normalized = normalized.substring(0, index) + "%20"
+ normalized.substring(index + 1);
}
return normalized;
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/FileResources.java
Index: FileResources.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.webdav.util.StringManager;
/**
* Implementation of the <b>Resources</b> that operates off a document
* base that is a directory in the local filesystem. If the specified
* document base is relative, it is resolved against the application base
* directory for our surrounding virtual host (if any), or against the
* value of the "catalina.home" system property.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that new files may
* be added, and existing files modified, while this web application is
* running. Therefore, only resources (not directories) are cached, and
* the background thread must remove cached entries that have been modified
* since they were cached.
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:41 $
*/
public final class FileResources extends ResourcesBase {
// ----------------------------------------------------- Instance Variables
// title used when rendering directory resources ( no other use )
String contextPath;
public static String serverInfo="Apache Tomcat/3.3";
// XXX add code to set it
/**
* The document base directory for this component.
*/
protected File base = null;
/**
* The descriptive information string for this implementation.
*/
protected static final String info =
"org.apache.catalina.resources.FileResources/1.0";
/**
* The descriptive information string for this implementation.
*/
protected static final int BUFFER_SIZE = 2048;
// ------------------------------------------------------------- Properties
/**
* Set the document root for this component.
*
* @param docBase The new document root - it must be an absolute path
*
* @exception IllegalArgumentException if the specified value is not
* supported by this implementation
* @exception IllegalArgumentException if this would create a
* malformed URL
*/
public void setDocBase(String docBase) {
// Validate the format of the proposed document root
if (docBase == null)
throw new IllegalArgumentException
(sm.getString("resources.null"));
// Calculate a File object referencing this document base directory
File base = new File(docBase);
// Validate that the document base is an existing directory
if (!base.exists() || !base.isDirectory() || !base.canRead())
throw new IllegalArgumentException
(sm.getString("fileResources.base", docBase));
this.base = base;
// Perform the standard superclass processing
super.setDocBase(docBase);
}
public void setContextPath( String cp ) {
contextPath=cp;
}
// --------------------------------------------------------- Public Methods
/**
* Return the real path for a given virtual path, or <code>null</code>
* if no such path can be identified.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public String getRealPath(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null) {
// if (debug >= 1)
// log("getRealPath(" + path + ") --> NULL");
return (null);
}
try {
ResourceUtils.validate(normalized);
} catch (IllegalArgumentException e) {
// if (debug >= 1)
// log("getRealPath(" + path + ") --> IAE");
throw e;
}
// Return a real path to where this file does, or would, exist
File file = new File(base, normalized.substring(1));
// if (debug >= 1)
// log("getRealPath(" + path + ") --> " + file.getAbsolutePath());
return (file.getAbsolutePath());
}
/**
* Return a URL to the resource specified by the given virtual path,
* or <code>null</code> if no such URL can be identified.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Use of this method bypasses any caching
* performed by this component. To take advantage of local caching,
* use <code>getResourceAsStream()</code> instead.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
* @exception MalformedURLException if the resulting URL does not
* have legal syntax
*/
public URL getResource(String path) throws MalformedURLException {
// Acquire an absolute pathname for the requested resource
String pathname = getRealPath(path);
if (pathname == null) {
// if (debug >= 1)
// log("getResource(" + path + ") --> NULL");
return (null);
}
// Construct a URL that refers to this file
URL url = new URL("file", null, 0, pathname);
// if (debug >= 1)
// log("getResource(" + path + ") --> " + url.toString());
return (url);
}
/**
* Return an InputStream to the contents of the resource specified
* by the given virtual path, or <code>null</code> if no resource
* exists at the specified path.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public InputStream getResourceAsStream(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null) {
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> NULL");
return (null);
}
try {
ResourceUtils.validate(normalized);
} catch (IllegalArgumentException e) {
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> IAE");
throw e;
}
// Look up the cached resource entry (if it exists) for this path
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null) {
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> CACHED");
return (new ByteArrayInputStream(resource.getData()));
}
// Create a File object referencing the requested resource
File file = file(normalized);
if ((file == null) || !file.exists() || !file.canRead()) {
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> NO FILE");
return (null);
}
// If the resource path ends in "/", this *must* be a directory
if (normalized.endsWith("/") && !file.isDirectory()) {
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> NOT DIR");
return (null);
}
// Special handling for directories
if (file.isDirectory()) {
if (contextPath == null)
contextPath = "";
DirectoryBean directory = new DirectoryBean(normalized, file);
File[] fileList = file.listFiles();
for (int i=0; i<fileList.length; i++) {
File currentFile = fileList[i];
ResourceBean newEntry = null;
if (currentFile.isDirectory()) {
newEntry =
new DirectoryBean(ResourceUtils.normalize(normalized + "/"
+ currentFile.getName()),
currentFile);
} else {
newEntry =
new ResourceBean(ResourceUtils.normalize(normalized + "/"
+ currentFile.getName()),
currentFile);
}
directory.addResource(newEntry);
}
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> DIRECTORY");
return (directory.render(contextPath, serverInfo));
}
// Cache the data for this resource (if appropriate and not yet done)
if (cacheable(normalized, file.length())) {
resource = new ResourceBean(normalized, file);
try {
resource.cache(inputStream(resource.getName()));
} catch (IOException e) {
log(sm.getString("resources.input", resource.getName()), e);
return (null);
}
synchronized (resourcesCache) {
resourcesCache.put(resource.getName(), resource);
resourcesCount++;
}
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> CACHE AND SERVE");
return (new ByteArrayInputStream(resource.getData()));
}
// Serve the contents directly from the filesystem
try {
// if (debug >= 1)
// log("getResourceAsStream(" + path + ") --> SERVE FILE");
return (new FileInputStream(file));
} catch (IOException e) {
log(sm.getString("resoruces.input", resource.getName()), e);
return (null);
}
}
/**
* Returns true if a resource exists at the specified path,
* where <code>path</code> would be suitable for passing as an argument to
* <code>getResource()</code> or <code>getResourceAsStream()</code>.
* If there is no resource at the specified location, return false.
*
* @param path The path to the desired resource
*/
public boolean exists(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null) {
// if (debug >= 1)
// log("exists(" + path + ") --> NULL");
return (false);
}
try {
ResourceUtils.validate(normalized);
} catch (IllegalArgumentException e) {
// if (debug >= 1)
// log("exists(" + path + ") --> IAE");
throw e;
}
File file = new File(base, normalized.substring(1));
if (file != null) {
// if (debug >= 1)
// log("exists(" + path + ") --> " + file.exists() +
// " isDirectory=" + file.isDirectory());
return (file.exists());
} else {
// if (debug >= 1)
// log("exists(" + path + ") --> NO FILE");
return (false);
}
}
/**
* Return the last modified time for the resource specified by
* the given virtual path, or -1 if no such resource exists (or
* the last modified time cannot be determined).
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: We are assuming that
* files may be modified while the application is running, so we
* bypass the cache and check the filesystem directly.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public long getResourceModified(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (-1L);
ResourceUtils.validate(normalized);
File file = file(normalized);
if (file != null)
return (file.lastModified());
else
return (-1L);
}
/**
* Return the set of context-relative paths of all available resources.
* Each path will begin with a "/" character.
*/
public String[] getResourcePaths() {
ArrayList paths = new ArrayList();
paths.add("/"); // NOTE: Assumes directories are included
appendResourcePaths(paths, "", base);
String results[] = new String[paths.size()];
return (results);
}
/**
* Return the creation date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1. If this time is unknown, the
* implementation should return getResourceModified(path).
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: The creation date of a file
* shouldn't change except if the file is deleted and the recreated, so
* this method uses the cache.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public long getResourceCreated(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (-1L);
ResourceUtils.validate(normalized);
File file = file(normalized);
if (file != null)
return (file.lastModified());
else
return (-1L);
}
/**
* Return the content length of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If the content length
* of the resource can't be determined, return -1. If no content is
* available (when for exemple, the resource is a collection), return 0.
*
* @param path The path to the desired resource
*/
public long getResourceLength(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (-1L);
ResourceUtils.validate(normalized);
// Look up the cached resource entry (if it exists) for this path
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null) {
return (resource.getSize());
}
// No entry was found in the cache
File file = file(normalized);
if (file != null)
return (file.length());
else
return (-1L);
}
/**
* Return true if the resource at the specified path is a collection. A
* collection is a special type of resource which has no content but
* contains child resources.
*
* @param path The path to the desired resource
*/
public boolean isCollection(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (false);
ResourceUtils.validate(normalized);
File file = file(normalized);
if (file != null)
return (file.isDirectory());
else
return (false);
}
/**
* Return the children of the resource at the specified path, if any. This
* will return null if the resource is not a collection, or if it is a
* collection but has no children.
*
* @param path The path to the desired resource
*/
public String[] getCollectionMembers(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (null);
ResourceUtils.validate(normalized);
File file = file(normalized);
if (file != null) {
String[] dirList = file.list();
for (int i=0; i<dirList.length; i++) {
dirList[i] = ResourceUtils.normalize(normalized + "/" + dirList[i]);
}
return dirList;
} else {
return (null);
}
}
/**
* Set the content of the resource at the specified path. If the resource
* already exists, its previous content is overwritten. If the resource
* doesn't exist, its immediate parent collection (according to the path
* given) exists, then its created, and the given content is associated
* with it. Return false if either the resource is a collection, or
* no parent collection exist.
*
* @param path The path to the desired resource
* @param content InputStream to the content to be set
*/
public boolean setResource(String path, InputStream content) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (false);
ResourceUtils.validate(normalized);
File file = new File(base, normalized.substring(1));
//if ((file.exists()) && (file.isDirectory()))
//return (false);
OutputStream os = null;
try {
os = new FileOutputStream(file);
} catch (FileNotFoundException e) {
return (false);
} catch (IOException e) {
return (false);
}
try {
byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
int nb = content.read(buffer);
if (nb == -1)
break;
os.write(buffer, 0, nb);
}
} catch (IOException e) {
return (false);
}
try {
os.close();
} catch (IOException e) {
return (false);
}
try {
content.close();
} catch (IOException e) {
return (false);
}
return (true);
}
/**
* Create a collection at the specified path. A parent collection for this
* collection must exist. Return false if a resource already exist at the
* path specified, or if the parent collection doesn't exist.
*
* @param path The path to the desired resource
*/
public boolean createCollection(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (false);
ResourceUtils.validate(normalized);
File file = new File(base, normalized.substring(1));
if (file != null)
return (file.mkdir());
else
return (false);
}
/**
* Delete the specified resource. Non-empty collections cannot be deleted
* before deleting all their member resources. Return false is deletion
* fails because either the resource specified doesn't exist, or the
* resource is a non-empty collection.
*
* @param path The path to the desired resource
*/
public boolean deleteResource(String path) {
String normalized = ResourceUtils.normalize(path);
if (normalized == null) {
return (false);
}
ResourceUtils.validate(normalized);
File file = file(normalized);
if (file != null) {
return (file.delete());
} else {
return (false);
}
}
// -------------------------------------------------------- Private Methods
/**
* Append resource paths for files in the specified directory to the
* list we are accumulating.
*
* @param paths The list containing our accumulated paths
* @param path Context-relative path for this directory
* @param dir File object for this directory
*/
private void appendResourcePaths(List paths, String path, File dir) {
String names[] = dir.list();
for (int i = 0; i < names.length; i++) {
paths.add(path + "/" + names[i]);
File file = new File(dir, names[i]); // Assume dirs included
if (file.isDirectory())
appendResourcePaths(paths, path + "/" + names[i], file);
}
}
/**
* Return a File object representing the specified normalized
* context-relative path if it exists and is readable. Otherwise,
* return <code>null</code>.
*
* @param name Normalized context-relative path (with leading '/')
*/
private File file(String name) {
if (name == null)
return (null);
File file = new File(base, name.substring(1));
if (file.exists() && file.canRead())
return (file);
else
return (null);
}
/**
* Return an input stream to the data content of the underlying file
* that corresponds to the specified normalized context-relative path.
*
* @param name Normalized context-relative path (with leading '/')
*
* @exception IOException if an input/output error occurs
*/
private InputStream inputStream(String name) throws IOException {
File file = file(name);
if ((file == null) || file.isDirectory())
return (null);
else
return (new FileInputStream(file));
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/JarResources.java
Index: JarResources.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.tomcat.webdav.util.StringManager;
/**
* Implementation of the <b>Resources</b> that decompresses and renders
* entries from a JAR file that is located either locally or remotely.
* Valid syntax for the <code>docBase</code> property corresponds to the
* syntax supported by the <code>java.net.JarURLConnection</code> class,
* and is illustrated by the following examples:
* <ul>
* <li><b>jar:file:/path/to/filename.jar!/</b> - Uses the JAR file
* <code>/path/to/filename.jar</code> as the source of resources.
* <li><b>jar:http://www.foo.com/bar/baz.jar!/</b> - Uses the JAR file
* retrieved by doing an HTTP GET operation on the URL
* <code>http://www.foo.com/bar/baz.jar</code> (which makes this
* instance of Catalina serve as a proxy for the specified application).
* </ul>
* In all cases, the <code>docBase</code> you specify must begin with
* <code>jar:</code>. If your <code>docBase</code> value does not end with
* "!/", this will be added for you.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: It is assumed that the underlying
* JAR file itself will not be modified without restarting this web application
* (or at least this <code>Resources</code> implementation). Therefore, the
* set of directory and resource entries is pre-loaded into our resource cache.
* The actual data associated with these resources is not cached until it is
* requested the first time (and passes the "cacheable" test).
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:41 $
*/
public final class JarResources extends ResourcesBase {
// ----------------------------------------------------- Instance Variables
// title used when rendering directory resources ( no other use )
String contextPath;
public static String serverInfo="Apache Tomcat/3.3";
// XXX add code to set it
/**
* The URLConnection to our JAR file.
*/
protected JarURLConnection conn = null;
/**
* The descriptive information string for this implementation.
*/
protected static final String info =
"org.apache.catalina.resources.JarResources/1.0";
/**
* The JarFile object associated with our document base.
*/
protected JarFile jarFile = null;
// ------------------------------------------------------------- Properties
/**
* Set the document root for this component.
*
* @param docBase The new document root
*
* @exception IllegalArgumentException if the specified value is not
* supported by this implementation
* @exception IllegalArgumentException if this would create a
* malformed URL
*/
public void setDocBase(String docBase) {
// Validate the format of the proposed document root
if (docBase == null)
throw new IllegalArgumentException
(sm.getString("resources.null"));
if (!docBase.startsWith("jar:"))
throw new IllegalArgumentException
(sm.getString("jarResources.syntax", docBase));
if (!docBase.endsWith("!/"))
docBase += "!/";
// Close any previous JAR that we have opened
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
log("Closing JAR file", e);
}
jarFile = null;
conn = null;
}
// Open a URLConnection to the specified JAR file
try {
URL url = new URL(docBase);
conn = (JarURLConnection) url.openConnection();
conn.setAllowUserInteraction(false);
conn.setDoInput(true);
conn.setDoOutput(false);
conn.connect();
jarFile = conn.getJarFile();
} catch (Exception e) {
log("Establishing connection", e);
throw new IllegalArgumentException
(sm.getString("resources.connect", docBase));
}
// Populate our cache of directory and resource entries
populate();
// Perform the standard superclass processing
super.setDocBase(docBase);
}
public void setContextPath( String cp ) {
contextPath=cp;
}
// --------------------------------------------------------- Public Methods
/**
* Return the real path for a given virtual path, or <code>null</code>
* if no such path can be identified.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public String getRealPath(String path) {
ResourceUtils.validate(path);
return (null); // JAR entries do not have a real path
}
/**
* Return a URL to the resource specified by the given virtual path,
* or <code>null</code> if no such URL can be identified.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Use of this method bypasses any caching
* performed by this component. To take advantage of local caching,
* use <code>getResourceAsStream()</code> instead.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
* @exception MalformedURLException if the resulting URL does not
* have legal syntax
*/
public URL getResource(String path) throws MalformedURLException {
ResourceUtils.validate(path);
// Construct a URL from the normalized version of the specified path
String normalized = ResourceUtils.normalize(path);
if (normalized != null)
return (new URL(docBase + normalized.substring(1)));
else
return (null);
}
/**
* Return an InputStream to the contents of the resource specified
* by the given virtual path, or <code>null</code> if no resource
* exists at the specified path.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public InputStream getResourceAsStream(String path) {
ResourceUtils.validate(path);
// Look up the cached resource entry (if it exists) for this path
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (null);
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource == null)
return (null);
// Special handling for directories
if (resource instanceof DirectoryBean) {
if (expand) {
if (contextPath == null)
contextPath = "";
return (((DirectoryBean) resource).render(contextPath,
serverInfo));
} else {
return (null);
}
}
// Cache the data for this resource (if appropriate and not yet done)
if (resource.getData() == null) {
try {
resource.cache(inputStream(resource.getName()));
} catch (IOException e) {
log(sm.getString("resources.input", resource.getName()), e);
return (null);
}
if (resource.getData() != null)
resourcesCount++;
}
// Return an input stream to the cached or uncached data
if (resource.getData() != null)
return (new ByteArrayInputStream(resource.getData()));
else {
try {
return (inputStream(normalized));
} catch (IOException e) {
log(sm.getString("resources.input", resource.getName()), e);
return (null);
}
}
}
/**
* Returns true if a resource exists at the specified path,
* where <code>path</code> would be suitable for passing as an argument to
* <code>getResource()</code> or <code>getResourceAsStream()</code>.
* If there is no resource at the specified location, return false.
*
* @param path The path to the desired resource
*/
public boolean exists(String path) {
// Look up and return the last modified time for this resource
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (false);
ResourceUtils.validate(normalized);
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null)
return (true);
else
return (false);
}
/**
* Return the last modified time for the resource specified by
* the given virtual path, or -1 if no such resource exists (or
* the last modified time cannot be determined).
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: We are assuming that the
* underlying JAR file will not be modified without restarting our
* associated Context, so it is sufficient to return the last modified
* timestamp from our cached resource bean.
*
* @param path Context-relative path starting with '/'
*
* @exception IllegalArgumentException if the path argument is null
* or does not start with a '/'
*/
public long getResourceModified(String path) {
ResourceUtils.validate(path);
// Look up and return the last modified time for this resource
String normalized = ResourceUtils.normalize(path);
if (normalized == null)
return (-1L);
ResourceBean resource = null;
synchronized (resourcesCache) {
resource = (ResourceBean) resourcesCache.get(normalized);
}
if (resource != null)
return (resource.getLastModified());
else
return (-1L);
}
/**
* Return the set of context-relative paths of all available resources.
* Each path will begin with a "/" character.
*/
public String[] getResourcePaths() {
ArrayList paths = new ArrayList();
// NOTE: assumes directories are included
synchronized (resourcesCache) {
Iterator names = resourcesCache.keySet().iterator();
while (names.hasNext())
paths.add((String) names.next());
}
String results[] = new String[paths.size()];
return ((String[]) paths.toArray(results));
}
/**
* Return the creation date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1. If this time is unknown, the
* implementation should return getResourceModified(path).
*
* @param path The path to the desired resource
*/
public long getResourceCreated(String path) {
return 0;
}
/**
* Return the content length of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If the content length
* of the resource can't be determined, return -1. If no content is
* available (when for exemple, the resource is a collection), return 0.
*
* @param path The path to the desired resource
*/
public long getResourceLength(String path) {
return -1;
}
/**
* Return true if the resource at the specified path is a collection. A
* collection is a special type of resource which has no content but
* contains child resources.
*
* @param path The path to the desired resource
*/
public boolean isCollection(String path) {
return false;
}
/**
* Return the children of the resource at the specified path, if any. This
* will return null if the resource is not a collection, or if it is a
* collection but has no children.
*
* @param path The path to the desired resource
*/
public String[] getCollectionMembers(String path) {
return null;
}
/**
* Set the content of the resource at the specified path. If the resource
* already exists, its previous content is overwritten. If the resource
* doesn't exist, its immediate parent collection (according to the path
* given) exists, then its created, and the given content is associated
* with it. Return false if either the resource is a collection, or
* no parent collection exist.
*
* @param path The path to the desired resource
* @param content InputStream to the content to be set
*/
public boolean setResource(String path, InputStream content) {
return false;
}
/**
* Create a collection at the specified path. A parent collection for this
* collection must exist. Return false if a resource already exist at the
* path specified, or if the parent collection doesn't exist.
*
* @param path The path to the desired resource
*/
public boolean createCollection(String path) {
return false;
}
/**
* Delete the specified resource. Non-empty collections cannot be deleted
* before deleting all their member resources. Return false is deletion
* fails because either the resource specified doesn't exist, or the
* resource is a non-empty collection.
*
* @param path The path to the desired resource
*/
public boolean deleteResource(String path) {
return false;
}
// -------------------------------------------------------- Private Methods
/**
* Return an input stream to the data content of the underlying JAR entry
* that corresponds to the specified normalized context-relative path.
*
* @param name Normalized context-relative path (with leading '/')
*
* @exception IOException if an input/output error occurs
*/
private InputStream inputStream(String name) throws IOException {
if (name == null)
return (null);
JarEntry entry = jarFile.getJarEntry(name.substring(1));
if (entry == null)
return (null);
return (jarFile.getInputStream(entry));
}
/**
* Populate our resources cache based on all of the entries in the
* underlying JAR file.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: This method assumes that the
* "name" of an entry within the JAR file is exactly the same as the
* result of performing a <code>normalize()</code> call on that name,
* with the exception of the leading slash that is added.
*/
private void populate() {
synchronized (resourcesCache) {
// Erase the existing cache
resourcesCache.clear();
resourcesCount = 0;
// Construct a pseudo-directory for the entire JAR file
DirectoryBean top = new DirectoryBean("/");
resourcesCache.put(top.getName(), top);
// Process the entries in this JAR file
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = (JarEntry) entries.nextElement();
// Create and cache the resource for this entry
String name = "/" + entry.getName();
ResourceBean resource = null;
if (entry.isDirectory())
resource = new DirectoryBean(name, entry);
else
resource = new ResourceBean(name, entry);
// Connect to our parent entry (if any)
int last = name.lastIndexOf("/");
String parentName = name.substring(0, last);
if (parentName.length() < 1)
parentName = "/";
ResourceBean parent =
(ResourceBean) resourcesCache.get(parentName);
if ((parent != null) && (parent instanceof DirectoryBean))
resource.setParent((DirectoryBean) parent);
}
}
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Shut down this component.
*
* @exception LifecycleException if a major problem occurs
*/
public void stop() {
// Shut down our current connection (if any)
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
log("Closing JAR file", e);
}
jarFile = null;
}
conn = null;
// Perform standard superclass processing
super.stop();
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/LocalStrings.properties
Index: LocalStrings.properties
===================================================================
directory.filename=Filename
directory.lastModified=Last Modified
directory.parent=Up To {0}
directory.size=Size
directory.title=Directory Listing For {0}
directory.version=Tomcat Catalina version 4.0
fileResources.base=Document base {0} does not exist or is not a readable directory
jarResources.syntax=Document base {0} must start with 'jar:' and end with '!/'
resources.alreadyStarted=Resources has already been started
resources.connect=Cannot connect to document base {0}
resources.input=Cannot create input stream for resource {0}
resources.notStarted=Resources has not yet been started
resources.null=Document base cannot be null
resources.path=Context relative path {0} must start with '/'
standardResources.alreadyStarted=Resources has already been started
standardResources.directory=File base {0} is not a directory
standardResources.exists=File base {0} does not exist
standardResources.notStarted=Resources has not yet been started
standardResources.null=Document base cannot be null
standardResources.slash=Document base {0} must not end with a slash
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/ResourceBean.java
Index: ResourceBean.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.jar.JarEntry;
/**
* Abstraction bean that represents the properties of a "resource" that
* may or may not be a "directory", in a fashion that is independent
* of the actual underlying medium used to represent those entries.
* Convenient constructors are provided to populate our properties from
* common sources, but it is feasible to do everything with property
* setters if necessary.
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:42 $
*/
public class ResourceBean {
// ----------------------------------------------------------- Constructors
/**
* Construct a new resource bean for the named resource, with default
* properties.
*
* @param name Normalized context-relative name of this resource
*/
public ResourceBean(String name) {
super();
setName(name);
}
/**
* Construct a new resource bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param file File representing this resource entry
*/
public ResourceBean(String name, File file) {
this(name);
populate(file);
}
/**
* Construct a new resource bean for the named resource, with properties
* populated from the specified object. Note that the data content of
* this resource is <strong>not</strong> initialized unless and until
* <code>setData()</code> is called.
*
* @param name Normalized context-relative name of this resource
* @param entry JAR entry representing this resource entry
*/
public ResourceBean(String name, JarEntry entry) {
this(name);
populate(entry);
}
// ------------------------------------------------------------- Properties
/**
* The data content of this resource. This property is
* <strong>only</strong> initialized when the corresponding property
* setter method is called.
*/
protected byte[] data = null;
public byte[] getData() {
return (this.data);
}
public void setData(byte[] data) {
this.data = data;
}
/**
* The last modified date/time for this resource, in milliseconds since
* the epoch.
*/
protected long lastModified = 0L;
public long getLastModified() {
return (this.lastModified);
}
public void setLastModified(long lastModified) {
this.lastModified = lastModified;
}
/**
* The normalized context-relative name of this resource.
*/
protected String name = null;
public String getName() {
return (this.name);
}
public void setName(String name) {
this.name = name;
}
/**
* The parent resource (normally a directory entry) of this resource.
* Note that this property is <strong>not</strong> set from an underlying
* File or JarEntry argument to our constructor -- you must call
* <code>setParent()</code> explicitly if you wish to maintain this
* relationship.
*/
protected DirectoryBean parent = null;
public DirectoryBean getParent() {
return (this.parent);
}
public void setParent(DirectoryBean parent) {
if (this.parent != null)
this.parent.removeResource(this);
this.parent = parent;
if (this.parent != null)
this.parent.addResource(this);
}
/**
* The size of this resource, in bytes.
*/
protected long size = 0L;
public long getSize() {
return (this.size);
}
public void setSize(long size) {
this.size = size;
}
// --------------------------------------------------------- Public Methods
/**
* Cache the data for this resource from the specified input stream.
*
* @param input InputStream from which to read the data for this resource
*
* @exception IOException if an input/output error occurs
*/
public void cache(InputStream input) throws IOException {
BufferedInputStream in = new BufferedInputStream(input);
ByteArrayOutputStream out = new ByteArrayOutputStream();
while (true) {
int ch = in.read();
if (ch < 0)
break;
out.write(ch);
}
in.close();
data = out.toByteArray();
}
// ------------------------------------------------------ Protected Methods
/**
* Populate our properties from the specified File object.
*
* @param file File representing this entry
*/
protected void populate(File file) {
this.lastModified = file.lastModified();
this.size = file.length();
}
/**
* Populate our properties from the specified JarEntry object.
*
* @param entry JarEntry representing this entry
*/
protected void populate(JarEntry entry) {
this.lastModified = entry.getTime();
this.size = entry.getSize();
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/ResourceUtils.java
Index: ResourceUtils.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import org.apache.tomcat.webdav.util.StringManager;
/**
* Convenience base class for implementations of the <b>Resources</b>
* interface. It is expected that subclasses of this class will be
* created for each flavor of document root to be supported.
* <p>
* Included in the basic support provided by this class is provisions
* for caching of resources according to configurable policy properties.
* This will be especially useful for web applications with relatively
* small amounts of static content (such as a 100% dynamic JSP based
* application with just a few images), as well as environments where
* accessing the underlying resources is relatively time consuming
* (such as a local or remote JAR file).
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:42 $
*/
public class ResourceUtils {
/** Find extension
*/
public static String getExtension(String file) {
if (file == null)
return (null);
int period = file.lastIndexOf(".");
if (period < 0)
return (null);
String extension = file.substring(period + 1);
if (extension.length() < 1)
return (null);
return extension;
}
/**
* 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 Path to be normalized
*/
public static String normalize(String path) {
// Normalize the slashes and add leading slash if necessary
String normalized = path;
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 "%20" in the normalized path
while (true) {
int index = normalized.indexOf("%20");
if (index < 0)
break;
normalized = normalized.substring(0, index) + " " +
normalized.substring(index + 3);
}
// 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);
}
// Return the normalized path that we have completed
return (normalized);
}
private static final StringManager sm =
StringManager.getManager("org.apache.tomcat.webdav.resources");
/**
* Validate the format of the specified path, which should be context
* relative and begin with a slash character.
*
* @param path Context-relative path to be validated
*
* @exception IllegalArgumentException if the specified path is null
* or does not have a valid format
*/
public static void validate(String path) {
if ((path == null) || !path.startsWith("/"))
throw new IllegalArgumentException
(sm.getString("resources.path", path));
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/Resources.java
Index: Resources.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
/**
* A <b>Resources</b> is a generic interface for the resource acquisition
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @author Costin Manolache
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:43 $
*/
public interface Resources {
/**
* Return the resource located at the named path as an
* <code>InputStream</code> object.
* <p>
* The data in the <code>InputStream</code> can be of any type or length.
* The path must be specified according to the rules given in
* <code>getResource()</code>. This method returns <code>null</code>
* if no resource exists at the specified path.
* <p>
* Meta-information such as content length and content type that is
* available via the <code>getResource()</code> method is lost when
* using this method.
* <p>
* The servlet container must implement the URL handlers and
* <code>URLConnection</code> objects that are necessary to access
* the resource.
* <p>
* This method is different from
* <code>java.lang.Class.getResourceAsStream()</code>, which uses a
* class loader. This method allows servlet containers to make a
* resource available to a servlet from any location, without using
* a class loader.
*
* @param path The path to the desired resource
*/
public InputStream getResourceAsStream(String path);
/**
* Returns true if a resource exists at the specified path,
* where <code>path</code> would be suitable for passing as an argument to
* <code>getResource()</code> or <code>getResourceAsStream()</code>.
* If there is no resource at the specified location, return false.
*
* @param path The path to the desired resource
*/
public boolean exists(String path);
/**
* Return the last modified date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1.
*
* @param path The path to the desired resource
*/
public long getResourceModified(String path);
/**
* Return the creation date/time of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If there is no resource at the
* specified location, return -1. If this time is unknown, the
* implementation should return getResourceModified(path).
*
* @param path The path to the desired resource
*/
public long getResourceCreated(String path);
/**
* Return the content length of the resource at the specified
* path, where <code>path</code> would be suitable for passing as an
* argument to <code>getResource()</code> or
* <code>getResourceAsStream()</code>. If the content length
* of the resource can't be determinedof if the resource is a collection,
* return -1. If no content is available, return 0.
*
* @param path The path to the desired resource
*/
public long getResourceLength(String path);
/**
* Return true if the resource at the specified path is a collection. A
* collection is a special type of resource which has no content but
* contains child resources.
*
* @param path The path to the desired resource
*/
public boolean isCollection(String path);
/**
* Return the children of the resource at the specified path, if any. This
* will return null if the resource is not a collection, or if it is a
* collection but has no children.
*
* @param path The path to the desired resource
*/
public String[] getCollectionMembers(String path);
/**
* Set the content of the resource at the specified path. If the resource
* already exists, its previous content is overwritten. If the resource
* doesn't exist, its immediate parent collection (according to the path
* given) exists, then its created, and the given content is associated
* with it. Return false if either the resource is a collection, or
* no parent collection exist.
*
* @param path The path to the desired resource
* @param content InputStream to the content to be set
*/
public boolean setResource(String path, InputStream content);
/**
* Create a collection at the specified path. A parent collection for this
* collection must exist. Return false if a resource already exist at the
* path specified, or if the parent collection doesn't exist.
*
* @param path The path to the desired resource
*/
public boolean createCollection(String path);
/**
* Delete the specified resource. Non-empty collections cannot be deleted
* before deleting all their member resources. Return false is deletion
* fails because either the resource specified doesn't exist, or the
* resource is a non-empty collection.
*
* @param path The path to the desired resource
*/
public boolean deleteResource(String path);
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/resources/ResourcesBase.java
Index: ResourcesBase.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.resources;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import org.apache.tomcat.webdav.util.StringManager;
/**
* Convenience base class for implementations of the <b>Resources</b>
* interface. It is expected that subclasses of this class will be
* created for each flavor of document root to be supported.
* <p>
* Included in the basic support provided by this class is provisions
* for caching of resources according to configurable policy properties.
* This will be especially useful for web applications with relatively
* small amounts of static content (such as a 100% dynamic JSP based
* application with just a few images), as well as environments where
* accessing the underlying resources is relatively time consuming
* (such as a local or remote JAR file).
*
* @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:43 $
*/
public abstract class ResourcesBase
implements Resources, Runnable {
// ----------------------------------------------------------- Constructors
/**
* Construct a new instance of this class with default values.
*/
public ResourcesBase() {
super();
}
// ----------------------------------------------------- Instance Variables
/**
* The interval (in seconds) at which our background task should check
* for out-of-date cached resources, or zero for no checks.
*/
protected int checkInterval = 0;
/**
* The debugging detail level for this component.
*/
protected int debug = 0;
/**
* The document root for this component.
*/
protected String docBase = null;
/**
* Should "directory" entries be expanded?
*/
protected boolean expand = true;
/**
* The maximum number of resources to cache.
*/
protected int maxCount = 0;
/**
* The maximum size of resources to be cached.
*/
protected long maxSize = 0L;
/**
* The minimum size of resources to be cached.
*/
protected long minSize = 0L;
/**
* The set of ResourceBean entries for this component,
* keyed by the normalized context-relative resource URL.
*/
protected HashMap resourcesCache = new HashMap();
/**
* The count of ResourceBean entries for which we have actually
* cached data. This can be different from the number of elements
* in the <code>resourcesCache</code> collection.
*/
protected int resourcesCount = 0;
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager("org.apache.tomcat.webdav.resources");
/**
* Has this component been started?
*/
protected boolean started = false;
/**
* The background thread.
*/
protected Thread thread = null;
/**
* The background thread completion semaphore.
*/
protected boolean threadDone = false;
/**
* The name to register for the background thread.
*/
protected String threadName = "ResourcesBase";
// ------------------------------------------------------------- Properties
/**
* Return the resource cache check interval.
*/
public int getCheckInterval() {
return (this.checkInterval);
}
/**
* Set the resource cache check interval.
*
* @param checkInterval The new check interval
*/
public void setCheckInterval(int checkInterval) {
// Perform the property update
int oldCheckInterval = this.checkInterval;
this.checkInterval = checkInterval;
// Start or stop the background thread (if necessary)
if (started) {
if ((oldCheckInterval > 0) && (this.checkInterval <= 0))
threadStop();
else if ((oldCheckInterval <= 0) && (this.checkInterval > 0))
threadStart();
}
}
/**
* Return the debugging detail level for this component.
*/
public int getDebug() {
return (this.debug);
}
/**
* Set the debugging detail level for this component.
*
* @param debug The new debugging detail level
*/
public void setDebug(int debug) {
this.debug = debug;
}
/**
* Return the document root for this component.
*/
public String getDocBase() {
return (this.docBase);
}
/**
* Set the document root for this component.
*
* @param docBase The new document root
*
* @exception IllegalArgumentException if the specified value is not
* supported by this implementation
* @exception IllegalArgumentException if this would create a
* malformed URL
*/
public void setDocBase(String docBase) {
this.docBase = docBase.toString();
if (debug >= 1)
log("Setting docBase to '" + this.docBase + "'");
}
/**
* Return the "expand directories" flag.
*/
public boolean getExpand() {
return (this.expand);
}
/**
* Set the "expand directories" flag.
*
* @param expand The new "expand directories" flag
*/
public void setExpand(boolean expand) {
this.expand = expand;
}
/**
* Return the maximum number of resources to cache.
*/
public int getMaxCount() {
return (this.maxCount);
}
/**
* Set the maximum number of resources to cache.
*
* @param maxCount The new maximum count
*/
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
}
/**
* Return the maximum size of resources to be cached.
*/
public long getMaxSize() {
return (this.maxSize);
}
/**
* Set the maximum size of resources to be cached.
*
* @param maxSize The new maximum size
*/
public void setMaxSize(long maxSize) {
this.maxSize = maxSize;
}
/**
* Return the minimum size of resources to be cached.
*/
public long getMinSize() {
return (this.minSize);
}
/**
* Set the minimum size of resources to be cached.
*
* @param minSize The new minimum size
*/
public void setMinSize(long minSize) {
this.minSize = minSize;
}
/**
* Prepare for the beginning of active use of the public methods of this
* component. This method should be called after <code>configure()</code>,
* and before any of the public methods of the component are utilized.
*
* @exception IllegalStateException if this component has already been
* started
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() {
// Validate and update our current component state
if (started)
throw new RuntimeException
(sm.getString("resources.alreadyStarted"));
// lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start the background expiration checking thread (if necessary)
if (checkInterval > 0)
threadStart();
}
/**
* Gracefully terminate the active use of the public methods of this
* component. This method should be the last one called on a given
* instance of this component.
*
* @exception IllegalStateException if this component has not been started
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() {
// Validate and update our current state
if (!started)
throw new RuntimeException
(sm.getString("resources.notStarted"));
// lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
// Stop the background expiration checking thread (if necessary)
threadStop();
}
// ------------------------------------------------------ Protected Methods
/**
* Should the resource specified by our parameters be cached?
*
* @param name Name of the proposed resource
* @param size Size (in bytes) of the proposed resource
*/
protected boolean cacheable(String name, long size) {
if ((size < minSize) || (size > maxSize))
return (false);
else if (resourcesCount >= maxCount)
return (false);
else
return (true);
}
// /**
// * Return a File object representing the base directory for the
// * entire servlet container (i.e. the Engine container if present).
// */
// protected File engineBase() {
// /*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
// return (new File(System.getProperty("catalina.home")));
// }
// -------------------- Logging --------------------
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
*/
protected void log(String message) {
System.out.println("ResourceBase: " + message );
}
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
* @param throwable Associated exception
*/
protected void log(String message, Throwable throwable) {
System.out.println("ResourceBase: " + message );
throwable.printStackTrace();
}
/**
* Scan our cached resources, looking for cases where the underlying
* resource has been modified since we cached it.
*/
protected void threadProcess() {
// Create a list of the cached resources we know about
ResourceBean entries[] = new ResourceBean[0];
synchronized(resourcesCache) {
entries =
(ResourceBean[]) resourcesCache.values().toArray(entries);
}
// Check the last modified date on each entry
for (int i = 0; i < entries.length; i++) {
if (entries[i].getLastModified() !=
getResourceModified(entries[i].getName())) {
synchronized (resourcesCache) {
resourcesCache.remove(entries[i].getName());
}
}
}
}
/**
* Start the background thread that will periodically check for
* session timeouts.
*/
protected void threadStart() {
if (thread != null)
return;
threadDone = false;
threadName = "ResourcesBase[" + docBase + "]";
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
}
/**
* Stop the background thread that is periodically checking for
* session timeouts.
*/
protected void threadStop() {
if (thread == null)
return;
threadDone = true;
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
;
}
thread = null;
}
// ------------------------------------------------------ Background Thread
/**
* The background thread that checks for session timeouts and shutdown.
*/
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
try {
Thread.sleep(checkInterval * 1000L);
} catch (InterruptedException e) {
;
}
threadProcess();
}
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/util/DOMWriter.java
Index: DOMWriter.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.util;
import java.io.*;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* A sample DOM writer. This sample program illustrates how to
* traverse a DOM tree in order to print a document that is parsed.
*/
public class DOMWriter {
//
// Data
//
/** Default Encoding */
private static String
PRINTWRITER_ENCODING = "UTF8";
private static String MIME2JAVA_ENCODINGS[] =
{ "Default", "UTF-8", "US-ASCII", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4",
"ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-2022-JP",
"SHIFT_JIS", "EUC-JP","GB2312", "BIG5", "EUC-KR", "ISO-2022-KR", "KOI8-R", "EBCDIC-CP-US",
"EBCDIC-CP-CA", "EBCDIC-CP-NL", "EBCDIC-CP-DK", "EBCDIC-CP-NO", "EBCDIC-CP-FI", "EBCDIC-CP-SE",
"EBCDIC-CP-IT", "EBCDIC-CP-ES", "EBCDIC-CP-GB", "EBCDIC-CP-FR", "EBCDIC-CP-AR1",
"EBCDIC-CP-HE", "EBCDIC-CP-CH", "EBCDIC-CP-ROECE","EBCDIC-CP-YU",
"EBCDIC-CP-IS", "EBCDIC-CP-AR2", "UTF-16"
};
/** Print writer. */
protected PrintWriter out;
/** Canonical output. */
protected boolean canonical;
public DOMWriter(String encoding, boolean canonical)
throws UnsupportedEncodingException {
out = new PrintWriter(new OutputStreamWriter(System.out, encoding));
this.canonical = canonical;
} // <init>(String,boolean)
//
// Constructors
//
/** Default constructor. */
public DOMWriter(boolean canonical) throws UnsupportedEncodingException {
this( getWriterEncoding(), canonical);
}
public DOMWriter(Writer writer, boolean canonical) {
out = new PrintWriter(writer);
this.canonical = canonical;
}
public static String getWriterEncoding( ) {
return (PRINTWRITER_ENCODING);
}// getWriterEncoding
public static void setWriterEncoding( String encoding ) {
if( encoding.equalsIgnoreCase( "DEFAULT" ) )
PRINTWRITER_ENCODING = "UTF8";
else if( encoding.equalsIgnoreCase( "UTF-16" ) )
PRINTWRITER_ENCODING = "Unicode";
else
PRINTWRITER_ENCODING = MIME2Java.convert( encoding );
}// setWriterEncoding
public static boolean isValidJavaEncoding( String encoding ) {
for ( int i = 0; i < MIME2JAVA_ENCODINGS.length; i++ )
if ( encoding.equals( MIME2JAVA_ENCODINGS[i] ) )
return (true);
return (false);
}// isValidJavaEncoding
/** Prints the specified node, recursively. */
public void print(Node node) {
// is there anything to do?
if ( node == null ) {
return;
}
int type = node.getNodeType();
switch ( type ) {
// print document
case Node.DOCUMENT_NODE: {
if ( !canonical ) {
String Encoding = this.getWriterEncoding();
if( Encoding.equalsIgnoreCase( "DEFAULT" ) )
Encoding = "UTF-8";
else if( Encoding.equalsIgnoreCase( "Unicode" ) )
Encoding = "UTF-16";
else
Encoding = MIME2Java.reverse( Encoding );
out.println("<?xml version=\"1.0\" encoding=\""+
Encoding + "\"?>");
}
print(((Document)node).getDocumentElement());
out.flush();
break;
}
// print element with attributes
case Node.ELEMENT_NODE: {
out.print('<');
out.print(node.getNodeName());
Attr attrs[] = sortAttributes(node.getAttributes());
for ( int i = 0; i < attrs.length; i++ ) {
Attr attr = attrs[i];
out.print(' ');
out.print(attr.getNodeName());
out.print("=\"");
out.print(normalize(attr.getNodeValue()));
out.print('"');
}
out.print('>');
NodeList children = node.getChildNodes();
if ( children != null ) {
int len = children.getLength();
for ( int i = 0; i < len; i++ ) {
print(children.item(i));
}
}
break;
}
// handle entity reference nodes
case Node.ENTITY_REFERENCE_NODE: {
if ( canonical ) {
NodeList children = node.getChildNodes();
if ( children != null ) {
int len = children.getLength();
for ( int i = 0; i < len; i++ ) {
print(children.item(i));
}
}
} else {
out.print('&');
out.print(node.getNodeName());
out.print(';');
}
break;
}
// print cdata sections
case Node.CDATA_SECTION_NODE: {
if ( canonical ) {
out.print(normalize(node.getNodeValue()));
} else {
out.print("<![CDATA[");
out.print(node.getNodeValue());
out.print("]]>");
}
break;
}
// print text
case Node.TEXT_NODE: {
out.print(normalize(node.getNodeValue()));
break;
}
// print processing instruction
case Node.PROCESSING_INSTRUCTION_NODE: {
out.print("<?");
out.print(node.getNodeName());
String data = node.getNodeValue();
if ( data != null && data.length() > 0 ) {
out.print(' ');
out.print(data);
}
out.print("?>");
break;
}
}
if ( type == Node.ELEMENT_NODE ) {
out.print("</");
out.print(node.getNodeName());
out.print('>');
}
out.flush();
} // print(Node)
/** Returns a sorted list of attributes. */
protected Attr[] sortAttributes(NamedNodeMap attrs) {
int len = (attrs != null) ? attrs.getLength() : 0;
Attr array[] = new Attr[len];
for ( int i = 0; i < len; i++ ) {
array[i] = (Attr)attrs.item(i);
}
for ( int i = 0; i < len - 1; i++ ) {
String name = array[i].getNodeName();
int index = i;
for ( int j = i + 1; j < len; j++ ) {
String curName = array[j].getNodeName();
if ( curName.compareTo(name) < 0 ) {
name = curName;
index = j;
}
}
if ( index != i ) {
Attr temp = array[i];
array[i] = array[index];
array[index] = temp;
}
}
return (array);
} // sortAttributes(NamedNodeMap):Attr[]
/** Normalizes the given string. */
protected String normalize(String s) {
StringBuffer str = new StringBuffer();
int len = (s != null) ? s.length() : 0;
for ( int i = 0; i < len; i++ ) {
char ch = s.charAt(i);
switch ( ch ) {
case '<': {
str.append("<");
break;
}
case '>': {
str.append(">");
break;
}
case '&': {
str.append("&");
break;
}
case '"': {
str.append(""");
break;
}
case '\r':
case '\n': {
if ( canonical ) {
str.append("&#");
str.append(Integer.toString(ch));
str.append(';');
break;
}
// else, default append char
}
default: {
str.append(ch);
}
}
}
return (str.toString());
} // normalize(String):String
private static void printValidJavaEncoding() {
System.err.println( " ENCODINGS:" );
System.err.print( " " );
for( int i = 0;
i < MIME2JAVA_ENCODINGS.length; i++) {
System.err.print( MIME2JAVA_ENCODINGS[i] + " " );
if( (i % 7 ) == 0 ){
System.err.println();
System.err.print( " " );
}
}
} // printJavaEncoding()
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/util/MD5Encoder.java
Index: MD5Encoder.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.util;
/**
* Encode an MD5 digest into a String.
* <p>
* The 128 bit MD5 hash is converted into a 32 character long String.
* Each character of the String is the hexadecimal representation of 4 bits
* of the digest.
*
* @author Remy Maucherat
* @version $Revision: 1.1 $ $Date: 2000/11/03 21:27:47 $
*/
public final class MD5Encoder {
// ----------------------------------------------------- Instance Variables
private static final char[] hexadecimal =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
// --------------------------------------------------------- Public Methods
/**
* Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
*
* @param binaryData Array containing the digest
* @return Encoded MD5, or null if encoding failed
*/
public String encode( byte[] binaryData ) {
if (binaryData.length != 16)
return null;
char[] buffer = new char[32];
for (int i=0; i<16; i++) {
int low = (int) (binaryData[i] & 0x0f);
int high = (int) ((binaryData[i] & 0xf0) >> 4);
buffer[i*2] = hexadecimal[high];
buffer[i*2 + 1] = hexadecimal[low];
}
return new String(buffer);
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/util/MIME2Java.java
Index: MIME2Java.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.tomcat.webdav.util;
import java.util.*;
/**
* MIME2Java is a convenience class which handles conversions between MIME charset names
* and Java encoding names.
* <p>The supported XML encodings are the intersection of XML-supported code sets and those
* supported in JDK 1.1.
* <p>MIME charset names are used on <var>xmlEncoding</var> parameters to methods such
* as <code>TXDocument#setEncoding</code> and <code>DTD#setEncoding</code>.
* <p>Java encoding names are used on <var>encoding</var> parameters to
* methods such as <code>TXDocument#printWithFormat</code> and <code>DTD#printExternal</code>.
* <P>
* <TABLE BORDER="0" WIDTH="100%">
* <TR>
* <TD WIDTH="33%">
* <P ALIGN="CENTER"><B>Common Name</B>
* </TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER"><B>Use this name in XML files</B>
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER"><B>Name Type</B>
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B>
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">8 bit Unicode</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">UTF-8
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">UTF8
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin 1</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-1
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-1
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin 2</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-2
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-2
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin 3</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-3
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-3
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin 4</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-4
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-4
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin Cyrillic</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-5
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-5
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin Arabic</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-6
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-6
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin Greek</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-7
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-7
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin Hebrew</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-8
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-8
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">ISO Latin 5</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ISO-8859-9
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">ISO-8859-9
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: US</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-us
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp037
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Canada</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-ca
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp037
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Netherlands</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-nl
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp037
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Denmark</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-dk
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp277
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Norway</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-no
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp277
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Finland</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-fi
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp278
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Sweden</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-se
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp278
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Italy</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-it
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp280
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-es
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp284
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Great Britain</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-gb
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp285
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: France</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-fr
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp297
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Arabic</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-ar1
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp420
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Hebrew</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-he
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp424
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Switzerland</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-ch
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp500
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Roece</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-roece
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp870
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Yogoslavia</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-yu
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp870
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Iceland</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-is
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp871
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">EBCDIC: Urdu</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">ebcdic-cp-ar2
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">IANA
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">cp918
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">gb2312
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">GB2312
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">euc-jp
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">eucjis
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Japanese: iso-2022-jp</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">iso-2020-jp
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">JIS
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Japanese: Shift JIS</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">Shift_JIS
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">SJIS
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Chinese: Big5</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">Big5
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">Big5
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">euc-kr
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">iso2022kr
* </TD>
* </TR>
* <TR>
* <TD WIDTH="33%">Cyrillic</TD>
* <TD WIDTH="15%">
* <P ALIGN="CENTER">koi8-r
* </TD>
* <TD WIDTH="12%">
* <P ALIGN="CENTER">MIME
* </TD>
* <TD WIDTH="31%">
* <P ALIGN="CENTER">koi8-r
* </TD>
* </TR>
* </TABLE>
*
* @version
* @author TAMURA Kent <kent@trl.ibm.co.jp>
*/
public class MIME2Java {
static private Hashtable s_enchash;
static private Hashtable s_revhash;
static {
s_enchash = new Hashtable();
// <preferred MIME name>, <Java encoding name>
s_enchash.put("UTF-8", "UTF8");
s_enchash.put("US-ASCII", "8859_1"); // ?
s_enchash.put("ISO-8859-1", "8859_1");
s_enchash.put("ISO-8859-2", "8859_2");
s_enchash.put("ISO-8859-3", "8859_3");
s_enchash.put("ISO-8859-4", "8859_4");
s_enchash.put("ISO-8859-5", "8859_5");
s_enchash.put("ISO-8859-6", "8859_6");
s_enchash.put("ISO-8859-7", "8859_7");
s_enchash.put("ISO-8859-8", "8859_8");
s_enchash.put("ISO-8859-9", "8859_9");
s_enchash.put("ISO-2022-JP", "JIS");
s_enchash.put("SHIFT_JIS", "SJIS");
s_enchash.put("EUC-JP", "EUCJIS");
s_enchash.put("GB2312", "GB2312");
s_enchash.put("BIG5", "Big5");
s_enchash.put("EUC-KR", "KSC5601");
s_enchash.put("ISO-2022-KR", "ISO2022KR");
s_enchash.put("KOI8-R", "KOI8_R");
s_enchash.put("EBCDIC-CP-US", "CP037");
s_enchash.put("EBCDIC-CP-CA", "CP037");
s_enchash.put("EBCDIC-CP-NL", "CP037");
s_enchash.put("EBCDIC-CP-DK", "CP277");
s_enchash.put("EBCDIC-CP-NO", "CP277");
s_enchash.put("EBCDIC-CP-FI", "CP278");
s_enchash.put("EBCDIC-CP-SE", "CP278");
s_enchash.put("EBCDIC-CP-IT", "CP280");
s_enchash.put("EBCDIC-CP-ES", "CP284");
s_enchash.put("EBCDIC-CP-GB", "CP285");
s_enchash.put("EBCDIC-CP-FR", "CP297");
s_enchash.put("EBCDIC-CP-AR1", "CP420");
s_enchash.put("EBCDIC-CP-HE", "CP424");
s_enchash.put("EBCDIC-CP-CH", "CP500");
s_enchash.put("EBCDIC-CP-ROECE", "CP870");
s_enchash.put("EBCDIC-CP-YU", "CP870");
s_enchash.put("EBCDIC-CP-IS", "CP871");
s_enchash.put("EBCDIC-CP-AR2", "CP918");
// j:CNS11643 -> EUC-TW?
// ISO-2022-CN? ISO-2022-CN-EXT?
s_revhash = new Hashtable();
// <Java encoding name>, <preferred MIME name>
s_revhash.put("UTF8", "UTF-8");
//s_revhash.put("8859_1", "US-ASCII"); // ?
s_revhash.put("8859_1", "ISO-8859-1");
s_revhash.put("8859_2", "ISO-8859-2");
s_revhash.put("8859_3", "ISO-8859-3");
s_revhash.put("8859_4", "ISO-8859-4");
s_revhash.put("8859_5", "ISO-8859-5");
s_revhash.put("8859_6", "ISO-8859-6");
s_revhash.put("8859_7", "ISO-8859-7");
s_revhash.put("8859_8", "ISO-8859-8");
s_revhash.put("8859_9", "ISO-8859-9");
s_revhash.put("JIS", "ISO-2022-JP");
s_revhash.put("SJIS", "Shift_JIS");
s_revhash.put("EUCJIS", "EUC-JP");
s_revhash.put("GB2312", "GB2312");
s_revhash.put("BIG5", "Big5");
s_revhash.put("KSC5601", "EUC-KR");
s_revhash.put("ISO2022KR", "ISO-2022-KR");
s_revhash.put("KOI8_R", "KOI8-R");
s_revhash.put("CP037", "EBCDIC-CP-US");
s_revhash.put("CP037", "EBCDIC-CP-CA");
s_revhash.put("CP037", "EBCDIC-CP-NL");
s_revhash.put("CP277", "EBCDIC-CP-DK");
s_revhash.put("CP277", "EBCDIC-CP-NO");
s_revhash.put("CP278", "EBCDIC-CP-FI");
s_revhash.put("CP278", "EBCDIC-CP-SE");
s_revhash.put("CP280", "EBCDIC-CP-IT");
s_revhash.put("CP284", "EBCDIC-CP-ES");
s_revhash.put("CP285", "EBCDIC-CP-GB");
s_revhash.put("CP297", "EBCDIC-CP-FR");
s_revhash.put("CP420", "EBCDIC-CP-AR1");
s_revhash.put("CP424", "EBCDIC-CP-HE");
s_revhash.put("CP500", "EBCDIC-CP-CH");
s_revhash.put("CP870", "EBCDIC-CP-ROECE");
s_revhash.put("CP870", "EBCDIC-CP-YU");
s_revhash.put("CP871", "EBCDIC-CP-IS");
s_revhash.put("CP918", "EBCDIC-CP-AR2");
}
private MIME2Java() {
}
/**
* Convert a MIME charset name, also known as an XML encoding name, to a Java encoding name.
* @param mimeCharsetName Case insensitive MIME charset name: <code>UTF-8, US-ASCII, ISO-8859-1,
* ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
* ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-2022-JP, Shift_JIS,
* EUC-JP, GB2312, Big5, EUC-KR, ISO-2022-KR, KOI8-R,
* EBCDIC-CP-US, EBCDIC-CP-CA, EBCDIC-CP-NL, EBCDIC-CP-DK,
* EBCDIC-CP-NO, EBCDIC-CP-FI, EBCDIC-CP-SE, EBCDIC-CP-IT,
* EBCDIC-CP-ES, EBCDIC-CP-GB, EBCDIC-CP-FR, EBCDIC-CP-AR1,
* EBCDIC-CP-HE, EBCDIC-CP-CH, EBCDIC-CP-ROECE, EBCDIC-CP-YU,
* EBCDIC-CP-IS and EBCDIC-CP-AR2</code>.
* @return Java encoding name, or <var>null</var> if <var>mimeCharsetName</var>
* is unknown.
* @see #reverse
*/
public static String convert(String mimeCharsetName) {
return (String)s_enchash.get(mimeCharsetName.toUpperCase());
}
/**
* Convert a Java encoding name to MIME charset name.
* Available values of <i>encoding</i> are "UTF8", "8859_1", "8859_2", "8859_3", "8859_4",
* "8859_5", "8859_6", "8859_7", "8859_8", "8859_9", "JIS", "SJIS", "EUCJIS",
* "GB2312", "BIG5", "KSC5601", "ISO2022KR", "KOI8_R", "CP037", "CP277", "CP278",
* "CP280", "CP284", "CP285", "CP297", "CP420", "CP424", "CP500", "CP870", "CP871" and "CP918".
* @param encoding Case insensitive Java encoding name: <code>UTF8, 8859_1, 8859_2, 8859_3,
* 8859_4, 8859_5, 8859_6, 8859_7, 8859_8, 8859_9, JIS, SJIS, EUCJIS,
* GB2312, BIG5, KSC5601, ISO2022KR, KOI8_R, CP037, CP277, CP278,
* CP280, CP284, CP285, CP297, CP420, CP424, CP500, CP870, CP871
* and CP918</code>.
* @return MIME charset name, or <var>null</var> if <var>encoding</var> is unknown.
* @see #convert
*/
public static String reverse(String encoding) {
return (String)s_revhash.get(encoding.toUpperCase());
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/util/StringManager.java
Index: StringManager.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.util;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* An internationalization / localization helper class which reduces
* the bother of handling ResourceBundles and takes care of the
* common cases of message formating which otherwise require the
* creation of Object arrays and such.
*
* <p>The StringManager operates on a package basis. One StringManager
* per package can be created and accessed via the getManager method
* call.
*
* <p>The StringManager will look for a ResourceBundle named by
* the package name given plus the suffix of "LocalStrings". In
* practice, this means that the localized information will be contained
* in a LocalStrings.properties file located in the package
* directory of the classpath.
*
* <p>Please see the documentation for java.util.ResourceBundle for
* more information.
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class StringManager {
/**
* The ResourceBundle for this StringManager.
*/
private ResourceBundle bundle;
/**
* Creates a new StringManager for a given package. This is a
* private method and all access to it is arbitrated by the
* static getManager method call so that only one StringManager
* per package will be created.
*
* @param packageName Name of package to create StringManager for.
*/
private StringManager(String packageName) {
String bundleName = packageName + ".LocalStrings";
bundle = ResourceBundle.getBundle(bundleName);
}
/**
* Get a string from the underlying resource bundle.
*
* @param key
*/
public String getString(String key) {
if (key == null) {
String msg = "key is null";
throw new NullPointerException(msg);
}
String str = null;
try {
str = bundle.getString(key);
} catch (MissingResourceException mre) {
str = "Cannot find message associated with key '" + key + "'";
}
return str;
}
/**
* Get a string from the underlying resource bundle and format
* it with the given set of arguments.
*
* @param key
* @param args
*/
public String getString(String key, Object[] args) {
String iString = null;
String value = getString(key);
// this check for the runtime exception is some pre 1.1.6
// VM's don't do an automatic toString() on the passed in
// objects and barf out
try {
// ensure the arguments are not null so pre 1.2 VM's don't barf
Object nonNullArgs[] = args;
for (int i=0; i<args.length; i++) {
if (args[i] == null) {
if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();
nonNullArgs[i] = "null";
}
}
iString = MessageFormat.format(value, nonNullArgs);
} catch (IllegalArgumentException iae) {
StringBuffer buf = new StringBuffer();
buf.append(value);
for (int i = 0; i < args.length; i++) {
buf.append(" arg[" + i + "]=" + args[i]);
}
iString = buf.toString();
}
return iString;
}
/**
* Get a string from the underlying resource bundle and format it
* with the given object argument. This argument can of course be
* a String object.
*
* @param key
* @param arg
*/
public String getString(String key, Object arg) {
Object[] args = new Object[] {arg};
return getString(key, args);
}
/**
* Get a string from the underlying resource bundle and format it
* with the given object arguments. These arguments can of course
* be String objects.
*
* @param key
* @param arg1
* @param arg2
*/
public String getString(String key, Object arg1, Object arg2) {
Object[] args = new Object[] {arg1, arg2};
return getString(key, args);
}
/**
* Get a string from the underlying resource bundle and format it
* with the given object arguments. These arguments can of course
* be String objects.
*
* @param key
* @param arg1
* @param arg2
* @param arg3
*/
public String getString(String key, Object arg1, Object arg2,
Object arg3) {
Object[] args = new Object[] {arg1, arg2, arg3};
return getString(key, args);
}
/**
* Get a string from the underlying resource bundle and format it
* with the given object arguments. These arguments can of course
* be String objects.
*
* @param key
* @param arg1
* @param arg2
* @param arg3
* @param arg4
*/
public String getString(String key, Object arg1, Object arg2,
Object arg3, Object arg4) {
Object[] args = new Object[] {arg1, arg2, arg3, arg4};
return getString(key, args);
}
// --------------------------------------------------------------
// STATIC SUPPORT METHODS
// --------------------------------------------------------------
private static Hashtable managers = new Hashtable();
/**
* Get the StringManager for a particular package. If a manager for
* a package already exists, it will be reused, else a new
* StringManager will be created and returned.
*
* @param packageName
*/
public synchronized static StringManager getManager(String packageName) {
StringManager mgr = (StringManager)managers.get(packageName);
if (mgr == null) {
mgr = new StringManager(packageName);
managers.put(packageName, mgr);
}
return mgr;
}
}
1.1 jakarta-tomcat/src/webdav/org/apache/tomcat/webdav/util/XMLWriter.java
Index: XMLWriter.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.webdav.util;
/**
* XMLWriter helper class.
*
* @author Remy Maucherat
*/
public class XMLWriter {
// -------------------------------------------------------------- Constants
/**
* Opening tag.
*/
public static final int OPENING = 0;
/**
* Closing tag.
*/
public static final int CLOSING = 1;
/**
* Element with no content.
*/
public static final int NO_CONTENT = 2;
// ----------------------------------------------------- Instance Variables
/**
* Buffer.
*/
protected StringBuffer buffer;
// ----------------------------------------------------------- Constructors
/**
* Constructor.
*/
public XMLWriter() {
buffer = new StringBuffer();
}
// --------------------------------------------------------- Public Methods
/**
* Retrieve generated XML.
*
* @return String containing the generated XML
*/
public String toString() {
return buffer.toString();
}
/**
* Write property to the XML.
*
* @param namespace Namespace
* @param namespaceInfo Namespace info
* @param name Property name
* @param value Property value
*/
public void writeProperty(String namespace, String namespaceInfo,
String name, String value) {
writeElement(namespace, namespaceInfo, name, OPENING);
buffer.append(value);
writeElement(namespace, namespaceInfo, name, CLOSING);
}
/**
* Write property to the XML.
*
* @param namespace Namespace
* @param name Property name
* @param value Property value
*/
public void writeProperty(String namespace, String name, String value) {
writeElement(namespace, name, OPENING);
buffer.append(value);
writeElement(namespace, name, CLOSING);
}
/**
* Write property to the XML.
*
* @param namespace Namespace
* @param name Property name
*/
public void writeProperty(String namespace, String name) {
writeElement(namespace, name, NO_CONTENT);
}
/**
* Write an element.
*
* @param name Element name
* @param namespace Namespace abbreviation
* @param type Element type
*/
public void writeElement(String namespace, String name, int type) {
writeElement(namespace, null, name, type);
}
/**
* Write an element.
*
* @param namespace Namespace abbreviation
* @param namespaceInfo Namespace info
* @param name Element name
* @param type Element type
*/
public void writeElement(String namespace, String namespaceInfo,
String name, int type) {
if ((namespace != null) && (namespace.length() > 0)) {
switch (type) {
case OPENING:
if (namespaceInfo != null) {
buffer.append("<" + namespace + ":" + name + " xmlns:"
+ namespace + "=\""
+ namespaceInfo + "\">");
} else {
buffer.append("<" + namespace + ":" + name + ">");
}
break;
case CLOSING:
buffer.append("</" + namespace + ":" + name + ">\n");
break;
case NO_CONTENT:
default:
if (namespaceInfo != null) {
buffer.append("<" + namespace + ":" + name + " xmlns:"
+ namespace + "=\""
+ namespaceInfo + "\"/>");
} else {
buffer.append("<" + namespace + ":" + name + "/>");
}
break;
}
} else {
switch (type) {
case OPENING:
buffer.append("<" + name + ">");
break;
case CLOSING:
buffer.append("</" + name + ">\n");
break;
case NO_CONTENT:
default:
buffer.append("<" + name + "/>");
break;
}
}
}
/**
* Write text.
*
* @param text Text to append
*/
public void writeText(String text) {
buffer.append(text);
}
/**
* Write XML Header.
*/
public void writeXMLHeader() {
buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
}
}