/* * ==================================================================== * 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; import java.io.File; import java.io.OutputStream; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.logging.Level; import javax.servlet.http.HttpServletResponse; import org.tmatesoft.svn.core.ISVNDirEntryHandler; import org.tmatesoft.svn.core.SVNDirEntry; 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.SVNProperties; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNRevisionProperty; import org.tmatesoft.svn.core.internal.io.dav.DAVElement; import org.tmatesoft.svn.core.internal.io.fs.FSFS; import org.tmatesoft.svn.core.internal.io.fs.FSRepository; import org.tmatesoft.svn.core.internal.io.fs.FSRevisionNode; import org.tmatesoft.svn.core.internal.io.fs.FSRoot; import org.tmatesoft.svn.core.internal.io.fs.FSTransactionInfo; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; /** * @author TMate Software Ltd. * @version 1.2.0 */ public class DAVResource { public static final long INVALID_REVISION = SVNRepository.INVALID_REVISION; public static final String DEFAULT_COLLECTION_CONTENT_TYPE = "text/html; charset=\"utf-8\""; public static final String DEFAULT_FILE_CONTENT_TYPE = "text/plain"; private DAVRepositoryManager myRepositoryManager; private DAVResourceURI myResourceURI; private FSRepository myRepository; private long myRevision; private long myVersion; private boolean myIsCollection; private boolean myIsSVNClient; private boolean myIsAutoCheckedOut; private String myDeltaBase; private String myClientOptions; private String myBaseChecksum; private String myResultChecksum; private String myUserName; private SVNProperties mySVNProperties; private Collection myDeadProperties; private Collection myEntries; private File myActivitiesDB; private FSFS myFSFS; private String myTxnName; private FSRoot myRoot; private FSTransactionInfo myTxnInfo; private Map myClientCapabilities; private Collection myLockTokens; /** * DAVResource constructor * * @param repository repository resource connect to * @param context contains requested url requestContext and name of repository if servlet use SVNParentPath directive. * @param uri special uri for DAV requests can be /path or /SPECIAL_URI/xxx/path * @param label request's label header * @param useCheckedIn special case for VCC resource * @throws SVNException if an error occurs while fetching repository properties. */ public DAVResource(SVNRepository repository, DAVRepositoryManager manager, DAVResourceURI resourceURI, boolean isSVNClient, String deltaBase, long version, String clientOptions, String baseChecksum, String resultChecksum, String userName, File activitiesDB, Collection lockTokens, Map clientCapabilities) throws DAVException { myRepositoryManager = manager; myRepository = (FSRepository) repository; try { myRepository.testConnection();//this should create an FSFS object } catch (SVNException svne) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.FSFS, svne.getMessage()); SVNErrorMessage err = SVNErrorMessage.create(svne.getErrorMessage().getErrorCode(), "Could not open the requested SVN filesystem"); throw DAVException.convertError(err, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not fetch resource information.", null); } myLockTokens = lockTokens; myClientCapabilities = clientCapabilities; myFSFS = myRepository.getFSFS(); myResourceURI = resourceURI; myIsSVNClient = isSVNClient; myDeltaBase = deltaBase; myVersion = version; myClientOptions = clientOptions; myBaseChecksum = baseChecksum; myResultChecksum = resultChecksum; myRevision = resourceURI.getRevision(); myUserName = userName; myActivitiesDB = activitiesDB; DAVResourceHelper.prepareResource(this); } public DAVResource(DAVRepositoryManager manager, SVNRepository repository, DAVResourceURI resourceURI, long revision, boolean isSVNClient, String deltaBase, long version, String clientOptions, String baseChecksum, String resultChecksum, String userName, File activitiesDB, Collection lockTokens, Map clientCapabilities) { myRepositoryManager = manager; myResourceURI = resourceURI; myRepository = (FSRepository) repository; myFSFS = myRepository.getFSFS(); myRevision = revision; myIsSVNClient = isSVNClient; myDeltaBase = deltaBase; myVersion = version; myClientOptions = clientOptions; myBaseChecksum = baseChecksum; myResultChecksum = resultChecksum; myUserName = userName; myActivitiesDB = activitiesDB; myLockTokens = lockTokens; myClientCapabilities = clientCapabilities; } public DAVResource() { } public void setRoot(FSRoot root) { myRoot = root; } public FSRoot getRoot() { return myRoot; } public FSTransactionInfo getTxnInfo() { return myTxnInfo; } public void setTxnInfo(FSTransactionInfo txnInfo) { myTxnInfo = txnInfo; } public DAVResourceURI getResourceURI() { return myResourceURI; } public SVNRepository getRepository() { return myRepository; } public long getRevision() { return myRevision; } public boolean exists() { return myResourceURI.exists(); } public boolean isVersioned() { return myResourceURI.isVersioned(); } public boolean isWorking() { return myResourceURI.isWorking(); } public boolean isBaseLined() { return myResourceURI.isBaseLined(); } public DAVResourceType getType() { return getResourceURI().getType(); } //TODO: refactor DAVResourceKind later and name //this method as getPrivateResourceKind() public DAVResourceKind getKind() { return getResourceURI().getKind(); } public String getActivityID() { return myResourceURI.getActivityID(); } public boolean lacksETagPotential() { DAVResourceType type = getResourceURI().getType(); return !exists() || (type != DAVResourceType.REGULAR && type != DAVResourceType.VERSION) || (type == DAVResourceType.VERSION && isBaseLined()); } public boolean canBeActivity() { return isAutoCheckedOut() || (getType() == DAVResourceType.ACTIVITY && !exists()); } public boolean isCollection() { return myIsCollection; } public boolean isSVNClient() { return myIsSVNClient; } public DAVAutoVersion getAutoVersion() { if (getType() == DAVResourceType.VERSION && isBaseLined()) { return DAVAutoVersion.ALWAYS; } DAVConfig config = myRepositoryManager.getDAVConfig(); if (config.isAutoVersioning()) { if (getType() == DAVResourceType.REGULAR) { return DAVAutoVersion.ALWAYS; } if (getType() == DAVResourceType.WORKING && isAutoCheckedOut()) { return DAVAutoVersion.ALWAYS; } } return DAVAutoVersion.NEVER; } public String getUserName() { return myUserName; } public String getDeltaBase() { return myDeltaBase; } public long getVersion() { return myVersion; } public String getClientOptions() { return myClientOptions; } public String getBaseChecksum() { return myBaseChecksum; } public String getResultChecksum() { return myResultChecksum; } public File getActivitiesDB() { return myActivitiesDB; } public void versionControl(String target) throws DAVException { if (exists()) { throw new DAVException("vsn_control called on already-versioned resource.", HttpServletResponse.SC_BAD_REQUEST, 0); } if (target != null) { throw new DAVException("vsn_control called with non-null target.", 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); } } public Iterator getChildren() throws SVNException { return new Iterator() { Iterator entriesIterator = getEntries().iterator(); public void remove() { } public boolean hasNext() { return entriesIterator.hasNext(); } public Object next() { SVNDirEntry entry = (SVNDirEntry) entriesIterator.next(); String childURI = DAVPathUtil.append(getResourceURI().getURI(), entry.getName()); try { DAVResourceURI newResourceURI = new DAVResourceURI(getResourceURI().getContext(), childURI, null, false); return new DAVResource(myRepositoryManager, getRepository(), newResourceURI, getRevision(), isSVNClient(), getDeltaBase(), getVersion(), getClientOptions(), null, null, getUserName(), getActivitiesDB(), getLockTokens(), getClientCapabilities()); } catch (SVNException e) { return null; } } }; } public Map getClientCapabilities() { return myClientCapabilities; } public Collection getLockTokens() { return myLockTokens; } public void setLockTokens(Collection lockTokens) { if (myLockTokens != null) { myLockTokens.addAll(lockTokens); } myLockTokens = lockTokens; } public Collection getEntries() throws SVNException { if (isCollection() && myEntries == null) { myEntries = new LinkedList(); getRepository().getDir(getResourceURI().getPath(), getRevision(), null, SVNDirEntry.DIRENT_KIND, myEntries); } return myEntries; } public long getCreatedRevision() throws SVNException { String revisionParameter = getProperty(SVNProperty.COMMITTED_REVISION); try { return Long.parseLong(revisionParameter); } catch (NumberFormatException e) { return getRevision(); } } public long getCreatedRevisionUsingFS(String path) throws SVNException { path = path == null ? getResourceURI().getPath() : path; FSRevisionNode node = myRoot.getRevisionNode(path); return node.getCreatedRevision(); } public Date getLastModified() throws SVNException { if (lacksETagPotential()) { return null; } return getRevisionDate(getCreatedRevision()); } public Date getRevisionDate(long revision) throws SVNException { //TODO: insert here later an authz check return SVNDate.parseDate(getRevisionProperty(revision, SVNRevisionProperty.DATE)); } public String getETag() { if (lacksETagPotential()) { return null; } long createdRevision = -1; try { createdRevision = getCreatedRevisionUsingFS(null); } catch (SVNException svne) { return null; } StringBuffer eTag = new StringBuffer(); eTag.append(isCollection() ? "W/" : ""); eTag.append("\""); eTag.append(createdRevision); eTag.append("/"); eTag.append(SVNEncodingUtil.xmlEncodeCDATA(getResourceURI().getPath(), true)); eTag.append("\""); return eTag.toString(); } public String getRepositoryUUID(boolean forceConnect) throws SVNException { return getRepository().getRepositoryUUID(forceConnect); } public String getContentType() throws SVNException { if (getResourceURI().isBaseLined() && getResourceURI().getType() == DAVResourceType.VERSION) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_PROPS_NOT_FOUND, "Failed to determine property"), SVNLogType.NETWORK); return null; } if (getResourceURI().getType() == DAVResourceType.PRIVATE && getResourceURI().getKind() == DAVResourceKind.VCC) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_PROPS_NOT_FOUND, "Failed to determine property"), SVNLogType.NETWORK); return null; } if (isCollection()) { return DEFAULT_COLLECTION_CONTENT_TYPE; } String contentType = getProperty(SVNProperty.MIME_TYPE); if (contentType != null) { return contentType; } return DEFAULT_FILE_CONTENT_TYPE; } public long getLatestRevision() throws SVNException { return getRepository().getLatestRevision(); } //TODO: remove this method later, use getContentLength(String path) instead /** * @deprecated */ public long getContentLength() throws SVNException { SVNDirEntry entry = getRepository().getDir(getResourceURI().getPath(), getRevision(), false, null); return entry.getSize(); } public long getContentLength(String path) throws SVNException { path = path == null ? getResourceURI().getPath() : path; FSRevisionNode node = myRoot.getRevisionNode(path); return node.getFileLength(); } public SVNLock[] getLocks() throws SVNException { if (getResourceURI().getPath() == null) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, "get-locks-report run on resource which doesn't represent a path within a repository."), SVNLogType.NETWORK); } return getRepository().getLocks(getResourceURI().getPath()); } public SVNLock getLock() throws SVNException { return getRepository().getLock(getResourceURI().getPath()); } public void unlock(String token, boolean force) throws SVNException { Map pathsToTokens = new HashMap(); pathsToTokens.put(getResourceURI().getPath(), token); getRepository().unlock(pathsToTokens, force, null); } public String getAuthor(long revision) throws SVNException { return getRevisionProperty(revision, SVNRevisionProperty.AUTHOR); } /** * @deprecated use getMD5Checksum() instead */ public String getMD5Checksum() throws SVNException { return getProperty(SVNProperty.CHECKSUM); } public String getMD5Checksum(String path) throws SVNException { path = path == null ? getResourceURI().getPath() : path; FSRevisionNode node = myRoot.getRevisionNode(path); return node.getFileMD5Checksum(); } public String getLog(long revision) throws SVNException { return getRevisionProperty(revision, SVNRevisionProperty.LOG); } //TODO: replace later with getProperty(path, propName) /** * @deprecated */ public String getProperty(String propertyName) throws SVNException { return getSVNProperties().getStringValue(propertyName); } public SVNPropertyValue getProperty(String path, String propertyName) throws SVNException { return getSVNProperties(path).getSVNPropertyValue(propertyName); } public String getRevisionProperty(long revision, String propertyName) throws SVNException { SVNPropertyValue value = getRepository().getRevisionPropertyValue(revision, propertyName); return value == null ? null : value.getString(); } public void writeTo(OutputStream out) throws SVNException { if (isCollection()) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED), SVNLogType.NETWORK); } getRepository().getFile(getResourceURI().getPath(), getRevision(), null, out); } public boolean isAutoCheckedOut() { return myIsAutoCheckedOut; } public void setIsAutoCkeckedOut(boolean isAutoCheckedOut) { myIsAutoCheckedOut = isAutoCheckedOut; } public String getTxnName() { return myTxnName; } public void setExists(boolean exists) { myResourceURI.setExists(exists); } public void setVersioned(boolean isVersioned) { myResourceURI.setVersioned(isVersioned); } public void setWorking(boolean isWorking) { myResourceURI.setWorking(isWorking); } public void setBaseLined(boolean isBaseLined) { myResourceURI.setBaseLined(isBaseLined); } public void setCollection(boolean isCollection) { myIsCollection = isCollection; } public void setTxnName(String txnName) { myTxnName = txnName; } public void setRevision(long revision) { myRevision = revision; myResourceURI.setRevision(revision); } public void setResourceURI(DAVResourceURI resourceURI) { myResourceURI = resourceURI; } public boolean equals(Object o) { if (o == null || o.getClass() != this.getClass()) { return false; } if (o == this) { return true; } DAVResource otherResource = (DAVResource) o; if (!isOurResource(otherResource)) { return false; } String myRequestURI = myResourceURI.getRequestURI(); String otherRequestURI = otherResource.getResourceURI().getRequestURI(); return myRequestURI.equals(otherRequestURI); } public DAVResource dup() { DAVResource copy = new DAVResource(); copyTo(copy); return copy; } public FSFS getFSFS() { return myFSFS; } public DAVRepositoryManager getRepositoryManager() { return myRepositoryManager; } public boolean isParentResource(DAVResource resource) { if (!isOurResource(resource)) { return false; } String thisURIPath = myResourceURI.getURI(); String otherURIPath = resource.getResourceURI().getURI(); return otherURIPath.length() > thisURIPath.length() && otherURIPath.startsWith(thisURIPath) && otherURIPath.charAt(thisURIPath.length()) == '/'; } public SVNProperties getSVNProperties(String path) throws SVNException { path = path == null ? getResourceURI().getPath() : path; if (mySVNProperties == null) { mySVNProperties = myFSFS.getProperties(myRoot.getRevisionNode(path)); } return mySVNProperties; } private boolean isOurResource(DAVResource resource) { File reposRoot1 = myFSFS.getDBRoot(); File reposRoot2 = resource.myFSFS.getDBRoot(); if (!reposRoot1.equals(reposRoot2)) { return false; } return true; } //TODO: replace occurances of getSVNProperties() with getSVNProperties(path) /** * @deprecated */ public SVNProperties getSVNProperties() throws SVNException { if (mySVNProperties == null) { mySVNProperties = new SVNProperties(); if (getResourceURI().getType() == DAVResourceType.REGULAR) { if (isCollection()) { getRepository().getDir(getResourceURI().getPath(), getRevision(), mySVNProperties, (ISVNDirEntryHandler) null); } else { getRepository().getFile(getResourceURI().getPath(), getRevision(), mySVNProperties, null); } } } return mySVNProperties; } protected void copyTo(DAVResource copy) { copy.myRepositoryManager = myRepositoryManager; copy.myResourceURI = myResourceURI.dup(); copy.myRepository = myRepository; copy.myRevision = myRevision; copy.myIsCollection = myIsCollection; copy.myIsSVNClient = myIsCollection; copy.myIsAutoCheckedOut = myIsAutoCheckedOut; copy.myDeltaBase = myDeltaBase; copy.myVersion = myVersion; copy.myClientOptions = myClientOptions; copy.myBaseChecksum = myBaseChecksum; copy.myResultChecksum = myResultChecksum; copy.myUserName = myUserName; copy.mySVNProperties = mySVNProperties; copy.myDeadProperties = myDeadProperties; copy.myEntries = myEntries; copy.myActivitiesDB = myActivitiesDB; copy.myFSFS = myFSFS; copy.myTxnName = myTxnName; copy.myRoot = myRoot; copy.myTxnInfo = myTxnInfo; copy.myClientCapabilities = myClientCapabilities; copy.myLockTokens = myLockTokens; } }