/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing" */
package org.alfresco.filesys.avm;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.StringTokenizer;
import javax.transaction.UserTransaction;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.state.FileState;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.core.DeviceContext;
import org.alfresco.jlan.server.core.DeviceContextException;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.DirectoryNotEmptyException;
import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileExistsException;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.PathNotFoundException;
import org.alfresco.jlan.server.filesys.SearchContext;
import org.alfresco.jlan.server.filesys.TreeConnection;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFolderNetworkFile;
import org.alfresco.jlan.util.StringList;
import org.alfresco.jlan.util.WildCard;
import org.alfresco.model.WCMAppModel;
import org.alfresco.repo.avm.CreateStoreTxnListener;
import org.alfresco.repo.avm.CreateVersionTxnListener;
import org.alfresco.repo.avm.PurgeStoreTxnListener;
import org.alfresco.repo.avm.PurgeVersionTxnListener;
import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.sandbox.SandboxConstants;
import org.alfresco.service.cmr.avm.AVMBadArgumentException;
import org.alfresco.service.cmr.avm.AVMExistsException;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMNotFoundException;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avm.AVMStoreDescriptor;
import org.alfresco.service.cmr.avm.AVMWrongTypeException;
import org.alfresco.service.cmr.avm.VersionDescriptor;
import org.alfresco.service.cmr.avm.locking.AVMLockingException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* AVM Repository Filesystem Driver Class
*
* Provides a filesystem interface for various protocols such as SMB/CIFS and FTP.
*
* @author GKSpencer
*/
public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface
{
// Logging
private static final Log logger = LogFactory.getLog(AVMDiskDriver.class);
// Configuration key names
private static final String KEY_STORE = "storePath";
private static final String KEY_VERSION = "version";
private static final String KEY_CREATE = "createStore";
// AVM path seperator
public static final char AVM_SEPERATOR = '/';
public static final String AVM_SEPERATOR_STR = "/";
// Define client role names
public static final String RoleContentManager = "ContentManager";
public static final String RoleWebProject = "WebProject";
public static final String RoleNotWebAuthor = "NotWebAuthor";
// Content manager web project role
private static final String ROLE_CONTENT_MANAGER = "ContentManager";
// Services and helpers
private AVMService m_avmService;
private MimetypeService m_mimetypeService;
private AuthenticationComponent m_authComponent;
private AuthenticationService m_authService;
private NodeService m_nodeService;
// AVM listeners
private CreateStoreTxnListener m_createStoreListener;
private PurgeStoreTxnListener m_purgeStoreListener;
private CreateVersionTxnListener m_createVerListener;
private PurgeVersionTxnListener m_purgeVerListener;
// Web project store
private String m_webProjectStore;
/**
* Default constructor
*/
public AVMDiskDriver()
{
}
/**
* Return the AVM service
*
* @return AVMService
*/
public final AVMService getAvmService()
{
return m_avmService;
}
/**
* Return the authentication service
*
* @return AuthenticationService
*/
public final AuthenticationService getAuthenticationService()
{
return m_authService;
}
/**
* Set the AVM service
*
* @param avmService
* AVMService
*/
public void setAvmService(AVMService avmService)
{
m_avmService = avmService;
}
/**
* Set the authentication component
*
* @param authComponent
* AuthenticationComponent
*/
public void setAuthenticationComponent(AuthenticationComponent authComponent)
{
m_authComponent = authComponent;
}
/**
* Set the authentication service
*
* @param authService
* AuthenticationService
*/
public void setAuthenticationService(AuthenticationService authService)
{
m_authService = authService;
}
/**
* Set the mimetype service
*
* @param mimetypeService
* MimetypeService
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
m_mimetypeService = mimetypeService;
}
/**
* Set the node service
*
* @param nodeService NodeService
*/
public void setNodeService(NodeService nodeService)
{
m_nodeService = nodeService;
}
/**
* Set the create store listener
*
* @param createStoreListener
* CreateStoreTxnListener
*/
public void setCreateStoreListener(CreateStoreTxnListener createStoreListener)
{
m_createStoreListener = createStoreListener;
}
/**
* Set the purge store listener
*
* @param purgeStoreListener
* PurgeStoreTxnListener
*/
public void setPurgeStoreListener(PurgeStoreTxnListener purgeStoreListener)
{
m_purgeStoreListener = purgeStoreListener;
}
/**
* Set the create version listener
*
* @param createVersionListener
* CreateVersionTxnListener
*/
public void setCreateVersionListener(CreateVersionTxnListener createVersionListener)
{
m_createVerListener = createVersionListener;
}
/**
* Set the purge version listener
*
* @param purgeVersionListener
* PurgeVersionTxnListener
*/
public void setPurgeVersionListener(PurgeVersionTxnListener purgeVersionListener)
{
m_purgeVerListener = purgeVersionListener;
}
/**
* Set the web project store
*
* @param webStore String
*/
public void setWebProjectStore(String webStore)
{
m_webProjectStore = webStore;
}
/**
* Parse and validate the parameter string and create a device context object for this instance of the shared
* device.
*
* @param shareName String
* @param cfg ConfigElement
* @return DeviceContext
* @exception DeviceContextException
*/
public DeviceContext createContext(String shareName, ConfigElement cfg)
throws DeviceContextException
{
// Use the system user as the authenticated context for the filesystem initialization
String currentUser = m_authComponent.getCurrentUserName();
try
{
m_authComponent.setCurrentUser(m_authComponent.getSystemUserName());
// Wrap the initialization in a transaction
UserTransaction tx = getTransactionService().getUserTransaction(false);
AVMContext context = null;
try
{
// Start the transaction
if (tx != null)
tx.begin();
// Check if the share is a virtualization view
ConfigElement virtElem = cfg.getChild("virtualView");
if (virtElem != null)
{
// Check if virtualization view show options have been specified
int showOptions = AVMContext.ShowStagingStores + AVMContext.ShowAuthorStores;
String showAttr = virtElem.getAttribute( "stores");
if ( showAttr != null)
{
// Split the show options string
StringTokenizer tokens = new StringTokenizer( showAttr, ",");
StringList optList = new StringList();
while ( tokens.hasMoreTokens())
optList.addString( tokens.nextToken().trim().toLowerCase());
// Build the show options mask
showOptions = 0;
if ( optList.containsString("normal"))
showOptions += AVMContext.ShowNormalStores;
if ( optList.containsString("site"))
showOptions += AVMContext.ShowSiteStores;
if ( optList.containsString("author"))
showOptions += AVMContext.ShowAuthorStores;
if ( optList.containsString("preview"))
showOptions += AVMContext.ShowPreviewStores;
if ( optList.containsString("staging"))
showOptions += AVMContext.ShowStagingStores;
}
else if ( cfg.getChild("showAllSandboxes") != null)
{
// Old style show options
showOptions = AVMContext.ShowNormalStores + AVMContext.ShowSiteStores +
AVMContext.ShowAuthorStores + AVMContext.ShowPreviewStores +
AVMContext.ShowStagingStores;
}
// Create the context
context = new AVMContext(shareName, showOptions, this);
// Enable file state caching
context.enableStateTable(true, getStateReaper());
// Plug the virtualization view context into the various store/version call back listeners
// so that store/version pseudo folders can be kept in sync with AVM
m_createStoreListener.addCallback(context);
m_purgeStoreListener.addCallback(context);
m_createVerListener.addCallback(context);
m_purgeVerListener.addCallback(context);
// Create the file state for the root path, this will build the store pseudo folder list
findPseudoState( new AVMPath( ""), context);
}
else
{
// Get the store path
ConfigElement storeElement = cfg.getChild(KEY_STORE);
if (storeElement == null
|| storeElement.getValue() == null || storeElement.getValue().length() == 0)
throw new DeviceContextException("Device missing init value: " + KEY_STORE);
String storePath = storeElement.getValue();
// Get the version if specified, or default to the head version
int version = AVMContext.VERSION_HEAD;
ConfigElement versionElem = cfg.getChild(KEY_VERSION);
if (versionElem != null)
{
// Check if the version is valid
if (versionElem.getValue() == null || versionElem.getValue().length() == 0)
throw new DeviceContextException("Store version not specified");
// Validate the version id
try
{
version = Integer.parseInt(versionElem.getValue());
}
catch (NumberFormatException ex)
{
throw new DeviceContextException("Invalid store version specified, "
+ versionElem.getValue());
}
// Range check the version id
if (version < 0 && version != AVMContext.VERSION_HEAD)
throw new DeviceContextException("Invalid store version id specified, " + version);
}
// Check if the create flag is enabled
ConfigElement createStore = cfg.getChild(KEY_CREATE);
// Validate the store path
AVMNodeDescriptor rootNode = m_avmService.lookup(version, storePath);
if (rootNode == null)
{
// Check if the store should be created
if (createStore == null || version != AVMContext.VERSION_HEAD)
throw new DeviceContextException("Invalid store path/version, "
+ storePath + " (" + version + ")");
// Parse the store path
String storeName = null;
String path = null;
int pos = storePath.indexOf(":/");
if (pos != -1)
{
storeName = storePath.substring(0, pos);
if (storePath.length() > pos)
path = storePath.substring(pos + 2);
}
else
storeName = storePath;
// Check if the store exists
AVMStoreDescriptor storeDesc = null;
try
{
storeDesc = m_avmService.getStore(storeName);
}
catch (AVMNotFoundException ex)
{
}
// Create a new store if it does not exist
if (storeDesc == null)
m_avmService.createStore(storeName);
// Check if there is an optional path
if (path != null)
{
// Split the path
StringTokenizer tokens = new StringTokenizer(path, AVMPath.AVM_SEPERATOR_STR);
StringList paths = new StringList();
while (tokens.hasMoreTokens())
paths.addString(tokens.nextToken());
// Create the path, or folders that do not exist
AVMPath curPath = new AVMPath(storeName, version, FileName.DOS_SEPERATOR_STR);
AVMNodeDescriptor curDesc = m_avmService.lookup(curPath.getVersion(), curPath.getAVMPath());
// Walk the path checking creating each folder as required
for (int i = 0; i < paths.numberOfStrings(); i++)
{
AVMNodeDescriptor nextDesc = null;
try
{
// Check if the child folder exists
nextDesc = m_avmService.lookup(curDesc, paths.getStringAt(i));
}
catch (AVMNotFoundException ex)
{
}
// Check if the folder exists
if (nextDesc == null)
{
// Create the new folder
m_avmService.createDirectory(curPath.getAVMPath(), paths.getStringAt(i));
// Get the details of the new folder
nextDesc = m_avmService.lookup(curDesc, paths.getStringAt(i));
}
else if (nextDesc.isFile())
throw new DeviceContextException("Path element error, not a folder, "
+ paths.getStringAt(i));
// Step to the next level
curPath.parsePath(storeName, version, curPath.getRelativePath()
+ paths.getStringAt(i) + FileName.DOS_SEPERATOR_STR);
curDesc = nextDesc;
}
}
// Validate the store path again
rootNode = m_avmService.lookup(version, storePath);
if (rootNode == null)
throw new DeviceContextException("Failed to create new store " + storePath);
}
// Create the context
context = new AVMContext(shareName, storePath, version);
// Enable file state caching
context.enableStateTable(true, getStateReaper());
}
// Commit the transaction
tx.commit();
tx = null;
}
catch (Exception ex)
{
logger.error("Error during create context", ex);
// Rethrow the exception
throw new DeviceContextException("Driver setup error, " + ex.getMessage());
}
finally
{
// If there is an active transaction then roll it back
if (tx != null)
{
try
{
tx.rollback();
}
catch (Exception ex)
{
logger.warn("Failed to rollback transaction", ex);
}
}
}
// Return the context for this shared filesystem
return context;
}
finally
{
m_authComponent.setCurrentUser(currentUser);
}
}
/**
* Return a list of the available AVM store names
*
* @return StringList
*/
public final StringList getAVMStoreNames()
{
// Use the system user as the authenticated context to get the AVM store list
String currentUser = m_authComponent.getCurrentUserName();
try
{
m_authComponent.setCurrentUser(m_authComponent.getSystemUserName());
// Wrap the service request in a transaction
UserTransaction tx = getTransactionService().getUserTransaction(false);
StringList storeNames = new StringList();
try
{
// Start the transaction
if (tx != null)
tx.begin();
// Get the list of AVM stores
List storeList = m_avmService.getStores();
if (storeList != null)
{
for (AVMStoreDescriptor storeDesc : storeList)
storeNames.addString(storeDesc.getName());
}
// Commit the transaction
tx.commit();
tx = null;
}
catch (Exception ex)
{
logger.error("Error getting store names", ex);
}
finally
{
// If there is an active transaction then roll it back
if (tx != null)
{
try
{
tx.rollback();
}
catch (Exception ex)
{
logger.warn("Failed to rollback transaction", ex);
}
}
}
// Return the list of AVM store names
return storeNames;
}
finally
{
m_authComponent.setCurrentUser(currentUser);
}
}
/**
* Get the properties for a store
*
* @param storeName
* String
* @return Map
*/
protected final Map getAVMStoreProperties(String storeName)
{
// Use the system user as the authenticated context to get the AVM store properties
String currentUser = m_authComponent.getCurrentUserName();
try
{
m_authComponent.setCurrentUser(m_authComponent.getSystemUserName());
// Wrap the service request in a transaction
UserTransaction tx = getTransactionService().getUserTransaction(false);
Map properties = null;
try
{
// Start the transaction
if (tx != null)
tx.begin();
// Get the list of properties for AVM store
properties = m_avmService.getStoreProperties(storeName);
// Commit the transaction
tx.commit();
tx = null;
}
catch (Exception ex)
{
logger.error("Error getting store properties", ex);
}
finally
{
// If there is an active transaction then roll it back
if (tx != null)
{
try
{
tx.rollback();
}
catch (Exception ex)
{
logger.warn("Failed to rollback transaction", ex);
}
}
}
// Return the list of AVM store properties
return properties;
}
finally
{
m_authComponent.setCurrentUser(currentUser);
}
}
/**
* Build the full store path for a file/folder using the share relative path
*
* @param ctx AVMContext
* @param path String
* @param sess SrvSession
* @return AVMPath
* @exception AccessDeniedException
*/
protected final AVMPath buildStorePath(AVMContext ctx, String path, SrvSession sess)
throws AccessDeniedException
{
// Check if the AVM filesystem is a normal or virtualization view
AVMPath avmPath = null;
if (ctx.isVirtualizationView())
{
// Create a path for the virtualization view
avmPath = new AVMPath(path);
// Check that the user has access to the path
checkPathAccess( avmPath, ctx, sess);
}
else
{
// Create a path to a single store/version
avmPath = new AVMPath(ctx.getStorePath(), ctx.isVersion(), path);
}
// Return the path
return avmPath;
}
/**
* Close the file.
*
* @param sess
* Server session
* @param tree
* Tree connection.
* @param file
* Network file context.
* @exception java.io.IOException
* If an error occurs.
*/
public void closeFile(SrvSession sess, TreeConnection tree, NetworkFile file) throws java.io.IOException
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Close file " + file.getFullName());
// Start a transaction if the file has been updated
if ( file.getWriteCount() > 0)
beginWriteTransaction( sess);
// Close the file
file.closeFile();
// Check if the file/directory is marked for delete
if (file.hasDeleteOnClose())
{
// Check for a file or directory
if (file.isDirectory())
deleteDirectory(sess, tree, file.getFullName());
else
deleteFile(sess, tree, file.getFullName());
}
}
/**
* Create a new directory on this file system.
*
* @param sess
* Server session
* @param tree
* Tree connection.
* @param params
* Directory create parameters
* @exception java.io.IOException
* If an error occurs.
*/
public void createDirectory(SrvSession sess, TreeConnection tree, FileOpenParams params) throws java.io.IOException
{
// Check if the filesystem is writable
AVMContext ctx = (AVMContext) tree.getContext();
if (ctx.isVersion() != AVMContext.VERSION_HEAD)
throw new AccessDeniedException("Cannot create " + params.getPath() + ", filesys not writable");
// Split the path to get the new folder name and relative path
String[] paths = FileName.splitPath(params.getPath());
// Convert the relative path to a store path
AVMPath storePath = buildStorePath(ctx, paths[0], sess);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Create directory params=" + params + ", storePath=" + storePath + ", name=" + paths[1]);
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && storePath.isReadOnlyPseudoPath())
{
throw new AccessDeniedException("Cannot create folder in store/version layer, " + params.getPath());
}
// Create a new file
beginWriteTransaction( sess);
try
{
// Create the new file entry
m_avmService.createDirectory(storePath.getAVMPath(), paths[1]);
}
catch (AVMExistsException ex)
{
throw new FileExistsException(params.getPath());
}
catch (AVMNotFoundException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMWrongTypeException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMBadArgumentException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMLockingException ex)
{
throw new AccessDeniedException(params.getPath());
}
catch ( org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
throw new AccessDeniedException(params.getPath());
}
}
/**
* Create a new file on the file system.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param params
* File create parameters
* @return NetworkFile
* @exception java.io.IOException
* If an error occurs.
*/
public NetworkFile createFile(SrvSession sess, TreeConnection tree, FileOpenParams params)
throws java.io.IOException
{
// Check if the filesystem is writable
AVMContext ctx = (AVMContext) tree.getContext();
// Split the path to get the file name and relative path
String[] paths = FileName.splitPath(params.getPath());
// Convert the relative path to a store path
AVMPath storePath = buildStorePath(ctx, paths[0], sess);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Create file params=" + params + ", storePath=" + storePath + ", name=" + paths[1]);
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && storePath.isReadOnlyPseudoPath())
{
throw new AccessDeniedException("Cannot create file in store/version layer, " + params.getPath());
}
else if (storePath.getVersion() != AVMContext.VERSION_HEAD)
{
throw new AccessDeniedException("Cannot create " + params.getPath() + ", filesys not writable");
}
// Create a new file
beginWriteTransaction( sess);
AVMNetworkFile netFile = null;
try
{
// Create the new file entry
m_avmService.createFile(storePath.getAVMPath(), paths[1]).close();
// Get the new file details
AVMPath fileStorePath = buildStorePath(ctx, params.getPath(), sess);
AVMNodeDescriptor nodeDesc = m_avmService.lookup(fileStorePath.getVersion(), fileStorePath.getAVMPath());
if (nodeDesc != null)
{
// Create the network file object for the new file
netFile = new AVMNetworkFile(nodeDesc, fileStorePath.getAVMPath(), fileStorePath.getVersion(),
m_avmService);
netFile.setGrantedAccess(NetworkFile.READWRITE);
netFile.setFullName(params.getPath());
netFile.setFileId(fileStorePath.generateFileId());
// Set the mime-type for the new file
netFile.setMimeType(m_mimetypeService.guessMimetype(paths[1]));
}
}
catch (AVMExistsException ex)
{
throw new FileExistsException(params.getPath());
}
catch (AVMNotFoundException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMWrongTypeException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMBadArgumentException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMLockingException ex)
{
throw new AccessDeniedException(params.getPath());
}
catch ( org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
throw new AccessDeniedException(params.getPath());
}
// Return the file
return netFile;
}
/**
* Delete the directory from the filesystem.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param dir
* Directory name.
* @exception java.io.IOException
* The exception description.
*/
public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir) throws java.io.IOException
{
// Convert the relative path to a store path
AVMContext ctx = (AVMContext) tree.getContext();
AVMPath storePath = buildStorePath(ctx, dir, sess);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Delete directory, path=" + dir + ", storePath=" + storePath);
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && storePath.isPseudoPath())
{
throw new AccessDeniedException("Cannot delete pseudo folder, " + dir);
}
// Make sure the path is to a folder before deleting it
beginWriteTransaction( sess);
try
{
AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath());
if (nodeDesc != null)
{
// Check that we are deleting a folder
if (nodeDesc.isDirectory())
{
// Make sure the directory is empty
SortedMap fileList = m_avmService.getDirectoryListing(nodeDesc);
if (fileList != null && fileList.size() > 0)
throw new DirectoryNotEmptyException(dir);
// Delete the folder
m_avmService.removeNode(storePath.getAVMPath());
}
else
throw new IOException("Delete directory path is not a directory, " + dir);
}
}
catch (AVMNotFoundException ex)
{
throw new IOException("Directory not found, " + dir);
}
catch (AVMWrongTypeException ex)
{
throw new IOException("Invalid path, " + dir);
}
catch ( org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
throw new AccessDeniedException("Access denied, " + dir);
}
}
/**
* Delete the specified file.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param file
* NetworkFile
* @exception java.io.IOException
* The exception description.
*/
public void deleteFile(SrvSession sess, TreeConnection tree, String name) throws java.io.IOException
{
// Convert the relative path to a store path
AVMContext ctx = (AVMContext) tree.getContext();
AVMPath storePath = buildStorePath(ctx, name, sess);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Delete file, path=" + name + ", storePath=" + storePath);
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && storePath.isPseudoPath())
{
throw new AccessDeniedException("Cannot delete pseudo file, " + name);
}
// Make sure the path is to a file before deleting it
beginWriteTransaction( sess);
try
{
AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath());
if (nodeDesc != null)
{
// Check that we are deleting a file
if (nodeDesc.isFile())
{
// Delete the file
m_avmService.removeNode(storePath.getAVMPath());
}
else
throw new IOException("Delete file path is not a file, " + name);
}
}
catch (AVMNotFoundException ex)
{
throw new IOException("File not found, " + name);
}
catch (AVMWrongTypeException ex)
{
throw new IOException("Invalid path, " + name);
}
catch (AVMLockingException ex)
{
throw new AccessDeniedException("File locked, " + name);
}
catch ( org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
throw new AccessDeniedException("Access denied, " + name);
}
}
/**
* Check if the specified file exists, and whether it is a file or directory.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param name
* java.lang.String
* @return int
* @see FileStatus
*/
public int fileExists(SrvSession sess, TreeConnection tree, String name)
{
// Convert the relative path to a store path
AVMContext ctx = (AVMContext) tree.getContext();
AVMPath storePath = null;
try
{
storePath = buildStorePath(ctx, name, sess);
}
catch ( AccessDeniedException ex)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("File exists check, path=" + name + " Access denied");
return FileStatus.NotExist;
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("File exists check, path=" + name + ", storePath=" + storePath);
// Check if the path is valid
int status = FileStatus.NotExist;
if (storePath.isValid() == false)
return status;
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && storePath.isReadOnlyPseudoPath())
{
// Find the file state for the pseudo folder
FileState fstate = findPseudoState(storePath, ctx);
if (fstate != null)
{
// DEBUG
if (logger.isDebugEnabled())
logger.debug(" Found pseudo file " + fstate);
// Check if the pseudo file is a file or folder
if (fstate.isDirectory())
status = FileStatus.DirectoryExists;
else
status = FileStatus.FileExists;
}
else
{
// Invalid pseudo file path
status = FileStatus.NotExist;
}
// Return the file status
return status;
}
// Search for the file/folder
beginReadTransaction( sess);
AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath());
if (nodeDesc != null)
{
// Check if the path is to a file or folder
if (nodeDesc.isDirectory())
status = FileStatus.DirectoryExists;
else
status = FileStatus.FileExists;
}
// Return the file status
return status;
}
/**
* Flush any buffered output for the specified file.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param file
* Network file context.
* @exception java.io.IOException
* The exception description.
*/
public void flushFile(SrvSession sess, TreeConnection tree, NetworkFile file) throws java.io.IOException
{
// Flush the file
file.flushFile();
}
/**
* Get the file information for the specified file.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param name
* File name/path that information is required for.
* @return File information if valid, else null
* @exception java.io.IOException
* The exception description.
*/
public FileInfo getFileInformation(SrvSession sess, TreeConnection tree, String name) throws java.io.IOException
{
// Convert the relative path to a store path
AVMContext ctx = (AVMContext) tree.getContext();
AVMPath storePath = null;
try
{
storePath = buildStorePath( ctx, name, sess);
}
catch ( Exception ex)
{
throw new FileNotFoundException( name);
}
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Get file information, path=" + name + ", storePath=" + storePath);
// Check if hte path is valid
if ( storePath.isValid() == false)
throw new FileNotFoundException( name);
// Check if the filesystem is the virtualization view
if ( ctx.isVirtualizationView() && storePath.isReadOnlyPseudoPath())
{
// Check if the search path is for the root, a store or version folder
if ( storePath.isRootPath())
{
// Return dummy file informatiom for the root folder
return new FileInfo( name, 0L, FileAttribute.Directory);
}
else
{
// Find the pseudo file for the store/version folder
PseudoFile psFile = findPseudoFolder( storePath, ctx);
if ( psFile != null)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( " Found pseudo file " + psFile);
return psFile.getFileInfo();
}
else
throw new FileNotFoundException( name);
}
}
// Search for the file/folder
beginReadTransaction( sess);
FileInfo info = null;
try
{
AVMNodeDescriptor nodeDesc = m_avmService.lookup( storePath.getVersion(), storePath.getAVMPath());
if ( nodeDesc != null)
{
// Create, and fill in, the file information
info = new FileInfo();
info.setFileName( nodeDesc.getName());
if ( nodeDesc.isFile())
{
info.setFileSize( nodeDesc.getLength());
info.setAllocationSize((nodeDesc.getLength() + 512L) & 0xFFFFFFFFFFFFFE00L);
}
else
info.setFileSize( 0L);
info.setAccessDateTime( nodeDesc.getAccessDate());
info.setCreationDateTime( nodeDesc.getCreateDate());
info.setModifyDateTime( nodeDesc.getModDate());
info.setChangeDateTime( nodeDesc.getModDate());
// Build the file attributes
int attr = 0;
if ( nodeDesc.isDirectory())
attr += FileAttribute.Directory;
if ( nodeDesc.getName().startsWith( ".") ||
nodeDesc.getName().equalsIgnoreCase( "Desktop.ini") ||
nodeDesc.getName().equalsIgnoreCase( "Thumbs.db"))
attr += FileAttribute.Hidden;
// Mark the file/folder as read-only if not the head version
if ( ctx.isVersion() != AVMContext.VERSION_HEAD || storePath.isReadOnlyAccess())
attr += FileAttribute.ReadOnly;
if ( attr == 0)
attr = FileAttribute.NTNormal;
info.setFileAttributes( attr);
// Set the file id
info.setFileId( storePath.generateFileId());
// DEBUG
if ( logger.isDebugEnabled())
logger.debug(" File info=" + info);
}
}
catch ( AVMNotFoundException ex)
{
throw new FileNotFoundException( name);
}
catch ( AVMWrongTypeException ex)
{
throw new PathNotFoundException( name);
}
// Return the file information
return info;
}
/**
* Determine if the disk device is read-only.
*
* @param sess
* Server session
* @param ctx
* Device context
* @return boolean
* @exception java.io.IOException
* If an error occurs.
*/
public boolean isReadOnly(SrvSession sess, DeviceContext ctx) throws java.io.IOException
{
// Check if the version indicates the head version, only the head is writable
AVMContext avmCtx = (AVMContext) ctx;
return avmCtx.isVersion() == AVMContext.VERSION_HEAD ? true : false;
}
/**
* Open a file on the file system.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param params
* File open parameters
* @return NetworkFile
* @exception java.io.IOException
* If an error occurs.
*/
public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws java.io.IOException
{
// Convert the relative path to a store path
AVMContext ctx = (AVMContext) tree.getContext();
AVMPath storePath = buildStorePath(ctx, params.getPath(), sess);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Open file params=" + params + ", storePath=" + storePath);
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && storePath.isReadOnlyPseudoPath())
{
// Check if the path is for the root, a store or version folder
if (storePath.isRootPath())
{
// Return a dummy file for the root folder
return new PseudoFolderNetworkFile(FileName.DOS_SEPERATOR_STR);
}
else
{
// Find the pseudo file for the store/version folder
PseudoFile psFile = findPseudoFolder(storePath, ctx);
if (psFile != null)
{
// DEBUG
if (logger.isDebugEnabled())
logger.debug(" Found pseudo file " + psFile);
return psFile.getFile(params.getPath());
}
else
return null;
}
}
// Search for the file/folder
beginReadTransaction( sess);
AVMNetworkFile netFile = null;
try
{
// Get the details of the file/folder
AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath());
if (nodeDesc != null)
{
// Check if the filesystem is read-only and write access has been requested
if (storePath.getVersion() != AVMContext.VERSION_HEAD
&& (params.isReadWriteAccess() || params.isWriteOnlyAccess()))
throw new AccessDeniedException("File " + params.getPath() + " is read-only");
// Create the network file object for the opened file/folder
netFile = new AVMNetworkFile(nodeDesc, storePath.getAVMPath(), storePath.getVersion(), m_avmService);
if (params.isReadOnlyAccess() || storePath.getVersion() != AVMContext.VERSION_HEAD)
netFile.setGrantedAccess(NetworkFile.READONLY);
else
netFile.setGrantedAccess(NetworkFile.READWRITE);
netFile.setFullName(params.getPath());
netFile.setFileId(storePath.generateFileId());
// Set the mime-type for the new file
netFile.setMimeType(m_mimetypeService.guessMimetype(params.getPath()));
}
else
throw new FileNotFoundException(params.getPath());
}
catch (AVMNotFoundException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch (AVMWrongTypeException ex)
{
throw new FileNotFoundException(params.getPath());
}
catch ( org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
throw new FileNotFoundException(params.getPath());
}
// Return the file
return netFile;
}
/**
* Read a block of data from the specified file.
*
* @param sess
* Session details
* @param tree
* Tree connection
* @param file
* Network file
* @param buf
* Buffer to return data to
* @param bufPos
* Starting position in the return buffer
* @param siz
* Maximum size of data to return
* @param filePos
* File offset to read data
* @return Number of bytes read
* @exception java.io.IOException
* The exception description.
*/
public int readFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buf, int bufPos, int siz,
long filePos) throws java.io.IOException
{
// Check if the file is a directory
if (file.isDirectory())
throw new AccessDeniedException();
// If the content channel is not open for the file then start a transaction
AVMNetworkFile avmFile = (AVMNetworkFile) file;
if (avmFile.hasContentChannel() == false)
beginReadTransaction( sess);
// Read the file
int rdlen = file.readFile(buf, siz, bufPos, filePos);
// If we have reached end of file return a zero length read
if (rdlen == -1)
rdlen = 0;
// Return the actual read length
return rdlen;
}
/**
* Rename the specified file.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param oldName
* java.lang.String
* @param newName
* java.lang.String
* @exception java.io.IOException
* The exception description.
*/
public void renameFile(SrvSession sess, TreeConnection tree, String oldName, String newName)
throws java.io.IOException
{
// Split the relative paths into parent and file/folder name pairs
AVMContext ctx = (AVMContext) tree.getContext();
String[] oldPaths = FileName.splitPath(oldName);
String[] newPaths = FileName.splitPath(newName);
// Convert the parent paths to store paths
AVMPath oldAVMPath = buildStorePath(ctx, oldPaths[0], sess);
AVMPath newAVMPath = buildStorePath(ctx, newPaths[0], sess);
// DEBUG
if (logger.isDebugEnabled())
{
logger.debug("Rename from path=" + oldPaths[0] + ", name=" + oldPaths[1]);
logger.debug(" new path=" + newPaths[0] + ", name=" + newPaths[1]);
}
// Check if the filesystem is the virtualization view
if (ctx.isVirtualizationView() && oldAVMPath.isReadOnlyPseudoPath())
{
throw new AccessDeniedException("Cannot rename folder in store/version layer, " + oldName);
}
// Start a transaction for the rename
beginWriteTransaction( sess);
try
{
// Rename the file/folder
m_avmService.rename(oldAVMPath.getAVMPath(), oldPaths[1], newAVMPath.getAVMPath(), newPaths[1]);
}
catch (AVMNotFoundException ex)
{
throw new IOException("Source not found, " + oldName);
}
catch (AVMWrongTypeException ex)
{
throw new IOException("Invalid path, " + oldName);
}
catch (AVMExistsException ex)
{
throw new FileExistsException("Destination exists, " + newName);
}
catch ( org.alfresco.repo.security.permissions.AccessDeniedException ex)
{
throw new AccessDeniedException("Access denied, " + oldName);
}
}
/**
* Seek to the specified file position.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param file
* Network file.
* @param pos
* Position to seek to.
* @param typ
* Seek type.
* @return New file position, relative to the start of file.
*/
public long seekFile(SrvSession sess, TreeConnection tree, NetworkFile file, long pos, int typ)
throws java.io.IOException
{
// Check if the file is a directory
if (file.isDirectory())
throw new AccessDeniedException();
// If the content channel is not open for the file then start a transaction
AVMNetworkFile avmFile = (AVMNetworkFile) file;
if (avmFile.hasContentChannel() == false)
beginReadTransaction( sess);
// Set the file position
return file.seekFile(pos, typ);
}
/**
* Set the file information for the specified file.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param name
* java.lang.String
* @param info
* FileInfo
* @exception java.io.IOException
* The exception description.
*/
public void setFileInformation(SrvSession sess, TreeConnection tree, String name, FileInfo info)
throws java.io.IOException
{
// Check if the file is being marked for deletion, check if the file is writable
if (info.hasSetFlag(FileInfo.SetDeleteOnClose) && info.hasDeleteOnClose())
{
// If this is not the head version then it's not writable
AVMContext avmCtx = (AVMContext) tree.getContext();
if (avmCtx.isVersion() != AVMContext.VERSION_HEAD)
throw new AccessDeniedException("Store not writable, cannot set delete on close");
}
}
/**
* Start a new search on the filesystem using the specified searchPath that may contain wildcards.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param searchPath
* File(s) to search for, may include wildcards.
* @param attrib
* Attributes of the file(s) to search for, see class SMBFileAttribute.
* @return SearchContext
* @exception java.io.FileNotFoundException
* If the search could not be started.
*/
public SearchContext startSearch(SrvSession sess, TreeConnection tree, String searchPath, int attrib)
throws java.io.FileNotFoundException
{
// Access the AVM context
AVMContext avmCtx = (AVMContext) tree.getContext();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Start search path=" + searchPath);
// Split the search path into relative path and search name
String[] paths = FileName.splitPath(searchPath);
// Build the store path to the folder being searched
AVMPath storePath = null;
try
{
storePath = buildStorePath(avmCtx, paths[0], sess);
}
catch ( AccessDeniedException ex)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Start search access denied");
throw new FileNotFoundException("Access denied");
}
// Check if the filesystem is the virtualization view
if (avmCtx.isVirtualizationView())
{
// Check for a search of a pseudo folder
if (storePath.isReadOnlyPseudoPath())
{
// Get the file state for the folder being searched
FileState fstate = findPseudoState(storePath, avmCtx);
if (fstate != null)
{
// Get the pseudo file list for the parent directory
PseudoFileList searchList = null;
if ( storePath.isLevel() == AVMPath.LevelId.Root)
searchList = filterPseudoFolders(avmCtx, sess, storePath, fstate);
else
searchList = fstate.getPseudoFileList();
// Check if the pseudo file list is valid
if (searchList == null)
searchList = new PseudoFileList();
// Check if this is a single file or wildcard search
if (WildCard.containsWildcards(searchPath))
{
// Create the search context, wildcard filter will take care of secondary filtering of the
// folder listing
WildCard wildCardFilter = new WildCard(paths[1], false);
return new PseudoFileListSearchContext(searchList, attrib, wildCardFilter, storePath.isReadOnlyAccess());
}
else
{
// Search the pseudo file list for the required file
PseudoFile pseudoFile = searchList.findFile(paths[1], false);
if (pseudoFile != null)
{
// Create a search context using the single file details
PseudoFileList singleList = new PseudoFileList();
singleList.addFile(pseudoFile);
return new PseudoFileListSearchContext(singleList, attrib, null, storePath.isReadOnlyAccess());
}
}
}
// File not found
throw new FileNotFoundException(searchPath);
}
else if (storePath.isLevel() == AVMPath.LevelId.HeadMetaData
|| storePath.isLevel() == AVMPath.LevelId.VersionMetaData)
{
// Return an empty file list for now
PseudoFileList metaFiles = new PseudoFileList();
return new PseudoFileListSearchContext(metaFiles, attrib, null, storePath.isReadOnlyAccess());
}
}
// Check if the path is a wildcard search
beginReadTransaction( sess);
SearchContext context = null;
if (WildCard.containsWildcards(searchPath))
{
// Get the file listing for the folder
AVMNodeDescriptor[] fileList = m_avmService.getDirectoryListingArray(storePath.getVersion(), storePath.getAVMPath(), false);
// Create the search context
if (fileList != null)
{
// DEBUG
if (logger.isDebugEnabled())
logger.debug(" Wildcard search returned " + fileList.length + " files");
// Create the search context, wildcard filter will take care of secondary filtering of the
// folder listing
WildCard wildCardFilter = new WildCard(paths[1], false);
context = new AVMSearchContext(fileList, attrib, wildCardFilter, storePath.getRelativePath(), storePath.isReadOnlyAccess());
}
}
else
{
// Single file/folder search, convert the path to a store path
try
{
storePath = buildStorePath(avmCtx, searchPath, sess);
}
catch ( AccessDeniedException ex)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Start search access denied");
throw new FileNotFoundException("Access denied");
}
// Get the single file/folder details
AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath());
if (nodeDesc != null)
{
// Create the search context for the single file/folder
context = new AVMSingleFileSearchContext(nodeDesc, storePath.getRelativePath(), storePath.isReadOnlyAccess());
}
}
// Return the search context
return context;
}
/**
* Truncate a file to the specified size
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param file
* Network file details
* @param siz
* New file length
* @exception java.io.IOException
* The exception description.
*/
public void truncateFile(SrvSession sess, TreeConnection tree, NetworkFile file, long siz)
throws java.io.IOException
{
// Check if the file is a directory, or only has read access
if (file.getGrantedAccess() == NetworkFile.READONLY)
throw new AccessDeniedException();
// If the content channel is not open for the file then start a transaction
AVMNetworkFile avmFile = (AVMNetworkFile) file;
if (avmFile.hasContentChannel() == false || avmFile.isWritable() == false)
beginWriteTransaction( sess);
// Truncate or extend the file
file.truncateFile(siz);
file.flushFile();
}
/**
* Write a block of data to the file.
*
* @param sess
* Server session
* @param tree
* Tree connection
* @param file
* Network file details
* @param buf
* byte[] Data to be written
* @param bufoff
* Offset within the buffer that the data starts
* @param siz
* int Data length
* @param fileoff
* Position within the file that the data is to be written.
* @return Number of bytes actually written
* @exception java.io.IOException
* The exception description.
*/
public int writeFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buf, int bufoff, int siz,
long fileoff) throws java.io.IOException
{
// Check if the file is a directory, or only has read access
if (file.isDirectory() || file.getGrantedAccess() == NetworkFile.READONLY)
throw new AccessDeniedException();
// If the content channel is not open for the file, or the channel is not writable, then start a transaction
AVMNetworkFile avmFile = (AVMNetworkFile) file;
if (avmFile.hasContentChannel() == false || avmFile.isWritable() == false)
beginWriteTransaction( sess);
// Write the data to the file
file.writeFile(buf, siz, bufoff, fileoff);
// Return the actual write length
return siz;
}
/**
* Connection opened to this disk device
*
* @param sess
* Server session
* @param tree
* Tree connection
*/
public void treeClosed(SrvSession sess, TreeConnection tree)
{
// Nothing to do
}
/**
* Connection closed to this device
*
* @param sess
* Server session
* @param tree
* Tree connection
*/
public void treeOpened(SrvSession sess, TreeConnection tree)
{
// Nothing to do
}
/**
* Find the pseudo file for a virtual path
*
* @param avmPath
* AVMPath
* @param avmCtx
* AVMContext
* @return PseudoFile
*/
private final PseudoFile findPseudoFolder(AVMPath avmPath, AVMContext avmCtx)
{
return findPseudoFolder(avmPath, avmCtx, true);
}
/**
* Find the pseudo file for a virtual path
*
* @param avmPath
* AVMPath
* @param avmCtx
* AVMContext
* @param generateStates
* boolean
* @return PseudoFile
*/
private final PseudoFile findPseudoFolder(AVMPath avmPath, AVMContext avmCtx, boolean generateStates)
{
// Check if the path is to a store pseudo folder
if (avmPath.isRootPath())
return null;
// Get the file state for the parent of the required folder
FileState fstate = null;
StringBuilder str = null;
PseudoFile psFile = null;
switch (avmPath.isLevel())
{
// Store root folder
case StoreRoot:
// Get the root folder file state
fstate = avmCtx.getStateTable().findFileState(FileName.DOS_SEPERATOR_STR);
if (fstate != null && fstate.hasPseudoFiles())
psFile = fstate.getPseudoFileList().findFile(avmPath.getStoreName(), false);
break;
// Versions root or Head folder
case VersionRoot:
case Head:
// Create a path to the parent store
str = new StringBuilder();
str.append(FileName.DOS_SEPERATOR);
str.append(avmPath.getStoreName());
// Find/create the file state for the store
AVMPath storePath = new AVMPath(str.toString());
fstate = findPseudoState(storePath, avmCtx);
// Find the version root or head pseudo folder
if (fstate != null)
{
if (avmPath.isLevel() == AVMPath.LevelId.Head)
psFile = fstate.getPseudoFileList().findFile(AVMPath.VersionNameHead, true);
else
psFile = fstate.getPseudoFileList().findFile(AVMPath.VersionsFolder, true);
}
break;
// Version folder
case Version:
// Create a path to the versions folder
str = new StringBuilder();
str.append(FileName.DOS_SEPERATOR);
str.append(avmPath.getStoreName());
str.append(FileName.DOS_SEPERATOR);
str.append(AVMPath.VersionsFolder);
// Find/create the file state for the store
AVMPath verrootPath = new AVMPath(str.toString());
fstate = findPseudoState(verrootPath, avmCtx);
// Find the version pseudo file
if (fstate != null)
{
// Build the version folder name string
str.setLength(0);
str.append(AVMPath.VersionFolderPrefix);
str.append(avmPath.getVersion());
// find the version folder pseduo file
psFile = fstate.getPseudoFileList().findFile(str.toString(), true);
}
break;
// Head data or metadata folder
case HeadData:
case HeadMetaData:
// Create a path to the head folder
str = new StringBuilder();
str.append(FileName.DOS_SEPERATOR);
str.append(avmPath.getStoreName());
str.append(FileName.DOS_SEPERATOR);
str.append(AVMPath.VersionNameHead);
// Find/create the file state for the store
AVMPath headPath = new AVMPath(str.toString());
fstate = findPseudoState(headPath, avmCtx);
// Find the data or metadata pseudo folder
if (fstate != null)
{
// Find the pseudo folder
if (avmPath.isLevel() == AVMPath.LevelId.HeadData)
{
psFile = fstate.getPseudoFileList().findFile(AVMPath.DataFolder, true);
}
else
{
psFile = fstate.getPseudoFileList().findFile(AVMPath.MetaDataFolder, true);
}
}
break;
// Version data or metadata folder
case VersionData:
case VersionMetaData:
// Create a path to the version folder
str = new StringBuilder();
str.append(FileName.DOS_SEPERATOR);
str.append(avmPath.getStoreName());
str.append(FileName.DOS_SEPERATOR);
str.append(AVMPath.VersionFolderPrefix);
str.append(avmPath.getVersion());
// Find/create the file state for the store
AVMPath verPath = new AVMPath(str.toString());
fstate = findPseudoState(verPath, avmCtx);
// Find the data or metadata pseudo folder
if (fstate != null)
{
// Find the pseudo folder
if (avmPath.isLevel() == AVMPath.LevelId.VersionData)
{
psFile = fstate.getPseudoFileList().findFile(AVMPath.DataFolder, true);
}
else
{
psFile = fstate.getPseudoFileList().findFile(AVMPath.MetaDataFolder, true);
}
}
break;
}
// Check if the pseudo file was not found but file states should be generated
if (psFile == null && generateStates == true)
{
// Generate the file states for the path, this is required if a request is made to a path without
// walking the folder tree
generatePseudoFolders(avmPath, avmCtx);
// Try and find the pseudo file again
psFile = findPseudoFolder(avmPath, avmCtx, false);
}
// Return the pseudo file, or null if not found
return psFile;
}
/**
* Find the file state for a pseudo folder path
*
* @param avmPath
* AVMPath
* @param avmCtx
* AVMContext
* @return FileState
*/
protected final FileState findPseudoState(AVMPath avmPath, AVMContext avmCtx)
{
// Make sure the is to a pseudo file/folder
if ( avmPath.isPseudoPath() == false)
return null;
// Check if the path is to a store pseudo folder
FileState fstate = null;
StringBuilder str = null;
String relPath = null;
switch ( avmPath.isLevel())
{
// Root of the hieararchy
case Root:
// Get the root path file state
fstate = avmCtx.getStateTable().findFileState( FileName.DOS_SEPERATOR_STR);
// Check if the root file state is valid
if ( fstate == null)
{
// Create a file state for the root folder
fstate = avmCtx.getStateTable().findFileState( FileName.DOS_SEPERATOR_STR, true, true);
fstate.setExpiryTime( FileState.NoTimeout);
// Get a list of the available AVM stores
List storeList = m_avmService.getStores();
if ( storeList != null && storeList.size() > 0)
{
// Add pseudo files for the stores
for ( AVMStoreDescriptor storeDesc : storeList)
{
// Get the properties for the current store
String storeName = storeDesc.getName();
Map props = m_avmService.getStoreProperties( storeName);
// Check if the store is a main web project
if ( props.containsKey( SandboxConstants.PROP_SANDBOX_STAGING_MAIN))
{
// Get the noderef for the web project
PropertyValue prop = props.get( SandboxConstants.PROP_WEB_PROJECT_NODE_REF);
if ( prop != null) {
// Get the web project noderef
NodeRef webNodeRef = new NodeRef( prop.getStringValue());
// Create the web project pseudo folder
WebProjectStorePseudoFile webProjFolder = new WebProjectStorePseudoFile( storeDesc, FileName.DOS_SEPERATOR_STR + storeName, webNodeRef);
fstate.addPseudoFile( webProjFolder);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "Found web project " + webProjFolder.getFileName());
// Get the list of content managers for this web project
List mgrAssocs = m_nodeService.getChildAssocs( webNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL);
for ( ChildAssociationRef mgrRef : mgrAssocs)
{
// Get the child node and see if it is a content manager association
NodeRef childRef = mgrRef.getChildRef();
if ( m_nodeService.getProperty( childRef, WCMAppModel.PROP_WEBUSERROLE).equals(ROLE_CONTENT_MANAGER))
{
// Get the user name add it to the web project pseudo folder
String userName = (String) m_nodeService.getProperty( childRef, WCMAppModel.PROP_WEBUSERNAME);
webProjFolder.addUserRole( userName, WebProjectStorePseudoFile.RoleContentManager);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug(" Added content manager " + userName);
}
}
}
}
else
{
// Check if this store is a web project sandbox
int storeType = StoreType.Normal;
String webProjName = null;
String userName = null;
if ( props.containsKey( SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN))
{
// Sandbox store, linked to a web project
storeType = StoreType.WebAuthorMain;
// Get the associated web project name
webProjName = props.get( SandboxConstants.PROP_WEBSITE_NAME).getStringValue();
// Get the user name from teh store name
userName = storeName.substring( webProjName.length() + 2);
}
else if ( props.containsKey( SandboxConstants.PROP_SANDBOX_AUTHOR_PREVIEW))
{
// Author preview sandbox store, linked to a web project
storeType = StoreType.WebAuthorPreview;
// Get the associated web project name
String projPlusUser = storeName.substring( 0, storeName.length() - "--preview".length());
int pos = projPlusUser.lastIndexOf("--");
if ( pos != -1)
{
webProjName = projPlusUser.substring( 0, pos);
userName = projPlusUser.substring(pos + 2);
}
}
else if ( props.containsKey( SandboxConstants.PROP_SANDBOX_WORKFLOW_PREVIEW))
{
// Staging preview sandbox store, linked to a web project
storeType = StoreType.WebStagingPreview;
}
else if ( props.containsKey( SandboxConstants.PROP_SANDBOX_STAGING_PREVIEW))
{
// Staging preview sandbox store, linked to a web project
storeType = StoreType.WebStagingPreview;
// Get the associated web project name
webProjName = storeName.substring( 0, storeName.length() - "--preview".length());
}
else if ( props.containsKey(QName.createQName(null, ".sitestore")))
{
// Site data store type
storeType = StoreType.SiteStore;
}
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "Store " + storeDesc.getName() + ", type=" + StoreType.asString( storeType) + ", webproj=" + webProjName + ", username=" + userName);
// Add a pseudo file for the current store
if ( avmCtx.showStoreType( storeType))
{
// Create the pseudo folder for the store
StorePseudoFile storeFolder = new StorePseudoFile( storeDesc, FileName.DOS_SEPERATOR_STR + storeName, storeType);
if (storeType == StoreType.WebAuthorMain || storeType == StoreType.WebAuthorPreview ||
storeType == StoreType.WebStagingMain || storeType == StoreType.WebStagingPreview)
{
storeFolder.setWebProject( webProjName);
storeFolder.setUserName( userName);
}
// Add the store pseudo folder to the root folder file list
fstate.addPseudoFile( storeFolder);
}
}
}
}
// Scan the pseudo folder list and add all publisher/reviewer user names to the web project roles list
PseudoFileList folderList = fstate.getPseudoFileList();
if ( folderList != null && folderList.numberOfFiles() > 0)
{
// Scan the pseudo folder list
for ( int i = 0; i < folderList.numberOfFiles(); i++)
{
// Check if the current pseduo file is a store folder
if ( folderList.getFileAt( i) instanceof StorePseudoFile)
{
// Check if the store has an associated web project
StorePseudoFile curFile = (StorePseudoFile) folderList.getFileAt( i);
if ( curFile.hasWebProject())
{
// Find the associated web project pseudo folder
WebProjectStorePseudoFile webProj = (WebProjectStorePseudoFile) folderList.findFile( curFile.getWebProject(), true);
// Strip the web project name from the sandbox store name and extract the user name.
// Add the user as a publisher/reviewer to the web project roles list
String userName = curFile.getFileName().substring( webProj.getFileName().length() + 2);
// If the user does not have a content manager role then add as a publisher
if ( webProj.getUserRole( userName) == WebProjectStorePseudoFile.RoleNone)
{
webProj.addUserRole( userName, WebProjectStorePseudoFile.RolePublisher);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "Added publisher " + userName + " to " + webProj.getFileName());
}
}
}
}
}
}
break;
// Store folder
case StoreRoot:
// Build the path to the parent store folder
str = new StringBuilder();
str.append( FileName.DOS_SEPERATOR);
str.append( avmPath.getStoreName());
// Search for the file state for the store pseudo folder
relPath = str.toString();
fstate = avmCtx.getStateTable().findFileState( relPath);
if ( fstate == null)
{
// Create a file state for the store path
fstate = avmCtx.getStateTable().findFileState( str.toString(), true, true);
// Add a pseudo file for the head version
str.append( FileName.DOS_SEPERATOR);
str.append( AVMPath.VersionNameHead);
fstate.addPseudoFile( new VersionPseudoFile( AVMPath.VersionNameHead, str.toString()));
// Add a pseudo file for the version root folder
str.setLength( relPath.length() + 1);
str.append( AVMPath.VersionsFolder);
fstate.addPseudoFile( new DummyFolderPseudoFile( AVMPath.VersionsFolder, str.toString()));
}
break;
// Head folder
case Head:
// Build the path to the store head version folder
str = new StringBuilder();
str.append( FileName.DOS_SEPERATOR);
str.append( avmPath.getStoreName());
str.append( FileName.DOS_SEPERATOR);
str.append( AVMPath.VersionNameHead);
// Search for the file state for the store head version pseudo folder
relPath = str.toString();
fstate = avmCtx.getStateTable().findFileState( relPath);
if ( fstate == null)
{
// Create a file state for the store head folder path
fstate = avmCtx.getStateTable().findFileState( str.toString(), true, true);
// Add a pseudo file for the data pseudo folder
str.append( FileName.DOS_SEPERATOR);
str.append( AVMPath.DataFolder);
fstate.addPseudoFile( new DummyFolderPseudoFile( AVMPath.DataFolder, str.toString()));
// Add a pseudo file for the metadata pseudo folder
str.setLength( relPath.length() + 1);
str.append( AVMPath.MetaDataFolder);
fstate.addPseudoFile( new DummyFolderPseudoFile( AVMPath.MetaDataFolder, str.toString()));
}
break;
// Version root folder
case VersionRoot:
// Get the list of AVM store versions
try
{
// Build the path to the parent store folder
str = new StringBuilder();
str.append( FileName.DOS_SEPERATOR);
str.append( avmPath.getStoreName());
str.append( FileName.DOS_SEPERATOR);
str.append( AVMPath.VersionsFolder);
// Create a file state for the store path
relPath = str.toString();
fstate = avmCtx.getStateTable().findFileState( relPath, true, true);
// Add pseudo folders if the list is empty
if ( fstate.hasPseudoFiles() == false)
{
// Build the version folder name for the head version
StringBuilder verStr = new StringBuilder( AVMPath.VersionFolderPrefix);
verStr.append( "-1");
// Add a pseudo file for the head version
str.append( FileName.DOS_SEPERATOR);
str.append( verStr.toString());
fstate.addPseudoFile( new VersionPseudoFile( verStr.toString(), str.toString()));
// Get the list of versions for the store
List verList = m_avmService.getStoreVersions( avmPath.getStoreName());
// Add pseudo files for the versions to the store state
if ( verList.size() > 0)
{
for ( VersionDescriptor verDesc : verList)
{
// Generate the version string
String verName = null;
verStr.setLength( AVMPath.VersionFolderPrefix.length());
verStr.append( verDesc.getVersionID());
verName = verStr.toString();
str.setLength( relPath.length() + 1);
str.append( verName);
// Add the version pseudo folder
fstate.addPseudoFile( new VersionPseudoFile ( verName, verDesc, str.toString()));
}
}
}
}
catch ( AVMNotFoundException ex)
{
// Invalid store name
}
break;
// Version folder
case Version:
// Build the path to the store version folder
str = new StringBuilder();
str.append( FileName.DOS_SEPERATOR);
str.append( avmPath.getStoreName());
str.append( FileName.DOS_SEPERATOR);
str.append( AVMPath.VersionFolderPrefix);
str.append( avmPath.getVersion());
// Search for the file state for the version pseudo folder
relPath = str.toString();
fstate = avmCtx.getStateTable().findFileState( relPath);
if ( fstate == null)
{
// Create a file state for the version folder path
fstate = avmCtx.getStateTable().findFileState( str.toString(), true, true);
// Add a pseudo file for the data pseudo folder
str.append( FileName.DOS_SEPERATOR);
str.append( AVMPath.DataFolder);
fstate.addPseudoFile( new DummyFolderPseudoFile( AVMPath.DataFolder, str.toString()));
// Add a pseudo file for the metadata pseudo folder
str.setLength( relPath.length() + 1);
str.append( AVMPath.MetaDataFolder);
fstate.addPseudoFile( new DummyFolderPseudoFile( AVMPath.MetaDataFolder, str.toString()));
}
break;
}
// Return the file state
return fstate;
}
/**
* Generate the pseudo folders for the specified path
*
* @param avmPath
* AVMPath
* @param avmCtx
* AVMContext
*/
private final void generatePseudoFolders(AVMPath avmPath, AVMContext avmCtx)
{
// Create the root file state
AVMPath createPath = new AVMPath();
StringBuilder pathStr = new StringBuilder();
pathStr.append(FileName.DOS_SEPERATOR);
createPath.parsePath(pathStr.toString());
FileState rootState = findPseudoState(createPath, avmCtx);
// Check if the path has a store name
if (avmPath.getStoreName() != null)
{
// Check if the store name is valid
if (rootState.hasPseudoFiles()
&& rootState.getPseudoFileList().findFile(avmPath.getStoreName(), false) != null)
{
// Create the store file state
pathStr.append(avmPath.getStoreName());
pathStr.append(FileName.DOS_SEPERATOR);
createPath.parsePath(pathStr.toString());
findPseudoState(createPath, avmCtx);
// Add the head and version root pseudo folders
createPath.parsePath(pathStr.toString() + AVMPath.VersionNameHead);
findPseudoState(createPath, avmCtx);
createPath.parsePath(pathStr.toString() + AVMPath.VersionsFolder);
findPseudoState(createPath, avmCtx);
// Check if the path is to a version folder
if (avmPath.isLevel().ordinal() >= AVMPath.LevelId.Version.ordinal())
{
// Build the path
pathStr.append(AVMPath.VersionsFolder);
pathStr.append(FileName.DOS_SEPERATOR);
pathStr.append(AVMPath.VersionFolderPrefix);
pathStr.append(avmPath.getVersion());
createPath.parsePath(pathStr.toString());
// Generate the version folders
findPseudoState(createPath, avmCtx);
}
}
}
}
/**
* Check that the user has access to the path
*
* @param avmPath AVMPath
* @param avmCtx AVMContext
* @param sess SrvSession
* @exception AccessDeniedException
*/
private final void checkPathAccess( AVMPath avmPath, AVMContext avmCtx, SrvSession sess)
throws AccessDeniedException {
// Only enforce access checks on virtualization views
if ( avmCtx.isVirtualizationView() == false)
return;
// Get the client details for the session
ClientInfo cInfo = sess.getClientInformation();
if ( cInfo == null || cInfo.getUserName() == null || cInfo.getUserName().length() == 0)
throw new AccessDeniedException();
// Allow access to the root folder
if ( avmPath.isLevel() == AVMPath.LevelId.Root)
return;
// Admin user has full access
if ( cInfo.getUserName().equalsIgnoreCase( m_authComponent.getSystemUserName()))
return;
// Get root file state, get the store pseudo folder details
FileState rootState = avmCtx.getStateTable().findFileState( FileName.DOS_SEPERATOR_STR);
if ( rootState == null){
// Recreate the root file state, new stores may have been added
rootState = findPseudoState( new AVMPath( FileName.DOS_SEPERATOR_STR), avmCtx);
}
// Check if there are any store pseudo folders
if ( rootState != null && rootState.hasPseudoFiles())
{
PseudoFile pseudoFolder = rootState.getPseudoFileList().findFile( avmPath.getStoreName(), false);
if ( pseudoFolder != null)
{
// Check if the pseudo folder is a web project folder or sandbox within a web project
if ( pseudoFolder instanceof WebProjectStorePseudoFile)
{
// Check the users role within the web project
WebProjectStorePseudoFile webFolder = (WebProjectStorePseudoFile) pseudoFolder;
int role = webFolder.getUserRole( cInfo.getUserName());
if ( role == WebProjectStorePseudoFile.RoleNone)
{
// User does not have access to this web project
throw new AccessDeniedException("User " + cInfo.getUserName() + " has no access to web project, " + webFolder.getFileName());
}
else if ( role == WebProjectStorePseudoFile.RolePublisher)
{
// Only allow read-only access to the staging area
avmPath.setReadOnlyAccess( true);
}
}
else if ( pseudoFolder instanceof StorePseudoFile)
{
// Check the store type
StorePseudoFile storeFolder = (StorePseudoFile) pseudoFolder;
if ( storeFolder.isStoreType() == StoreType.Normal)
return;
else if ( storeFolder.hasWebProject())
{
// Get the web project that the sandbox is linked to
WebProjectStorePseudoFile webFolder = (WebProjectStorePseudoFile) rootState.getPseudoFileList().findFile( storeFolder.getWebProject(), false);
int role = webFolder.getUserRole( cInfo.getUserName());
if ( role == WebProjectStorePseudoFile.RoleNone)
{
// User does not have access to this web project
throw new AccessDeniedException("User " + cInfo.getUserName() + " has no access to web project, " + webFolder.getFileName() + "/" + storeFolder.getFileName());
}
else if ( role == WebProjectStorePseudoFile.RolePublisher &&
storeFolder.getUserName().equalsIgnoreCase( cInfo.getUserName()) == false)
{
// User does not have access to this web project
throw new AccessDeniedException("User " + cInfo.getUserName() + " has no access to web project, " + webFolder.getFileName() + "/" + storeFolder.getFileName());
}
}
}
}
}
else
{
// Store does not exist
throw new AccessDeniedException("Store does not exist, " + avmPath.getStoreName());
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug( "Check access " + avmPath);
}
/**
* Filter the list of pseudo folders returned in a search
*
* @param avmCtx AVMContext
* @param sess SrvSession
* @param avmPath AVMPath
* @param fstate FileState
* @return PseudoFileList
*/
private final PseudoFileList filterPseudoFolders( AVMContext avmCtx, SrvSession sess, AVMPath avmPath, FileState fstate)
{
// Check if the root folder file state has any store pseudo folders
if ( fstate.hasPseudoFiles() == false)
return null;
// Get the client details for the session
ClientInfo cInfo = sess.getClientInformation();
if ( cInfo == null || cInfo.getUserName() == null || cInfo.getUserName().length() == 0)
return null;
// Check for the admin user, no need to filter the list
PseudoFileList fullList = fstate.getPseudoFileList();
if ( cInfo.getUserName().equalsIgnoreCase( m_authComponent.getSystemUserName()))
return fullList;
// Create a filtered list of store pseudo folders that the user has access to
PseudoFileList filterList = new PseudoFileList();
for ( int i = 0; i < fullList.numberOfFiles(); i++)
{
// Get the current store pseudo folder
PseudoFile pseudoFolder = fullList.getFileAt( i);
// Check if the pseudo folder is a web project folder or sandbox within a web project
if ( pseudoFolder instanceof WebProjectStorePseudoFile)
{
// Check the users role within the web project
WebProjectStorePseudoFile webFolder = (WebProjectStorePseudoFile) pseudoFolder;
if ( avmCtx.showStagingStores() && webFolder.getUserRole( cInfo.getUserName()) != WebProjectStorePseudoFile.RoleNone)
{
// User has access to this store
filterList.addFile( pseudoFolder);
}
}
else if ( pseudoFolder instanceof StorePseudoFile)
{
// Check if the store type should be included in the visible list
StorePseudoFile storeFolder = (StorePseudoFile) pseudoFolder;
if ( avmCtx.showStoreType( storeFolder.isStoreType()))
{
// Check if the user has access to this store
if ( storeFolder.hasWebProject())
{
// Get the web project that the sandbox is linked to
WebProjectStorePseudoFile webFolder = (WebProjectStorePseudoFile) fullList.findFile( storeFolder.getWebProject(), false);
int role = webFolder.getUserRole( cInfo.getUserName());
if ( role == WebProjectStorePseudoFile.RoleContentManager && avmCtx.showStoreType( storeFolder.isStoreType()))
{
// User is a content manager, allow access to the store
filterList.addFile( storeFolder);
}
else if ( role == WebProjectStorePseudoFile.RolePublisher && avmCtx.showStoreType( storeFolder.isStoreType()))
{
// Allow access if the user owns the current folder
if ( storeFolder.getUserName().equalsIgnoreCase( cInfo.getUserName()))
filterList.addFile( storeFolder);
}
}
else if ( avmCtx.showNormalStores() || avmCtx.showSiteStores())
{
// Store is not linked to a web project, allow access to the store
filterList.addFile( storeFolder);
}
}
}
}
// Return the filtered list
return filterList;
}
}