/* * 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; } }