commit message
This commit is contained in:
commit
21281d05fe
|
@ -0,0 +1,33 @@
|
|||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
|
@ -0,0 +1,4 @@
|
|||
FROM java:8
|
||||
VOLUME /tmp
|
||||
ADD webdav-teambition.jar /webdav-teambition.jar
|
||||
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/webdav-teambition.jar"]
|
|
@ -0,0 +1,310 @@
|
|||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
|
@ -0,0 +1,182 @@
|
|||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
|
||||
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.4.1</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.github.zxbu</groupId>
|
||||
<artifactId>webdav-teambition</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>webdav-teambition</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.14.9</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,48 @@
|
|||
package com.github.zxbu.webdavteambition;
|
||||
|
||||
import com.github.zxbu.webdavteambition.store.TeambitionFileSystemStore;
|
||||
import net.sf.webdav.LocalFileSystemStore;
|
||||
import net.sf.webdav.WebdavServlet;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.support.ErrorPageFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SpringBootApplication
|
||||
public class WebdavTeambitionApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WebdavTeambitionApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<WebdavServlet> myServlet(){
|
||||
ServletRegistrationBean<WebdavServlet> servletRegistrationBean = new ServletRegistrationBean<>(new WebdavServlet(), "/*");
|
||||
Map<String, String> inits = new LinkedHashMap<>();
|
||||
inits.put("ResourceHandlerImplementation", TeambitionFileSystemStore.class.getName());
|
||||
// inits.put("ResourceHandlerImplementation", LocalFileSystemStore.class.getName());
|
||||
inits.put("rootpath", "./");
|
||||
inits.put("storeDebug", "1");
|
||||
servletRegistrationBean.setInitParameters(inits);
|
||||
return servletRegistrationBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ErrorPageFilter errorPageFilter() {
|
||||
return new ErrorPageFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter) {
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
|
||||
filterRegistrationBean.setFilter(filter);
|
||||
filterRegistrationBean.setEnabled(false);
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package com.github.zxbu.webdavteambition.client;
|
||||
|
||||
import com.github.zxbu.webdavteambition.config.TeambitionProperties;
|
||||
import com.github.zxbu.webdavteambition.util.JsonUtil;
|
||||
import okhttp3.*;
|
||||
import okio.BufferedSink;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TeambitionClient {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionClient.class);
|
||||
private OkHttpClient okHttpClient;
|
||||
private TeambitionProperties teambitionProperties;
|
||||
|
||||
public TeambitionClient(OkHttpClient okHttpClient, TeambitionProperties teambitionProperties) {
|
||||
this.okHttpClient = okHttpClient;
|
||||
this.teambitionProperties = teambitionProperties;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
if (getOrgId() == null || getRootId() == null || getDriveId() == null || getSpaceId() == null) {
|
||||
String personalJson = get("https://www.teambition.com/api/organizations/personal", Collections.emptyMap());
|
||||
String orgId = (String) JsonUtil.getJsonNodeValue(personalJson, "_id");
|
||||
teambitionProperties.setOrgId(orgId);
|
||||
String memberId = (String) JsonUtil.getJsonNodeValue(personalJson, "_creatorId");
|
||||
|
||||
String orgJson = get("/pan/api/orgs/" + orgId, Collections.singletonMap("orgId", orgId));
|
||||
String driveId = (String) JsonUtil.getJsonNodeValue(orgJson, "data.driveId");
|
||||
teambitionProperties.setDriveId(driveId);
|
||||
|
||||
Map<String, String> params = new LinkedHashMap<>();
|
||||
params.put("orgId", orgId);
|
||||
params.put("memberId", memberId);
|
||||
String spacesJson = get("/pan/api/spaces", params);
|
||||
String rootId = (String) JsonUtil.getJsonNodeValue(spacesJson, "[0].rootId");
|
||||
String spaceId = (String) JsonUtil.getJsonNodeValue(spacesJson, "[0].spaceId");
|
||||
teambitionProperties.setRootId(rootId);
|
||||
teambitionProperties.setSpaceId(spaceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getOrgId() {
|
||||
return teambitionProperties.getOrgId();
|
||||
}
|
||||
|
||||
public String getDriveId() {
|
||||
return teambitionProperties.getDriveId();
|
||||
}
|
||||
|
||||
public String getSpaceId() {
|
||||
return teambitionProperties.getSpaceId();
|
||||
}
|
||||
|
||||
public String getRootId() {
|
||||
return teambitionProperties.getRootId();
|
||||
}
|
||||
|
||||
public InputStream download(String url) {
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = null;
|
||||
try {
|
||||
response = okHttpClient.newCall(request).execute();
|
||||
return response.body().byteStream();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void upload(String url, byte[] bytes, final int offset, final int byteCount) {
|
||||
Request request = new Request.Builder()
|
||||
.put(RequestBody.create(MediaType.parse(""), bytes, offset, byteCount))
|
||||
.url(url).build();
|
||||
try (Response response = okHttpClient.newCall(request).execute()){
|
||||
LOGGER.info("post {}, code {}", url, response.code());
|
||||
if (!response.isSuccessful()) {
|
||||
LOGGER.error("请求失败,url={}, code={}, body={}", url, response.code(), response.body().string());
|
||||
throw new RuntimeException("请求失败:" + url);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String post(String url, Object body) {
|
||||
Request request = new Request.Builder()
|
||||
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonUtil.toJson(body)))
|
||||
.url(getTotalUrl(url)).build();
|
||||
try (Response response = okHttpClient.newCall(request).execute()){
|
||||
LOGGER.info("post {}, code {}", url, response.code());
|
||||
if (!response.isSuccessful()) {
|
||||
LOGGER.error("请求失败,url={}, code={}, body={}", url, response.code(), response.body().string());
|
||||
throw new RuntimeException("请求失败:" + url);
|
||||
}
|
||||
return toString(response.body());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String put(String url, Object body) {
|
||||
Request request = new Request.Builder()
|
||||
.put(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonUtil.toJson(body)))
|
||||
.url(getTotalUrl(url)).build();
|
||||
try (Response response = okHttpClient.newCall(request).execute()){
|
||||
LOGGER.info("put {}, code {}", url, response.code());
|
||||
if (!response.isSuccessful()) {
|
||||
LOGGER.error("请求失败,url={}, code={}, body={}", url, response.code(), response.body().string());
|
||||
throw new RuntimeException("请求失败:" + url);
|
||||
}
|
||||
return toString(response.body());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String get(String url, Map<String, String> params) {
|
||||
try {
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(getTotalUrl(url)).newBuilder();
|
||||
params.forEach(urlBuilder::addQueryParameter);
|
||||
|
||||
Request request = new Request.Builder().get().url(urlBuilder.build()).build();
|
||||
try (Response response = okHttpClient.newCall(request).execute()){
|
||||
LOGGER.info("get {}, code {}", urlBuilder.build(), response.code());
|
||||
if (!response.isSuccessful()) {
|
||||
throw new RuntimeException("请求失败:" + urlBuilder.build().toString());
|
||||
}
|
||||
return toString(response.body());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String toString(ResponseBody responseBody) throws IOException {
|
||||
if (responseBody == null) {
|
||||
return null;
|
||||
}
|
||||
return responseBody.string();
|
||||
}
|
||||
|
||||
private String getTotalUrl(String url) {
|
||||
if (url.startsWith("http")) {
|
||||
return url;
|
||||
}
|
||||
return teambitionProperties.getUrl() + url;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.github.zxbu.webdavteambition.config;
|
||||
|
||||
import com.github.zxbu.webdavteambition.client.TeambitionClient;
|
||||
import com.github.zxbu.webdavteambition.store.TeambitionFileSystemStore;
|
||||
import okhttp3.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TeambitionProperties.class)
|
||||
public class TeambitionAutoConfig implements ApplicationContextAware {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionAutoConfig.class);
|
||||
|
||||
@Autowired
|
||||
private TeambitionProperties teambitionProperties;
|
||||
|
||||
@Bean
|
||||
public TeambitionClient teambitionClient(ApplicationContext applicationContext) throws Exception {
|
||||
|
||||
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
request = request.newBuilder()
|
||||
.removeHeader("User-Agent")
|
||||
.addHeader("User-Agent", teambitionProperties.getAgent())
|
||||
.build();
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}).cookieJar(new CookieJar() {
|
||||
@Override
|
||||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||||
if (StringUtils.hasLength(teambitionProperties.getCookies())) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||
String cookies = teambitionProperties.getCookies();
|
||||
String[] cookieSplit = cookies.split("; ");
|
||||
List<Cookie> cookieList = new ArrayList<>(cookieSplit.length);
|
||||
for (String cookie : cookieSplit) {
|
||||
Cookie parse = Cookie.parse(url, cookie);
|
||||
cookieList.add(parse);
|
||||
}
|
||||
return cookieList;
|
||||
}
|
||||
}).build();
|
||||
TeambitionClient teambitionClient = new TeambitionClient(okHttpClient, teambitionProperties);
|
||||
try (Response response = okHttpClient.newCall(new Request.Builder().get().url(teambitionProperties.getUrl() + "/pan/api").build()).execute()) {
|
||||
if (response.isSuccessful()) {
|
||||
LOGGER.info("TeambitionClient 启动成功");
|
||||
}
|
||||
}
|
||||
teambitionClient.init();
|
||||
return teambitionClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
TeambitionFileSystemStore.setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.github.zxbu.webdavteambition.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "teambition", ignoreUnknownFields = true)
|
||||
public class TeambitionProperties {
|
||||
private String url = "https://pan.teambition.com";
|
||||
private String cookies;
|
||||
private String agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36";
|
||||
private String orgId;
|
||||
private String driveId;
|
||||
private String spaceId;
|
||||
private String rootId;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getCookies() {
|
||||
return cookies;
|
||||
}
|
||||
|
||||
public void setCookies(String cookies) {
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getSpaceId() {
|
||||
return spaceId;
|
||||
}
|
||||
|
||||
public String getAgent() {
|
||||
return agent;
|
||||
}
|
||||
|
||||
public void setAgent(String agent) {
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
public void setSpaceId(String spaceId) {
|
||||
this.spaceId = spaceId;
|
||||
}
|
||||
|
||||
public String getRootId() {
|
||||
return rootId;
|
||||
}
|
||||
|
||||
public void setRootId(String rootId) {
|
||||
this.rootId = rootId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class BaseQuery extends Page{
|
||||
private String orgId;
|
||||
private String driveId;
|
||||
private String spaceId;
|
||||
private String parentId;
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getSpaceId() {
|
||||
return spaceId;
|
||||
}
|
||||
|
||||
public void setSpaceId(String spaceId) {
|
||||
this.spaceId = spaceId;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class CreateFileRequest {
|
||||
private String ccpParentId;
|
||||
private String checkNameMode = "refuse";
|
||||
private String driveId;
|
||||
private String name;
|
||||
private String orgId;
|
||||
private String parentId;
|
||||
private String spaceId;
|
||||
private String type;
|
||||
|
||||
public String getCheckNameMode() {
|
||||
return checkNameMode;
|
||||
}
|
||||
|
||||
public void setCheckNameMode(String checkNameMode) {
|
||||
this.checkNameMode = checkNameMode;
|
||||
}
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getSpaceId() {
|
||||
return spaceId;
|
||||
}
|
||||
|
||||
public void setSpaceId(String spaceId) {
|
||||
this.spaceId = spaceId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getCcpParentId() {
|
||||
return ccpParentId;
|
||||
}
|
||||
|
||||
public void setCcpParentId(String ccpParentId) {
|
||||
this.ccpParentId = ccpParentId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public enum FileType {
|
||||
folder, file;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MoveRequest {
|
||||
private String driveId;
|
||||
private String parentId;
|
||||
private String orgId;
|
||||
private boolean sameLevel = false;
|
||||
private List<MoveRequestId> ids;
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public boolean isSameLevel() {
|
||||
return sameLevel;
|
||||
}
|
||||
|
||||
public void setSameLevel(boolean sameLevel) {
|
||||
this.sameLevel = sameLevel;
|
||||
}
|
||||
|
||||
public List<MoveRequestId> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void setIds(List<MoveRequestId> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class MoveRequestId {
|
||||
private String ccpFileId;
|
||||
private String id;
|
||||
|
||||
public String getCcpFileId() {
|
||||
return ccpFileId;
|
||||
}
|
||||
|
||||
public void setCcpFileId(String ccpFileId) {
|
||||
this.ccpFileId = ccpFileId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class NodeQuery extends BaseQuery {
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class Page {
|
||||
private int offset;
|
||||
private int limit;
|
||||
private String orderBy;
|
||||
private String orderDirection;
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public String getOrderBy() {
|
||||
return orderBy;
|
||||
}
|
||||
|
||||
public void setOrderBy(String orderBy) {
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
public String getOrderDirection() {
|
||||
return orderDirection;
|
||||
}
|
||||
|
||||
public void setOrderDirection(String orderDirection) {
|
||||
this.orderDirection = orderDirection;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class PathInfo {
|
||||
private String path;
|
||||
private String parentPath;
|
||||
private String name;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getParentPath() {
|
||||
return parentPath;
|
||||
}
|
||||
|
||||
public void setParentPath(String parentPath) {
|
||||
this.parentPath = parentPath;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RemoveRequest {
|
||||
private String orgId;
|
||||
private List<String> nodeIds;
|
||||
|
||||
public List<String> getNodeIds() {
|
||||
return nodeIds;
|
||||
}
|
||||
|
||||
public void setNodeIds(List<String> nodeIds) {
|
||||
this.nodeIds = nodeIds;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class RenameRequest {
|
||||
private String ccpFileId;
|
||||
private String driveId;
|
||||
private String name;
|
||||
private String orgId;
|
||||
|
||||
public String getCcpFileId() {
|
||||
return ccpFileId;
|
||||
}
|
||||
|
||||
public void setCcpFileId(String ccpFileId) {
|
||||
this.ccpFileId = ccpFileId;
|
||||
}
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class UploadFinalRequest {
|
||||
private String ccpFileId;
|
||||
private String driveId;
|
||||
private String nodeId;
|
||||
private String orgId;
|
||||
private String uploadId;
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
|
||||
public String getCcpFileId() {
|
||||
return ccpFileId;
|
||||
}
|
||||
|
||||
public void setCcpFileId(String ccpFileId) {
|
||||
this.ccpFileId = ccpFileId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
public class UploadPreInfo {
|
||||
private String ccpParentId;
|
||||
private int chunkCount;
|
||||
private String contentType = "";
|
||||
private String driveId;
|
||||
private String name;
|
||||
private int size;
|
||||
private String type;
|
||||
|
||||
public int getChunkCount() {
|
||||
return chunkCount;
|
||||
}
|
||||
|
||||
public void setChunkCount(int chunkCount) {
|
||||
this.chunkCount = chunkCount;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getDriveId() {
|
||||
return driveId;
|
||||
}
|
||||
|
||||
public void setDriveId(String driveId) {
|
||||
this.driveId = driveId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getCcpParentId() {
|
||||
return ccpParentId;
|
||||
}
|
||||
|
||||
public void setCcpParentId(String ccpParentId) {
|
||||
this.ccpParentId = ccpParentId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.github.zxbu.webdavteambition.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class UploadPreRequest {
|
||||
private String checkNameMode = "autoRename";
|
||||
private String orgId;
|
||||
private String parentId;
|
||||
private String spaceId;
|
||||
private List<UploadPreInfo> infos;
|
||||
|
||||
public String getCheckNameMode() {
|
||||
return checkNameMode;
|
||||
}
|
||||
|
||||
public void setCheckNameMode(String checkNameMode) {
|
||||
this.checkNameMode = checkNameMode;
|
||||
}
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getSpaceId() {
|
||||
return spaceId;
|
||||
}
|
||||
|
||||
public void setSpaceId(String spaceId) {
|
||||
this.spaceId = spaceId;
|
||||
}
|
||||
|
||||
public List<UploadPreInfo> getInfos() {
|
||||
return infos;
|
||||
}
|
||||
|
||||
public void setInfos(List<UploadPreInfo> infos) {
|
||||
this.infos = infos;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.github.zxbu.webdavteambition.model.result;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ListResult<T> {
|
||||
private List<T> data;
|
||||
|
||||
public List<T> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(List<T> data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package com.github.zxbu.webdavteambition.model.result;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class TFile {
|
||||
private String kind;
|
||||
private String nodeId;
|
||||
private String name;
|
||||
private Date created;
|
||||
private Date updated;
|
||||
private String parentId;
|
||||
private String status;
|
||||
private String downloadUrl;
|
||||
private Long size;
|
||||
private String ccpFileId;
|
||||
private String ccpParentFileId;
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
public void setUpdated(Date updated) {
|
||||
this.updated = updated;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getDownloadUrl() {
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
public void setDownloadUrl(String downloadUrl) {
|
||||
this.downloadUrl = downloadUrl;
|
||||
}
|
||||
|
||||
public String getCcpFileId() {
|
||||
return ccpFileId;
|
||||
}
|
||||
|
||||
public void setCcpFileId(String ccpFileId) {
|
||||
this.ccpFileId = ccpFileId;
|
||||
}
|
||||
|
||||
public String getCcpParentFileId() {
|
||||
return ccpParentFileId;
|
||||
}
|
||||
|
||||
public void setCcpParentFileId(String ccpParentFileId) {
|
||||
this.ccpParentFileId = ccpParentFileId;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.github.zxbu.webdavteambition.model.result;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class UploadPreResult {
|
||||
private String ccpFileId;
|
||||
private String nodeId;
|
||||
private String name;
|
||||
private String kind;
|
||||
private String uploadId;
|
||||
private List<String> uploadUrl;
|
||||
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCcpFileId() {
|
||||
return ccpFileId;
|
||||
}
|
||||
|
||||
public void setCcpFileId(String ccpFileId) {
|
||||
this.ccpFileId = ccpFileId;
|
||||
}
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
|
||||
public List<String> getUploadUrl() {
|
||||
return uploadUrl;
|
||||
}
|
||||
|
||||
public void setUploadUrl(List<String> uploadUrl) {
|
||||
this.uploadUrl = uploadUrl;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
package com.github.zxbu.webdavteambition.store;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.zxbu.webdavteambition.client.TeambitionClient;
|
||||
import com.github.zxbu.webdavteambition.model.*;
|
||||
import com.github.zxbu.webdavteambition.model.result.ListResult;
|
||||
import com.github.zxbu.webdavteambition.model.result.TFile;
|
||||
import com.github.zxbu.webdavteambition.model.result.UploadPreResult;
|
||||
import com.github.zxbu.webdavteambition.util.JsonUtil;
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
public class TeambitionClientService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionClientService.class);
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static String rootPath = "/";
|
||||
private static int chunkSize = 10485760; // 10MB
|
||||
private TFile rootTFile = null;
|
||||
private Map<String, TFile> nodeIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final TeambitionClient client;
|
||||
|
||||
public TeambitionClientService(TeambitionClient teambitionClient) {
|
||||
this.client = teambitionClient;
|
||||
}
|
||||
|
||||
public List<TFile> getTFiles(String nodeId) {
|
||||
NodeQuery nodeQuery = new NodeQuery();
|
||||
nodeQuery.setOrgId(client.getOrgId());
|
||||
nodeQuery.setOffset(0);
|
||||
nodeQuery.setLimit(10000);
|
||||
nodeQuery.setOrderBy("updateTime");
|
||||
nodeQuery.setOrderDirection("desc");
|
||||
nodeQuery.setDriveId(client.getDriveId());
|
||||
nodeQuery.setSpaceId(client.getSpaceId());
|
||||
nodeQuery.setParentId(nodeId);
|
||||
String json = client.get("/pan/api/nodes", toMap(nodeQuery));
|
||||
ListResult<TFile> tFileListResult = JsonUtil.readValue(json, new TypeReference<ListResult<TFile>>() {
|
||||
});
|
||||
return tFileListResult.getData();
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> toMap(Object o) {
|
||||
try {
|
||||
String json = objectMapper.writeValueAsString(o);
|
||||
Map<String, Object> rawMap = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
Map<String, String> stringMap = new LinkedHashMap<>();
|
||||
rawMap.forEach((s, o1) -> {
|
||||
if (o1 != null) {
|
||||
stringMap.put(s, o1.toString());
|
||||
}
|
||||
});
|
||||
return stringMap;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void uploadPre(String path, int size, InputStream inputStream) {
|
||||
path = normalizingPath(path);
|
||||
PathInfo pathInfo = getPathInfo(path);
|
||||
TFile parent = getTFileByPath(pathInfo.getParentPath());
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
int chunkCount = (int) Math.ceil(((double) size) / chunkSize); // 进1法
|
||||
|
||||
UploadPreRequest uploadPreRequest = new UploadPreRequest();
|
||||
uploadPreRequest.setOrgId(client.getOrgId());
|
||||
uploadPreRequest.setParentId(parent.getNodeId());
|
||||
uploadPreRequest.setSpaceId(client.getSpaceId());
|
||||
UploadPreInfo uploadPreInfo = new UploadPreInfo();
|
||||
uploadPreInfo.setCcpParentId(parent.getCcpFileId());
|
||||
uploadPreInfo.setDriveId(client.getDriveId());
|
||||
uploadPreInfo.setName(pathInfo.getName());
|
||||
uploadPreInfo.setSize(size);
|
||||
uploadPreInfo.setChunkCount(chunkCount);
|
||||
uploadPreInfo.setType(FileType.file.name());
|
||||
uploadPreRequest.setInfos(Collections.singletonList(uploadPreInfo));
|
||||
LOGGER.info("开始上传文件,文件名:{},总大小:{}, 文件块数量:{}", path, size, chunkCount);
|
||||
|
||||
String json = client.post("/pan/api/nodes/file", uploadPreRequest);
|
||||
List<UploadPreResult> uploadPreResultList = JsonUtil.readValue(json, new TypeReference<List<UploadPreResult>>() {
|
||||
});
|
||||
UploadPreResult uploadPreResult = uploadPreResultList.get(0);
|
||||
List<String> uploadUrl = uploadPreResult.getUploadUrl();
|
||||
LOGGER.info("文件预处理成功,开始上传。文件名:{},上传URL数量:{}", path, uploadUrl.size());
|
||||
|
||||
byte[] buffer = new byte[chunkSize];
|
||||
for (String oneUploadUrl : uploadUrl) {
|
||||
try {
|
||||
int read = IOUtils.read(inputStream, buffer, 0, buffer.length);
|
||||
if (read == -1) {
|
||||
return;
|
||||
}
|
||||
client.upload(oneUploadUrl, buffer, 0, read);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
UploadFinalRequest uploadFinalRequest = new UploadFinalRequest();
|
||||
uploadFinalRequest.setCcpFileId(uploadPreResult.getCcpFileId());
|
||||
uploadFinalRequest.setDriveId(client.getDriveId());
|
||||
uploadFinalRequest.setNodeId(uploadPreResult.getNodeId());
|
||||
uploadFinalRequest.setOrgId(client.getOrgId());
|
||||
uploadFinalRequest.setUploadId(uploadPreResult.getUploadId());
|
||||
client.post("/pan/api/nodes/complete", uploadFinalRequest);
|
||||
LOGGER.info("文件上传成功。文件名:{}", path);
|
||||
if (!uploadPreResult.getName().equals(pathInfo.getName())) {
|
||||
LOGGER.info("上传文件名{}与原文件名{}不同,对文件进行重命名", uploadPreResult.getName(), pathInfo.getName());
|
||||
RenameRequest renameRequest = new RenameRequest();
|
||||
renameRequest.setCcpFileId(uploadPreResult.getCcpFileId());
|
||||
renameRequest.setDriveId(client.getDriveId());
|
||||
renameRequest.setOrgId(client.getOrgId());
|
||||
renameRequest.setName(pathInfo.getName());
|
||||
client.put("/pan/api/nodes/" + parent.getNodeId(), renameRequest);
|
||||
}
|
||||
clearCache(path);
|
||||
}
|
||||
|
||||
public void rename(String sourcePath, String newName) {
|
||||
sourcePath = normalizingPath(sourcePath);
|
||||
TFile tFile = getTFileByPath(sourcePath);
|
||||
RenameRequest renameRequest = new RenameRequest();
|
||||
renameRequest.setCcpFileId(tFile.getCcpFileId());
|
||||
renameRequest.setDriveId(client.getDriveId());
|
||||
renameRequest.setOrgId(client.getOrgId());
|
||||
renameRequest.setName(newName);
|
||||
client.put("/pan/api/nodes/" + tFile.getParentId(), renameRequest);
|
||||
clearCache(sourcePath);
|
||||
}
|
||||
|
||||
public void move(String sourcePath, String targetPath) {
|
||||
sourcePath = normalizingPath(sourcePath);
|
||||
targetPath = normalizingPath(targetPath);
|
||||
|
||||
TFile sourceTFile = getTFileByPath(sourcePath);
|
||||
TFile targetTFile = getTFileByPath(targetPath);
|
||||
MoveRequest moveRequest = new MoveRequest();
|
||||
moveRequest.setOrgId(client.getOrgId());
|
||||
moveRequest.setDriveId(client.getDriveId());
|
||||
moveRequest.setParentId(targetTFile.getNodeId());
|
||||
MoveRequestId moveRequestId = new MoveRequestId();
|
||||
moveRequestId.setCcpFileId(sourceTFile.getCcpFileId());
|
||||
moveRequestId.setId(sourceTFile.getNodeId());
|
||||
moveRequest.setIds(Collections.singletonList(moveRequestId));
|
||||
client.post("/pan/api/nodes/move", moveRequest);
|
||||
clearCache(sourcePath);
|
||||
clearCache(targetPath);
|
||||
}
|
||||
|
||||
public void remove(String path) {
|
||||
path = normalizingPath(path);
|
||||
TFile tFile = getTFileByPath(path);
|
||||
if (tFile == null) {
|
||||
return;
|
||||
}
|
||||
RemoveRequest removeRequest = new RemoveRequest();
|
||||
removeRequest.setOrgId(client.getOrgId());
|
||||
removeRequest.setNodeIds(Collections.singletonList(tFile.getNodeId()));
|
||||
client.post("/pan/api/nodes/archive", removeRequest);
|
||||
clearCache(path);
|
||||
}
|
||||
|
||||
|
||||
public void createFolder(String path) {
|
||||
path = normalizingPath(path);
|
||||
PathInfo pathInfo = getPathInfo(path);
|
||||
TFile parent = getTFileByPath(pathInfo.getParentPath());
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CreateFileRequest createFileRequest = new CreateFileRequest();
|
||||
createFileRequest.setCcpParentId(parent.getCcpFileId());
|
||||
createFileRequest.setDriveId(client.getDriveId());
|
||||
createFileRequest.setName(pathInfo.getName());
|
||||
createFileRequest.setOrgId(client.getOrgId());
|
||||
createFileRequest.setParentId(parent.getNodeId());
|
||||
createFileRequest.setSpaceId(client.getSpaceId());
|
||||
createFileRequest.setType(FileType.folder.name());
|
||||
client.post("/pan/api/nodes/folder", createFileRequest);
|
||||
clearCache(path);
|
||||
}
|
||||
|
||||
|
||||
public TFile getTFileByPath(String path) {
|
||||
path = normalizingPath(path);
|
||||
|
||||
return nodeIdMap.computeIfAbsent(path, this::getNodeIdByPath2);
|
||||
}
|
||||
|
||||
public InputStream download(String path) {
|
||||
String downloadUrl = getTFileByPath(path).getDownloadUrl();
|
||||
return client.download(downloadUrl);
|
||||
}
|
||||
|
||||
private TFile getNodeIdByPath2(String path) {
|
||||
if (!StringUtils.hasLength(path)) {
|
||||
path = rootPath;
|
||||
}
|
||||
if (path.equals(rootPath)) {
|
||||
return getRootTFile();
|
||||
}
|
||||
PathInfo pathInfo = getPathInfo(path);
|
||||
TFile tFile = getTFileByPath(pathInfo.getParentPath());
|
||||
if (tFile == null ) {
|
||||
return null;
|
||||
}
|
||||
return getNodeIdByParentId(tFile.getNodeId(), pathInfo.getName());
|
||||
}
|
||||
|
||||
|
||||
public PathInfo getPathInfo(String path) {
|
||||
path = normalizingPath(path);
|
||||
if (path.equals(rootPath)) {
|
||||
PathInfo pathInfo = new PathInfo();
|
||||
pathInfo.setPath(path);
|
||||
pathInfo.setName(path);
|
||||
return pathInfo;
|
||||
}
|
||||
int index = path.lastIndexOf("/");
|
||||
String parentPath = path.substring(0, index + 1);
|
||||
String name = path.substring(index+1);
|
||||
PathInfo pathInfo = new PathInfo();
|
||||
pathInfo.setPath(path);
|
||||
pathInfo.setParentPath(parentPath);
|
||||
pathInfo.setName(name);
|
||||
return pathInfo;
|
||||
}
|
||||
|
||||
private TFile getRootTFile() {
|
||||
if (rootTFile == null) {
|
||||
NodeQuery nodeQuery = new NodeQuery();
|
||||
nodeQuery.setOrgId(client.getOrgId());
|
||||
nodeQuery.setDriveId(client.getDriveId());
|
||||
nodeQuery.setSpaceId(client.getSpaceId());
|
||||
String json = client.get("/pan/api/nodes/" + client.getRootId(), toMap(nodeQuery));
|
||||
rootTFile = JsonUtil.readValue(json, TFile.class);
|
||||
}
|
||||
return rootTFile;
|
||||
}
|
||||
|
||||
private TFile getNodeIdByParentId(String parentId, String name) {
|
||||
List<TFile> tFiles = getTFiles(parentId);
|
||||
for (TFile tFile : tFiles) {
|
||||
if (tFile.getName().equals(name)) {
|
||||
return tFile;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String normalizingPath(String path) {
|
||||
path = path.replaceAll("//", "/");
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private void clearCache(String path) {
|
||||
nodeIdMap.remove(path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package com.github.zxbu.webdavteambition.store;
|
||||
|
||||
import com.github.zxbu.webdavteambition.model.PathInfo;
|
||||
import com.github.zxbu.webdavteambition.model.result.TFile;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class TeambitionFileSystemStore implements IWebdavStore {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TeambitionFileSystemStore.class);
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
private static TeambitionClientService teambitionClientService;
|
||||
|
||||
|
||||
public TeambitionFileSystemStore(File file) {
|
||||
}
|
||||
|
||||
public static void setApplicationContext(ApplicationContext applicationContext) {
|
||||
TeambitionFileSystemStore.applicationContext = applicationContext;
|
||||
TeambitionFileSystemStore.teambitionClientService = applicationContext.getBean(TeambitionClientService.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
LOGGER.debug("destroy");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITransaction begin(Principal principal) {
|
||||
LOGGER.debug("begin");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAuthentication(ITransaction transaction) {
|
||||
LOGGER.debug("checkAuthentication");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(ITransaction transaction) {
|
||||
LOGGER.debug("commit");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(ITransaction transaction) {
|
||||
LOGGER.debug("rollback");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFolder(ITransaction transaction, String folderUri) {
|
||||
LOGGER.info("createFolder {}", folderUri);
|
||||
|
||||
teambitionClientService.createFolder(folderUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createResource(ITransaction transaction, String resourceUri) {
|
||||
LOGGER.info("createResource {}", resourceUri);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceContent(ITransaction transaction, String resourceUri) {
|
||||
LOGGER.debug("getResourceContent: {}", resourceUri);
|
||||
return teambitionClientService.download(resourceUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long setResourceContent(ITransaction transaction, String resourceUri, InputStream content, String contentType, String characterEncoding) {
|
||||
LOGGER.info("setResourceContent {}", resourceUri);
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = requestAttributes.getRequest();
|
||||
int contentLength = request.getContentLength();
|
||||
if (contentLength <= 0) {
|
||||
return 0;
|
||||
}
|
||||
teambitionClientService.uploadPre(resourceUri, contentLength, content);
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getChildrenNames(ITransaction transaction, String folderUri) {
|
||||
LOGGER.debug("getChildrenNames: {}", folderUri);
|
||||
TFile tFile = teambitionClientService.getTFileByPath(folderUri);
|
||||
List<TFile> tFileList = teambitionClientService.getTFiles(tFile.getNodeId());
|
||||
return tFileList.stream().map(TFile::getName).toArray(String[]::new);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public long getResourceLength(ITransaction transaction, String path) {
|
||||
LOGGER.debug("getResourceLength: {}", path);
|
||||
TFile tFile = teambitionClientService.getTFileByPath(path);
|
||||
if (tFile == null || tFile.getSize() == null) {
|
||||
return 384;
|
||||
}
|
||||
|
||||
return tFile.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeObject(ITransaction transaction, String uri) {
|
||||
LOGGER.info("removeObject: {}", uri);
|
||||
teambitionClientService.remove(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveObject(ITransaction transaction, String destinationPath, String sourcePath) {
|
||||
LOGGER.info("moveObject, destinationPath={}, sourcePath={}", destinationPath, sourcePath);
|
||||
|
||||
PathInfo destinationPathInfo = teambitionClientService.getPathInfo(destinationPath);
|
||||
PathInfo sourcePathInfo = teambitionClientService.getPathInfo(sourcePath);
|
||||
// 名字相同,说明是移动目录
|
||||
if (sourcePathInfo.getName().equals(destinationPathInfo.getName())) {
|
||||
teambitionClientService.move(sourcePath, destinationPathInfo.getParentPath());
|
||||
} else {
|
||||
if (!destinationPathInfo.getParentPath().equals(sourcePathInfo.getParentPath())) {
|
||||
throw new RuntimeException("不支持目录和名字同时修改");
|
||||
}
|
||||
// 名字不同,说明是修改名字。不考虑目录和名字同时修改的情况
|
||||
teambitionClientService.rename(sourcePath, destinationPathInfo.getName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
||||
LOGGER.debug("getStoredObject: {}", uri);
|
||||
TFile tFile = teambitionClientService.getTFileByPath(uri);
|
||||
if (tFile != null) {
|
||||
StoredObject so = new StoredObject();
|
||||
so.setFolder(tFile.getKind().equalsIgnoreCase("folder"));
|
||||
so.setResourceLength(getResourceLength(transaction, uri));
|
||||
so.setCreationDate(tFile.getCreated());
|
||||
so.setLastModified(tFile.getUpdated());
|
||||
return so;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package com.github.zxbu.webdavteambition.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonUtil {
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static String toJson(Object o) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(o);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T readValue(String json, TypeReference<T> valueTypeRef) {
|
||||
try {
|
||||
return objectMapper.readValue(json, valueTypeRef);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static <T> T readValue(String json, Class<T> valueType) {
|
||||
try {
|
||||
return objectMapper.readValue(json, valueType);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
// 忽略未知的字段
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
// 读取不认识的枚举时,当null值处理
|
||||
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
|
||||
|
||||
objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
|
||||
|
||||
}
|
||||
|
||||
public static Object getJsonNodeValue(String json, String path) {
|
||||
try {
|
||||
JsonNode jsonNode = getJsonNode(json);
|
||||
List<PathToken> pathTokens = getPathTokens(path);
|
||||
for (PathToken pathToken : pathTokens) {
|
||||
if (pathToken.getType() == PathType.KEY) {
|
||||
jsonNode = jsonNode.get(pathToken.getValue());
|
||||
}
|
||||
if (pathToken.getType() == PathType.NUMBER) {
|
||||
jsonNode = jsonNode.get(Integer.parseInt(pathToken.getValue()));
|
||||
}
|
||||
}
|
||||
return objectMapper.treeToValue(jsonNode, Object.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static List<PathToken> getPathTokens(String path) {
|
||||
List<PathToken> pathTokenList = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean escape = false;
|
||||
for (char c : path.toCharArray()) {
|
||||
if (c == '[') {
|
||||
if (sb.length() > 0) {
|
||||
pathTokenList.add(new PathToken(PathType.KEY, sb.toString()));
|
||||
}
|
||||
sb.setLength(0);
|
||||
} else if (c == ']') {
|
||||
if (sb.length() > 0) {
|
||||
pathTokenList.add(new PathToken(PathType.NUMBER, sb.toString()));
|
||||
}
|
||||
sb.setLength(0);
|
||||
} else if (c == '.') {
|
||||
if (escape) {
|
||||
sb.append(c);
|
||||
} else {
|
||||
if (sb.length() > 0) {
|
||||
pathTokenList.add(new PathToken(PathType.KEY, sb.toString()));
|
||||
}
|
||||
sb.setLength(0);
|
||||
}
|
||||
|
||||
} else if (c == '\\') {
|
||||
escape = true;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
pathTokenList.add(new PathToken(PathType.KEY, sb.toString()));
|
||||
}
|
||||
return pathTokenList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static JsonNode getJsonNode(String responseBody) {
|
||||
try {
|
||||
return objectMapper.readTree(responseBody);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private enum PathType {
|
||||
KEY, NUMBER;
|
||||
}
|
||||
|
||||
private static class PathToken {
|
||||
private PathType type; // 0 是key, 1 是索引数字
|
||||
private String value;
|
||||
|
||||
public PathToken(PathType type, String value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public PathType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
|
||||
public interface IMethodExecutor {
|
||||
|
||||
void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package net.sf.webdav;
|
||||
|
||||
public interface IMimeTyper {
|
||||
|
||||
/**
|
||||
* Detect the mime type of this object
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
String getMimeType(ITransaction transaction, String path);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package net.sf.webdav;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
public interface ITransaction {
|
||||
|
||||
Principal getPrincipal();
|
||||
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* $Header: /Users/ak/temp/cvs2svn/webdav-servlet/src/main/java/net/sf/webdav/IWebdavStore.java,v 1.1 2008-08-05 07:38:42 bauhardt Exp $
|
||||
* $Revision: 1.1 $
|
||||
* $Date: 2008-08-05 07:38:42 $
|
||||
*
|
||||
* ====================================================================
|
||||
*
|
||||
* Copyright 2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package net.sf.webdav;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
|
||||
/**
|
||||
* Interface for simple implementation of any store for the WebdavServlet
|
||||
* <p>
|
||||
* based on the BasicWebdavStore from Oliver Zeigermann, that was part of the
|
||||
* Webdav Construcktion Kit from slide
|
||||
*
|
||||
*/
|
||||
public interface IWebdavStore {
|
||||
|
||||
/**
|
||||
* Life cycle method, called by WebdavServlet's destroy() method. Should be used to clean up resources.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Indicates that a new request or transaction with this store involved has
|
||||
* been started. The request will be terminated by either {@link #commit()}
|
||||
* or {@link #rollback()}. If only non-read methods have been called, the
|
||||
* request will be terminated by a {@link #commit()}. This method will be
|
||||
* called by (@link WebdavStoreAdapter} at the beginning of each request.
|
||||
*
|
||||
*
|
||||
* @param principal
|
||||
* the principal that started this request or <code>null</code> if
|
||||
* there is non available
|
||||
*
|
||||
* @throws WebdavException
|
||||
*/
|
||||
ITransaction begin(Principal principal);
|
||||
|
||||
/**
|
||||
* Checks if authentication information passed in is valid. If not throws an
|
||||
* exception.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
*/
|
||||
void checkAuthentication(ITransaction transaction);
|
||||
|
||||
/**
|
||||
* Indicates that all changes done inside this request shall be made
|
||||
* permanent and any transactions, connections and other temporary resources
|
||||
* shall be terminated.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
*
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void commit(ITransaction transaction);
|
||||
|
||||
/**
|
||||
* Indicates that all changes done inside this request shall be undone and
|
||||
* any transactions, connections and other temporary resources shall be
|
||||
* terminated.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
*
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void rollback(ITransaction transaction);
|
||||
|
||||
/**
|
||||
* Creates a folder at the position specified by <code>folderUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param folderUri
|
||||
* URI of the folder
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void createFolder(ITransaction transaction, String folderUri);
|
||||
|
||||
/**
|
||||
* Creates a content resource at the position specified by
|
||||
* <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the content resource
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void createResource(ITransaction transaction, String resourceUri);
|
||||
|
||||
/**
|
||||
* Gets the content of the resource specified by <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the content resource
|
||||
* @return input stream you can read the content of the resource from
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
InputStream getResourceContent(ITransaction transaction, String resourceUri);
|
||||
|
||||
/**
|
||||
* Sets / stores the content of the resource specified by
|
||||
* <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the resource where the content will be stored
|
||||
* @param content
|
||||
* input stream from which the content will be read from
|
||||
* @param contentType
|
||||
* content type of the resource or <code>null</code> if unknown
|
||||
* @param characterEncoding
|
||||
* character encoding of the resource or <code>null</code> if unknown
|
||||
* or not applicable
|
||||
* @return lenght of resource
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
long setResourceContent(ITransaction transaction, String resourceUri,
|
||||
InputStream content, String contentType, String characterEncoding);
|
||||
|
||||
/**
|
||||
* Gets the names of the children of the folder specified by
|
||||
* <code>folderUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param folderUri
|
||||
* URI of the folder
|
||||
* @return a (possibly empty) list of children, or <code>null</code> if the
|
||||
* uri points to a file
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
String[] getChildrenNames(ITransaction transaction, String folderUri);
|
||||
|
||||
/**
|
||||
* Gets the length of the content resource specified by
|
||||
* <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the content resource
|
||||
* @return length of the resource in bytes, <code>-1</code> declares this
|
||||
* value as invalid and asks the adapter to try to set it from the
|
||||
* properties if possible
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
long getResourceLength(ITransaction transaction, String path);
|
||||
|
||||
/**
|
||||
* Removes the object specified by <code>uri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param uri
|
||||
* URI of the object, i.e. content resource or folder
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void removeObject(ITransaction transaction, String uri);
|
||||
|
||||
boolean moveObject(ITransaction transaction, String destinationPath, String sourcePath);
|
||||
|
||||
/**
|
||||
* Gets the storedObject specified by <code>uri</code>
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param uri
|
||||
* URI
|
||||
* @return StoredObject
|
||||
*/
|
||||
StoredObject getStoredObject(ITransaction transaction, String uri);
|
||||
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package net.sf.webdav;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.webdav.exceptions.UnauthenticatedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
|
||||
/**
|
||||
* Reference Implementation of WebdavStore
|
||||
*
|
||||
* @author joa
|
||||
* @author re
|
||||
*/
|
||||
public class LocalFileSystemStore implements IWebdavStore {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(LocalFileSystemStore.class);
|
||||
|
||||
private static int BUF_SIZE = 65536;
|
||||
|
||||
private File _root = null;
|
||||
|
||||
public LocalFileSystemStore(File root) {
|
||||
_root = root;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
;
|
||||
}
|
||||
|
||||
public ITransaction begin(Principal principal) throws WebdavException {
|
||||
LOG.info("LocalFileSystemStore.begin()");
|
||||
if (!_root.exists()) {
|
||||
if (!_root.mkdirs()) {
|
||||
throw new WebdavException("root path: "
|
||||
+ _root.getAbsolutePath()
|
||||
+ " does not exist and could not be created");
|
||||
}
|
||||
}
|
||||
// if (principal == null) {
|
||||
// throw new UnauthenticatedException(WebdavStatus.SC_UNAUTHORIZED);
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkAuthentication(ITransaction transaction)
|
||||
throws SecurityException {
|
||||
LOG.info("LocalFileSystemStore.checkAuthentication()");
|
||||
// do nothing
|
||||
// throw new UnauthenticatedException(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
public void commit(ITransaction transaction) throws WebdavException {
|
||||
// do nothing
|
||||
LOG.info("LocalFileSystemStore.commit()");
|
||||
}
|
||||
|
||||
public void rollback(ITransaction transaction) throws WebdavException {
|
||||
// do nothing
|
||||
LOG.info("LocalFileSystemStore.rollback()");
|
||||
|
||||
}
|
||||
|
||||
public void createFolder(ITransaction transaction, String uri)
|
||||
throws WebdavException {
|
||||
LOG.info("LocalFileSystemStore.createFolder(" + uri + ")");
|
||||
File file = new File(_root, uri);
|
||||
if (!file.mkdir())
|
||||
throw new WebdavException("cannot create folder: " + uri);
|
||||
}
|
||||
|
||||
public void createResource(ITransaction transaction, String uri)
|
||||
throws WebdavException {
|
||||
LOG.info("LocalFileSystemStore.createResource(" + uri + ")");
|
||||
File file = new File(_root, uri);
|
||||
try {
|
||||
if (!file.createNewFile())
|
||||
throw new WebdavException("cannot create file: " + uri);
|
||||
} catch (IOException e) {
|
||||
LOG
|
||||
.error("LocalFileSystemStore.createResource(" + uri
|
||||
+ ") failed");
|
||||
throw new WebdavException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public long setResourceContent(ITransaction transaction, String uri,
|
||||
InputStream is, String contentType, String characterEncoding)
|
||||
throws WebdavException {
|
||||
|
||||
LOG.info("LocalFileSystemStore.setResourceContent(" + uri + ")");
|
||||
File file = new File(_root, uri);
|
||||
try {
|
||||
OutputStream os = new BufferedOutputStream(new FileOutputStream(
|
||||
file), BUF_SIZE);
|
||||
try {
|
||||
int read;
|
||||
byte[] copyBuffer = new byte[BUF_SIZE];
|
||||
|
||||
while ((read = is.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||
os.write(copyBuffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} finally {
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("LocalFileSystemStore.setResourceContent(" + uri
|
||||
+ ") failed");
|
||||
throw new WebdavException(e);
|
||||
}
|
||||
long length = -1;
|
||||
|
||||
try {
|
||||
length = file.length();
|
||||
} catch (SecurityException e) {
|
||||
LOG.error("LocalFileSystemStore.setResourceContent(" + uri
|
||||
+ ") failed" + "\nCan't get file.length");
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public String[] getChildrenNames(ITransaction transaction, String uri)
|
||||
throws WebdavException {
|
||||
LOG.info("LocalFileSystemStore.getChildrenNames(" + uri + ")");
|
||||
File file = new File(_root, uri);
|
||||
String[] childrenNames = null;
|
||||
if (file.isDirectory()) {
|
||||
File[] children = file.listFiles();
|
||||
List<String> childList = new ArrayList<String>();
|
||||
String name = null;
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
name = children[i].getName();
|
||||
childList.add(name);
|
||||
LOG.info("Child " + i + ": " + name);
|
||||
}
|
||||
childrenNames = new String[childList.size()];
|
||||
childrenNames = (String[]) childList.toArray(childrenNames);
|
||||
}
|
||||
return childrenNames;
|
||||
}
|
||||
|
||||
public void removeObject(ITransaction transaction, String uri)
|
||||
throws WebdavException {
|
||||
File file = new File(_root, uri);
|
||||
boolean success = file.delete();
|
||||
LOG.info("LocalFileSystemStore.removeObject(" + uri + ")=" + success);
|
||||
if (!success) {
|
||||
throw new WebdavException("cannot delete object: " + uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveObject(ITransaction transaction, String destinationPath, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public InputStream getResourceContent(ITransaction transaction, String uri)
|
||||
throws WebdavException {
|
||||
LOG.info("LocalFileSystemStore.getResourceContent(" + uri + ")");
|
||||
File file = new File(_root, uri);
|
||||
|
||||
InputStream in;
|
||||
try {
|
||||
in = new BufferedInputStream(new FileInputStream(file));
|
||||
} catch (IOException e) {
|
||||
LOG.error("LocalFileSystemStore.getResourceContent(" + uri
|
||||
+ ") failed");
|
||||
throw new WebdavException(e);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
public long getResourceLength(ITransaction transaction, String uri)
|
||||
throws WebdavException {
|
||||
LOG.info("LocalFileSystemStore.getResourceLength(" + uri + ")");
|
||||
File file = new File(_root, uri);
|
||||
return file.length();
|
||||
}
|
||||
|
||||
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
||||
|
||||
StoredObject so = null;
|
||||
|
||||
File file = new File(_root, uri);
|
||||
if (file.exists()) {
|
||||
so = new StoredObject();
|
||||
so.setFolder(file.isDirectory());
|
||||
so.setLastModified(new Date(file.lastModified()));
|
||||
so.setCreationDate(new Date(file.lastModified()));
|
||||
so.setResourceLength(getResourceLength(transaction, uri));
|
||||
}
|
||||
|
||||
return so;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
*
|
||||
* Copyright 2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package net.sf.webdav;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class StoredObject {
|
||||
|
||||
private boolean isFolder;
|
||||
private Date lastModified;
|
||||
private Date creationDate;
|
||||
private long contentLength;
|
||||
private String mimeType;
|
||||
|
||||
private boolean isNullRessource;
|
||||
|
||||
/**
|
||||
* Determines whether the StoredObject is a folder or a resource
|
||||
*
|
||||
* @return true if the StoredObject is a collection
|
||||
*/
|
||||
public boolean isFolder() {
|
||||
return (isFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the StoredObject is a folder or a resource
|
||||
*
|
||||
* @return true if the StoredObject is a resource
|
||||
*/
|
||||
public boolean isResource() {
|
||||
return (!isFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new StoredObject as a collection or resource
|
||||
*
|
||||
* @param f
|
||||
* true - collection ; false - resource
|
||||
*/
|
||||
public void setFolder(boolean f) {
|
||||
this.isFolder = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date of the last modification
|
||||
*
|
||||
* @return last modification Date
|
||||
*/
|
||||
public Date getLastModified() {
|
||||
return (lastModified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the last modification
|
||||
*
|
||||
* @param d
|
||||
* date of the last modification
|
||||
*/
|
||||
public void setLastModified(Date d) {
|
||||
this.lastModified = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date of the creation
|
||||
*
|
||||
* @return creation Date
|
||||
*/
|
||||
public Date getCreationDate() {
|
||||
return (creationDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the creation
|
||||
*
|
||||
* @param d
|
||||
* date of the creation
|
||||
*/
|
||||
public void setCreationDate(Date c) {
|
||||
this.creationDate = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of the resource content
|
||||
*
|
||||
* @return length of the resource content
|
||||
*/
|
||||
public long getResourceLength() {
|
||||
return (contentLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the resource content
|
||||
*
|
||||
* @param l
|
||||
* the length of the resource content
|
||||
*/
|
||||
public void setResourceLength(long l) {
|
||||
this.contentLength = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the resource
|
||||
*
|
||||
* @return true if the resource is in lock-null state
|
||||
*/
|
||||
public boolean isNullResource() {
|
||||
return isNullRessource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a StoredObject as a lock-null resource
|
||||
*
|
||||
* @param f
|
||||
* true to set the resource as lock-null resource
|
||||
*/
|
||||
public void setNullResource(boolean f) {
|
||||
this.isNullRessource = f;
|
||||
this.isFolder = false;
|
||||
this.creationDate = null;
|
||||
this.lastModified = null;
|
||||
// this.content = null;
|
||||
this.contentLength = 0;
|
||||
this.mimeType= null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the myme type from the store object.
|
||||
* Can also return NULL if the store does not handle
|
||||
* mime type stuff.
|
||||
* In that case the mime type is determined by the servletcontext
|
||||
*
|
||||
* @return the mimeType
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mime type of this object
|
||||
*
|
||||
* @param mimeType the mimeType to set
|
||||
*/
|
||||
public void setMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
package net.sf.webdav;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Principal;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.exceptions.UnauthenticatedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.fromcatalina.MD5Encoder;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
import net.sf.webdav.methods.DoCopy;
|
||||
import net.sf.webdav.methods.DoDelete;
|
||||
import net.sf.webdav.methods.DoGet;
|
||||
import net.sf.webdav.methods.DoHead;
|
||||
import net.sf.webdav.methods.DoLock;
|
||||
import net.sf.webdav.methods.DoMkcol;
|
||||
import net.sf.webdav.methods.DoMove;
|
||||
import net.sf.webdav.methods.DoNotImplemented;
|
||||
import net.sf.webdav.methods.DoOptions;
|
||||
import net.sf.webdav.methods.DoPropfind;
|
||||
import net.sf.webdav.methods.DoProppatch;
|
||||
import net.sf.webdav.methods.DoPut;
|
||||
import net.sf.webdav.methods.DoUnlock;
|
||||
|
||||
public class WebDavServletBean extends HttpServlet {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(WebDavServletBean.class);
|
||||
|
||||
/**
|
||||
* MD5 message digest provider.
|
||||
*/
|
||||
protected static MessageDigest MD5_HELPER;
|
||||
|
||||
/**
|
||||
* The MD5 helper object for this class.
|
||||
*/
|
||||
protected static final MD5Encoder MD5_ENCODER = new MD5Encoder();
|
||||
|
||||
private static final boolean READ_ONLY = false;
|
||||
protected ResourceLocks _resLocks;
|
||||
protected IWebdavStore _store;
|
||||
private HashMap<String, IMethodExecutor> _methodMap = new HashMap<String, IMethodExecutor>();
|
||||
|
||||
public WebDavServletBean() {
|
||||
_resLocks = new ResourceLocks();
|
||||
|
||||
try {
|
||||
MD5_HELPER = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public void init(IWebdavStore store, String dftIndexFile,
|
||||
String insteadOf404, int nocontentLenghHeaders,
|
||||
boolean lazyFolderCreationOnPut) throws ServletException {
|
||||
|
||||
_store = store;
|
||||
|
||||
IMimeTyper mimeTyper = new IMimeTyper() {
|
||||
public String getMimeType(ITransaction transaction, String path) {
|
||||
String retVal= _store.getStoredObject(transaction, path).getMimeType();
|
||||
if ( retVal== null) {
|
||||
retVal= getServletContext().getMimeType( path);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
};
|
||||
|
||||
register("GET", new DoGet(store, dftIndexFile, insteadOf404, _resLocks,
|
||||
mimeTyper, nocontentLenghHeaders));
|
||||
register("HEAD", new DoHead(store, dftIndexFile, insteadOf404,
|
||||
_resLocks, mimeTyper, nocontentLenghHeaders));
|
||||
DoDelete doDelete = (DoDelete) register("DELETE", new DoDelete(store,
|
||||
_resLocks, READ_ONLY));
|
||||
DoCopy doCopy = (DoCopy) register("COPY", new DoCopy(store, _resLocks,
|
||||
doDelete, READ_ONLY));
|
||||
register("LOCK", new DoLock(store, _resLocks, READ_ONLY));
|
||||
register("UNLOCK", new DoUnlock(store, _resLocks, READ_ONLY));
|
||||
register("MOVE", new DoMove(store, _resLocks, doDelete, doCopy, READ_ONLY));
|
||||
register("MKCOL", new DoMkcol(store, _resLocks, READ_ONLY));
|
||||
register("OPTIONS", new DoOptions(store, _resLocks));
|
||||
register("PUT", new DoPut(store, _resLocks, READ_ONLY,
|
||||
lazyFolderCreationOnPut));
|
||||
register("PROPFIND", new DoPropfind(store, _resLocks, mimeTyper));
|
||||
register("PROPPATCH", new DoProppatch(store, _resLocks, READ_ONLY));
|
||||
register("*NO*IMPL*", new DoNotImplemented(READ_ONLY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if(_store != null)
|
||||
_store.destroy();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
protected IMethodExecutor register(String methodName, IMethodExecutor method) {
|
||||
_methodMap.put(methodName, method);
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the special WebDAV methods.
|
||||
*/
|
||||
@Override
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
|
||||
String methodName = req.getMethod();
|
||||
ITransaction transaction = null;
|
||||
boolean needRollback = false;
|
||||
|
||||
if (LOG.isTraceEnabled())
|
||||
debugRequest(methodName, req);
|
||||
|
||||
try {
|
||||
Principal userPrincipal = getUserPrincipal(req);
|
||||
transaction = _store.begin(userPrincipal);
|
||||
needRollback = true;
|
||||
_store.checkAuthentication(transaction);
|
||||
resp.setStatus(WebdavStatus.SC_OK);
|
||||
|
||||
try {
|
||||
IMethodExecutor methodExecutor = (IMethodExecutor) _methodMap
|
||||
.get(methodName);
|
||||
if (methodExecutor == null) {
|
||||
methodExecutor = (IMethodExecutor) _methodMap
|
||||
.get("*NO*IMPL*");
|
||||
}
|
||||
|
||||
methodExecutor.execute(transaction, req, resp);
|
||||
|
||||
_store.commit(transaction);
|
||||
/** Clear not consumed data
|
||||
*
|
||||
* Clear input stream if available otherwise later access
|
||||
* include current input. These cases occure if the client
|
||||
* sends a request with body to an not existing resource.
|
||||
*/
|
||||
if (req.getContentLength() != 0 && req.getInputStream().available() > 0) {
|
||||
if (LOG.isTraceEnabled()) { LOG.trace("Clear not consumed data!"); }
|
||||
while (req.getInputStream().available() > 0) {
|
||||
req.getInputStream().read();
|
||||
}
|
||||
}
|
||||
needRollback = false;
|
||||
} catch (IOException e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
LOG.error("IOException: " + sw.toString());
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
_store.rollback(transaction);
|
||||
throw new ServletException(e);
|
||||
}
|
||||
|
||||
} catch (UnauthenticatedException e) {
|
||||
resp.sendError(e.getCode());
|
||||
} catch (WebdavException e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
LOG.error("WebdavException: " + sw.toString());
|
||||
throw new ServletException(e);
|
||||
} catch (Exception e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
LOG.error("Exception: " + sw.toString());
|
||||
} finally {
|
||||
if (needRollback)
|
||||
_store.rollback(transaction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that permit to customize the way
|
||||
* user information are extracted from the request, default use JAAS
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
protected Principal getUserPrincipal(HttpServletRequest req) {
|
||||
return req.getUserPrincipal();
|
||||
}
|
||||
|
||||
private void debugRequest(String methodName, HttpServletRequest req) {
|
||||
LOG.trace("-----------");
|
||||
LOG.trace("WebdavServlet\n request: methodName = " + methodName);
|
||||
LOG.trace("time: " + System.currentTimeMillis());
|
||||
LOG.trace("path: " + req.getRequestURI());
|
||||
LOG.trace("-----------");
|
||||
Enumeration<?> e = req.getHeaderNames();
|
||||
while (e.hasMoreElements()) {
|
||||
String s = (String) e.nextElement();
|
||||
LOG.trace("header: " + s + " " + req.getHeader(s));
|
||||
}
|
||||
e = req.getAttributeNames();
|
||||
while (e.hasMoreElements()) {
|
||||
String s = (String) e.nextElement();
|
||||
LOG.trace("attribute: " + s + " " + req.getAttribute(s));
|
||||
}
|
||||
e = req.getParameterNames();
|
||||
while (e.hasMoreElements()) {
|
||||
String s = (String) e.nextElement();
|
||||
LOG.trace("parameter: " + s + " " + req.getParameter(s));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
|
||||
/**
|
||||
* Servlet which provides support for WebDAV level 2.
|
||||
*
|
||||
* the original class is org.apache.catalina.servlets.WebdavServlet by Remy
|
||||
* Maucherat, which was heavily changed
|
||||
*
|
||||
* @author Remy Maucherat
|
||||
*/
|
||||
|
||||
public class WebdavServlet extends WebDavServletBean {
|
||||
|
||||
private static final String ROOTPATH_PARAMETER = "rootpath";
|
||||
|
||||
public void init() throws ServletException {
|
||||
|
||||
// Parameters from web.xml
|
||||
String clazzName = getServletConfig().getInitParameter(
|
||||
"ResourceHandlerImplementation");
|
||||
if (clazzName == null || clazzName.equals("")) {
|
||||
clazzName = LocalFileSystemStore.class.getName();
|
||||
}
|
||||
|
||||
File root = getFileRoot();
|
||||
|
||||
IWebdavStore webdavStore = constructStore(clazzName, root);
|
||||
|
||||
boolean lazyFolderCreationOnPut = getInitParameter("lazyFolderCreationOnPut") != null
|
||||
&& getInitParameter("lazyFolderCreationOnPut").equals("1");
|
||||
|
||||
String dftIndexFile = getInitParameter("default-index-file");
|
||||
String insteadOf404 = getInitParameter("instead-of-404");
|
||||
|
||||
int noContentLengthHeader = getIntInitParameter("no-content-length-headers");
|
||||
|
||||
super.init(webdavStore, dftIndexFile, insteadOf404,
|
||||
noContentLengthHeader, lazyFolderCreationOnPut);
|
||||
}
|
||||
|
||||
private int getIntInitParameter(String key) {
|
||||
return getInitParameter(key) == null ? -1 : Integer
|
||||
.parseInt(getInitParameter(key));
|
||||
}
|
||||
|
||||
protected IWebdavStore constructStore(String clazzName, File root) {
|
||||
IWebdavStore webdavStore;
|
||||
try {
|
||||
Class<?> clazz = WebdavServlet.class.getClassLoader().loadClass(
|
||||
clazzName);
|
||||
|
||||
Constructor<?> ctor = clazz
|
||||
.getConstructor(new Class[] { File.class });
|
||||
|
||||
webdavStore = (IWebdavStore) ctor
|
||||
.newInstance(new Object[] { root });
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("some problem making store component", e);
|
||||
}
|
||||
return webdavStore;
|
||||
}
|
||||
|
||||
private File getFileRoot() {
|
||||
String rootPath = getInitParameter(ROOTPATH_PARAMETER);
|
||||
if (rootPath == null) {
|
||||
throw new WebdavException("missing parameter: "
|
||||
+ ROOTPATH_PARAMETER);
|
||||
}
|
||||
if (rootPath.equals("*WAR-FILE-ROOT*")) {
|
||||
String file = LocalFileSystemStore.class.getProtectionDomain()
|
||||
.getCodeSource().getLocation().getFile().replace('\\', '/');
|
||||
if (file.charAt(0) == '/'
|
||||
&& System.getProperty("os.name").indexOf("Windows") != -1) {
|
||||
file = file.substring(1, file.length());
|
||||
}
|
||||
|
||||
int ix = file.indexOf("/WEB-INF/");
|
||||
if (ix != -1) {
|
||||
rootPath = file.substring(0, ix).replace('/',
|
||||
File.separatorChar);
|
||||
} else {
|
||||
throw new WebdavException(
|
||||
"Could not determine root of war file. Can't extract from path '"
|
||||
+ file + "' for this web container");
|
||||
}
|
||||
}
|
||||
return new File(rootPath);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
package net.sf.webdav;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Wraps the HttpServletResponse class to abstract the specific protocol used.
|
||||
* To support other protocols we would only need to modify this class and the
|
||||
* WebDavRetCode classes.
|
||||
*
|
||||
* @author Marc Eaddy
|
||||
* @version 1.0, 16 Nov 1997
|
||||
*/
|
||||
public class WebdavStatus {
|
||||
|
||||
// ----------------------------------------------------- Instance Variables
|
||||
|
||||
/**
|
||||
* This Hashtable contains the mapping of HTTP and WebDAV status codes to
|
||||
* descriptive text. This is a static variable.
|
||||
*/
|
||||
private static Hashtable<Integer, String> _mapStatusCodes = new Hashtable<Integer, String>();
|
||||
|
||||
// ------------------------------------------------------ HTTP Status Codes
|
||||
|
||||
/**
|
||||
* Status code (200) indicating the request succeeded normally.
|
||||
*/
|
||||
public static final int SC_OK = HttpServletResponse.SC_OK;
|
||||
|
||||
/**
|
||||
* Status code (201) indicating the request succeeded and created a new
|
||||
* resource on the server.
|
||||
*/
|
||||
public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
|
||||
|
||||
/**
|
||||
* Status code (202) indicating that a request was accepted for processing,
|
||||
* but was not completed.
|
||||
*/
|
||||
public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
|
||||
|
||||
/**
|
||||
* Status code (204) indicating that the request succeeded but that there
|
||||
* was no new information to return.
|
||||
*/
|
||||
public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
|
||||
|
||||
/**
|
||||
* Status code (301) indicating that the resource has permanently moved to a
|
||||
* new location, and that future references should use a new URI with their
|
||||
* requests.
|
||||
*/
|
||||
public static final int SC_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY;
|
||||
|
||||
/**
|
||||
* Status code (302) indicating that the resource has temporarily moved to
|
||||
* another location, but that future references should still use the
|
||||
* original URI to access the resource.
|
||||
*/
|
||||
public static final int SC_MOVED_TEMPORARILY = HttpServletResponse.SC_MOVED_TEMPORARILY;
|
||||
|
||||
/**
|
||||
* Status code (304) indicating that a conditional GET operation found that
|
||||
* the resource was available and not modified.
|
||||
*/
|
||||
public static final int SC_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED;
|
||||
|
||||
/**
|
||||
* Status code (400) indicating the request sent by the client was
|
||||
* syntactically incorrect.
|
||||
*/
|
||||
public static final int SC_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* Status code (401) indicating that the request requires HTTP
|
||||
* authentication.
|
||||
*/
|
||||
public static final int SC_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED;
|
||||
|
||||
/**
|
||||
* Status code (403) indicating the server understood the request but
|
||||
* refused to fulfill it.
|
||||
*/
|
||||
public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
|
||||
|
||||
/**
|
||||
* Status code (404) indicating that the requested resource is not
|
||||
* available.
|
||||
*/
|
||||
public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* Status code (500) indicating an error inside the HTTP service which
|
||||
* prevented it from fulfilling the request.
|
||||
*/
|
||||
public static final int SC_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
|
||||
/**
|
||||
* Status code (501) indicating the HTTP service does not support the
|
||||
* functionality needed to fulfill the request.
|
||||
*/
|
||||
public static final int SC_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED;
|
||||
|
||||
/**
|
||||
* Status code (502) indicating that the HTTP server received an invalid
|
||||
* response from a server it consulted when acting as a proxy or gateway.
|
||||
*/
|
||||
public static final int SC_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY;
|
||||
|
||||
/**
|
||||
* Status code (503) indicating that the HTTP service is temporarily
|
||||
* overloaded, and unable to handle the request.
|
||||
*/
|
||||
public static final int SC_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Status code (100) indicating the client may continue with its request.
|
||||
* This interim response is used to inform the client that the initial part
|
||||
* of the request has been received and has not yet been rejected by the
|
||||
* server.
|
||||
*/
|
||||
public static final int SC_CONTINUE = 100;
|
||||
|
||||
/**
|
||||
* Status code (405) indicating the method specified is not allowed for the
|
||||
* resource.
|
||||
*/
|
||||
public static final int SC_METHOD_NOT_ALLOWED = 405;
|
||||
|
||||
/**
|
||||
* Status code (409) indicating that the request could not be completed due
|
||||
* to a conflict with the current state of the resource.
|
||||
*/
|
||||
public static final int SC_CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* Status code (412) indicating the precondition given in one or more of the
|
||||
* request-header fields evaluated to false when it was tested on the
|
||||
* server.
|
||||
*/
|
||||
public static final int SC_PRECONDITION_FAILED = 412;
|
||||
|
||||
/**
|
||||
* Status code (413) indicating the server is refusing to process a request
|
||||
* because the request entity is larger than the server is willing or able
|
||||
* to process.
|
||||
*/
|
||||
public static final int SC_REQUEST_TOO_LONG = 413;
|
||||
|
||||
/**
|
||||
* Status code (415) indicating the server is refusing to service the
|
||||
* request because the entity of the request is in a format not supported by
|
||||
* the requested resource for the requested method.
|
||||
*/
|
||||
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
|
||||
// -------------------------------------------- Extended WebDav status code
|
||||
|
||||
/**
|
||||
* Status code (207) indicating that the response requires providing status
|
||||
* for multiple independent operations.
|
||||
*/
|
||||
public static final int SC_MULTI_STATUS = 207;
|
||||
|
||||
// This one colides with HTTP 1.1
|
||||
// "207 Parital Update OK"
|
||||
|
||||
/**
|
||||
* Status code (418) indicating the entity body submitted with the PATCH
|
||||
* method was not understood by the resource.
|
||||
*/
|
||||
public static final int SC_UNPROCESSABLE_ENTITY = 418;
|
||||
|
||||
// This one colides with HTTP 1.1
|
||||
// "418 Reauthentication Required"
|
||||
|
||||
/**
|
||||
* Status code (419) indicating that the resource does not have sufficient
|
||||
* space to record the state of the resource after the execution of this
|
||||
* method.
|
||||
*/
|
||||
public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
|
||||
|
||||
// This one colides with HTTP 1.1
|
||||
// "419 Proxy Reauthentication Required"
|
||||
|
||||
/**
|
||||
* Status code (420) indicating the method was not executed on a particular
|
||||
* resource within its scope because some part of the method's execution
|
||||
* failed causing the entire method to be aborted.
|
||||
*/
|
||||
public static final int SC_METHOD_FAILURE = 420;
|
||||
|
||||
/**
|
||||
* Status code (423) indicating the destination resource of a method is
|
||||
* locked, and either the request did not contain a valid Lock-Info header,
|
||||
* or the Lock-Info header identifies a lock held by another principal.
|
||||
*/
|
||||
public static final int SC_LOCKED = 423;
|
||||
|
||||
// ------------------------------------------------------------ Initializer
|
||||
|
||||
static {
|
||||
// HTTP 1.0 Status Code
|
||||
addStatusCodeMap(SC_OK, "OK");
|
||||
addStatusCodeMap(SC_CREATED, "Created");
|
||||
addStatusCodeMap(SC_ACCEPTED, "Accepted");
|
||||
addStatusCodeMap(SC_NO_CONTENT, "No Content");
|
||||
addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
|
||||
addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
|
||||
addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
|
||||
addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
|
||||
addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
|
||||
addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
|
||||
addStatusCodeMap(SC_NOT_FOUND, "Not Found");
|
||||
addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
|
||||
addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
|
||||
addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
|
||||
addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
|
||||
addStatusCodeMap(SC_CONTINUE, "Continue");
|
||||
addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
|
||||
addStatusCodeMap(SC_CONFLICT, "Conflict");
|
||||
addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
|
||||
addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
|
||||
addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
|
||||
// WebDav Status Codes
|
||||
addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
|
||||
addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
|
||||
addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
|
||||
"Insufficient Space On Resource");
|
||||
addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
|
||||
addStatusCodeMap(SC_LOCKED, "Locked");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- Public Methods
|
||||
|
||||
/**
|
||||
* Returns the HTTP status text for the HTTP or WebDav status code specified
|
||||
* by looking it up in the static mapping. This is a static function.
|
||||
*
|
||||
* @param nHttpStatusCode
|
||||
* [IN] HTTP or WebDAV status code
|
||||
* @return A string with a short descriptive phrase for the HTTP status code
|
||||
* (e.g., "OK").
|
||||
*/
|
||||
public static String getStatusText(int nHttpStatusCode) {
|
||||
Integer intKey = new Integer(nHttpStatusCode);
|
||||
|
||||
if (!_mapStatusCodes.containsKey(intKey)) {
|
||||
return "";
|
||||
} else {
|
||||
return (String) _mapStatusCodes.get(intKey);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- Private Methods
|
||||
|
||||
/**
|
||||
* Adds a new status code -> status text mapping. This is a static method
|
||||
* because the mapping is a static variable.
|
||||
*
|
||||
* @param nKey
|
||||
* [IN] HTTP or WebDAV status code
|
||||
* @param strVal
|
||||
* [IN] HTTP status text
|
||||
*/
|
||||
private static void addStatusCodeMap(int nKey, String strVal) {
|
||||
_mapStatusCodes.put(new Integer(nKey), strVal);
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.exceptions;
|
||||
|
||||
public class AccessDeniedException extends WebdavException {
|
||||
|
||||
public AccessDeniedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public AccessDeniedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AccessDeniedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AccessDeniedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package net.sf.webdav.exceptions;
|
||||
|
||||
public class LockFailedException extends WebdavException {
|
||||
|
||||
public LockFailedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LockFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LockFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LockFailedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.exceptions;
|
||||
|
||||
public class ObjectAlreadyExistsException extends WebdavException {
|
||||
|
||||
public ObjectAlreadyExistsException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ObjectAlreadyExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ObjectAlreadyExistsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ObjectAlreadyExistsException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.exceptions;
|
||||
|
||||
public class ObjectNotFoundException extends WebdavException {
|
||||
|
||||
public ObjectNotFoundException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ObjectNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ObjectNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ObjectNotFoundException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.exceptions;
|
||||
|
||||
public class UnauthenticatedException extends WebdavException {
|
||||
private final int code;
|
||||
|
||||
public UnauthenticatedException(int code) {
|
||||
super();
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.exceptions;
|
||||
|
||||
public class WebdavException extends RuntimeException {
|
||||
|
||||
public WebdavException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public WebdavException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WebdavException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public WebdavException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.fromcatalina;
|
||||
|
||||
/**
|
||||
* Encode an MD5 digest into a String.
|
||||
* <p>
|
||||
* The 128 bit MD5 hash is converted into a 32 character long String. Each
|
||||
* character of the String is the hexadecimal representation of 4 bits of the
|
||||
* digest.
|
||||
*
|
||||
* @author Remy Maucherat
|
||||
* @version $Revision: 1.2 $ $Date: 2008-08-05 07:38:45 $
|
||||
*/
|
||||
|
||||
public final class MD5Encoder {
|
||||
|
||||
// ----------------------------------------------------- Instance Variables
|
||||
|
||||
private static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
// --------------------------------------------------------- Public Methods
|
||||
|
||||
/**
|
||||
* Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
|
||||
*
|
||||
* @param binaryData
|
||||
* Array containing the digest
|
||||
* @return Encoded MD5, or null if encoding failed
|
||||
*/
|
||||
public String encode(byte[] binaryData) {
|
||||
|
||||
if (binaryData.length != 16)
|
||||
return null;
|
||||
|
||||
char[] buffer = new char[32];
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int low = (int) (binaryData[i] & 0x0f);
|
||||
int high = (int) ((binaryData[i] & 0xf0) >> 4);
|
||||
buffer[i * 2] = HEXADECIMAL[high];
|
||||
buffer[i * 2 + 1] = HEXADECIMAL[low];
|
||||
}
|
||||
|
||||
return new String(buffer);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.fromcatalina;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
/**
|
||||
* General purpose request parsing and encoding utility methods.
|
||||
*
|
||||
* @author Craig R. McClanahan
|
||||
* @author Tim Tye
|
||||
* @version $Revision: 1.2 $ $Date: 2008-08-05 07:38:45 $
|
||||
*/
|
||||
|
||||
public final class RequestUtil {
|
||||
|
||||
/**
|
||||
* Encode a cookie as per RFC 2109. The resulting string can be used as the
|
||||
* value for a <code>Set-Cookie</code> header.
|
||||
*
|
||||
* @param cookie
|
||||
* The cookie to encode.
|
||||
* @return A string following RFC 2109.
|
||||
*/
|
||||
public static String encodeCookie(Cookie cookie) {
|
||||
|
||||
StringBuffer buf = new StringBuffer(cookie.getName());
|
||||
buf.append("=");
|
||||
buf.append(cookie.getValue());
|
||||
|
||||
String comment = cookie.getComment();
|
||||
if (comment != null) {
|
||||
buf.append("; Comment=\"");
|
||||
buf.append(comment);
|
||||
buf.append("\"");
|
||||
}
|
||||
|
||||
String domain = cookie.getDomain();
|
||||
if (domain != null) {
|
||||
buf.append("; Domain=\"");
|
||||
buf.append(domain);
|
||||
buf.append("\"");
|
||||
}
|
||||
|
||||
int age = cookie.getMaxAge();
|
||||
if (age >= 0) {
|
||||
buf.append("; Max-Age=\"");
|
||||
buf.append(age);
|
||||
buf.append("\"");
|
||||
}
|
||||
|
||||
String path = cookie.getPath();
|
||||
if (path != null) {
|
||||
buf.append("; Path=\"");
|
||||
buf.append(path);
|
||||
buf.append("\"");
|
||||
}
|
||||
|
||||
if (cookie.getSecure()) {
|
||||
buf.append("; Secure");
|
||||
}
|
||||
|
||||
int version = cookie.getVersion();
|
||||
if (version > 0) {
|
||||
buf.append("; Version=\"");
|
||||
buf.append(version);
|
||||
buf.append("\"");
|
||||
}
|
||||
|
||||
return (buf.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the specified message string for characters that are sensitive in
|
||||
* HTML. This avoids potential attacks caused by including JavaScript codes
|
||||
* in the request URL that is often reported in error messages.
|
||||
*
|
||||
* @param message
|
||||
* The message string to be filtered
|
||||
*/
|
||||
public static String filter(String message) {
|
||||
|
||||
if (message == null)
|
||||
return (null);
|
||||
|
||||
char content[] = new char[message.length()];
|
||||
message.getChars(0, message.length(), content, 0);
|
||||
StringBuffer result = new StringBuffer(content.length + 50);
|
||||
for (int i = 0; i < content.length; i++) {
|
||||
switch (content[i]) {
|
||||
case '<':
|
||||
result.append("<");
|
||||
break;
|
||||
case '>':
|
||||
result.append(">");
|
||||
break;
|
||||
case '&':
|
||||
result.append("&");
|
||||
break;
|
||||
case '"':
|
||||
result.append(""");
|
||||
break;
|
||||
default:
|
||||
result.append(content[i]);
|
||||
}
|
||||
}
|
||||
return (result.toString());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a relative URI path that may have relative values ("/./",
|
||||
* "/../", and so on ) it it. <strong>WARNING</strong> - This method is
|
||||
* useful only for normalizing application-generated paths. It does not try
|
||||
* to perform security checks for malicious input.
|
||||
*
|
||||
* @param path
|
||||
* Relative path to be normalized
|
||||
*/
|
||||
public static String normalize(String path) {
|
||||
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
// Create a place for the normalized path
|
||||
String normalized = path;
|
||||
|
||||
if (normalized.equals("/."))
|
||||
return "/";
|
||||
|
||||
// Add a leading "/" if necessary
|
||||
if (!normalized.startsWith("/"))
|
||||
normalized = "/" + normalized;
|
||||
|
||||
// Resolve occurrences of "//" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("//");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index)
|
||||
+ normalized.substring(index + 1);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/./" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/./");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index)
|
||||
+ normalized.substring(index + 2);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/../" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/../");
|
||||
if (index < 0)
|
||||
break;
|
||||
if (index == 0)
|
||||
return (null); // Trying to go outside our context
|
||||
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||
normalized = normalized.substring(0, index2)
|
||||
+ normalized.substring(index + 3);
|
||||
}
|
||||
|
||||
// Return the normalized path that we have completed
|
||||
return (normalized);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the character encoding from the specified content type header. If
|
||||
* the content type is null, or there is no explicit character encoding,
|
||||
* <code>null</code> is returned.
|
||||
*
|
||||
* @param contentType
|
||||
* a content type header
|
||||
*/
|
||||
public static String parseCharacterEncoding(String contentType) {
|
||||
|
||||
if (contentType == null)
|
||||
return (null);
|
||||
int start = contentType.indexOf("charset=");
|
||||
if (start < 0)
|
||||
return (null);
|
||||
String encoding = contentType.substring(start + 8);
|
||||
int end = encoding.indexOf(';');
|
||||
if (end >= 0)
|
||||
encoding = encoding.substring(0, end);
|
||||
encoding = encoding.trim();
|
||||
if ((encoding.length() > 2) && (encoding.startsWith("\""))
|
||||
&& (encoding.endsWith("\"")))
|
||||
encoding = encoding.substring(1, encoding.length() - 1);
|
||||
return (encoding.trim());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a cookie header into an array of cookies according to RFC 2109.
|
||||
*
|
||||
* @param header
|
||||
* Value of an HTTP "Cookie" header
|
||||
*/
|
||||
public static Cookie[] parseCookieHeader(String header) {
|
||||
|
||||
if ((header == null) || (header.length() < 1))
|
||||
return (new Cookie[0]);
|
||||
|
||||
ArrayList<Cookie> cookies = new ArrayList<Cookie>();
|
||||
while (header.length() > 0) {
|
||||
int semicolon = header.indexOf(';');
|
||||
if (semicolon < 0)
|
||||
semicolon = header.length();
|
||||
if (semicolon == 0)
|
||||
break;
|
||||
String token = header.substring(0, semicolon);
|
||||
if (semicolon < header.length())
|
||||
header = header.substring(semicolon + 1);
|
||||
else
|
||||
header = "";
|
||||
try {
|
||||
int equals = token.indexOf('=');
|
||||
if (equals > 0) {
|
||||
String name = token.substring(0, equals).trim();
|
||||
String value = token.substring(equals + 1).trim();
|
||||
cookies.add(new Cookie(name, value));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Append request parameters from the specified String to the specified Map.
|
||||
* It is presumed that the specified Map is not accessed from any other
|
||||
* thread, so no synchronization is performed.
|
||||
* <p>
|
||||
* <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
|
||||
* individually on the parsed name and value elements, rather than on the
|
||||
* entire query string ahead of time, to properly deal with the case where
|
||||
* the name or value includes an encoded "=" or "&" character that would
|
||||
* otherwise be interpreted as a delimiter.
|
||||
*
|
||||
* @param map
|
||||
* Map that accumulates the resulting parameters
|
||||
* @param data
|
||||
* Input string containing request parameters
|
||||
*
|
||||
* @exception IllegalArgumentException
|
||||
* if the data is malformed
|
||||
*/
|
||||
public static void parseParameters(Map<String, String[]> map, String data,
|
||||
String encoding) throws UnsupportedEncodingException {
|
||||
|
||||
if ((data != null) && (data.length() > 0)) {
|
||||
|
||||
// use the specified encoding to extract bytes out of the
|
||||
// given string so that the encoding is not lost. If an
|
||||
// encoding is not specified, let it use platform default
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
if (encoding == null) {
|
||||
bytes = data.getBytes();
|
||||
} else {
|
||||
bytes = data.getBytes(encoding);
|
||||
}
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
}
|
||||
|
||||
parseParameters(map, bytes, encoding);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded String. When the byte array
|
||||
* is converted to a string, the system default character encoding is
|
||||
* used... This may be different than some other servers.
|
||||
*
|
||||
* @param str
|
||||
* The url-encoded string
|
||||
*
|
||||
* @exception IllegalArgumentException
|
||||
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||
* number
|
||||
*/
|
||||
public static String URLDecode(String str) {
|
||||
|
||||
return URLDecode(str, null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded String.
|
||||
*
|
||||
* @param str
|
||||
* The url-encoded string
|
||||
* @param enc
|
||||
* The encoding to use; if null, the default encoding is used
|
||||
* @exception IllegalArgumentException
|
||||
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||
* number
|
||||
*/
|
||||
public static String URLDecode(String str, String enc) {
|
||||
|
||||
if (str == null)
|
||||
return (null);
|
||||
|
||||
// use the specified encoding to extract bytes out of the
|
||||
// given string so that the encoding is not lost. If an
|
||||
// encoding is not specified, let it use platform default
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
if (enc == null) {
|
||||
bytes = str.getBytes();
|
||||
} else {
|
||||
bytes = str.getBytes(enc);
|
||||
}
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
}
|
||||
|
||||
return URLDecode(bytes, enc);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded byte array.
|
||||
*
|
||||
* @param bytes
|
||||
* The url-encoded byte array
|
||||
* @exception IllegalArgumentException
|
||||
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||
* number
|
||||
*/
|
||||
public static String URLDecode(byte[] bytes) {
|
||||
return URLDecode(bytes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded byte array.
|
||||
*
|
||||
* @param bytes
|
||||
* The url-encoded byte array
|
||||
* @param enc
|
||||
* The encoding to use; if null, the default encoding is used
|
||||
* @exception IllegalArgumentException
|
||||
* if a '%' character is not followed by a valid 2-digit hexadecimal
|
||||
* number
|
||||
*/
|
||||
public static String URLDecode(byte[] bytes, String enc) {
|
||||
|
||||
if (bytes == null)
|
||||
return (null);
|
||||
|
||||
int len = bytes.length;
|
||||
int ix = 0;
|
||||
int ox = 0;
|
||||
while (ix < len) {
|
||||
byte b = bytes[ix++]; // Get byte to test
|
||||
if (b == '+') {
|
||||
b = (byte) ' ';
|
||||
} else if (b == '%') {
|
||||
b = (byte) ((convertHexDigit(bytes[ix++]) << 4) + convertHexDigit(bytes[ix++]));
|
||||
}
|
||||
bytes[ox++] = b;
|
||||
}
|
||||
if (enc != null) {
|
||||
try {
|
||||
return new String(bytes, 0, ox, enc);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return new String(bytes, 0, ox);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte character value to hexidecimal digit value.
|
||||
*
|
||||
* @param b
|
||||
* the character value byte
|
||||
*/
|
||||
private static byte convertHexDigit(byte b) {
|
||||
if ((b >= '0') && (b <= '9'))
|
||||
return (byte) (b - '0');
|
||||
if ((b >= 'a') && (b <= 'f'))
|
||||
return (byte) (b - 'a' + 10);
|
||||
if ((b >= 'A') && (b <= 'F'))
|
||||
return (byte) (b - 'A' + 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put name and value pair in map. When name already exist, add value to
|
||||
* array of values.
|
||||
*
|
||||
* @param map
|
||||
* The map to populate
|
||||
* @param name
|
||||
* The parameter name
|
||||
* @param value
|
||||
* The parameter value
|
||||
*/
|
||||
private static void putMapEntry(Map<String, String[]> map, String name,
|
||||
String value) {
|
||||
String[] newValues = null;
|
||||
String[] oldValues = (String[]) map.get(name);
|
||||
if (oldValues == null) {
|
||||
newValues = new String[1];
|
||||
newValues[0] = value;
|
||||
} else {
|
||||
newValues = new String[oldValues.length + 1];
|
||||
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
|
||||
newValues[oldValues.length] = value;
|
||||
}
|
||||
map.put(name, newValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append request parameters from the specified String to the specified Map.
|
||||
* It is presumed that the specified Map is not accessed from any other
|
||||
* thread, so no synchronization is performed.
|
||||
* <p>
|
||||
* <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
|
||||
* individually on the parsed name and value elements, rather than on the
|
||||
* entire query string ahead of time, to properly deal with the case where
|
||||
* the name or value includes an encoded "=" or "&" character that would
|
||||
* otherwise be interpreted as a delimiter. NOTE: byte array data is
|
||||
* modified by this method. Caller beware.
|
||||
*
|
||||
* @param map
|
||||
* Map that accumulates the resulting parameters
|
||||
* @param data
|
||||
* Input string containing request parameters
|
||||
* @param encoding
|
||||
* Encoding to use for converting hex
|
||||
*
|
||||
* @exception UnsupportedEncodingException
|
||||
* if the data is malformed
|
||||
*/
|
||||
public static void parseParameters(Map<String, String[]> map, byte[] data,
|
||||
String encoding) throws UnsupportedEncodingException {
|
||||
|
||||
if (data != null && data.length > 0) {
|
||||
int ix = 0;
|
||||
int ox = 0;
|
||||
String key = null;
|
||||
String value = null;
|
||||
while (ix < data.length) {
|
||||
byte c = data[ix++];
|
||||
switch ((char) c) {
|
||||
case '&':
|
||||
value = new String(data, 0, ox, encoding);
|
||||
if (key != null) {
|
||||
putMapEntry(map, key, value);
|
||||
key = null;
|
||||
}
|
||||
ox = 0;
|
||||
break;
|
||||
case '=':
|
||||
if (key == null) {
|
||||
key = new String(data, 0, ox, encoding);
|
||||
ox = 0;
|
||||
} else {
|
||||
data[ox++] = c;
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
data[ox++] = (byte) ' ';
|
||||
break;
|
||||
case '%':
|
||||
data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++]));
|
||||
break;
|
||||
default:
|
||||
data[ox++] = c;
|
||||
}
|
||||
}
|
||||
// The last value does not end in '&'. So save it now.
|
||||
if (key != null) {
|
||||
value = new String(data, 0, ox, encoding);
|
||||
putMapEntry(map, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.fromcatalina;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* This class is very similar to the java.net.URLEncoder class.
|
||||
*
|
||||
* Unfortunately, with java.net.URLEncoder there is no way to specify to the
|
||||
* java.net.URLEncoder which characters should NOT be encoded.
|
||||
*
|
||||
* This code was moved from DefaultServlet.java
|
||||
*
|
||||
* @author Craig R. McClanahan
|
||||
* @author Remy Maucherat
|
||||
*/
|
||||
public class URLEncoder
|
||||
{
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(URLEncoder.class);
|
||||
|
||||
|
||||
protected static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
// Array containing the safe characters set.
|
||||
protected BitSet _safeCharacters = new BitSet(256);
|
||||
|
||||
public URLEncoder() {
|
||||
for (char i = 'a'; i <= 'z'; i++) {
|
||||
addSafeCharacter(i);
|
||||
}
|
||||
for (char i = 'A'; i <= 'Z'; i++) {
|
||||
addSafeCharacter(i);
|
||||
}
|
||||
for (char i = '0'; i <= '9'; i++) {
|
||||
addSafeCharacter(i);
|
||||
}
|
||||
for(char c : "$-_.+!*'(),".toCharArray()){
|
||||
addSafeCharacter(c);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSafeCharacter(char c) {
|
||||
_safeCharacters.set(c);
|
||||
}
|
||||
|
||||
public String encode(String path) {
|
||||
int maxBytesPerChar = 10;
|
||||
// int caseDiff = ('a' - 'A');
|
||||
StringBuffer rewrittenPath = new StringBuffer(path.length());
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
|
||||
OutputStreamWriter writer = null;
|
||||
try {
|
||||
writer = new OutputStreamWriter(buf, "UTF8");
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error in encode <"+path+">", e);
|
||||
writer = new OutputStreamWriter(buf);
|
||||
}
|
||||
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
int c = (int) path.charAt(i);
|
||||
if (_safeCharacters.get(c)) {
|
||||
rewrittenPath.append((char) c);
|
||||
} else {
|
||||
// convert to external encoding before hex conversion
|
||||
try {
|
||||
writer.write((char) c);
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
buf.reset();
|
||||
continue;
|
||||
}
|
||||
byte[] ba = buf.toByteArray();
|
||||
for (int j = 0; j < ba.length; j++) {
|
||||
// Converting each byte in the buffer
|
||||
byte toEncode = ba[j];
|
||||
rewrittenPath.append('%');
|
||||
int low = (int) (toEncode & 0x0f);
|
||||
int high = (int) ((toEncode & 0xf0) >> 4);
|
||||
rewrittenPath.append(HEXADECIMAL[high]);
|
||||
rewrittenPath.append(HEXADECIMAL[low]);
|
||||
}
|
||||
buf.reset();
|
||||
}
|
||||
}
|
||||
return rewrittenPath.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package net.sf.webdav.fromcatalina;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
public class XMLHelper {
|
||||
|
||||
public static Node findSubElement(Node parent, String localName) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
Node child = parent.getFirstChild();
|
||||
while (child != null) {
|
||||
if ((child.getNodeType() == Node.ELEMENT_NODE)
|
||||
&& (child.getLocalName().equals(localName))) {
|
||||
return child;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Vector<String> getPropertiesFromXML(Node propNode) {
|
||||
Vector<String> properties;
|
||||
properties = new Vector<String>();
|
||||
NodeList childList = propNode.getChildNodes();
|
||||
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
Node currentNode = childList.item(i);
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String nodeName = currentNode.getLocalName();
|
||||
String namespace = currentNode.getNamespaceURI();
|
||||
// href is a live property which is handled differently
|
||||
properties.addElement(namespace + ":" + nodeName);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.fromcatalina;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* XMLWriter helper class.
|
||||
*
|
||||
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
|
||||
*/
|
||||
public class XMLWriter {
|
||||
|
||||
// -------------------------------------------------------------- Constants
|
||||
|
||||
/**
|
||||
* Opening tag.
|
||||
*/
|
||||
public static final int OPENING = 0;
|
||||
|
||||
/**
|
||||
* Closing tag.
|
||||
*/
|
||||
public static final int CLOSING = 1;
|
||||
|
||||
/**
|
||||
* Element with no content.
|
||||
*/
|
||||
public static final int NO_CONTENT = 2;
|
||||
|
||||
// ----------------------------------------------------- Instance Variables
|
||||
|
||||
/**
|
||||
* Buffer.
|
||||
*/
|
||||
protected StringBuffer _buffer = new StringBuffer();
|
||||
|
||||
/**
|
||||
* Writer.
|
||||
*/
|
||||
protected Writer _writer = null;
|
||||
|
||||
/**
|
||||
* Namespaces to be declared in the root element
|
||||
*/
|
||||
protected Map<String, String> _namespaces;
|
||||
|
||||
/**
|
||||
* Is true until the root element is written
|
||||
*/
|
||||
protected boolean _isRootElement = true;
|
||||
|
||||
// ----------------------------------------------------------- Constructors
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public XMLWriter(Map<String, String> namespaces) {
|
||||
_namespaces = namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public XMLWriter(Writer writer, Map<String, String> namespaces) {
|
||||
_writer = writer;
|
||||
_namespaces = namespaces;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- Public Methods
|
||||
|
||||
/**
|
||||
* Retrieve generated XML.
|
||||
*
|
||||
* @return String containing the generated XML
|
||||
*/
|
||||
public String toString() {
|
||||
return _buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write property to the XML.
|
||||
*
|
||||
* @param name
|
||||
* Property name
|
||||
* @param value
|
||||
* Property value
|
||||
*/
|
||||
public void writeProperty(String name, String value) {
|
||||
writeElement(name, OPENING);
|
||||
_buffer.append(value);
|
||||
writeElement(name, CLOSING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write property to the XML.
|
||||
*
|
||||
* @param name
|
||||
* Property name
|
||||
*/
|
||||
public void writeProperty(String name) {
|
||||
writeElement(name, NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an element.
|
||||
*
|
||||
* @param name
|
||||
* Element name
|
||||
* @param type
|
||||
* Element type
|
||||
*/
|
||||
public void writeElement(String name, int type) {
|
||||
StringBuffer nsdecl = new StringBuffer();
|
||||
|
||||
if (_isRootElement) {
|
||||
for (Iterator<String> iter = _namespaces.keySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
String fullName = (String) iter.next();
|
||||
String abbrev = (String) _namespaces.get(fullName);
|
||||
nsdecl.append(" xmlns:").append(abbrev).append("=\"").append(
|
||||
fullName).append("\"");
|
||||
}
|
||||
_isRootElement = false;
|
||||
}
|
||||
|
||||
int pos = name.lastIndexOf(':');
|
||||
if (pos >= 0) {
|
||||
// lookup prefix for namespace
|
||||
String fullns = name.substring(0, pos);
|
||||
String prefix = (String) _namespaces.get(fullns);
|
||||
if (prefix == null) {
|
||||
// there is no prefix for this namespace
|
||||
name = name.substring(pos + 1);
|
||||
nsdecl.append(" xmlns=\"").append(fullns).append("\"");
|
||||
} else {
|
||||
// there is a prefix
|
||||
name = prefix + ":" + name.substring(pos + 1);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"All XML elements must have a namespace");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case OPENING:
|
||||
_buffer.append("<" + name + nsdecl + ">");
|
||||
break;
|
||||
case CLOSING:
|
||||
_buffer.append("</" + name + ">\n");
|
||||
break;
|
||||
case NO_CONTENT:
|
||||
default:
|
||||
_buffer.append("<" + name + nsdecl + "/>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write text.
|
||||
*
|
||||
* @param text
|
||||
* Text to append
|
||||
*/
|
||||
public void writeText(String text) {
|
||||
_buffer.append(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data.
|
||||
*
|
||||
* @param data
|
||||
* Data to append
|
||||
*/
|
||||
public void writeData(String data) {
|
||||
_buffer.append("<![CDATA[" + data + "]]>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write XML Header.
|
||||
*/
|
||||
public void writeXMLHeader() {
|
||||
_buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data and reinitializes buffer.
|
||||
*/
|
||||
public void sendData() throws IOException {
|
||||
if (_writer != null) {
|
||||
_writer.write(_buffer.toString());
|
||||
_writer.flush();
|
||||
_buffer = new StringBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package net.sf.webdav.locking;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
|
||||
public interface IResourceLocks {
|
||||
|
||||
/**
|
||||
* Tries to lock the resource at "path".
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* what resource to lock
|
||||
* @param owner
|
||||
* the owner of the lock
|
||||
* @param exclusive
|
||||
* if the lock should be exclusive (or shared)
|
||||
* @param depth
|
||||
* depth
|
||||
* @param timeout
|
||||
* Lock Duration in seconds.
|
||||
* @return true if the resource at path was successfully locked, false if an
|
||||
* existing lock prevented this
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
boolean lock(ITransaction transaction, String path, String owner,
|
||||
boolean exclusive, int depth, int timeout, boolean temporary)
|
||||
throws LockFailedException;
|
||||
|
||||
/**
|
||||
* Unlocks all resources at "path" (and all subfolders if existing)<p/> that
|
||||
* have the same owner.
|
||||
*
|
||||
* @param transaction
|
||||
* @param id
|
||||
* id to the resource to unlock
|
||||
* @param owner
|
||||
* who wants to unlock
|
||||
*/
|
||||
boolean unlock(ITransaction transaction, String id, String owner);
|
||||
|
||||
/**
|
||||
* Unlocks all resources at "path" (and all subfolders if existing)<p/> that
|
||||
* have the same owner.
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* what resource to unlock
|
||||
* @param owner
|
||||
* who wants to unlock
|
||||
*/
|
||||
void unlockTemporaryLockedObjects(ITransaction transaction, String path,
|
||||
String owner);
|
||||
|
||||
/**
|
||||
* Deletes LockedObjects, where timeout has reached.
|
||||
*
|
||||
* @param transaction
|
||||
* @param temporary
|
||||
* Check timeout on temporary or real locks
|
||||
*/
|
||||
void checkTimeouts(ITransaction transaction, boolean temporary);
|
||||
|
||||
/**
|
||||
* Tries to lock the resource at "path" exclusively.
|
||||
*
|
||||
* @param transaction
|
||||
* Transaction
|
||||
* @param path
|
||||
* what resource to lock
|
||||
* @param owner
|
||||
* the owner of the lock
|
||||
* @param depth
|
||||
* depth
|
||||
* @param timeout
|
||||
* Lock Duration in seconds.
|
||||
* @return true if the resource at path was successfully locked, false if an
|
||||
* existing lock prevented this
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
boolean exclusiveLock(ITransaction transaction, String path, String owner,
|
||||
int depth, int timeout) throws LockFailedException;
|
||||
|
||||
/**
|
||||
* Tries to lock the resource at "path" shared.
|
||||
*
|
||||
* @param transaction
|
||||
* Transaction
|
||||
* @param path
|
||||
* what resource to lock
|
||||
* @param owner
|
||||
* the owner of the lock
|
||||
* @param depth
|
||||
* depth
|
||||
* @param timeout
|
||||
* Lock Duration in seconds.
|
||||
* @return true if the resource at path was successfully locked, false if an
|
||||
* existing lock prevented this
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
boolean sharedLock(ITransaction transaction, String path, String owner,
|
||||
int depth, int timeout) throws LockFailedException;
|
||||
|
||||
/**
|
||||
* Gets the LockedObject corresponding to specified id.
|
||||
*
|
||||
* @param transaction
|
||||
* @param id
|
||||
* LockToken to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getLockedObjectByID(ITransaction transaction, String id);
|
||||
|
||||
/**
|
||||
* Gets the LockedObject on specified path.
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* Path to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getLockedObjectByPath(ITransaction transaction, String path);
|
||||
|
||||
/**
|
||||
* Gets the LockedObject corresponding to specified id (locktoken).
|
||||
*
|
||||
* @param transaction
|
||||
* @param id
|
||||
* LockToken to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getTempLockedObjectByID(ITransaction transaction, String id);
|
||||
|
||||
/**
|
||||
* Gets the LockedObject on specified path.
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* Path to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getTempLockedObjectByPath(ITransaction transaction, String path);
|
||||
|
||||
}
|
|
@ -0,0 +1,422 @@
|
|||
package net.sf.webdav.locking;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* a helper class for ResourceLocks, represents the Locks
|
||||
*
|
||||
* @author re
|
||||
*
|
||||
*/
|
||||
public class LockedObject {
|
||||
|
||||
private ResourceLocks _resourceLocks;
|
||||
|
||||
private String _path;
|
||||
|
||||
private String _id;
|
||||
|
||||
/**
|
||||
* Describing the depth of a locked collection. If the locked resource is
|
||||
* not a collection, depth is 0 / doesn't matter.
|
||||
*/
|
||||
protected int _lockDepth;
|
||||
|
||||
/**
|
||||
* Describing the timeout of a locked object (ms)
|
||||
*/
|
||||
protected long _expiresAt;
|
||||
|
||||
/**
|
||||
* owner of the lock. shared locks can have multiple owners. is null if no
|
||||
* owner is present
|
||||
*/
|
||||
// protected String[] _owner = null;
|
||||
protected String[] _owner = null;
|
||||
|
||||
/**
|
||||
* children of that lock
|
||||
*/
|
||||
protected LockedObject[] _children = null;
|
||||
|
||||
protected LockedObject _parent = null;
|
||||
|
||||
/**
|
||||
* weather the lock is exclusive or not. if owner=null the exclusive value
|
||||
* doesn't matter
|
||||
*/
|
||||
protected boolean _exclusive = false;
|
||||
|
||||
/**
|
||||
* weather the lock is a write or read lock
|
||||
*/
|
||||
protected String _type = null;
|
||||
|
||||
/**
|
||||
* @param _resourceLocks
|
||||
* the resourceLocks where locks are stored
|
||||
* @param path
|
||||
* the path to the locked object
|
||||
* @param temporary
|
||||
* indicates if the LockedObject should be temporary or not
|
||||
*/
|
||||
public LockedObject(ResourceLocks resLocks, String path, boolean temporary) {
|
||||
_path = path;
|
||||
_id = UUID.randomUUID().toString();
|
||||
_resourceLocks = resLocks;
|
||||
|
||||
if (!temporary) {
|
||||
_resourceLocks._locks.put(path, this);
|
||||
_resourceLocks._locksByID.put(_id, this);
|
||||
} else {
|
||||
_resourceLocks._tempLocks.put(path, this);
|
||||
_resourceLocks._tempLocksByID.put(_id, this);
|
||||
}
|
||||
_resourceLocks._cleanupCounter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a new owner to a lock
|
||||
*
|
||||
* @param owner
|
||||
* string that represents the owner
|
||||
* @return true if the owner was added, false otherwise
|
||||
*/
|
||||
public boolean addLockedObjectOwner(String owner) {
|
||||
|
||||
if (_owner == null) {
|
||||
_owner = new String[1];
|
||||
} else {
|
||||
|
||||
int size = _owner.length;
|
||||
String[] newLockObjectOwner = new String[size + 1];
|
||||
|
||||
// check if the owner is already here (that should actually not
|
||||
// happen)
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_owner[i].equals(owner)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
System.arraycopy(_owner, 0, newLockObjectOwner, 0, size);
|
||||
_owner = newLockObjectOwner;
|
||||
}
|
||||
|
||||
_owner[_owner.length - 1] = owner;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to remove the owner from the lock
|
||||
*
|
||||
* @param owner
|
||||
* string that represents the owner
|
||||
*/
|
||||
public void removeLockedObjectOwner(String owner) {
|
||||
|
||||
try {
|
||||
if (_owner != null) {
|
||||
int size = _owner.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
// check every owner if it is the requested one
|
||||
if (_owner[i].equals(owner)) {
|
||||
// remove the owner
|
||||
size -= 1;
|
||||
String[] newLockedObjectOwner = new String[size];
|
||||
for (int j = 0; j < size; j++) {
|
||||
if (j < i) {
|
||||
newLockedObjectOwner[j] = _owner[j];
|
||||
} else {
|
||||
newLockedObjectOwner[j] = _owner[j + 1];
|
||||
}
|
||||
}
|
||||
_owner = newLockedObjectOwner;
|
||||
|
||||
}
|
||||
}
|
||||
if (_owner.length == 0) {
|
||||
_owner = null;
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
System.out.println("LockedObject.removeLockedObjectOwner()");
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a new child lock to this lock
|
||||
*
|
||||
* @param newChild
|
||||
* new child
|
||||
*/
|
||||
public void addChild(LockedObject newChild) {
|
||||
if (_children == null) {
|
||||
_children = new LockedObject[0];
|
||||
}
|
||||
int size = _children.length;
|
||||
LockedObject[] newChildren = new LockedObject[size + 1];
|
||||
System.arraycopy(_children, 0, newChildren, 0, size);
|
||||
newChildren[size] = newChild;
|
||||
_children = newChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes this Lock object. assumes that it has no children and no owners
|
||||
* (does not check this itself)
|
||||
*
|
||||
*/
|
||||
public void removeLockedObject() {
|
||||
if (this != _resourceLocks._root && !this.getPath().equals("/")) {
|
||||
|
||||
int size = _parent._children.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_parent._children[i].equals(this)) {
|
||||
LockedObject[] newChildren = new LockedObject[size - 1];
|
||||
for (int i2 = 0; i2 < (size - 1); i2++) {
|
||||
if (i2 < i) {
|
||||
newChildren[i2] = _parent._children[i2];
|
||||
} else {
|
||||
newChildren[i2] = _parent._children[i2 + 1];
|
||||
}
|
||||
}
|
||||
if (newChildren.length != 0) {
|
||||
_parent._children = newChildren;
|
||||
} else {
|
||||
_parent._children = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// removing from hashtable
|
||||
_resourceLocks._locksByID.remove(getID());
|
||||
_resourceLocks._locks.remove(getPath());
|
||||
|
||||
// now the garbage collector has some work to do
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes this Lock object. assumes that it has no children and no owners
|
||||
* (does not check this itself)
|
||||
*
|
||||
*/
|
||||
public void removeTempLockedObject() {
|
||||
if (this != _resourceLocks._tempRoot) {
|
||||
// removing from tree
|
||||
if (_parent != null && _parent._children != null) {
|
||||
int size = _parent._children.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_parent._children[i].equals(this)) {
|
||||
LockedObject[] newChildren = new LockedObject[size - 1];
|
||||
for (int i2 = 0; i2 < (size - 1); i2++) {
|
||||
if (i2 < i) {
|
||||
newChildren[i2] = _parent._children[i2];
|
||||
} else {
|
||||
newChildren[i2] = _parent._children[i2 + 1];
|
||||
}
|
||||
}
|
||||
if (newChildren.length != 0) {
|
||||
_parent._children = newChildren;
|
||||
} else {
|
||||
_parent._children = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// removing from hashtable
|
||||
_resourceLocks._tempLocksByID.remove(getID());
|
||||
_resourceLocks._tempLocks.remove(getPath());
|
||||
|
||||
// now the garbage collector has some work to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if a lock of the given exclusivity can be placed, only considering
|
||||
* children up to "depth"
|
||||
*
|
||||
* @param exclusive
|
||||
* wheather the new lock should be exclusive
|
||||
* @param depth
|
||||
* the depth to which should be checked
|
||||
* @return true if the lock can be placed
|
||||
*/
|
||||
public boolean checkLocks(boolean exclusive, int depth) {
|
||||
if (checkParents(exclusive) && checkChildren(exclusive, depth)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper of checkLocks(). looks if the parents are locked
|
||||
*
|
||||
* @param exclusive
|
||||
* wheather the new lock should be exclusive
|
||||
* @return true if no locks at the parent path are forbidding a new lock
|
||||
*/
|
||||
private boolean checkParents(boolean exclusive) {
|
||||
if (_path.equals("/")) {
|
||||
return true;
|
||||
} else {
|
||||
if (_owner == null) {
|
||||
// no owner, checking parents
|
||||
return _parent != null && _parent.checkParents(exclusive);
|
||||
} else {
|
||||
// there already is a owner
|
||||
return !(_exclusive || exclusive)
|
||||
&& _parent.checkParents(exclusive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper of checkLocks(). looks if the children are locked
|
||||
*
|
||||
* @param exclusive
|
||||
* wheather the new lock should be exclusive
|
||||
* @return true if no locks at the children paths are forbidding a new lock
|
||||
* @param depth
|
||||
* depth
|
||||
*/
|
||||
private boolean checkChildren(boolean exclusive, int depth) {
|
||||
if (_children == null) {
|
||||
// a file
|
||||
|
||||
return _owner == null || !(_exclusive || exclusive);
|
||||
} else {
|
||||
// a folder
|
||||
|
||||
if (_owner == null) {
|
||||
// no owner, checking children
|
||||
|
||||
if (depth != 0) {
|
||||
boolean canLock = true;
|
||||
int limit = _children.length;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
if (!_children[i].checkChildren(exclusive, depth - 1)) {
|
||||
canLock = false;
|
||||
}
|
||||
}
|
||||
return canLock;
|
||||
} else {
|
||||
// depth == 0 -> we don't care for children
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// there already is a owner
|
||||
return !(_exclusive || exclusive);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new timeout for the LockedObject
|
||||
*
|
||||
* @param timeout
|
||||
*/
|
||||
public void refreshTimeout(int timeout) {
|
||||
_expiresAt = System.currentTimeMillis() + (timeout * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timeout for the LockedObject
|
||||
*
|
||||
* @return timeout
|
||||
*/
|
||||
public long getTimeoutMillis() {
|
||||
return (_expiresAt - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the lock has expired.
|
||||
*
|
||||
* @return true if timeout has passed
|
||||
*/
|
||||
public boolean hasExpired() {
|
||||
if (_expiresAt != 0) {
|
||||
return (System.currentTimeMillis() > _expiresAt);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the LockID (locktoken) for the LockedObject
|
||||
*
|
||||
* @return locktoken
|
||||
*/
|
||||
public String getID() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the owners for the LockedObject
|
||||
*
|
||||
* @return owners
|
||||
*/
|
||||
public String[] getOwner() {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for the LockedObject
|
||||
*
|
||||
* @return path
|
||||
*/
|
||||
public String getPath() {
|
||||
return _path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the exclusivity for the LockedObject
|
||||
*
|
||||
* @param exclusive
|
||||
*/
|
||||
public void setExclusive(boolean exclusive) {
|
||||
_exclusive = exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exclusivity for the LockedObject
|
||||
*
|
||||
* @return exclusivity
|
||||
*/
|
||||
public boolean isExclusive() {
|
||||
return _exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exclusivity for the LockedObject
|
||||
*
|
||||
* @return exclusivity
|
||||
*/
|
||||
public boolean isShared() {
|
||||
return !_exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the lock
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public String getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the depth of the lock
|
||||
*
|
||||
* @return depth
|
||||
*/
|
||||
public int getLockDepth() {
|
||||
return _lockDepth;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* Copyright 2005-2006 webdav-servlet group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.locking;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
|
||||
/**
|
||||
* simple locking management for concurrent data access, NOT the webdav locking.
|
||||
* ( could that be used instead? )
|
||||
*
|
||||
* IT IS ACTUALLY USED FOR DOLOCK
|
||||
*
|
||||
* @author re
|
||||
*/
|
||||
public class ResourceLocks implements IResourceLocks {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(ResourceLocks.class);
|
||||
|
||||
/**
|
||||
* after creating this much LockedObjects, a cleanup deletes unused
|
||||
* LockedObjects
|
||||
*/
|
||||
private final int _cleanupLimit = 100000;
|
||||
|
||||
protected int _cleanupCounter = 0;
|
||||
|
||||
/**
|
||||
* keys: path value: LockedObject from that path
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _locks = new Hashtable<String, LockedObject>();
|
||||
|
||||
/**
|
||||
* keys: id value: LockedObject from that id
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _locksByID = new Hashtable<String, LockedObject>();
|
||||
|
||||
/**
|
||||
* keys: path value: Temporary LockedObject from that path
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _tempLocks = new Hashtable<String, LockedObject>();
|
||||
|
||||
/**
|
||||
* keys: id value: Temporary LockedObject from that id
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _tempLocksByID = new Hashtable<String, LockedObject>();
|
||||
|
||||
// REMEMBER TO REMOVE UNUSED LOCKS FROM THE HASHTABLE AS WELL
|
||||
|
||||
protected LockedObject _root = null;
|
||||
|
||||
protected LockedObject _tempRoot = null;
|
||||
|
||||
private boolean _temporary = true;
|
||||
|
||||
public ResourceLocks() {
|
||||
_root = new LockedObject(this, "/", true);
|
||||
_tempRoot = new LockedObject(this, "/", false);
|
||||
}
|
||||
|
||||
public synchronized boolean lock(ITransaction transaction, String path,
|
||||
String owner, boolean exclusive, int depth, int timeout,
|
||||
boolean temporary) throws LockFailedException {
|
||||
|
||||
LockedObject lo = null;
|
||||
|
||||
if (temporary) {
|
||||
lo = generateTempLockedObjects(transaction, path);
|
||||
lo._type = "read";
|
||||
} else {
|
||||
lo = generateLockedObjects(transaction, path);
|
||||
lo._type = "write";
|
||||
}
|
||||
|
||||
if (lo.checkLocks(exclusive, depth)) {
|
||||
|
||||
lo._exclusive = exclusive;
|
||||
lo._lockDepth = depth;
|
||||
lo._expiresAt = System.currentTimeMillis() + (timeout * 1000);
|
||||
if (lo._parent != null) {
|
||||
lo._parent._expiresAt = lo._expiresAt;
|
||||
if (lo._parent.equals(_root)) {
|
||||
LockedObject rootLo = getLockedObjectByPath(transaction,
|
||||
_root.getPath());
|
||||
rootLo._expiresAt = lo._expiresAt;
|
||||
} else if (lo._parent.equals(_tempRoot)) {
|
||||
LockedObject tempRootLo = getTempLockedObjectByPath(
|
||||
transaction, _tempRoot.getPath());
|
||||
tempRootLo._expiresAt = lo._expiresAt;
|
||||
}
|
||||
}
|
||||
if (lo.addLockedObjectOwner(owner)) {
|
||||
return true;
|
||||
} else {
|
||||
LOG.trace("Couldn't set owner \"" + owner
|
||||
+ "\" to resource at '" + path + "'");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// can not lock
|
||||
LOG.trace("Lock resource at " + path + " failed because"
|
||||
+ "\na parent or child resource is currently locked");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean unlock(ITransaction transaction, String id,
|
||||
String owner) {
|
||||
|
||||
if (_locksByID.containsKey(id)) {
|
||||
String path = _locksByID.get(id).getPath();
|
||||
if (_locks.containsKey(path)) {
|
||||
LockedObject lo = _locks.get(path);
|
||||
lo.removeLockedObjectOwner(owner);
|
||||
|
||||
if (lo._children == null && lo._owner == null)
|
||||
lo.removeLockedObject();
|
||||
|
||||
} else {
|
||||
// there is no lock at that path. someone tried to unlock it
|
||||
// anyway. could point to a problem
|
||||
LOG
|
||||
.trace("net.sf.webdav.locking.ResourceLocks.unlock(): no lock for path "
|
||||
+ path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cleanupCounter > _cleanupLimit) {
|
||||
_cleanupCounter = 0;
|
||||
cleanLockedObjects(transaction, _root, !_temporary);
|
||||
}
|
||||
}
|
||||
checkTimeouts(transaction, !_temporary);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public synchronized void unlockTemporaryLockedObjects(
|
||||
ITransaction transaction, String path, String owner) {
|
||||
if (_tempLocks.containsKey(path)) {
|
||||
LockedObject lo = _tempLocks.get(path);
|
||||
lo.removeLockedObjectOwner(owner);
|
||||
|
||||
} else {
|
||||
// there is no lock at that path. someone tried to unlock it
|
||||
// anyway. could point to a problem
|
||||
LOG
|
||||
.trace("net.sf.webdav.locking.ResourceLocks.unlock(): no lock for path "
|
||||
+ path);
|
||||
}
|
||||
|
||||
if (_cleanupCounter > _cleanupLimit) {
|
||||
_cleanupCounter = 0;
|
||||
cleanLockedObjects(transaction, _tempRoot, _temporary);
|
||||
}
|
||||
|
||||
checkTimeouts(transaction, _temporary);
|
||||
|
||||
}
|
||||
|
||||
public void checkTimeouts(ITransaction transaction, boolean temporary) {
|
||||
if (!temporary) {
|
||||
Enumeration<LockedObject> lockedObjects = _locks.elements();
|
||||
while (lockedObjects.hasMoreElements()) {
|
||||
LockedObject currentLockedObject = lockedObjects.nextElement();
|
||||
|
||||
if (currentLockedObject._expiresAt < System.currentTimeMillis()) {
|
||||
currentLockedObject.removeLockedObject();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Enumeration<LockedObject> lockedObjects = _tempLocks.elements();
|
||||
while (lockedObjects.hasMoreElements()) {
|
||||
LockedObject currentLockedObject = lockedObjects.nextElement();
|
||||
|
||||
if (currentLockedObject._expiresAt < System.currentTimeMillis()) {
|
||||
currentLockedObject.removeTempLockedObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean exclusiveLock(ITransaction transaction, String path,
|
||||
String owner, int depth, int timeout) throws LockFailedException {
|
||||
return lock(transaction, path, owner, true, depth, timeout, false);
|
||||
}
|
||||
|
||||
public boolean sharedLock(ITransaction transaction, String path,
|
||||
String owner, int depth, int timeout) throws LockFailedException {
|
||||
return lock(transaction, path, owner, false, depth, timeout, false);
|
||||
}
|
||||
|
||||
public LockedObject getLockedObjectByID(ITransaction transaction, String id) {
|
||||
if (_locksByID.containsKey(id)) {
|
||||
return _locksByID.get(id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LockedObject getLockedObjectByPath(ITransaction transaction,
|
||||
String path) {
|
||||
if (_locks.containsKey(path)) {
|
||||
return (LockedObject) this._locks.get(path);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LockedObject getTempLockedObjectByID(ITransaction transaction,
|
||||
String id) {
|
||||
if (_tempLocksByID.containsKey(id)) {
|
||||
return _tempLocksByID.get(id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LockedObject getTempLockedObjectByPath(ITransaction transaction,
|
||||
String path) {
|
||||
if (_tempLocks.containsKey(path)) {
|
||||
return (LockedObject) this._tempLocks.get(path);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generates real LockedObjects for the resource at path and its parent
|
||||
* folders. does not create new LockedObjects if they already exist
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* path to the (new) LockedObject
|
||||
* @return the LockedObject for path.
|
||||
*/
|
||||
private LockedObject generateLockedObjects(ITransaction transaction,
|
||||
String path) {
|
||||
if (!_locks.containsKey(path)) {
|
||||
LockedObject returnObject = new LockedObject(this, path,
|
||||
!_temporary);
|
||||
String parentPath = getParentPath(path);
|
||||
if (parentPath != null) {
|
||||
LockedObject parentLockedObject = generateLockedObjects(
|
||||
transaction, parentPath);
|
||||
parentLockedObject.addChild(returnObject);
|
||||
returnObject._parent = parentLockedObject;
|
||||
}
|
||||
return returnObject;
|
||||
} else {
|
||||
// there is already a LockedObject on the specified path
|
||||
return (LockedObject) this._locks.get(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* generates temporary LockedObjects for the resource at path and its parent
|
||||
* folders. does not create new LockedObjects if they already exist
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* path to the (new) LockedObject
|
||||
* @return the LockedObject for path.
|
||||
*/
|
||||
private LockedObject generateTempLockedObjects(ITransaction transaction,
|
||||
String path) {
|
||||
if (!_tempLocks.containsKey(path)) {
|
||||
LockedObject returnObject = new LockedObject(this, path, _temporary);
|
||||
String parentPath = getParentPath(path);
|
||||
if (parentPath != null) {
|
||||
LockedObject parentLockedObject = generateTempLockedObjects(
|
||||
transaction, parentPath);
|
||||
parentLockedObject.addChild(returnObject);
|
||||
returnObject._parent = parentLockedObject;
|
||||
}
|
||||
return returnObject;
|
||||
} else {
|
||||
// there is already a LockedObject on the specified path
|
||||
return (LockedObject) this._tempLocks.get(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes unused LockedObjects and resets the counter. works recursively
|
||||
* starting at the given LockedObject
|
||||
*
|
||||
* @param transaction
|
||||
* @param lo
|
||||
* LockedObject
|
||||
* @param temporary
|
||||
* Clean temporary or real locks
|
||||
*
|
||||
* @return if cleaned
|
||||
*/
|
||||
private boolean cleanLockedObjects(ITransaction transaction,
|
||||
LockedObject lo, boolean temporary) {
|
||||
|
||||
if (lo._children == null) {
|
||||
if (lo._owner == null) {
|
||||
if (temporary) {
|
||||
lo.removeTempLockedObject();
|
||||
} else {
|
||||
lo.removeLockedObject();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
boolean canDelete = true;
|
||||
int limit = lo._children.length;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
if (!cleanLockedObjects(transaction, lo._children[i], temporary)) {
|
||||
canDelete = false;
|
||||
} else {
|
||||
|
||||
// because the deleting shifts the array
|
||||
i--;
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
if (canDelete) {
|
||||
if (lo._owner == null) {
|
||||
if (temporary) {
|
||||
lo.removeTempLockedObject();
|
||||
} else {
|
||||
lo.removeLockedObject();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* creates the parent path from the given path by removing the last '/' and
|
||||
* everything after that
|
||||
*
|
||||
* @param path
|
||||
* the path
|
||||
* @return parent path
|
||||
*/
|
||||
private String getParentPath(String path) {
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash == -1) {
|
||||
return null;
|
||||
} else {
|
||||
if (slash == 0) {
|
||||
// return "root" if parent path is empty string
|
||||
return "/";
|
||||
} else {
|
||||
return path.substring(0, slash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,596 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import net.sf.webdav.IMethodExecutor;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.fromcatalina.RequestUtil;
|
||||
import net.sf.webdav.fromcatalina.URLEncoder;
|
||||
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||
import net.sf.webdav.locking.IResourceLocks;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
|
||||
public abstract class AbstractMethod implements IMethodExecutor {
|
||||
|
||||
private static final ThreadLocal<DateFormat> thLastmodifiedDateFormat = new ThreadLocal<DateFormat>();
|
||||
private static final ThreadLocal<DateFormat> thCreationDateFormat = new ThreadLocal<DateFormat>();
|
||||
private static final ThreadLocal<DateFormat> thLocalDateFormat = new ThreadLocal<DateFormat>();
|
||||
|
||||
/**
|
||||
* Array containing the safe characters set.
|
||||
*/
|
||||
protected static URLEncoder URL_ENCODER;
|
||||
|
||||
/**
|
||||
* Default depth is infite.
|
||||
*/
|
||||
protected static final int INFINITY = 3;
|
||||
|
||||
/**
|
||||
* Simple date format for the creation date ISO 8601 representation
|
||||
* (partial).
|
||||
*/
|
||||
protected static final String CREATION_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
/**
|
||||
* Simple date format for the last modified date. (RFC 822 updated by RFC
|
||||
* 1123)
|
||||
*/
|
||||
protected static final String LAST_MODIFIED_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
|
||||
|
||||
protected static final String LOCAL_DATE_FORMAT = "dd/MM/yy' 'HH:mm:ss";
|
||||
|
||||
static {
|
||||
/**
|
||||
* GMT timezone - all HTTP dates are on GMT
|
||||
*/
|
||||
URL_ENCODER = new URLEncoder();
|
||||
URL_ENCODER.addSafeCharacter('-');
|
||||
URL_ENCODER.addSafeCharacter('_');
|
||||
URL_ENCODER.addSafeCharacter('.');
|
||||
URL_ENCODER.addSafeCharacter('*');
|
||||
URL_ENCODER.addSafeCharacter('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* size of the io-buffer
|
||||
*/
|
||||
protected static int BUF_SIZE = 65536;
|
||||
|
||||
/**
|
||||
* Default lock timeout value.
|
||||
*/
|
||||
protected static final int DEFAULT_TIMEOUT = 3600;
|
||||
|
||||
/**
|
||||
* Maximum lock timeout.
|
||||
*/
|
||||
protected static final int MAX_TIMEOUT = 604800;
|
||||
|
||||
/**
|
||||
* Boolean value to temporary lock resources (for method locks)
|
||||
*/
|
||||
protected static final boolean TEMPORARY = true;
|
||||
|
||||
/**
|
||||
* Timeout for temporary locks
|
||||
*/
|
||||
protected static final int TEMP_TIMEOUT = 10;
|
||||
|
||||
|
||||
public static String lastModifiedDateFormat(final Date date) {
|
||||
DateFormat df = thLastmodifiedDateFormat.get();
|
||||
if( df == null ) {
|
||||
df = new SimpleDateFormat(LAST_MODIFIED_DATE_FORMAT, Locale.US);
|
||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
thLastmodifiedDateFormat.set( df );
|
||||
}
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static String creationDateFormat(final Date date) {
|
||||
DateFormat df = thCreationDateFormat.get();
|
||||
if( df == null ) {
|
||||
df = new SimpleDateFormat(CREATION_DATE_FORMAT);
|
||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
thCreationDateFormat.set( df );
|
||||
}
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static String getLocalDateFormat(final Date date, final Locale loc) {
|
||||
DateFormat df = thLocalDateFormat.get();
|
||||
if( df == null ) {
|
||||
df = new SimpleDateFormat(LOCAL_DATE_FORMAT, loc);
|
||||
}
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses and normalizes the destination header.
|
||||
*
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @return destinationPath
|
||||
* @throws IOException
|
||||
* if an error occurs while sending response
|
||||
*/
|
||||
protected String parseDestinationHeader(HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
String destinationPath = req.getHeader("Destination");
|
||||
|
||||
if (destinationPath == null) {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove url encoding from destination
|
||||
destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
|
||||
|
||||
int protocolIndex = destinationPath.indexOf("://");
|
||||
if (protocolIndex >= 0) {
|
||||
// if the Destination URL contains the protocol, we can safely
|
||||
// trim everything upto the first "/" character after "://"
|
||||
int firstSeparator = destinationPath
|
||||
.indexOf("/", protocolIndex + 4);
|
||||
if (firstSeparator < 0) {
|
||||
destinationPath = "/";
|
||||
} else {
|
||||
destinationPath = destinationPath.substring(firstSeparator);
|
||||
}
|
||||
} else {
|
||||
String hostName = req.getServerName();
|
||||
if ((hostName != null) && (destinationPath.startsWith(hostName))) {
|
||||
destinationPath = destinationPath.substring(hostName.length());
|
||||
}
|
||||
|
||||
int portIndex = destinationPath.indexOf(":");
|
||||
if (portIndex >= 0) {
|
||||
destinationPath = destinationPath.substring(portIndex);
|
||||
}
|
||||
|
||||
if (destinationPath.startsWith(":")) {
|
||||
int firstSeparator = destinationPath.indexOf("/");
|
||||
if (firstSeparator < 0) {
|
||||
destinationPath = "/";
|
||||
} else {
|
||||
destinationPath = destinationPath.substring(firstSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize destination path (remove '.' and' ..')
|
||||
destinationPath = normalize(destinationPath);
|
||||
|
||||
String contextPath = req.getContextPath();
|
||||
if ((contextPath != null) && (destinationPath.startsWith(contextPath))) {
|
||||
destinationPath = destinationPath.substring(contextPath.length());
|
||||
}
|
||||
|
||||
String pathInfo = req.getPathInfo();
|
||||
if (pathInfo != null) {
|
||||
String servletPath = req.getServletPath();
|
||||
if ((servletPath != null)
|
||||
&& (destinationPath.startsWith(servletPath))) {
|
||||
destinationPath = destinationPath.substring(servletPath
|
||||
.length());
|
||||
}
|
||||
}
|
||||
|
||||
return destinationPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a context-relative path, beginning with a "/", that represents the
|
||||
* canonical version of the specified path after ".." and "." elements are
|
||||
* resolved out. If the specified path attempts to go outside the boundaries
|
||||
* of the current context (i.e. too many ".." path elements are present),
|
||||
* return <code>null</code> instead.
|
||||
*
|
||||
* @param path
|
||||
* Path to be normalized
|
||||
* @return normalized path
|
||||
*/
|
||||
protected String normalize(String path) {
|
||||
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
// Create a place for the normalized path
|
||||
String normalized = path;
|
||||
|
||||
if (normalized.equals("/."))
|
||||
return "/";
|
||||
|
||||
// Normalize the slashes and add leading slash if necessary
|
||||
if (normalized.indexOf('\\') >= 0)
|
||||
normalized = normalized.replace('\\', '/');
|
||||
if (!normalized.startsWith("/"))
|
||||
normalized = "/" + normalized;
|
||||
|
||||
// Resolve occurrences of "//" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("//");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index)
|
||||
+ normalized.substring(index + 1);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/./" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/./");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index)
|
||||
+ normalized.substring(index + 2);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/../" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/../");
|
||||
if (index < 0)
|
||||
break;
|
||||
if (index == 0)
|
||||
return (null); // Trying to go outside our context
|
||||
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||
normalized = normalized.substring(0, index2)
|
||||
+ normalized.substring(index + 3);
|
||||
}
|
||||
|
||||
// Return the normalized path that we have completed
|
||||
return (normalized);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path associated with this servlet.
|
||||
*
|
||||
* @param request
|
||||
* The servlet request we are processing
|
||||
*/
|
||||
protected String getRelativePath(HttpServletRequest request) {
|
||||
|
||||
// Are we being processed by a RequestDispatcher.include()?
|
||||
if (request.getAttribute("javax.servlet.include.request_uri") != null) {
|
||||
String result = (String) request
|
||||
.getAttribute("javax.servlet.include.path_info");
|
||||
// if (result == null)
|
||||
// result = (String) request
|
||||
// .getAttribute("javax.servlet.include.servlet_path");
|
||||
if ((result == null) || (result.equals("")))
|
||||
result = "/";
|
||||
return (result);
|
||||
}
|
||||
|
||||
// No, extract the desired path directly from the request
|
||||
String result = request.getPathInfo();
|
||||
// if (result == null) {
|
||||
// result = request.getServletPath();
|
||||
// }
|
||||
if ((result == null) || (result.equals(""))) {
|
||||
result = "/";
|
||||
}
|
||||
return (result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* creates the parent path from the given path by removing the last '/' and
|
||||
* everything after that
|
||||
*
|
||||
* @param path
|
||||
* the path
|
||||
* @return parent path
|
||||
*/
|
||||
protected String getParentPath(String path) {
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash != -1) {
|
||||
return path.substring(0, slash);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* removes a / at the end of the path string, if present
|
||||
*
|
||||
* @param path
|
||||
* the path
|
||||
* @return the path without trailing /
|
||||
*/
|
||||
protected String getCleanPath(String path) {
|
||||
|
||||
if (path.endsWith("/") && path.length() > 1)
|
||||
path = path.substring(0, path.length() - 1);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JAXP document builder instance.
|
||||
*/
|
||||
protected DocumentBuilder getDocumentBuilder() throws ServletException {
|
||||
DocumentBuilder documentBuilder = null;
|
||||
DocumentBuilderFactory documentBuilderFactory = null;
|
||||
try {
|
||||
documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
documentBuilderFactory.setNamespaceAware(true);
|
||||
documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new ServletException("jaxp failed");
|
||||
}
|
||||
return documentBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads the depth header from the request and returns it as a int
|
||||
*
|
||||
* @param req
|
||||
* @return the depth from the depth header
|
||||
*/
|
||||
protected int getDepth(HttpServletRequest req) {
|
||||
int depth = INFINITY;
|
||||
String depthStr = req.getHeader("Depth");
|
||||
if (depthStr != null) {
|
||||
if (depthStr.equals("0")) {
|
||||
depth = 0;
|
||||
} else if (depthStr.equals("1")) {
|
||||
depth = 1;
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL rewriter.
|
||||
*
|
||||
* @param path
|
||||
* Path which has to be rewiten
|
||||
* @return the rewritten path
|
||||
*/
|
||||
protected String rewriteUrl(String path) {
|
||||
return URL_ENCODER.encode(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ETag associated with a file.
|
||||
*
|
||||
* @param StoredObject
|
||||
* StoredObject to get resourceLength, lastModified and a hashCode of
|
||||
* StoredObject
|
||||
* @return the ETag
|
||||
*/
|
||||
protected String getETag(StoredObject so) {
|
||||
|
||||
String resourceLength = "";
|
||||
String lastModified = "";
|
||||
|
||||
if (so != null && so.isResource()) {
|
||||
resourceLength = new Long(so.getResourceLength()).toString();
|
||||
lastModified = new Long(so.getLastModified().getTime()).toString();
|
||||
}
|
||||
|
||||
return "W/\"" + resourceLength + "-" + lastModified + "\"";
|
||||
|
||||
}
|
||||
|
||||
protected String[] getLockIdFromIfHeader(HttpServletRequest req) {
|
||||
String[] ids = new String[2];
|
||||
String id = req.getHeader("If");
|
||||
|
||||
if (id != null && !id.equals("")) {
|
||||
if (id.indexOf(">)") == id.lastIndexOf(">)")) {
|
||||
id = id.substring(id.indexOf("(<"), id.indexOf(">)"));
|
||||
|
||||
if (id.indexOf("locktoken:") != -1) {
|
||||
id = id.substring(id.indexOf(':') + 1);
|
||||
}
|
||||
ids[0] = id;
|
||||
} else {
|
||||
String firstId = id.substring(id.indexOf("(<"), id
|
||||
.indexOf(">)"));
|
||||
if (firstId.indexOf("locktoken:") != -1) {
|
||||
firstId = firstId.substring(firstId.indexOf(':') + 1);
|
||||
}
|
||||
ids[0] = firstId;
|
||||
|
||||
String secondId = id.substring(id.lastIndexOf("(<"), id
|
||||
.lastIndexOf(">)"));
|
||||
if (secondId.indexOf("locktoken:") != -1) {
|
||||
secondId = secondId.substring(secondId.indexOf(':') + 1);
|
||||
}
|
||||
ids[1] = secondId;
|
||||
}
|
||||
|
||||
} else {
|
||||
ids = null;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
protected String getLockIdFromLockTokenHeader(HttpServletRequest req) {
|
||||
String id = req.getHeader("Lock-Token");
|
||||
|
||||
if (id != null) {
|
||||
id = id.substring(id.indexOf(":") + 1, id.indexOf(">"));
|
||||
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if locks on resources at the given path exists and if so checks
|
||||
* the If-Header to make sure the If-Header corresponds to the locked
|
||||
* resource. Returning true if no lock exists or the If-Header is
|
||||
* corresponding to the locked resource
|
||||
*
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @param resourceLocks
|
||||
* @param path
|
||||
* path to the resource
|
||||
* @param errorList
|
||||
* List of error to be displayed
|
||||
* @return true if no lock on a resource with the given path exists or if
|
||||
* the If-Header corresponds to the locked resource
|
||||
* @throws IOException
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
protected boolean checkLocks(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp,
|
||||
IResourceLocks resourceLocks, String path) throws IOException,
|
||||
LockFailedException {
|
||||
|
||||
LockedObject loByPath = resourceLocks.getLockedObjectByPath(
|
||||
transaction, path);
|
||||
if (loByPath != null) {
|
||||
|
||||
if (loByPath.isShared())
|
||||
return true;
|
||||
|
||||
// the resource is locked
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null)
|
||||
lockToken = lockTokens[0];
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (lockToken != null) {
|
||||
LockedObject loByIf = resourceLocks.getLockedObjectByID(
|
||||
transaction, lockToken);
|
||||
if (loByIf == null) {
|
||||
// no locked resource to the given lockToken
|
||||
return false;
|
||||
}
|
||||
if (!loByIf.equals(loByPath)) {
|
||||
loByIf = null;
|
||||
return false;
|
||||
}
|
||||
loByIf = null;
|
||||
}
|
||||
|
||||
}
|
||||
loByPath = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a multistatus element containing a complete error report to the
|
||||
* client. If the errorList contains only one error, send the error
|
||||
* directly without wrapping it in a multistatus message.
|
||||
*
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @param errorList
|
||||
* List of error to be displayed
|
||||
*/
|
||||
protected void sendReport(HttpServletRequest req, HttpServletResponse resp,
|
||||
Hashtable<String, Integer> errorList) throws IOException {
|
||||
|
||||
if (errorList.size() == 1) {
|
||||
int code = errorList.elements().nextElement();
|
||||
if (WebdavStatus.getStatusText(code) != "") {
|
||||
resp.sendError(code, WebdavStatus.getStatusText(code));
|
||||
} else {
|
||||
resp.sendError(code);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||
|
||||
String absoluteUri = req.getRequestURI();
|
||||
// String relativePath = getRelativePath(req);
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
XMLWriter generatedXML = new XMLWriter(namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
|
||||
generatedXML.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||
|
||||
Enumeration<String> pathList = errorList.keys();
|
||||
while (pathList.hasMoreElements()) {
|
||||
|
||||
String errorPath = (String) pathList.nextElement();
|
||||
int errorCode = ((Integer) errorList.get(errorPath)).intValue();
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
String toAppend = null;
|
||||
if (absoluteUri.endsWith(errorPath)) {
|
||||
toAppend = absoluteUri;
|
||||
|
||||
} else if (absoluteUri.contains(errorPath)) {
|
||||
|
||||
int endIndex = absoluteUri.indexOf(errorPath)
|
||||
+ errorPath.length();
|
||||
toAppend = absoluteUri.substring(0, endIndex);
|
||||
}
|
||||
if (!toAppend.startsWith("/") && !toAppend.startsWith("http:"))
|
||||
toAppend = "/" + toAppend;
|
||||
generatedXML.writeText(errorPath);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText("HTTP/1.1 " + errorCode + " "
|
||||
+ WebdavStatus.getStatusText(errorCode));
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||
|
||||
Writer writer = resp.getWriter();
|
||||
writer.write(generatedXML.toString());
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import net.sf.webdav.StoredObject;
|
||||
|
||||
public abstract class DeterminableMethod extends AbstractMethod {
|
||||
|
||||
private static final String NULL_RESOURCE_METHODS_ALLOWED = "OPTIONS, MKCOL, PUT, PROPFIND, LOCK, UNLOCK";
|
||||
|
||||
private static final String RESOURCE_METHODS_ALLOWED = "OPTIONS, GET, HEAD, POST, DELETE, TRACE"
|
||||
+ ", PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND";
|
||||
|
||||
private static final String FOLDER_METHOD_ALLOWED = ", PUT";
|
||||
|
||||
private static final String LESS_ALLOWED_METHODS = "OPTIONS, MKCOL, PUT";
|
||||
|
||||
/**
|
||||
* Determines the methods normally allowed for the resource.
|
||||
*
|
||||
* @param so
|
||||
* StoredObject representing the resource
|
||||
* @return all allowed methods, separated by commas
|
||||
*/
|
||||
protected static String determineMethodsAllowed(StoredObject so) {
|
||||
|
||||
try {
|
||||
if (so != null) {
|
||||
if (so.isNullResource()) {
|
||||
|
||||
return NULL_RESOURCE_METHODS_ALLOWED;
|
||||
|
||||
} else if (so.isFolder()) {
|
||||
return RESOURCE_METHODS_ALLOWED + FOLDER_METHOD_ALLOWED;
|
||||
}
|
||||
// else resource
|
||||
return RESOURCE_METHODS_ALLOWED;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// we do nothing, just return less allowed methods
|
||||
}
|
||||
|
||||
return LESS_ALLOWED_METHODS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||
import net.sf.webdav.exceptions.ObjectNotFoundException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.fromcatalina.RequestUtil;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
public class DoCopy extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoCopy.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private DoDelete _doDelete;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoCopy(IWebdavStore store, ResourceLocks resourceLocks,
|
||||
DoDelete doDelete, boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_doDelete = doDelete;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
String path = getRelativePath(req);
|
||||
if (!_readOnly) {
|
||||
|
||||
String tempLockOwner = "doCopy" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
if (!copyResource(transaction, req, resp))
|
||||
return;
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_CONFLICT, req
|
||||
.getRequestURI());
|
||||
} catch (ObjectNotFoundException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a resource.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @return true if the copy is successful
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
* @throws IOException
|
||||
* when an error occurs while sending the response
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
public boolean copyResource(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws WebdavException, IOException, LockFailedException {
|
||||
|
||||
// Parsing destination header
|
||||
String destinationPath = parseDestinationHeader(req, resp);
|
||||
|
||||
if (destinationPath == null)
|
||||
return false;
|
||||
|
||||
String path = getRelativePath(req);
|
||||
|
||||
if (path.equals(destinationPath)) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
String parentDestinationPath = getParentPath(getCleanPath(destinationPath));
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks,
|
||||
parentDestinationPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return false; // parentDestination is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, destinationPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return false; // destination is locked
|
||||
}
|
||||
|
||||
// Parsing overwrite header
|
||||
|
||||
boolean overwrite = true;
|
||||
String overwriteHeader = req.getHeader("Overwrite");
|
||||
|
||||
if (overwriteHeader != null) {
|
||||
overwrite = overwriteHeader.equalsIgnoreCase("T");
|
||||
}
|
||||
|
||||
// Overwriting the destination
|
||||
String lockOwner = "copyResource" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, destinationPath, lockOwner, false,
|
||||
0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject copySo, destinationSo = null;
|
||||
try {
|
||||
copySo = _store.getStoredObject(transaction, path);
|
||||
// Retrieve the resources
|
||||
if (copySo == null) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copySo.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(copySo);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return false;
|
||||
}
|
||||
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
|
||||
destinationSo = _store.getStoredObject(transaction,
|
||||
destinationPath);
|
||||
|
||||
if (overwrite) {
|
||||
|
||||
// Delete destination resource, if it exists
|
||||
if (destinationSo != null) {
|
||||
_doDelete.deleteResource(transaction, destinationPath,
|
||||
errorList, req, resp);
|
||||
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
} else {
|
||||
|
||||
// If the destination exists, then it's a conflict
|
||||
if (destinationSo != null) {
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
return false;
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
|
||||
}
|
||||
copy(transaction, path, destinationPath, errorList, req, resp);
|
||||
|
||||
if (!errorList.isEmpty()) {
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
destinationPath, lockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* copies the specified resource(s) to the specified destination.
|
||||
* preconditions must be handled by the caller. Standard status codes must
|
||||
* be handled by the caller. a multi status report in case of errors is
|
||||
* created here.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param sourcePath
|
||||
* path from where to read
|
||||
* @param destinationPath
|
||||
* path where to write
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
* @throws IOException
|
||||
*/
|
||||
private void copy(ITransaction transaction, String sourcePath,
|
||||
String destinationPath, Hashtable<String, Integer> errorList,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws WebdavException, IOException {
|
||||
|
||||
StoredObject sourceSo = _store.getStoredObject(transaction, sourcePath);
|
||||
if (sourceSo.isResource()) {
|
||||
_store.createResource(transaction, destinationPath);
|
||||
long resourceLength = _store.setResourceContent(transaction,
|
||||
destinationPath, _store.getResourceContent(transaction,
|
||||
sourcePath), null, null);
|
||||
|
||||
if (resourceLength != -1) {
|
||||
StoredObject destinationSo = _store.getStoredObject(
|
||||
transaction, destinationPath);
|
||||
destinationSo.setResourceLength(resourceLength);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (sourceSo.isFolder()) {
|
||||
copyFolder(transaction, sourcePath, destinationPath, errorList,
|
||||
req, resp);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper method of copy() recursively copies the FOLDER at source path to
|
||||
* destination path
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param sourcePath
|
||||
* where to read
|
||||
* @param destinationPath
|
||||
* where to write
|
||||
* @param errorList
|
||||
* all errors that ocurred
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
*/
|
||||
private void copyFolder(ITransaction transaction, String sourcePath,
|
||||
String destinationPath, Hashtable<String, Integer> errorList,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws WebdavException {
|
||||
|
||||
_store.createFolder(transaction, destinationPath);
|
||||
boolean infiniteDepth = true;
|
||||
String depth = req.getHeader("Depth");
|
||||
if (depth != null) {
|
||||
if (depth.equals("0")) {
|
||||
infiniteDepth = false;
|
||||
}
|
||||
}
|
||||
if (infiniteDepth) {
|
||||
String[] children = _store
|
||||
.getChildrenNames(transaction, sourcePath);
|
||||
children = children == null ? new String[] {} : children;
|
||||
|
||||
StoredObject childSo;
|
||||
for (int i = children.length - 1; i >= 0; i--) {
|
||||
children[i] = "/" + children[i];
|
||||
try {
|
||||
childSo = _store.getStoredObject(transaction,
|
||||
(sourcePath + children[i]));
|
||||
if (childSo.isResource()) {
|
||||
_store.createResource(transaction, destinationPath
|
||||
+ children[i]);
|
||||
long resourceLength = _store.setResourceContent(
|
||||
transaction, destinationPath + children[i],
|
||||
_store.getResourceContent(transaction,
|
||||
sourcePath + children[i]), null, null);
|
||||
|
||||
if (resourceLength != -1) {
|
||||
StoredObject destinationSo = _store
|
||||
.getStoredObject(transaction,
|
||||
destinationPath + children[i]);
|
||||
destinationSo.setResourceLength(resourceLength);
|
||||
}
|
||||
|
||||
} else {
|
||||
copyFolder(transaction, sourcePath + children[i],
|
||||
destinationPath + children[i], errorList, req,
|
||||
resp);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_FORBIDDEN));
|
||||
} catch (ObjectNotFoundException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_NOT_FOUND));
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_CONFLICT));
|
||||
} catch (WebdavException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||
import net.sf.webdav.exceptions.ObjectNotFoundException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
public class DoDelete extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoDelete.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoDelete(IWebdavStore store, ResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (!_readOnly) {
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(getCleanPath(path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
String tempLockOwner = "doDelete" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
deleteResource(transaction, path, errorList, req, resp);
|
||||
if (!errorList.isEmpty()) {
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the recources at "path"
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param path
|
||||
* the folder to be deleted
|
||||
* @param errorList
|
||||
* all errors that ocurred
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
* @throws IOException
|
||||
* when an error occurs while sending the response
|
||||
*/
|
||||
public void deleteResource(ITransaction transaction, String path,
|
||||
Hashtable<String, Integer> errorList, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, WebdavException {
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
|
||||
if (!_readOnly) {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
if (so != null) {
|
||||
|
||||
if (so.isResource()) {
|
||||
_store.removeObject(transaction, path);
|
||||
} else {
|
||||
if (so.isFolder()) {
|
||||
deleteFolder(transaction, path, errorList, req, resp);
|
||||
_store.removeObject(transaction, path);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
}
|
||||
so = null;
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* helper method of deleteResource() deletes the folder and all of its
|
||||
* contents
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param path
|
||||
* the folder to be deleted
|
||||
* @param errorList
|
||||
* all errors that ocurred
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
*/
|
||||
private void deleteFolder(ITransaction transaction, String path,
|
||||
Hashtable<String, Integer> errorList, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws WebdavException {
|
||||
|
||||
String[] children = _store.getChildrenNames(transaction, path);
|
||||
children = children == null ? new String[] {} : children;
|
||||
StoredObject so = null;
|
||||
for (int i = children.length - 1; i >= 0; i--) {
|
||||
children[i] = "/" + children[i];
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path + children[i]);
|
||||
if (so.isResource()) {
|
||||
_store.removeObject(transaction, path + children[i]);
|
||||
|
||||
} else {
|
||||
deleteFolder(transaction, path + children[i], errorList,
|
||||
req, resp);
|
||||
|
||||
_store.removeObject(transaction, path + children[i]);
|
||||
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
errorList.put(path + children[i], new Integer(
|
||||
WebdavStatus.SC_FORBIDDEN));
|
||||
} catch (ObjectNotFoundException e) {
|
||||
errorList.put(path + children[i], new Integer(
|
||||
WebdavStatus.SC_NOT_FOUND));
|
||||
} catch (WebdavException e) {
|
||||
errorList.put(path + children[i], new Integer(
|
||||
WebdavStatus.SC_INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
}
|
||||
so = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.IMimeTyper;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
public class DoGet extends DoHead {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoGet.class);
|
||||
|
||||
public DoGet(IWebdavStore store, String dftIndexFile, String insteadOf404,
|
||||
ResourceLocks resourceLocks, IMimeTyper mimeTyper,
|
||||
int contentLengthHeader) {
|
||||
super(store, dftIndexFile, insteadOf404, resourceLocks, mimeTyper,
|
||||
contentLengthHeader);
|
||||
|
||||
}
|
||||
|
||||
protected void doBody(ITransaction transaction, HttpServletResponse resp,
|
||||
String path) {
|
||||
|
||||
try {
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
OutputStream out = resp.getOutputStream();
|
||||
InputStream in = _store.getResourceContent(transaction, path);
|
||||
try {
|
||||
int read = -1;
|
||||
byte[] copyBuffer = new byte[BUF_SIZE];
|
||||
|
||||
while ((read = in.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||
out.write(copyBuffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
// flushing causes a IOE if a file is opened on the webserver
|
||||
// client disconnected before server finished sending response
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Closing InputStream causes Exception!\n"
|
||||
+ e.toString());
|
||||
}
|
||||
try {
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Flushing OutputStream causes Exception!\n"
|
||||
+ e.toString());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.trace(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected void folderBody(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
throws IOException {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} else {
|
||||
|
||||
if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (so.isFolder()) {
|
||||
// TODO some folder response (for browsers, DAV tools
|
||||
// use propfind) in html?
|
||||
Locale locale = req.getLocale();
|
||||
DateFormat shortDF= getDateTimeFormat(req.getLocale());
|
||||
resp.setContentType("text/html");
|
||||
resp.setCharacterEncoding("UTF8");
|
||||
OutputStream out = resp.getOutputStream();
|
||||
String[] children = _store.getChildrenNames(transaction, path);
|
||||
// Make sure it's not null
|
||||
children = children == null ? new String[] {} : children;
|
||||
// Sort by name
|
||||
Arrays.sort(children);
|
||||
StringBuilder childrenTemp = new StringBuilder();
|
||||
childrenTemp.append("<html><head><title>Content of folder");
|
||||
childrenTemp.append(path);
|
||||
childrenTemp.append("</title><style type=\"text/css\">");
|
||||
childrenTemp.append(getCSS());
|
||||
childrenTemp.append("</style></head>");
|
||||
childrenTemp.append("<body>");
|
||||
childrenTemp.append(getHeader(transaction, path, resp, req));
|
||||
childrenTemp.append("<table>");
|
||||
childrenTemp.append("<tr><th>Name</th><th>Size</th><th>Created</th><th>Modified</th></tr>");
|
||||
childrenTemp.append("<tr>");
|
||||
childrenTemp.append("<td colspan=\"4\"><a href=\"../\">Parent</a></td></tr>");
|
||||
boolean isEven= false;
|
||||
for (String child : children)
|
||||
{
|
||||
isEven= !isEven;
|
||||
childrenTemp.append("<tr class=\"");
|
||||
childrenTemp.append(isEven ? "even" : "odd");
|
||||
childrenTemp.append("\">");
|
||||
childrenTemp.append("<td>");
|
||||
childrenTemp.append("<a href=\"");
|
||||
childrenTemp.append(child);
|
||||
StoredObject obj= _store.getStoredObject(transaction, path+"/"+child);
|
||||
if (obj == null)
|
||||
{
|
||||
LOG.error("Should not return null for "+path+"/"+child);
|
||||
}
|
||||
if (obj != null && obj.isFolder())
|
||||
{
|
||||
childrenTemp.append("/");
|
||||
}
|
||||
childrenTemp.append("\">");
|
||||
childrenTemp.append(child);
|
||||
childrenTemp.append("</a></td>");
|
||||
if (obj != null && obj.isFolder())
|
||||
{
|
||||
childrenTemp.append("<td>Folder</td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("<td>");
|
||||
if (obj != null )
|
||||
{
|
||||
childrenTemp.append(obj.getResourceLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("Unknown");
|
||||
}
|
||||
childrenTemp.append(" Bytes</td>");
|
||||
}
|
||||
if (obj != null && obj.getCreationDate() != null)
|
||||
{
|
||||
childrenTemp.append("<td>");
|
||||
childrenTemp.append(shortDF.format(obj.getCreationDate()));
|
||||
childrenTemp.append("</td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("<td></td>");
|
||||
}
|
||||
if (obj != null && obj.getLastModified() != null)
|
||||
{
|
||||
childrenTemp.append("<td>");
|
||||
childrenTemp.append(shortDF.format(obj.getLastModified()));
|
||||
childrenTemp.append("</td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("<td></td>");
|
||||
}
|
||||
childrenTemp.append("</tr>");
|
||||
}
|
||||
childrenTemp.append("</table>");
|
||||
childrenTemp.append(getFooter(transaction, path, resp, req));
|
||||
childrenTemp.append("</body></html>");
|
||||
out.write(childrenTemp.toString().getBytes("UTF-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CSS styles used to display the HTML representation
|
||||
* of the webdav content.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getCSS()
|
||||
{
|
||||
// The default styles to use
|
||||
String retVal= "body {\n"+
|
||||
" font-family: Arial, Helvetica, sans-serif;\n"+
|
||||
"}\n"+
|
||||
"h1 {\n"+
|
||||
" font-size: 1.5em;\n"+
|
||||
"}\n"+
|
||||
"th {\n"+
|
||||
" background-color: #9DACBF;\n"+
|
||||
"}\n"+
|
||||
"table {\n"+
|
||||
" border-top-style: solid;\n"+
|
||||
" border-right-style: solid;\n"+
|
||||
" border-bottom-style: solid;\n"+
|
||||
" border-left-style: solid;\n"+
|
||||
"}\n"+
|
||||
"td {\n"+
|
||||
" margin: 0px;\n"+
|
||||
" padding-top: 2px;\n"+
|
||||
" padding-right: 5px;\n"+
|
||||
" padding-bottom: 2px;\n"+
|
||||
" padding-left: 5px;\n"+
|
||||
"}\n"+
|
||||
"tr.even {\n"+
|
||||
" background-color: #CCCCCC;\n"+
|
||||
"}\n"+
|
||||
"tr.odd {\n"+
|
||||
" background-color: #FFFFFF;\n"+
|
||||
"}\n"+
|
||||
"";
|
||||
try
|
||||
{
|
||||
// Try loading one via class loader and use that one instead
|
||||
ClassLoader cl = getClass().getClassLoader();
|
||||
InputStream iStream = cl.getResourceAsStream("webdav.css");
|
||||
if(iStream != null)
|
||||
{
|
||||
// Found css via class loader, use that one
|
||||
StringBuffer out = new StringBuffer();
|
||||
byte[] b = new byte[4096];
|
||||
for (int n; (n = iStream.read(b)) != -1;)
|
||||
{
|
||||
out.append(new String(b, 0, n));
|
||||
}
|
||||
retVal= out.toString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.error("Error in reading webdav.css", ex);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the header to be displayed in front of the folder content
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* @param resp
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
protected String getHeader(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
{
|
||||
return "<h1>Content of folder "+path+"</h1>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the footer to be displayed after the folder content
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* @param resp
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
protected String getFooter(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this as the Date/Time format for displaying Creation + Modification dates
|
||||
*
|
||||
* @param browserLocale
|
||||
* @return DateFormat used to display creation and modification dates
|
||||
*/
|
||||
protected DateFormat getDateTimeFormat(Locale browserLocale)
|
||||
{
|
||||
return SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.MEDIUM, browserLocale);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.IMimeTyper;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
public class DoHead extends AbstractMethod {
|
||||
|
||||
protected String _dftIndexFile;
|
||||
protected IWebdavStore _store;
|
||||
protected String _insteadOf404;
|
||||
protected ResourceLocks _resourceLocks;
|
||||
protected IMimeTyper _mimeTyper;
|
||||
protected int _contentLength;
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoHead.class);
|
||||
|
||||
public DoHead(IWebdavStore store, String dftIndexFile, String insteadOf404,
|
||||
ResourceLocks resourceLocks, IMimeTyper mimeTyper,
|
||||
int contentLengthHeader) {
|
||||
_store = store;
|
||||
_dftIndexFile = dftIndexFile;
|
||||
_insteadOf404 = insteadOf404;
|
||||
_resourceLocks = resourceLocks;
|
||||
_mimeTyper = mimeTyper;
|
||||
_contentLength = contentLengthHeader;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
// determines if the uri exists.
|
||||
|
||||
boolean bUriExists = false;
|
||||
|
||||
String path = getRelativePath(req);
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
StoredObject so;
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
if (this._insteadOf404 != null && !_insteadOf404.trim().equals("")) {
|
||||
path = this._insteadOf404;
|
||||
so = _store.getStoredObject(transaction, this._insteadOf404);
|
||||
}
|
||||
} else
|
||||
bUriExists = true;
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (so != null) {
|
||||
if (so.isFolder()) {
|
||||
if (_dftIndexFile != null && !_dftIndexFile.trim().equals("")) {
|
||||
resp.sendRedirect(resp.encodeRedirectURL(req
|
||||
.getRequestURI()
|
||||
+ this._dftIndexFile));
|
||||
return;
|
||||
}
|
||||
} else if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempLockOwner = "doGet" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
|
||||
String eTagMatch = req.getHeader("If-None-Match");
|
||||
if (eTagMatch != null) {
|
||||
if (eTagMatch.equals(getETag(so))) {
|
||||
resp.setStatus(WebdavStatus.SC_NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (so.isResource()) {
|
||||
// path points to a file but ends with / or \
|
||||
if (path.endsWith("/") || (path.endsWith("\\"))) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND,
|
||||
req.getRequestURI());
|
||||
} else {
|
||||
|
||||
// setting headers
|
||||
long lastModified = so.getLastModified().getTime();
|
||||
resp.setDateHeader("last-modified", lastModified);
|
||||
|
||||
String eTag = getETag(so);
|
||||
resp.addHeader("ETag", eTag);
|
||||
|
||||
long resourceLength = so.getResourceLength();
|
||||
|
||||
if (_contentLength == 1) {
|
||||
if (resourceLength > 0) {
|
||||
if (resourceLength <= Integer.MAX_VALUE) {
|
||||
resp
|
||||
.setContentLength((int) resourceLength);
|
||||
} else {
|
||||
resp.setHeader("content-length", ""
|
||||
+ resourceLength);
|
||||
// is "content-length" the right header?
|
||||
// is long a valid format?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String mimeType = _mimeTyper.getMimeType(transaction, path);
|
||||
if (mimeType != null) {
|
||||
resp.setContentType(mimeType);
|
||||
} else {
|
||||
int lastSlash = path.replace('\\', '/')
|
||||
.lastIndexOf('/');
|
||||
int lastDot = path.indexOf(".", lastSlash);
|
||||
if (lastDot == -1) {
|
||||
resp.setContentType("text/html");
|
||||
}
|
||||
}
|
||||
|
||||
doBody(transaction, resp, path);
|
||||
}
|
||||
} else {
|
||||
folderBody(transaction, path, resp, req);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
folderBody(transaction, path, resp, req);
|
||||
}
|
||||
|
||||
if (!bUriExists)
|
||||
resp.setStatus(WebdavStatus.SC_NOT_FOUND);
|
||||
|
||||
}
|
||||
|
||||
protected void folderBody(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
throws IOException {
|
||||
// no body for HEAD
|
||||
}
|
||||
|
||||
protected void doBody(ITransaction transaction, HttpServletResponse resp,
|
||||
String path) throws IOException {
|
||||
// no body for HEAD
|
||||
}
|
||||
}
|
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||
import net.sf.webdav.locking.IResourceLocks;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
|
||||
import org.w3c.dom.DOMException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class DoLock extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoLock.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
private boolean _macLockRequest = false;
|
||||
|
||||
private boolean _exclusive = false;
|
||||
private String _type = null;
|
||||
private String _lockOwner = null;
|
||||
|
||||
private String _path = null;
|
||||
private String _parentPath = null;
|
||||
|
||||
private String _userAgent = null;
|
||||
|
||||
public DoLock(IWebdavStore store, IResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
} else {
|
||||
_path = getRelativePath(req);
|
||||
_parentPath = getParentPath(getCleanPath(_path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, _path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, _parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
// Mac OS Finder (whether 10.4.x or 10.5) can't store files
|
||||
// because executing a LOCK without lock information causes a
|
||||
// SC_BAD_REQUEST
|
||||
_userAgent = req.getHeader("User-Agent");
|
||||
if (_userAgent != null && _userAgent.indexOf("Darwin") != -1) {
|
||||
_macLockRequest = true;
|
||||
|
||||
String timeString = new Long(System.currentTimeMillis())
|
||||
.toString();
|
||||
_lockOwner = _userAgent.concat(timeString);
|
||||
}
|
||||
|
||||
String tempLockOwner = "doLock" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, _path, tempLockOwner, false,
|
||||
0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
if (req.getHeader("If") != null) {
|
||||
doRefreshLock(transaction, req, resp);
|
||||
} else {
|
||||
doLock(transaction, req, resp);
|
||||
}
|
||||
} catch (LockFailedException e) {
|
||||
resp.sendError(WebdavStatus.SC_LOCKED);
|
||||
LOG.error("Lockfailed exception", e);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
_path, tempLockOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doLock(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, _path);
|
||||
|
||||
if (so != null) {
|
||||
doLocking(transaction, req, resp);
|
||||
} else {
|
||||
// resource doesn't exist, null-resource lock
|
||||
doNullResourceLock(transaction, req, resp);
|
||||
}
|
||||
|
||||
so = null;
|
||||
_exclusive = false;
|
||||
_type = null;
|
||||
_lockOwner = null;
|
||||
|
||||
}
|
||||
|
||||
private void doLocking(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
|
||||
// Tests if LockObject on requested path exists, and if so, tests
|
||||
// exclusivity
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
_path);
|
||||
if (lo != null) {
|
||||
if (lo.isExclusive()) {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Thats the locking itself
|
||||
executeLock(transaction, req, resp);
|
||||
|
||||
} catch (ServletException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.trace(e.toString());
|
||||
} catch (LockFailedException e) {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
} finally {
|
||||
lo = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void doNullResourceLock(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException {
|
||||
|
||||
StoredObject parentSo, nullSo = null;
|
||||
|
||||
try {
|
||||
parentSo = _store.getStoredObject(transaction, _parentPath);
|
||||
if (_parentPath != null && parentSo == null) {
|
||||
_store.createFolder(transaction, _parentPath);
|
||||
} else if (_parentPath != null && parentSo != null
|
||||
&& parentSo.isResource()) {
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
nullSo = _store.getStoredObject(transaction, _path);
|
||||
if (nullSo == null) {
|
||||
// resource doesn't exist
|
||||
_store.createResource(transaction, _path);
|
||||
|
||||
// Transmit expects 204 response-code, not 201
|
||||
if (_userAgent != null && _userAgent.indexOf("Transmit") != -1) {
|
||||
LOG
|
||||
.trace("DoLock.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
|
||||
} else {
|
||||
// resource already exists, could not execute null-resource lock
|
||||
sendLockFailError(transaction, req, resp);
|
||||
return;
|
||||
}
|
||||
nullSo = _store.getStoredObject(transaction, _path);
|
||||
// define the newly created resource as null-resource
|
||||
nullSo.setNullResource(true);
|
||||
|
||||
// Thats the locking itself
|
||||
executeLock(transaction, req, resp);
|
||||
|
||||
} catch (LockFailedException e) {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("Webdav exception", e);
|
||||
} catch (ServletException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("Servlet exception", e);
|
||||
} finally {
|
||||
parentSo = null;
|
||||
nullSo = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void doRefreshLock(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException, LockFailedException {
|
||||
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null)
|
||||
lockToken = lockTokens[0];
|
||||
|
||||
if (lockToken != null) {
|
||||
// Getting LockObject of specified lockToken in If header
|
||||
LockedObject refreshLo = _resourceLocks.getLockedObjectByID(
|
||||
transaction, lockToken);
|
||||
if (refreshLo != null) {
|
||||
int timeout = getTimeout(transaction, req);
|
||||
|
||||
refreshLo.refreshTimeout(timeout);
|
||||
// sending success response
|
||||
generateXMLReport(transaction, resp, refreshLo);
|
||||
|
||||
refreshLo = null;
|
||||
} else {
|
||||
// no LockObject to given lockToken
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------- helper methods
|
||||
|
||||
/**
|
||||
* Executes the LOCK
|
||||
*/
|
||||
private void executeLock(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws LockFailedException, IOException,
|
||||
ServletException {
|
||||
|
||||
// Mac OS lock request workaround
|
||||
if (_macLockRequest) {
|
||||
LOG.trace("DoLock.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
|
||||
doMacLockRequestWorkaround(transaction, req, resp);
|
||||
} else {
|
||||
// Getting LockInformation from request
|
||||
if (getLockInformation(transaction, req, resp)) {
|
||||
int depth = getDepth(req);
|
||||
int lockDuration = getTimeout(transaction, req);
|
||||
|
||||
boolean lockSuccess = false;
|
||||
if (_exclusive) {
|
||||
lockSuccess = _resourceLocks.exclusiveLock(transaction,
|
||||
_path, _lockOwner, depth, lockDuration);
|
||||
} else {
|
||||
lockSuccess = _resourceLocks.sharedLock(transaction, _path,
|
||||
_lockOwner, depth, lockDuration);
|
||||
}
|
||||
|
||||
if (lockSuccess) {
|
||||
// Locks successfully placed - return information about
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(
|
||||
transaction, _path);
|
||||
if (lo != null) {
|
||||
generateXMLReport(transaction, resp, lo);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
|
||||
throw new LockFailedException();
|
||||
}
|
||||
} else {
|
||||
// information for LOCK could not be read successfully
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get the LockInformation from LOCK request
|
||||
*/
|
||||
private boolean getLockInformation(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
|
||||
Node lockInfoNode = null;
|
||||
DocumentBuilder documentBuilder = null;
|
||||
|
||||
documentBuilder = getDocumentBuilder();
|
||||
try {
|
||||
Document document = documentBuilder.parse(new InputSource(req
|
||||
.getInputStream()));
|
||||
|
||||
// Get the root element of the document
|
||||
Element rootElement = document.getDocumentElement();
|
||||
|
||||
lockInfoNode = rootElement;
|
||||
|
||||
if (lockInfoNode != null) {
|
||||
NodeList childList = lockInfoNode.getChildNodes();
|
||||
Node lockScopeNode = null;
|
||||
Node lockTypeNode = null;
|
||||
Node lockOwnerNode = null;
|
||||
|
||||
Node currentNode = null;
|
||||
String nodeName = null;
|
||||
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||
|
||||
nodeName = currentNode.getNodeName();
|
||||
|
||||
if (nodeName.endsWith("locktype")) {
|
||||
lockTypeNode = currentNode;
|
||||
}
|
||||
if (nodeName.endsWith("lockscope")) {
|
||||
lockScopeNode = currentNode;
|
||||
}
|
||||
if (nodeName.endsWith("owner")) {
|
||||
lockOwnerNode = currentNode;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lockScopeNode != null) {
|
||||
String scope = null;
|
||||
childList = lockScopeNode.getChildNodes();
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
scope = currentNode.getNodeName();
|
||||
|
||||
if (scope.endsWith("exclusive")) {
|
||||
_exclusive = true;
|
||||
} else if (scope.equals("shared")) {
|
||||
_exclusive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lockTypeNode != null) {
|
||||
childList = lockTypeNode.getChildNodes();
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
_type = currentNode.getNodeName();
|
||||
|
||||
if (_type.endsWith("write")) {
|
||||
_type = "write";
|
||||
} else if (_type.equals("read")) {
|
||||
_type = "read";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_type == null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lockOwnerNode != null) {
|
||||
childList = lockOwnerNode.getChildNodes();
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||
_lockOwner = currentNode.getFirstChild()
|
||||
.getNodeValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_lockOwner == null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (DOMException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("DOM exception", e);
|
||||
return false;
|
||||
} catch (SAXException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("SAX exception", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ties to read the timeout from request
|
||||
*/
|
||||
private int getTimeout(ITransaction transaction, HttpServletRequest req) {
|
||||
|
||||
int lockDuration = DEFAULT_TIMEOUT;
|
||||
String lockDurationStr = req.getHeader("Timeout");
|
||||
|
||||
if (lockDurationStr == null) {
|
||||
lockDuration = DEFAULT_TIMEOUT;
|
||||
} else {
|
||||
int commaPos = lockDurationStr.indexOf(',');
|
||||
// if multiple timeouts, just use the first one
|
||||
if (commaPos != -1) {
|
||||
lockDurationStr = lockDurationStr.substring(0, commaPos);
|
||||
}
|
||||
if (lockDurationStr.startsWith("Second-")) {
|
||||
lockDuration = new Integer(lockDurationStr.substring(7))
|
||||
.intValue();
|
||||
} else {
|
||||
if (lockDurationStr.equalsIgnoreCase("infinity")) {
|
||||
lockDuration = MAX_TIMEOUT;
|
||||
} else {
|
||||
try {
|
||||
lockDuration = new Integer(lockDurationStr).intValue();
|
||||
} catch (NumberFormatException e) {
|
||||
lockDuration = MAX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lockDuration <= 0) {
|
||||
lockDuration = DEFAULT_TIMEOUT;
|
||||
}
|
||||
if (lockDuration > MAX_TIMEOUT) {
|
||||
lockDuration = MAX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return lockDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the response XML with all lock information
|
||||
*/
|
||||
private void generateXMLReport(ITransaction transaction,
|
||||
HttpServletResponse resp, LockedObject lo) throws IOException {
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_OK);
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
|
||||
XMLWriter generatedXML = new XMLWriter(resp.getWriter(), namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeProperty("DAV::" + _type);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
if (_exclusive) {
|
||||
generatedXML.writeProperty("DAV::exclusive");
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::shared");
|
||||
}
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
int depth = lo.getLockDepth();
|
||||
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
|
||||
if (depth == INFINITY) {
|
||||
generatedXML.writeText("Infinity");
|
||||
} else {
|
||||
generatedXML.writeText(String.valueOf(depth));
|
||||
}
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText(_lockOwner);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
|
||||
|
||||
long timeout = lo.getTimeoutMillis();
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
|
||||
generatedXML.writeText("Second-" + timeout / 1000);
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
|
||||
|
||||
String lockToken = lo.getID();
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText("opaquelocktoken:" + lockToken);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
|
||||
resp.addHeader("Lock-Token", "<opaquelocktoken:" + lockToken + ">");
|
||||
|
||||
generatedXML.sendData();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the lock for a Mac OS Finder client
|
||||
*/
|
||||
private void doMacLockRequestWorkaround(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws LockFailedException, IOException {
|
||||
LockedObject lo;
|
||||
int depth = getDepth(req);
|
||||
int lockDuration = getTimeout(transaction, req);
|
||||
if (lockDuration < 0 || lockDuration > MAX_TIMEOUT)
|
||||
lockDuration = DEFAULT_TIMEOUT;
|
||||
|
||||
boolean lockSuccess = false;
|
||||
lockSuccess = _resourceLocks.exclusiveLock(transaction, _path,
|
||||
_lockOwner, depth, lockDuration);
|
||||
|
||||
if (lockSuccess) {
|
||||
// Locks successfully placed - return information about
|
||||
lo = _resourceLocks.getLockedObjectByPath(transaction, _path);
|
||||
if (lo != null) {
|
||||
generateXMLReport(transaction, resp, lo);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
// Locking was not successful
|
||||
sendLockFailError(transaction, req, resp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an error report to the client
|
||||
*/
|
||||
private void sendLockFailError(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException {
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
errorList.put(_path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.locking.IResourceLocks;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
|
||||
public class DoMkcol extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoMkcol.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoMkcol(IWebdavStore store, IResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (!_readOnly) {
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(getCleanPath(path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
// TODO remove
|
||||
LOG
|
||||
.trace("MkCol on locked resource (parentPath) not executable!"
|
||||
+ "\n Sending SC_FORBIDDEN (403) error response!");
|
||||
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempLockOwner = "doMkcol" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject parentSo, so = null;
|
||||
try {
|
||||
parentSo = _store.getStoredObject(transaction, parentPath);
|
||||
if (parentSo == null) {
|
||||
// parent not exists
|
||||
resp.sendError(WebdavStatus.SC_CONFLICT);
|
||||
return;
|
||||
}
|
||||
if (parentPath != null && parentSo.isFolder()) {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
_store.createFolder(transaction, path);
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else {
|
||||
// object already exists
|
||||
if (so.isNullResource()) {
|
||||
|
||||
LockedObject nullResourceLo = _resourceLocks
|
||||
.getLockedObjectByPath(transaction,
|
||||
path);
|
||||
if (nullResourceLo == null) {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
String nullResourceLockToken = nullResourceLo
|
||||
.getID();
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null)
|
||||
lockToken = lockTokens[0];
|
||||
else {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
if (lockToken.equals(nullResourceLockToken)) {
|
||||
so.setNullResource(false);
|
||||
so.setFolder(true);
|
||||
|
||||
String[] nullResourceLockOwners = nullResourceLo
|
||||
.getOwner();
|
||||
String owner = null;
|
||||
if (nullResourceLockOwners != null)
|
||||
owner = nullResourceLockOwners[0];
|
||||
|
||||
if (_resourceLocks.unlock(transaction,
|
||||
lockToken, owner)) {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO remove
|
||||
LOG
|
||||
.trace("MkCol on lock-null-resource with wrong lock-token!"
|
||||
+ "\n Sending multistatus error report!");
|
||||
|
||||
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
|
||||
} else {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (parentPath != null && parentSo.isResource()) {
|
||||
// TODO remove
|
||||
LOG
|
||||
.trace("MkCol on resource is not executable"
|
||||
+ "\n Sending SC_METHOD_NOT_ALLOWED (405) error response!");
|
||||
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(parentSo);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
public class DoMove extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoMove.class);
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private DoDelete _doDelete;
|
||||
private DoCopy _doCopy;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoMove(IWebdavStore _store, ResourceLocks resourceLocks, DoDelete doDelete,
|
||||
DoCopy doCopy, boolean readOnly) {
|
||||
this._store = _store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_doDelete = doDelete;
|
||||
_doCopy = doCopy;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
if (!_readOnly) {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
String sourcePath = getRelativePath(req);
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, sourcePath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return;
|
||||
}
|
||||
|
||||
String destinationPath = parseDestinationHeader(req, resp);
|
||||
if (destinationPath == null) {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks,
|
||||
destinationPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempLockOwner = "doMove" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, sourcePath, tempLockOwner,
|
||||
false, 0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
boolean ok = _store.moveObject(transaction, destinationPath, sourcePath);
|
||||
if (!ok) {
|
||||
if (_doCopy.copyResource(transaction, req, resp)) {
|
||||
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
_doDelete.deleteResource(transaction, sourcePath,
|
||||
errorList, req, resp);
|
||||
if (!errorList.isEmpty()) {
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
sourcePath, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
errorList.put(req.getHeader("Destination"),
|
||||
WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.IMethodExecutor;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
|
||||
public class DoNotImplemented implements IMethodExecutor {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoNotImplemented.class);
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoNotImplemented(boolean readOnly) {
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
LOG.trace("-- " + req.getMethod());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} else
|
||||
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
public class DoOptions extends DeterminableMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoOptions.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
|
||||
public DoOptions(IWebdavStore store, ResourceLocks resLocks) {
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
String tempLockOwner = "doOptions" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
String path = getRelativePath(req);
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject so = null;
|
||||
try {
|
||||
resp.addHeader("DAV", "1, 2");
|
||||
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
String methodsAllowed = determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.addHeader("MS-Author-Via", "DAV");
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,627 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
import net.sf.webdav.IMimeTyper;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.fromcatalina.URLEncoder;
|
||||
import net.sf.webdav.fromcatalina.XMLHelper;
|
||||
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
public class DoPropfind extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoPropfind.class);
|
||||
|
||||
/**
|
||||
* Array containing the safe characters set.
|
||||
*/
|
||||
protected static URLEncoder URL_ENCODER;
|
||||
|
||||
/**
|
||||
* PROPFIND - Specify a property mask.
|
||||
*/
|
||||
private static final int FIND_BY_PROPERTY = 0;
|
||||
|
||||
/**
|
||||
* PROPFIND - Display all properties.
|
||||
*/
|
||||
private static final int FIND_ALL_PROP = 1;
|
||||
|
||||
/**
|
||||
* PROPFIND - Return property names.
|
||||
*/
|
||||
private static final int FIND_PROPERTY_NAMES = 2;
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private IMimeTyper _mimeTyper;
|
||||
|
||||
private int _depth;
|
||||
|
||||
public DoPropfind(IWebdavStore store, ResourceLocks resLocks,
|
||||
IMimeTyper mimeTyper) {
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
_mimeTyper = mimeTyper;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
// Retrieve the resources
|
||||
String path = getCleanPath(getRelativePath(req));
|
||||
String tempLockOwner = "doPropfind" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
_depth = getDepth(req);
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false,
|
||||
_depth, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
|
||||
StoredObject so = null;
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<String> properties = null;
|
||||
path = getCleanPath(getRelativePath(req));
|
||||
|
||||
int propertyFindType = FIND_ALL_PROP;
|
||||
Node propNode = null;
|
||||
|
||||
if (req.getContentLength() > 0) {
|
||||
DocumentBuilder documentBuilder = getDocumentBuilder();
|
||||
try {
|
||||
Document document = documentBuilder
|
||||
.parse(new InputSource(req.getInputStream()));
|
||||
// Get the root element of the document
|
||||
Element rootElement = document.getDocumentElement();
|
||||
|
||||
propNode = XMLHelper
|
||||
.findSubElement(rootElement, "prop");
|
||||
if (propNode != null) {
|
||||
propertyFindType = FIND_BY_PROPERTY;
|
||||
} else if (XMLHelper.findSubElement(rootElement,
|
||||
"propname") != null) {
|
||||
propertyFindType = FIND_PROPERTY_NAMES;
|
||||
} else if (XMLHelper.findSubElement(rootElement,
|
||||
"allprop") != null) {
|
||||
propertyFindType = FIND_ALL_PROP;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// no content, which means it is a allprop request
|
||||
propertyFindType = FIND_ALL_PROP;
|
||||
}
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
if (propertyFindType == FIND_BY_PROPERTY) {
|
||||
propertyFindType = 0;
|
||||
properties = XMLHelper.getPropertiesFromXML(propNode);
|
||||
}
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
|
||||
// Create multistatus object
|
||||
XMLWriter generatedXML = new XMLWriter(resp.getWriter(),
|
||||
namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||
if (_depth == 0) {
|
||||
parseProperties(transaction, req, generatedXML, path,
|
||||
propertyFindType, properties, _mimeTyper
|
||||
.getMimeType(transaction, path));
|
||||
} else {
|
||||
recursiveParseProperties(transaction, path, req,
|
||||
generatedXML, propertyFindType, properties, _depth,
|
||||
_mimeTyper.getMimeType(transaction, path));
|
||||
}
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.sendData();
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
LOG.warn("Sending internal error!");
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} catch (ServletException e) {
|
||||
e.printStackTrace(); // To change body of catch statement use
|
||||
// File | Settings | File Templates.
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* goes recursive through all folders. used by propfind
|
||||
*
|
||||
* @param currentPath
|
||||
* the current path
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param generatedXML
|
||||
* @param propertyFindType
|
||||
* @param properties
|
||||
* @param depth
|
||||
* depth of the propfind
|
||||
* @throws IOException
|
||||
* if an error in the underlying store occurs
|
||||
*/
|
||||
private void recursiveParseProperties(ITransaction transaction,
|
||||
String currentPath, HttpServletRequest req, XMLWriter generatedXML,
|
||||
int propertyFindType, Vector<String> properties, int depth,
|
||||
String mimeType) throws WebdavException {
|
||||
|
||||
parseProperties(transaction, req, generatedXML, currentPath,
|
||||
propertyFindType, properties, mimeType);
|
||||
|
||||
if (depth > 0) {
|
||||
// no need to get name if depth is already zero
|
||||
String[] names = _store.getChildrenNames(transaction, currentPath);
|
||||
names = names == null ? new String[] {} : names;
|
||||
String newPath = null;
|
||||
|
||||
for (String name : names) {
|
||||
newPath = currentPath;
|
||||
if (!(newPath.endsWith("/"))) {
|
||||
newPath += "/";
|
||||
}
|
||||
newPath += name;
|
||||
recursiveParseProperties(transaction, newPath, req,
|
||||
generatedXML, propertyFindType, properties, depth - 1,
|
||||
mimeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propfind helper method.
|
||||
*
|
||||
* @param req
|
||||
* The servlet request
|
||||
* @param generatedXML
|
||||
* XML response to the Propfind request
|
||||
* @param path
|
||||
* Path of the current resource
|
||||
* @param type
|
||||
* Propfind type
|
||||
* @param propertiesVector
|
||||
* If the propfind type is find properties by name, then this Vector
|
||||
* contains those properties
|
||||
*/
|
||||
private void parseProperties(ITransaction transaction,
|
||||
HttpServletRequest req, XMLWriter generatedXML, String path,
|
||||
int type, Vector<String> propertiesVector, String mimeType)
|
||||
throws WebdavException {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
|
||||
boolean isFolder = so.isFolder();
|
||||
final String creationdate = creationDateFormat(so.getCreationDate());
|
||||
final String lastModified = lastModifiedDateFormat(so.getLastModified());
|
||||
String resourceLength = String.valueOf(so.getResourceLength());
|
||||
|
||||
// ResourceInfo resourceInfo = new ResourceInfo(path, resources);
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
|
||||
+ WebdavStatus.getStatusText(WebdavStatus.SC_OK));
|
||||
|
||||
// Generating href element
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
|
||||
String href = req.getContextPath();
|
||||
String servletPath = req.getServletPath();
|
||||
if (servletPath != null) {
|
||||
if ((href.endsWith("/")) && (servletPath.startsWith("/")))
|
||||
href += servletPath.substring(1);
|
||||
else
|
||||
href += servletPath;
|
||||
}
|
||||
if ((href.endsWith("/")) && (path.startsWith("/")))
|
||||
href += path.substring(1);
|
||||
else
|
||||
href += path;
|
||||
if ((isFolder) && (!href.endsWith("/")))
|
||||
href += "/";
|
||||
|
||||
generatedXML.writeText(rewriteUrl(href));
|
||||
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
|
||||
String resourceName = path;
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
if (lastSlash != -1)
|
||||
resourceName = resourceName.substring(lastSlash + 1);
|
||||
|
||||
switch (type) {
|
||||
|
||||
case FIND_ALL_PROP:
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeProperty("DAV::creationdate", creationdate);
|
||||
generatedXML.writeElement("DAV::displayname", XMLWriter.OPENING);
|
||||
generatedXML.writeData(resourceName);
|
||||
generatedXML.writeElement("DAV::displayname", XMLWriter.CLOSING);
|
||||
if (!isFolder) {
|
||||
generatedXML
|
||||
.writeProperty("DAV::getlastmodified", lastModified);
|
||||
generatedXML.writeProperty("DAV::getcontentlength",
|
||||
resourceLength);
|
||||
String contentType = mimeType;
|
||||
if (contentType != null) {
|
||||
generatedXML.writeProperty("DAV::getcontenttype",
|
||||
contentType);
|
||||
}
|
||||
generatedXML.writeProperty("DAV::getetag", getETag(so));
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.NO_CONTENT);
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::collection",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.CLOSING);
|
||||
}
|
||||
|
||||
writeSupportedLockElements(transaction, generatedXML, path);
|
||||
|
||||
writeLockDiscoveryElements(transaction, generatedXML, path);
|
||||
|
||||
generatedXML.writeProperty("DAV::source", "");
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
break;
|
||||
|
||||
case FIND_PROPERTY_NAMES:
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
generatedXML
|
||||
.writeElement("DAV::creationdate", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::displayname", XMLWriter.NO_CONTENT);
|
||||
if (!isFolder) {
|
||||
generatedXML.writeElement("DAV::getcontentlanguage",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getcontentlength",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getcontenttype",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getetag", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getlastmodified",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
generatedXML
|
||||
.writeElement("DAV::resourcetype", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::supportedlock",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::source", XMLWriter.NO_CONTENT);
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
break;
|
||||
|
||||
case FIND_BY_PROPERTY:
|
||||
|
||||
Vector<String> propertiesNotFound = new Vector<String>();
|
||||
|
||||
// Parse the list of properties
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
Enumeration<String> properties = propertiesVector.elements();
|
||||
|
||||
while (properties.hasMoreElements()) {
|
||||
|
||||
String property = (String) properties.nextElement();
|
||||
|
||||
if (property.equals("DAV::creationdate")) {
|
||||
generatedXML.writeProperty("DAV::creationdate",
|
||||
creationdate);
|
||||
} else if (property.equals("DAV::displayname")) {
|
||||
generatedXML.writeElement("DAV::displayname",
|
||||
XMLWriter.OPENING);
|
||||
generatedXML.writeData(resourceName);
|
||||
generatedXML.writeElement("DAV::displayname",
|
||||
XMLWriter.CLOSING);
|
||||
} else if (property.equals("DAV::getcontentlanguage")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::getcontentlanguage",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
} else if (property.equals("DAV::getcontentlength")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getcontentlength",
|
||||
resourceLength);
|
||||
}
|
||||
} else if (property.equals("DAV::getcontenttype")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getcontenttype",
|
||||
mimeType);
|
||||
}
|
||||
} else if (property.equals("DAV::getetag")) {
|
||||
if (isFolder || so.isNullResource()) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getetag", getETag(so));
|
||||
}
|
||||
} else if (property.equals("DAV::getlastmodified")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getlastmodified",
|
||||
lastModified);
|
||||
}
|
||||
} else if (property.equals("DAV::resourcetype")) {
|
||||
if (isFolder) {
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::collection",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.CLOSING);
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
} else if (property.equals("DAV::source")) {
|
||||
generatedXML.writeProperty("DAV::source", "");
|
||||
} else if (property.equals("DAV::supportedlock")) {
|
||||
|
||||
writeSupportedLockElements(transaction, generatedXML, path);
|
||||
|
||||
} else if (property.equals("DAV::lockdiscovery")) {
|
||||
|
||||
writeLockDiscoveryElements(transaction, generatedXML, path);
|
||||
|
||||
} else {
|
||||
propertiesNotFound.addElement(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
Enumeration<String> propertiesNotFoundList = propertiesNotFound
|
||||
.elements();
|
||||
|
||||
if (propertiesNotFoundList.hasMoreElements()) {
|
||||
|
||||
status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
|
||||
+ " "
|
||||
+ WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND));
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
while (propertiesNotFoundList.hasMoreElements()) {
|
||||
generatedXML.writeElement((String) propertiesNotFoundList
|
||||
.nextElement(), XMLWriter.NO_CONTENT);
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||
|
||||
so = null;
|
||||
}
|
||||
|
||||
private void writeSupportedLockElements(ITransaction transaction,
|
||||
XMLWriter generatedXML, String path) {
|
||||
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
path);
|
||||
|
||||
generatedXML.writeElement("DAV::supportedlock", XMLWriter.OPENING);
|
||||
|
||||
if (lo == null) {
|
||||
// both locks (shared/exclusive) can be granted
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::exclusive", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||
|
||||
} else {
|
||||
// LockObject exists, checking lock state
|
||||
// if an exclusive lock exists, no further lock is possible
|
||||
if (lo.isShared()) {
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::" + lo.getType(),
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||
}
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::supportedlock", XMLWriter.CLOSING);
|
||||
|
||||
lo = null;
|
||||
}
|
||||
|
||||
private void writeLockDiscoveryElements(ITransaction transaction,
|
||||
XMLWriter generatedXML, String path) {
|
||||
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
path);
|
||||
|
||||
if (lo != null && !lo.hasExpired()) {
|
||||
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeProperty("DAV::" + lo.getType());
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
if (lo.isExclusive()) {
|
||||
generatedXML.writeProperty("DAV::exclusive");
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::shared");
|
||||
}
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
|
||||
if (_depth == INFINITY) {
|
||||
generatedXML.writeText("Infinity");
|
||||
} else {
|
||||
generatedXML.writeText(String.valueOf(_depth));
|
||||
}
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
|
||||
|
||||
String[] owners = lo.getOwner();
|
||||
if (owners != null) {
|
||||
for (int i = 0; i < owners.length; i++) {
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText(owners[i]);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
|
||||
}
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.NO_CONTENT);
|
||||
}
|
||||
|
||||
int timeout = (int) (lo.getTimeoutMillis() / 1000);
|
||||
String timeoutStr = new Integer(timeout).toString();
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
|
||||
generatedXML.writeText("Second-" + timeoutStr);
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
|
||||
|
||||
String lockToken = lo.getID();
|
||||
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText("opaquelocktoken:" + lockToken);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
|
||||
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::lockdiscovery",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
|
||||
lo = null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.fromcatalina.XMLHelper;
|
||||
import net.sf.webdav.fromcatalina.XMLWriter;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
import net.sf.webdav.locking.ResourceLocks;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
public class DoProppatch extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoProppatch.class);
|
||||
|
||||
private boolean _readOnly;
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
|
||||
public DoProppatch(IWebdavStore store, ResourceLocks resLocks,
|
||||
boolean readOnly) {
|
||||
_readOnly = readOnly;
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(getCleanPath(path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
// TODO for now, PROPPATCH just sends a valid response, stating that
|
||||
// everything is fine, but doesn't do anything.
|
||||
|
||||
// Retrieve the resources
|
||||
String tempLockOwner = "doProppatch" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject so = null;
|
||||
LockedObject lo = null;
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
getCleanPath(path));
|
||||
|
||||
if (so == null) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
// we do not to continue since there is no root
|
||||
// resource
|
||||
}
|
||||
|
||||
if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
boolean lockTokenMatchesIfHeader = (lockTokens != null && lockTokens[0].equals(lo.getID()));
|
||||
if (lo != null && lo.isExclusive() && !lockTokenMatchesIfHeader) {
|
||||
// Object on specified path is LOCKED
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
errorList.put(path, new Integer(WebdavStatus.SC_LOCKED));
|
||||
sendReport(req, resp, errorList);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> toset = null;
|
||||
List<String> toremove = null;
|
||||
List<String> tochange = new Vector<String>();
|
||||
// contains all properties from
|
||||
// toset and toremove
|
||||
|
||||
path = getCleanPath(getRelativePath(req));
|
||||
|
||||
Node tosetNode = null;
|
||||
Node toremoveNode = null;
|
||||
|
||||
if (req.getContentLength() != 0) {
|
||||
DocumentBuilder documentBuilder = getDocumentBuilder();
|
||||
try {
|
||||
Document document = documentBuilder
|
||||
.parse(new InputSource(req.getInputStream()));
|
||||
// Get the root element of the document
|
||||
Element rootElement = document.getDocumentElement();
|
||||
|
||||
tosetNode = XMLHelper.findSubElement(XMLHelper
|
||||
.findSubElement(rootElement, "set"), "prop");
|
||||
toremoveNode = XMLHelper.findSubElement(XMLHelper
|
||||
.findSubElement(rootElement, "remove"), "prop");
|
||||
} catch (Exception e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// no content: error
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
if (tosetNode != null) {
|
||||
toset = XMLHelper.getPropertiesFromXML(tosetNode);
|
||||
tochange.addAll(toset);
|
||||
}
|
||||
|
||||
if (toremoveNode != null) {
|
||||
toremove = XMLHelper.getPropertiesFromXML(toremoveNode);
|
||||
tochange.addAll(toremove);
|
||||
}
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
|
||||
// Create multistatus object
|
||||
XMLWriter generatedXML = new XMLWriter(resp.getWriter(),
|
||||
namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
|
||||
+ " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK));
|
||||
|
||||
// Generating href element
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
|
||||
String href = req.getContextPath();
|
||||
if ((href.endsWith("/")) && (path.startsWith("/")))
|
||||
href += path.substring(1);
|
||||
else
|
||||
href += path;
|
||||
if ((so.isFolder()) && (!href.endsWith("/")))
|
||||
href += "/";
|
||||
|
||||
generatedXML.writeText(rewriteUrl(href));
|
||||
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
|
||||
for (Iterator<String> iter = tochange.iterator(); iter
|
||||
.hasNext();) {
|
||||
String property = (String) iter.next();
|
||||
|
||||
generatedXML.writeElement("DAV::propstat",
|
||||
XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
generatedXML.writeElement(property, XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::propstat",
|
||||
XMLWriter.CLOSING);
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.sendData();
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} catch (ServletException e) {
|
||||
e.printStackTrace(); // To change body of catch statement use
|
||||
// File | Settings | File Templates.
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.exceptions.AccessDeniedException;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.exceptions.WebdavException;
|
||||
import net.sf.webdav.locking.IResourceLocks;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
|
||||
public class DoPut extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoPut.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
private boolean _lazyFolderCreationOnPut;
|
||||
|
||||
private String _userAgent;
|
||||
|
||||
public DoPut(IWebdavStore store, IResourceLocks resLocks, boolean readOnly,
|
||||
boolean lazyFolderCreationOnPut) {
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
_readOnly = readOnly;
|
||||
_lazyFolderCreationOnPut = lazyFolderCreationOnPut;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (!_readOnly) {
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(path);
|
||||
|
||||
_userAgent = req.getHeader("User-Agent");
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
String tempLockOwner = "doPut" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject parentSo, so = null;
|
||||
try {
|
||||
parentSo = _store.getStoredObject(transaction, parentPath);
|
||||
if (parentPath != null && parentSo != null
|
||||
&& parentSo.isResource()) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
|
||||
} else if (parentPath != null && parentSo == null
|
||||
&& _lazyFolderCreationOnPut) {
|
||||
_store.createFolder(transaction, parentPath);
|
||||
|
||||
} else if (parentPath != null && parentSo == null
|
||||
&& !_lazyFolderCreationOnPut) {
|
||||
errorList.put(parentPath, WebdavStatus.SC_NOT_FOUND);
|
||||
sendReport(req, resp, errorList);
|
||||
return;
|
||||
}
|
||||
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
|
||||
if (so == null) {
|
||||
_store.createResource(transaction, path);
|
||||
// resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else {
|
||||
// This has already been created, just update the data
|
||||
if (so.isNullResource()) {
|
||||
|
||||
LockedObject nullResourceLo = _resourceLocks
|
||||
.getLockedObjectByPath(transaction, path);
|
||||
if (nullResourceLo == null) {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
String nullResourceLockToken = nullResourceLo
|
||||
.getID();
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null) {
|
||||
lockToken = lockTokens[0];
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
if (lockToken.equals(nullResourceLockToken)) {
|
||||
so.setNullResource(false);
|
||||
so.setFolder(false);
|
||||
|
||||
String[] nullResourceLockOwners = nullResourceLo
|
||||
.getOwner();
|
||||
String owner = null;
|
||||
if (nullResourceLockOwners != null)
|
||||
owner = nullResourceLockOwners[0];
|
||||
|
||||
if (!_resourceLocks.unlock(transaction,
|
||||
lockToken, owner)) {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
}
|
||||
}
|
||||
// User-Agent workarounds
|
||||
doUserAgentWorkaround(resp);
|
||||
|
||||
// setting resourceContent
|
||||
long resourceLength = _store
|
||||
.setResourceContent(transaction, path, req
|
||||
.getInputStream(), null, null);
|
||||
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (resourceLength > 0)
|
||||
so.setResourceLength(resourceLength);
|
||||
// Now lets report back what was actually saved
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resp
|
||||
*/
|
||||
private void doUserAgentWorkaround(HttpServletResponse resp) {
|
||||
if (_userAgent != null && _userAgent.indexOf("WebDAVFS") != -1
|
||||
&& _userAgent.indexOf("Transmit") == -1) {
|
||||
LOG.trace("DoPut.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else if (_userAgent != null && _userAgent.indexOf("Transmit") != -1) {
|
||||
// Transmit also uses WEBDAVFS 1.x.x but crashes
|
||||
// with SC_CREATED response
|
||||
LOG.trace("DoPut.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package net.sf.webdav.methods;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.sf.webdav.StoredObject;
|
||||
import net.sf.webdav.ITransaction;
|
||||
import net.sf.webdav.WebdavStatus;
|
||||
import net.sf.webdav.IWebdavStore;
|
||||
import net.sf.webdav.exceptions.LockFailedException;
|
||||
import net.sf.webdav.locking.IResourceLocks;
|
||||
import net.sf.webdav.locking.LockedObject;
|
||||
|
||||
public class DoUnlock extends DeterminableMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoUnlock.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoUnlock(IWebdavStore store, IResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
} else {
|
||||
|
||||
String path = getRelativePath(req);
|
||||
String tempLockOwner = "doUnlock" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
try {
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner,
|
||||
false, 0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
|
||||
String lockId = getLockIdFromLockTokenHeader(req);
|
||||
LockedObject lo;
|
||||
if (lockId != null
|
||||
&& ((lo = _resourceLocks.getLockedObjectByID(
|
||||
transaction, lockId)) != null)) {
|
||||
|
||||
String[] owners = lo.getOwner();
|
||||
String owner = null;
|
||||
if (lo.isShared()) {
|
||||
// more than one owner is possible
|
||||
if (owners != null) {
|
||||
for (int i = 0; i < owners.length; i++) {
|
||||
// remove owner from LockedObject
|
||||
lo.removeLockedObjectOwner(owners[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// exclusive, only one lock owner
|
||||
if (owners != null)
|
||||
owner = owners[0];
|
||||
else
|
||||
owner = null;
|
||||
}
|
||||
|
||||
if (_resourceLocks.unlock(transaction, lockId, owner)) {
|
||||
StoredObject so = _store.getStoredObject(
|
||||
transaction, path);
|
||||
if (so.isNullResource()) {
|
||||
_store.removeObject(transaction, path);
|
||||
}
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
} else {
|
||||
LOG.trace("DoUnlock failure at " + lo.getPath());
|
||||
resp.sendError(WebdavStatus.SC_METHOD_FAILURE);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
} catch (LockFailedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
#logging.level.net.sf.webdav=trace
|
|
@ -0,0 +1,13 @@
|
|||
package com.github.zxbu.webdavteambition;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class WebdavTeambitionApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue