commit message
This commit is contained in:
commit
21281d05fe
|
@ -0,0 +1,33 @@
|
||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
|
@ -0,0 +1,4 @@
|
||||||
|
FROM java:8
|
||||||
|
VOLUME /tmp
|
||||||
|
ADD webdav-teambition.jar /webdav-teambition.jar
|
||||||
|
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/webdav-teambition.jar"]
|
|
@ -0,0 +1,310 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`which java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||||
|
fi
|
||||||
|
if [ -n "$MVNW_REPOURL" ]; then
|
||||||
|
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||||
|
else
|
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||||
|
fi
|
||||||
|
while IFS="=" read key value; do
|
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Downloading from: $jarUrl"
|
||||||
|
fi
|
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
if $cygwin; then
|
||||||
|
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found wget ... using wget"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
wget "$jarUrl" -O "$wrapperJarPath"
|
||||||
|
else
|
||||||
|
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found curl ... using curl"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
else
|
||||||
|
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Falling back to using Java to download"
|
||||||
|
fi
|
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
# For Cygwin, switch paths to Windows format before running javac
|
||||||
|
if $cygwin; then
|
||||||
|
javaClass=`cygpath --path --windows "$javaClass"`
|
||||||
|
fi
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
# Compiling the Java class
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
# Running the downloader
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Running MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
fi
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
# work with both Windows and non-Windows executions.
|
||||||
|
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||||
|
export MAVEN_CMD_LINE_ARGS
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
|
@ -0,0 +1,182 @@
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Maven Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM M2_HOME - location of maven2's installed home dir
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||||
|
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||||
|
|
||||||
|
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
if exist %WRAPPER_JAR% (
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Found %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
if not "%MVNW_REPOURL%" == "" (
|
||||||
|
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||||
|
)
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||||
|
echo Downloading from: %DOWNLOAD_URL%
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$webclient = new-object System.Net.WebClient;"^
|
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||||
|
"}"^
|
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||||
|
"}"
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Finished downloading %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@REM End of extension
|
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
@REM work with both Windows and non-Windows executions.
|
||||||
|
set MAVEN_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||||
|
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
exit /B %ERROR_CODE%
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.4.1</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.github.zxbu</groupId>
|
||||||
|
<artifactId>webdav-teambition</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>webdav-teambition</name>
|
||||||
|
<description>Demo project for Spring Boot</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>3.14.9</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.github.zxbu.webdavteambition;
|
||||||
|
|
||||||
|
import com.github.zxbu.webdavteambition.store.TeambitionFileSystemStore;
|
||||||
|
import net.sf.webdav.LocalFileSystemStore;
|
||||||
|
import net.sf.webdav.WebdavServlet;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.boot.web.servlet.support.ErrorPageFilter;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class WebdavTeambitionApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(WebdavTeambitionApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServletRegistrationBean<WebdavServlet> myServlet(){
|
||||||
|
ServletRegistrationBean<WebdavServlet> servletRegistrationBean = new ServletRegistrationBean<>(new WebdavServlet(), "/*");
|
||||||
|
Map<String, String> inits = new LinkedHashMap<>();
|
||||||
|
inits.put("ResourceHandlerImplementation", TeambitionFileSystemStore.class.getName());
|
||||||
|
// inits.put("ResourceHandlerImplementation", LocalFileSystemStore.class.getName());
|
||||||
|
inits.put("rootpath", "./");
|
||||||
|
inits.put("storeDebug", "1");
|
||||||
|
servletRegistrationBean.setInitParameters(inits);
|
||||||
|
return servletRegistrationBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ErrorPageFilter errorPageFilter() {
|
||||||
|
return new ErrorPageFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter) {
|
||||||
|
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
|
||||||
|
filterRegistrationBean.setFilter(filter);
|
||||||
|
filterRegistrationBean.setEnabled(false);
|
||||||
|
return filterRegistrationBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
package com.github.zxbu.webdavteambition.client;
|
||||||
|
|
||||||
|
import com.github.zxbu.webdavteambition.config.TeambitionProperties;
|
||||||
|
import com.github.zxbu.webdavteambition.util.JsonUtil;
|
||||||
|
import okhttp3.*;
|
||||||
|
import okio.BufferedSink;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TeambitionClient {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionClient.class);
|
||||||
|
private OkHttpClient okHttpClient;
|
||||||
|
private TeambitionProperties teambitionProperties;
|
||||||
|
|
||||||
|
public TeambitionClient(OkHttpClient okHttpClient, TeambitionProperties teambitionProperties) {
|
||||||
|
this.okHttpClient = okHttpClient;
|
||||||
|
this.teambitionProperties = teambitionProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
if (getOrgId() == null || getRootId() == null || getDriveId() == null || getSpaceId() == null) {
|
||||||
|
String personalJson = get("https://www.teambition.com/api/organizations/personal", Collections.emptyMap());
|
||||||
|
String orgId = (String) JsonUtil.getJsonNodeValue(personalJson, "_id");
|
||||||
|
teambitionProperties.setOrgId(orgId);
|
||||||
|
String memberId = (String) JsonUtil.getJsonNodeValue(personalJson, "_creatorId");
|
||||||
|
|
||||||
|
String orgJson = get("/pan/api/orgs/" + orgId, Collections.singletonMap("orgId", orgId));
|
||||||
|
String driveId = (String) JsonUtil.getJsonNodeValue(orgJson, "data.driveId");
|
||||||
|
teambitionProperties.setDriveId(driveId);
|
||||||
|
|
||||||
|
Map<String, String> params = new LinkedHashMap<>();
|
||||||
|
params.put("orgId", orgId);
|
||||||
|
params.put("memberId", memberId);
|
||||||
|
String spacesJson = get("/pan/api/spaces", params);
|
||||||
|
String rootId = (String) JsonUtil.getJsonNodeValue(spacesJson, "[0].rootId");
|
||||||
|
String spaceId = (String) JsonUtil.getJsonNodeValue(spacesJson, "[0].spaceId");
|
||||||
|
teambitionProperties.setRootId(rootId);
|
||||||
|
teambitionProperties.setSpaceId(spaceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return teambitionProperties.getOrgId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return teambitionProperties.getDriveId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpaceId() {
|
||||||
|
return teambitionProperties.getSpaceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRootId() {
|
||||||
|
return teambitionProperties.getRootId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream download(String url) {
|
||||||
|
Request request = new Request.Builder().url(url).build();
|
||||||
|
Response response = null;
|
||||||
|
try {
|
||||||
|
response = okHttpClient.newCall(request).execute();
|
||||||
|
return response.body().byteStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upload(String url, byte[] bytes, final int offset, final int byteCount) {
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.put(RequestBody.create(MediaType.parse(""), bytes, offset, byteCount))
|
||||||
|
.url(url).build();
|
||||||
|
try (Response response = okHttpClient.newCall(request).execute()){
|
||||||
|
LOGGER.info("post {}, code {}", url, response.code());
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
LOGGER.error("请求失败,url={}, code={}, body={}", url, response.code(), response.body().string());
|
||||||
|
throw new RuntimeException("请求失败:" + url);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String post(String url, Object body) {
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonUtil.toJson(body)))
|
||||||
|
.url(getTotalUrl(url)).build();
|
||||||
|
try (Response response = okHttpClient.newCall(request).execute()){
|
||||||
|
LOGGER.info("post {}, code {}", url, response.code());
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
LOGGER.error("请求失败,url={}, code={}, body={}", url, response.code(), response.body().string());
|
||||||
|
throw new RuntimeException("请求失败:" + url);
|
||||||
|
}
|
||||||
|
return toString(response.body());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String put(String url, Object body) {
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.put(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonUtil.toJson(body)))
|
||||||
|
.url(getTotalUrl(url)).build();
|
||||||
|
try (Response response = okHttpClient.newCall(request).execute()){
|
||||||
|
LOGGER.info("put {}, code {}", url, response.code());
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
LOGGER.error("请求失败,url={}, code={}, body={}", url, response.code(), response.body().string());
|
||||||
|
throw new RuntimeException("请求失败:" + url);
|
||||||
|
}
|
||||||
|
return toString(response.body());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(String url, Map<String, String> params) {
|
||||||
|
try {
|
||||||
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(getTotalUrl(url)).newBuilder();
|
||||||
|
params.forEach(urlBuilder::addQueryParameter);
|
||||||
|
|
||||||
|
Request request = new Request.Builder().get().url(urlBuilder.build()).build();
|
||||||
|
try (Response response = okHttpClient.newCall(request).execute()){
|
||||||
|
LOGGER.info("get {}, code {}", urlBuilder.build(), response.code());
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
throw new RuntimeException("请求失败:" + urlBuilder.build().toString());
|
||||||
|
}
|
||||||
|
return toString(response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toString(ResponseBody responseBody) throws IOException {
|
||||||
|
if (responseBody == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return responseBody.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTotalUrl(String url) {
|
||||||
|
if (url.startsWith("http")) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return teambitionProperties.getUrl() + url;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.github.zxbu.webdavteambition.config;
|
||||||
|
|
||||||
|
import com.github.zxbu.webdavteambition.client.TeambitionClient;
|
||||||
|
import com.github.zxbu.webdavteambition.store.TeambitionFileSystemStore;
|
||||||
|
import okhttp3.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(TeambitionProperties.class)
|
||||||
|
public class TeambitionAutoConfig implements ApplicationContextAware {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionAutoConfig.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TeambitionProperties teambitionProperties;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TeambitionClient teambitionClient(ApplicationContext applicationContext) throws Exception {
|
||||||
|
|
||||||
|
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
|
||||||
|
@Override
|
||||||
|
public Response intercept(Chain chain) throws IOException {
|
||||||
|
Request request = chain.request();
|
||||||
|
request = request.newBuilder()
|
||||||
|
.removeHeader("User-Agent")
|
||||||
|
.addHeader("User-Agent", teambitionProperties.getAgent())
|
||||||
|
.build();
|
||||||
|
return chain.proceed(request);
|
||||||
|
}
|
||||||
|
}).cookieJar(new CookieJar() {
|
||||||
|
@Override
|
||||||
|
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||||||
|
if (StringUtils.hasLength(teambitionProperties.getCookies())) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||||
|
String cookies = teambitionProperties.getCookies();
|
||||||
|
String[] cookieSplit = cookies.split("; ");
|
||||||
|
List<Cookie> cookieList = new ArrayList<>(cookieSplit.length);
|
||||||
|
for (String cookie : cookieSplit) {
|
||||||
|
Cookie parse = Cookie.parse(url, cookie);
|
||||||
|
cookieList.add(parse);
|
||||||
|
}
|
||||||
|
return cookieList;
|
||||||
|
}
|
||||||
|
}).build();
|
||||||
|
TeambitionClient teambitionClient = new TeambitionClient(okHttpClient, teambitionProperties);
|
||||||
|
try (Response response = okHttpClient.newCall(new Request.Builder().get().url(teambitionProperties.getUrl() + "/pan/api").build()).execute()) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
LOGGER.info("TeambitionClient 启动成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
teambitionClient.init();
|
||||||
|
return teambitionClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
TeambitionFileSystemStore.setApplicationContext(applicationContext);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.github.zxbu.webdavteambition.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "teambition", ignoreUnknownFields = true)
|
||||||
|
public class TeambitionProperties {
|
||||||
|
private String url = "https://pan.teambition.com";
|
||||||
|
private String cookies;
|
||||||
|
private String agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36";
|
||||||
|
private String orgId;
|
||||||
|
private String driveId;
|
||||||
|
private String spaceId;
|
||||||
|
private String rootId;
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCookies() {
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookies(String cookies) {
|
||||||
|
this.cookies = cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpaceId() {
|
||||||
|
return spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAgent() {
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAgent(String agent) {
|
||||||
|
this.agent = agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpaceId(String spaceId) {
|
||||||
|
this.spaceId = spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRootId() {
|
||||||
|
return rootId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootId(String rootId) {
|
||||||
|
this.rootId = rootId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class BaseQuery extends Page{
|
||||||
|
private String orgId;
|
||||||
|
private String driveId;
|
||||||
|
private String spaceId;
|
||||||
|
private String parentId;
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpaceId() {
|
||||||
|
return spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpaceId(String spaceId) {
|
||||||
|
this.spaceId = spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class CreateFileRequest {
|
||||||
|
private String ccpParentId;
|
||||||
|
private String checkNameMode = "refuse";
|
||||||
|
private String driveId;
|
||||||
|
private String name;
|
||||||
|
private String orgId;
|
||||||
|
private String parentId;
|
||||||
|
private String spaceId;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public String getCheckNameMode() {
|
||||||
|
return checkNameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckNameMode(String checkNameMode) {
|
||||||
|
this.checkNameMode = checkNameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpaceId() {
|
||||||
|
return spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpaceId(String spaceId) {
|
||||||
|
this.spaceId = spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCcpParentId() {
|
||||||
|
return ccpParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpParentId(String ccpParentId) {
|
||||||
|
this.ccpParentId = ccpParentId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public enum FileType {
|
||||||
|
folder, file;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MoveRequest {
|
||||||
|
private String driveId;
|
||||||
|
private String parentId;
|
||||||
|
private String orgId;
|
||||||
|
private boolean sameLevel = false;
|
||||||
|
private List<MoveRequestId> ids;
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSameLevel() {
|
||||||
|
return sameLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSameLevel(boolean sameLevel) {
|
||||||
|
this.sameLevel = sameLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MoveRequestId> getIds() {
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIds(List<MoveRequestId> ids) {
|
||||||
|
this.ids = ids;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class MoveRequestId {
|
||||||
|
private String ccpFileId;
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
public String getCcpFileId() {
|
||||||
|
return ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpFileId(String ccpFileId) {
|
||||||
|
this.ccpFileId = ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class NodeQuery extends BaseQuery {
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class Page {
|
||||||
|
private int offset;
|
||||||
|
private int limit;
|
||||||
|
private String orderBy;
|
||||||
|
private String orderDirection;
|
||||||
|
|
||||||
|
public int getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOffset(int offset) {
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLimit() {
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimit(int limit) {
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderBy() {
|
||||||
|
return orderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderBy(String orderBy) {
|
||||||
|
this.orderBy = orderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderDirection() {
|
||||||
|
return orderDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderDirection(String orderDirection) {
|
||||||
|
this.orderDirection = orderDirection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class PathInfo {
|
||||||
|
private String path;
|
||||||
|
private String parentPath;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentPath() {
|
||||||
|
return parentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentPath(String parentPath) {
|
||||||
|
this.parentPath = parentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RemoveRequest {
|
||||||
|
private String orgId;
|
||||||
|
private List<String> nodeIds;
|
||||||
|
|
||||||
|
public List<String> getNodeIds() {
|
||||||
|
return nodeIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeIds(List<String> nodeIds) {
|
||||||
|
this.nodeIds = nodeIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class RenameRequest {
|
||||||
|
private String ccpFileId;
|
||||||
|
private String driveId;
|
||||||
|
private String name;
|
||||||
|
private String orgId;
|
||||||
|
|
||||||
|
public String getCcpFileId() {
|
||||||
|
return ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpFileId(String ccpFileId) {
|
||||||
|
this.ccpFileId = ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class UploadFinalRequest {
|
||||||
|
private String ccpFileId;
|
||||||
|
private String driveId;
|
||||||
|
private String nodeId;
|
||||||
|
private String orgId;
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeId(String nodeId) {
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUploadId() {
|
||||||
|
return uploadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploadId(String uploadId) {
|
||||||
|
this.uploadId = uploadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCcpFileId() {
|
||||||
|
return ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpFileId(String ccpFileId) {
|
||||||
|
this.ccpFileId = ccpFileId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
public class UploadPreInfo {
|
||||||
|
private String ccpParentId;
|
||||||
|
private int chunkCount;
|
||||||
|
private String contentType = "";
|
||||||
|
private String driveId;
|
||||||
|
private String name;
|
||||||
|
private int size;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public int getChunkCount() {
|
||||||
|
return chunkCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChunkCount(int chunkCount) {
|
||||||
|
this.chunkCount = chunkCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDriveId() {
|
||||||
|
return driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDriveId(String driveId) {
|
||||||
|
this.driveId = driveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(int size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCcpParentId() {
|
||||||
|
return ccpParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpParentId(String ccpParentId) {
|
||||||
|
this.ccpParentId = ccpParentId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UploadPreRequest {
|
||||||
|
private String checkNameMode = "autoRename";
|
||||||
|
private String orgId;
|
||||||
|
private String parentId;
|
||||||
|
private String spaceId;
|
||||||
|
private List<UploadPreInfo> infos;
|
||||||
|
|
||||||
|
public String getCheckNameMode() {
|
||||||
|
return checkNameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckNameMode(String checkNameMode) {
|
||||||
|
this.checkNameMode = checkNameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrgId() {
|
||||||
|
return orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrgId(String orgId) {
|
||||||
|
this.orgId = orgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpaceId() {
|
||||||
|
return spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpaceId(String spaceId) {
|
||||||
|
this.spaceId = spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UploadPreInfo> getInfos() {
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfos(List<UploadPreInfo> infos) {
|
||||||
|
this.infos = infos;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model.result;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ListResult<T> {
|
||||||
|
private List<T> data;
|
||||||
|
|
||||||
|
public List<T> getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(List<T> data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model.result;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class TFile {
|
||||||
|
private String kind;
|
||||||
|
private String nodeId;
|
||||||
|
private String name;
|
||||||
|
private Date created;
|
||||||
|
private Date updated;
|
||||||
|
private String parentId;
|
||||||
|
private String status;
|
||||||
|
private String downloadUrl;
|
||||||
|
private Long size;
|
||||||
|
private String ccpFileId;
|
||||||
|
private String ccpParentFileId;
|
||||||
|
|
||||||
|
public String getKind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKind(String kind) {
|
||||||
|
this.kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeId(String nodeId) {
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(Date created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getUpdated() {
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdated(Date updated) {
|
||||||
|
this.updated = updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDownloadUrl() {
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDownloadUrl(String downloadUrl) {
|
||||||
|
this.downloadUrl = downloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCcpFileId() {
|
||||||
|
return ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpFileId(String ccpFileId) {
|
||||||
|
this.ccpFileId = ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCcpParentFileId() {
|
||||||
|
return ccpParentFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpParentFileId(String ccpParentFileId) {
|
||||||
|
this.ccpParentFileId = ccpParentFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(Long size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.github.zxbu.webdavteambition.model.result;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UploadPreResult {
|
||||||
|
private String ccpFileId;
|
||||||
|
private String nodeId;
|
||||||
|
private String name;
|
||||||
|
private String kind;
|
||||||
|
private String uploadId;
|
||||||
|
private List<String> uploadUrl;
|
||||||
|
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeId(String nodeId) {
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCcpFileId() {
|
||||||
|
return ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCcpFileId(String ccpFileId) {
|
||||||
|
this.ccpFileId = ccpFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKind(String kind) {
|
||||||
|
this.kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUploadId() {
|
||||||
|
return uploadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploadId(String uploadId) {
|
||||||
|
this.uploadId = uploadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUploadUrl() {
|
||||||
|
return uploadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploadUrl(List<String> uploadUrl) {
|
||||||
|
this.uploadUrl = uploadUrl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
package com.github.zxbu.webdavteambition.store;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.github.zxbu.webdavteambition.client.TeambitionClient;
|
||||||
|
import com.github.zxbu.webdavteambition.model.*;
|
||||||
|
import com.github.zxbu.webdavteambition.model.result.ListResult;
|
||||||
|
import com.github.zxbu.webdavteambition.model.result.TFile;
|
||||||
|
import com.github.zxbu.webdavteambition.model.result.UploadPreResult;
|
||||||
|
import com.github.zxbu.webdavteambition.util.JsonUtil;
|
||||||
|
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TeambitionClientService {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionClientService.class);
|
||||||
|
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private static String rootPath = "/";
|
||||||
|
private static int chunkSize = 10485760; // 10MB
|
||||||
|
private TFile rootTFile = null;
|
||||||
|
private Map<String, TFile> nodeIdMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final TeambitionClient client;
|
||||||
|
|
||||||
|
public TeambitionClientService(TeambitionClient teambitionClient) {
|
||||||
|
this.client = teambitionClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TFile> getTFiles(String nodeId) {
|
||||||
|
NodeQuery nodeQuery = new NodeQuery();
|
||||||
|
nodeQuery.setOrgId(client.getOrgId());
|
||||||
|
nodeQuery.setOffset(0);
|
||||||
|
nodeQuery.setLimit(10000);
|
||||||
|
nodeQuery.setOrderBy("updateTime");
|
||||||
|
nodeQuery.setOrderDirection("desc");
|
||||||
|
nodeQuery.setDriveId(client.getDriveId());
|
||||||
|
nodeQuery.setSpaceId(client.getSpaceId());
|
||||||
|
nodeQuery.setParentId(nodeId);
|
||||||
|
String json = client.get("/pan/api/nodes", toMap(nodeQuery));
|
||||||
|
ListResult<TFile> tFileListResult = JsonUtil.readValue(json, new TypeReference<ListResult<TFile>>() {
|
||||||
|
});
|
||||||
|
return tFileListResult.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> toMap(Object o) {
|
||||||
|
try {
|
||||||
|
String json = objectMapper.writeValueAsString(o);
|
||||||
|
Map<String, Object> rawMap = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {
|
||||||
|
});
|
||||||
|
Map<String, String> stringMap = new LinkedHashMap<>();
|
||||||
|
rawMap.forEach((s, o1) -> {
|
||||||
|
if (o1 != null) {
|
||||||
|
stringMap.put(s, o1.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return stringMap;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uploadPre(String path, int size, InputStream inputStream) {
|
||||||
|
path = normalizingPath(path);
|
||||||
|
PathInfo pathInfo = getPathInfo(path);
|
||||||
|
TFile parent = getTFileByPath(pathInfo.getParentPath());
|
||||||
|
if (parent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int chunkCount = (int) Math.ceil(((double) size) / chunkSize); // 进1法
|
||||||
|
|
||||||
|
UploadPreRequest uploadPreRequest = new UploadPreRequest();
|
||||||
|
uploadPreRequest.setOrgId(client.getOrgId());
|
||||||
|
uploadPreRequest.setParentId(parent.getNodeId());
|
||||||
|
uploadPreRequest.setSpaceId(client.getSpaceId());
|
||||||
|
UploadPreInfo uploadPreInfo = new UploadPreInfo();
|
||||||
|
uploadPreInfo.setCcpParentId(parent.getCcpFileId());
|
||||||
|
uploadPreInfo.setDriveId(client.getDriveId());
|
||||||
|
uploadPreInfo.setName(pathInfo.getName());
|
||||||
|
uploadPreInfo.setSize(size);
|
||||||
|
uploadPreInfo.setChunkCount(chunkCount);
|
||||||
|
uploadPreInfo.setType(FileType.file.name());
|
||||||
|
uploadPreRequest.setInfos(Collections.singletonList(uploadPreInfo));
|
||||||
|
LOGGER.info("开始上传文件,文件名:{},总大小:{}, 文件块数量:{}", path, size, chunkCount);
|
||||||
|
|
||||||
|
String json = client.post("/pan/api/nodes/file", uploadPreRequest);
|
||||||
|
List<UploadPreResult> uploadPreResultList = JsonUtil.readValue(json, new TypeReference<List<UploadPreResult>>() {
|
||||||
|
});
|
||||||
|
UploadPreResult uploadPreResult = uploadPreResultList.get(0);
|
||||||
|
List<String> uploadUrl = uploadPreResult.getUploadUrl();
|
||||||
|
LOGGER.info("文件预处理成功,开始上传。文件名:{},上传URL数量:{}", path, uploadUrl.size());
|
||||||
|
|
||||||
|
byte[] buffer = new byte[chunkSize];
|
||||||
|
for (String oneUploadUrl : uploadUrl) {
|
||||||
|
try {
|
||||||
|
int read = IOUtils.read(inputStream, buffer, 0, buffer.length);
|
||||||
|
if (read == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client.upload(oneUploadUrl, buffer, 0, read);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UploadFinalRequest uploadFinalRequest = new UploadFinalRequest();
|
||||||
|
uploadFinalRequest.setCcpFileId(uploadPreResult.getCcpFileId());
|
||||||
|
uploadFinalRequest.setDriveId(client.getDriveId());
|
||||||
|
uploadFinalRequest.setNodeId(uploadPreResult.getNodeId());
|
||||||
|
uploadFinalRequest.setOrgId(client.getOrgId());
|
||||||
|
uploadFinalRequest.setUploadId(uploadPreResult.getUploadId());
|
||||||
|
client.post("/pan/api/nodes/complete", uploadFinalRequest);
|
||||||
|
LOGGER.info("文件上传成功。文件名:{}", path);
|
||||||
|
if (!uploadPreResult.getName().equals(pathInfo.getName())) {
|
||||||
|
LOGGER.info("上传文件名{}与原文件名{}不同,对文件进行重命名", uploadPreResult.getName(), pathInfo.getName());
|
||||||
|
RenameRequest renameRequest = new RenameRequest();
|
||||||
|
renameRequest.setCcpFileId(uploadPreResult.getCcpFileId());
|
||||||
|
renameRequest.setDriveId(client.getDriveId());
|
||||||
|
renameRequest.setOrgId(client.getOrgId());
|
||||||
|
renameRequest.setName(pathInfo.getName());
|
||||||
|
client.put("/pan/api/nodes/" + parent.getNodeId(), renameRequest);
|
||||||
|
}
|
||||||
|
clearCache(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rename(String sourcePath, String newName) {
|
||||||
|
sourcePath = normalizingPath(sourcePath);
|
||||||
|
TFile tFile = getTFileByPath(sourcePath);
|
||||||
|
RenameRequest renameRequest = new RenameRequest();
|
||||||
|
renameRequest.setCcpFileId(tFile.getCcpFileId());
|
||||||
|
renameRequest.setDriveId(client.getDriveId());
|
||||||
|
renameRequest.setOrgId(client.getOrgId());
|
||||||
|
renameRequest.setName(newName);
|
||||||
|
client.put("/pan/api/nodes/" + tFile.getParentId(), renameRequest);
|
||||||
|
clearCache(sourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(String sourcePath, String targetPath) {
|
||||||
|
sourcePath = normalizingPath(sourcePath);
|
||||||
|
targetPath = normalizingPath(targetPath);
|
||||||
|
|
||||||
|
TFile sourceTFile = getTFileByPath(sourcePath);
|
||||||
|
TFile targetTFile = getTFileByPath(targetPath);
|
||||||
|
MoveRequest moveRequest = new MoveRequest();
|
||||||
|
moveRequest.setOrgId(client.getOrgId());
|
||||||
|
moveRequest.setDriveId(client.getDriveId());
|
||||||
|
moveRequest.setParentId(targetTFile.getNodeId());
|
||||||
|
MoveRequestId moveRequestId = new MoveRequestId();
|
||||||
|
moveRequestId.setCcpFileId(sourceTFile.getCcpFileId());
|
||||||
|
moveRequestId.setId(sourceTFile.getNodeId());
|
||||||
|
moveRequest.setIds(Collections.singletonList(moveRequestId));
|
||||||
|
client.post("/pan/api/nodes/move", moveRequest);
|
||||||
|
clearCache(sourcePath);
|
||||||
|
clearCache(targetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String path) {
|
||||||
|
path = normalizingPath(path);
|
||||||
|
TFile tFile = getTFileByPath(path);
|
||||||
|
if (tFile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RemoveRequest removeRequest = new RemoveRequest();
|
||||||
|
removeRequest.setOrgId(client.getOrgId());
|
||||||
|
removeRequest.setNodeIds(Collections.singletonList(tFile.getNodeId()));
|
||||||
|
client.post("/pan/api/nodes/archive", removeRequest);
|
||||||
|
clearCache(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void createFolder(String path) {
|
||||||
|
path = normalizingPath(path);
|
||||||
|
PathInfo pathInfo = getPathInfo(path);
|
||||||
|
TFile parent = getTFileByPath(pathInfo.getParentPath());
|
||||||
|
if (parent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateFileRequest createFileRequest = new CreateFileRequest();
|
||||||
|
createFileRequest.setCcpParentId(parent.getCcpFileId());
|
||||||
|
createFileRequest.setDriveId(client.getDriveId());
|
||||||
|
createFileRequest.setName(pathInfo.getName());
|
||||||
|
createFileRequest.setOrgId(client.getOrgId());
|
||||||
|
createFileRequest.setParentId(parent.getNodeId());
|
||||||
|
createFileRequest.setSpaceId(client.getSpaceId());
|
||||||
|
createFileRequest.setType(FileType.folder.name());
|
||||||
|
client.post("/pan/api/nodes/folder", createFileRequest);
|
||||||
|
clearCache(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TFile getTFileByPath(String path) {
|
||||||
|
path = normalizingPath(path);
|
||||||
|
|
||||||
|
return nodeIdMap.computeIfAbsent(path, this::getNodeIdByPath2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream download(String path) {
|
||||||
|
String downloadUrl = getTFileByPath(path).getDownloadUrl();
|
||||||
|
return client.download(downloadUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TFile getNodeIdByPath2(String path) {
|
||||||
|
if (!StringUtils.hasLength(path)) {
|
||||||
|
path = rootPath;
|
||||||
|
}
|
||||||
|
if (path.equals(rootPath)) {
|
||||||
|
return getRootTFile();
|
||||||
|
}
|
||||||
|
PathInfo pathInfo = getPathInfo(path);
|
||||||
|
TFile tFile = getTFileByPath(pathInfo.getParentPath());
|
||||||
|
if (tFile == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getNodeIdByParentId(tFile.getNodeId(), pathInfo.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PathInfo getPathInfo(String path) {
|
||||||
|
path = normalizingPath(path);
|
||||||
|
if (path.equals(rootPath)) {
|
||||||
|
PathInfo pathInfo = new PathInfo();
|
||||||
|
pathInfo.setPath(path);
|
||||||
|
pathInfo.setName(path);
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
int index = path.lastIndexOf("/");
|
||||||
|
String parentPath = path.substring(0, index + 1);
|
||||||
|
String name = path.substring(index+1);
|
||||||
|
PathInfo pathInfo = new PathInfo();
|
||||||
|
pathInfo.setPath(path);
|
||||||
|
pathInfo.setParentPath(parentPath);
|
||||||
|
pathInfo.setName(name);
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TFile getRootTFile() {
|
||||||
|
if (rootTFile == null) {
|
||||||
|
NodeQuery nodeQuery = new NodeQuery();
|
||||||
|
nodeQuery.setOrgId(client.getOrgId());
|
||||||
|
nodeQuery.setDriveId(client.getDriveId());
|
||||||
|
nodeQuery.setSpaceId(client.getSpaceId());
|
||||||
|
String json = client.get("/pan/api/nodes/" + client.getRootId(), toMap(nodeQuery));
|
||||||
|
rootTFile = JsonUtil.readValue(json, TFile.class);
|
||||||
|
}
|
||||||
|
return rootTFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TFile getNodeIdByParentId(String parentId, String name) {
|
||||||
|
List<TFile> tFiles = getTFiles(parentId);
|
||||||
|
for (TFile tFile : tFiles) {
|
||||||
|
if (tFile.getName().equals(name)) {
|
||||||
|
return tFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizingPath(String path) {
|
||||||
|
path = path.replaceAll("//", "/");
|
||||||
|
if (path.endsWith("/")) {
|
||||||
|
path = path.substring(0, path.length() - 1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearCache(String path) {
|
||||||
|
nodeIdMap.remove(path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
package com.github.zxbu.webdavteambition.store;
|
||||||
|
|
||||||
|
import com.github.zxbu.webdavteambition.model.PathInfo;
|
||||||
|
import com.github.zxbu.webdavteambition.model.result.TFile;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TeambitionFileSystemStore implements IWebdavStore {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionFileSystemStore.class);
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
private static TeambitionClientService teambitionClientService;
|
||||||
|
|
||||||
|
|
||||||
|
public TeambitionFileSystemStore(File file) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setApplicationContext(ApplicationContext applicationContext) {
|
||||||
|
TeambitionFileSystemStore.applicationContext = applicationContext;
|
||||||
|
TeambitionFileSystemStore.teambitionClientService = applicationContext.getBean(TeambitionClientService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
LOGGER.debug("destroy");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ITransaction begin(Principal principal) {
|
||||||
|
LOGGER.debug("begin");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkAuthentication(ITransaction transaction) {
|
||||||
|
LOGGER.debug("checkAuthentication");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commit(ITransaction transaction) {
|
||||||
|
LOGGER.debug("commit");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rollback(ITransaction transaction) {
|
||||||
|
LOGGER.debug("rollback");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createFolder(ITransaction transaction, String folderUri) {
|
||||||
|
LOGGER.info("createFolder {}", folderUri);
|
||||||
|
|
||||||
|
teambitionClientService.createFolder(folderUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createResource(ITransaction transaction, String resourceUri) {
|
||||||
|
LOGGER.info("createResource {}", resourceUri);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getResourceContent(ITransaction transaction, String resourceUri) {
|
||||||
|
LOGGER.debug("getResourceContent: {}", resourceUri);
|
||||||
|
return teambitionClientService.download(resourceUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long setResourceContent(ITransaction transaction, String resourceUri, InputStream content, String contentType, String characterEncoding) {
|
||||||
|
LOGGER.info("setResourceContent {}", resourceUri);
|
||||||
|
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
|
HttpServletRequest request = requestAttributes.getRequest();
|
||||||
|
int contentLength = request.getContentLength();
|
||||||
|
if (contentLength <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
teambitionClientService.uploadPre(resourceUri, contentLength, content);
|
||||||
|
return contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getChildrenNames(ITransaction transaction, String folderUri) {
|
||||||
|
LOGGER.debug("getChildrenNames: {}", folderUri);
|
||||||
|
TFile tFile = teambitionClientService.getTFileByPath(folderUri);
|
||||||
|
List<TFile> tFileList = teambitionClientService.getTFiles(tFile.getNodeId());
|
||||||
|
return tFileList.stream().map(TFile::getName).toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getResourceLength(ITransaction transaction, String path) {
|
||||||
|
LOGGER.debug("getResourceLength: {}", path);
|
||||||
|
TFile tFile = teambitionClientService.getTFileByPath(path);
|
||||||
|
if (tFile == null || tFile.getSize() == null) {
|
||||||
|
return 384;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tFile.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeObject(ITransaction transaction, String uri) {
|
||||||
|
LOGGER.info("removeObject: {}", uri);
|
||||||
|
teambitionClientService.remove(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean moveObject(ITransaction transaction, String destinationPath, String sourcePath) {
|
||||||
|
LOGGER.info("moveObject, destinationPath={}, sourcePath={}", destinationPath, sourcePath);
|
||||||
|
|
||||||
|
PathInfo destinationPathInfo = teambitionClientService.getPathInfo(destinationPath);
|
||||||
|
PathInfo sourcePathInfo = teambitionClientService.getPathInfo(sourcePath);
|
||||||
|
// 名字相同,说明是移动目录
|
||||||
|
if (sourcePathInfo.getName().equals(destinationPathInfo.getName())) {
|
||||||
|
teambitionClientService.move(sourcePath, destinationPathInfo.getParentPath());
|
||||||
|
} else {
|
||||||
|
if (!destinationPathInfo.getParentPath().equals(sourcePathInfo.getParentPath())) {
|
||||||
|
throw new RuntimeException("不支持目录和名字同时修改");
|
||||||
|
}
|
||||||
|
// 名字不同,说明是修改名字。不考虑目录和名字同时修改的情况
|
||||||
|
teambitionClientService.rename(sourcePath, destinationPathInfo.getName());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
||||||
|
LOGGER.debug("getStoredObject: {}", uri);
|
||||||
|
TFile tFile = teambitionClientService.getTFileByPath(uri);
|
||||||
|
if (tFile != null) {
|
||||||
|
StoredObject so = new StoredObject();
|
||||||
|
so.setFolder(tFile.getKind().equalsIgnoreCase("folder"));
|
||||||
|
so.setResourceLength(getResourceLength(transaction, uri));
|
||||||
|
so.setCreationDate(tFile.getCreated());
|
||||||
|
so.setLastModified(tFile.getUpdated());
|
||||||
|
return so;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
package com.github.zxbu.webdavteambition.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JsonUtil {
|
||||||
|
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
public static String toJson(Object o) {
|
||||||
|
try {
|
||||||
|
return objectMapper.writeValueAsString(o);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T readValue(String json, TypeReference<T> valueTypeRef) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(json, valueTypeRef);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static <T> T readValue(String json, Class<T> valueType) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(json, valueType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 忽略未知的字段
|
||||||
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
// 读取不认识的枚举时,当null值处理
|
||||||
|
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
|
||||||
|
|
||||||
|
objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getJsonNodeValue(String json, String path) {
|
||||||
|
try {
|
||||||
|
JsonNode jsonNode = getJsonNode(json);
|
||||||
|
List<PathToken> pathTokens = getPathTokens(path);
|
||||||
|
for (PathToken pathToken : pathTokens) {
|
||||||
|
if (pathToken.getType() == PathType.KEY) {
|
||||||
|
jsonNode = jsonNode.get(pathToken.getValue());
|
||||||
|
}
|
||||||
|
if (pathToken.getType() == PathType.NUMBER) {
|
||||||
|
jsonNode = jsonNode.get(Integer.parseInt(pathToken.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objectMapper.treeToValue(jsonNode, Object.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static List<PathToken> getPathTokens(String path) {
|
||||||
|
List<PathToken> pathTokenList = new ArrayList<>();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean escape = false;
|
||||||
|
for (char c : path.toCharArray()) {
|
||||||
|
if (c == '[') {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
pathTokenList.add(new PathToken(PathType.KEY, sb.toString()));
|
||||||
|
}
|
||||||
|
sb.setLength(0);
|
||||||
|
} else if (c == ']') {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
pathTokenList.add(new PathToken(PathType.NUMBER, sb.toString()));
|
||||||
|
}
|
||||||
|
sb.setLength(0);
|
||||||
|
} else if (c == '.') {
|
||||||
|
if (escape) {
|
||||||
|
sb.append(c);
|
||||||
|
} else {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
pathTokenList.add(new PathToken(PathType.KEY, sb.toString()));
|
||||||
|
}
|
||||||
|
sb.setLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (c == '\\') {
|
||||||
|
escape = true;
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
pathTokenList.add(new PathToken(PathType.KEY, sb.toString()));
|
||||||
|
}
|
||||||
|
return pathTokenList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static JsonNode getJsonNode(String responseBody) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readTree(responseBody);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum PathType {
|
||||||
|
KEY, NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PathToken {
|
||||||
|
private PathType type; // 0 是key, 1 是索引数字
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public PathToken(PathType type, String value) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
|
||||||
|
public interface IMethodExecutor {
|
||||||
|
|
||||||
|
void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
public interface IMimeTyper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect the mime type of this object
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getMimeType(ITransaction transaction, String path);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
public interface ITransaction {
|
||||||
|
|
||||||
|
Principal getPrincipal();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* $Header: /Users/ak/temp/cvs2svn/webdav-servlet/src/main/java/net/sf/webdav/IWebdavStore.java,v 1.1 2008-08-05 07:38:42 bauhardt Exp $
|
||||||
|
* $Revision: 1.1 $
|
||||||
|
* $Date: 2008-08-05 07:38:42 $
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* Copyright 2004 The Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for simple implementation of any store for the WebdavServlet
|
||||||
|
* <p>
|
||||||
|
* based on the BasicWebdavStore from Oliver Zeigermann, that was part of the
|
||||||
|
* Webdav Construcktion Kit from slide
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface IWebdavStore {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Life cycle method, called by WebdavServlet's destroy() method. Should be used to clean up resources.
|
||||||
|
*/
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that a new request or transaction with this store involved has
|
||||||
|
* been started. The request will be terminated by either {@link #commit()}
|
||||||
|
* or {@link #rollback()}. If only non-read methods have been called, the
|
||||||
|
* request will be terminated by a {@link #commit()}. This method will be
|
||||||
|
* called by (@link WebdavStoreAdapter} at the beginning of each request.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param principal
|
||||||
|
* the principal that started this request or <code>null</code> if
|
||||||
|
* there is non available
|
||||||
|
*
|
||||||
|
* @throws WebdavException
|
||||||
|
*/
|
||||||
|
ITransaction begin(Principal principal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if authentication information passed in is valid. If not throws an
|
||||||
|
* exception.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
*/
|
||||||
|
void checkAuthentication(ITransaction transaction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that all changes done inside this request shall be made
|
||||||
|
* permanent and any transactions, connections and other temporary resources
|
||||||
|
* shall be terminated.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
*
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
void commit(ITransaction transaction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that all changes done inside this request shall be undone and
|
||||||
|
* any transactions, connections and other temporary resources shall be
|
||||||
|
* terminated.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
*
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
void rollback(ITransaction transaction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a folder at the position specified by <code>folderUri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param folderUri
|
||||||
|
* URI of the folder
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
void createFolder(ITransaction transaction, String folderUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a content resource at the position specified by
|
||||||
|
* <code>resourceUri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param resourceUri
|
||||||
|
* URI of the content resource
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
void createResource(ITransaction transaction, String resourceUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the content of the resource specified by <code>resourceUri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param resourceUri
|
||||||
|
* URI of the content resource
|
||||||
|
* @return input stream you can read the content of the resource from
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
InputStream getResourceContent(ITransaction transaction, String resourceUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets / stores the content of the resource specified by
|
||||||
|
* <code>resourceUri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param resourceUri
|
||||||
|
* URI of the resource where the content will be stored
|
||||||
|
* @param content
|
||||||
|
* input stream from which the content will be read from
|
||||||
|
* @param contentType
|
||||||
|
* content type of the resource or <code>null</code> if unknown
|
||||||
|
* @param characterEncoding
|
||||||
|
* character encoding of the resource or <code>null</code> if unknown
|
||||||
|
* or not applicable
|
||||||
|
* @return lenght of resource
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
long setResourceContent(ITransaction transaction, String resourceUri,
|
||||||
|
InputStream content, String contentType, String characterEncoding);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the names of the children of the folder specified by
|
||||||
|
* <code>folderUri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param folderUri
|
||||||
|
* URI of the folder
|
||||||
|
* @return a (possibly empty) list of children, or <code>null</code> if the
|
||||||
|
* uri points to a file
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
String[] getChildrenNames(ITransaction transaction, String folderUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the length of the content resource specified by
|
||||||
|
* <code>resourceUri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param resourceUri
|
||||||
|
* URI of the content resource
|
||||||
|
* @return length of the resource in bytes, <code>-1</code> declares this
|
||||||
|
* value as invalid and asks the adapter to try to set it from the
|
||||||
|
* properties if possible
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
long getResourceLength(ITransaction transaction, String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the object specified by <code>uri</code>.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param uri
|
||||||
|
* URI of the object, i.e. content resource or folder
|
||||||
|
* @throws WebdavException
|
||||||
|
* if something goes wrong on the store level
|
||||||
|
*/
|
||||||
|
void removeObject(ITransaction transaction, String uri);
|
||||||
|
|
||||||
|
boolean moveObject(ITransaction transaction, String destinationPath, String sourcePath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the storedObject specified by <code>uri</code>
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param uri
|
||||||
|
* URI
|
||||||
|
* @return StoredObject
|
||||||
|
*/
|
||||||
|
StoredObject getStoredObject(ITransaction transaction, String uri);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.sf.webdav.exceptions.UnauthenticatedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference Implementation of WebdavStore
|
||||||
|
*
|
||||||
|
* @author joa
|
||||||
|
* @author re
|
||||||
|
*/
|
||||||
|
public class LocalFileSystemStore implements IWebdavStore {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(LocalFileSystemStore.class);
|
||||||
|
|
||||||
|
private static int BUF_SIZE = 65536;
|
||||||
|
|
||||||
|
private File _root = null;
|
||||||
|
|
||||||
|
public LocalFileSystemStore(File root) {
|
||||||
|
_root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITransaction begin(Principal principal) throws WebdavException {
|
||||||
|
LOG.info("LocalFileSystemStore.begin()");
|
||||||
|
if (!_root.exists()) {
|
||||||
|
if (!_root.mkdirs()) {
|
||||||
|
throw new WebdavException("root path: "
|
||||||
|
+ _root.getAbsolutePath()
|
||||||
|
+ " does not exist and could not be created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (principal == null) {
|
||||||
|
// throw new UnauthenticatedException(WebdavStatus.SC_UNAUTHORIZED);
|
||||||
|
// }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkAuthentication(ITransaction transaction)
|
||||||
|
throws SecurityException {
|
||||||
|
LOG.info("LocalFileSystemStore.checkAuthentication()");
|
||||||
|
// do nothing
|
||||||
|
// throw new UnauthenticatedException(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commit(ITransaction transaction) throws WebdavException {
|
||||||
|
// do nothing
|
||||||
|
LOG.info("LocalFileSystemStore.commit()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rollback(ITransaction transaction) throws WebdavException {
|
||||||
|
// do nothing
|
||||||
|
LOG.info("LocalFileSystemStore.rollback()");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createFolder(ITransaction transaction, String uri)
|
||||||
|
throws WebdavException {
|
||||||
|
LOG.info("LocalFileSystemStore.createFolder(" + uri + ")");
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
if (!file.mkdir())
|
||||||
|
throw new WebdavException("cannot create folder: " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createResource(ITransaction transaction, String uri)
|
||||||
|
throws WebdavException {
|
||||||
|
LOG.info("LocalFileSystemStore.createResource(" + uri + ")");
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
try {
|
||||||
|
if (!file.createNewFile())
|
||||||
|
throw new WebdavException("cannot create file: " + uri);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG
|
||||||
|
.error("LocalFileSystemStore.createResource(" + uri
|
||||||
|
+ ") failed");
|
||||||
|
throw new WebdavException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long setResourceContent(ITransaction transaction, String uri,
|
||||||
|
InputStream is, String contentType, String characterEncoding)
|
||||||
|
throws WebdavException {
|
||||||
|
|
||||||
|
LOG.info("LocalFileSystemStore.setResourceContent(" + uri + ")");
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
try {
|
||||||
|
OutputStream os = new BufferedOutputStream(new FileOutputStream(
|
||||||
|
file), BUF_SIZE);
|
||||||
|
try {
|
||||||
|
int read;
|
||||||
|
byte[] copyBuffer = new byte[BUF_SIZE];
|
||||||
|
|
||||||
|
while ((read = is.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||||
|
os.write(copyBuffer, 0, read);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} finally {
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("LocalFileSystemStore.setResourceContent(" + uri
|
||||||
|
+ ") failed");
|
||||||
|
throw new WebdavException(e);
|
||||||
|
}
|
||||||
|
long length = -1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
length = file.length();
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
LOG.error("LocalFileSystemStore.setResourceContent(" + uri
|
||||||
|
+ ") failed" + "\nCan't get file.length");
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getChildrenNames(ITransaction transaction, String uri)
|
||||||
|
throws WebdavException {
|
||||||
|
LOG.info("LocalFileSystemStore.getChildrenNames(" + uri + ")");
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
String[] childrenNames = null;
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
File[] children = file.listFiles();
|
||||||
|
List<String> childList = new ArrayList<String>();
|
||||||
|
String name = null;
|
||||||
|
for (int i = 0; i < children.length; i++) {
|
||||||
|
name = children[i].getName();
|
||||||
|
childList.add(name);
|
||||||
|
LOG.info("Child " + i + ": " + name);
|
||||||
|
}
|
||||||
|
childrenNames = new String[childList.size()];
|
||||||
|
childrenNames = (String[]) childList.toArray(childrenNames);
|
||||||
|
}
|
||||||
|
return childrenNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeObject(ITransaction transaction, String uri)
|
||||||
|
throws WebdavException {
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
boolean success = file.delete();
|
||||||
|
LOG.info("LocalFileSystemStore.removeObject(" + uri + ")=" + success);
|
||||||
|
if (!success) {
|
||||||
|
throw new WebdavException("cannot delete object: " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean moveObject(ITransaction transaction, String destinationPath, String path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getResourceContent(ITransaction transaction, String uri)
|
||||||
|
throws WebdavException {
|
||||||
|
LOG.info("LocalFileSystemStore.getResourceContent(" + uri + ")");
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
|
||||||
|
InputStream in;
|
||||||
|
try {
|
||||||
|
in = new BufferedInputStream(new FileInputStream(file));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("LocalFileSystemStore.getResourceContent(" + uri
|
||||||
|
+ ") failed");
|
||||||
|
throw new WebdavException(e);
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getResourceLength(ITransaction transaction, String uri)
|
||||||
|
throws WebdavException {
|
||||||
|
LOG.info("LocalFileSystemStore.getResourceLength(" + uri + ")");
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
return file.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
||||||
|
|
||||||
|
StoredObject so = null;
|
||||||
|
|
||||||
|
File file = new File(_root, uri);
|
||||||
|
if (file.exists()) {
|
||||||
|
so = new StoredObject();
|
||||||
|
so.setFolder(file.isDirectory());
|
||||||
|
so.setLastModified(new Date(file.lastModified()));
|
||||||
|
so.setCreationDate(new Date(file.lastModified()));
|
||||||
|
so.setResourceLength(getResourceLength(transaction, uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
return so;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* Copyright 2004 The Apache Software Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class StoredObject {
|
||||||
|
|
||||||
|
private boolean isFolder;
|
||||||
|
private Date lastModified;
|
||||||
|
private Date creationDate;
|
||||||
|
private long contentLength;
|
||||||
|
private String mimeType;
|
||||||
|
|
||||||
|
private boolean isNullRessource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the StoredObject is a folder or a resource
|
||||||
|
*
|
||||||
|
* @return true if the StoredObject is a collection
|
||||||
|
*/
|
||||||
|
public boolean isFolder() {
|
||||||
|
return (isFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the StoredObject is a folder or a resource
|
||||||
|
*
|
||||||
|
* @return true if the StoredObject is a resource
|
||||||
|
*/
|
||||||
|
public boolean isResource() {
|
||||||
|
return (!isFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new StoredObject as a collection or resource
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* true - collection ; false - resource
|
||||||
|
*/
|
||||||
|
public void setFolder(boolean f) {
|
||||||
|
this.isFolder = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the date of the last modification
|
||||||
|
*
|
||||||
|
* @return last modification Date
|
||||||
|
*/
|
||||||
|
public Date getLastModified() {
|
||||||
|
return (lastModified);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the date of the last modification
|
||||||
|
*
|
||||||
|
* @param d
|
||||||
|
* date of the last modification
|
||||||
|
*/
|
||||||
|
public void setLastModified(Date d) {
|
||||||
|
this.lastModified = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the date of the creation
|
||||||
|
*
|
||||||
|
* @return creation Date
|
||||||
|
*/
|
||||||
|
public Date getCreationDate() {
|
||||||
|
return (creationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the date of the creation
|
||||||
|
*
|
||||||
|
* @param d
|
||||||
|
* date of the creation
|
||||||
|
*/
|
||||||
|
public void setCreationDate(Date c) {
|
||||||
|
this.creationDate = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the length of the resource content
|
||||||
|
*
|
||||||
|
* @return length of the resource content
|
||||||
|
*/
|
||||||
|
public long getResourceLength() {
|
||||||
|
return (contentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the length of the resource content
|
||||||
|
*
|
||||||
|
* @param l
|
||||||
|
* the length of the resource content
|
||||||
|
*/
|
||||||
|
public void setResourceLength(long l) {
|
||||||
|
this.contentLength = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the state of the resource
|
||||||
|
*
|
||||||
|
* @return true if the resource is in lock-null state
|
||||||
|
*/
|
||||||
|
public boolean isNullResource() {
|
||||||
|
return isNullRessource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a StoredObject as a lock-null resource
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* true to set the resource as lock-null resource
|
||||||
|
*/
|
||||||
|
public void setNullResource(boolean f) {
|
||||||
|
this.isNullRessource = f;
|
||||||
|
this.isFolder = false;
|
||||||
|
this.creationDate = null;
|
||||||
|
this.lastModified = null;
|
||||||
|
// this.content = null;
|
||||||
|
this.contentLength = 0;
|
||||||
|
this.mimeType= null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the myme type from the store object.
|
||||||
|
* Can also return NULL if the store does not handle
|
||||||
|
* mime type stuff.
|
||||||
|
* In that case the mime type is determined by the servletcontext
|
||||||
|
*
|
||||||
|
* @return the mimeType
|
||||||
|
*/
|
||||||
|
public String getMimeType() {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the mime type of this object
|
||||||
|
*
|
||||||
|
* @param mimeType the mimeType to set
|
||||||
|
*/
|
||||||
|
public void setMimeType(String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.exceptions.UnauthenticatedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.fromcatalina.MD5Encoder;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
import net.sf.webdav.methods.DoCopy;
|
||||||
|
import net.sf.webdav.methods.DoDelete;
|
||||||
|
import net.sf.webdav.methods.DoGet;
|
||||||
|
import net.sf.webdav.methods.DoHead;
|
||||||
|
import net.sf.webdav.methods.DoLock;
|
||||||
|
import net.sf.webdav.methods.DoMkcol;
|
||||||
|
import net.sf.webdav.methods.DoMove;
|
||||||
|
import net.sf.webdav.methods.DoNotImplemented;
|
||||||
|
import net.sf.webdav.methods.DoOptions;
|
||||||
|
import net.sf.webdav.methods.DoPropfind;
|
||||||
|
import net.sf.webdav.methods.DoProppatch;
|
||||||
|
import net.sf.webdav.methods.DoPut;
|
||||||
|
import net.sf.webdav.methods.DoUnlock;
|
||||||
|
|
||||||
|
public class WebDavServletBean extends HttpServlet {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(WebDavServletBean.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5 message digest provider.
|
||||||
|
*/
|
||||||
|
protected static MessageDigest MD5_HELPER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MD5 helper object for this class.
|
||||||
|
*/
|
||||||
|
protected static final MD5Encoder MD5_ENCODER = new MD5Encoder();
|
||||||
|
|
||||||
|
private static final boolean READ_ONLY = false;
|
||||||
|
protected ResourceLocks _resLocks;
|
||||||
|
protected IWebdavStore _store;
|
||||||
|
private HashMap<String, IMethodExecutor> _methodMap = new HashMap<String, IMethodExecutor>();
|
||||||
|
|
||||||
|
public WebDavServletBean() {
|
||||||
|
_resLocks = new ResourceLocks();
|
||||||
|
|
||||||
|
try {
|
||||||
|
MD5_HELPER = MessageDigest.getInstance("MD5");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(IWebdavStore store, String dftIndexFile,
|
||||||
|
String insteadOf404, int nocontentLenghHeaders,
|
||||||
|
boolean lazyFolderCreationOnPut) throws ServletException {
|
||||||
|
|
||||||
|
_store = store;
|
||||||
|
|
||||||
|
IMimeTyper mimeTyper = new IMimeTyper() {
|
||||||
|
public String getMimeType(ITransaction transaction, String path) {
|
||||||
|
String retVal= _store.getStoredObject(transaction, path).getMimeType();
|
||||||
|
if ( retVal== null) {
|
||||||
|
retVal= getServletContext().getMimeType( path);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
register("GET", new DoGet(store, dftIndexFile, insteadOf404, _resLocks,
|
||||||
|
mimeTyper, nocontentLenghHeaders));
|
||||||
|
register("HEAD", new DoHead(store, dftIndexFile, insteadOf404,
|
||||||
|
_resLocks, mimeTyper, nocontentLenghHeaders));
|
||||||
|
DoDelete doDelete = (DoDelete) register("DELETE", new DoDelete(store,
|
||||||
|
_resLocks, READ_ONLY));
|
||||||
|
DoCopy doCopy = (DoCopy) register("COPY", new DoCopy(store, _resLocks,
|
||||||
|
doDelete, READ_ONLY));
|
||||||
|
register("LOCK", new DoLock(store, _resLocks, READ_ONLY));
|
||||||
|
register("UNLOCK", new DoUnlock(store, _resLocks, READ_ONLY));
|
||||||
|
register("MOVE", new DoMove(store, _resLocks, doDelete, doCopy, READ_ONLY));
|
||||||
|
register("MKCOL", new DoMkcol(store, _resLocks, READ_ONLY));
|
||||||
|
register("OPTIONS", new DoOptions(store, _resLocks));
|
||||||
|
register("PUT", new DoPut(store, _resLocks, READ_ONLY,
|
||||||
|
lazyFolderCreationOnPut));
|
||||||
|
register("PROPFIND", new DoPropfind(store, _resLocks, mimeTyper));
|
||||||
|
register("PROPPATCH", new DoProppatch(store, _resLocks, READ_ONLY));
|
||||||
|
register("*NO*IMPL*", new DoNotImplemented(READ_ONLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
if(_store != null)
|
||||||
|
_store.destroy();
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IMethodExecutor register(String methodName, IMethodExecutor method) {
|
||||||
|
_methodMap.put(methodName, method);
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the special WebDAV methods.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
String methodName = req.getMethod();
|
||||||
|
ITransaction transaction = null;
|
||||||
|
boolean needRollback = false;
|
||||||
|
|
||||||
|
if (LOG.isTraceEnabled())
|
||||||
|
debugRequest(methodName, req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Principal userPrincipal = getUserPrincipal(req);
|
||||||
|
transaction = _store.begin(userPrincipal);
|
||||||
|
needRollback = true;
|
||||||
|
_store.checkAuthentication(transaction);
|
||||||
|
resp.setStatus(WebdavStatus.SC_OK);
|
||||||
|
|
||||||
|
try {
|
||||||
|
IMethodExecutor methodExecutor = (IMethodExecutor) _methodMap
|
||||||
|
.get(methodName);
|
||||||
|
if (methodExecutor == null) {
|
||||||
|
methodExecutor = (IMethodExecutor) _methodMap
|
||||||
|
.get("*NO*IMPL*");
|
||||||
|
}
|
||||||
|
|
||||||
|
methodExecutor.execute(transaction, req, resp);
|
||||||
|
|
||||||
|
_store.commit(transaction);
|
||||||
|
/** Clear not consumed data
|
||||||
|
*
|
||||||
|
* Clear input stream if available otherwise later access
|
||||||
|
* include current input. These cases occure if the client
|
||||||
|
* sends a request with body to an not existing resource.
|
||||||
|
*/
|
||||||
|
if (req.getContentLength() != 0 && req.getInputStream().available() > 0) {
|
||||||
|
if (LOG.isTraceEnabled()) { LOG.trace("Clear not consumed data!"); }
|
||||||
|
while (req.getInputStream().available() > 0) {
|
||||||
|
req.getInputStream().read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
needRollback = false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
java.io.StringWriter sw = new java.io.StringWriter();
|
||||||
|
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||||
|
e.printStackTrace(pw);
|
||||||
|
LOG.error("IOException: " + sw.toString());
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
_store.rollback(transaction);
|
||||||
|
throw new ServletException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (UnauthenticatedException e) {
|
||||||
|
resp.sendError(e.getCode());
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
java.io.StringWriter sw = new java.io.StringWriter();
|
||||||
|
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||||
|
e.printStackTrace(pw);
|
||||||
|
LOG.error("WebdavException: " + sw.toString());
|
||||||
|
throw new ServletException(e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
java.io.StringWriter sw = new java.io.StringWriter();
|
||||||
|
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||||
|
e.printStackTrace(pw);
|
||||||
|
LOG.error("Exception: " + sw.toString());
|
||||||
|
} finally {
|
||||||
|
if (needRollback)
|
||||||
|
_store.rollback(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that permit to customize the way
|
||||||
|
* user information are extracted from the request, default use JAAS
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Principal getUserPrincipal(HttpServletRequest req) {
|
||||||
|
return req.getUserPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debugRequest(String methodName, HttpServletRequest req) {
|
||||||
|
LOG.trace("-----------");
|
||||||
|
LOG.trace("WebdavServlet\n request: methodName = " + methodName);
|
||||||
|
LOG.trace("time: " + System.currentTimeMillis());
|
||||||
|
LOG.trace("path: " + req.getRequestURI());
|
||||||
|
LOG.trace("-----------");
|
||||||
|
Enumeration<?> e = req.getHeaderNames();
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
String s = (String) e.nextElement();
|
||||||
|
LOG.trace("header: " + s + " " + req.getHeader(s));
|
||||||
|
}
|
||||||
|
e = req.getAttributeNames();
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
String s = (String) e.nextElement();
|
||||||
|
LOG.trace("attribute: " + s + " " + req.getAttribute(s));
|
||||||
|
}
|
||||||
|
e = req.getParameterNames();
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
String s = (String) e.nextElement();
|
||||||
|
LOG.trace("parameter: " + s + " " + req.getParameter(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servlet which provides support for WebDAV level 2.
|
||||||
|
*
|
||||||
|
* the original class is org.apache.catalina.servlets.WebdavServlet by Remy
|
||||||
|
* Maucherat, which was heavily changed
|
||||||
|
*
|
||||||
|
* @author Remy Maucherat
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class WebdavServlet extends WebDavServletBean {
|
||||||
|
|
||||||
|
private static final String ROOTPATH_PARAMETER = "rootpath";
|
||||||
|
|
||||||
|
public void init() throws ServletException {
|
||||||
|
|
||||||
|
// Parameters from web.xml
|
||||||
|
String clazzName = getServletConfig().getInitParameter(
|
||||||
|
"ResourceHandlerImplementation");
|
||||||
|
if (clazzName == null || clazzName.equals("")) {
|
||||||
|
clazzName = LocalFileSystemStore.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
File root = getFileRoot();
|
||||||
|
|
||||||
|
IWebdavStore webdavStore = constructStore(clazzName, root);
|
||||||
|
|
||||||
|
boolean lazyFolderCreationOnPut = getInitParameter("lazyFolderCreationOnPut") != null
|
||||||
|
&& getInitParameter("lazyFolderCreationOnPut").equals("1");
|
||||||
|
|
||||||
|
String dftIndexFile = getInitParameter("default-index-file");
|
||||||
|
String insteadOf404 = getInitParameter("instead-of-404");
|
||||||
|
|
||||||
|
int noContentLengthHeader = getIntInitParameter("no-content-length-headers");
|
||||||
|
|
||||||
|
super.init(webdavStore, dftIndexFile, insteadOf404,
|
||||||
|
noContentLengthHeader, lazyFolderCreationOnPut);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getIntInitParameter(String key) {
|
||||||
|
return getInitParameter(key) == null ? -1 : Integer
|
||||||
|
.parseInt(getInitParameter(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IWebdavStore constructStore(String clazzName, File root) {
|
||||||
|
IWebdavStore webdavStore;
|
||||||
|
try {
|
||||||
|
Class<?> clazz = WebdavServlet.class.getClassLoader().loadClass(
|
||||||
|
clazzName);
|
||||||
|
|
||||||
|
Constructor<?> ctor = clazz
|
||||||
|
.getConstructor(new Class[] { File.class });
|
||||||
|
|
||||||
|
webdavStore = (IWebdavStore) ctor
|
||||||
|
.newInstance(new Object[] { root });
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("some problem making store component", e);
|
||||||
|
}
|
||||||
|
return webdavStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getFileRoot() {
|
||||||
|
String rootPath = getInitParameter(ROOTPATH_PARAMETER);
|
||||||
|
if (rootPath == null) {
|
||||||
|
throw new WebdavException("missing parameter: "
|
||||||
|
+ ROOTPATH_PARAMETER);
|
||||||
|
}
|
||||||
|
if (rootPath.equals("*WAR-FILE-ROOT*")) {
|
||||||
|
String file = LocalFileSystemStore.class.getProtectionDomain()
|
||||||
|
.getCodeSource().getLocation().getFile().replace('\\', '/');
|
||||||
|
if (file.charAt(0) == '/'
|
||||||
|
&& System.getProperty("os.name").indexOf("Windows") != -1) {
|
||||||
|
file = file.substring(1, file.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ix = file.indexOf("/WEB-INF/");
|
||||||
|
if (ix != -1) {
|
||||||
|
rootPath = file.substring(0, ix).replace('/',
|
||||||
|
File.separatorChar);
|
||||||
|
} else {
|
||||||
|
throw new WebdavException(
|
||||||
|
"Could not determine root of war file. Can't extract from path '"
|
||||||
|
+ file + "' for this web container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new File(rootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,273 @@
|
||||||
|
package net.sf.webdav;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public 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<Integer, String> _mapStatusCodes = new Hashtable<Integer, String>();
|
||||||
|
|
||||||
|
// ------------------------------------------------------ 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 Status 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.exceptions;
|
||||||
|
|
||||||
|
public class AccessDeniedException extends WebdavException {
|
||||||
|
|
||||||
|
public AccessDeniedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessDeniedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessDeniedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessDeniedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package net.sf.webdav.exceptions;
|
||||||
|
|
||||||
|
public class LockFailedException extends WebdavException {
|
||||||
|
|
||||||
|
public LockFailedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockFailedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.exceptions;
|
||||||
|
|
||||||
|
public class ObjectAlreadyExistsException extends WebdavException {
|
||||||
|
|
||||||
|
public ObjectAlreadyExistsException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectAlreadyExistsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectAlreadyExistsException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectAlreadyExistsException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.exceptions;
|
||||||
|
|
||||||
|
public class ObjectNotFoundException extends WebdavException {
|
||||||
|
|
||||||
|
public ObjectNotFoundException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNotFoundException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNotFoundException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.exceptions;
|
||||||
|
|
||||||
|
public class UnauthenticatedException extends WebdavException {
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
public UnauthenticatedException(int code) {
|
||||||
|
super();
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.exceptions;
|
||||||
|
|
||||||
|
public class WebdavException extends RuntimeException {
|
||||||
|
|
||||||
|
public WebdavException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebdavException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebdavException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebdavException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.fromcatalina;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.2 $ $Date: 2008-08-05 07:38:45 $
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,510 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.fromcatalina;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General purpose request parsing and encoding utility methods.
|
||||||
|
*
|
||||||
|
* @author Craig R. McClanahan
|
||||||
|
* @author Tim Tye
|
||||||
|
* @version $Revision: 1.2 $ $Date: 2008-08-05 07:38:45 $
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class RequestUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a cookie as per RFC 2109. The resulting string can be used as the
|
||||||
|
* value for a <code>Set-Cookie</code> header.
|
||||||
|
*
|
||||||
|
* @param cookie
|
||||||
|
* The cookie to encode.
|
||||||
|
* @return A string following RFC 2109.
|
||||||
|
*/
|
||||||
|
public static String encodeCookie(Cookie cookie) {
|
||||||
|
|
||||||
|
StringBuffer buf = new StringBuffer(cookie.getName());
|
||||||
|
buf.append("=");
|
||||||
|
buf.append(cookie.getValue());
|
||||||
|
|
||||||
|
String comment = cookie.getComment();
|
||||||
|
if (comment != null) {
|
||||||
|
buf.append("; Comment=\"");
|
||||||
|
buf.append(comment);
|
||||||
|
buf.append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
String domain = cookie.getDomain();
|
||||||
|
if (domain != null) {
|
||||||
|
buf.append("; Domain=\"");
|
||||||
|
buf.append(domain);
|
||||||
|
buf.append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
int age = cookie.getMaxAge();
|
||||||
|
if (age >= 0) {
|
||||||
|
buf.append("; Max-Age=\"");
|
||||||
|
buf.append(age);
|
||||||
|
buf.append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = cookie.getPath();
|
||||||
|
if (path != null) {
|
||||||
|
buf.append("; Path=\"");
|
||||||
|
buf.append(path);
|
||||||
|
buf.append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookie.getSecure()) {
|
||||||
|
buf.append("; Secure");
|
||||||
|
}
|
||||||
|
|
||||||
|
int version = cookie.getVersion();
|
||||||
|
if (version > 0) {
|
||||||
|
buf.append("; Version=\"");
|
||||||
|
buf.append(version);
|
||||||
|
buf.append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (buf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the specified message string for characters that are sensitive in
|
||||||
|
* HTML. This avoids potential attacks caused by including JavaScript codes
|
||||||
|
* in the request URL that is often reported in error messages.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The message string to be filtered
|
||||||
|
*/
|
||||||
|
public static String filter(String message) {
|
||||||
|
|
||||||
|
if (message == null)
|
||||||
|
return (null);
|
||||||
|
|
||||||
|
char content[] = new char[message.length()];
|
||||||
|
message.getChars(0, message.length(), content, 0);
|
||||||
|
StringBuffer result = new StringBuffer(content.length + 50);
|
||||||
|
for (int i = 0; i < content.length; i++) {
|
||||||
|
switch (content[i]) {
|
||||||
|
case '<':
|
||||||
|
result.append("<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
result.append(">");
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
result.append("&");
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
result.append(""");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.append(content[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (result.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a relative URI path that may have relative values ("/./",
|
||||||
|
* "/../", and so on ) it it. <strong>WARNING</strong> - This method is
|
||||||
|
* useful only for normalizing application-generated paths. It does not try
|
||||||
|
* to perform security checks for malicious input.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* Relative path to be normalized
|
||||||
|
*/
|
||||||
|
public static String normalize(String path) {
|
||||||
|
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Create a place for the normalized path
|
||||||
|
String normalized = path;
|
||||||
|
|
||||||
|
if (normalized.equals("/."))
|
||||||
|
return "/";
|
||||||
|
|
||||||
|
// Add a leading "/" if necessary
|
||||||
|
if (!normalized.startsWith("/"))
|
||||||
|
normalized = "/" + normalized;
|
||||||
|
|
||||||
|
// Resolve occurrences of "//" in the normalized path
|
||||||
|
while (true) {
|
||||||
|
int index = normalized.indexOf("//");
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
normalized = normalized.substring(0, index)
|
||||||
|
+ normalized.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve occurrences of "/./" in the normalized path
|
||||||
|
while (true) {
|
||||||
|
int index = normalized.indexOf("/./");
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
normalized = normalized.substring(0, index)
|
||||||
|
+ normalized.substring(index + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve occurrences of "/../" in the normalized path
|
||||||
|
while (true) {
|
||||||
|
int index = normalized.indexOf("/../");
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
if (index == 0)
|
||||||
|
return (null); // Trying to go outside our context
|
||||||
|
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||||
|
normalized = normalized.substring(0, index2)
|
||||||
|
+ normalized.substring(index + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the normalized path that we have completed
|
||||||
|
return (normalized);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the character encoding from the specified content type header. If
|
||||||
|
* the content type is null, or there is no explicit character encoding,
|
||||||
|
* <code>null</code> is returned.
|
||||||
|
*
|
||||||
|
* @param contentType
|
||||||
|
* a content type header
|
||||||
|
*/
|
||||||
|
public static String parseCharacterEncoding(String contentType) {
|
||||||
|
|
||||||
|
if (contentType == null)
|
||||||
|
return (null);
|
||||||
|
int start = contentType.indexOf("charset=");
|
||||||
|
if (start < 0)
|
||||||
|
return (null);
|
||||||
|
String encoding = contentType.substring(start + 8);
|
||||||
|
int end = encoding.indexOf(';');
|
||||||
|
if (end >= 0)
|
||||||
|
encoding = encoding.substring(0, end);
|
||||||
|
encoding = encoding.trim();
|
||||||
|
if ((encoding.length() > 2) && (encoding.startsWith("\""))
|
||||||
|
&& (encoding.endsWith("\"")))
|
||||||
|
encoding = encoding.substring(1, encoding.length() - 1);
|
||||||
|
return (encoding.trim());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a cookie header into an array of cookies according to RFC 2109.
|
||||||
|
*
|
||||||
|
* @param header
|
||||||
|
* Value of an HTTP "Cookie" header
|
||||||
|
*/
|
||||||
|
public static Cookie[] parseCookieHeader(String header) {
|
||||||
|
|
||||||
|
if ((header == null) || (header.length() < 1))
|
||||||
|
return (new Cookie[0]);
|
||||||
|
|
||||||
|
ArrayList<Cookie> cookies = new ArrayList<Cookie>();
|
||||||
|
while (header.length() > 0) {
|
||||||
|
int semicolon = header.indexOf(';');
|
||||||
|
if (semicolon < 0)
|
||||||
|
semicolon = header.length();
|
||||||
|
if (semicolon == 0)
|
||||||
|
break;
|
||||||
|
String token = header.substring(0, semicolon);
|
||||||
|
if (semicolon < header.length())
|
||||||
|
header = header.substring(semicolon + 1);
|
||||||
|
else
|
||||||
|
header = "";
|
||||||
|
try {
|
||||||
|
int equals = token.indexOf('=');
|
||||||
|
if (equals > 0) {
|
||||||
|
String name = token.substring(0, equals).trim();
|
||||||
|
String value = token.substring(equals + 1).trim();
|
||||||
|
cookies.add(new Cookie(name, value));
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append request parameters from the specified String to the specified Map.
|
||||||
|
* It is presumed that the specified Map is not accessed from any other
|
||||||
|
* thread, so no synchronization is performed.
|
||||||
|
* <p>
|
||||||
|
* <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
|
||||||
|
* individually on the parsed name and value elements, rather than on the
|
||||||
|
* entire query string ahead of time, to properly deal with the case where
|
||||||
|
* the name or value includes an encoded "=" or "&" character that would
|
||||||
|
* otherwise be interpreted as a delimiter.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* Map that accumulates the resulting parameters
|
||||||
|
* @param data
|
||||||
|
* Input string containing request parameters
|
||||||
|
*
|
||||||
|
* @exception IllegalArgumentException
|
||||||
|
* if the data is malformed
|
||||||
|
*/
|
||||||
|
public static void parseParameters(Map<String, String[]> map, String data,
|
||||||
|
String encoding) throws UnsupportedEncodingException {
|
||||||
|
|
||||||
|
if ((data != null) && (data.length() > 0)) {
|
||||||
|
|
||||||
|
// use the specified encoding to extract bytes out of the
|
||||||
|
// given string so that the encoding is not lost. If an
|
||||||
|
// encoding is not specified, let it use platform default
|
||||||
|
byte[] bytes = null;
|
||||||
|
try {
|
||||||
|
if (encoding == null) {
|
||||||
|
bytes = data.getBytes();
|
||||||
|
} else {
|
||||||
|
bytes = data.getBytes(encoding);
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
}
|
||||||
|
|
||||||
|
parseParameters(map, bytes, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded String. When the byte array
|
||||||
|
* is converted to a string, the system default character encoding is
|
||||||
|
* used... This may be different than some other servers.
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* The url-encoded string
|
||||||
|
*
|
||||||
|
* @exception IllegalArgumentException
|
||||||
|
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||||
|
* number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(String str) {
|
||||||
|
|
||||||
|
return URLDecode(str, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded String.
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* The url-encoded string
|
||||||
|
* @param enc
|
||||||
|
* The encoding to use; if null, the default encoding is used
|
||||||
|
* @exception IllegalArgumentException
|
||||||
|
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||||
|
* number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(String str, String enc) {
|
||||||
|
|
||||||
|
if (str == null)
|
||||||
|
return (null);
|
||||||
|
|
||||||
|
// use the specified encoding to extract bytes out of the
|
||||||
|
// given string so that the encoding is not lost. If an
|
||||||
|
// encoding is not specified, let it use platform default
|
||||||
|
byte[] bytes = null;
|
||||||
|
try {
|
||||||
|
if (enc == null) {
|
||||||
|
bytes = str.getBytes();
|
||||||
|
} else {
|
||||||
|
bytes = str.getBytes(enc);
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return URLDecode(bytes, enc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded byte array.
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* The url-encoded byte array
|
||||||
|
* @exception IllegalArgumentException
|
||||||
|
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||||
|
* number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(byte[] bytes) {
|
||||||
|
return URLDecode(bytes, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode and return the specified URL-encoded byte array.
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* The url-encoded byte array
|
||||||
|
* @param enc
|
||||||
|
* The encoding to use; if null, the default encoding is used
|
||||||
|
* @exception IllegalArgumentException
|
||||||
|
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||||
|
* number
|
||||||
|
*/
|
||||||
|
public static String URLDecode(byte[] bytes, String enc) {
|
||||||
|
|
||||||
|
if (bytes == null)
|
||||||
|
return (null);
|
||||||
|
|
||||||
|
int len = bytes.length;
|
||||||
|
int ix = 0;
|
||||||
|
int ox = 0;
|
||||||
|
while (ix < len) {
|
||||||
|
byte b = bytes[ix++]; // Get byte to test
|
||||||
|
if (b == '+') {
|
||||||
|
b = (byte) ' ';
|
||||||
|
} else if (b == '%') {
|
||||||
|
b = (byte) ((convertHexDigit(bytes[ix++]) << 4) + convertHexDigit(bytes[ix++]));
|
||||||
|
}
|
||||||
|
bytes[ox++] = b;
|
||||||
|
}
|
||||||
|
if (enc != null) {
|
||||||
|
try {
|
||||||
|
return new String(bytes, 0, ox, enc);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new String(bytes, 0, ox);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a byte character value to hexidecimal digit value.
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* the character value byte
|
||||||
|
*/
|
||||||
|
private static byte convertHexDigit(byte b) {
|
||||||
|
if ((b >= '0') && (b <= '9'))
|
||||||
|
return (byte) (b - '0');
|
||||||
|
if ((b >= 'a') && (b <= 'f'))
|
||||||
|
return (byte) (b - 'a' + 10);
|
||||||
|
if ((b >= 'A') && (b <= 'F'))
|
||||||
|
return (byte) (b - 'A' + 10);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put name and value pair in map. When name already exist, add value to
|
||||||
|
* array of values.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* The map to populate
|
||||||
|
* @param name
|
||||||
|
* The parameter name
|
||||||
|
* @param value
|
||||||
|
* The parameter value
|
||||||
|
*/
|
||||||
|
private static void putMapEntry(Map<String, String[]> map, String name,
|
||||||
|
String value) {
|
||||||
|
String[] newValues = null;
|
||||||
|
String[] oldValues = (String[]) map.get(name);
|
||||||
|
if (oldValues == null) {
|
||||||
|
newValues = new String[1];
|
||||||
|
newValues[0] = value;
|
||||||
|
} else {
|
||||||
|
newValues = new String[oldValues.length + 1];
|
||||||
|
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
|
||||||
|
newValues[oldValues.length] = value;
|
||||||
|
}
|
||||||
|
map.put(name, newValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append request parameters from the specified String to the specified Map.
|
||||||
|
* It is presumed that the specified Map is not accessed from any other
|
||||||
|
* thread, so no synchronization is performed.
|
||||||
|
* <p>
|
||||||
|
* <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
|
||||||
|
* individually on the parsed name and value elements, rather than on the
|
||||||
|
* entire query string ahead of time, to properly deal with the case where
|
||||||
|
* the name or value includes an encoded "=" or "&" character that would
|
||||||
|
* otherwise be interpreted as a delimiter. NOTE: byte array data is
|
||||||
|
* modified by this method. Caller beware.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* Map that accumulates the resulting parameters
|
||||||
|
* @param data
|
||||||
|
* Input string containing request parameters
|
||||||
|
* @param encoding
|
||||||
|
* Encoding to use for converting hex
|
||||||
|
*
|
||||||
|
* @exception UnsupportedEncodingException
|
||||||
|
* if the data is malformed
|
||||||
|
*/
|
||||||
|
public static void parseParameters(Map<String, String[]> map, byte[] data,
|
||||||
|
String encoding) throws UnsupportedEncodingException {
|
||||||
|
|
||||||
|
if (data != null && data.length > 0) {
|
||||||
|
int ix = 0;
|
||||||
|
int ox = 0;
|
||||||
|
String key = null;
|
||||||
|
String value = null;
|
||||||
|
while (ix < data.length) {
|
||||||
|
byte c = data[ix++];
|
||||||
|
switch ((char) c) {
|
||||||
|
case '&':
|
||||||
|
value = new String(data, 0, ox, encoding);
|
||||||
|
if (key != null) {
|
||||||
|
putMapEntry(map, key, value);
|
||||||
|
key = null;
|
||||||
|
}
|
||||||
|
ox = 0;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (key == null) {
|
||||||
|
key = new String(data, 0, ox, encoding);
|
||||||
|
ox = 0;
|
||||||
|
} else {
|
||||||
|
data[ox++] = c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
data[ox++] = (byte) ' ';
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++]));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data[ox++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The last value does not end in '&'. So save it now.
|
||||||
|
if (key != null) {
|
||||||
|
value = new String(data, 0, ox, encoding);
|
||||||
|
putMapEntry(map, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.fromcatalina;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This class is very similar to the java.net.URLEncoder class.
|
||||||
|
*
|
||||||
|
* Unfortunately, with java.net.URLEncoder there is no way to specify to the
|
||||||
|
* java.net.URLEncoder which characters should NOT be encoded.
|
||||||
|
*
|
||||||
|
* This code was moved from DefaultServlet.java
|
||||||
|
*
|
||||||
|
* @author Craig R. McClanahan
|
||||||
|
* @author Remy Maucherat
|
||||||
|
*/
|
||||||
|
public class URLEncoder
|
||||||
|
{
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(URLEncoder.class);
|
||||||
|
|
||||||
|
|
||||||
|
protected static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5',
|
||||||
|
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||||
|
|
||||||
|
// Array containing the safe characters set.
|
||||||
|
protected BitSet _safeCharacters = new BitSet(256);
|
||||||
|
|
||||||
|
public URLEncoder() {
|
||||||
|
for (char i = 'a'; i <= 'z'; i++) {
|
||||||
|
addSafeCharacter(i);
|
||||||
|
}
|
||||||
|
for (char i = 'A'; i <= 'Z'; i++) {
|
||||||
|
addSafeCharacter(i);
|
||||||
|
}
|
||||||
|
for (char i = '0'; i <= '9'; i++) {
|
||||||
|
addSafeCharacter(i);
|
||||||
|
}
|
||||||
|
for(char c : "$-_.+!*'(),".toCharArray()){
|
||||||
|
addSafeCharacter(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSafeCharacter(char c) {
|
||||||
|
_safeCharacters.set(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encode(String path) {
|
||||||
|
int maxBytesPerChar = 10;
|
||||||
|
// int caseDiff = ('a' - 'A');
|
||||||
|
StringBuffer rewrittenPath = new StringBuffer(path.length());
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
|
||||||
|
OutputStreamWriter writer = null;
|
||||||
|
try {
|
||||||
|
writer = new OutputStreamWriter(buf, "UTF8");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error in encode <"+path+">", e);
|
||||||
|
writer = new OutputStreamWriter(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < path.length(); i++) {
|
||||||
|
int c = (int) path.charAt(i);
|
||||||
|
if (_safeCharacters.get(c)) {
|
||||||
|
rewrittenPath.append((char) c);
|
||||||
|
} else {
|
||||||
|
// convert to external encoding before hex conversion
|
||||||
|
try {
|
||||||
|
writer.write((char) c);
|
||||||
|
writer.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
buf.reset();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
byte[] ba = buf.toByteArray();
|
||||||
|
for (int j = 0; j < ba.length; j++) {
|
||||||
|
// Converting each byte in the buffer
|
||||||
|
byte toEncode = ba[j];
|
||||||
|
rewrittenPath.append('%');
|
||||||
|
int low = (int) (toEncode & 0x0f);
|
||||||
|
int high = (int) ((toEncode & 0xf0) >> 4);
|
||||||
|
rewrittenPath.append(HEXADECIMAL[high]);
|
||||||
|
rewrittenPath.append(HEXADECIMAL[low]);
|
||||||
|
}
|
||||||
|
buf.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rewrittenPath.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package net.sf.webdav.fromcatalina;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
public class XMLHelper {
|
||||||
|
|
||||||
|
public static Node findSubElement(Node parent, String localName) {
|
||||||
|
if (parent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Node child = parent.getFirstChild();
|
||||||
|
while (child != null) {
|
||||||
|
if ((child.getNodeType() == Node.ELEMENT_NODE)
|
||||||
|
&& (child.getLocalName().equals(localName))) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
child = child.getNextSibling();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector<String> getPropertiesFromXML(Node propNode) {
|
||||||
|
Vector<String> properties;
|
||||||
|
properties = new Vector<String>();
|
||||||
|
NodeList childList = propNode.getChildNodes();
|
||||||
|
|
||||||
|
for (int i = 0; i < childList.getLength(); i++) {
|
||||||
|
Node currentNode = childList.item(i);
|
||||||
|
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
String nodeName = currentNode.getLocalName();
|
||||||
|
String namespace = currentNode.getNamespaceURI();
|
||||||
|
// href is a live property which is handled differently
|
||||||
|
properties.addElement(namespace + ":" + nodeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.fromcatalina;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XMLWriter helper class.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
|
||||||
|
*/
|
||||||
|
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 = new StringBuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writer.
|
||||||
|
*/
|
||||||
|
protected Writer _writer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespaces to be declared in the root element
|
||||||
|
*/
|
||||||
|
protected Map<String, String> _namespaces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is true until the root element is written
|
||||||
|
*/
|
||||||
|
protected boolean _isRootElement = true;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------- Constructors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public XMLWriter(Map<String, String> namespaces) {
|
||||||
|
_namespaces = namespaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public XMLWriter(Writer writer, Map<String, String> namespaces) {
|
||||||
|
_writer = writer;
|
||||||
|
_namespaces = namespaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------- Public Methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve generated XML.
|
||||||
|
*
|
||||||
|
* @return String containing the generated XML
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return _buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write property to the XML.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* Property name
|
||||||
|
* @param value
|
||||||
|
* Property value
|
||||||
|
*/
|
||||||
|
public void writeProperty(String name, String value) {
|
||||||
|
writeElement(name, OPENING);
|
||||||
|
_buffer.append(value);
|
||||||
|
writeElement(name, CLOSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write property to the XML.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* Property name
|
||||||
|
*/
|
||||||
|
public void writeProperty(String name) {
|
||||||
|
writeElement(name, NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an element.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* Element name
|
||||||
|
* @param type
|
||||||
|
* Element type
|
||||||
|
*/
|
||||||
|
public void writeElement(String name, int type) {
|
||||||
|
StringBuffer nsdecl = new StringBuffer();
|
||||||
|
|
||||||
|
if (_isRootElement) {
|
||||||
|
for (Iterator<String> iter = _namespaces.keySet().iterator(); iter
|
||||||
|
.hasNext();) {
|
||||||
|
String fullName = (String) iter.next();
|
||||||
|
String abbrev = (String) _namespaces.get(fullName);
|
||||||
|
nsdecl.append(" xmlns:").append(abbrev).append("=\"").append(
|
||||||
|
fullName).append("\"");
|
||||||
|
}
|
||||||
|
_isRootElement = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = name.lastIndexOf(':');
|
||||||
|
if (pos >= 0) {
|
||||||
|
// lookup prefix for namespace
|
||||||
|
String fullns = name.substring(0, pos);
|
||||||
|
String prefix = (String) _namespaces.get(fullns);
|
||||||
|
if (prefix == null) {
|
||||||
|
// there is no prefix for this namespace
|
||||||
|
name = name.substring(pos + 1);
|
||||||
|
nsdecl.append(" xmlns=\"").append(fullns).append("\"");
|
||||||
|
} else {
|
||||||
|
// there is a prefix
|
||||||
|
name = prefix + ":" + name.substring(pos + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"All XML elements must have a namespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case OPENING:
|
||||||
|
_buffer.append("<" + name + nsdecl + ">");
|
||||||
|
break;
|
||||||
|
case CLOSING:
|
||||||
|
_buffer.append("</" + name + ">\n");
|
||||||
|
break;
|
||||||
|
case NO_CONTENT:
|
||||||
|
default:
|
||||||
|
_buffer.append("<" + name + nsdecl + "/>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write text.
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* Text to append
|
||||||
|
*/
|
||||||
|
public void writeText(String text) {
|
||||||
|
_buffer.append(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* Data to append
|
||||||
|
*/
|
||||||
|
public void writeData(String data) {
|
||||||
|
_buffer.append("<![CDATA[" + data + "]]>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write XML Header.
|
||||||
|
*/
|
||||||
|
public void writeXMLHeader() {
|
||||||
|
_buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data and reinitializes buffer.
|
||||||
|
*/
|
||||||
|
public void sendData() throws IOException {
|
||||||
|
if (_writer != null) {
|
||||||
|
_writer.write(_buffer.toString());
|
||||||
|
_writer.flush();
|
||||||
|
_buffer = new StringBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package net.sf.webdav.locking;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
|
||||||
|
public interface IResourceLocks {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to lock the resource at "path".
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* what resource to lock
|
||||||
|
* @param owner
|
||||||
|
* the owner of the lock
|
||||||
|
* @param exclusive
|
||||||
|
* if the lock should be exclusive (or shared)
|
||||||
|
* @param depth
|
||||||
|
* depth
|
||||||
|
* @param timeout
|
||||||
|
* Lock Duration in seconds.
|
||||||
|
* @return true if the resource at path was successfully locked, false if an
|
||||||
|
* existing lock prevented this
|
||||||
|
* @throws LockFailedException
|
||||||
|
*/
|
||||||
|
boolean lock(ITransaction transaction, String path, String owner,
|
||||||
|
boolean exclusive, int depth, int timeout, boolean temporary)
|
||||||
|
throws LockFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks all resources at "path" (and all subfolders if existing)<p/> that
|
||||||
|
* have the same owner.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param id
|
||||||
|
* id to the resource to unlock
|
||||||
|
* @param owner
|
||||||
|
* who wants to unlock
|
||||||
|
*/
|
||||||
|
boolean unlock(ITransaction transaction, String id, String owner);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks all resources at "path" (and all subfolders if existing)<p/> that
|
||||||
|
* have the same owner.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* what resource to unlock
|
||||||
|
* @param owner
|
||||||
|
* who wants to unlock
|
||||||
|
*/
|
||||||
|
void unlockTemporaryLockedObjects(ITransaction transaction, String path,
|
||||||
|
String owner);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes LockedObjects, where timeout has reached.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param temporary
|
||||||
|
* Check timeout on temporary or real locks
|
||||||
|
*/
|
||||||
|
void checkTimeouts(ITransaction transaction, boolean temporary);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to lock the resource at "path" exclusively.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* Transaction
|
||||||
|
* @param path
|
||||||
|
* what resource to lock
|
||||||
|
* @param owner
|
||||||
|
* the owner of the lock
|
||||||
|
* @param depth
|
||||||
|
* depth
|
||||||
|
* @param timeout
|
||||||
|
* Lock Duration in seconds.
|
||||||
|
* @return true if the resource at path was successfully locked, false if an
|
||||||
|
* existing lock prevented this
|
||||||
|
* @throws LockFailedException
|
||||||
|
*/
|
||||||
|
boolean exclusiveLock(ITransaction transaction, String path, String owner,
|
||||||
|
int depth, int timeout) throws LockFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to lock the resource at "path" shared.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* Transaction
|
||||||
|
* @param path
|
||||||
|
* what resource to lock
|
||||||
|
* @param owner
|
||||||
|
* the owner of the lock
|
||||||
|
* @param depth
|
||||||
|
* depth
|
||||||
|
* @param timeout
|
||||||
|
* Lock Duration in seconds.
|
||||||
|
* @return true if the resource at path was successfully locked, false if an
|
||||||
|
* existing lock prevented this
|
||||||
|
* @throws LockFailedException
|
||||||
|
*/
|
||||||
|
boolean sharedLock(ITransaction transaction, String path, String owner,
|
||||||
|
int depth, int timeout) throws LockFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LockedObject corresponding to specified id.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param id
|
||||||
|
* LockToken to requested resource
|
||||||
|
* @return LockedObject or null if no LockedObject on specified path exists
|
||||||
|
*/
|
||||||
|
LockedObject getLockedObjectByID(ITransaction transaction, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LockedObject on specified path.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* Path to requested resource
|
||||||
|
* @return LockedObject or null if no LockedObject on specified path exists
|
||||||
|
*/
|
||||||
|
LockedObject getLockedObjectByPath(ITransaction transaction, String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LockedObject corresponding to specified id (locktoken).
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param id
|
||||||
|
* LockToken to requested resource
|
||||||
|
* @return LockedObject or null if no LockedObject on specified path exists
|
||||||
|
*/
|
||||||
|
LockedObject getTempLockedObjectByID(ITransaction transaction, String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LockedObject on specified path.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* Path to requested resource
|
||||||
|
* @return LockedObject or null if no LockedObject on specified path exists
|
||||||
|
*/
|
||||||
|
LockedObject getTempLockedObjectByPath(ITransaction transaction, String path);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,422 @@
|
||||||
|
package net.sf.webdav.locking;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a helper class for ResourceLocks, represents the Locks
|
||||||
|
*
|
||||||
|
* @author re
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LockedObject {
|
||||||
|
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
|
||||||
|
private String _path;
|
||||||
|
|
||||||
|
private String _id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describing the depth of a locked collection. If the locked resource is
|
||||||
|
* not a collection, depth is 0 / doesn't matter.
|
||||||
|
*/
|
||||||
|
protected int _lockDepth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describing the timeout of a locked object (ms)
|
||||||
|
*/
|
||||||
|
protected long _expiresAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* owner of the lock. shared locks can have multiple owners. is null if no
|
||||||
|
* owner is present
|
||||||
|
*/
|
||||||
|
// protected String[] _owner = null;
|
||||||
|
protected String[] _owner = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* children of that lock
|
||||||
|
*/
|
||||||
|
protected LockedObject[] _children = null;
|
||||||
|
|
||||||
|
protected LockedObject _parent = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* weather the lock is exclusive or not. if owner=null the exclusive value
|
||||||
|
* doesn't matter
|
||||||
|
*/
|
||||||
|
protected boolean _exclusive = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* weather the lock is a write or read lock
|
||||||
|
*/
|
||||||
|
protected String _type = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param _resourceLocks
|
||||||
|
* the resourceLocks where locks are stored
|
||||||
|
* @param path
|
||||||
|
* the path to the locked object
|
||||||
|
* @param temporary
|
||||||
|
* indicates if the LockedObject should be temporary or not
|
||||||
|
*/
|
||||||
|
public LockedObject(ResourceLocks resLocks, String path, boolean temporary) {
|
||||||
|
_path = path;
|
||||||
|
_id = UUID.randomUUID().toString();
|
||||||
|
_resourceLocks = resLocks;
|
||||||
|
|
||||||
|
if (!temporary) {
|
||||||
|
_resourceLocks._locks.put(path, this);
|
||||||
|
_resourceLocks._locksByID.put(_id, this);
|
||||||
|
} else {
|
||||||
|
_resourceLocks._tempLocks.put(path, this);
|
||||||
|
_resourceLocks._tempLocksByID.put(_id, this);
|
||||||
|
}
|
||||||
|
_resourceLocks._cleanupCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds a new owner to a lock
|
||||||
|
*
|
||||||
|
* @param owner
|
||||||
|
* string that represents the owner
|
||||||
|
* @return true if the owner was added, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean addLockedObjectOwner(String owner) {
|
||||||
|
|
||||||
|
if (_owner == null) {
|
||||||
|
_owner = new String[1];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
int size = _owner.length;
|
||||||
|
String[] newLockObjectOwner = new String[size + 1];
|
||||||
|
|
||||||
|
// check if the owner is already here (that should actually not
|
||||||
|
// happen)
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (_owner[i].equals(owner)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(_owner, 0, newLockObjectOwner, 0, size);
|
||||||
|
_owner = newLockObjectOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
_owner[_owner.length - 1] = owner;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tries to remove the owner from the lock
|
||||||
|
*
|
||||||
|
* @param owner
|
||||||
|
* string that represents the owner
|
||||||
|
*/
|
||||||
|
public void removeLockedObjectOwner(String owner) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (_owner != null) {
|
||||||
|
int size = _owner.length;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
// check every owner if it is the requested one
|
||||||
|
if (_owner[i].equals(owner)) {
|
||||||
|
// remove the owner
|
||||||
|
size -= 1;
|
||||||
|
String[] newLockedObjectOwner = new String[size];
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
if (j < i) {
|
||||||
|
newLockedObjectOwner[j] = _owner[j];
|
||||||
|
} else {
|
||||||
|
newLockedObjectOwner[j] = _owner[j + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_owner = newLockedObjectOwner;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_owner.length == 0) {
|
||||||
|
_owner = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
System.out.println("LockedObject.removeLockedObjectOwner()");
|
||||||
|
System.out.println(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds a new child lock to this lock
|
||||||
|
*
|
||||||
|
* @param newChild
|
||||||
|
* new child
|
||||||
|
*/
|
||||||
|
public void addChild(LockedObject newChild) {
|
||||||
|
if (_children == null) {
|
||||||
|
_children = new LockedObject[0];
|
||||||
|
}
|
||||||
|
int size = _children.length;
|
||||||
|
LockedObject[] newChildren = new LockedObject[size + 1];
|
||||||
|
System.arraycopy(_children, 0, newChildren, 0, size);
|
||||||
|
newChildren[size] = newChild;
|
||||||
|
_children = newChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes this Lock object. assumes that it has no children and no owners
|
||||||
|
* (does not check this itself)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void removeLockedObject() {
|
||||||
|
if (this != _resourceLocks._root && !this.getPath().equals("/")) {
|
||||||
|
|
||||||
|
int size = _parent._children.length;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (_parent._children[i].equals(this)) {
|
||||||
|
LockedObject[] newChildren = new LockedObject[size - 1];
|
||||||
|
for (int i2 = 0; i2 < (size - 1); i2++) {
|
||||||
|
if (i2 < i) {
|
||||||
|
newChildren[i2] = _parent._children[i2];
|
||||||
|
} else {
|
||||||
|
newChildren[i2] = _parent._children[i2 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newChildren.length != 0) {
|
||||||
|
_parent._children = newChildren;
|
||||||
|
} else {
|
||||||
|
_parent._children = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removing from hashtable
|
||||||
|
_resourceLocks._locksByID.remove(getID());
|
||||||
|
_resourceLocks._locks.remove(getPath());
|
||||||
|
|
||||||
|
// now the garbage collector has some work to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes this Lock object. assumes that it has no children and no owners
|
||||||
|
* (does not check this itself)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void removeTempLockedObject() {
|
||||||
|
if (this != _resourceLocks._tempRoot) {
|
||||||
|
// removing from tree
|
||||||
|
if (_parent != null && _parent._children != null) {
|
||||||
|
int size = _parent._children.length;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (_parent._children[i].equals(this)) {
|
||||||
|
LockedObject[] newChildren = new LockedObject[size - 1];
|
||||||
|
for (int i2 = 0; i2 < (size - 1); i2++) {
|
||||||
|
if (i2 < i) {
|
||||||
|
newChildren[i2] = _parent._children[i2];
|
||||||
|
} else {
|
||||||
|
newChildren[i2] = _parent._children[i2 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newChildren.length != 0) {
|
||||||
|
_parent._children = newChildren;
|
||||||
|
} else {
|
||||||
|
_parent._children = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removing from hashtable
|
||||||
|
_resourceLocks._tempLocksByID.remove(getID());
|
||||||
|
_resourceLocks._tempLocks.remove(getPath());
|
||||||
|
|
||||||
|
// now the garbage collector has some work to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks if a lock of the given exclusivity can be placed, only considering
|
||||||
|
* children up to "depth"
|
||||||
|
*
|
||||||
|
* @param exclusive
|
||||||
|
* wheather the new lock should be exclusive
|
||||||
|
* @param depth
|
||||||
|
* the depth to which should be checked
|
||||||
|
* @return true if the lock can be placed
|
||||||
|
*/
|
||||||
|
public boolean checkLocks(boolean exclusive, int depth) {
|
||||||
|
if (checkParents(exclusive) && checkChildren(exclusive, depth)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper of checkLocks(). looks if the parents are locked
|
||||||
|
*
|
||||||
|
* @param exclusive
|
||||||
|
* wheather the new lock should be exclusive
|
||||||
|
* @return true if no locks at the parent path are forbidding a new lock
|
||||||
|
*/
|
||||||
|
private boolean checkParents(boolean exclusive) {
|
||||||
|
if (_path.equals("/")) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (_owner == null) {
|
||||||
|
// no owner, checking parents
|
||||||
|
return _parent != null && _parent.checkParents(exclusive);
|
||||||
|
} else {
|
||||||
|
// there already is a owner
|
||||||
|
return !(_exclusive || exclusive)
|
||||||
|
&& _parent.checkParents(exclusive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper of checkLocks(). looks if the children are locked
|
||||||
|
*
|
||||||
|
* @param exclusive
|
||||||
|
* wheather the new lock should be exclusive
|
||||||
|
* @return true if no locks at the children paths are forbidding a new lock
|
||||||
|
* @param depth
|
||||||
|
* depth
|
||||||
|
*/
|
||||||
|
private boolean checkChildren(boolean exclusive, int depth) {
|
||||||
|
if (_children == null) {
|
||||||
|
// a file
|
||||||
|
|
||||||
|
return _owner == null || !(_exclusive || exclusive);
|
||||||
|
} else {
|
||||||
|
// a folder
|
||||||
|
|
||||||
|
if (_owner == null) {
|
||||||
|
// no owner, checking children
|
||||||
|
|
||||||
|
if (depth != 0) {
|
||||||
|
boolean canLock = true;
|
||||||
|
int limit = _children.length;
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
if (!_children[i].checkChildren(exclusive, depth - 1)) {
|
||||||
|
canLock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canLock;
|
||||||
|
} else {
|
||||||
|
// depth == 0 -> we don't care for children
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// there already is a owner
|
||||||
|
return !(_exclusive || exclusive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new timeout for the LockedObject
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
*/
|
||||||
|
public void refreshTimeout(int timeout) {
|
||||||
|
_expiresAt = System.currentTimeMillis() + (timeout * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the timeout for the LockedObject
|
||||||
|
*
|
||||||
|
* @return timeout
|
||||||
|
*/
|
||||||
|
public long getTimeoutMillis() {
|
||||||
|
return (_expiresAt - System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the lock has expired.
|
||||||
|
*
|
||||||
|
* @return true if timeout has passed
|
||||||
|
*/
|
||||||
|
public boolean hasExpired() {
|
||||||
|
if (_expiresAt != 0) {
|
||||||
|
return (System.currentTimeMillis() > _expiresAt);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LockID (locktoken) for the LockedObject
|
||||||
|
*
|
||||||
|
* @return locktoken
|
||||||
|
*/
|
||||||
|
public String getID() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the owners for the LockedObject
|
||||||
|
*
|
||||||
|
* @return owners
|
||||||
|
*/
|
||||||
|
public String[] getOwner() {
|
||||||
|
return _owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path for the LockedObject
|
||||||
|
*
|
||||||
|
* @return path
|
||||||
|
*/
|
||||||
|
public String getPath() {
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the exclusivity for the LockedObject
|
||||||
|
*
|
||||||
|
* @param exclusive
|
||||||
|
*/
|
||||||
|
public void setExclusive(boolean exclusive) {
|
||||||
|
_exclusive = exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the exclusivity for the LockedObject
|
||||||
|
*
|
||||||
|
* @return exclusivity
|
||||||
|
*/
|
||||||
|
public boolean isExclusive() {
|
||||||
|
return _exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the exclusivity for the LockedObject
|
||||||
|
*
|
||||||
|
* @return exclusivity
|
||||||
|
*/
|
||||||
|
public boolean isShared() {
|
||||||
|
return !_exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of the lock
|
||||||
|
*
|
||||||
|
* @return type
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the depth of the lock
|
||||||
|
*
|
||||||
|
* @return depth
|
||||||
|
*/
|
||||||
|
public int getLockDepth() {
|
||||||
|
return _lockDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,384 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2005-2006 webdav-servlet group.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.locking;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simple locking management for concurrent data access, NOT the webdav locking.
|
||||||
|
* ( could that be used instead? )
|
||||||
|
*
|
||||||
|
* IT IS ACTUALLY USED FOR DOLOCK
|
||||||
|
*
|
||||||
|
* @author re
|
||||||
|
*/
|
||||||
|
public class ResourceLocks implements IResourceLocks {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(ResourceLocks.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* after creating this much LockedObjects, a cleanup deletes unused
|
||||||
|
* LockedObjects
|
||||||
|
*/
|
||||||
|
private final int _cleanupLimit = 100000;
|
||||||
|
|
||||||
|
protected int _cleanupCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keys: path value: LockedObject from that path
|
||||||
|
*/
|
||||||
|
protected Hashtable<String, LockedObject> _locks = new Hashtable<String, LockedObject>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keys: id value: LockedObject from that id
|
||||||
|
*/
|
||||||
|
protected Hashtable<String, LockedObject> _locksByID = new Hashtable<String, LockedObject>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keys: path value: Temporary LockedObject from that path
|
||||||
|
*/
|
||||||
|
protected Hashtable<String, LockedObject> _tempLocks = new Hashtable<String, LockedObject>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keys: id value: Temporary LockedObject from that id
|
||||||
|
*/
|
||||||
|
protected Hashtable<String, LockedObject> _tempLocksByID = new Hashtable<String, LockedObject>();
|
||||||
|
|
||||||
|
// REMEMBER TO REMOVE UNUSED LOCKS FROM THE HASHTABLE AS WELL
|
||||||
|
|
||||||
|
protected LockedObject _root = null;
|
||||||
|
|
||||||
|
protected LockedObject _tempRoot = null;
|
||||||
|
|
||||||
|
private boolean _temporary = true;
|
||||||
|
|
||||||
|
public ResourceLocks() {
|
||||||
|
_root = new LockedObject(this, "/", true);
|
||||||
|
_tempRoot = new LockedObject(this, "/", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean lock(ITransaction transaction, String path,
|
||||||
|
String owner, boolean exclusive, int depth, int timeout,
|
||||||
|
boolean temporary) throws LockFailedException {
|
||||||
|
|
||||||
|
LockedObject lo = null;
|
||||||
|
|
||||||
|
if (temporary) {
|
||||||
|
lo = generateTempLockedObjects(transaction, path);
|
||||||
|
lo._type = "read";
|
||||||
|
} else {
|
||||||
|
lo = generateLockedObjects(transaction, path);
|
||||||
|
lo._type = "write";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lo.checkLocks(exclusive, depth)) {
|
||||||
|
|
||||||
|
lo._exclusive = exclusive;
|
||||||
|
lo._lockDepth = depth;
|
||||||
|
lo._expiresAt = System.currentTimeMillis() + (timeout * 1000);
|
||||||
|
if (lo._parent != null) {
|
||||||
|
lo._parent._expiresAt = lo._expiresAt;
|
||||||
|
if (lo._parent.equals(_root)) {
|
||||||
|
LockedObject rootLo = getLockedObjectByPath(transaction,
|
||||||
|
_root.getPath());
|
||||||
|
rootLo._expiresAt = lo._expiresAt;
|
||||||
|
} else if (lo._parent.equals(_tempRoot)) {
|
||||||
|
LockedObject tempRootLo = getTempLockedObjectByPath(
|
||||||
|
transaction, _tempRoot.getPath());
|
||||||
|
tempRootLo._expiresAt = lo._expiresAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lo.addLockedObjectOwner(owner)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG.trace("Couldn't set owner \"" + owner
|
||||||
|
+ "\" to resource at '" + path + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// can not lock
|
||||||
|
LOG.trace("Lock resource at " + path + " failed because"
|
||||||
|
+ "\na parent or child resource is currently locked");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean unlock(ITransaction transaction, String id,
|
||||||
|
String owner) {
|
||||||
|
|
||||||
|
if (_locksByID.containsKey(id)) {
|
||||||
|
String path = _locksByID.get(id).getPath();
|
||||||
|
if (_locks.containsKey(path)) {
|
||||||
|
LockedObject lo = _locks.get(path);
|
||||||
|
lo.removeLockedObjectOwner(owner);
|
||||||
|
|
||||||
|
if (lo._children == null && lo._owner == null)
|
||||||
|
lo.removeLockedObject();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// there is no lock at that path. someone tried to unlock it
|
||||||
|
// anyway. could point to a problem
|
||||||
|
LOG
|
||||||
|
.trace("net.sf.webdav.locking.ResourceLocks.unlock(): no lock for path "
|
||||||
|
+ path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cleanupCounter > _cleanupLimit) {
|
||||||
|
_cleanupCounter = 0;
|
||||||
|
cleanLockedObjects(transaction, _root, !_temporary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkTimeouts(transaction, !_temporary);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unlockTemporaryLockedObjects(
|
||||||
|
ITransaction transaction, String path, String owner) {
|
||||||
|
if (_tempLocks.containsKey(path)) {
|
||||||
|
LockedObject lo = _tempLocks.get(path);
|
||||||
|
lo.removeLockedObjectOwner(owner);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// there is no lock at that path. someone tried to unlock it
|
||||||
|
// anyway. could point to a problem
|
||||||
|
LOG
|
||||||
|
.trace("net.sf.webdav.locking.ResourceLocks.unlock(): no lock for path "
|
||||||
|
+ path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cleanupCounter > _cleanupLimit) {
|
||||||
|
_cleanupCounter = 0;
|
||||||
|
cleanLockedObjects(transaction, _tempRoot, _temporary);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkTimeouts(transaction, _temporary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkTimeouts(ITransaction transaction, boolean temporary) {
|
||||||
|
if (!temporary) {
|
||||||
|
Enumeration<LockedObject> lockedObjects = _locks.elements();
|
||||||
|
while (lockedObjects.hasMoreElements()) {
|
||||||
|
LockedObject currentLockedObject = lockedObjects.nextElement();
|
||||||
|
|
||||||
|
if (currentLockedObject._expiresAt < System.currentTimeMillis()) {
|
||||||
|
currentLockedObject.removeLockedObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Enumeration<LockedObject> lockedObjects = _tempLocks.elements();
|
||||||
|
while (lockedObjects.hasMoreElements()) {
|
||||||
|
LockedObject currentLockedObject = lockedObjects.nextElement();
|
||||||
|
|
||||||
|
if (currentLockedObject._expiresAt < System.currentTimeMillis()) {
|
||||||
|
currentLockedObject.removeTempLockedObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exclusiveLock(ITransaction transaction, String path,
|
||||||
|
String owner, int depth, int timeout) throws LockFailedException {
|
||||||
|
return lock(transaction, path, owner, true, depth, timeout, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sharedLock(ITransaction transaction, String path,
|
||||||
|
String owner, int depth, int timeout) throws LockFailedException {
|
||||||
|
return lock(transaction, path, owner, false, depth, timeout, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockedObject getLockedObjectByID(ITransaction transaction, String id) {
|
||||||
|
if (_locksByID.containsKey(id)) {
|
||||||
|
return _locksByID.get(id);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockedObject getLockedObjectByPath(ITransaction transaction,
|
||||||
|
String path) {
|
||||||
|
if (_locks.containsKey(path)) {
|
||||||
|
return (LockedObject) this._locks.get(path);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockedObject getTempLockedObjectByID(ITransaction transaction,
|
||||||
|
String id) {
|
||||||
|
if (_tempLocksByID.containsKey(id)) {
|
||||||
|
return _tempLocksByID.get(id);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockedObject getTempLockedObjectByPath(ITransaction transaction,
|
||||||
|
String path) {
|
||||||
|
if (_tempLocks.containsKey(path)) {
|
||||||
|
return (LockedObject) this._tempLocks.get(path);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generates real LockedObjects for the resource at path and its parent
|
||||||
|
* folders. does not create new LockedObjects if they already exist
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* path to the (new) LockedObject
|
||||||
|
* @return the LockedObject for path.
|
||||||
|
*/
|
||||||
|
private LockedObject generateLockedObjects(ITransaction transaction,
|
||||||
|
String path) {
|
||||||
|
if (!_locks.containsKey(path)) {
|
||||||
|
LockedObject returnObject = new LockedObject(this, path,
|
||||||
|
!_temporary);
|
||||||
|
String parentPath = getParentPath(path);
|
||||||
|
if (parentPath != null) {
|
||||||
|
LockedObject parentLockedObject = generateLockedObjects(
|
||||||
|
transaction, parentPath);
|
||||||
|
parentLockedObject.addChild(returnObject);
|
||||||
|
returnObject._parent = parentLockedObject;
|
||||||
|
}
|
||||||
|
return returnObject;
|
||||||
|
} else {
|
||||||
|
// there is already a LockedObject on the specified path
|
||||||
|
return (LockedObject) this._locks.get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generates temporary LockedObjects for the resource at path and its parent
|
||||||
|
* folders. does not create new LockedObjects if they already exist
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* path to the (new) LockedObject
|
||||||
|
* @return the LockedObject for path.
|
||||||
|
*/
|
||||||
|
private LockedObject generateTempLockedObjects(ITransaction transaction,
|
||||||
|
String path) {
|
||||||
|
if (!_tempLocks.containsKey(path)) {
|
||||||
|
LockedObject returnObject = new LockedObject(this, path, _temporary);
|
||||||
|
String parentPath = getParentPath(path);
|
||||||
|
if (parentPath != null) {
|
||||||
|
LockedObject parentLockedObject = generateTempLockedObjects(
|
||||||
|
transaction, parentPath);
|
||||||
|
parentLockedObject.addChild(returnObject);
|
||||||
|
returnObject._parent = parentLockedObject;
|
||||||
|
}
|
||||||
|
return returnObject;
|
||||||
|
} else {
|
||||||
|
// there is already a LockedObject on the specified path
|
||||||
|
return (LockedObject) this._tempLocks.get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes unused LockedObjects and resets the counter. works recursively
|
||||||
|
* starting at the given LockedObject
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param lo
|
||||||
|
* LockedObject
|
||||||
|
* @param temporary
|
||||||
|
* Clean temporary or real locks
|
||||||
|
*
|
||||||
|
* @return if cleaned
|
||||||
|
*/
|
||||||
|
private boolean cleanLockedObjects(ITransaction transaction,
|
||||||
|
LockedObject lo, boolean temporary) {
|
||||||
|
|
||||||
|
if (lo._children == null) {
|
||||||
|
if (lo._owner == null) {
|
||||||
|
if (temporary) {
|
||||||
|
lo.removeTempLockedObject();
|
||||||
|
} else {
|
||||||
|
lo.removeLockedObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boolean canDelete = true;
|
||||||
|
int limit = lo._children.length;
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
if (!cleanLockedObjects(transaction, lo._children[i], temporary)) {
|
||||||
|
canDelete = false;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// because the deleting shifts the array
|
||||||
|
i--;
|
||||||
|
limit--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canDelete) {
|
||||||
|
if (lo._owner == null) {
|
||||||
|
if (temporary) {
|
||||||
|
lo.removeTempLockedObject();
|
||||||
|
} else {
|
||||||
|
lo.removeLockedObject();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates the parent path from the given path by removing the last '/' and
|
||||||
|
* everything after that
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* the path
|
||||||
|
* @return parent path
|
||||||
|
*/
|
||||||
|
private String getParentPath(String path) {
|
||||||
|
int slash = path.lastIndexOf('/');
|
||||||
|
if (slash == -1) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
if (slash == 0) {
|
||||||
|
// return "root" if parent path is empty string
|
||||||
|
return "/";
|
||||||
|
} else {
|
||||||
|
return path.substring(0, slash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,596 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import net.sf.webdav.IMethodExecutor;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.fromcatalina.RequestUtil;
|
||||||
|
import net.sf.webdav.fromcatalina.URLEncoder;
|
||||||
|
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||||
|
import net.sf.webdav.locking.IResourceLocks;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
|
||||||
|
public abstract class AbstractMethod implements IMethodExecutor {
|
||||||
|
|
||||||
|
private static final ThreadLocal<DateFormat> thLastmodifiedDateFormat = new ThreadLocal<DateFormat>();
|
||||||
|
private static final ThreadLocal<DateFormat> thCreationDateFormat = new ThreadLocal<DateFormat>();
|
||||||
|
private static final ThreadLocal<DateFormat> thLocalDateFormat = new ThreadLocal<DateFormat>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array containing the safe characters set.
|
||||||
|
*/
|
||||||
|
protected static URLEncoder URL_ENCODER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default depth is infite.
|
||||||
|
*/
|
||||||
|
protected static final int INFINITY = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple date format for the creation date ISO 8601 representation
|
||||||
|
* (partial).
|
||||||
|
*/
|
||||||
|
protected static final String CREATION_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple date format for the last modified date. (RFC 822 updated by RFC
|
||||||
|
* 1123)
|
||||||
|
*/
|
||||||
|
protected static final String LAST_MODIFIED_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
|
||||||
|
|
||||||
|
protected static final String LOCAL_DATE_FORMAT = "dd/MM/yy' 'HH:mm:ss";
|
||||||
|
|
||||||
|
static {
|
||||||
|
/**
|
||||||
|
* GMT timezone - all HTTP dates are on GMT
|
||||||
|
*/
|
||||||
|
URL_ENCODER = new URLEncoder();
|
||||||
|
URL_ENCODER.addSafeCharacter('-');
|
||||||
|
URL_ENCODER.addSafeCharacter('_');
|
||||||
|
URL_ENCODER.addSafeCharacter('.');
|
||||||
|
URL_ENCODER.addSafeCharacter('*');
|
||||||
|
URL_ENCODER.addSafeCharacter('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* size of the io-buffer
|
||||||
|
*/
|
||||||
|
protected static int BUF_SIZE = 65536;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default lock timeout value.
|
||||||
|
*/
|
||||||
|
protected static final int DEFAULT_TIMEOUT = 3600;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum lock timeout.
|
||||||
|
*/
|
||||||
|
protected static final int MAX_TIMEOUT = 604800;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean value to temporary lock resources (for method locks)
|
||||||
|
*/
|
||||||
|
protected static final boolean TEMPORARY = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout for temporary locks
|
||||||
|
*/
|
||||||
|
protected static final int TEMP_TIMEOUT = 10;
|
||||||
|
|
||||||
|
|
||||||
|
public static String lastModifiedDateFormat(final Date date) {
|
||||||
|
DateFormat df = thLastmodifiedDateFormat.get();
|
||||||
|
if( df == null ) {
|
||||||
|
df = new SimpleDateFormat(LAST_MODIFIED_DATE_FORMAT, Locale.US);
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
thLastmodifiedDateFormat.set( df );
|
||||||
|
}
|
||||||
|
return df.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String creationDateFormat(final Date date) {
|
||||||
|
DateFormat df = thCreationDateFormat.get();
|
||||||
|
if( df == null ) {
|
||||||
|
df = new SimpleDateFormat(CREATION_DATE_FORMAT);
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
thCreationDateFormat.set( df );
|
||||||
|
}
|
||||||
|
return df.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLocalDateFormat(final Date date, final Locale loc) {
|
||||||
|
DateFormat df = thLocalDateFormat.get();
|
||||||
|
if( df == null ) {
|
||||||
|
df = new SimpleDateFormat(LOCAL_DATE_FORMAT, loc);
|
||||||
|
}
|
||||||
|
return df.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses and normalizes the destination header.
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* Servlet request
|
||||||
|
* @param resp
|
||||||
|
* Servlet response
|
||||||
|
* @return destinationPath
|
||||||
|
* @throws IOException
|
||||||
|
* if an error occurs while sending response
|
||||||
|
*/
|
||||||
|
protected String parseDestinationHeader(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException {
|
||||||
|
String destinationPath = req.getHeader("Destination");
|
||||||
|
|
||||||
|
if (destinationPath == null) {
|
||||||
|
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove url encoding from destination
|
||||||
|
destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
|
||||||
|
|
||||||
|
int protocolIndex = destinationPath.indexOf("://");
|
||||||
|
if (protocolIndex >= 0) {
|
||||||
|
// if the Destination URL contains the protocol, we can safely
|
||||||
|
// trim everything upto the first "/" character after "://"
|
||||||
|
int firstSeparator = destinationPath
|
||||||
|
.indexOf("/", protocolIndex + 4);
|
||||||
|
if (firstSeparator < 0) {
|
||||||
|
destinationPath = "/";
|
||||||
|
} else {
|
||||||
|
destinationPath = destinationPath.substring(firstSeparator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String hostName = req.getServerName();
|
||||||
|
if ((hostName != null) && (destinationPath.startsWith(hostName))) {
|
||||||
|
destinationPath = destinationPath.substring(hostName.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
int portIndex = destinationPath.indexOf(":");
|
||||||
|
if (portIndex >= 0) {
|
||||||
|
destinationPath = destinationPath.substring(portIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destinationPath.startsWith(":")) {
|
||||||
|
int firstSeparator = destinationPath.indexOf("/");
|
||||||
|
if (firstSeparator < 0) {
|
||||||
|
destinationPath = "/";
|
||||||
|
} else {
|
||||||
|
destinationPath = destinationPath.substring(firstSeparator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize destination path (remove '.' and' ..')
|
||||||
|
destinationPath = normalize(destinationPath);
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return destinationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @return normalized path
|
||||||
|
*/
|
||||||
|
protected String normalize(String path) {
|
||||||
|
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Create a place for the normalized path
|
||||||
|
String normalized = path;
|
||||||
|
|
||||||
|
if (normalized.equals("/."))
|
||||||
|
return "/";
|
||||||
|
|
||||||
|
// Normalize the slashes and add leading slash if necessary
|
||||||
|
if (normalized.indexOf('\\') >= 0)
|
||||||
|
normalized = normalized.replace('\\', '/');
|
||||||
|
if (!normalized.startsWith("/"))
|
||||||
|
normalized = "/" + normalized;
|
||||||
|
|
||||||
|
// Resolve occurrences of "//" in the normalized path
|
||||||
|
while (true) {
|
||||||
|
int index = normalized.indexOf("//");
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
normalized = normalized.substring(0, index)
|
||||||
|
+ normalized.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve occurrences of "/./" in the normalized path
|
||||||
|
while (true) {
|
||||||
|
int index = normalized.indexOf("/./");
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
normalized = normalized.substring(0, index)
|
||||||
|
+ normalized.substring(index + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve occurrences of "/../" in the normalized path
|
||||||
|
while (true) {
|
||||||
|
int index = normalized.indexOf("/../");
|
||||||
|
if (index < 0)
|
||||||
|
break;
|
||||||
|
if (index == 0)
|
||||||
|
return (null); // Trying to go outside our context
|
||||||
|
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||||
|
normalized = normalized.substring(0, index2)
|
||||||
|
+ normalized.substring(index + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the normalized path that we have completed
|
||||||
|
return (normalized);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates the parent path from the given path by removing the last '/' and
|
||||||
|
* everything after that
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* the path
|
||||||
|
* @return parent path
|
||||||
|
*/
|
||||||
|
protected String getParentPath(String path) {
|
||||||
|
int slash = path.lastIndexOf('/');
|
||||||
|
if (slash != -1) {
|
||||||
|
return path.substring(0, slash);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removes a / at the end of the path string, if present
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* the path
|
||||||
|
* @return the path without trailing /
|
||||||
|
*/
|
||||||
|
protected String getCleanPath(String path) {
|
||||||
|
|
||||||
|
if (path.endsWith("/") && path.length() > 1)
|
||||||
|
path = path.substring(0, path.length() - 1);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return JAXP document builder instance.
|
||||||
|
*/
|
||||||
|
protected DocumentBuilder getDocumentBuilder() throws ServletException {
|
||||||
|
DocumentBuilder documentBuilder = null;
|
||||||
|
DocumentBuilderFactory documentBuilderFactory = null;
|
||||||
|
try {
|
||||||
|
documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
documentBuilderFactory.setNamespaceAware(true);
|
||||||
|
documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
throw new ServletException("jaxp failed");
|
||||||
|
}
|
||||||
|
return documentBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reads the depth header from the request and returns it as a int
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return the depth from the depth header
|
||||||
|
*/
|
||||||
|
protected int getDepth(HttpServletRequest req) {
|
||||||
|
int depth = INFINITY;
|
||||||
|
String depthStr = req.getHeader("Depth");
|
||||||
|
if (depthStr != null) {
|
||||||
|
if (depthStr.equals("0")) {
|
||||||
|
depth = 0;
|
||||||
|
} else if (depthStr.equals("1")) {
|
||||||
|
depth = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL rewriter.
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* Path which has to be rewiten
|
||||||
|
* @return the rewritten path
|
||||||
|
*/
|
||||||
|
protected String rewriteUrl(String path) {
|
||||||
|
return URL_ENCODER.encode(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ETag associated with a file.
|
||||||
|
*
|
||||||
|
* @param StoredObject
|
||||||
|
* StoredObject to get resourceLength, lastModified and a hashCode of
|
||||||
|
* StoredObject
|
||||||
|
* @return the ETag
|
||||||
|
*/
|
||||||
|
protected String getETag(StoredObject so) {
|
||||||
|
|
||||||
|
String resourceLength = "";
|
||||||
|
String lastModified = "";
|
||||||
|
|
||||||
|
if (so != null && so.isResource()) {
|
||||||
|
resourceLength = new Long(so.getResourceLength()).toString();
|
||||||
|
lastModified = new Long(so.getLastModified().getTime()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "W/\"" + resourceLength + "-" + lastModified + "\"";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[] getLockIdFromIfHeader(HttpServletRequest req) {
|
||||||
|
String[] ids = new String[2];
|
||||||
|
String id = req.getHeader("If");
|
||||||
|
|
||||||
|
if (id != null && !id.equals("")) {
|
||||||
|
if (id.indexOf(">)") == id.lastIndexOf(">)")) {
|
||||||
|
id = id.substring(id.indexOf("(<"), id.indexOf(">)"));
|
||||||
|
|
||||||
|
if (id.indexOf("locktoken:") != -1) {
|
||||||
|
id = id.substring(id.indexOf(':') + 1);
|
||||||
|
}
|
||||||
|
ids[0] = id;
|
||||||
|
} else {
|
||||||
|
String firstId = id.substring(id.indexOf("(<"), id
|
||||||
|
.indexOf(">)"));
|
||||||
|
if (firstId.indexOf("locktoken:") != -1) {
|
||||||
|
firstId = firstId.substring(firstId.indexOf(':') + 1);
|
||||||
|
}
|
||||||
|
ids[0] = firstId;
|
||||||
|
|
||||||
|
String secondId = id.substring(id.lastIndexOf("(<"), id
|
||||||
|
.lastIndexOf(">)"));
|
||||||
|
if (secondId.indexOf("locktoken:") != -1) {
|
||||||
|
secondId = secondId.substring(secondId.indexOf(':') + 1);
|
||||||
|
}
|
||||||
|
ids[1] = secondId;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ids = null;
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getLockIdFromLockTokenHeader(HttpServletRequest req) {
|
||||||
|
String id = req.getHeader("Lock-Token");
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
id = id.substring(id.indexOf(":") + 1, id.indexOf(">"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if locks on resources at the given path exists and if so checks
|
||||||
|
* the If-Header to make sure the If-Header corresponds to the locked
|
||||||
|
* resource. Returning true if no lock exists or the If-Header is
|
||||||
|
* corresponding to the locked resource
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* Servlet request
|
||||||
|
* @param resp
|
||||||
|
* Servlet response
|
||||||
|
* @param resourceLocks
|
||||||
|
* @param path
|
||||||
|
* path to the resource
|
||||||
|
* @param errorList
|
||||||
|
* List of error to be displayed
|
||||||
|
* @return true if no lock on a resource with the given path exists or if
|
||||||
|
* the If-Header corresponds to the locked resource
|
||||||
|
* @throws IOException
|
||||||
|
* @throws LockFailedException
|
||||||
|
*/
|
||||||
|
protected boolean checkLocks(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp,
|
||||||
|
IResourceLocks resourceLocks, String path) throws IOException,
|
||||||
|
LockFailedException {
|
||||||
|
|
||||||
|
LockedObject loByPath = resourceLocks.getLockedObjectByPath(
|
||||||
|
transaction, path);
|
||||||
|
if (loByPath != null) {
|
||||||
|
|
||||||
|
if (loByPath.isShared())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// the resource is locked
|
||||||
|
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||||
|
String lockToken = null;
|
||||||
|
if (lockTokens != null)
|
||||||
|
lockToken = lockTokens[0];
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lockToken != null) {
|
||||||
|
LockedObject loByIf = resourceLocks.getLockedObjectByID(
|
||||||
|
transaction, lockToken);
|
||||||
|
if (loByIf == null) {
|
||||||
|
// no locked resource to the given lockToken
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!loByIf.equals(loByPath)) {
|
||||||
|
loByIf = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
loByIf = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
loByPath = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a multistatus element containing a complete error report to the
|
||||||
|
* client. If the errorList contains only one error, send the error
|
||||||
|
* directly without wrapping it in a multistatus message.
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* Servlet request
|
||||||
|
* @param resp
|
||||||
|
* Servlet response
|
||||||
|
* @param errorList
|
||||||
|
* List of error to be displayed
|
||||||
|
*/
|
||||||
|
protected void sendReport(HttpServletRequest req, HttpServletResponse resp,
|
||||||
|
Hashtable<String, Integer> errorList) throws IOException {
|
||||||
|
|
||||||
|
if (errorList.size() == 1) {
|
||||||
|
int code = errorList.elements().nextElement();
|
||||||
|
if (WebdavStatus.getStatusText(code) != "") {
|
||||||
|
resp.sendError(code, WebdavStatus.getStatusText(code));
|
||||||
|
} else {
|
||||||
|
resp.sendError(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||||
|
|
||||||
|
String absoluteUri = req.getRequestURI();
|
||||||
|
// String relativePath = getRelativePath(req);
|
||||||
|
|
||||||
|
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||||
|
namespaces.put("DAV:", "D");
|
||||||
|
|
||||||
|
XMLWriter generatedXML = new XMLWriter(namespaces);
|
||||||
|
generatedXML.writeXMLHeader();
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
Enumeration<String> pathList = errorList.keys();
|
||||||
|
while (pathList.hasMoreElements()) {
|
||||||
|
|
||||||
|
String errorPath = (String) pathList.nextElement();
|
||||||
|
int errorCode = ((Integer) errorList.get(errorPath)).intValue();
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
String toAppend = null;
|
||||||
|
if (absoluteUri.endsWith(errorPath)) {
|
||||||
|
toAppend = absoluteUri;
|
||||||
|
|
||||||
|
} else if (absoluteUri.contains(errorPath)) {
|
||||||
|
|
||||||
|
int endIndex = absoluteUri.indexOf(errorPath)
|
||||||
|
+ errorPath.length();
|
||||||
|
toAppend = absoluteUri.substring(0, endIndex);
|
||||||
|
}
|
||||||
|
if (!toAppend.startsWith("/") && !toAppend.startsWith("http:"))
|
||||||
|
toAppend = "/" + toAppend;
|
||||||
|
generatedXML.writeText(errorPath);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText("HTTP/1.1 " + errorCode + " "
|
||||||
|
+ WebdavStatus.getStatusText(errorCode));
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
Writer writer = resp.getWriter();
|
||||||
|
writer.write(generatedXML.toString());
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
|
||||||
|
public abstract class DeterminableMethod extends AbstractMethod {
|
||||||
|
|
||||||
|
private static final String NULL_RESOURCE_METHODS_ALLOWED = "OPTIONS, MKCOL, PUT, PROPFIND, LOCK, UNLOCK";
|
||||||
|
|
||||||
|
private static final String RESOURCE_METHODS_ALLOWED = "OPTIONS, GET, HEAD, POST, DELETE, TRACE"
|
||||||
|
+ ", PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND";
|
||||||
|
|
||||||
|
private static final String FOLDER_METHOD_ALLOWED = ", PUT";
|
||||||
|
|
||||||
|
private static final String LESS_ALLOWED_METHODS = "OPTIONS, MKCOL, PUT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the methods normally allowed for the resource.
|
||||||
|
*
|
||||||
|
* @param so
|
||||||
|
* StoredObject representing the resource
|
||||||
|
* @return all allowed methods, separated by commas
|
||||||
|
*/
|
||||||
|
protected static String determineMethodsAllowed(StoredObject so) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (so != null) {
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
|
||||||
|
return NULL_RESOURCE_METHODS_ALLOWED;
|
||||||
|
|
||||||
|
} else if (so.isFolder()) {
|
||||||
|
return RESOURCE_METHODS_ALLOWED + FOLDER_METHOD_ALLOWED;
|
||||||
|
}
|
||||||
|
// else resource
|
||||||
|
return RESOURCE_METHODS_ALLOWED;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// we do nothing, just return less allowed methods
|
||||||
|
}
|
||||||
|
|
||||||
|
return LESS_ALLOWED_METHODS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||||
|
import net.sf.webdav.exceptions.ObjectNotFoundException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.fromcatalina.RequestUtil;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
public class DoCopy extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoCopy.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
private DoDelete _doDelete;
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
public DoCopy(IWebdavStore store, ResourceLocks resourceLocks,
|
||||||
|
DoDelete doDelete, boolean readOnly) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_doDelete = doDelete;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
if (!_readOnly) {
|
||||||
|
|
||||||
|
String tempLockOwner = "doCopy" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
try {
|
||||||
|
if (!copyResource(transaction, req, resp))
|
||||||
|
return;
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (ObjectAlreadyExistsException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_CONFLICT, req
|
||||||
|
.getRequestURI());
|
||||||
|
} catch (ObjectNotFoundException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||||
|
.getRequestURI());
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
path, tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a resource.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param req
|
||||||
|
* Servlet request
|
||||||
|
* @param resp
|
||||||
|
* Servlet response
|
||||||
|
* @return true if the copy is successful
|
||||||
|
* @throws WebdavException
|
||||||
|
* if an error in the underlying store occurs
|
||||||
|
* @throws IOException
|
||||||
|
* when an error occurs while sending the response
|
||||||
|
* @throws LockFailedException
|
||||||
|
*/
|
||||||
|
public boolean copyResource(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws WebdavException, IOException, LockFailedException {
|
||||||
|
|
||||||
|
// Parsing destination header
|
||||||
|
String destinationPath = parseDestinationHeader(req, resp);
|
||||||
|
|
||||||
|
if (destinationPath == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
|
||||||
|
if (path.equals(destinationPath)) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
String parentDestinationPath = getParentPath(getCleanPath(destinationPath));
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks,
|
||||||
|
parentDestinationPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return false; // parentDestination is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, destinationPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return false; // destination is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsing overwrite header
|
||||||
|
|
||||||
|
boolean overwrite = true;
|
||||||
|
String overwriteHeader = req.getHeader("Overwrite");
|
||||||
|
|
||||||
|
if (overwriteHeader != null) {
|
||||||
|
overwrite = overwriteHeader.equalsIgnoreCase("T");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwriting the destination
|
||||||
|
String lockOwner = "copyResource" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
|
||||||
|
if (_resourceLocks.lock(transaction, destinationPath, lockOwner, false,
|
||||||
|
0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
StoredObject copySo, destinationSo = null;
|
||||||
|
try {
|
||||||
|
copySo = _store.getStoredObject(transaction, path);
|
||||||
|
// Retrieve the resources
|
||||||
|
if (copySo == null) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copySo.isNullResource()) {
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(copySo);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
destinationSo = _store.getStoredObject(transaction,
|
||||||
|
destinationPath);
|
||||||
|
|
||||||
|
if (overwrite) {
|
||||||
|
|
||||||
|
// Delete destination resource, if it exists
|
||||||
|
if (destinationSo != null) {
|
||||||
|
_doDelete.deleteResource(transaction, destinationPath,
|
||||||
|
errorList, req, resp);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// If the destination exists, then it's a conflict
|
||||||
|
if (destinationSo != null) {
|
||||||
|
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
copy(transaction, path, destinationPath, errorList, req, resp);
|
||||||
|
|
||||||
|
if (!errorList.isEmpty()) {
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
destinationPath, lockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copies the specified resource(s) to the specified destination.
|
||||||
|
* preconditions must be handled by the caller. Standard status codes must
|
||||||
|
* be handled by the caller. a multi status report in case of errors is
|
||||||
|
* created here.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param sourcePath
|
||||||
|
* path from where to read
|
||||||
|
* @param destinationPath
|
||||||
|
* path where to write
|
||||||
|
* @param req
|
||||||
|
* HttpServletRequest
|
||||||
|
* @param resp
|
||||||
|
* HttpServletResponse
|
||||||
|
* @throws WebdavException
|
||||||
|
* if an error in the underlying store occurs
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void copy(ITransaction transaction, String sourcePath,
|
||||||
|
String destinationPath, Hashtable<String, Integer> errorList,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws WebdavException, IOException {
|
||||||
|
|
||||||
|
StoredObject sourceSo = _store.getStoredObject(transaction, sourcePath);
|
||||||
|
if (sourceSo.isResource()) {
|
||||||
|
_store.createResource(transaction, destinationPath);
|
||||||
|
long resourceLength = _store.setResourceContent(transaction,
|
||||||
|
destinationPath, _store.getResourceContent(transaction,
|
||||||
|
sourcePath), null, null);
|
||||||
|
|
||||||
|
if (resourceLength != -1) {
|
||||||
|
StoredObject destinationSo = _store.getStoredObject(
|
||||||
|
transaction, destinationPath);
|
||||||
|
destinationSo.setResourceLength(resourceLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (sourceSo.isFolder()) {
|
||||||
|
copyFolder(transaction, sourcePath, destinationPath, errorList,
|
||||||
|
req, resp);
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper method of copy() recursively copies the FOLDER at source path to
|
||||||
|
* destination path
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param sourcePath
|
||||||
|
* where to read
|
||||||
|
* @param destinationPath
|
||||||
|
* where to write
|
||||||
|
* @param errorList
|
||||||
|
* all errors that ocurred
|
||||||
|
* @param req
|
||||||
|
* HttpServletRequest
|
||||||
|
* @param resp
|
||||||
|
* HttpServletResponse
|
||||||
|
* @throws WebdavException
|
||||||
|
* if an error in the underlying store occurs
|
||||||
|
*/
|
||||||
|
private void copyFolder(ITransaction transaction, String sourcePath,
|
||||||
|
String destinationPath, Hashtable<String, Integer> errorList,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws WebdavException {
|
||||||
|
|
||||||
|
_store.createFolder(transaction, destinationPath);
|
||||||
|
boolean infiniteDepth = true;
|
||||||
|
String depth = req.getHeader("Depth");
|
||||||
|
if (depth != null) {
|
||||||
|
if (depth.equals("0")) {
|
||||||
|
infiniteDepth = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (infiniteDepth) {
|
||||||
|
String[] children = _store
|
||||||
|
.getChildrenNames(transaction, sourcePath);
|
||||||
|
children = children == null ? new String[] {} : children;
|
||||||
|
|
||||||
|
StoredObject childSo;
|
||||||
|
for (int i = children.length - 1; i >= 0; i--) {
|
||||||
|
children[i] = "/" + children[i];
|
||||||
|
try {
|
||||||
|
childSo = _store.getStoredObject(transaction,
|
||||||
|
(sourcePath + children[i]));
|
||||||
|
if (childSo.isResource()) {
|
||||||
|
_store.createResource(transaction, destinationPath
|
||||||
|
+ children[i]);
|
||||||
|
long resourceLength = _store.setResourceContent(
|
||||||
|
transaction, destinationPath + children[i],
|
||||||
|
_store.getResourceContent(transaction,
|
||||||
|
sourcePath + children[i]), null, null);
|
||||||
|
|
||||||
|
if (resourceLength != -1) {
|
||||||
|
StoredObject destinationSo = _store
|
||||||
|
.getStoredObject(transaction,
|
||||||
|
destinationPath + children[i]);
|
||||||
|
destinationSo.setResourceLength(resourceLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
copyFolder(transaction, sourcePath + children[i],
|
||||||
|
destinationPath + children[i], errorList, req,
|
||||||
|
resp);
|
||||||
|
}
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
errorList.put(destinationPath + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_FORBIDDEN));
|
||||||
|
} catch (ObjectNotFoundException e) {
|
||||||
|
errorList.put(destinationPath + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_NOT_FOUND));
|
||||||
|
} catch (ObjectAlreadyExistsException e) {
|
||||||
|
errorList.put(destinationPath + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_CONFLICT));
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
errorList.put(destinationPath + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_INTERNAL_SERVER_ERROR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||||
|
import net.sf.webdav.exceptions.ObjectNotFoundException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
public class DoDelete extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoDelete.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
public DoDelete(IWebdavStore store, ResourceLocks resourceLocks,
|
||||||
|
boolean readOnly) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
if (!_readOnly) {
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
String parentPath = getParentPath(getCleanPath(path));
|
||||||
|
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // parent is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // resource is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
String tempLockOwner = "doDelete" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
try {
|
||||||
|
errorList = new Hashtable<String, Integer>();
|
||||||
|
deleteResource(transaction, path, errorList, req, resp);
|
||||||
|
if (!errorList.isEmpty()) {
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (ObjectAlreadyExistsException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||||
|
.getRequestURI());
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
path, tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes the recources at "path"
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param path
|
||||||
|
* the folder to be deleted
|
||||||
|
* @param errorList
|
||||||
|
* all errors that ocurred
|
||||||
|
* @param req
|
||||||
|
* HttpServletRequest
|
||||||
|
* @param resp
|
||||||
|
* HttpServletResponse
|
||||||
|
* @throws WebdavException
|
||||||
|
* if an error in the underlying store occurs
|
||||||
|
* @throws IOException
|
||||||
|
* when an error occurs while sending the response
|
||||||
|
*/
|
||||||
|
public void deleteResource(ITransaction transaction, String path,
|
||||||
|
Hashtable<String, Integer> errorList, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, WebdavException {
|
||||||
|
|
||||||
|
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||||
|
|
||||||
|
if (!_readOnly) {
|
||||||
|
|
||||||
|
StoredObject so = _store.getStoredObject(transaction, path);
|
||||||
|
if (so != null) {
|
||||||
|
|
||||||
|
if (so.isResource()) {
|
||||||
|
_store.removeObject(transaction, path);
|
||||||
|
} else {
|
||||||
|
if (so.isFolder()) {
|
||||||
|
deleteFolder(transaction, path, errorList, req, resp);
|
||||||
|
_store.removeObject(transaction, path);
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
so = null;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* helper method of deleteResource() deletes the folder and all of its
|
||||||
|
* contents
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* indicates that the method is within the scope of a WebDAV
|
||||||
|
* transaction
|
||||||
|
* @param path
|
||||||
|
* the folder to be deleted
|
||||||
|
* @param errorList
|
||||||
|
* all errors that ocurred
|
||||||
|
* @param req
|
||||||
|
* HttpServletRequest
|
||||||
|
* @param resp
|
||||||
|
* HttpServletResponse
|
||||||
|
* @throws WebdavException
|
||||||
|
* if an error in the underlying store occurs
|
||||||
|
*/
|
||||||
|
private void deleteFolder(ITransaction transaction, String path,
|
||||||
|
Hashtable<String, Integer> errorList, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws WebdavException {
|
||||||
|
|
||||||
|
String[] children = _store.getChildrenNames(transaction, path);
|
||||||
|
children = children == null ? new String[] {} : children;
|
||||||
|
StoredObject so = null;
|
||||||
|
for (int i = children.length - 1; i >= 0; i--) {
|
||||||
|
children[i] = "/" + children[i];
|
||||||
|
try {
|
||||||
|
so = _store.getStoredObject(transaction, path + children[i]);
|
||||||
|
if (so.isResource()) {
|
||||||
|
_store.removeObject(transaction, path + children[i]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
deleteFolder(transaction, path + children[i], errorList,
|
||||||
|
req, resp);
|
||||||
|
|
||||||
|
_store.removeObject(transaction, path + children[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
errorList.put(path + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_FORBIDDEN));
|
||||||
|
} catch (ObjectNotFoundException e) {
|
||||||
|
errorList.put(path + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_NOT_FOUND));
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
errorList.put(path + children[i], new Integer(
|
||||||
|
WebdavStatus.SC_INTERNAL_SERVER_ERROR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
so = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,307 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.IMimeTyper;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
public class DoGet extends DoHead {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoGet.class);
|
||||||
|
|
||||||
|
public DoGet(IWebdavStore store, String dftIndexFile, String insteadOf404,
|
||||||
|
ResourceLocks resourceLocks, IMimeTyper mimeTyper,
|
||||||
|
int contentLengthHeader) {
|
||||||
|
super(store, dftIndexFile, insteadOf404, resourceLocks, mimeTyper,
|
||||||
|
contentLengthHeader);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doBody(ITransaction transaction, HttpServletResponse resp,
|
||||||
|
String path) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
StoredObject so = _store.getStoredObject(transaction, path);
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(so);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
InputStream in = _store.getResourceContent(transaction, path);
|
||||||
|
try {
|
||||||
|
int read = -1;
|
||||||
|
byte[] copyBuffer = new byte[BUF_SIZE];
|
||||||
|
|
||||||
|
while ((read = in.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||||
|
out.write(copyBuffer, 0, read);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// flushing causes a IOE if a file is opened on the webserver
|
||||||
|
// client disconnected before server finished sending response
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Closing InputStream causes Exception!\n"
|
||||||
|
+ e.toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Flushing OutputStream causes Exception!\n"
|
||||||
|
+ e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.trace(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void folderBody(ITransaction transaction, String path,
|
||||||
|
HttpServletResponse resp, HttpServletRequest req)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
StoredObject so = _store.getStoredObject(transaction, path);
|
||||||
|
if (so == null) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_FOUND, req
|
||||||
|
.getRequestURI());
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(so);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (so.isFolder()) {
|
||||||
|
// TODO some folder response (for browsers, DAV tools
|
||||||
|
// use propfind) in html?
|
||||||
|
Locale locale = req.getLocale();
|
||||||
|
DateFormat shortDF= getDateTimeFormat(req.getLocale());
|
||||||
|
resp.setContentType("text/html");
|
||||||
|
resp.setCharacterEncoding("UTF8");
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
String[] children = _store.getChildrenNames(transaction, path);
|
||||||
|
// Make sure it's not null
|
||||||
|
children = children == null ? new String[] {} : children;
|
||||||
|
// Sort by name
|
||||||
|
Arrays.sort(children);
|
||||||
|
StringBuilder childrenTemp = new StringBuilder();
|
||||||
|
childrenTemp.append("<html><head><title>Content of folder");
|
||||||
|
childrenTemp.append(path);
|
||||||
|
childrenTemp.append("</title><style type=\"text/css\">");
|
||||||
|
childrenTemp.append(getCSS());
|
||||||
|
childrenTemp.append("</style></head>");
|
||||||
|
childrenTemp.append("<body>");
|
||||||
|
childrenTemp.append(getHeader(transaction, path, resp, req));
|
||||||
|
childrenTemp.append("<table>");
|
||||||
|
childrenTemp.append("<tr><th>Name</th><th>Size</th><th>Created</th><th>Modified</th></tr>");
|
||||||
|
childrenTemp.append("<tr>");
|
||||||
|
childrenTemp.append("<td colspan=\"4\"><a href=\"../\">Parent</a></td></tr>");
|
||||||
|
boolean isEven= false;
|
||||||
|
for (String child : children)
|
||||||
|
{
|
||||||
|
isEven= !isEven;
|
||||||
|
childrenTemp.append("<tr class=\"");
|
||||||
|
childrenTemp.append(isEven ? "even" : "odd");
|
||||||
|
childrenTemp.append("\">");
|
||||||
|
childrenTemp.append("<td>");
|
||||||
|
childrenTemp.append("<a href=\"");
|
||||||
|
childrenTemp.append(child);
|
||||||
|
StoredObject obj= _store.getStoredObject(transaction, path+"/"+child);
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
LOG.error("Should not return null for "+path+"/"+child);
|
||||||
|
}
|
||||||
|
if (obj != null && obj.isFolder())
|
||||||
|
{
|
||||||
|
childrenTemp.append("/");
|
||||||
|
}
|
||||||
|
childrenTemp.append("\">");
|
||||||
|
childrenTemp.append(child);
|
||||||
|
childrenTemp.append("</a></td>");
|
||||||
|
if (obj != null && obj.isFolder())
|
||||||
|
{
|
||||||
|
childrenTemp.append("<td>Folder</td>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childrenTemp.append("<td>");
|
||||||
|
if (obj != null )
|
||||||
|
{
|
||||||
|
childrenTemp.append(obj.getResourceLength());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childrenTemp.append("Unknown");
|
||||||
|
}
|
||||||
|
childrenTemp.append(" Bytes</td>");
|
||||||
|
}
|
||||||
|
if (obj != null && obj.getCreationDate() != null)
|
||||||
|
{
|
||||||
|
childrenTemp.append("<td>");
|
||||||
|
childrenTemp.append(shortDF.format(obj.getCreationDate()));
|
||||||
|
childrenTemp.append("</td>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childrenTemp.append("<td></td>");
|
||||||
|
}
|
||||||
|
if (obj != null && obj.getLastModified() != null)
|
||||||
|
{
|
||||||
|
childrenTemp.append("<td>");
|
||||||
|
childrenTemp.append(shortDF.format(obj.getLastModified()));
|
||||||
|
childrenTemp.append("</td>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childrenTemp.append("<td></td>");
|
||||||
|
}
|
||||||
|
childrenTemp.append("</tr>");
|
||||||
|
}
|
||||||
|
childrenTemp.append("</table>");
|
||||||
|
childrenTemp.append(getFooter(transaction, path, resp, req));
|
||||||
|
childrenTemp.append("</body></html>");
|
||||||
|
out.write(childrenTemp.toString().getBytes("UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the CSS styles used to display the HTML representation
|
||||||
|
* of the webdav content.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected String getCSS()
|
||||||
|
{
|
||||||
|
// The default styles to use
|
||||||
|
String retVal= "body {\n"+
|
||||||
|
" font-family: Arial, Helvetica, sans-serif;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"h1 {\n"+
|
||||||
|
" font-size: 1.5em;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"th {\n"+
|
||||||
|
" background-color: #9DACBF;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"table {\n"+
|
||||||
|
" border-top-style: solid;\n"+
|
||||||
|
" border-right-style: solid;\n"+
|
||||||
|
" border-bottom-style: solid;\n"+
|
||||||
|
" border-left-style: solid;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"td {\n"+
|
||||||
|
" margin: 0px;\n"+
|
||||||
|
" padding-top: 2px;\n"+
|
||||||
|
" padding-right: 5px;\n"+
|
||||||
|
" padding-bottom: 2px;\n"+
|
||||||
|
" padding-left: 5px;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"tr.even {\n"+
|
||||||
|
" background-color: #CCCCCC;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"tr.odd {\n"+
|
||||||
|
" background-color: #FFFFFF;\n"+
|
||||||
|
"}\n"+
|
||||||
|
"";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Try loading one via class loader and use that one instead
|
||||||
|
ClassLoader cl = getClass().getClassLoader();
|
||||||
|
InputStream iStream = cl.getResourceAsStream("webdav.css");
|
||||||
|
if(iStream != null)
|
||||||
|
{
|
||||||
|
// Found css via class loader, use that one
|
||||||
|
StringBuffer out = new StringBuffer();
|
||||||
|
byte[] b = new byte[4096];
|
||||||
|
for (int n; (n = iStream.read(b)) != -1;)
|
||||||
|
{
|
||||||
|
out.append(new String(b, 0, n));
|
||||||
|
}
|
||||||
|
retVal= out.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LOG.error("Error in reading webdav.css", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the header to be displayed in front of the folder content
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* @param resp
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected String getHeader(ITransaction transaction, String path,
|
||||||
|
HttpServletResponse resp, HttpServletRequest req)
|
||||||
|
{
|
||||||
|
return "<h1>Content of folder "+path+"</h1>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the footer to be displayed after the folder content
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
* @param path
|
||||||
|
* @param resp
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected String getFooter(ITransaction transaction, String path,
|
||||||
|
HttpServletResponse resp, HttpServletRequest req)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return this as the Date/Time format for displaying Creation + Modification dates
|
||||||
|
*
|
||||||
|
* @param browserLocale
|
||||||
|
* @return DateFormat used to display creation and modification dates
|
||||||
|
*/
|
||||||
|
protected DateFormat getDateTimeFormat(Locale browserLocale)
|
||||||
|
{
|
||||||
|
return SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.MEDIUM, browserLocale);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.IMimeTyper;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
public class DoHead extends AbstractMethod {
|
||||||
|
|
||||||
|
protected String _dftIndexFile;
|
||||||
|
protected IWebdavStore _store;
|
||||||
|
protected String _insteadOf404;
|
||||||
|
protected ResourceLocks _resourceLocks;
|
||||||
|
protected IMimeTyper _mimeTyper;
|
||||||
|
protected int _contentLength;
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoHead.class);
|
||||||
|
|
||||||
|
public DoHead(IWebdavStore store, String dftIndexFile, String insteadOf404,
|
||||||
|
ResourceLocks resourceLocks, IMimeTyper mimeTyper,
|
||||||
|
int contentLengthHeader) {
|
||||||
|
_store = store;
|
||||||
|
_dftIndexFile = dftIndexFile;
|
||||||
|
_insteadOf404 = insteadOf404;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_mimeTyper = mimeTyper;
|
||||||
|
_contentLength = contentLengthHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
|
||||||
|
// determines if the uri exists.
|
||||||
|
|
||||||
|
boolean bUriExists = false;
|
||||||
|
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
StoredObject so;
|
||||||
|
try {
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
if (so == null) {
|
||||||
|
if (this._insteadOf404 != null && !_insteadOf404.trim().equals("")) {
|
||||||
|
path = this._insteadOf404;
|
||||||
|
so = _store.getStoredObject(transaction, this._insteadOf404);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
bUriExists = true;
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (so != null) {
|
||||||
|
if (so.isFolder()) {
|
||||||
|
if (_dftIndexFile != null && !_dftIndexFile.trim().equals("")) {
|
||||||
|
resp.sendRedirect(resp.encodeRedirectURL(req
|
||||||
|
.getRequestURI()
|
||||||
|
+ this._dftIndexFile));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (so.isNullResource()) {
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(so);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tempLockOwner = "doGet" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String eTagMatch = req.getHeader("If-None-Match");
|
||||||
|
if (eTagMatch != null) {
|
||||||
|
if (eTagMatch.equals(getETag(so))) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_NOT_MODIFIED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (so.isResource()) {
|
||||||
|
// path points to a file but ends with / or \
|
||||||
|
if (path.endsWith("/") || (path.endsWith("\\"))) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_FOUND,
|
||||||
|
req.getRequestURI());
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// setting headers
|
||||||
|
long lastModified = so.getLastModified().getTime();
|
||||||
|
resp.setDateHeader("last-modified", lastModified);
|
||||||
|
|
||||||
|
String eTag = getETag(so);
|
||||||
|
resp.addHeader("ETag", eTag);
|
||||||
|
|
||||||
|
long resourceLength = so.getResourceLength();
|
||||||
|
|
||||||
|
if (_contentLength == 1) {
|
||||||
|
if (resourceLength > 0) {
|
||||||
|
if (resourceLength <= Integer.MAX_VALUE) {
|
||||||
|
resp
|
||||||
|
.setContentLength((int) resourceLength);
|
||||||
|
} else {
|
||||||
|
resp.setHeader("content-length", ""
|
||||||
|
+ resourceLength);
|
||||||
|
// is "content-length" the right header?
|
||||||
|
// is long a valid format?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String mimeType = _mimeTyper.getMimeType(transaction, path);
|
||||||
|
if (mimeType != null) {
|
||||||
|
resp.setContentType(mimeType);
|
||||||
|
} else {
|
||||||
|
int lastSlash = path.replace('\\', '/')
|
||||||
|
.lastIndexOf('/');
|
||||||
|
int lastDot = path.indexOf(".", lastSlash);
|
||||||
|
if (lastDot == -1) {
|
||||||
|
resp.setContentType("text/html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doBody(transaction, resp, path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
folderBody(transaction, path, resp, req);
|
||||||
|
}
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (ObjectAlreadyExistsException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||||
|
.getRequestURI());
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
path, tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
folderBody(transaction, path, resp, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bUriExists)
|
||||||
|
resp.setStatus(WebdavStatus.SC_NOT_FOUND);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void folderBody(ITransaction transaction, String path,
|
||||||
|
HttpServletResponse resp, HttpServletRequest req)
|
||||||
|
throws IOException {
|
||||||
|
// no body for HEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doBody(ITransaction transaction, HttpServletResponse resp,
|
||||||
|
String path) throws IOException {
|
||||||
|
// no body for HEAD
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,593 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||||
|
import net.sf.webdav.locking.IResourceLocks;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
|
||||||
|
import org.w3c.dom.DOMException;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
public class DoLock extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoLock.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private IResourceLocks _resourceLocks;
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
private boolean _macLockRequest = false;
|
||||||
|
|
||||||
|
private boolean _exclusive = false;
|
||||||
|
private String _type = null;
|
||||||
|
private String _lockOwner = null;
|
||||||
|
|
||||||
|
private String _path = null;
|
||||||
|
private String _parentPath = null;
|
||||||
|
|
||||||
|
private String _userAgent = null;
|
||||||
|
|
||||||
|
public DoLock(IWebdavStore store, IResourceLocks resourceLocks,
|
||||||
|
boolean readOnly) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
if (_readOnly) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_path = getRelativePath(req);
|
||||||
|
_parentPath = getParentPath(getCleanPath(_path));
|
||||||
|
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, _path)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // resource is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, _parentPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // parent is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mac OS Finder (whether 10.4.x or 10.5) can't store files
|
||||||
|
// because executing a LOCK without lock information causes a
|
||||||
|
// SC_BAD_REQUEST
|
||||||
|
_userAgent = req.getHeader("User-Agent");
|
||||||
|
if (_userAgent != null && _userAgent.indexOf("Darwin") != -1) {
|
||||||
|
_macLockRequest = true;
|
||||||
|
|
||||||
|
String timeString = new Long(System.currentTimeMillis())
|
||||||
|
.toString();
|
||||||
|
_lockOwner = _userAgent.concat(timeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
String tempLockOwner = "doLock" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
if (_resourceLocks.lock(transaction, _path, tempLockOwner, false,
|
||||||
|
0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
try {
|
||||||
|
if (req.getHeader("If") != null) {
|
||||||
|
doRefreshLock(transaction, req, resp);
|
||||||
|
} else {
|
||||||
|
doLock(transaction, req, resp);
|
||||||
|
}
|
||||||
|
} catch (LockFailedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_LOCKED);
|
||||||
|
LOG.error("Lockfailed exception", e);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
_path, tempLockOwner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLock(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
|
||||||
|
StoredObject so = _store.getStoredObject(transaction, _path);
|
||||||
|
|
||||||
|
if (so != null) {
|
||||||
|
doLocking(transaction, req, resp);
|
||||||
|
} else {
|
||||||
|
// resource doesn't exist, null-resource lock
|
||||||
|
doNullResourceLock(transaction, req, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
so = null;
|
||||||
|
_exclusive = false;
|
||||||
|
_type = null;
|
||||||
|
_lockOwner = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLocking(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException {
|
||||||
|
|
||||||
|
// Tests if LockObject on requested path exists, and if so, tests
|
||||||
|
// exclusivity
|
||||||
|
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||||
|
_path);
|
||||||
|
if (lo != null) {
|
||||||
|
if (lo.isExclusive()) {
|
||||||
|
sendLockFailError(transaction, req, resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Thats the locking itself
|
||||||
|
executeLock(transaction, req, resp);
|
||||||
|
|
||||||
|
} catch (ServletException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
LOG.trace(e.toString());
|
||||||
|
} catch (LockFailedException e) {
|
||||||
|
sendLockFailError(transaction, req, resp);
|
||||||
|
} finally {
|
||||||
|
lo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doNullResourceLock(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
StoredObject parentSo, nullSo = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
parentSo = _store.getStoredObject(transaction, _parentPath);
|
||||||
|
if (_parentPath != null && parentSo == null) {
|
||||||
|
_store.createFolder(transaction, _parentPath);
|
||||||
|
} else if (_parentPath != null && parentSo != null
|
||||||
|
&& parentSo.isResource()) {
|
||||||
|
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nullSo = _store.getStoredObject(transaction, _path);
|
||||||
|
if (nullSo == null) {
|
||||||
|
// resource doesn't exist
|
||||||
|
_store.createResource(transaction, _path);
|
||||||
|
|
||||||
|
// Transmit expects 204 response-code, not 201
|
||||||
|
if (_userAgent != null && _userAgent.indexOf("Transmit") != -1) {
|
||||||
|
LOG
|
||||||
|
.trace("DoLock.execute() : do workaround for user agent '"
|
||||||
|
+ _userAgent + "'");
|
||||||
|
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||||
|
} else {
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// resource already exists, could not execute null-resource lock
|
||||||
|
sendLockFailError(transaction, req, resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nullSo = _store.getStoredObject(transaction, _path);
|
||||||
|
// define the newly created resource as null-resource
|
||||||
|
nullSo.setNullResource(true);
|
||||||
|
|
||||||
|
// Thats the locking itself
|
||||||
|
executeLock(transaction, req, resp);
|
||||||
|
|
||||||
|
} catch (LockFailedException e) {
|
||||||
|
sendLockFailError(transaction, req, resp);
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
LOG.error("Webdav exception", e);
|
||||||
|
} catch (ServletException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
LOG.error("Servlet exception", e);
|
||||||
|
} finally {
|
||||||
|
parentSo = null;
|
||||||
|
nullSo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRefreshLock(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws IOException, LockFailedException {
|
||||||
|
|
||||||
|
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||||
|
String lockToken = null;
|
||||||
|
if (lockTokens != null)
|
||||||
|
lockToken = lockTokens[0];
|
||||||
|
|
||||||
|
if (lockToken != null) {
|
||||||
|
// Getting LockObject of specified lockToken in If header
|
||||||
|
LockedObject refreshLo = _resourceLocks.getLockedObjectByID(
|
||||||
|
transaction, lockToken);
|
||||||
|
if (refreshLo != null) {
|
||||||
|
int timeout = getTimeout(transaction, req);
|
||||||
|
|
||||||
|
refreshLo.refreshTimeout(timeout);
|
||||||
|
// sending success response
|
||||||
|
generateXMLReport(transaction, resp, refreshLo);
|
||||||
|
|
||||||
|
refreshLo = null;
|
||||||
|
} else {
|
||||||
|
// no LockObject to given lockToken
|
||||||
|
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------- helper methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the LOCK
|
||||||
|
*/
|
||||||
|
private void executeLock(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws LockFailedException, IOException,
|
||||||
|
ServletException {
|
||||||
|
|
||||||
|
// Mac OS lock request workaround
|
||||||
|
if (_macLockRequest) {
|
||||||
|
LOG.trace("DoLock.execute() : do workaround for user agent '"
|
||||||
|
+ _userAgent + "'");
|
||||||
|
|
||||||
|
doMacLockRequestWorkaround(transaction, req, resp);
|
||||||
|
} else {
|
||||||
|
// Getting LockInformation from request
|
||||||
|
if (getLockInformation(transaction, req, resp)) {
|
||||||
|
int depth = getDepth(req);
|
||||||
|
int lockDuration = getTimeout(transaction, req);
|
||||||
|
|
||||||
|
boolean lockSuccess = false;
|
||||||
|
if (_exclusive) {
|
||||||
|
lockSuccess = _resourceLocks.exclusiveLock(transaction,
|
||||||
|
_path, _lockOwner, depth, lockDuration);
|
||||||
|
} else {
|
||||||
|
lockSuccess = _resourceLocks.sharedLock(transaction, _path,
|
||||||
|
_lockOwner, depth, lockDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockSuccess) {
|
||||||
|
// Locks successfully placed - return information about
|
||||||
|
LockedObject lo = _resourceLocks.getLockedObjectByPath(
|
||||||
|
transaction, _path);
|
||||||
|
if (lo != null) {
|
||||||
|
generateXMLReport(transaction, resp, lo);
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendLockFailError(transaction, req, resp);
|
||||||
|
|
||||||
|
throw new LockFailedException();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// information for LOCK could not be read successfully
|
||||||
|
resp.setContentType("text/xml; charset=UTF-8");
|
||||||
|
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to get the LockInformation from LOCK request
|
||||||
|
*/
|
||||||
|
private boolean getLockInformation(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
Node lockInfoNode = null;
|
||||||
|
DocumentBuilder documentBuilder = null;
|
||||||
|
|
||||||
|
documentBuilder = getDocumentBuilder();
|
||||||
|
try {
|
||||||
|
Document document = documentBuilder.parse(new InputSource(req
|
||||||
|
.getInputStream()));
|
||||||
|
|
||||||
|
// Get the root element of the document
|
||||||
|
Element rootElement = document.getDocumentElement();
|
||||||
|
|
||||||
|
lockInfoNode = rootElement;
|
||||||
|
|
||||||
|
if (lockInfoNode != null) {
|
||||||
|
NodeList childList = lockInfoNode.getChildNodes();
|
||||||
|
Node lockScopeNode = null;
|
||||||
|
Node lockTypeNode = null;
|
||||||
|
Node lockOwnerNode = null;
|
||||||
|
|
||||||
|
Node currentNode = null;
|
||||||
|
String nodeName = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < childList.getLength(); i++) {
|
||||||
|
currentNode = childList.item(i);
|
||||||
|
|
||||||
|
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||||
|
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||||
|
|
||||||
|
nodeName = currentNode.getNodeName();
|
||||||
|
|
||||||
|
if (nodeName.endsWith("locktype")) {
|
||||||
|
lockTypeNode = currentNode;
|
||||||
|
}
|
||||||
|
if (nodeName.endsWith("lockscope")) {
|
||||||
|
lockScopeNode = currentNode;
|
||||||
|
}
|
||||||
|
if (nodeName.endsWith("owner")) {
|
||||||
|
lockOwnerNode = currentNode;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockScopeNode != null) {
|
||||||
|
String scope = null;
|
||||||
|
childList = lockScopeNode.getChildNodes();
|
||||||
|
for (int i = 0; i < childList.getLength(); i++) {
|
||||||
|
currentNode = childList.item(i);
|
||||||
|
|
||||||
|
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
scope = currentNode.getNodeName();
|
||||||
|
|
||||||
|
if (scope.endsWith("exclusive")) {
|
||||||
|
_exclusive = true;
|
||||||
|
} else if (scope.equals("shared")) {
|
||||||
|
_exclusive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scope == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockTypeNode != null) {
|
||||||
|
childList = lockTypeNode.getChildNodes();
|
||||||
|
for (int i = 0; i < childList.getLength(); i++) {
|
||||||
|
currentNode = childList.item(i);
|
||||||
|
|
||||||
|
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
_type = currentNode.getNodeName();
|
||||||
|
|
||||||
|
if (_type.endsWith("write")) {
|
||||||
|
_type = "write";
|
||||||
|
} else if (_type.equals("read")) {
|
||||||
|
_type = "read";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_type == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockOwnerNode != null) {
|
||||||
|
childList = lockOwnerNode.getChildNodes();
|
||||||
|
for (int i = 0; i < childList.getLength(); i++) {
|
||||||
|
currentNode = childList.item(i);
|
||||||
|
|
||||||
|
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||||
|
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||||
|
_lockOwner = currentNode.getFirstChild()
|
||||||
|
.getNodeValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_lockOwner == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (DOMException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
LOG.error("DOM exception", e);
|
||||||
|
return false;
|
||||||
|
} catch (SAXException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
LOG.error("SAX exception", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ties to read the timeout from request
|
||||||
|
*/
|
||||||
|
private int getTimeout(ITransaction transaction, HttpServletRequest req) {
|
||||||
|
|
||||||
|
int lockDuration = DEFAULT_TIMEOUT;
|
||||||
|
String lockDurationStr = req.getHeader("Timeout");
|
||||||
|
|
||||||
|
if (lockDurationStr == null) {
|
||||||
|
lockDuration = DEFAULT_TIMEOUT;
|
||||||
|
} else {
|
||||||
|
int commaPos = lockDurationStr.indexOf(',');
|
||||||
|
// if multiple timeouts, just use the first one
|
||||||
|
if (commaPos != -1) {
|
||||||
|
lockDurationStr = lockDurationStr.substring(0, commaPos);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lockDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the response XML with all lock information
|
||||||
|
*/
|
||||||
|
private void generateXMLReport(ITransaction transaction,
|
||||||
|
HttpServletResponse resp, LockedObject lo) throws IOException {
|
||||||
|
|
||||||
|
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||||
|
namespaces.put("DAV:", "D");
|
||||||
|
|
||||||
|
resp.setStatus(WebdavStatus.SC_OK);
|
||||||
|
resp.setContentType("text/xml; charset=UTF-8");
|
||||||
|
|
||||||
|
XMLWriter generatedXML = new XMLWriter(resp.getWriter(), namespaces);
|
||||||
|
generatedXML.writeXMLHeader();
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeProperty("DAV::" + _type);
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||||
|
if (_exclusive) {
|
||||||
|
generatedXML.writeProperty("DAV::exclusive");
|
||||||
|
} else {
|
||||||
|
generatedXML.writeProperty("DAV::shared");
|
||||||
|
}
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
int depth = lo.getLockDepth();
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
|
||||||
|
if (depth == INFINITY) {
|
||||||
|
generatedXML.writeText("Infinity");
|
||||||
|
} else {
|
||||||
|
generatedXML.writeText(String.valueOf(depth));
|
||||||
|
}
|
||||||
|
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(_lockOwner);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
long timeout = lo.getTimeoutMillis();
|
||||||
|
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText("Second-" + timeout / 1000);
|
||||||
|
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
String lockToken = lo.getID();
|
||||||
|
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText("opaquelocktoken:" + lockToken);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
resp.addHeader("Lock-Token", "<opaquelocktoken:" + lockToken + ">");
|
||||||
|
|
||||||
|
generatedXML.sendData();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the lock for a Mac OS Finder client
|
||||||
|
*/
|
||||||
|
private void doMacLockRequestWorkaround(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws LockFailedException, IOException {
|
||||||
|
LockedObject lo;
|
||||||
|
int depth = getDepth(req);
|
||||||
|
int lockDuration = getTimeout(transaction, req);
|
||||||
|
if (lockDuration < 0 || lockDuration > MAX_TIMEOUT)
|
||||||
|
lockDuration = DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
|
boolean lockSuccess = false;
|
||||||
|
lockSuccess = _resourceLocks.exclusiveLock(transaction, _path,
|
||||||
|
_lockOwner, depth, lockDuration);
|
||||||
|
|
||||||
|
if (lockSuccess) {
|
||||||
|
// Locks successfully placed - return information about
|
||||||
|
lo = _resourceLocks.getLockedObjectByPath(transaction, _path);
|
||||||
|
if (lo != null) {
|
||||||
|
generateXMLReport(transaction, resp, lo);
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Locking was not successful
|
||||||
|
sendLockFailError(transaction, req, resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an error report to the client
|
||||||
|
*/
|
||||||
|
private void sendLockFailError(ITransaction transaction,
|
||||||
|
HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws IOException {
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
errorList.put(_path, WebdavStatus.SC_LOCKED);
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.locking.IResourceLocks;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
|
||||||
|
public class DoMkcol extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoMkcol.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private IResourceLocks _resourceLocks;
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
public DoMkcol(IWebdavStore store, IResourceLocks resourceLocks,
|
||||||
|
boolean readOnly) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
if (!_readOnly) {
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
String parentPath = getParentPath(getCleanPath(path));
|
||||||
|
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||||
|
// TODO remove
|
||||||
|
LOG
|
||||||
|
.trace("MkCol on locked resource (parentPath) not executable!"
|
||||||
|
+ "\n Sending SC_FORBIDDEN (403) error response!");
|
||||||
|
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tempLockOwner = "doMkcol" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
StoredObject parentSo, so = null;
|
||||||
|
try {
|
||||||
|
parentSo = _store.getStoredObject(transaction, parentPath);
|
||||||
|
if (parentSo == null) {
|
||||||
|
// parent not exists
|
||||||
|
resp.sendError(WebdavStatus.SC_CONFLICT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parentPath != null && parentSo.isFolder()) {
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
if (so == null) {
|
||||||
|
_store.createFolder(transaction, path);
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
} else {
|
||||||
|
// object already exists
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
|
||||||
|
LockedObject nullResourceLo = _resourceLocks
|
||||||
|
.getLockedObjectByPath(transaction,
|
||||||
|
path);
|
||||||
|
if (nullResourceLo == null) {
|
||||||
|
resp
|
||||||
|
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String nullResourceLockToken = nullResourceLo
|
||||||
|
.getID();
|
||||||
|
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||||
|
String lockToken = null;
|
||||||
|
if (lockTokens != null)
|
||||||
|
lockToken = lockTokens[0];
|
||||||
|
else {
|
||||||
|
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lockToken.equals(nullResourceLockToken)) {
|
||||||
|
so.setNullResource(false);
|
||||||
|
so.setFolder(true);
|
||||||
|
|
||||||
|
String[] nullResourceLockOwners = nullResourceLo
|
||||||
|
.getOwner();
|
||||||
|
String owner = null;
|
||||||
|
if (nullResourceLockOwners != null)
|
||||||
|
owner = nullResourceLockOwners[0];
|
||||||
|
|
||||||
|
if (_resourceLocks.unlock(transaction,
|
||||||
|
lockToken, owner)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
} else {
|
||||||
|
resp
|
||||||
|
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// TODO remove
|
||||||
|
LOG
|
||||||
|
.trace("MkCol on lock-null-resource with wrong lock-token!"
|
||||||
|
+ "\n Sending multistatus error report!");
|
||||||
|
|
||||||
|
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(so);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp
|
||||||
|
.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (parentPath != null && parentSo.isResource()) {
|
||||||
|
// TODO remove
|
||||||
|
LOG
|
||||||
|
.trace("MkCol on resource is not executable"
|
||||||
|
+ "\n Sending SC_METHOD_NOT_ALLOWED (405) error response!");
|
||||||
|
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(parentSo);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
path, tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
public class DoMove extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoMove.class);
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
private DoDelete _doDelete;
|
||||||
|
private DoCopy _doCopy;
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
public DoMove(IWebdavStore _store, ResourceLocks resourceLocks, DoDelete doDelete,
|
||||||
|
DoCopy doCopy, boolean readOnly) {
|
||||||
|
this._store = _store;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_doDelete = doDelete;
|
||||||
|
_doCopy = doCopy;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
|
||||||
|
if (!_readOnly) {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
String sourcePath = getRelativePath(req);
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, sourcePath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String destinationPath = parseDestinationHeader(req, resp);
|
||||||
|
if (destinationPath == null) {
|
||||||
|
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks,
|
||||||
|
destinationPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tempLockOwner = "doMove" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
|
||||||
|
if (_resourceLocks.lock(transaction, sourcePath, tempLockOwner,
|
||||||
|
false, 0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
try {
|
||||||
|
boolean ok = _store.moveObject(transaction, destinationPath, sourcePath);
|
||||||
|
if (!ok) {
|
||||||
|
if (_doCopy.copyResource(transaction, req, resp)) {
|
||||||
|
|
||||||
|
errorList = new Hashtable<String, Integer>();
|
||||||
|
_doDelete.deleteResource(transaction, sourcePath,
|
||||||
|
errorList, req, resp);
|
||||||
|
if (!errorList.isEmpty()) {
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (ObjectAlreadyExistsException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||||
|
.getRequestURI());
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
sourcePath, tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorList.put(req.getHeader("Destination"),
|
||||||
|
WebdavStatus.SC_LOCKED);
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.IMethodExecutor;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
|
||||||
|
public class DoNotImplemented implements IMethodExecutor {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoNotImplemented.class);
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
public DoNotImplemented(boolean readOnly) {
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException {
|
||||||
|
LOG.trace("-- " + req.getMethod());
|
||||||
|
|
||||||
|
if (_readOnly) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} else
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
public class DoOptions extends DeterminableMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoOptions.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
|
||||||
|
public DoOptions(IWebdavStore store, ResourceLocks resLocks) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resLocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
String tempLockOwner = "doOptions" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
StoredObject so = null;
|
||||||
|
try {
|
||||||
|
resp.addHeader("DAV", "1, 2");
|
||||||
|
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
String methodsAllowed = determineMethodsAllowed(so);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.addHeader("MS-Author-Via", "DAV");
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||||
|
tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,627 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
|
||||||
|
import net.sf.webdav.IMimeTyper;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.fromcatalina.URLEncoder;
|
||||||
|
import net.sf.webdav.fromcatalina.XMLHelper;
|
||||||
|
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
public class DoPropfind extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoPropfind.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array containing the safe characters set.
|
||||||
|
*/
|
||||||
|
protected static URLEncoder URL_ENCODER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
private IMimeTyper _mimeTyper;
|
||||||
|
|
||||||
|
private int _depth;
|
||||||
|
|
||||||
|
public DoPropfind(IWebdavStore store, ResourceLocks resLocks,
|
||||||
|
IMimeTyper mimeTyper) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resLocks;
|
||||||
|
_mimeTyper = mimeTyper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
// Retrieve the resources
|
||||||
|
String path = getCleanPath(getRelativePath(req));
|
||||||
|
String tempLockOwner = "doPropfind" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
_depth = getDepth(req);
|
||||||
|
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false,
|
||||||
|
_depth, TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
|
||||||
|
StoredObject so = null;
|
||||||
|
try {
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
if (so == null) {
|
||||||
|
resp.setContentType("text/xml; charset=UTF-8");
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_FOUND, req
|
||||||
|
.getRequestURI());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<String> properties = null;
|
||||||
|
path = getCleanPath(getRelativePath(req));
|
||||||
|
|
||||||
|
int propertyFindType = FIND_ALL_PROP;
|
||||||
|
Node propNode = null;
|
||||||
|
|
||||||
|
if (req.getContentLength() > 0) {
|
||||||
|
DocumentBuilder documentBuilder = getDocumentBuilder();
|
||||||
|
try {
|
||||||
|
Document document = documentBuilder
|
||||||
|
.parse(new InputSource(req.getInputStream()));
|
||||||
|
// Get the root element of the document
|
||||||
|
Element rootElement = document.getDocumentElement();
|
||||||
|
|
||||||
|
propNode = XMLHelper
|
||||||
|
.findSubElement(rootElement, "prop");
|
||||||
|
if (propNode != null) {
|
||||||
|
propertyFindType = FIND_BY_PROPERTY;
|
||||||
|
} else if (XMLHelper.findSubElement(rootElement,
|
||||||
|
"propname") != null) {
|
||||||
|
propertyFindType = FIND_PROPERTY_NAMES;
|
||||||
|
} else if (XMLHelper.findSubElement(rootElement,
|
||||||
|
"allprop") != null) {
|
||||||
|
propertyFindType = FIND_ALL_PROP;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no content, which means it is a allprop request
|
||||||
|
propertyFindType = FIND_ALL_PROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||||
|
namespaces.put("DAV:", "D");
|
||||||
|
|
||||||
|
if (propertyFindType == FIND_BY_PROPERTY) {
|
||||||
|
propertyFindType = 0;
|
||||||
|
properties = XMLHelper.getPropertiesFromXML(propNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||||
|
resp.setContentType("text/xml; charset=UTF-8");
|
||||||
|
|
||||||
|
// Create multistatus object
|
||||||
|
XMLWriter generatedXML = new XMLWriter(resp.getWriter(),
|
||||||
|
namespaces);
|
||||||
|
generatedXML.writeXMLHeader();
|
||||||
|
generatedXML
|
||||||
|
.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||||
|
if (_depth == 0) {
|
||||||
|
parseProperties(transaction, req, generatedXML, path,
|
||||||
|
propertyFindType, properties, _mimeTyper
|
||||||
|
.getMimeType(transaction, path));
|
||||||
|
} else {
|
||||||
|
recursiveParseProperties(transaction, path, req,
|
||||||
|
generatedXML, propertyFindType, properties, _depth,
|
||||||
|
_mimeTyper.getMimeType(transaction, path));
|
||||||
|
}
|
||||||
|
generatedXML
|
||||||
|
.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.sendData();
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
LOG.warn("Sending internal error!");
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} catch (ServletException e) {
|
||||||
|
e.printStackTrace(); // To change body of catch statement use
|
||||||
|
// File | Settings | File Templates.
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||||
|
tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* goes recursive through all folders. used by propfind
|
||||||
|
*
|
||||||
|
* @param currentPath
|
||||||
|
* the current path
|
||||||
|
* @param req
|
||||||
|
* HttpServletRequest
|
||||||
|
* @param generatedXML
|
||||||
|
* @param propertyFindType
|
||||||
|
* @param properties
|
||||||
|
* @param depth
|
||||||
|
* depth of the propfind
|
||||||
|
* @throws IOException
|
||||||
|
* if an error in the underlying store occurs
|
||||||
|
*/
|
||||||
|
private void recursiveParseProperties(ITransaction transaction,
|
||||||
|
String currentPath, HttpServletRequest req, XMLWriter generatedXML,
|
||||||
|
int propertyFindType, Vector<String> properties, int depth,
|
||||||
|
String mimeType) throws WebdavException {
|
||||||
|
|
||||||
|
parseProperties(transaction, req, generatedXML, currentPath,
|
||||||
|
propertyFindType, properties, mimeType);
|
||||||
|
|
||||||
|
if (depth > 0) {
|
||||||
|
// no need to get name if depth is already zero
|
||||||
|
String[] names = _store.getChildrenNames(transaction, currentPath);
|
||||||
|
names = names == null ? new String[] {} : names;
|
||||||
|
String newPath = null;
|
||||||
|
|
||||||
|
for (String name : names) {
|
||||||
|
newPath = currentPath;
|
||||||
|
if (!(newPath.endsWith("/"))) {
|
||||||
|
newPath += "/";
|
||||||
|
}
|
||||||
|
newPath += name;
|
||||||
|
recursiveParseProperties(transaction, newPath, req,
|
||||||
|
generatedXML, propertyFindType, properties, depth - 1,
|
||||||
|
mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propfind helper method.
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* The servlet request
|
||||||
|
* @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(ITransaction transaction,
|
||||||
|
HttpServletRequest req, XMLWriter generatedXML, String path,
|
||||||
|
int type, Vector<String> propertiesVector, String mimeType)
|
||||||
|
throws WebdavException {
|
||||||
|
|
||||||
|
StoredObject so = _store.getStoredObject(transaction, path);
|
||||||
|
|
||||||
|
boolean isFolder = so.isFolder();
|
||||||
|
final String creationdate = creationDateFormat(so.getCreationDate());
|
||||||
|
final String lastModified = lastModifiedDateFormat(so.getLastModified());
|
||||||
|
String resourceLength = String.valueOf(so.getResourceLength());
|
||||||
|
|
||||||
|
// ResourceInfo resourceInfo = new ResourceInfo(path, resources);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||||
|
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
|
||||||
|
+ WebdavStatus.getStatusText(WebdavStatus.SC_OK));
|
||||||
|
|
||||||
|
// Generating href element
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
String href = req.getContextPath();
|
||||||
|
String servletPath = req.getServletPath();
|
||||||
|
if (servletPath != null) {
|
||||||
|
if ((href.endsWith("/")) && (servletPath.startsWith("/")))
|
||||||
|
href += servletPath.substring(1);
|
||||||
|
else
|
||||||
|
href += servletPath;
|
||||||
|
}
|
||||||
|
if ((href.endsWith("/")) && (path.startsWith("/")))
|
||||||
|
href += path.substring(1);
|
||||||
|
else
|
||||||
|
href += path;
|
||||||
|
if ((isFolder) && (!href.endsWith("/")))
|
||||||
|
href += "/";
|
||||||
|
|
||||||
|
generatedXML.writeText(rewriteUrl(href));
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
String resourceName = path;
|
||||||
|
int lastSlash = path.lastIndexOf('/');
|
||||||
|
if (lastSlash != -1)
|
||||||
|
resourceName = resourceName.substring(lastSlash + 1);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
case FIND_ALL_PROP:
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeProperty("DAV::creationdate", creationdate);
|
||||||
|
generatedXML.writeElement("DAV::displayname", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeData(resourceName);
|
||||||
|
generatedXML.writeElement("DAV::displayname", XMLWriter.CLOSING);
|
||||||
|
if (!isFolder) {
|
||||||
|
generatedXML
|
||||||
|
.writeProperty("DAV::getlastmodified", lastModified);
|
||||||
|
generatedXML.writeProperty("DAV::getcontentlength",
|
||||||
|
resourceLength);
|
||||||
|
String contentType = mimeType;
|
||||||
|
if (contentType != null) {
|
||||||
|
generatedXML.writeProperty("DAV::getcontenttype",
|
||||||
|
contentType);
|
||||||
|
}
|
||||||
|
generatedXML.writeProperty("DAV::getetag", getETag(so));
|
||||||
|
generatedXML.writeElement("DAV::resourcetype",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeElement("DAV::resourcetype",
|
||||||
|
XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::collection",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::resourcetype",
|
||||||
|
XMLWriter.CLOSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSupportedLockElements(transaction, generatedXML, path);
|
||||||
|
|
||||||
|
writeLockDiscoveryElements(transaction, generatedXML, path);
|
||||||
|
|
||||||
|
generatedXML.writeProperty("DAV::source", "");
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(status);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FIND_PROPERTY_NAMES:
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML
|
||||||
|
.writeElement("DAV::creationdate", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::displayname", XMLWriter.NO_CONTENT);
|
||||||
|
if (!isFolder) {
|
||||||
|
generatedXML.writeElement("DAV::getcontentlanguage",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::getcontentlength",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::getcontenttype",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::getetag", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::getlastmodified",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
}
|
||||||
|
generatedXML
|
||||||
|
.writeElement("DAV::resourcetype", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::supportedlock",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::source", XMLWriter.NO_CONTENT);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(status);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FIND_BY_PROPERTY:
|
||||||
|
|
||||||
|
Vector<String> propertiesNotFound = new Vector<String>();
|
||||||
|
|
||||||
|
// Parse the list of properties
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
Enumeration<String> properties = propertiesVector.elements();
|
||||||
|
|
||||||
|
while (properties.hasMoreElements()) {
|
||||||
|
|
||||||
|
String property = (String) properties.nextElement();
|
||||||
|
|
||||||
|
if (property.equals("DAV::creationdate")) {
|
||||||
|
generatedXML.writeProperty("DAV::creationdate",
|
||||||
|
creationdate);
|
||||||
|
} else if (property.equals("DAV::displayname")) {
|
||||||
|
generatedXML.writeElement("DAV::displayname",
|
||||||
|
XMLWriter.OPENING);
|
||||||
|
generatedXML.writeData(resourceName);
|
||||||
|
generatedXML.writeElement("DAV::displayname",
|
||||||
|
XMLWriter.CLOSING);
|
||||||
|
} else if (property.equals("DAV::getcontentlanguage")) {
|
||||||
|
if (isFolder) {
|
||||||
|
propertiesNotFound.addElement(property);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeElement("DAV::getcontentlanguage",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
}
|
||||||
|
} else if (property.equals("DAV::getcontentlength")) {
|
||||||
|
if (isFolder) {
|
||||||
|
propertiesNotFound.addElement(property);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeProperty("DAV::getcontentlength",
|
||||||
|
resourceLength);
|
||||||
|
}
|
||||||
|
} else if (property.equals("DAV::getcontenttype")) {
|
||||||
|
if (isFolder) {
|
||||||
|
propertiesNotFound.addElement(property);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeProperty("DAV::getcontenttype",
|
||||||
|
mimeType);
|
||||||
|
}
|
||||||
|
} else if (property.equals("DAV::getetag")) {
|
||||||
|
if (isFolder || so.isNullResource()) {
|
||||||
|
propertiesNotFound.addElement(property);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeProperty("DAV::getetag", getETag(so));
|
||||||
|
}
|
||||||
|
} else if (property.equals("DAV::getlastmodified")) {
|
||||||
|
if (isFolder) {
|
||||||
|
propertiesNotFound.addElement(property);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeProperty("DAV::getlastmodified",
|
||||||
|
lastModified);
|
||||||
|
}
|
||||||
|
} else if (property.equals("DAV::resourcetype")) {
|
||||||
|
if (isFolder) {
|
||||||
|
generatedXML.writeElement("DAV::resourcetype",
|
||||||
|
XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::collection",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::resourcetype",
|
||||||
|
XMLWriter.CLOSING);
|
||||||
|
} else {
|
||||||
|
generatedXML.writeElement("DAV::resourcetype",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
}
|
||||||
|
} else if (property.equals("DAV::source")) {
|
||||||
|
generatedXML.writeProperty("DAV::source", "");
|
||||||
|
} else if (property.equals("DAV::supportedlock")) {
|
||||||
|
|
||||||
|
writeSupportedLockElements(transaction, generatedXML, path);
|
||||||
|
|
||||||
|
} else if (property.equals("DAV::lockdiscovery")) {
|
||||||
|
|
||||||
|
writeLockDiscoveryElements(transaction, generatedXML, path);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
propertiesNotFound.addElement(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(status);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
Enumeration<String> propertiesNotFoundList = propertiesNotFound
|
||||||
|
.elements();
|
||||||
|
|
||||||
|
if (propertiesNotFoundList.hasMoreElements()) {
|
||||||
|
|
||||||
|
status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
|
||||||
|
+ " "
|
||||||
|
+ WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND));
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
while (propertiesNotFoundList.hasMoreElements()) {
|
||||||
|
generatedXML.writeElement((String) propertiesNotFoundList
|
||||||
|
.nextElement(), XMLWriter.NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(status);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
so = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSupportedLockElements(ITransaction transaction,
|
||||||
|
XMLWriter generatedXML, String path) {
|
||||||
|
|
||||||
|
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||||
|
path);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::supportedlock", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
if (lo == null) {
|
||||||
|
// both locks (shared/exclusive) can be granted
|
||||||
|
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::exclusive", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// LockObject exists, checking lock state
|
||||||
|
// if an exclusive lock exists, no further lock is possible
|
||||||
|
if (lo.isShared()) {
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::" + lo.getType(),
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::supportedlock", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
lo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLockDiscoveryElements(ITransaction transaction,
|
||||||
|
XMLWriter generatedXML, String path) {
|
||||||
|
|
||||||
|
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||||
|
path);
|
||||||
|
|
||||||
|
if (lo != null && !lo.hasExpired()) {
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeProperty("DAV::" + lo.getType());
|
||||||
|
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||||
|
if (lo.isExclusive()) {
|
||||||
|
generatedXML.writeProperty("DAV::exclusive");
|
||||||
|
} else {
|
||||||
|
generatedXML.writeProperty("DAV::shared");
|
||||||
|
}
|
||||||
|
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
|
||||||
|
if (_depth == INFINITY) {
|
||||||
|
generatedXML.writeText("Infinity");
|
||||||
|
} else {
|
||||||
|
generatedXML.writeText(String.valueOf(_depth));
|
||||||
|
}
|
||||||
|
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
String[] owners = lo.getOwner();
|
||||||
|
if (owners != null) {
|
||||||
|
for (int i = 0; i < owners.length; i++) {
|
||||||
|
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(owners[i]);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generatedXML.writeElement("DAV::owner", XMLWriter.NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int timeout = (int) (lo.getTimeoutMillis() / 1000);
|
||||||
|
String timeoutStr = new Integer(timeout).toString();
|
||||||
|
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText("Second-" + timeoutStr);
|
||||||
|
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
String lockToken = lo.getID();
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText("opaquelocktoken:" + lockToken);
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
|
||||||
|
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
generatedXML.writeElement("DAV::lockdiscovery",
|
||||||
|
XMLWriter.NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
lo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.fromcatalina.XMLHelper;
|
||||||
|
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
public class DoProppatch extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoProppatch.class);
|
||||||
|
|
||||||
|
private boolean _readOnly;
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private ResourceLocks _resourceLocks;
|
||||||
|
|
||||||
|
public DoProppatch(IWebdavStore store, ResourceLocks resLocks,
|
||||||
|
boolean readOnly) {
|
||||||
|
_readOnly = readOnly;
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resLocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
if (_readOnly) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
String parentPath = getParentPath(getCleanPath(path));
|
||||||
|
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // parent is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // resource is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO for now, PROPPATCH just sends a valid response, stating that
|
||||||
|
// everything is fine, but doesn't do anything.
|
||||||
|
|
||||||
|
// Retrieve the resources
|
||||||
|
String tempLockOwner = "doProppatch" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
StoredObject so = null;
|
||||||
|
LockedObject lo = null;
|
||||||
|
try {
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||||
|
getCleanPath(path));
|
||||||
|
|
||||||
|
if (so == null) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
// we do not to continue since there is no root
|
||||||
|
// resource
|
||||||
|
}
|
||||||
|
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
String methodsAllowed = DeterminableMethod
|
||||||
|
.determineMethodsAllowed(so);
|
||||||
|
resp.addHeader("Allow", methodsAllowed);
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||||
|
boolean lockTokenMatchesIfHeader = (lockTokens != null && lockTokens[0].equals(lo.getID()));
|
||||||
|
if (lo != null && lo.isExclusive() && !lockTokenMatchesIfHeader) {
|
||||||
|
// Object on specified path is LOCKED
|
||||||
|
errorList = new Hashtable<String, Integer>();
|
||||||
|
errorList.put(path, new Integer(WebdavStatus.SC_LOCKED));
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> toset = null;
|
||||||
|
List<String> toremove = null;
|
||||||
|
List<String> tochange = new Vector<String>();
|
||||||
|
// contains all properties from
|
||||||
|
// toset and toremove
|
||||||
|
|
||||||
|
path = getCleanPath(getRelativePath(req));
|
||||||
|
|
||||||
|
Node tosetNode = null;
|
||||||
|
Node toremoveNode = null;
|
||||||
|
|
||||||
|
if (req.getContentLength() != 0) {
|
||||||
|
DocumentBuilder documentBuilder = getDocumentBuilder();
|
||||||
|
try {
|
||||||
|
Document document = documentBuilder
|
||||||
|
.parse(new InputSource(req.getInputStream()));
|
||||||
|
// Get the root element of the document
|
||||||
|
Element rootElement = document.getDocumentElement();
|
||||||
|
|
||||||
|
tosetNode = XMLHelper.findSubElement(XMLHelper
|
||||||
|
.findSubElement(rootElement, "set"), "prop");
|
||||||
|
toremoveNode = XMLHelper.findSubElement(XMLHelper
|
||||||
|
.findSubElement(rootElement, "remove"), "prop");
|
||||||
|
} catch (Exception e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no content: error
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||||
|
namespaces.put("DAV:", "D");
|
||||||
|
|
||||||
|
if (tosetNode != null) {
|
||||||
|
toset = XMLHelper.getPropertiesFromXML(tosetNode);
|
||||||
|
tochange.addAll(toset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toremoveNode != null) {
|
||||||
|
toremove = XMLHelper.getPropertiesFromXML(toremoveNode);
|
||||||
|
tochange.addAll(toremove);
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||||
|
resp.setContentType("text/xml; charset=UTF-8");
|
||||||
|
|
||||||
|
// Create multistatus object
|
||||||
|
XMLWriter generatedXML = new XMLWriter(resp.getWriter(),
|
||||||
|
namespaces);
|
||||||
|
generatedXML.writeXMLHeader();
|
||||||
|
generatedXML
|
||||||
|
.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||||
|
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
|
||||||
|
+ " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK));
|
||||||
|
|
||||||
|
// Generating href element
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||||
|
|
||||||
|
String href = req.getContextPath();
|
||||||
|
if ((href.endsWith("/")) && (path.startsWith("/")))
|
||||||
|
href += path.substring(1);
|
||||||
|
else
|
||||||
|
href += path;
|
||||||
|
if ((so.isFolder()) && (!href.endsWith("/")))
|
||||||
|
href += "/";
|
||||||
|
|
||||||
|
generatedXML.writeText(rewriteUrl(href));
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
for (Iterator<String> iter = tochange.iterator(); iter
|
||||||
|
.hasNext();) {
|
||||||
|
String property = (String) iter.next();
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::propstat",
|
||||||
|
XMLWriter.OPENING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeElement(property, XMLWriter.NO_CONTENT);
|
||||||
|
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||||
|
generatedXML.writeText(status);
|
||||||
|
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::propstat",
|
||||||
|
XMLWriter.CLOSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML
|
||||||
|
.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||||
|
|
||||||
|
generatedXML.sendData();
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} catch (ServletException e) {
|
||||||
|
e.printStackTrace(); // To change body of catch statement use
|
||||||
|
// File | Settings | File Templates.
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||||
|
tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999,2004 The Apache Software Foundation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
|
import net.sf.webdav.locking.IResourceLocks;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
|
||||||
|
public class DoPut extends AbstractMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoPut.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private IResourceLocks _resourceLocks;
|
||||||
|
private boolean _readOnly;
|
||||||
|
private boolean _lazyFolderCreationOnPut;
|
||||||
|
|
||||||
|
private String _userAgent;
|
||||||
|
|
||||||
|
public DoPut(IWebdavStore store, IResourceLocks resLocks, boolean readOnly,
|
||||||
|
boolean lazyFolderCreationOnPut) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resLocks;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
_lazyFolderCreationOnPut = lazyFolderCreationOnPut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
if (!_readOnly) {
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
String parentPath = getParentPath(path);
|
||||||
|
|
||||||
|
_userAgent = req.getHeader("User-Agent");
|
||||||
|
|
||||||
|
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // parent is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||||
|
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||||
|
return; // resource is locked
|
||||||
|
}
|
||||||
|
|
||||||
|
String tempLockOwner = "doPut" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||||
|
TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
StoredObject parentSo, so = null;
|
||||||
|
try {
|
||||||
|
parentSo = _store.getStoredObject(transaction, parentPath);
|
||||||
|
if (parentPath != null && parentSo != null
|
||||||
|
&& parentSo.isResource()) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else if (parentPath != null && parentSo == null
|
||||||
|
&& _lazyFolderCreationOnPut) {
|
||||||
|
_store.createFolder(transaction, parentPath);
|
||||||
|
|
||||||
|
} else if (parentPath != null && parentSo == null
|
||||||
|
&& !_lazyFolderCreationOnPut) {
|
||||||
|
errorList.put(parentPath, WebdavStatus.SC_NOT_FOUND);
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
|
||||||
|
if (so == null) {
|
||||||
|
_store.createResource(transaction, path);
|
||||||
|
// resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
} else {
|
||||||
|
// This has already been created, just update the data
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
|
||||||
|
LockedObject nullResourceLo = _resourceLocks
|
||||||
|
.getLockedObjectByPath(transaction, path);
|
||||||
|
if (nullResourceLo == null) {
|
||||||
|
resp
|
||||||
|
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String nullResourceLockToken = nullResourceLo
|
||||||
|
.getID();
|
||||||
|
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||||
|
String lockToken = null;
|
||||||
|
if (lockTokens != null) {
|
||||||
|
lockToken = lockTokens[0];
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lockToken.equals(nullResourceLockToken)) {
|
||||||
|
so.setNullResource(false);
|
||||||
|
so.setFolder(false);
|
||||||
|
|
||||||
|
String[] nullResourceLockOwners = nullResourceLo
|
||||||
|
.getOwner();
|
||||||
|
String owner = null;
|
||||||
|
if (nullResourceLockOwners != null)
|
||||||
|
owner = nullResourceLockOwners[0];
|
||||||
|
|
||||||
|
if (!_resourceLocks.unlock(transaction,
|
||||||
|
lockToken, owner)) {
|
||||||
|
resp
|
||||||
|
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||||
|
sendReport(req, resp, errorList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// User-Agent workarounds
|
||||||
|
doUserAgentWorkaround(resp);
|
||||||
|
|
||||||
|
// setting resourceContent
|
||||||
|
long resourceLength = _store
|
||||||
|
.setResourceContent(transaction, path, req
|
||||||
|
.getInputStream(), null, null);
|
||||||
|
|
||||||
|
so = _store.getStoredObject(transaction, path);
|
||||||
|
if (resourceLength > 0)
|
||||||
|
so.setResourceLength(resourceLength);
|
||||||
|
// Now lets report back what was actually saved
|
||||||
|
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
} catch (WebdavException e) {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||||
|
path, tempLockOwner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resp
|
||||||
|
*/
|
||||||
|
private void doUserAgentWorkaround(HttpServletResponse resp) {
|
||||||
|
if (_userAgent != null && _userAgent.indexOf("WebDAVFS") != -1
|
||||||
|
&& _userAgent.indexOf("Transmit") == -1) {
|
||||||
|
LOG.trace("DoPut.execute() : do workaround for user agent '"
|
||||||
|
+ _userAgent + "'");
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
} else if (_userAgent != null && _userAgent.indexOf("Transmit") != -1) {
|
||||||
|
// Transmit also uses WEBDAVFS 1.x.x but crashes
|
||||||
|
// with SC_CREATED response
|
||||||
|
LOG.trace("DoPut.execute() : do workaround for user agent '"
|
||||||
|
+ _userAgent + "'");
|
||||||
|
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||||
|
} else {
|
||||||
|
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package net.sf.webdav.methods;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import net.sf.webdav.StoredObject;
|
||||||
|
import net.sf.webdav.ITransaction;
|
||||||
|
import net.sf.webdav.WebdavStatus;
|
||||||
|
import net.sf.webdav.IWebdavStore;
|
||||||
|
import net.sf.webdav.exceptions.LockFailedException;
|
||||||
|
import net.sf.webdav.locking.IResourceLocks;
|
||||||
|
import net.sf.webdav.locking.LockedObject;
|
||||||
|
|
||||||
|
public class DoUnlock extends DeterminableMethod {
|
||||||
|
|
||||||
|
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||||
|
.getLogger(DoUnlock.class);
|
||||||
|
|
||||||
|
private IWebdavStore _store;
|
||||||
|
private IResourceLocks _resourceLocks;
|
||||||
|
private boolean _readOnly;
|
||||||
|
|
||||||
|
public DoUnlock(IWebdavStore store, IResourceLocks resourceLocks,
|
||||||
|
boolean readOnly) {
|
||||||
|
_store = store;
|
||||||
|
_resourceLocks = resourceLocks;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||||
|
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||||
|
LOG.trace("-- " + this.getClass().getName());
|
||||||
|
|
||||||
|
if (_readOnly) {
|
||||||
|
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
String path = getRelativePath(req);
|
||||||
|
String tempLockOwner = "doUnlock" + System.currentTimeMillis()
|
||||||
|
+ req.toString();
|
||||||
|
try {
|
||||||
|
if (_resourceLocks.lock(transaction, path, tempLockOwner,
|
||||||
|
false, 0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||||
|
|
||||||
|
String lockId = getLockIdFromLockTokenHeader(req);
|
||||||
|
LockedObject lo;
|
||||||
|
if (lockId != null
|
||||||
|
&& ((lo = _resourceLocks.getLockedObjectByID(
|
||||||
|
transaction, lockId)) != null)) {
|
||||||
|
|
||||||
|
String[] owners = lo.getOwner();
|
||||||
|
String owner = null;
|
||||||
|
if (lo.isShared()) {
|
||||||
|
// more than one owner is possible
|
||||||
|
if (owners != null) {
|
||||||
|
for (int i = 0; i < owners.length; i++) {
|
||||||
|
// remove owner from LockedObject
|
||||||
|
lo.removeLockedObjectOwner(owners[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// exclusive, only one lock owner
|
||||||
|
if (owners != null)
|
||||||
|
owner = owners[0];
|
||||||
|
else
|
||||||
|
owner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_resourceLocks.unlock(transaction, lockId, owner)) {
|
||||||
|
StoredObject so = _store.getStoredObject(
|
||||||
|
transaction, path);
|
||||||
|
if (so.isNullResource()) {
|
||||||
|
_store.removeObject(transaction, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||||
|
} else {
|
||||||
|
LOG.trace("DoUnlock failure at " + lo.getPath());
|
||||||
|
resp.sendError(WebdavStatus.SC_METHOD_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (LockFailedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||||
|
tempLockOwner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
#logging.level.net.sf.webdav=trace
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.github.zxbu.webdavteambition;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class WebdavTeambitionApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue