/*
* ====================================================================
* Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.server.dav.handlers;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.internal.delta.SVNDeltaReader;
import org.tmatesoft.svn.core.internal.io.dav.DAVElement;
import org.tmatesoft.svn.core.internal.io.dav.handlers.BasicDAVHandler;
import org.tmatesoft.svn.core.internal.io.dav.http.HTTPHeader;
import org.tmatesoft.svn.core.internal.io.fs.FSCommitter;
import org.tmatesoft.svn.core.internal.io.fs.FSDeltaConsumer;
import org.tmatesoft.svn.core.internal.io.fs.FSFS;
import org.tmatesoft.svn.core.internal.io.fs.FSRevisionNode;
import org.tmatesoft.svn.core.internal.io.fs.FSRevisionRoot;
import org.tmatesoft.svn.core.internal.io.fs.FSRoot;
import org.tmatesoft.svn.core.internal.io.fs.FSTransactionInfo;
import org.tmatesoft.svn.core.internal.io.fs.FSTransactionRoot;
import org.tmatesoft.svn.core.internal.server.dav.DAVAutoVersion;
import org.tmatesoft.svn.core.internal.server.dav.DAVConfig;
import org.tmatesoft.svn.core.internal.server.dav.DAVDepth;
import org.tmatesoft.svn.core.internal.server.dav.DAVException;
import org.tmatesoft.svn.core.internal.server.dav.DAVIFHeader;
import org.tmatesoft.svn.core.internal.server.dav.DAVIFState;
import org.tmatesoft.svn.core.internal.server.dav.DAVIFStateType;
import org.tmatesoft.svn.core.internal.server.dav.DAVLock;
import org.tmatesoft.svn.core.internal.server.dav.DAVLockScope;
import org.tmatesoft.svn.core.internal.server.dav.DAVPathUtil;
import org.tmatesoft.svn.core.internal.server.dav.DAVRepositoryManager;
import org.tmatesoft.svn.core.internal.server.dav.DAVResource;
import org.tmatesoft.svn.core.internal.server.dav.DAVResourceHelper;
import org.tmatesoft.svn.core.internal.server.dav.DAVResourceKind;
import org.tmatesoft.svn.core.internal.server.dav.DAVResourceState;
import org.tmatesoft.svn.core.internal.server.dav.DAVResourceType;
import org.tmatesoft.svn.core.internal.server.dav.DAVResourceURI;
import org.tmatesoft.svn.core.internal.server.dav.DAVServlet;
import org.tmatesoft.svn.core.internal.server.dav.DAVServletUtil;
import org.tmatesoft.svn.core.internal.server.dav.DAVURIInfo;
import org.tmatesoft.svn.core.internal.server.dav.DAVVersionResourceHelper;
import org.tmatesoft.svn.core.internal.server.dav.DAVWorkingResourceHelper;
import org.tmatesoft.svn.core.internal.server.dav.DAVXMLUtil;
import org.tmatesoft.svn.core.internal.util.CountingInputStream;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashSet;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNUUIDGenerator;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.io.ISVNDeltaConsumer;
import org.tmatesoft.svn.core.io.SVNCapability;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
import org.tmatesoft.svn.util.Version;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.tmatesoft.svn.core.internal.io.dav.http.XMLReader;
/**
* @author TMate Software Ltd.
* @version 1.2.0
*/
public abstract class ServletDAVHandler extends BasicDAVHandler {
public static final int SC_MULTISTATUS = 207;
public static final int SC_HTTP_LOCKED = 423;
public static final int SC_FAILED_DEPENDANCY = 424;
//some flag constants
public static final int DAV_VALIDATE_RESOURCE = 0x0010;
public static final int DAV_VALIDATE_PARENT = 0x0020;
public static final int DAV_VALIDATE_ADD_LD = 0x0040;
public static final int DAV_VALIDATE_USE_424 = 0x0080;
public static final int DAV_VALIDATE_IS_PARENT = 0x0100;
public static final int DAV_MODE_WRITE_TRUNC = 0;
public static final int DAV_MODE_WRITE_SEEKABLE = 1;
protected static final String CAPABILITY_YES = "yes";
protected static final String CAPABILITY_NO = "no";
protected static final String HTTP_STATUS_OK_LINE = "HTTP/1.1 200 OK";
protected static final String HTTP_NOT_FOUND_LINE = "HTTP/1.1 404 NOT FOUND";
protected static final String DAV_RESPONSE_BODY_1 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>";
protected static final String DAV_RESPONSE_BODY_2 = "</title>\n</head><body>\n<h1>";
protected static final String DAV_RESPONSE_BODY_3 = "</h1>\n<p>";
protected static final String DAV_RESPONSE_BODY_4 = "</p>\n";
protected static final String DAV_RESPONSE_BODY_5 = "</body></html>\n";
protected static final String DEFAULT_XML_CONTENT_TYPE = "text/xml; charset=\"utf-8\"";
protected static final String UTF8_ENCODING = "UTF-8";
protected static final String BASE64_ENCODING = "base64";
//Specific svn headers
protected static final String SVN_OPTIONS_HEADER = "X-SVN-Options";
protected static final String SVN_DELTA_BASE_HEADER = "X-SVN-VR-Base";
protected static final String SVN_VERSION_NAME_HEADER = "X-SVN-Version-Name";
protected static final String SVN_CREATIONDATE_HEADER = "X-SVN-Creation-Date";
protected static final String SVN_LOCK_OWNER_HEADER = "X-SVN-Lock-Owner";
protected static final String SVN_BASE_FULLTEXT_MD5_HEADER = "X-SVN-Base-Fulltext-MD5";
protected static final String SVN_RESULT_FULLTEXT_MD5_HEADER = "X-SVN-Result-Fulltext-MD5";
//Precondition headers
protected static final String IF_MATCH_HEADER = "If-Match";
protected static final String IF_UNMODIFIED_SINCE_HEADER = "If-Unmodified-Since";
protected static final String IF_NONE_MATCH_HEADER = "If-None-Match";
protected static final String IF_MODIFIED_SINCE_HEADER = "If-Modified-Since";
protected static final String ETAG_HEADER = "ETag";
protected static final String RANGE_HEADER = "Range";
//Common HTTP headers
protected static final String DEPTH_HEADER = "Depth";
protected static final String VARY_HEADER = "Vary";
protected static final String LAST_MODIFIED_HEADER = "Last-Modified";
protected static final String LABEL_HEADER = "Label";
protected static final String USER_AGENT_HEADER = "User-Agent";
protected static final String CONNECTION_HEADER = "Connection";
protected static final String DATE_HEADER = "Date";
protected static final String KEEP_ALIVE_HEADER = "Keep-Alive";
protected static final String ACCEPT_RANGES_HEADER = "Accept-Ranges";
protected static final String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
protected static final String CACHE_CONTROL_HEADER = "Cache-Control";
//Common xml attributes
protected static final String NAME_ATTR = "name";
protected static final String ENCODING_ATTR = "encoding";
protected static final String NAMESPACE_ATTR = "namespace";
protected static final String DIFF_VERSION_1 = "svndiff1";
protected static final String DIFF_VERSION = "svndiff";
protected static final String ACCEPT_RANGES_DEFAULT_VALUE = "bytes";
protected static final String CACHE_CONTROL_VALUE = "no-cache";
private static final Pattern COMMA = Pattern.compile(",");
//Report related stuff DAVOptionsHandler uses
protected static Set REPORT_ELEMENTS = new SVNHashSet();
protected static final DAVElement UPDATE_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "update-report");
protected static final DAVElement LOG_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "log-report");
protected static final DAVElement DATED_REVISIONS_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "dated-rev-report");
protected static final DAVElement GET_LOCATIONS = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "get-locations");
protected static final DAVElement GET_LOCATION_SEGMENTS = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "get-location-segments");
protected static final DAVElement FILE_REVISIONS_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "file-revs-report");
protected static final DAVElement GET_LOCKS_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "get-locks-report");
protected static final DAVElement REPLAY_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "replay-report");
protected static final DAVElement MERGEINFO_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "mergeinfo-report");
protected static final DAVElement GET_DELETED_REVISION_REPORT = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "get-deleted-rev-report");
protected static final DAVElement LOCK_PATH_ELEM = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "lock-path");
protected static final DAVElement LOCK_TOKEN_ELEM = DAVElement.getElement(DAVElement.SVN_NAMESPACE, "lock-token");
private static SAXParserFactory ourSAXParserFactory;
private SAXParser mySAXParser;
private DAVRepositoryManager myRepositoryManager;
private HttpServletRequest myRequest;
private HttpServletResponse myResponse;
private FSCommitter myCommitter;
private FSDeltaConsumer myDeltaConsumer;
static {
REPORT_ELEMENTS.add(UPDATE_REPORT);
REPORT_ELEMENTS.add(LOG_REPORT);
REPORT_ELEMENTS.add(DATED_REVISIONS_REPORT);
REPORT_ELEMENTS.add(GET_LOCATIONS);
REPORT_ELEMENTS.add(FILE_REVISIONS_REPORT);
REPORT_ELEMENTS.add(GET_LOCKS_REPORT);
REPORT_ELEMENTS.add(REPLAY_REPORT);
REPORT_ELEMENTS.add(MERGEINFO_REPORT);
}
protected static final Map OUR_LIVE_PROPS = new HashMap();
protected static final Map OUR_CORE_LIVE_PROPS = new HashMap();
static {
OUR_LIVE_PROPS.put(DAVElement.GET_CONTENT_LENGTH, new LivePropertySpecification(DAVElement.GET_CONTENT_LENGTH, false, true));
OUR_LIVE_PROPS.put(DAVElement.GET_CONTENT_TYPE, new LivePropertySpecification(DAVElement.GET_CONTENT_TYPE, false, true));
OUR_LIVE_PROPS.put(DAVElement.GET_ETAG, new LivePropertySpecification(DAVElement.GET_ETAG, false, true));
OUR_LIVE_PROPS.put(DAVElement.CREATION_DATE, new LivePropertySpecification(DAVElement.CREATION_DATE, false, true));
OUR_LIVE_PROPS.put(DAVElement.GET_LAST_MODIFIED, new LivePropertySpecification(DAVElement.GET_LAST_MODIFIED, false, true));
OUR_LIVE_PROPS.put(DAVElement.BASELINE_COLLECTION, new LivePropertySpecification(DAVElement.BASELINE_COLLECTION, false, true));
OUR_LIVE_PROPS.put(DAVElement.CHECKED_IN, new LivePropertySpecification(DAVElement.CHECKED_IN, false, true));
OUR_LIVE_PROPS.put(DAVElement.VERSION_CONTROLLED_CONFIGURATION,
new LivePropertySpecification(DAVElement.VERSION_CONTROLLED_CONFIGURATION, false, true));
OUR_LIVE_PROPS.put(DAVElement.VERSION_NAME, new LivePropertySpecification(DAVElement.VERSION_NAME, false, true));
OUR_LIVE_PROPS.put(DAVElement.CREATOR_DISPLAY_NAME, new LivePropertySpecification(DAVElement.CREATOR_DISPLAY_NAME, false, true));
OUR_LIVE_PROPS.put(DAVElement.AUTO_VERSION, new LivePropertySpecification(DAVElement.AUTO_VERSION, false, true));
OUR_LIVE_PROPS.put(DAVElement.BASELINE_RELATIVE_PATH, new LivePropertySpecification(DAVElement.BASELINE_RELATIVE_PATH, false, true));
OUR_LIVE_PROPS.put(DAVElement.MD5_CHECKSUM, new LivePropertySpecification(DAVElement.MD5_CHECKSUM, false, true));
OUR_LIVE_PROPS.put(DAVElement.REPOSITORY_UUID, new LivePropertySpecification(DAVElement.REPOSITORY_UUID, false, true));
OUR_LIVE_PROPS.put(DAVElement.DEADPROP_COUNT, new LivePropertySpecification(DAVElement.DEADPROP_COUNT, false, true));
//TODO: this probably should be later moved to CORE_LIVE_PROPS and shouldn't be actually SVN supported
OUR_LIVE_PROPS.put(DAVElement.COMMENT, new LivePropertySpecification(DAVElement.COMMENT, true, true));
OUR_LIVE_PROPS.put(DAVElement.DISPLAY_NAME, new LivePropertySpecification(DAVElement.DISPLAY_NAME, true, true));
OUR_LIVE_PROPS.put(DAVElement.RESOURCE_TYPE, new LivePropertySpecification(DAVElement.RESOURCE_TYPE, false, true));
OUR_LIVE_PROPS.put(DAVElement.SOURCE, new LivePropertySpecification(DAVElement.SOURCE, true, true));
//TODO: the following three props are supported by DAV itself, should we do that as well?
OUR_CORE_LIVE_PROPS.put(DAVElement.GET_CONTENT_TYPE, new LivePropertySpecification(DAVElement.GET_CONTENT_TYPE, false, false));
OUR_CORE_LIVE_PROPS.put(DAVElement.GET_CONTENT_LANGUAGE, new LivePropertySpecification(DAVElement.GET_CONTENT_LANGUAGE, false, false));
OUR_CORE_LIVE_PROPS.put(DAVElement.LOCK_DISCOVERY, new LivePropertySpecification(DAVElement.LOCK_DISCOVERY, false, false));
OUR_CORE_LIVE_PROPS.put(DAVElement.SUPPORTED_LOCK, new LivePropertySpecification(DAVElement.SUPPORTED_LOCK, false, false));
};
protected ServletDAVHandler(DAVRepositoryManager connector, HttpServletRequest request, HttpServletResponse response) {
myRepositoryManager = connector;
myRequest = request;
myResponse = response;
init();
}
protected DAVRepositoryManager getRepositoryManager() {
return myRepositoryManager;
}
protected DAVConfig getConfig() {
return myRepositoryManager.getDAVConfig();
}
public abstract void execute() throws SVNException;
protected abstract DAVRequest getDAVRequest();
protected void startElement(DAVElement parent, DAVElement element, Attributes attrs) throws SVNException {
getDAVRequest().startElement(parent, element, attrs);
}
protected void endElement(DAVElement parent, DAVElement element, StringBuffer cdata) throws SVNException {
getDAVRequest().endElement(parent, element, cdata);
}
protected DAVResource getRequestedDAVResource(boolean labelAllowed, boolean useCheckedIn) throws SVNException {
return getRequestedDAVResource(labelAllowed, useCheckedIn, null);
}
protected DAVResource getRequestedDAVResource(boolean labelAllowed, boolean useCheckedIn, String pathInfo) throws SVNException {
String label = labelAllowed ? getRequestHeader(LABEL_HEADER) : null;
String versionName = getRequestHeader(SVN_VERSION_NAME_HEADER);
long version = DAVResource.INVALID_REVISION;
try {
version = Long.parseLong(versionName);
} catch (NumberFormatException e) {
}
String clientOptions = getRequestHeader(SVN_OPTIONS_HEADER);
String baseChecksum = getRequestHeader(SVN_BASE_FULLTEXT_MD5_HEADER);
String resultChecksum = getRequestHeader(SVN_RESULT_FULLTEXT_MD5_HEADER);
String deltaBase = getRequestHeader(SVN_DELTA_BASE_HEADER);
String userAgent = getRequestHeader(USER_AGENT_HEADER);
Map clientCapabilities = new HashMap();
clientCapabilities.put(SVNCapability.MERGE_INFO, CAPABILITY_NO);
boolean isSVNClient = false;
if (userAgent != null && (userAgent.startsWith("SVN/") || userAgent.startsWith("SVNKit"))) {
isSVNClient = true;
String clientCapabilitiesList = getRequestHeader(HTTPHeader.DAV_HEADER);
if (clientCapabilitiesList != null) {
for(StringTokenizer tokens = new StringTokenizer(clientCapabilitiesList, ","); tokens.hasMoreTokens();) {
String token = tokens.nextToken().trim();
if (DAVElement.MERGE_INFO_OPTION.equalsIgnoreCase(token)) {
clientCapabilities.put(SVNCapability.MERGE_INFO, CAPABILITY_YES);
}
}
}
}
List lockTokens = getLockTokensList();
DAVResource resource = getRepositoryManager().getRequestedDAVResource(isSVNClient, deltaBase, pathInfo, version, clientOptions,
baseChecksum, resultChecksum, label, useCheckedIn, lockTokens, clientCapabilities);
setDefaultResponseHeaders();
setResponseContentType(DEFAULT_XML_CONTENT_TYPE);
setResponseStatus(HttpServletResponse.SC_OK);
return resource;
}
protected List getLockTokensList() throws DAVException {
List ifHeaders = DAVServletUtil.processIfHeader(getRequestHeader(HTTPHeader.IF_HEADER));
LinkedList lockTokens = null;
if (ifHeaders != null) {
for (Iterator ifHeadersIter = ifHeaders.iterator(); ifHeadersIter.hasNext();) {
DAVIFHeader ifHeader = (DAVIFHeader) ifHeadersIter.next();
List ifStateList = ifHeader.getStateList();
if (ifStateList != null) {
for (Iterator ifStateIter = ifStateList.iterator(); ifStateIter.hasNext();) {
DAVIFState ifState = (DAVIFState) ifStateIter.next();
if (ifState.getCondition() == DAVIFState.IF_CONDITION_NORMAL && ifState.getType() == DAVIFStateType.IF_OPAQUE_LOCK) {
if (lockTokens == null) {
lockTokens = new LinkedList();
}
lockTokens.add(ifState.getLockToken());
}
}
}
}
}
return lockTokens;
}
protected DAVResourceState getResourceState(DAVResource resource) throws SVNException {
if (resource.exists()) {
return DAVResourceState.EXISTS;
}
DAVLockInfoProvider lockInfoProvider = DAVLockInfoProvider.createLockInfoProvider(this, true);
try {
if (lockInfoProvider.hasLocks(resource)) {
return DAVResourceState.LOCK_NULL;
}
} catch (DAVException e) {
SVNDebugLog.getDefaultLog().logFine(SVNLogType.FSFS, "Failed to query lock-null status for " + resource.getResourceURI().getPath());
return DAVResourceState.ERROR;
}
return DAVResourceState.NULL;
}
protected LivePropertySpecification findLiveProperty(DAVElement property) {
String nameSpace = property.getNamespace();
if (!DAVElement.DAV_NAMESPACE.equals(nameSpace) && !DAVElement.SVN_DAV_PROPERTY_NAMESPACE.equals(nameSpace)) {
return null;
}
LivePropertySpecification lps = (LivePropertySpecification) OUR_LIVE_PROPS.get(property);
if (lps == null) {
//search in core props
lps = (LivePropertySpecification) OUR_CORE_LIVE_PROPS.get(property);
}
return lps;
}
protected void validateRequest(DAVResource resource, DAVDepth depth, int flags, DAVLockScope lockScope, String lockToken,
DAVLockInfoProvider lockInfoProvider) throws SVNException {
boolean setETag = false;
String eTag = getRequestHeader(ETAG_HEADER);
if (eTag == null) {
eTag = resource.getETag();
if (eTag != null && eTag.length() > 0) {
setResponseHeader(ETAG_HEADER, eTag);
setETag = true;
}
}
DAVResourceState resourceState = getResourceState(resource);
int result = meetsCondition(resource, resourceState);
if (setETag) {
setResponseHeader(ETAG_HEADER, null);
}
if (result != 0) {
throw new DAVException(null, null, result, null, SVNLogType.NETWORK, Level.FINE, null, null, null, 0, null);
}
LinkedList ifHeaders = DAVServletUtil.processIfHeader(getRequestHeader(HTTPHeader.IF_HEADER));
if (lockToken != null) {
DAVIFState ifState = new DAVIFState(DAVIFState.IF_CONDITION_NORMAL, null, lockToken, DAVIFStateType.IF_OPAQUE_LOCK);
DAVIFHeader ifHeader = new DAVIFHeader(resource.getResourceURI().getRequestURI(), true);
ifHeader.addIFState(ifState);
if (ifHeaders == null) {
ifHeaders = new LinkedList();
}
ifHeaders.addFirst(ifHeader);
}
if (lockInfoProvider == null) {
lockInfoProvider = DAVLockInfoProvider.createLockInfoProvider(this, false);
}
DAVException exception = null;
DAVResponse response = null;
DAVValidateWalker validateHandler = new DAVValidateWalker();
if (resource.exists() && depth.getID() > 0) {
DAVResourceWalker walker = new DAVResourceWalker();
int walkType = DAVResourceWalker.DAV_WALKTYPE_NORMAL | DAVResourceWalker.DAV_WALKTYPE_LOCKNULL;
try {
response = walker.walk(lockInfoProvider, resource, ifHeaders, flags, lockScope, walkType, validateHandler, DAVDepth.DEPTH_INFINITY);
} catch (DAVException dave) {
exception = dave;
}
} else {
try {
validateHandler.validateResourceState(ifHeaders, resource, lockInfoProvider, lockScope, flags);
} catch (DAVException dave) {
exception = dave;
}
}
if (exception == null && (flags & DAV_VALIDATE_PARENT) != 0) {
DAVResource parentResource = null;
try {
parentResource = DAVResourceHelper.createParentResource(resource);
} catch (DAVException dave) {
exception = dave;
}
if (exception == null) {
try {
validateHandler.validateResourceState(ifHeaders, parentResource, lockInfoProvider, lockScope, flags | DAV_VALIDATE_IS_PARENT);
} catch (DAVException dave) {
exception = dave;
}
if (exception != null) {
String description = "A validation error has occurred on the parent resource, preventing the operation on the resource specified by the Request-URI.";
if (exception.getMessage() != null) {
description += " The error was: " + exception.getMessage();
}
response = new DAVResponse(description, parentResource.getResourceURI().getRequestURI(), response, null, exception.getResponseCode());
exception = null;
}
}
}
if (exception == null && response != null) {
if ((flags & DAV_VALIDATE_USE_424) != 0) {
throw new DAVException("An error occurred on another resource, preventing the requested operation on this resource.",
SC_FAILED_DEPENDANCY, 0, response);
}
DAVPropsResult propStat = null;
if ((flags & DAV_VALIDATE_ADD_LD) != 0) {
propStat = new DAVPropsResult();
propStat.addPropStatsText("<D:propstat>\n<D:prop><D:lockdiscovery/></D:prop>\n<D:status>HTTP/1.1 424 Failed Dependency</D:status>\n</D:propstat>\n");
}
response = new DAVResponse("An error occurred on another resource, preventing the requested operation on this resource.",
resource.getResourceURI().getRequestURI(), response, propStat, SC_FAILED_DEPENDANCY);
throw new DAVException("Error(s) occurred on resources during the validation process.", SC_MULTISTATUS, 0, response);
}
if (exception != null) {
exception.setResponse(response);
throw exception;
}
}
protected SVNDeltaReader openStream(DAVResource resource, int mode) throws DAVException {
if (mode == DAV_MODE_WRITE_TRUNC || mode == DAV_MODE_WRITE_SEEKABLE) {
if (resource.getType() != DAVResourceType.WORKING) {
throw new DAVException("Resource body changes may only be made to working resources [at this time].",
HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
}
if (mode == DAV_MODE_WRITE_SEEKABLE) {
throw new DAVException("Resource body writes cannot use ranges [at this time].", HttpServletResponse.SC_NOT_IMPLEMENTED, 0);
}
String path = resource.getResourceURI().getPath();
FSRoot root = resource.getRoot();
FSFS fsfs = resource.getFSFS();
FSTransactionInfo txn = resource.getTxnInfo();
FSCommitter committer = getCommitter(fsfs, root, txn, resource.getLockTokens(), resource.getUserName());
SVNNodeKind kind = DAVServletUtil.checkPath(resource.getRoot(), resource.getResourceURI().getPath());
if (kind == SVNNodeKind.NONE) {
try {
committer.makeFile(path);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not create file within the repository.", null);
}
}
if (resource.isAutoCheckedOut() && myRequest.getContentType() != null) {
SVNProperties props = null;
try {
props = fsfs.getProperties(root.getRevisionNode(path));
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Error fetching mime-type property.", null);
}
String mimeType = props.getStringValue(SVNProperty.MIME_TYPE);
if (mimeType == null) {
try {
committer.changeNodeProperty(path, SVNProperty.MIME_TYPE, SVNPropertyValue.create(myRequest.getContentType()));
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not set mime-type property.", null);
}
}
}
ISVNDeltaConsumer deltaConsumer = getDeltaConsumer(root, committer, fsfs, resource.getUserName(), resource.getLockTokens());
try {
deltaConsumer.applyTextDelta(path, resource.getBaseChecksum());
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not prepare to write the file", null);
}
if (isSVNDiff()) {
return new SVNDeltaReader();
}
return null;
}
protected void moveResource(DAVResource srcResource, DAVResource dstResource) throws DAVException {
if (srcResource.getType() != DAVResourceType.REGULAR || dstResource.getType() != DAVResourceType.REGULAR || !getConfig().isAutoVersioning()) {
throw new DAVException("MOVE only allowed on two public URIs, and autoversioning must be active.",
HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
checkOut(dstResource, true, false, false, null);
FSCommitter committer = getCommitter(dstResource.getFSFS(), dstResource.getRoot(), dstResource.getTxnInfo(), dstResource.getLockTokens(),
dstResource.getUserName());
String srcPath = srcResource.getResourceURI().getPath();
String dstPath = dstResource.getResourceURI().getPath();
try {
committer.makeCopy((FSRevisionRoot) srcResource.getRoot(), srcPath, dstPath, true);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Unable to make a filesystem copy.", null);
}
try {
committer.deleteNode(srcPath);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not delete the src resource.", null);
}
checkIn(dstResource, false, false);
}
protected void copyResource(DAVResource srcResource, DAVResource dstResource) throws DAVException {
if (dstResource.isBaseLined() && dstResource.getType() == DAVResourceType.VERSION) {
throw new DAVException("Illegal: COPY Destination is a baseline.", HttpServletResponse.SC_PRECONDITION_FAILED, 0);
}
if (dstResource.getType() == DAVResourceType.REGULAR && !getConfig().isAutoVersioning()) {
throw new DAVException("COPY called on regular resource, but autoversioning is not active.", HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
if (dstResource.getType() == DAVResourceType.REGULAR) {
checkOut(dstResource, true, false, false, null);
}
FSFS srcFSFS = srcResource.getFSFS();
FSFS dstFSFS = dstResource.getFSFS();
if (!srcFSFS.getDBRoot().equals(dstFSFS.getDBRoot())) {
throw new DAVException("Copy source and destination are in different repositories.", null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE, 0, null);
}
FSCommitter committer = getCommitter(dstResource.getFSFS(), dstResource.getRoot(), dstResource.getTxnInfo(), dstResource.getLockTokens(),
dstResource.getUserName());
String srcPath = srcResource.getResourceURI().getPath();
String dstPath = dstResource.getResourceURI().getPath();
try {
committer.makeCopy((FSRevisionRoot) srcResource.getRoot(), srcPath, dstPath, true);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Unable to make a filesystem copy.", null);
}
if (dstResource.isAutoCheckedOut()) {
checkIn(dstResource, false, false);
}
}
protected int unlock(DAVResource resource, String lockToken) {
DAVLockInfoProvider lockProvider = null;
try {
lockProvider = DAVLockInfoProvider.createLockInfoProvider(this, false);
} catch (SVNException svne) {
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
DAVResource lockResource = resource;
if (lockToken != null) {
try {
lockResource = DAVResourceHelper.getDirectResource(lockProvider, lockToken, resource);
} catch (DAVException dave) {
return dave.getResponseCode();
}
}
int walkType = DAVResourceWalker.DAV_WALKTYPE_NORMAL | DAVResourceWalker.DAV_WALKTYPE_LOCKNULL;
DAVResourceWalker walker = new DAVResourceWalker();
DAVUnlockWalker unlockHandler = new DAVUnlockWalker(lockToken, this);
try {
walker.walk(lockProvider, lockResource, null, 0, null, walkType, unlockHandler, DAVDepth.DEPTH_INFINITY);
} catch (DAVException dave) {
return dave.getResponseCode();
}
return HttpServletResponse.SC_OK;
}
protected DAVResource checkOut(DAVResource resource, boolean isAutoCheckOut, boolean isUnreserved, boolean isCreateActivity,
List activities) throws DAVException {
DAVResourceType resourceType = resource.getResourceURI().getType();
FSFS fsfs = resource.getFSFS();
if (isAutoCheckOut) {
if (resourceType == DAVResourceType.VERSION && resource.isBaseLined()) {
return null;
}
if (resourceType != DAVResourceType.REGULAR) {
throw new DAVException("auto-checkout attempted on non-regular version-controlled resource.", null,
HttpServletResponse.SC_METHOD_NOT_ALLOWED, null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG,
DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
if (resource.isBaseLined()) {
new DAVException("auto-checkout attempted on baseline collection, which is not supported.", null,
HttpServletResponse.SC_METHOD_NOT_ALLOWED, null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG,
DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
String sharedActivity = DAVServlet.getSharedActivity();
String sharedTxnName = null;
FSTransactionInfo sharedTxnInfo = null;
if (sharedActivity == null) {
try {
sharedActivity = SVNUUIDGenerator.formatUUID(SVNUUIDGenerator.generateUUID());
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"cannot generate UUID for a shared activity", null);
}
sharedTxnInfo = DAVServletUtil.createActivity(resource, fsfs);
sharedTxnName = sharedTxnInfo.getTxnId();
DAVServletUtil.storeActivity(resource, sharedTxnInfo.getTxnId());
DAVServlet.setSharedActivity(sharedActivity);
}
if (sharedTxnName == null) {
sharedTxnName = DAVServletUtil.getTxn(resource.getActivitiesDB(), sharedActivity);
if (sharedTxnName == null) {
throw new DAVException("Cannot look up a txn_name by activity", null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null,
SVNLogType.NETWORK, Level.FINE, null, null, null, 0, null);
}
}
resource = DAVWorkingResourceHelper.createWorkingResource(resource, sharedActivity, sharedTxnName, true);
resource.setIsAutoCkeckedOut(true);
FSTransactionInfo txnInfo = DAVServletUtil.openTxn(fsfs, resource.getTxnName());
FSTransactionRoot txnRoot = null;
try {
txnRoot = fsfs.createTransactionRoot(txnInfo);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not open a (transaction) root in the repository", null);
}
resource.setTxnInfo(txnInfo);
resource.setRoot(txnRoot);
return null;
}
if (resourceType != DAVResourceType.VERSION) {
throw new DAVException("CHECKOUT can only be performed on a version resource [at this time].", null,
HttpServletResponse.SC_METHOD_NOT_ALLOWED, null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG,
DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
if (isCreateActivity) {
throw new DAVException("CHECKOUT can not create an activity at this time. Use MKACTIVITY first.", null,
HttpServletResponse.SC_NOT_IMPLEMENTED, null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG,
DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
if (isUnreserved) {
throw new DAVException("Unreserved checkouts are not yet available. A version history may not be checked out more than once, into a specific activity.",
null, HttpServletResponse.SC_NOT_IMPLEMENTED, null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG,
DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
if (activities == null) {
throw new DAVException("An activity must be provided for checkout.", null, HttpServletResponse.SC_CONFLICT, null, SVNLogType.NETWORK,
Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.INCOMPLETE_DATA.getCode(),
null);
}
if (activities.size() != 1) {
throw new DAVException("Only one activity may be specified within the CHECKOUT.", null, HttpServletResponse.SC_CONFLICT, null,
SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE,
SVNErrorCode.INCORRECT_PARAMS.getCode(), null);
}
DAVURIInfo parse = null;
try {
parse = DAVPathUtil.simpleParseURI((String) activities.get(0), resource);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_CONFLICT, "The activity href could not be parsed properly.",
null);
}
if (parse.getActivityID() == null) {
throw new DAVException("The provided href is not an activity URI.", null, HttpServletResponse.SC_CONFLICT, null, SVNLogType.NETWORK,
Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.INCORRECT_PARAMS.getCode(),
null);
}
String txnName = DAVServletUtil.getTxn(resource.getActivitiesDB(), parse.getActivityID());
if (txnName == null) {
throw new DAVException("The specified activity does not exist.", null, HttpServletResponse.SC_CONFLICT, null, SVNLogType.NETWORK,
Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE,
SVNErrorCode.APMOD_ACTIVITY_NOT_FOUND.getCode(), null);
}
if (resource.isBaseLined() || !SVNRevision.isValidRevisionNumber(resource.getRevision())) {
long youngestRevision = -1;
try {
youngestRevision = fsfs.getYoungestRevision();
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not determine the youngest revision for verification against the baseline being checked out.", null);
}
if (resource.getRevision() != youngestRevision) {
throw new DAVException("The specified baseline is not the latest baseline, so it may not be checked out.", null,
HttpServletResponse.SC_CONFLICT, null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG,
DAVElement.SVN_DAV_ERROR_NAMESPACE, SVNErrorCode.APMOD_BAD_BASELINE.getCode(), null);
}
} else {
FSTransactionInfo txnInfo = DAVServletUtil.openTxn(fsfs, txnName);
FSTransactionRoot txnRoot = null;
String reposPath = resource.getResourceURI().getPath();
try {
txnRoot = fsfs.createTransactionRoot(txnInfo);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not open the transaction tree.", null);
}
long txnCreatedRevision = -1;
try {
FSRevisionNode node = txnRoot.getRevisionNode(reposPath);
txnCreatedRevision = node.getCreatedRevision();
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not get created-rev of transaction node.", null);
}
if (SVNRevision.isValidRevisionNumber(txnCreatedRevision)) {
SVNDebugLog.getDefaultLog().logFine(SVNLogType.DEFAULT, "resource.getRevision(): " + resource.getRevision() + ", txnCreatedRevision: " + txnCreatedRevision);
SVNDebugLog.getDefaultLog().logFine(SVNLogType.DEFAULT, "resource type: " + resource.getType());
if (resource.getRevision() < txnCreatedRevision) {
throw new DAVException("resource out of date; try updating", null, HttpServletResponse.SC_CONFLICT, null, SVNLogType.NETWORK,
Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE,
SVNErrorCode.FS_CONFLICT.getCode(), null);
} else if (resource.getRevision() > txnCreatedRevision) {
String txnNodeRevID = null;
try {
FSRevisionNode node = txnRoot.getRevisionNode(reposPath);
txnNodeRevID = node.getId().getNodeID();
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage();
throw new DAVException("Unable to fetch the node revision id of the version resource within the transaction.", null,
HttpServletResponse.SC_CONFLICT, err, SVNLogType.FSFS, Level.FINE, null,
DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE, err.getErrorCode().getCode(), null);
}
String urlNodeRevID = null;
try {
FSRoot root = resource.getRoot();
FSRevisionNode node = root.getRevisionNode(reposPath);
urlNodeRevID = node.getId().getNodeID();
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage();
throw new DAVException("Unable to fetch the node revision id of the version resource within the revision.", null,
HttpServletResponse.SC_CONFLICT, err, SVNLogType.FSFS, Level.FINE, null,
DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE, err.getErrorCode().getCode(), null);
}
if (!urlNodeRevID.equals(txnNodeRevID)) {
throw new DAVException("version resource newer than txn (restart the commit)", null, HttpServletResponse.SC_CONFLICT,
null, SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE,
SVNErrorCode.FS_CONFLICT.getCode(), null);
}
}
}
}
return DAVWorkingResourceHelper.createWorkingResource(resource, parse.getActivityID(), txnName, false);
}
protected DAVResource checkIn(DAVResource resource, boolean keepCheckedOut, boolean createVersionResource) throws DAVException {
if (resource.getType() != DAVResourceType.WORKING) {
throw new DAVException("CHECKIN called on non-working resource.", null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null,
SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE,
SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
DAVResource versionResource = null;
DAVResourceURI resourceURI = resource.getResourceURI();
String sharedActivity = DAVServlet.getSharedActivity();
if (sharedActivity != null && sharedActivity.equals(resource.getActivityID())) {
String sharedTxnName = DAVServletUtil.getTxn(resource.getActivitiesDB(), sharedActivity);
if (sharedTxnName == null) {
throw new DAVException("Cannot look up a txn_name by activity", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 0);
}
if (resource.getTxnName() != null && !sharedTxnName.equals(resource.getTxnName())) {
throw new DAVException("Internal txn_name doesn't match autoversioning transaction.", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 0);
}
if (resource.getTxnInfo() == null) {
throw new DAVException("Autoversioning txn isn't open when it should be.", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 0);
}
DAVServletUtil.setAutoRevisionProperties(resource);
FSCommitter committer = getCommitter(resource.getFSFS(), resource.getRoot(), resource.getTxnInfo(), resource.getLockTokens(),
resource.getUserName());
StringBuffer conflictPath = new StringBuffer();
long newRev = SVNRepository.INVALID_REVISION;
try {
newRev = committer.commitTxn(true, true, null, conflictPath);
} catch (SVNException svne) {
try {
FSCommitter.abortTransaction(resource.getFSFS(), resource.getTxnInfo().getTxnId());
} catch (SVNException svne2) {
//ignore
}
String message = null;
Object[] objects = null;
if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.FS_CONFLICT) {
message = "A conflict occurred during the CHECKIN processing. The problem occurred with the \"{0}\" resource.";
objects = new Object[] { conflictPath.toString() };
} else {
message = "An error occurred while committing the transaction.";
}
DAVServletUtil.deleteActivity(resource, sharedActivity);
DAVServlet.setSharedActivity(null);
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_CONFLICT, message, objects);
}
DAVServletUtil.deleteActivity(resource, sharedActivity);
if (createVersionResource) {
String uri = DAVPathUtil.buildURI(resourceURI.getContext(), DAVResourceKind.VERSION, newRev, resourceURI.getPath(), false);
versionResource = DAVVersionResourceHelper.createVersionResource(resource, uri);
}
}
resource.setTxnName(null);
resource.setTxnInfo(null);
if (!keepCheckedOut) {
resource.setIsAutoCkeckedOut(false);
DAVResourceHelper.convertWorkingToRegular(resource);
}
return versionResource;
}
protected void uncheckOut(DAVResource resource) throws DAVException {
if (resource.getType() != DAVResourceType.WORKING) {
throw new DAVException("UNCHECKOUT called on non-working resource.", null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null,
SVNLogType.NETWORK, Level.FINE, null, DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE,
SVNErrorCode.UNSUPPORTED_FEATURE.getCode(), null);
}
FSTransactionInfo txnInfo = resource.getTxnInfo();
if (txnInfo != null) {
try {
FSCommitter.abortTransaction(resource.getFSFS(), txnInfo.getTxnId());
} catch (SVNException svne) {
//ignore
}
}
DAVResourceURI resourceURI = resource.getResourceURI();
if (resourceURI.getActivityID() != null) {
try {
DAVServletUtil.deleteActivity(resource, resourceURI.getActivityID());
} catch (DAVException dave) {
//ignore
}
DAVServlet.setSharedActivity(null);
}
resource.setTxnName(null);
resource.setTxnInfo(null);
resource.setIsAutoCkeckedOut(false);
DAVResourceHelper.convertWorkingToRegular(resource);
}
protected DAVAutoVersionInfo autoCheckOut(DAVResource resource, boolean isParentOnly) throws DAVException {
DAVAutoVersionInfo info = new DAVAutoVersionInfo();
DAVLockInfoProvider[] lockProvider = new DAVLockInfoProvider[0];
if (!resource.exists() || isParentOnly) {
DAVResource parentResource = null;
try {
parentResource = DAVResourceHelper.createParentResource(resource);
} catch (DAVException dave) {
autoCheckIn(resource, true, false, info);
throw dave;
}
if (parentResource == null || !parentResource.exists()) {
autoCheckIn(resource, true, false, info);
throw new DAVException("Missing one or more intermediate collections. Cannot create resource {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI()) },
HttpServletResponse.SC_CONFLICT, 0);
}
info.setParentResource(parentResource);
if (parentResource.isVersioned() && !parentResource.isWorking()) {
boolean checkOutParent = false;
try {
checkOutParent = canAutoCheckOut(parentResource, lockProvider, parentResource.getAutoVersion());
} catch (DAVException dave) {
autoCheckIn(resource, true, false, info);
throw dave;
}
if (!checkOutParent) {
autoCheckIn(resource, true, false, info);
throw new DAVException("<DAV:cannot-modify-checked-in-parent>", HttpServletResponse.SC_CONFLICT, 0);
}
try {
checkOut(parentResource, true, false, false, null);
} catch (DAVException dave) {
autoCheckIn(resource, true, false, info);
throw new DAVException("Unable to auto-checkout parent collection. Cannot create resource {0}.",
new Object[] { resource.getResourceURI().getRequestURI() }, HttpServletResponse.SC_CONFLICT, dave, 0);
}
info.setParentCheckedOut(true);
}
}
if (isParentOnly) {
return info;
}
if (!resource.exists() && resource.getAutoVersion() == DAVAutoVersion.ALWAYS) {
try {
resource.versionControl(null);
} catch (DAVException dave) {
autoCheckIn(resource, true, false, info);
throw new DAVException("Unable to create versioned resource {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI()) },
HttpServletResponse.SC_CONFLICT, dave, 0);
}
info.setResourceVersioned(true);
}
if (resource.isVersioned() && !resource.isWorking()) {
boolean checkOutResource = false;
try {
checkOutResource = canAutoCheckOut(resource, lockProvider, resource.getAutoVersion());
} catch (DAVException dave) {
autoCheckIn(resource, true, false, info);
throw dave;
}
if (!checkOutResource) {
autoCheckIn(resource, true, false, info);
throw new DAVException("<DAV:cannot-modify-version-controlled-content>", HttpServletResponse.SC_CONFLICT, 0);
}
try {
checkOut(resource, true, false, false, null);
} catch (DAVException dave) {
autoCheckIn(resource, true, false, info);
throw new DAVException("Unable to checkout resource {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI()) },
HttpServletResponse.SC_CONFLICT, 0);
}
info.setResourceCheckedOut(true);
}
return info;
}
protected boolean canAutoCheckOut(DAVResource resource, DAVLockInfoProvider[] lockProvider, DAVAutoVersion autoVersion) throws DAVException {
boolean autoCheckOut = false;
DAVLock lock = null;
if (autoVersion == DAVAutoVersion.ALWAYS) {
autoCheckOut = true;
} else if (autoVersion == DAVAutoVersion.LOCKED) {
if (lockProvider[0] == null) {
try {
lockProvider[0] = DAVLockInfoProvider.createLockInfoProvider(this, false);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Cannot open lock database to determine auto-versioning behavior.", null);
}
}
try {
lock = lockProvider[0].getLock(resource);
} catch (DAVException dave) {
throw new DAVException("The locks could not be queried for determining auto-versioning behavior.", null,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, dave, 0);
}
if (lock != null) {
autoCheckOut = true;
}
}
return autoCheckOut;
}
protected void autoCheckIn(DAVResource resource, boolean undo, boolean unlock, DAVAutoVersionInfo info) throws DAVException {
if (undo) {
if (resource != null) {
if (info.isResourceCheckedOut()) {
try {
uncheckOut(resource);
} catch (DAVException dave) {
throw new DAVException("Unable to undo auto-checkout of resource {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI()) },
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, SVNLogType.NETWORK, Level.FINE, dave, null, null, 0, null);
}
}
if (info.isResourceVersioned()) {
try {
removeResource(resource);
} catch (DAVException dave) {
throw new DAVException("Unable to undo auto-version-control of resource {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI()) },
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, SVNLogType.NETWORK, Level.FINE, dave, null, null, 0, null);
}
}
}
if (info.getParentResource() != null && info.isParentCheckedOut()) {
try {
uncheckOut(info.getParentResource());
} catch (DAVException dave) {
throw new DAVException("Unable to undo auto-checkout of parent collection {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(info.getParentResource().getResourceURI().getRequestURI()) },
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, SVNLogType.NETWORK, Level.FINE, dave, null, null, 0, null);
}
}
return;
}
if (resource != null && resource.isWorking() && (unlock || info.isResourceCheckedOut())) {
DAVAutoVersion autoVersion = resource.getAutoVersion();
if (autoVersion == DAVAutoVersion.ALWAYS || (unlock && autoVersion == DAVAutoVersion.LOCKED)) {
try {
checkIn(resource, false, false);
} catch (DAVException dave) {
throw new DAVException("Unable to auto-checkin resource {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI()) },
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, SVNLogType.NETWORK, Level.FINE, dave, null, null, 0, null);
}
}
}
if (!unlock && info.isParentCheckedOut() && info.getParentResource() != null &&
info.getParentResource().getType() == DAVResourceType.WORKING) {
DAVAutoVersion autoVersion = info.getParentResource().getAutoVersion();
if (autoVersion == DAVAutoVersion.ALWAYS) {
try {
checkIn(info.getParentResource(), false, false);
} catch (DAVException dave) {
throw new DAVException("Unable to auto-checkin parent collection {0}.",
new Object[] { SVNEncodingUtil.xmlEncodeCDATA(info.getParentResource().getResourceURI().getRequestURI()) },
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, SVNLogType.NETWORK, Level.FINE, dave, null, null, 0, null);
}
}
}
}
protected void removeResource(DAVResource resource) throws DAVException {
DAVResourceURI uri = resource.getResourceURI();
DAVResourceType resourceType = uri.getType();
if (resourceType != DAVResourceType.REGULAR && resourceType != DAVResourceType.WORKING && resourceType != DAVResourceType.ACTIVITY) {
throw new DAVException("DELETE called on invalid resource type.", HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
DAVConfig config = getConfig();
if (resourceType == DAVResourceType.REGULAR && !config.isAutoVersioning()) {
throw new DAVException("DELETE called on regular resource, but autoversioning is not active.",
HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
if (resourceType == DAVResourceType.ACTIVITY) {
DAVServletUtil.deleteActivity(resource, uri.getActivityID());
return;
}
if (resourceType == DAVResourceType.REGULAR) {
checkOut(resource, true, false, false, null);
}
if (SVNRevision.isValidRevisionNumber(resource.getVersion())) {
long createdRevision = SVNRepository.INVALID_REVISION;
try {
createdRevision = resource.getCreatedRevisionUsingFS(null);
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not get created rev of resource", null);
}
if (resource.getVersion() < createdRevision) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_OUT_OF_DATE, "Item ''{0}'' is out of date",
uri.getPath());
throw DAVException.convertError(err, HttpServletResponse.SC_CONFLICT, "Can''t DELETE out-of-date resource", null);
}
}
//MERGE requests send can provide locks in a request body,
//COPY, MOVE requests do not, so check for the valid xml exists
if (getDAVRequest() != null) {
Map locks = parseLocks(getDAVRequest().getRootElement(), uri.getPath());
if (!locks.isEmpty()) {
resource.setLockTokens(locks.values());
}
}
FSCommitter committer = getCommitter(resource.getFSFS(), resource.getRoot(), resource.getTxnInfo(), resource.getLockTokens(),
resource.getUserName());
try {
committer.deleteNode(uri.getPath());
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not delete the resource", null);
}
if (resource.isAutoCheckedOut()) {
checkIn(resource, false, false);
}
}
protected Map parseLocks(DAVElementProperty rootElement, String pathPrefix) throws DAVException {
Map pathsToLockTokens = new HashMap();
List namespaces = getNamespaces();
if (namespaces == null || !namespaces.contains(DAVElement.SVN_NAMESPACE)) {
return pathsToLockTokens;
}
DAVElement rootElementName = rootElement.getName();
DAVElementProperty child = null;
if (rootElementName == DAVElement.SVN_LOCK_TOKEN_LIST) {
child = rootElement;
} else {
List children = rootElement.getChildren();
if (children != null) {
for (Iterator childIter = children.iterator(); childIter.hasNext();) {
DAVElementProperty nextChild = (DAVElementProperty) childIter.next();
if (nextChild.getName() == DAVElement.SVN_LOCK_TOKEN_LIST) {
child = nextChild;
break;
}
}
}
}
if (child == null) {
return pathsToLockTokens;
}
List children = child.getChildren();
if (children != null) {
for (Iterator childIter = children.iterator(); childIter.hasNext();) {
DAVElementProperty lockChild = (DAVElementProperty) childIter.next();
if (lockChild.getName() != DAVElement.SVN_LOCK) {
continue;
}
String lockPath = null;
String lockToken = null;
List lockChildren = lockChild.getChildren();
for (Iterator lockChildrenIter = lockChildren.iterator(); lockChildrenIter.hasNext();) {
DAVElementProperty lockElementChild = (DAVElementProperty) lockChildrenIter.next();
if (lockElementChild.getName() == LOCK_PATH_ELEM) {
String cdata = lockElementChild.getFirstValue(false);
DAVPathUtil.testCanonical(cdata);
lockPath = SVNPathUtil.append(pathPrefix, cdata);
if (!lockPath.startsWith("/")) {
lockPath = "/" + lockPath;
}
if (lockPath != null && lockToken != null) {
pathsToLockTokens.put(lockPath, lockToken);
lockPath = null;
lockToken = null;
}
} else if (lockElementChild.getName() == LOCK_TOKEN_ELEM) {
lockToken = lockElementChild.getFirstValue(true);
if (lockPath != null && lockToken != null) {
pathsToLockTokens.put(lockPath, lockToken);
lockPath = null;
lockToken = null;
}
}
}
}
}
return pathsToLockTokens;
}
protected DAVDepth getRequestDepth(DAVDepth defaultDepth) throws SVNException {
String depth = getRequestHeader(DEPTH_HEADER);
if (depth == null) {
return defaultDepth;
}
DAVDepth result = DAVDepth.parseDepth(depth);
if (result == null) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_MALFORMED_DATA, "Invalid depth ''{0}''", depth), SVNLogType.NETWORK);
}
return result;
}
protected Date getTimeout() {
String timeoutHeader = getRequestHeader(HTTPHeader.TIMEOUT_HEADER);
if (timeoutHeader == null) {
return null;
}
timeoutHeader = timeoutHeader.trim();
String[] values = timeoutHeader.split("[\\t\\s]");
for (int i = 0; i < values.length; i++) {
String value = values[i];
if ("Infinite".equalsIgnoreCase(value)) {
return null;
}
if (value.startsWith("Second-")) {
value = value.substring(7);
long expirationValue = 0;
try {
expirationValue = Long.parseLong(value);
} catch (NumberFormatException nfe) {
}
return new Date(System.currentTimeMillis() + expirationValue);
}
}
return null;
}
protected void setDefaultResponseHeaders() {
if (getRequestHeader(LABEL_HEADER) != null && getRequestHeader(LABEL_HEADER).length() > 0) {
setResponseHeader(VARY_HEADER, LABEL_HEADER);
}
}
protected String getURI() {
return myRequest.getRequestURI();
}
protected String getRequestHeader(String name) {
return myRequest.getHeader(name);
}
protected String getRequestMethod() {
return myRequest.getMethod();
}
protected Enumeration getRequestHeaders(String name) {
return myRequest.getHeaders(name);
}
protected long getRequestDateHeader(String name) {
return myRequest.getDateHeader(name);
}
protected boolean isSVNDiff() {
String contentType = myRequest.getContentType();
return contentType != null && contentType.equals(HTTPHeader.SVNDIFF_MIME_TYPE);
}
protected long[] parseRange() {
String range = getRequestHeader(HTTPHeader.CONTENT_RANGE_HEADER);
if (range == null) {
return null;
}
range = range.toLowerCase();
if (!range.startsWith("bytes ") || range.indexOf("-") < 0 || range.indexOf("/") < 0) {
return null;
}
range = range.substring("bytes ".length()).trim();
int ind = range.indexOf('-');
String rangeStartSubstring = range.substring(0, ind);
long rangeStart = -1;
try {
rangeStart = Long.parseLong(rangeStartSubstring);
} catch (NumberFormatException nfe) {
return null;
}
if (rangeStart < 0) {
return null;
}
range = range.substring(ind + 1);
ind = range.indexOf('/');
String rangeEndSubstring = range.substring(0, ind);
long rangeEnd = -1;
try {
rangeEnd = Long.parseLong(rangeEndSubstring);
} catch (NumberFormatException nfe) {
return null;
}
if (rangeEnd < 0 || rangeEnd < rangeStart) {
return null;
}
if (range.charAt(ind + 1) != '*') {
range = range.substring(ind + 1);
long length = -1;
try {
length = Long.parseLong(range);
} catch (NumberFormatException nfe) {
return null;
}
if (length <= rangeEnd) {
return null;
}
}
return new long[] { rangeStart, rangeEnd };
}
protected InputStream getRequestInputStream() throws SVNException {
try {
return myRequest.getInputStream();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e, SVNLogType.NETWORK);
}
return null;
}
protected HttpServletRequest getRequest() {
return myRequest;
}
protected void setResponseHeader(String name, String value) {
myResponse.setHeader(name, value);
}
protected void addResponseHeader(String name, String value) {
myResponse.addHeader(name, value);
}
protected void setResponseStatus(int statusCode) {
myResponse.setStatus(statusCode);
}
protected void sendError(int errorCode, String message) {
try {
myResponse.sendError(errorCode, message);
} catch (IOException e) {
SVNDebugLog.getDefaultLog().logFine(SVNLogType.DEFAULT, message);
}
}
protected void setResponseContentType(String contentType) {
myResponse.setContentType(contentType);
}
protected void setResponseContentLength(int length) {
myResponse.setContentLength(length);
}
protected Writer getResponseWriter() throws SVNException {
try {
return myResponse.getWriter();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e, SVNLogType.NETWORK);
}
return null;
}
protected void createdCollection(DAVResource resource) throws DAVException {
if (resource.getType() != DAVResourceType.WORKING && resource.getType() != DAVResourceType.REGULAR) {
throw new DAVException("Collections can only be created within a working or regular collection [at this time].",
HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
if (resource.getType() == DAVResourceType.REGULAR && !getConfig().isAutoVersioning()) {
throw new DAVException("MKCOL called on regular resource, but autoversioning is not active.",
HttpServletResponse.SC_METHOD_NOT_ALLOWED, 0);
}
if (resource.getType() == DAVResourceType.REGULAR) {
checkOut(resource, true, false, false, null);
}
FSCommitter committer = getCommitter(resource.getFSFS(), resource.getRoot(), resource.getTxnInfo(), resource.getLockTokens(),
resource.getUserName());
try {
committer.makeDir(resource.getResourceURI().getPath());
} catch (SVNException svne) {
throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Could not create the collection.", null);
}
if (resource.isAutoCheckedOut()) {
checkIn(resource, false, false);
}
}
protected OutputStream getResponseOutputStream() throws SVNException {
try {
return myResponse.getOutputStream();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e, SVNLogType.NETWORK);
}
return null;
}
protected void handleDAVCreated(String location, String what, boolean isReplaced) throws SVNException {
if (location == null) {
location = getURI();
}
if (isReplaced) {
setResponseStatus(HttpServletResponse.SC_NO_CONTENT);
return;
}
setResponseHeader(HTTPHeader.LOCATION_HEADER, constructURL(location));
String body = what + " " + SVNEncodingUtil.xmlEncodeCDATA(location) + " has been created.";
response(body, DAVServlet.getStatusLine(HttpServletResponse.SC_CREATED), HttpServletResponse.SC_CREATED);
}
protected void notifyCreated(DAVResource resource, DAVLockInfoProvider lockProvider, DAVResourceState resourceState, DAVDepth depth) throws DAVException {
if (resourceState == DAVResourceState.LOCK_NULL) {
if (depth != DAVDepth.DEPTH_ZERO) {
lockProvider.inheritLocks(resource, false);
}
} else if (resourceState == DAVResourceState.NULL) {
try {
lockProvider.inheritLocks(resource, true);
} catch (DAVException dave) {
throw new DAVException("The resource was created successfully, but there was a problem inheriting locks from the parent resource.",
dave.getResponseCode(), dave, 0);
}
}
}
protected void response(String body, String statusLine, int statusCode) throws SVNException {
setResponseStatus(statusCode);
setResponseContentType("text/html; charset=ISO-8859-1");
StringBuffer responseBuffer = new StringBuffer();
responseBuffer.append(DAV_RESPONSE_BODY_1);
responseBuffer.append(statusLine);
responseBuffer.append(DAV_RESPONSE_BODY_2);
responseBuffer.append(statusLine.substring(4));
responseBuffer.append(DAV_RESPONSE_BODY_3);
responseBuffer.append(body);
responseBuffer.append(DAV_RESPONSE_BODY_4);
appendServerSignature(responseBuffer, "<hr />\n");
responseBuffer.append(DAV_RESPONSE_BODY_5);
String responseBody = responseBuffer.toString();
try {
setResponseContentLength(responseBody.getBytes(UTF8_ENCODING).length);
} catch (UnsupportedEncodingException e) {
setResponseContentLength(responseBody.getBytes().length);
}
try {
getResponseWriter().write(responseBody);
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, e), e, SVNLogType.NETWORK);
}
}
protected String constructURL(String location) {
StringBuffer url = new StringBuffer ();
String scheme = myRequest.getScheme();
String host = myRequest.getServerName();
url.append(scheme);
url.append("://");
url.append(host);
int port = myRequest.getServerPort();
if ((scheme.equals ("http") && port != 80) || (scheme.equals ("https") && port != 443)) {
url.append(':');
url.append(port);
}
if (!location.startsWith("/")) {
url.append('/');
}
url.append(location);
return url.toString();
}
protected void appendServerSignature(StringBuffer buffer, String prefix) {
buffer.append(prefix);
buffer.append("<address>");
ServletContext context = myRequest.getSession().getServletContext();
buffer.append(context.getServerInfo());
buffer.append(" ");
buffer.append(Version.getVersionString());
buffer.append(" ");
String host = myRequest.getServerName();
buffer.append("Server at ");
buffer.append(SVNEncodingUtil.xmlEncodeCDATA(host));
buffer.append(" Port ");
int port = myRequest.getServerPort();
buffer.append(port);
buffer.append("</address>\n");
}
protected static Collection getSupportedLiveProperties(DAVResource resource, Collection properties) {
if (properties == null) {
properties = new ArrayList();
}
properties.add(DAVElement.DEADPROP_COUNT);
properties.add(DAVElement.REPOSITORY_UUID);
properties.add(DAVElement.VERSION_CONTROLLED_CONFIGURATION);
if (resource.getResourceURI().getKind() != DAVResourceKind.BASELINE_COLL) {
properties.add(DAVElement.BASELINE_COLLECTION);
} else {
properties.remove(DAVElement.BASELINE_COLLECTION);
}
properties.add(DAVElement.BASELINE_RELATIVE_PATH);
properties.add(DAVElement.RESOURCE_TYPE);
properties.add(DAVElement.CHECKED_IN);
properties.add(DAVElement.GET_ETAG);
properties.add(DAVElement.CREATOR_DISPLAY_NAME);
properties.add(DAVElement.CREATION_DATE);
properties.add(DAVElement.GET_LAST_MODIFIED);
properties.add(DAVElement.VERSION_NAME);
properties.add(DAVElement.GET_CONTENT_TYPE);
if (!resource.isCollection()) {
properties.add(DAVElement.GET_CONTENT_LENGTH);
properties.add(DAVElement.MD5_CHECKSUM);
} else {
properties.remove(DAVElement.GET_CONTENT_LENGTH);
properties.remove(DAVElement.MD5_CHECKSUM);
}
return properties;
}
protected int checkPreconditions(String eTag, Date lastModified) {
lastModified = lastModified == null ? new Date() : lastModified;
long lastModifiedTime = lastModified.getTime();
Enumeration ifMatch = getRequestHeaders(IF_MATCH_HEADER);
if (ifMatch != null && ifMatch.hasMoreElements()) {
String first = (String) ifMatch.nextElement();
if (!first.startsWith("*") && (eTag == null || eTag.startsWith("W") || !first.equals(eTag) || !containsValue(ifMatch, eTag, null))) {
return HttpServletResponse.SC_PRECONDITION_FAILED;
}
} else {
long ifUnmodified = getRequestDateHeader(IF_UNMODIFIED_SINCE_HEADER);
if (ifUnmodified != -1 && lastModifiedTime > ifUnmodified) {
return HttpServletResponse.SC_PRECONDITION_FAILED;
}
}
boolean notModified = false;
Enumeration ifNoneMatch = getRequestHeaders(IF_NONE_MATCH_HEADER);
if (ifNoneMatch != null && ifNoneMatch.hasMoreElements()) {
String first = (String) ifNoneMatch.nextElement();
if (DAVHandlerFactory.METHOD_GET.equals(getRequestMethod())) {
if (first.startsWith("*")) {
notModified = true;
} else if (eTag != null) {
if (getRequestHeader(RANGE_HEADER) != null) {
notModified = !eTag.startsWith("W") && containsValue(ifNoneMatch, eTag, null);
} else {
notModified = containsValue(ifNoneMatch, eTag, null);
}
}
} else if (first.startsWith("*") || (eTag != null && containsValue(ifNoneMatch, eTag, null))) {
return HttpServletResponse.SC_PRECONDITION_FAILED;
}
}
long ifModifiedSince = getRequestDateHeader(IF_MODIFIED_SINCE_HEADER);
if (DAVHandlerFactory.METHOD_GET.equals(getRequestMethod()) && (notModified || ifNoneMatch == null) && ifModifiedSince != -1) {
long requestTime = myRequest.getSession().getLastAccessedTime();
notModified = ifModifiedSince >= lastModifiedTime && ifModifiedSince <= requestTime;
}
if (notModified) {
return HttpServletResponse.SC_NOT_MODIFIED;
}
return 0;
}
protected boolean containsValue(Enumeration values, String stringToFind, String matchAllString) {
boolean contains = false;
if (values != null) {
while (values.hasMoreElements()) {
String currentCondition = (String) values.nextElement();
contains = currentCondition.equals(stringToFind) || currentCondition.equals(matchAllString);
if (contains) {
break;
}
}
}
return contains;
}
protected boolean getSVNDiffVersion() {
boolean diffCompress = false;
for (Enumeration headerEncodings = getRequestHeaders(ACCEPT_ENCODING_HEADER); headerEncodings.hasMoreElements();)
{
String currentEncodings = (String) headerEncodings.nextElement();
String[] encodings = COMMA.split(currentEncodings);
if (encodings.length > 1) {
Arrays.sort(encodings, new Comparator() {
public int compare(Object o1, Object o2) {
String encoding1 = (String) o1;
String encoding2 = (String) o2;
return getEncodingRange(encoding1) > getEncodingRange(encoding2) ? 1 : -1;
}
});
for (int i = encodings.length - 1; i >= 0; i--) {
if (DIFF_VERSION_1.equals(getEncodingName(encodings[i]))) {
diffCompress = true;
break;
} else if (DIFF_VERSION.equals(getEncodingName(encodings[i]))) {
break;
}
}
}
}
return diffCompress;
}
protected FSCommitter getCommitter(FSFS fsfs, FSRoot root, FSTransactionInfo txn, Collection lockTokens, String userName) {
myCommitter = new FSCommitter(fsfs, (FSTransactionRoot) root, txn, lockTokens, userName);
return myCommitter;
}
protected FSDeltaConsumer getDeltaConsumer(FSRoot root, FSCommitter committer, FSFS fsfs, String userName, Collection lockTokens) {
if (myDeltaConsumer == null) {
myDeltaConsumer = new FSDeltaConsumer("", (FSTransactionRoot) root, fsfs, committer, userName, lockTokens);
}
return myDeltaConsumer;
}
private float getEncodingRange(String encoding) {
int delimiterIndex = encoding.indexOf(";");
if (delimiterIndex != -1) {
String qualityString = encoding.substring(delimiterIndex + 1);
if (qualityString.startsWith("q=")) {
try {
return Float.parseFloat(qualityString.substring("q=".length()));
} catch (NumberFormatException e) {
}
}
}
return 1.0f;
}
private String getEncodingName(String encoding) {
int delimiterIndex = encoding.indexOf(";");
if (delimiterIndex != -1) {
return encoding.substring(0, delimiterIndex);
}
return encoding;
}
private int meetsCondition(DAVResource resource, DAVResourceState resourceState) throws SVNException {
Enumeration ifMatch = getRequestHeaders(IF_MATCH_HEADER);
if (ifMatch != null && ifMatch.hasMoreElements()) {
String first = (String) ifMatch.nextElement();
if (first.startsWith("*") && resourceState != DAVResourceState.EXISTS) {
return HttpServletResponse.SC_PRECONDITION_FAILED;
}
}
int retVal = checkPreconditions(resource.getETag(), resource.getLastModified());
if (retVal == HttpServletResponse.SC_PRECONDITION_FAILED) {
Enumeration ifNoneMatch = getRequestHeaders(IF_NONE_MATCH_HEADER);
if (ifNoneMatch != null && ifNoneMatch.hasMoreElements()) {
String first = (String) ifNoneMatch.nextElement();
if (first.startsWith("*") && resourceState != DAVResourceState.EXISTS) {
return 0;
}
}
}
return retVal;
}
protected long readInput(boolean ignoreInput) throws SVNException {
if (ignoreInput) {
InputStream inputStream = null;
try {
inputStream = getRequestInputStream();
while (inputStream.read() != -1) {
continue;
}
} catch (IOException ioe) {
//
} finally {
SVNFileUtil.closeFile(inputStream);
}
return -1;
}
if (mySAXParser == null) {
CountingInputStream stream = null;
try {
mySAXParser = getSAXParserFactory().newSAXParser();
if (myRequest.getContentLength() > 0) {
org.xml.sax.XMLReader reader = mySAXParser.getXMLReader();
reader.setContentHandler(this);
reader.setDTDHandler(this);
reader.setErrorHandler(this);
reader.setEntityResolver(this);
stream = new CountingInputStream(getRequestInputStream());
XMLReader xmlReader = new XMLReader(stream);
reader.parse(new InputSource(xmlReader));
}
} catch (ParserConfigurationException e) {
if (stream == null || stream.getBytesRead() > 0) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, e), e, SVNLogType.NETWORK);
}
} catch (SAXException e) {
if (stream == null || stream.getBytesRead() > 0) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, e), e, SVNLogType.NETWORK);
}
} catch (IOException e) {
if (stream == null || stream.getBytesRead() > 0) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, e), e, SVNLogType.NETWORK);
}
}
if (stream != null) {
if (stream.getBytesRead() > 0) {
getDAVRequest().init();
}
return stream.getBytesRead();
}
}
return 0;
}
//TODO: unused?
protected void handleError(DAVException error, DAVResponse response) {
if (response == null) {
DAVException stackErr = error;
while (stackErr != null && stackErr.getTagName() == null) {
stackErr = stackErr.getPreviousException();
}
if (stackErr != null && stackErr.getTagName() != null) {
myResponse.setContentType(DEFAULT_XML_CONTENT_TYPE);
StringBuffer errorMessageBuffer = new StringBuffer();
errorMessageBuffer.append('\n');
errorMessageBuffer.append("<D:error xmlns:D=\"DAV:\"");
if (stackErr.getMessage() != null) {
errorMessageBuffer.append(" xmlns:m=\"http://apache.org/dav/xmlns\"");
}
if (stackErr.getNameSpace() != null) {
errorMessageBuffer.append(" xmlns:C=\"");
errorMessageBuffer.append(stackErr.getNameSpace());
errorMessageBuffer.append("\">\n<C:");
errorMessageBuffer.append(stackErr.getTagName());
errorMessageBuffer.append("/>");
} else {
errorMessageBuffer.append(">\n<D:");
errorMessageBuffer.append(stackErr.getTagName());
errorMessageBuffer.append("/>");
}
if (stackErr.getMessage() != null) {
}
}
}
}
protected HttpServletResponse getHttpServletResponse() {
return myResponse;
}
public synchronized static SAXParserFactory getSAXParserFactory() {
if (ourSAXParserFactory == null) {
ourSAXParserFactory = SAXParserFactory.newInstance();
try {
ourSAXParserFactory.setFeature("http://xml.org/sax/features/namespaces", true);
} catch (SAXNotRecognizedException e) {
} catch (SAXNotSupportedException e) {
} catch (ParserConfigurationException e) {
}
try {
ourSAXParserFactory.setFeature("http://xml.org/sax/features/validation", false);
} catch (SAXNotRecognizedException e) {
} catch (SAXNotSupportedException e) {
} catch (ParserConfigurationException e) {
}
try {
ourSAXParserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
} catch (SAXNotRecognizedException e) {
} catch (SAXNotSupportedException e) {
} catch (ParserConfigurationException e) {
}
ourSAXParserFactory.setNamespaceAware(true);
ourSAXParserFactory.setValidating(false);
}
return ourSAXParserFactory;
}
protected static class DAVAutoVersionInfo {
private boolean myIsResourceVersioned;
private boolean myIsResourceCheckedOut;
private boolean myIsParentCheckedOut;
private DAVResource myParentResource;
public boolean isResourceVersioned() {
return myIsResourceVersioned;
}
public void setResourceVersioned(boolean isResourceVersioned) {
myIsResourceVersioned = isResourceVersioned;
}
public boolean isResourceCheckedOut() {
return myIsResourceCheckedOut;
}
public void setResourceCheckedOut(boolean isResourceCheckedOut) {
myIsResourceCheckedOut = isResourceCheckedOut;
}
public boolean isParentCheckedOut() {
return myIsParentCheckedOut;
}
public void setParentCheckedOut(boolean isParentCheckedOut) {
myIsParentCheckedOut = isParentCheckedOut;
}
public DAVResource getParentResource() {
return myParentResource;
}
public void setParentResource(DAVResource parentResource) {
myParentResource = parentResource;
}
}
}