zhouxin5253@163.com
4 years ago
commit
21281d05fe
68 changed files with 9430 additions and 0 deletions
@ -0,0 +1,33 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,5 @@
|
||||
package com.github.zxbu.webdavteambition.model; |
||||
|
||||
public enum FileType { |
||||
folder, file; |
||||
} |
@ -0,0 +1,51 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,4 @@
|
||||
package com.github.zxbu.webdavteambition.model; |
||||
|
||||
public class NodeQuery extends BaseQuery { |
||||
} |
@ -0,0 +1,40 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,9 @@
|
||||
package net.sf.webdav; |
||||
|
||||
import java.security.Principal; |
||||
|
||||
public interface ITransaction { |
||||
|
||||
Principal getPrincipal(); |
||||
|
||||
} |
@ -0,0 +1,223 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1 @@
|
||||
#logging.level.net.sf.webdav=trace |
@ -0,0 +1,13 @@
@@ -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