/*
* ====================================================================
* Copyright (c) 2004-2012 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.io.dav;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLock;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.io.dav.handlers.DAVGetLocksHandler;
import org.tmatesoft.svn.core.internal.io.dav.handlers.DAVLockHandler;
import org.tmatesoft.svn.core.internal.io.dav.handlers.DAVMergeHandler;
import org.tmatesoft.svn.core.internal.io.dav.handlers.DAVOptionsHandler;
import org.tmatesoft.svn.core.internal.io.dav.http.HTTPConnection;
import org.tmatesoft.svn.core.internal.io.dav.http.HTTPHeader;
import org.tmatesoft.svn.core.internal.io.dav.http.HTTPStatus;
import org.tmatesoft.svn.core.internal.io.dav.http.IHTTPConnection;
import org.tmatesoft.svn.core.internal.io.dav.http.IHTTPConnectionFactory;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNUUIDGenerator;
import org.tmatesoft.svn.core.internal.util.SVNXMLUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.io.ISVNWorkspaceMediator;
import org.tmatesoft.svn.core.io.SVNCapability;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.util.SVNLogType;
import org.xml.sax.helpers.DefaultHandler;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class DAVConnection {
protected static final String DAV_CAPABILITY_YES = "yes";
protected static final String DAV_CAPABILITY_NO = "no";
protected static final String DAV_CAPABILITY_SERVER_YES = "server-yes";
private IHTTPConnection myHttpConnection;
private String myActivityCollectionURL;
private SVNRepository myRepository;
private boolean myIsSpoolReport;
protected boolean myKeepLocks;
protected Map myLocks;
protected Map myCapabilities;
protected IHTTPConnectionFactory myConnectionFactory;
private HTTPStatus myLastStatus;
public DAVConnection(IHTTPConnectionFactory connectionFactory, SVNRepository repository) {
myRepository = repository;
myConnectionFactory = connectionFactory;
}
public boolean isReportResponseSpooled() {
return myIsSpoolReport;
}
public void setReportResponseSpooled(boolean spool) {
myIsSpoolReport = spool;
}
public SVNURL getLocation() {
return myRepository.getLocation();
}
public HTTPStatus getLastStatus() {
return myLastStatus;
}
public void updateLocation() {
myActivityCollectionURL = null;
}
public void open(DAVRepository repository) throws SVNException {
if (myHttpConnection == null) {
myHttpConnection = myConnectionFactory.createHTTPConnection(repository);
if (repository.getSpoolLocation() != null) {
if (myHttpConnection instanceof HTTPConnection) {
((HTTPConnection) myHttpConnection).setSpoolAll(true);
((HTTPConnection) myHttpConnection).setSpoolDirectory(repository.getSpoolLocation());
}
}
exchangeCapabilities();
}
}
public void fetchRepositoryRoot(DAVRepository repository) throws SVNException {
if (!repository.hasRepositoryRoot()) {
String rootPath = repository.getLocation().getURIEncodedPath();
DAVBaselineInfo info = DAVUtil.getBaselineInfo(this, repository, rootPath, -1, false, false, null);
// remove relative part from the path
rootPath = rootPath.substring(0, rootPath.length() - info.baselinePath.length());
SVNURL location = repository.getLocation();
SVNURL url = location.setPath(rootPath, true);
repository.setRepositoryRoot(url);
}
}
public void fetchRepositoryUUID(DAVRepository repository) throws SVNException {
if (!repository.hasRepositoryUUID()) {
DAVUtil.findStartingProperties(this, repository, repository.getLocation().getURIEncodedPath());
if (!repository.hasRepositoryUUID()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_NO_REPOS_UUID, "Please upgrade to server 0.19 or later");
SVNErrorManager.error(err, SVNLogType.NETWORK);
}
}
}
public HTTPStatus doPropfind(String path, HTTPHeader header, StringBuffer body, DefaultHandler handler) throws SVNException {
beforeCall();
IHTTPConnection httpConnection = getConnection();
return performHttpRequest(httpConnection, "PROPFIND", path, header, body, -1, 0, null, handler);
}
public SVNLock doGetLock(String path, DAVRepository repos) throws SVNException {
beforeCall();
DAVBaselineInfo info = DAVUtil.getBaselineInfo(this, repos, path, -1, false, true, null);
StringBuffer body = DAVLockHandler.generateGetLockRequest(null);
DAVLockHandler handler = new DAVLockHandler();
SVNErrorMessage context = SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, "Failed to fetch lock information");
IHTTPConnection httpConnection = getConnection();
HTTPStatus rc = performHttpRequest(httpConnection, "PROPFIND", path, null, body, 200, 207, null, handler, context);
String id = handler.getID();
if (id == null) {
return null;
}
String comment = handler.getComment();
String owner = rc.getHeader().getFirstHeaderValue(HTTPHeader.LOCK_OWNER_HEADER);
String created = rc.getHeader().getFirstHeaderValue(HTTPHeader.CREATION_DATE_HEADER);
Date createdDate = created != null ? SVNDate.parseDate(created) : null;
path = SVNEncodingUtil.uriDecode(info.baselinePath);
if (!path.startsWith("/")) {
path = "/" + path;
}
return new SVNLock(path, id, owner, comment, createdDate, null);
}
public SVNLock[] doGetLocks(String path) throws SVNException {
DAVGetLocksHandler handler = new DAVGetLocksHandler();
HTTPStatus status = null;
try {
status = doReport(path, DAVGetLocksHandler.generateGetLocksRequest(null), handler);
} catch (SVNException e) {
if (e.getErrorMessage() != null && e.getErrorMessage().getErrorCode() == SVNErrorCode.UNSUPPORTED_FEATURE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_NOT_IMPLEMENTED, "Server does not support locking features");
SVNErrorManager.error(err, e.getErrorMessage(), SVNLogType.NETWORK);
} else if (e.getErrorMessage() != null && e.getErrorMessage().getErrorCode() == SVNErrorCode.FS_NOT_FOUND) {
return new SVNLock[0];
}
throw e;
}
if (status.getCode() == 501) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_NOT_IMPLEMENTED, "Server does not support locking features");
SVNErrorManager.error(err, status.getError(), SVNLogType.NETWORK);
} else if (status.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return new SVNLock[0];
} else if (status.getError() != null && status.getError().getErrorCode() == SVNErrorCode.UNSUPPORTED_FEATURE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_NOT_IMPLEMENTED, "Server does not support locking features");
SVNErrorManager.error(err, status.getError(), SVNLogType.NETWORK);
} else if (status.getError() != null) {
SVNErrorManager.error(status.getError(), SVNLogType.NETWORK);
}
return handler.getLocks();
}
public SVNLock doLock(String repositoryPath, String path, DAVRepository repos, String comment, boolean force, long revision) throws SVNException {
beforeCall();
DAVBaselineInfo info = DAVUtil.getBaselineInfo(this, repos, path, -1, false, true, null);
StringBuffer body = DAVLockHandler.generateSetLockRequest(null, comment);
HTTPHeader header = new HTTPHeader();
header.setHeaderValue(HTTPHeader.DEPTH_HEADER, "0");
header.setHeaderValue(HTTPHeader.TIMEOUT_HEADER, "Infinite");
header.setHeaderValue(HTTPHeader.CONTENT_TYPE_HEADER, "text/xml; charset=\"utf-8\"");
if (revision >= 0) {
header.setHeaderValue(HTTPHeader.SVN_VERSION_NAME_HEADER, Long.toString(revision));
}
if (force) {
header.setHeaderValue(HTTPHeader.SVN_OPTIONS_HEADER, "lock-steal");
}
DAVLockHandler handler = new DAVLockHandler();
SVNErrorMessage context = SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, "Lock request failed");
SVNException exception = null;
IHTTPConnection httpConnection = getConnection();
try {
myLastStatus = performHttpRequest(httpConnection, "LOCK", path, header, body, -1, 0, null, handler, context);
} catch (SVNException e) {
myLastStatus = httpConnection.getLastStatus();
exception = e;
}
if (myLastStatus != null) {
if (myLastStatus.getCode() == 405) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_OUT_OF_DATE, "Lock request failed: {0} {1}",
new Object[] {myLastStatus.getCode(), myLastStatus.getReason()});
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
if (myLastStatus.getError() != null) {
myLastStatus.getError().setChildErrorMessage(null); // subversion doesn't have a child message for lock
SVNErrorManager.error(myLastStatus.getError(), SVNLogType.NETWORK);
}
if (myLastStatus.getHeader() == null) {
if (exception != null) {
throw exception;
}
return null;
}
String userName = myLastStatus.getHeader().getFirstHeaderValue(HTTPHeader.LOCK_OWNER_HEADER);
if (userName == null) {
userName = httpConnection.getLastValidCredentials() != null ? httpConnection.getLastValidCredentials().getUserName() : null;
}
String created = myLastStatus.getHeader().getFirstHeaderValue(HTTPHeader.CREATION_DATE_HEADER);
if (userName == null || created == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_DAV_MALFORMED_DATA, "Incomplete lock data returned");
SVNErrorManager.error(err, SVNLogType.NETWORK);
}
Date createdDate = created != null ? SVNDate.parseDate(created) : null;
return new SVNLock(repositoryPath, handler.getID(), userName, comment, createdDate, null);
}
if (exception != null) {
throw exception;
}
return null;
}
public void doUnlock(String path, DAVRepository repos, String id, boolean force) throws SVNException {
beforeCall();
if (id == null) {
SVNLock lock = doGetLock(path, repos);
if (lock != null) {
id = lock.getID();
}
if (id == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_NOT_LOCKED, "''{0}'' is not locked in the repository", path);
SVNErrorManager.error(err, SVNLogType.NETWORK);
}
}
HTTPHeader header = new HTTPHeader();
header.setHeaderValue(HTTPHeader.LOCK_TOKEN_HEADER, "<" + id + ">");
if (force) {
header.setHeaderValue(HTTPHeader.SVN_OPTIONS_HEADER, "lock-break");
}
SVNErrorMessage context = SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, "Unlock request failed");
IHTTPConnection httpConnection = getConnection();
SVNException exception = null;
try {
myLastStatus = performHttpRequest(httpConnection, "UNLOCK", path, header, (StringBuffer) null, -1, 0, null, null, context);
} catch (SVNException e) {
myLastStatus = httpConnection.getLastStatus();
exception = e;
}
if (myLastStatus != null) {
switch (myLastStatus.getCode()) {
case 403:
SVNErrorManager.error(
SVNErrorMessage.create(SVNErrorCode.FS_LOCK_OWNER_MISMATCH, "Unlock failed on ''{0}'' (403 Forbidden)", path),
SVNLogType.NETWORK);
break;
case 400:
SVNErrorManager.error(
SVNErrorMessage.create(SVNErrorCode.FS_NO_SUCH_LOCK, "No lock on path ''{0}'' (400 Bad Request)", path),
SVNLogType.NETWORK);
break;
default:
break;
}
if (myLastStatus.getCode() >= 300 && myLastStatus.getError() != null) {
SVNErrorMessage error = myLastStatus.getError() != null ? myLastStatus.getError() : SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED);
SVNErrorManager.error(error, SVNLogType.NETWORK);
}
}
if (exception != null) {
throw exception;
}
}
public void doGet(String path, OutputStream os) throws SVNException {
beforeCall();
SVNErrorMessage context = SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, "GET request failed for ''{0}''", path);
IHTTPConnection httpConnection = getConnection();
performHttpRequest(httpConnection, "GET", path, null, (StringBuffer) null, 200, 226, os, null, context);
}
public void doGet(String path, String deltaBaseVersionURL, OutputStream os) throws SVNException {
beforeCall();
HTTPHeader header = null;
if (deltaBaseVersionURL != null) {
header = new HTTPHeader();
header.addHeaderValue(HTTPHeader.SVN_DELTA_BASE_HEADER, deltaBaseVersionURL);
}
SVNErrorMessage context = SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED,
"GET request failed for ''{0}''", path);
IHTTPConnection httpConnection = getConnection();
performHttpRequest(httpConnection, "GET", path, header, (StringBuffer) null, 200, 226, os, null, context);
}
public HTTPStatus doReport(String path, StringBuffer requestBody, DefaultHandler handler) throws SVNException {
return doReport(path, requestBody, handler, false);
}
public HTTPStatus doReport(String path, StringBuffer requestBody, DefaultHandler handler, boolean spool) throws SVNException {
beforeCall();
IHTTPConnection httpConnection = getConnection();
httpConnection.setSpoolResponse(spool || isReportResponseSpooled());
try {
HTTPHeader header = new HTTPHeader();
header.addHeaderValue(HTTPHeader.ACCEPT_ENCODING_HEADER, "svndiff1;q=0.9,svndiff;q=0.8");
return performHttpRequest(httpConnection, "REPORT", path, header, requestBody, -1, 0, null, handler);
} finally {
httpConnection.setSpoolResponse(false);
}
}
public void doProppatch(String repositoryPath, String path, StringBuffer requestBody, DefaultHandler handler, SVNErrorMessage context) throws SVNException {
beforeCall();
HTTPHeader header = null;
if (myLocks != null && repositoryPath != null && myLocks.containsKey(repositoryPath)) {
header = new HTTPHeader();
header.setHeaderValue(HTTPHeader.IF_HEADER, "(<" + myLocks.get(repositoryPath) + ">)");
}
IHTTPConnection httpConnection = getConnection();
try {
performHttpRequest(httpConnection, "PROPPATCH", path, header, requestBody, 200, 207, null, handler, context);
} catch (SVNException e) {
HTTPStatus status = httpConnection.getLastStatus();
if (status != null) {
switch (status.getCode()) {
case 423:
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_NOT_LOCKED,
"No lock on path ''{0}''; repository is unchanged", new Object[]{path});
SVNErrorManager.error(err, e, SVNLogType.NETWORK);
break;
default:
break;
}
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_DAV_PROPPATCH_FAILED, "At least one property change failed; repository is unchanged");
SVNErrorManager.error(err, e, SVNLogType.NETWORK);
}
}
public String doMakeActivity(ISVNWorkspaceMediator mediator) throws SVNException {
beforeCall();
String url = null;
if (mediator != null) {
SVNPropertyValue property = mediator.getWorkspaceProperty("", SVNProperty.ACTIVITY_URL);
if (property != null && property.isString()) {
url = property.getString();
}
}
String locationPath = SVNEncodingUtil.uriEncode(getLocation().getPath());
if (url == null) {
url = getActivityCollectionURL(locationPath, false);
}
IHTTPConnection httpConnection = getConnection();
String activityURL = SVNPathUtil.append(url, generateUUID());
HTTPStatus status = performHttpRequest(httpConnection, "MKACTIVITY", activityURL, null, (StringBuffer) null, 201, 404, null, null);
if (status.getCode() == 404) {
url = getActivityCollectionURL(locationPath, true);
activityURL = SVNPathUtil.append(url, generateUUID());
status = performHttpRequest(httpConnection, "MKACTIVITY", activityURL, null, (StringBuffer) null, 201, 0, null, null);
}
if (url != null && mediator != null) {
mediator.setWorkspaceProperty("", SVNProperty.ACTIVITY_URL, SVNPropertyValue.create(url));
}
return activityURL;
}
public HTTPStatus doDelete(String path) throws SVNException {
beforeCall();
IHTTPConnection httpConnection = getConnection();
return performHttpRequest(httpConnection, "DELETE", path, null, (StringBuffer) null, 404, 204, null, null);
}
public HTTPStatus doDelete(String repositoryPath, String path, long revision) throws SVNException {
beforeCall();
HTTPHeader header = new HTTPHeader();
if (revision >= 0) {
header.setHeaderValue(HTTPHeader.SVN_VERSION_NAME_HEADER, Long.toString(revision));
}
header.setHeaderValue(HTTPHeader.DEPTH_HEADER, "infinity");
StringBuffer request = null;
if (myLocks != null) {
if (myLocks.containsKey(repositoryPath)) {
header.setHeaderValue(HTTPHeader.IF_HEADER, "<" + repositoryPath + "> (<" + myLocks.get(repositoryPath) + ">)");
}
if (myKeepLocks) {
header.setHeaderValue(HTTPHeader.SVN_OPTIONS_HEADER, "keep-locks");
}
request = new StringBuffer();
SVNXMLUtil.addXMLHeader(request);
String locationPath = getLocation().getPath();
locationPath = SVNEncodingUtil.uriEncode(locationPath);
request = DAVMergeHandler.generateLockDataRequest(request, locationPath, repositoryPath, myLocks);
}
IHTTPConnection httpConnection = getConnection();
SVNException exception = null;
try {
myLastStatus = performHttpRequest(httpConnection, "DELETE", path, header, request, 204, 0, null, null);
} catch (SVNException e) {
myLastStatus = httpConnection.getLastStatus();
exception = e;
}
if (myLastStatus != null) {
if (myLastStatus.getError() != null) {
SVNErrorCode errCode = myLastStatus.getError().getErrorCode();
if (errCode == SVNErrorCode.FS_BAD_LOCK_TOKEN || errCode == SVNErrorCode.FS_NO_LOCK_TOKEN ||
errCode == SVNErrorCode.FS_LOCK_OWNER_MISMATCH || errCode == SVNErrorCode.FS_PATH_ALREADY_LOCKED) {
Map childTokens = null;
if (myLocks != null) {
childTokens = new SVNHashMap();
for (Iterator locksIter = myLocks.keySet().iterator(); locksIter.hasNext();) {
String lockPath = (String) locksIter.next();
if (lockPath.startsWith(path)) {
childTokens.put(lockPath, myLocks.get(lockPath));
}
}
}
if (childTokens == null || childTokens.isEmpty()) {
SVNErrorManager.error(myLastStatus.getError(), SVNLogType.NETWORK);
} else {
myLastStatus.setError(null);
}
String token = myLocks != null ? (String) myLocks.get(path) : null;
if (token != null) {
childTokens.put(path, token);
}
request = new StringBuffer();
String locationPath = getLocation().getPath();
locationPath = SVNEncodingUtil.uriEncode(locationPath);
request = DAVMergeHandler.generateLockDataRequest(request, locationPath, repositoryPath, childTokens);
try {
myLastStatus = performHttpRequest(httpConnection, "DELETE", path, header, request, 204, 404, null, null);
} catch (SVNException e) {
myLastStatus = httpConnection.getLastStatus();
exception = e;
}
if (myLastStatus.getError() != null) {
SVNErrorManager.error(myLastStatus.getError(), SVNLogType.NETWORK);
}
if (exception != null) {
throw exception;
}
return myLastStatus;
}
SVNErrorManager.error(myLastStatus.getError(), SVNLogType.NETWORK);
}
}
if (exception != null) {
throw exception;
}
return myLastStatus;
}
public HTTPStatus doMakeCollection(String path) throws SVNException {
beforeCall();
IHTTPConnection httpConnection = getConnection();
return performHttpRequest(httpConnection, "MKCOL", path, null, (StringBuffer) null, 201, 0, null, null);
}
public HTTPStatus doPutDiff(String repositoryPath, String path, InputStream data, long size, String baseChecksum, String textChecksum) throws SVNException {
beforeCall();
HTTPHeader headers = new HTTPHeader();
headers.setHeaderValue(HTTPHeader.CONTENT_TYPE_HEADER, HTTPHeader.SVNDIFF_MIME_TYPE);
headers.setHeaderValue(HTTPHeader.CONTENT_LENGTH_HEADER, size + "");
if (myLocks != null && myLocks.containsKey(repositoryPath)) {
headers.setHeaderValue(HTTPHeader.IF_HEADER, "<" + repositoryPath + "> (<" + myLocks.get(repositoryPath) + ">)");
}
if (baseChecksum != null) {
headers.setHeaderValue(HTTPHeader.BASE_MD5, baseChecksum);
}
if (textChecksum != null) {
headers.setHeaderValue(HTTPHeader.TEXT_MD5, textChecksum);
}
IHTTPConnection httpConnection = getConnection();
return performHttpRequest(httpConnection, "PUT", path, headers, data, 201, 204, null, null);
}
public HTTPStatus doMerge(String activityURL, boolean response, DefaultHandler handler) throws SVNException {
beforeCall();
String locationPath = SVNEncodingUtil.uriEncode(getLocation().getPath());
StringBuffer request = DAVMergeHandler.generateMergeRequest(null, locationPath, activityURL, myLocks);
HTTPHeader header = null;
if (!response || (myLocks != null && !myKeepLocks)) {
header = new HTTPHeader();
String value = "";
if (!response) {
value += "no-merge-response";
}
if (myLocks != null && !myKeepLocks) {
value += " release-locks";
}
header.setHeaderValue(HTTPHeader.SVN_OPTIONS_HEADER, value);
}
IHTTPConnection httpConnection = getConnection();
return performHttpRequest(httpConnection, "MERGE", getLocation().getURIEncodedPath(), header, request, -1, 0, null, handler);
}
public HTTPStatus doCheckout(String activityPath, String repositoryPath, String path, boolean allow404) throws SVNException {
beforeCall();
StringBuffer request = new StringBuffer();
Collection namespaces = new LinkedList();
namespaces.add(DAVElement.DAV_NAMESPACE);
SVNXMLUtil.addXMLHeader(request);
SVNXMLUtil.openNamespaceDeclarationTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, "checkout", namespaces,
SVNXMLUtil.PREFIX_MAP, request);
SVNXMLUtil.openXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, "activity-set", SVNXMLUtil.XML_STYLE_NORMAL,
null, request);
SVNXMLUtil.openCDataTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, "href", activityPath, request);
SVNXMLUtil.closeXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, "activity-set", request);
SVNXMLUtil.closeXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, "checkout", request);
HTTPHeader header = null;
if (myLocks != null && repositoryPath != null && myLocks.containsKey(repositoryPath)) {
header = new HTTPHeader();
header.setHeaderValue(HTTPHeader.IF_HEADER, "(<" + myLocks.get(repositoryPath) + ">)");
}
IHTTPConnection httpConnection = getConnection();
HTTPStatus status = performHttpRequest(httpConnection, "CHECKOUT", path, header, request, 201, allow404 ? 404 : 0, null, null);
if (allow404 && status.getCode() == 404 && status.getError() != null) {
status.setError(null);
}
// update location to be a path!
if (status.getHeader().hasHeader(HTTPHeader.LOCATION_HEADER)) {
String location = null;
final String locationHeaderValue = status.getHeader().getFirstHeaderValue(HTTPHeader.LOCATION_HEADER);
if (locationHeaderValue.startsWith("/")) {
location = locationHeaderValue;
} else {
SVNURL url = SVNURL.parseURIEncoded(status.getHeader().getFirstHeaderValue(HTTPHeader.LOCATION_HEADER));
location = url.getURIEncodedPath();
}
status.getHeader().setHeaderValue(HTTPHeader.LOCATION_HEADER, location);
}
return status;
}
public void doCopy(String src, String dst, int depth) throws SVNException {
beforeCall();
HTTPHeader header = new HTTPHeader();
header.setHeaderValue(HTTPHeader.DESTINATION_HEADER, dst);
header.setHeaderValue(HTTPHeader.DEPTH_HEADER, depth > 0 ? "infinity" : "0");
SVNErrorMessage context = SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, "COPY of {0}", src);
IHTTPConnection httpConnection = getConnection();
HTTPStatus status = performHttpRequest(httpConnection, "COPY", src, header, (StringBuffer) null, -1, 0, null, null, context);
if (status.getCode() >= 300 && status.getError() != null) {
SVNErrorMessage err = status.getError();
SVNErrorManager.error(err, SVNLogType.NETWORK);
}
}
public void close() {
if (myHttpConnection != null) {
myHttpConnection.close();
myHttpConnection = null;
myLocks = null;
myKeepLocks = false;
}
}
public void setLocks(Map locks, boolean keepLocks) {
myLocks = locks;
myKeepLocks = keepLocks;
}
public void clearAuthenticationCache() {
if (myHttpConnection != null) {
myHttpConnection.clearAuthenticationCache();
}
}
public String getCapabilityResponse(SVNCapability capability) throws SVNException {
if (myCapabilities == null || myCapabilities.get(capability) == null) {
exchangeCapabilities();
}
return (String) myCapabilities.get(capability);
}
public void setCapability(SVNCapability capability, String capResult){
myCapabilities.put(capability, capResult);
}
protected IHTTPConnection getConnection() {
return myHttpConnection;
}
protected void exchangeCapabilities() throws SVNException {
String path = SVNEncodingUtil.uriEncode(getLocation().getPath());
IHTTPConnection httpConnection = getConnection();
HTTPStatus status = performHttpRequest(httpConnection, "OPTIONS", path, null, DAVOptionsHandler.OPTIONS_REQUEST, 200, 0, null, null);
if (status.getCode() == 200) {
parseCapabilities(status);
} else {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_DAV_OPTIONS_REQ_FAILED,
"OPTIONS request (for capabilities) got HTTP response code {0}",
new Integer(status.getCode()));
SVNErrorManager.error(err, SVNLogType.NETWORK);
}
}
protected SVNRepository getRepository() {
return myRepository;
}
private void parseCapabilities(HTTPStatus status) {
if (myCapabilities == null) {
myCapabilities = new SVNHashMap();
}
myCapabilities.put(SVNCapability.DEPTH, DAV_CAPABILITY_NO);
myCapabilities.put(SVNCapability.MERGE_INFO, DAV_CAPABILITY_NO);
myCapabilities.put(SVNCapability.LOG_REVPROPS, DAV_CAPABILITY_NO);
myCapabilities.put(SVNCapability.ATOMIC_REVPROPS, DAV_CAPABILITY_NO);
myCapabilities.put(SVNCapability.INHERITED_PROPS, DAV_CAPABILITY_NO);
myCapabilities.put(SVNCapability.EPHEMERAL_PROPS, DAV_CAPABILITY_NO);
Collection capValues = status.getHeader().getHeaderValues(HTTPHeader.DAV_HEADER);
if (capValues != null) {
for (Iterator valuesIter = capValues.iterator(); valuesIter.hasNext();) {
String value = (String) valuesIter.next();
if (DAVElement.DEPTH_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.DEPTH, DAV_CAPABILITY_YES);
} else if (DAVElement.MERGE_INFO_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.MERGE_INFO, DAV_CAPABILITY_SERVER_YES);
} else if (DAVElement.LOG_REVPROPS_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.LOG_REVPROPS, DAV_CAPABILITY_YES);
} else if (DAVElement.PARTIAL_REPLAY_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.PARTIAL_REPLAY, DAV_CAPABILITY_YES);
} else if (DAVElement.ATOMIC_REVPROPS_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.ATOMIC_REVPROPS, DAV_CAPABILITY_YES);
} else if (DAVElement.INHERITED_PROPS_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.INHERITED_PROPS, DAV_CAPABILITY_YES);
} else if (DAVElement.EPHEMERAL_PROPS_OPTION.equalsIgnoreCase(value)) {
myCapabilities.put(SVNCapability.EPHEMERAL_PROPS, DAV_CAPABILITY_YES);
}
}
}
}
private String getActivityCollectionURL(String path, boolean force) throws SVNException {
if (!force && myActivityCollectionURL != null) {
return myActivityCollectionURL;
}
DAVOptionsHandler handler = new DAVOptionsHandler();
IHTTPConnection httpConnection = getConnection();
performHttpRequest(httpConnection, "OPTIONS", path, null, DAVOptionsHandler.OPTIONS_REQUEST, -1, 0, null, handler);
myActivityCollectionURL = handler.getActivityCollectionURL();
if (myActivityCollectionURL == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_DAV_OPTIONS_REQ_FAILED,
"The OPTIONS request did not include the requested activity-collection-set; this often means that the URL is not WebDAV-enabled");
SVNErrorManager.error(err, SVNLogType.NETWORK);
}
return myActivityCollectionURL;
}
private static String generateUUID() {
try {
return SVNUUIDGenerator.formatUUID(SVNUUIDGenerator.generateUUID());
} catch (SVNException svne) {
long time = System.currentTimeMillis();
String uuid = Long.toHexString(time);
int zeroes = 16 - uuid.length();
for(int i = 0; i < zeroes; i++) {
uuid = "0" + uuid;
}
return uuid;
}
}
private void beforeCall() {
myLastStatus = null;
}
private HTTPStatus performHttpRequest(IHTTPConnection httpConnection, String method, String path, HTTPHeader header, StringBuffer body, int ok1, int ok2, OutputStream dst, DefaultHandler handler) throws SVNException {
myLastStatus = null;
try {
myLastStatus = httpConnection.request(method, path, header, body, ok1, ok2, dst, handler);
return myLastStatus;
} finally {
myLastStatus = httpConnection.getLastStatus();
}
}
private HTTPStatus performHttpRequest(IHTTPConnection httpConnection, String method, String src, HTTPHeader header, StringBuffer body, int ok1, int ok2, OutputStream dst, DefaultHandler handler, SVNErrorMessage context) throws SVNException {
myLastStatus = null;
try {
myLastStatus = httpConnection.request(method, src, header, body, ok1, ok2, dst, handler, context);
return myLastStatus;
} finally {
myLastStatus = httpConnection.getLastStatus();
}
}
private HTTPStatus performHttpRequest(IHTTPConnection httpConnection, String method, String path, HTTPHeader headers, InputStream data, int ok1, int ok2, OutputStream dst, DefaultHandler handler) throws SVNException {
myLastStatus = null;
try {
myLastStatus = httpConnection.request(method, path, headers, data, ok1, ok2, dst, handler);
return myLastStatus;
} finally {
myLastStatus = httpConnection.getLastStatus();
}
}
}