zhouxin5253@163.com
4 years ago
commit
21281d05fe
68 changed files with 9430 additions and 0 deletions
@ -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