diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index 050bee3be8..6895b50b97 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -64,12 +64,15 @@ import org.alfresco.filesys.server.core.ShareType; import org.alfresco.filesys.server.core.SharedDevice; import org.alfresco.filesys.server.core.SharedDeviceList; import org.alfresco.filesys.server.filesys.DefaultShareMapper; -import org.alfresco.filesys.server.filesys.DiskDeviceContext; import org.alfresco.filesys.server.filesys.DiskInterface; import org.alfresco.filesys.server.filesys.DiskSharedDevice; import org.alfresco.filesys.server.filesys.HomeShareMapper; import org.alfresco.filesys.smb.ServerType; import org.alfresco.filesys.smb.TcpipSMB; +import org.alfresco.filesys.smb.server.repo.ContentContext; +import org.alfresco.filesys.smb.server.repo.DesktopAction; +import org.alfresco.filesys.smb.server.repo.DesktopActionException; +import org.alfresco.filesys.smb.server.repo.DesktopActionTable; import org.alfresco.filesys.util.IPAddress; import org.alfresco.filesys.util.X64; import org.alfresco.repo.security.authentication.AuthenticationComponent; @@ -1582,7 +1585,7 @@ public class ServerConfiguration implements ApplicationListener } } - // Get the top level filesystems confgiruation element + // Get the top level filesystems configuration element ConfigElement filesystems = config.getConfigElement("filesystems"); @@ -1633,8 +1636,9 @@ public class ServerConfiguration implements ApplicationListener { // Create a new filesystem driver instance and create a context for // the new filesystem + DiskInterface filesysDriver = this.diskInterface; - DiskDeviceContext filesysContext = (DiskDeviceContext) filesysDriver.createContext(elem); + ContentContext filesysContext = (ContentContext) filesysDriver.createContext(elem); // Check if an access control list has been specified @@ -1664,6 +1668,18 @@ public class ServerConfiguration implements ApplicationListener DiskSharedDevice filesys = new DiskSharedDevice(filesysName, filesysDriver, filesysContext); + // Attach desktop actions to the filesystem + + ConfigElement deskActionsElem = elem.getChild("desktopActions"); + if ( deskActionsElem != null) + { + // Get the desktop actions list + + DesktopActionTable desktopActions = processDesktopActions(deskActionsElem, filesys); + if ( desktopActions != null) + filesysContext.setDesktopActions( desktopActions, filesysDriver); + } + // Add any access controls to the share filesys.setAccessControlList(acls); @@ -1899,6 +1915,99 @@ public class ServerConfiguration implements ApplicationListener return acls; } + /** + * Process a desktop actions sub-section and return the desktop action table + * + * @param deskActionElem ConfigElement + * @param fileSys DiskSharedDevice + */ + private final DesktopActionTable processDesktopActions(ConfigElement deskActionElem, DiskSharedDevice fileSys) + { + // Get the desktop action configuration elements + + DesktopActionTable desktopActions = null; + List actionElems = deskActionElem.getChildren(); + + if ( actionElems != null) + { + // Check for the global configuration section + + ConfigElement globalConfig = deskActionElem.getChild("global"); + + // Allocate the actions table + + desktopActions = new DesktopActionTable(); + + // Process the desktop actions list + + for ( ConfigElement actionElem : actionElems) + { + if ( actionElem.getName().equals("action")) + { + // Get the desktop action class name or bean id + + ConfigElement className = actionElem.getChild("class"); + if ( className != null) + { + // Load the desktop action class, create a new instance + + Object actionObj = null; + + try + { + // Create a new desktop action instance + + actionObj = Class.forName(className.getValue()).newInstance(); + + // Make sure the object is a desktop action + + if ( actionObj instanceof DesktopAction) + { + // Initialize the desktop action + + DesktopAction deskAction = (DesktopAction) actionObj; + deskAction.initializeAction(globalConfig, actionElem, fileSys); + + // Add the action to the list of desktop actions + + desktopActions.addAction(deskAction); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Added desktop action " + deskAction.getName()); + } + else + throw new AlfrescoRuntimeException("Desktop action does not extend DesktopAction class, " + className.getValue()); + } + catch ( ClassNotFoundException ex) + { + throw new AlfrescoRuntimeException("Desktop action class not found, " + className.getValue()); + } + catch (IllegalAccessException ex) + { + throw new AlfrescoRuntimeException("Failed to create desktop action instance, " + className.getValue(), ex); + } + catch ( InstantiationException ex) + { + throw new AlfrescoRuntimeException("Failed to create desktop action instance, " + className.getValue(), ex); + } + catch (DesktopActionException ex) + { + throw new AlfrescoRuntimeException("Failed to initialize desktop action", ex); + } + } + } + else if ( actionElem.getName().equals("global") == false) + throw new AlfrescoRuntimeException("Invalid configuration element in desktopActions section, " + actionElem.getName()); + } + } + + // Return the desktop actions list + + return desktopActions; + } + /** * Parse the platforms attribute returning the set of platform ids * diff --git a/source/java/org/alfresco/filesys/server/smb/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/server/smb/repo/ContentIOControlHandler.java deleted file mode 100644 index 47b947331f..0000000000 --- a/source/java/org/alfresco/filesys/server/smb/repo/ContentIOControlHandler.java +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (C) 2005-2006 Alfresco, Inc. - * - * Licensed under the Mozilla Public License version 1.1 - * with a permitted attribution clause. You may obtain a - * copy of the License at - * - * http://www.alfresco.org/legal/license.txt - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the specific - * language governing permissions and limitations under the - * License. - */ -package org.alfresco.filesys.server.smb.repo; - -import java.io.FileNotFoundException; - -import org.alfresco.filesys.server.SrvSession; -import org.alfresco.filesys.server.filesys.DiskDeviceContext; -import org.alfresco.filesys.server.filesys.FileName; -import org.alfresco.filesys.server.filesys.IOControlNotImplementedException; -import org.alfresco.filesys.server.filesys.NetworkFile; -import org.alfresco.filesys.server.filesys.NotifyChange; -import org.alfresco.filesys.server.filesys.TreeConnection; -import org.alfresco.filesys.smb.NTIOCtl; -import org.alfresco.filesys.smb.SMBException; -import org.alfresco.filesys.smb.SMBStatus; -import org.alfresco.filesys.smb.server.repo.CifsHelper; -import org.alfresco.filesys.smb.server.repo.ContentDiskDriver; -import org.alfresco.filesys.smb.server.repo.IOControlHandler; -import org.alfresco.filesys.util.DataBuffer; -import org.alfresco.model.ContentModel; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.lock.LockType; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.transaction.TransactionService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Content Disk Driver I/O Control Handler Class - * - *

Provides the custom I/O control code handling used by the CIFS client interface application. - * - * @author gkspencer - */ -public class ContentIOControlHandler implements IOControlHandler -{ - // Logging - - private static final Log logger = LogFactory.getLog(ContentIOControlHandler.class); - - // Services and helpers - - private CifsHelper cifsHelper; - private TransactionService transactionService; - private NodeService nodeService; - private CheckOutCheckInService checkInOutService; - - private ContentDiskDriver contentDriver; - - /** - * Default constructor - */ - public ContentIOControlHandler() - { - } - - /** - * Initalize the I/O control handler - * - * @param contentDriver ContentDiskDriver - * @param cifsHelper CifsHelper - * @param transService TransactionService - * @param nodeService NodeService - * @param cociService CheckOutCheckInService - */ - public void initialize( ContentDiskDriver contentDriver, CifsHelper cifsHelper, - TransactionService transService, NodeService nodeService, CheckOutCheckInService cociService) - { - this.contentDriver = contentDriver; - this.cifsHelper = cifsHelper; - this.transactionService = transService; - this.nodeService = nodeService; - this.checkInOutService = cociService; - } - - /** - * Process a filesystem I/O control request - * - * @param sess Server session - * @param tree Tree connection. - * @param ctrlCode I/O control code - * @param fid File id - * @param dataBuf I/O control specific input data - * @param isFSCtrl true if this is a filesystem control, or false for a device control - * @param filter if bit0 is set indicates that the control applies to the share root handle - * @return DataBuffer - * @exception IOControlNotImplementedException - * @exception SMBException - */ - public DataBuffer processIOControl(SrvSession sess, TreeConnection tree, int ctrlCode, int fid, DataBuffer dataBuf, - boolean isFSCtrl, int filter) - throws IOControlNotImplementedException, SMBException - { - // Validate the file id - - NetworkFile netFile = tree.findFile(fid); - if ( netFile == null || netFile.isDirectory() == false) - throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter); - - // Split the control code - - int devType = NTIOCtl.getDeviceType(ctrlCode); - int ioFunc = NTIOCtl.getFunctionCode(ctrlCode); - - if ( devType != NTIOCtl.DeviceFileSystem || dataBuf == null) - throw new IOControlNotImplementedException(); - - // Check if the request has a valid signature for an Alfresco CIFS server I/O control - - if ( dataBuf.getLength() < IOControl.Signature.length()) - throw new IOControlNotImplementedException("Bad request length"); - - String sig = dataBuf.getString(IOControl.Signature.length(), false); - - if ( sig == null || sig.compareTo(IOControl.Signature) != 0) - throw new IOControlNotImplementedException("Bad request signature"); - - // Get the node for the parent folder, make sure it is a folder - - NodeRef folderNode = null; - - try - { - folderNode = contentDriver.getNodeForPath(tree, netFile.getFullName()); - - if ( cifsHelper.isDirectory( folderNode) == false) - folderNode = null; - } - catch ( FileNotFoundException ex) - { - folderNode = null; - } - - // If the folder node is not valid return an error - - if ( folderNode == null) - throw new SMBException(SMBStatus.NTErr, SMBStatus.NTAccessDenied); - - // Debug - - if ( logger.isInfoEnabled()) { - logger.info("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf); - logger.info(" Folder nodeRef=" + folderNode); - } - - // Check if the I/O control code is one of our custom codes - - DataBuffer retBuffer = null; - - switch ( ioFunc) - { - // Probe to check if this is an Alfresco CIFS server - - case IOControl.CmdProbe: - - // Return a buffer with the signature - - retBuffer = new DataBuffer(IOControl.Signature.length()); - retBuffer.putFixedString(IOControl.Signature, IOControl.Signature.length()); - retBuffer.putInt(IOControl.StsSuccess); - break; - - // Get file information for a file within the current folder - - case IOControl.CmdFileStatus: - - // Process the file status request - - retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode); - break; - - // Check-in file request - - case IOControl.CmdCheckIn: - - // Process the check-in request - - retBuffer = procIOCheckIn( sess, tree, dataBuf, folderNode, netFile); - break; - - // Check-out file request - - case IOControl.CmdCheckOut: - - // Process the check-out request - - retBuffer = procIOCheckOut( sess, tree, dataBuf, folderNode, netFile); - break; - - // Unknown I/O control code - - default: - throw new IOControlNotImplementedException(); - } - - // Return the reply buffer, may be null - - return retBuffer; - } - - /** - * Process the file status I/O request - * - * @param sess Server session - * @param tree Tree connection - * @param reqBuf Request buffer - * @param folderNode NodeRef of parent folder - * @return DataBuffer - */ - private final DataBuffer procIOFileStatus( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode) - { - // Start a transaction - - sess.beginTransaction( transactionService, true); - - // Get the file name from the request - - String fName = reqBuf.getString( true); - logger.info(" File status, fname=" + fName); - - // Create a response buffer - - DataBuffer respBuf = new DataBuffer(256); - respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length()); - - // Get the node for the file/folder - - NodeRef childNode = null; - - try - { - childNode = cifsHelper.getNodeRef( folderNode, fName); - } - catch (FileNotFoundException ex) - { - } - - // Check if the file/folder was found - - if ( childNode == null) - { - // Return an error response - - respBuf.putInt(IOControl.StsFileNotFound); - return respBuf; - } - - // Check if this is a file or folder node - - if ( cifsHelper.isDirectory( childNode)) - { - // Only return the status and node type for folders - - respBuf.putInt(IOControl.StsSuccess); - respBuf.putInt(IOControl.TypeFolder); - } - else - { - // Indicate that this is a file node - - respBuf.putInt(IOControl.StsSuccess); - respBuf.putInt(IOControl.TypeFile); - - // Check if this file is a working copy - - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY)) - { - // Indicate that this is a working copy - - respBuf.putInt(IOControl.True); - - // Get the owner username and file it was copied from - - String owner = (String) nodeService.getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER); - String copiedFrom = null; - - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM)) - { - // Get the path of the file the working copy was generated from - - NodeRef fromNode = (NodeRef) nodeService.getProperty( childNode, ContentModel.PROP_COPY_REFERENCE); - if ( fromNode != null) - copiedFrom = (String) nodeService.getProperty( fromNode, ContentModel.PROP_NAME); - } - - // Pack the owner and copied from values - - respBuf.putString(owner != null ? owner : "", true, true); - respBuf.putString(copiedFrom != null ? copiedFrom : "", true, true); - } - else - { - // Not a working copy - - respBuf.putInt(IOControl.False); - } - - // Check the lock status of the file - - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_LOCKABLE)) - { - // Get the lock type and owner - - String lockTypeStr = (String) nodeService.getProperty( childNode, ContentModel.PROP_LOCK_TYPE); - String lockOwner = null; - - if ( lockTypeStr != null) - lockOwner = (String) nodeService.getProperty( childNode, ContentModel.PROP_LOCK_OWNER); - - // Pack the lock type, and owner if there is a lock on the file - - if ( lockTypeStr == null) - respBuf.putInt(IOControl.LockNone); - else - { - LockType lockType = LockType.valueOf( lockTypeStr); - - respBuf.putInt(lockType == LockType.READ_ONLY_LOCK ? IOControl.LockRead : IOControl.LockWrite); - respBuf.putString(lockOwner != null ? lockOwner : "", true, true); - } - } - else - { - // File is not lockable - - respBuf.putInt(IOControl.LockNone); - } - - // Get the content data details for the file - - ContentData contentData = (ContentData) nodeService.getProperty( childNode, ContentModel.PROP_CONTENT); - - if ( contentData != null) - { - // Get the content mime-type - - String mimeType = contentData.getMimetype(); - - // Pack the content length and mime-type - - respBuf.putInt( IOControl.True); - respBuf.putLong( contentData.getSize()); - respBuf.putString( mimeType != null ? mimeType : "", true, true); - } - else - { - // File does not have any content - - respBuf.putInt( IOControl.False); - } - } - - // Return the response - - return respBuf; - } - - /** - * Process the check in I/O request - * - * @param sess Server session - * @param tree Tree connection - * @param reqBuf Request buffer - * @param folderNode NodeRef of parent folder - * @param netFile NetworkFile for the folder - * @return DataBuffer - */ - private final DataBuffer procIOCheckIn( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode, - NetworkFile netFile) - { - // Start a transaction - - sess.beginTransaction( transactionService, false); - - // Get the file name from the request - - String fName = reqBuf.getString( true); - boolean keepCheckedOut = reqBuf.getInt() == IOControl.True ? true : false; - - logger.info(" CheckIn, fname=" + fName + ", keepCheckedOut=" + keepCheckedOut); - - // Create a response buffer - - DataBuffer respBuf = new DataBuffer(256); - respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length()); - - // Get the node for the file/folder - - NodeRef childNode = null; - - try - { - childNode = cifsHelper.getNodeRef( folderNode, fName); - } - catch (FileNotFoundException ex) - { - } - - // Check if the file/folder was found - - if ( childNode == null) - { - // Return an error response - - respBuf.putInt(IOControl.StsFileNotFound); - return respBuf; - } - - // Check if this is a file or folder node - - if ( cifsHelper.isDirectory( childNode)) - { - // Return an error status, attempt to check in a folder - - respBuf.putInt(IOControl.StsBadParameter); - } - else - { - // Check if this file is a working copy - - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY)) - { - try - { - // Check in the file - - checkInOutService.checkin( childNode, null, null, keepCheckedOut); - - // Check in was successful - - respBuf.putInt( IOControl.StsSuccess); - - // Check if there are any file/directory change notify requests active - - DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); - if (diskCtx.hasChangeHandler()) { - - // Build the relative path to the checked in file - - String fileName = FileName.buildPath( netFile.getFullName(), null, fName, FileName.DOS_SEPERATOR); - - // Queue a file deleted change notification - - diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, fileName); - } - } - catch (Exception ex) - { - // Return an error status and message - - respBuf.setPosition( IOControl.Signature.length()); - respBuf.putInt(IOControl.StsError); - respBuf.putString( ex.getMessage(), true, true); - } - } - else - { - // Not a working copy - - respBuf.putInt(IOControl.StsNotWorkingCopy); - } - } - - // Return the response - - return respBuf; - } - - /** - * Process the check out I/O request - * - * @param sess Server session - * @param tree Tree connection - * @param reqBuf Request buffer - * @param folderNode NodeRef of parent folder - * @param netFile NetworkFile for the folder - * @return DataBuffer - */ - private final DataBuffer procIOCheckOut( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode, - NetworkFile netFile) - { - // Start a transaction - - sess.beginTransaction( transactionService, false); - - // Get the file name from the request - - String fName = reqBuf.getString( true); - - logger.info(" CheckOut, fname=" + fName); - - // Create a response buffer - - DataBuffer respBuf = new DataBuffer(256); - respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length()); - - // Get the node for the file/folder - - NodeRef childNode = null; - - try - { - childNode = cifsHelper.getNodeRef( folderNode, fName); - } - catch (FileNotFoundException ex) - { - } - - // Check if the file/folder was found - - if ( childNode == null) - { - // Return an error response - - respBuf.putInt(IOControl.StsFileNotFound); - return respBuf; - } - - // Check if this is a file or folder node - - if ( cifsHelper.isDirectory( childNode)) - { - // Return an error status, attempt to check in a folder - - respBuf.putInt(IOControl.StsBadParameter); - } - else - { - try - { - // Check out the file - - NodeRef workingCopyNode = checkInOutService.checkout( childNode); - - // Get the working copy file name - - String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME); - - // Check out was successful, pack the working copy name - - respBuf.putInt( IOControl.StsSuccess); - respBuf.putString( workingCopyName, true, true); - - // Check if there are any file/directory change notify requests active - - DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); - if (diskCtx.hasChangeHandler()) { - - // Build the relative path to the checked in file - - String fileName = FileName.buildPath( netFile.getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR); - - // Queue a file added change notification - - diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, fileName); - } - } - catch (Exception ex) - { - // Return an error status and message - - respBuf.setPosition( IOControl.Signature.length()); - respBuf.putInt(IOControl.StsError); - respBuf.putString( ex.getMessage(), true, true); - } - } - - // Return the response - - return respBuf; - } -} diff --git a/source/java/org/alfresco/filesys/server/smb/repo/IOControl.java b/source/java/org/alfresco/filesys/server/smb/repo/IOControl.java deleted file mode 100644 index 7fb9b49f52..0000000000 --- a/source/java/org/alfresco/filesys/server/smb/repo/IOControl.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2005-2006 Alfresco, Inc. - * - * Licensed under the Mozilla Public License version 1.1 - * with a permitted attribution clause. You may obtain a - * copy of the License at - * - * http://www.alfresco.org/legal/license.txt - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the specific - * language governing permissions and limitations under the - * License. - */ -package org.alfresco.filesys.server.smb.repo; - -import org.alfresco.filesys.smb.NTIOCtl; - -/** - * Content Disk Driver I/O Control Codes Class - * - *

contains I/O control codes and status codes used by the content disk driver I/O control - * implementation. - * - * @author gkspencer - */ -public class IOControl -{ - // Custom I/O control codes - - public static final int CmdProbe = NTIOCtl.FsCtlCustom; - public static final int CmdFileStatus = NTIOCtl.FsCtlCustom + 1; - public static final int CmdCheckOut = NTIOCtl.FsCtlCustom + 2; - public static final int CmdCheckIn = NTIOCtl.FsCtlCustom + 3; - - // I/O control request/response signature - - public static final String Signature = "ALFRESCO"; - - // I/O control status codes - - public static final int StsSuccess = 0; - - public static final int StsError = 1; - public static final int StsFileNotFound = 2; - public static final int StsAccessDenied = 3; - public static final int StsBadParameter = 4; - public static final int StsNotWorkingCopy = 5; - - // Boolean field values - - public static final int True = 1; - public static final int False = 0; - - // File status field values - // - // Node type - - public static final int TypeFile = 0; - public static final int TypeFolder = 1; - - // Lock status - - public static final int LockNone = 0; - public static final int LockRead = 1; - public static final int LockWrite = 2; -} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java index d774b28437..211bcd684b 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Alfresco, Inc. + * Copyright (C) 2005-2006 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a @@ -16,8 +16,11 @@ */ package org.alfresco.filesys.smb.server.repo; +import java.util.Enumeration; + import org.alfresco.filesys.server.filesys.*; -import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFile; +import org.alfresco.filesys.smb.server.repo.pseudo.ContentPseudoFileImpl; +import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFileInterface; import org.alfresco.service.cmr.repository.*; /** @@ -42,15 +45,23 @@ public class ContentContext extends DiskDeviceContext private FileStateTable m_stateTable; - // Drag and drop pseudo file - - private PseudoFile m_dragAndDropApp; - // URL pseudo file web path prefix (server/port/webapp) and link file name private String m_urlPathPrefix; private String m_urlFileName; + // Pseudo file interface + + private PseudoFileInterface m_pseudoFileInterface; + + // Desktop actions + + private DesktopActionTable m_desktopActions; + + // I/O control handler + + private IOControlHandler m_ioHandler; + /** * Class constructor * @@ -146,25 +157,108 @@ public class ContentContext extends DiskDeviceContext } /** - * Determine if the drag and drop pseudo file has been configured + * Determine if the pseudo file interface is enabled * * @return boolean */ - public final boolean hasDragAndDropApp() + public final boolean hasPseudoFileInterface() { - return m_dragAndDropApp != null ? true : false; + return m_pseudoFileInterface != null ? true : false; } /** - * Return the drag and drop pseudo file + * Return the pseudo file interface * - * @return PseudoFile + * @return PseudoFileInterface */ - public final PseudoFile getDragAndDropApp() + public final PseudoFileInterface getPseudoFileInterface() { - return m_dragAndDropApp; + return m_pseudoFileInterface; } + /** + * Enable the pseudo file interface for this filesystem + */ + public final void enabledPseudoFileInterface() + { + if ( m_pseudoFileInterface == null) + m_pseudoFileInterface = new ContentPseudoFileImpl(); + } + + /** + * Determine if there are desktop actins configured + * + * @return boolean + */ + public final boolean hasDesktopActions() + { + return m_desktopActions != null ? true : false; + } + + /** + * Return the desktop actions table + * + * @return DesktopActionTable + */ + public final DesktopActionTable getDesktopActions() + { + return m_desktopActions; + } + + /** + * Return the count of desktop actions + * + * @return int + */ + public final int numberOfDesktopActions() + { + return m_desktopActions != null ? m_desktopActions.numberOfActions() : 0; + } + + /** + * Add a desktop action + * + * @param action DesktopAction + * @return boolean + */ + public final boolean addDesktopAction(DesktopAction action) + { + // Check if the desktop actions table has been created + + if ( m_desktopActions == null) + { + m_desktopActions = new DesktopActionTable(); + + // Enable pseudo files + + enabledPseudoFileInterface(); + } + + // Add the action + + return m_desktopActions.addAction(action); + } + + /** + * Determine if custom I/O control handling is enabled for this filesystem + * + * @return boolean + */ + public final boolean hasIOHandler() + { + return m_ioHandler != null ? true : false; + } + + /** + * Return the custom I/O control handler + * + * @return IOControlHandler + */ + public final IOControlHandler getIOHandler() + { + return m_ioHandler; + } + /** * Determine if the URL pseudo file is enabled * @@ -197,16 +291,6 @@ public class ContentContext extends DiskDeviceContext return m_urlFileName; } - /** - * Set the drag and drop application details - * - * @param dragDropApp PseudoFile - */ - public final void setDragAndDropApp(PseudoFile dragDropApp) - { - m_dragAndDropApp = dragDropApp; - } - /** * Set the URL path prefix * @@ -215,6 +299,9 @@ public class ContentContext extends DiskDeviceContext public final void setURLPrefix(String urlPrefix) { m_urlPathPrefix = urlPrefix; + + if ( urlPrefix != null) + enabledPseudoFileInterface(); } /** @@ -225,8 +312,43 @@ public class ContentContext extends DiskDeviceContext public final void setURLFileName(String urlFileName) { m_urlFileName = urlFileName; + + if ( urlFileName != null) + enabledPseudoFileInterface(); } + /** + * Set the desktop actions + * + * @param desktopActions DesktopActionTable + * @param filesysDriver DiskInterface + */ + public final void setDesktopActions(DesktopActionTable desktopActions, DiskInterface filesysDriver) + { + // Enumerate the desktop actions and add to this filesystem + + Enumeration names = desktopActions.enumerateActionNames(); + + while ( names.hasMoreElements()) + { + addDesktopAction( desktopActions.getAction(names.nextElement())); + } + + // If there are desktop actions then create the custom I/O control handler + + if ( numberOfDesktopActions() > 0) + { + // Access the filesystem driver + + ContentDiskDriver contentDriver = (ContentDiskDriver) filesysDriver; + + // Create the custom I/O control handler + + m_ioHandler = new ContentIOControlHandler(); + m_ioHandler.initialize(contentDriver, this); + } + } + /** * Close the filesystem context */ diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index 45ea54225e..d01bee8699 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -16,12 +16,8 @@ */ package org.alfresco.filesys.smb.server.repo; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLDecoder; import java.util.List; import javax.transaction.UserTransaction; @@ -52,8 +48,6 @@ import org.alfresco.filesys.smb.SMBStatus; import org.alfresco.filesys.smb.SharingMode; import org.alfresco.filesys.smb.server.SMBSrvSession; import org.alfresco.filesys.smb.server.repo.FileState.FileStateStatus; -import org.alfresco.filesys.smb.server.repo.pseudo.ContentPseudoFileImpl; -import org.alfresco.filesys.smb.server.repo.pseudo.LocalPseudoFile; import org.alfresco.filesys.smb.server.repo.pseudo.MemoryNetworkFile; import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFile; import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFileInterface; @@ -105,18 +99,10 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface private SearchService searchService; private ContentService contentService; private PermissionService permissionService; - private CheckOutCheckInService checkInOutService; + private CheckOutCheckInService checkOutInService; private AuthenticationComponent authComponent; - // I/O control handler - - private IOControlHandler m_ioHandler; - - // Pseudo files interface - - private PseudoFileInterface m_pseudoFiles; - /** * Class constructor * @@ -127,6 +113,75 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface this.cifsHelper = cifsHelper; } + /** + * Return the CIFS helper + * + * @return CifsHelper + */ + public final CifsHelper getCifsHelper() + { + return this.cifsHelper; + } + + /** + * Return the transaction service + * + * @return TransactionService + */ + public final TransactionService getTransactionService() + { + return this.transactionService; + } + + /** + * Return the node service + * + * @return NodeService + */ + public final NodeService getNodeService() + { + return this.nodeService; + } + + /** + * Return the content service + * + * @return ContentService + */ + public final ContentService getContentService() + { + return this.contentService; + } + + /** + * Return the namespace service + * + * @return NamespaceService + */ + public final NamespaceService getNamespaceService() + { + return this.namespaceService; + } + + /** + * Return the search service + * + * @return SearchService + */ + public final SearchService getSearchService(){ + return this.searchService; + } + + /** + * Return the check in/out service + * + * @return CheckOutInService + */ + public final CheckOutCheckInService getCheckInOutService() + { + return this.checkOutInService; + } + /** * @param contentService the content service */ @@ -158,7 +213,6 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface { this.searchService = searchService; } - /** * @param transactionService the transaction service @@ -181,11 +235,11 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface /** * Set the check in/out service * - * @param checkInOutService CheckOutCheckInService + * @param checkInService CheckOutInService */ - public void setCheckInOutService(CheckOutCheckInService checkInOutService) + public void setCheckInOutService(CheckOutCheckInService checkInService) { - this.checkInOutService = checkInOutService; + this.checkOutInService = checkInService; } /** @@ -337,67 +391,6 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface } } - // Check if the client side drag and drop appliction has been enabled - - ConfigElement dragDropElem = cfg.getChild( "dragAndDrop"); - if ( dragDropElem != null) - { - // Get the pseudo file name and path to the actual file on the local filesystem - - ConfigElement pseudoName = dragDropElem.getChild( "filename"); - ConfigElement appPath = dragDropElem.getChild( "path"); - - if ( pseudoName != null && appPath != null) - { - // Check that the application exists on the local filesystem - - URL appURL = this.getClass().getClassLoader().getResource(appPath.getValue()); - if ( appURL == null) - throw new DeviceContextException("Failed to find drag and drop application, " + appPath.getValue()); - - // Decode the URL path, it might contain escaped characters - - String appURLPath = null; - try - { - appURLPath = URLDecoder.decode( appURL.getFile(), "UTF-8"); - } - catch ( UnsupportedEncodingException ex) - { - throw new DeviceContextException("Failed to decode drag/drop path, " + ex.getMessage()); - } - - // Check that the drag/drop file exists - - File appFile = new File(appURLPath); - if ( appFile.exists() == false) - throw new DeviceContextException("Drag and drop application not found, " + appPath.getValue()); - - // Create the pseudo file for the drag and drop application - - PseudoFile dragDropPseudo = new LocalPseudoFile( pseudoName.getValue(), appFile.getAbsolutePath()); - context.setDragAndDropApp( dragDropPseudo); - } - - // Initialize the custom I/O handler - - try - { - m_ioHandler = new ContentIOControlHandler(); - m_ioHandler.initialize( this, cifsHelper, transactionService, nodeService, checkInOutService); - } - catch (Exception ex) - { - // Log the error - - logger.error("Failed to initialize I/O control handler", ex); - - // Rethrow the exception - - throw new DeviceContextException("Failed to initialize I/O control handler"); - } - } - // Check if URL link files are enabled ConfigElement urlFileElem = cfg.getChild( "urlFile"); @@ -428,15 +421,6 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface } } - // Enable pseudo file support if the drag and drop app and/or URL link files are enabled - - if ( context.hasDragAndDropApp() || context.hasURLFile()) - { - // Create the pseudo file handler - - m_pseudoFiles = new ContentPseudoFileImpl(); - } - // Check if locked files should be marked as offline ConfigElement offlineFiles = cfg.getChild( "offlineFiles"); @@ -459,21 +443,23 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface /** * Check if pseudo file support is enabled * + * @param context ContentContext * @return boolean */ - public final boolean hasPseudoFileInterface() + public final boolean hasPseudoFileInterface(ContentContext context) { - return m_pseudoFiles != null ? true : false; + return context.hasPseudoFileInterface(); } /** * Return the pseudo file support implementation - * + * + * @param context ContentContext * @return PseudoFileInterface */ - public final PseudoFileInterface getPseudoFileInterface() + public final PseudoFileInterface getPseudoFileInterface(ContentContext context) { - return m_pseudoFiles; + return context.getPseudoFileInterface(); } /** @@ -523,11 +509,11 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface FileInfo finfo = null; - if ( hasPseudoFileInterface()) + if ( hasPseudoFileInterface(ctx)) { // Get the pseudo file - PseudoFile pfile = getPseudoFileInterface().getPseudoFile( session, tree, path); + PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( session, tree, path); if ( pfile != null) { // DEBUG @@ -707,8 +693,8 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface // Add pseudo files to the folder being searched - if ( hasPseudoFileInterface()) - getPseudoFileInterface().addPseudoFilesToFolder( sess, tree, paths[0]); + if ( hasPseudoFileInterface(ctx)) + getPseudoFileInterface(ctx).addPseudoFilesToFolder( sess, tree, paths[0]); // Set the search node and file spec @@ -908,11 +894,11 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface // Check if pseudo files are enabled - if ( hasPseudoFileInterface()) + if ( hasPseudoFileInterface(ctx)) { // Check if the path is to a pseudo file - PseudoFile pfile = getPseudoFileInterface().getPseudoFile( sess, tree, params.getPath()); + PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, params.getPath()); if ( pfile != null) { // Create a network file to access the pseudo file data @@ -1425,11 +1411,11 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface { // Delete the pseudo file - if ( hasPseudoFileInterface()) + if ( hasPseudoFileInterface(ctx)) { // Delete the pseudo file - getPseudoFileInterface().deletePseudoFile( sess, tree, file.getFullName()); + getPseudoFileInterface(ctx).deletePseudoFile( sess, tree, file.getFullName()); } } } @@ -1698,10 +1684,14 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface { try { + // Get the device context + + ContentContext ctx = (ContentContext) tree.getContext(); + // Check if pseudo files are enabled - if ( hasPseudoFileInterface() && - getPseudoFileInterface().isPseudoFile( sess, tree, name)) + if ( hasPseudoFileInterface(ctx) && + getPseudoFileInterface(ctx).isPseudoFile( sess, tree, name)) { // Allow the file information to be changed @@ -1997,8 +1987,9 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface // Check if the I/O control handler is enabled - if ( m_ioHandler != null) - return m_ioHandler.processIOControl( sess, tree, ctrlCode, fid, dataBuf, isFSCtrl, filter); + ContentContext ctx = (ContentContext) tree.getContext(); + if ( ctx.hasIOHandler()) + return ctx.getIOHandler().processIOControl( sess, tree, ctrlCode, fid, dataBuf, isFSCtrl, filter); else throw new IOControlNotImplementedException(); } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java index d8f670d132..d9cb60e028 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java @@ -19,11 +19,8 @@ package org.alfresco.filesys.smb.server.repo; import java.io.FileNotFoundException; import org.alfresco.filesys.server.SrvSession; -import org.alfresco.filesys.server.filesys.DiskDeviceContext; -import org.alfresco.filesys.server.filesys.FileName; import org.alfresco.filesys.server.filesys.IOControlNotImplementedException; import org.alfresco.filesys.server.filesys.NetworkFile; -import org.alfresco.filesys.server.filesys.NotifyChange; import org.alfresco.filesys.server.filesys.TreeConnection; import org.alfresco.filesys.smb.NTIOCtl; import org.alfresco.filesys.smb.SMBException; @@ -33,7 +30,6 @@ import org.alfresco.filesys.smb.server.repo.ContentDiskDriver; import org.alfresco.filesys.smb.server.repo.IOControlHandler; import org.alfresco.filesys.util.DataBuffer; import org.alfresco.model.ContentModel; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; @@ -55,14 +51,10 @@ public class ContentIOControlHandler implements IOControlHandler private static final Log logger = LogFactory.getLog(ContentIOControlHandler.class); - // Services and helpers - - private CifsHelper cifsHelper; - private TransactionService transactionService; - private NodeService nodeService; - private CheckOutCheckInService checkInOutService; + // Filesystem driver and context private ContentDiskDriver contentDriver; + private ContentContext contentContext; /** * Default constructor @@ -75,21 +67,64 @@ public class ContentIOControlHandler implements IOControlHandler * Initalize the I/O control handler * * @param contentDriver ContentDiskDriver - * @param cifsHelper CifsHelper - * @param transService TransactionService - * @param nodeService NodeService - * @param cociService CheckOutCheckInService + * @param contentContext ContentContext */ - public void initialize( ContentDiskDriver contentDriver, CifsHelper cifsHelper, - TransactionService transService, NodeService nodeService, CheckOutCheckInService cociService) + public void initialize( ContentDiskDriver contentDriver, ContentContext contentContext) { - this.contentDriver = contentDriver; - this.cifsHelper = cifsHelper; - this.transactionService = transService; - this.nodeService = nodeService; - this.checkInOutService = cociService; + this.contentDriver = contentDriver; + this.contentContext = contentContext; } + /** + * Return the CIFS helper + * + * @return CifsHelper + */ + public final CifsHelper getCifsHelper() + { + return contentDriver.getCifsHelper(); + } + + /** + * Return the transaction service + * + * @return TransactionService + */ + public final TransactionService getTransactionService() + { + return contentDriver.getTransactionService(); + } + + /** + * Return the node service + * + * @return NodeService + */ + public final NodeService getNodeService() + { + return contentDriver.getNodeService(); + } + + /** + * Return the filesystem driver + * + * @return ContentDiskDriver + */ + public final ContentDiskDriver getContentDriver() + { + return contentDriver; + } + + /** + * Return the filesystem context + * + * @return ContentContext + */ + public final ContentContext getContentContext() + { + return contentContext; + } + /** * Process a filesystem I/O control request * @@ -140,7 +175,7 @@ public class ContentIOControlHandler implements IOControlHandler { folderNode = contentDriver.getNodeForPath(tree, netFile.getFullName()); - if ( cifsHelper.isDirectory( folderNode) == false) + if ( getCifsHelper().isDirectory( folderNode) == false) folderNode = null; } catch ( FileNotFoundException ex) @@ -155,9 +190,9 @@ public class ContentIOControlHandler implements IOControlHandler // Debug - if ( logger.isInfoEnabled()) { - logger.info("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf); - logger.info(" Folder nodeRef=" + folderNode); + if ( logger.isDebugEnabled()) { + logger.debug("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf); + logger.debug(" Folder nodeRef=" + folderNode); } // Check if the I/O control code is one of our custom codes @@ -166,48 +201,49 @@ public class ContentIOControlHandler implements IOControlHandler switch ( ioFunc) { - // Probe to check if this is an Alfresco CIFS server - - case IOControl.CmdProbe: - - // Return a buffer with the signature - - retBuffer = new DataBuffer(IOControl.Signature.length()); - retBuffer.putFixedString(IOControl.Signature, IOControl.Signature.length()); - retBuffer.putInt(IOControl.StsSuccess); - break; - - // Get file information for a file within the current folder - - case IOControl.CmdFileStatus: - - // Process the file status request - - retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode); - break; - - // Check-in file request - - case IOControl.CmdCheckIn: - - // Process the check-in request - - retBuffer = procIOCheckIn( sess, tree, dataBuf, folderNode, netFile); - break; - - // Check-out file request - - case IOControl.CmdCheckOut: - - // Process the check-out request - - retBuffer = procIOCheckOut( sess, tree, dataBuf, folderNode, netFile); - break; - - // Unknown I/O control code - - default: - throw new IOControlNotImplementedException(); + // Probe to check if this is an Alfresco CIFS server + + case IOControl.CmdProbe: + + // Return a buffer with the signature and protocol version + + retBuffer = new DataBuffer(IOControl.Signature.length()); + retBuffer.putFixedString(IOControl.Signature, IOControl.Signature.length()); + retBuffer.putInt(DesktopAction.StsSuccess); + retBuffer.putInt(IOControl.Version); + break; + + // Get file information for a file within the current folder + + case IOControl.CmdFileStatus: + + // Process the file status request + + retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode); + break; + + // Get action information for the specified executable path + + case IOControl.CmdGetActionInfo: + + // Process the get action information request + + retBuffer = procGetActionInfo(sess, tree, dataBuf, folderNode, netFile); + break; + + // Run the named action + + case IOControl.CmdRunAction: + + // Process the run action request + + retBuffer = procRunAction(sess, tree, dataBuf, folderNode, netFile); + break; + + // Unknown I/O control code + + default: + throw new IOControlNotImplementedException(); } // Return the reply buffer, may be null @@ -228,12 +264,14 @@ public class ContentIOControlHandler implements IOControlHandler { // Start a transaction - sess.beginTransaction( transactionService, true); + sess.beginTransaction( getTransactionService(), true); // Get the file name from the request String fName = reqBuf.getString( true); - logger.info(" File status, fname=" + fName); + + if ( logger.isDebugEnabled()) + logger.debug(" File status, fname=" + fName); // Create a response buffer @@ -246,7 +284,7 @@ public class ContentIOControlHandler implements IOControlHandler try { - childNode = cifsHelper.getNodeRef( folderNode, fName); + childNode = getCifsHelper().getNodeRef( folderNode, fName); } catch (FileNotFoundException ex) { @@ -258,29 +296,29 @@ public class ContentIOControlHandler implements IOControlHandler { // Return an error response - respBuf.putInt(IOControl.StsFileNotFound); + respBuf.putInt(DesktopAction.StsFileNotFound); return respBuf; } // Check if this is a file or folder node - if ( cifsHelper.isDirectory( childNode)) + if ( getCifsHelper().isDirectory( childNode)) { // Only return the status and node type for folders - respBuf.putInt(IOControl.StsSuccess); + respBuf.putInt(DesktopAction.StsSuccess); respBuf.putInt(IOControl.TypeFolder); } else { // Indicate that this is a file node - respBuf.putInt(IOControl.StsSuccess); + respBuf.putInt(DesktopAction.StsSuccess); respBuf.putInt(IOControl.TypeFile); // Check if this file is a working copy - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY)) + if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY)) { // Indicate that this is a working copy @@ -288,16 +326,16 @@ public class ContentIOControlHandler implements IOControlHandler // Get the owner username and file it was copied from - String owner = (String) nodeService.getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER); + String owner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER); String copiedFrom = null; - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM)) + if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM)) { // Get the path of the file the working copy was generated from - NodeRef fromNode = (NodeRef) nodeService.getProperty( childNode, ContentModel.PROP_COPY_REFERENCE); + NodeRef fromNode = (NodeRef) getNodeService().getProperty( childNode, ContentModel.PROP_COPY_REFERENCE); if ( fromNode != null) - copiedFrom = (String) nodeService.getProperty( fromNode, ContentModel.PROP_NAME); + copiedFrom = (String) getNodeService().getProperty( fromNode, ContentModel.PROP_NAME); } // Pack the owner and copied from values @@ -314,15 +352,15 @@ public class ContentIOControlHandler implements IOControlHandler // Check the lock status of the file - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_LOCKABLE)) + if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_LOCKABLE)) { // Get the lock type and owner - String lockTypeStr = (String) nodeService.getProperty( childNode, ContentModel.PROP_LOCK_TYPE); + String lockTypeStr = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_TYPE); String lockOwner = null; if ( lockTypeStr != null) - lockOwner = (String) nodeService.getProperty( childNode, ContentModel.PROP_LOCK_OWNER); + lockOwner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_OWNER); // Pack the lock type, and owner if there is a lock on the file @@ -345,7 +383,7 @@ public class ContentIOControlHandler implements IOControlHandler // Get the content data details for the file - ContentData contentData = (ContentData) nodeService.getProperty( childNode, ContentModel.PROP_CONTENT); + ContentData contentData = (ContentData) getNodeService().getProperty( childNode, ContentModel.PROP_CONTENT); if ( contentData != null) { @@ -371,9 +409,9 @@ public class ContentIOControlHandler implements IOControlHandler return respBuf; } - + /** - * Process the check in I/O request + * Process the get action information request * * @param sess Server session * @param tree Tree connection @@ -382,208 +420,208 @@ public class ContentIOControlHandler implements IOControlHandler * @param netFile NetworkFile for the folder * @return DataBuffer */ - private final DataBuffer procIOCheckIn( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode, + private final DataBuffer procGetActionInfo( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode, NetworkFile netFile) { - // Start a transaction + // Get the executable file name from the request - sess.beginTransaction( transactionService, false); - - // Get the file name from the request - - String fName = reqBuf.getString( true); - boolean keepCheckedOut = reqBuf.getInt() == IOControl.True ? true : false; - - logger.info(" CheckIn, fname=" + fName + ", keepCheckedOut=" + keepCheckedOut); + String exeName = reqBuf.getString( true); + + if ( logger.isDebugEnabled()) + logger.debug(" Get action info, exe=" + exeName); // Create a response buffer DataBuffer respBuf = new DataBuffer(256); respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length()); - // Get the node for the file/folder + // Get the desktop actions list - NodeRef childNode = null; - - try + DesktopActionTable deskActions = contentContext.getDesktopActions(); + if ( deskActions == null) { - childNode = cifsHelper.getNodeRef( folderNode, fName); + respBuf.putInt(DesktopAction.StsNoSuchAction); + return respBuf; } - catch (FileNotFoundException ex) + + // Convert the executable name to an action name + + DesktopAction deskAction = deskActions.getActionViaPseudoName(exeName); + if ( deskAction == null) { + respBuf.putInt(DesktopAction.StsNoSuchAction); + return respBuf; + } + + // Return the desktop action details + + respBuf.putInt(DesktopAction.StsSuccess); + respBuf.putString(deskAction.getName(), true); + respBuf.putInt(deskAction.getAttributes()); + respBuf.putInt(deskAction.getPreProcessActions()); + + String confirmStr = deskAction.getConfirmationString(); + respBuf.putString(confirmStr != null ? confirmStr : "", true); + + // Return the response + + return respBuf; + } + + /** + * Process the run action request + * + * @param sess Server session + * @param tree Tree connection + * @param reqBuf Request buffer + * @param folderNode NodeRef of parent folder + * @param netFile NetworkFile for the folder + * @return DataBuffer + */ + private final DataBuffer procRunAction( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode, + NetworkFile netFile) + { + // Get the name of the action to run + + String actionName = reqBuf.getString(true); + + if ( logger.isDebugEnabled()) + logger.debug(" Run action, name=" + actionName); + + // Create a response buffer + + DataBuffer respBuf = new DataBuffer(256); + respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length()); + + // Find the action handler + + DesktopActionTable deskActions = contentContext.getDesktopActions(); + DesktopAction action = null; + + if ( deskActions != null) + action = deskActions.getAction(actionName); + + if ( action == null) + { + respBuf.putInt(DesktopAction.StsNoSuchAction); + respBuf.putString("", true); + return respBuf; } - // Check if the file/folder was found + // Start a transaction - if ( childNode == null) - { - // Return an error response - - respBuf.putInt(IOControl.StsFileNotFound); - return respBuf; - } - - // Check if this is a file or folder node + sess.beginTransaction( getTransactionService(), true); - if ( cifsHelper.isDirectory( childNode)) + // Get the list of targets for the action + + int targetCnt = reqBuf.getInt(); + DesktopParams deskParams = new DesktopParams(sess, folderNode, netFile); + + while ( reqBuf.getAvailableLength() > 4 && targetCnt > 0) { - // Return an error status, attempt to check in a folder + // Get the desktop target details + + int typ = reqBuf.getInt(); + String path = reqBuf.getString(true); + + DesktopTarget target = new DesktopTarget(typ, path); + deskParams.addTarget(target); + + // Find the node for the target path + + NodeRef childNode = null; - respBuf.putInt(IOControl.StsBadParameter); - } - else - { - // Check if this file is a working copy - - if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY)) + try { - try - { - // Check in the file - - checkInOutService.checkin( childNode, null, null, keepCheckedOut); + // Check if the target path is relative to the folder we are working in or the root of the filesystem + + if ( path.startsWith("\\")) + { + // Path is relative to the root of the filesystem + + childNode = getCifsHelper().getNodeRef(contentContext.getRootNode(), path); + } + else + { + // Path is relative to the folder we are working in + + childNode = getCifsHelper().getNodeRef( folderNode, path); + } + } + catch (FileNotFoundException ex) + { + } - // Check in was successful - - respBuf.putInt( IOControl.StsSuccess); - - // Check if there are any file/directory change notify requests active - - DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); - if (diskCtx.hasChangeHandler()) { - - // Build the relative path to the checked in file - - String fileName = FileName.buildPath( netFile.getFullName(), null, fName, FileName.DOS_SEPERATOR); - - // Queue a file deleted change notification - - diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, fileName); - } - } - catch (Exception ex) - { - // Return an error status and message - - respBuf.setPosition( IOControl.Signature.length()); - respBuf.putInt(IOControl.StsError); - respBuf.putString( ex.getMessage(), true, true); - } + // If the node is not valid then return an error status + + if (childNode != null) + { + // Set the node ref for the target + + target.setNode(childNode); } else { - // Not a working copy - - respBuf.putInt(IOControl.StsNotWorkingCopy); + // Build an error response + + respBuf.putInt(DesktopAction.StsFileNotFound); + respBuf.putString("Cannot find noderef for path " + path, true); + + return respBuf; } + + // Update the target count + + targetCnt--; } - // Return the response + // DEBUG - return respBuf; - } - - /** - * Process the check out I/O request - * - * @param sess Server session - * @param tree Tree connection - * @param reqBuf Request buffer - * @param folderNode NodeRef of parent folder - * @param netFile NetworkFile for the folder - * @return DataBuffer - */ - private final DataBuffer procIOCheckOut( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode, - NetworkFile netFile) - { - // Start a transaction + if (logger.isDebugEnabled()) + { + logger.debug(" Desktop params: " + deskParams.numberOfTargetNodes()); + for ( int i = 0; i < deskParams.numberOfTargetNodes(); i++) { + DesktopTarget target = deskParams.getTarget(i); + logger.debug(" " + target); + } + } - sess.beginTransaction( transactionService, false); + // Run the desktop action - // Get the file name from the request - - String fName = reqBuf.getString( true); - - logger.info(" CheckOut, fname=" + fName); - - // Create a response buffer - - DataBuffer respBuf = new DataBuffer(256); - respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length()); - - // Get the node for the file/folder - - NodeRef childNode = null; + DesktopResponse deskResponse = null; try { - childNode = cifsHelper.getNodeRef( folderNode, fName); + // Run the desktop action + + deskResponse = action.runAction(deskParams); } - catch (FileNotFoundException ex) + catch (Exception ex) { + // Create an error response + + deskResponse = new DesktopResponse(DesktopAction.StsError, ex.getMessage()); } - - // Check if the file/folder was found - if ( childNode == null) - { - // Return an error response - - respBuf.putInt(IOControl.StsFileNotFound); - return respBuf; - } - - // Check if this is a file or folder node + // Pack the action response - if ( cifsHelper.isDirectory( childNode)) + if ( deskResponse != null) { - // Return an error status, attempt to check in a folder - - respBuf.putInt(IOControl.StsBadParameter); + // Pack the status + + respBuf.putInt(deskResponse.getStatus()); + respBuf.putString(deskResponse.hasStatusMessage() ? deskResponse.getStatusMessage() : "", true); } else { - try - { - // Check out the file - - NodeRef workingCopyNode = checkInOutService.checkout( childNode); - - // Get the working copy file name - - String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME); - - // Check out was successful, pack the working copy name - - respBuf.putInt( IOControl.StsSuccess); - respBuf.putString( workingCopyName, true, true); - - // Check if there are any file/directory change notify requests active - - DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); - if (diskCtx.hasChangeHandler()) { - - // Build the relative path to the checked in file - - String fileName = FileName.buildPath( netFile.getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR); - - // Queue a file added change notification - - diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, fileName); - } - } - catch (Exception ex) - { - // Return an error status and message - - respBuf.setPosition( IOControl.Signature.length()); - respBuf.putInt(IOControl.StsError); - respBuf.putString( ex.getMessage(), true, true); - } + // Pack an error response + + respBuf.putInt(DesktopAction.StsError); + respBuf.putString("Action did not return response", true); } // Return the response - return respBuf; - } + return respBuf; + } } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopActionException.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopActionException.java new file mode 100644 index 0000000000..1bcda21484 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopActionException.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ + +package org.alfresco.filesys.smb.server.repo; + +/** + * Desktop Action Exception Class + * + * @author gkspencer + */ +public class DesktopActionException extends Exception { + + private static final long serialVersionUID = 1006648817889605047L; + + // Status code + + private int m_stsCode; + + /** + * Class constructor + * + * @param sts int + * @param msg String + */ + public DesktopActionException(int sts, String msg) + { + super(msg); + m_stsCode = sts; + } + + /** + * Class constructor + * + * @param s String + */ + public DesktopActionException(String s) + { + super(s); + } + + /** + * Class constructor + * + * @param s String + * @param ex Exception + */ + public DesktopActionException(String s, Throwable ex) + { + super(s, ex); + } + + /** + * Return the status code + * + * @return int + */ + public final int getStatusCode() + { + return m_stsCode; + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopActionTable.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopActionTable.java new file mode 100644 index 0000000000..2bbf452fc4 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopActionTable.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ + +package org.alfresco.filesys.smb.server.repo; + +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * Desktop Action Table Class + * + *

Contains a list of desktop actions indexed by action name. + * + * @author gkspencer +*/ +public class DesktopActionTable { + + // Table of actions, indexed by action name and pseudo file name + + private Hashtable m_actions; + private Hashtable m_actionsPseudo; + + /** + * Default constructor + */ + public DesktopActionTable() + { + m_actions = new Hashtable(); + m_actionsPseudo = new Hashtable(); + } + + /** + * Find a named action + * + * @param name String + * @return DesktopAction + */ + public final DesktopAction getAction(String name) + { + return m_actions.get(name); + } + + /** + * Find an action via the pseudo file name + * + * @param pseudoName String + * @return DesktopAction + */ + public final DesktopAction getActionViaPseudoName(String pseudoName) + { + return m_actionsPseudo.get(pseudoName.toUpperCase()); + } + + /** + * Return the count of actions + * + * @return int + */ + public final int numberOfActions() + { + return m_actions.size(); + } + + /** + * Add an action + * + * @param action DesktopAction + * @return boolean + */ + public final boolean addAction(DesktopAction action) + { + if ( m_actions.get( action.getName()) == null) + { + m_actions.put(action.getName(), action); + m_actionsPseudo.put(action.getPseudoFile().getFileName().toUpperCase(), action); + return true; + } + return false; + } + + /** + * Enumerate the action names + * + * @return Enumeration + */ + public final Enumeration enumerateActionNames() + { + return m_actions.keys(); + } + + /** + * Remove an action + * + * @param name String + * @return DesktopAction + */ + public final DesktopAction removeAction(String name) + { + DesktopAction action = m_actions.remove(name); + if ( action != null) + m_actionsPseudo.remove(action.getPseudoFile().getFileName().toUpperCase()); + return action; + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java new file mode 100644 index 0000000000..1a2e294135 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.filesys.NetworkFile; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Desktop Parameters Class + * + *

Contains the parameters for a desktop action request from the client side application. + * + * @author gkspencer + */ +public class DesktopParams { + + // File server session + + private SrvSession m_session; + + // Folder node that the actions are working in + + private NodeRef m_folderNode; + + // Network file for the folder node + + private NetworkFile m_folderFile; + + // List of file/folder/node targets for the action + + private List m_targets; + + /** + * Default constructor + */ + public DesktopParams() + { + } + + /** + * Class constructor + * + * @param sess SrvSession + * @param folderNode NodeRef + * @param folderFile NetworkFile + */ + public DesktopParams(SrvSession sess, NodeRef folderNode, NetworkFile folderFile) + { + m_session = sess; + m_folderNode = folderNode; + m_folderFile = folderFile; + } + + /** + * Return the count of target nodes for the action + * + * @return int + */ + public final int numberOfTargetNodes() + { + return m_targets != null ? m_targets.size() : 0; + } + + /** + * Return the file server session + * + * @return SrvSession + */ + public final SrvSession getSession() + { + return m_session; + } + + /** + * Return the working directory node + * + * @return NodeRef + */ + public final NodeRef getFolderNode() + { + return m_folderNode; + } + + /** + * Return the folder network file + * + * @return NetworkFile + */ + public final NetworkFile getFolder() + { + return m_folderFile; + } + + /** + * Set the folder network file + * + * @param netFile NetworkFile + */ + public final void setFolder(NetworkFile netFile) + { + m_folderFile = netFile; + } + + /** + * Return the required target + * + * @param idx int + * @return DesktopTarget + */ + public final DesktopTarget getTarget(int idx) + { + DesktopTarget deskTarget = null; + + if ( m_targets != null && idx >= 0 && idx < m_targets.size()) + deskTarget = m_targets.get(idx); + + return deskTarget; + } + + /** + * Add a target node for the action + * + * @param target DesktopTarget + */ + public final void addTarget(DesktopTarget target) + { + if ( m_targets == null) + m_targets = new ArrayList(); + m_targets.add(target); + } + + /** + * Return the desktop parameters as a string + * + * @return String + */ + public String toString() + { + StringBuilder str = new StringBuilder(); + + str.append("["); + str.append("Targets="); + str.append(numberOfTargetNodes()); + str.append("]"); + + return str.toString(); + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopResponse.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopResponse.java new file mode 100644 index 0000000000..cf69283a7c --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopResponse.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Desktop Response Class + * + *

Holds the status code, optional status message and optional values returned by a desktop action. + * + * @author gkspencer + */ +public class DesktopResponse { + + // Desktop action status and optional status message + + private int m_status; + private String m_statusMsg; + + // Optional return values + + private List m_responseValues; + + /** + * Class constructor + * + * @param sts int + */ + public DesktopResponse(int sts) + { + m_status = sts; + } + + /** + * Class constructor + * + * @param sts int + * @param msg String + */ + public DesktopResponse(int sts, String msg) + { + m_status = sts; + m_statusMsg = msg; + } + + /** + * Return the status code + * + * @return int + */ + public final int getStatus() + { + return m_status; + } + + /** + * Determine if there is an optional status message + * + * @return boolean + */ + public final boolean hasStatusMessage() + { + return m_statusMsg != null ? true : false; + } + + /** + * Return the status message + * + * @return String + */ + public final String getStatusMessage() + { + return m_statusMsg; + } + + /** + * Determine if there are optional response values + * + * @return boolean + */ + public final boolean hasResponseValues() + { + return m_responseValues != null ? true : false; + } + + /** + * Return the count of response values + * + * @return int + */ + public final int numberOfResponseValues() + { + return m_responseValues != null ? m_responseValues.size() : 0; + } + + /** + * Get the response value list + * + * @return List + */ + public final List getResponseValues() + { + return m_responseValues; + } + + /** + * Add a response value + * + * @param respObj Object + */ + public final void addResponseValue(Object respObj) + { + if ( m_responseValues == null) + m_responseValues = new ArrayList(); + + m_responseValues.add(respObj); + } + + /** + * Set the status code and message + * + * @param sts int + * @param msg String + */ + public final void setStatus(int sts, String msg) + { + m_status = sts; + m_statusMsg = msg; + } + + /** + * Return the desktop response as a string + * + * @return String + */ + public String toString() + { + StringBuilder str = new StringBuilder(); + + str.append("["); + str.append(getStatus()); + str.append(":"); + if ( hasStatusMessage()) + str.append(getStatusMessage()); + else + str.append(""); + str.append(":Values="); + str.append(numberOfResponseValues()); + str.append("]"); + + return str.toString(); + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopTarget.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopTarget.java new file mode 100644 index 0000000000..877999c556 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopTarget.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Desktop Target Class + * + *

Contains the details of a target file/folder/node for a desktop action. + * + * @author gkspencer + */ +public class DesktopTarget { + + // Desktop target types + + public static final int TargetFile = 0; + public static final int TargetFolder = 1; + public static final int TargetCopiedFile = 2; + public static final int TargetCopiedFolder = 3; + public static final int TargetNodeRef = 4; + + // Target type + + private int m_type; + + // Target path/id + + private String m_target; + + // Associated noderef + + private NodeRef m_noderef; + + /** + * class constructor + * + * @param typ int + * @param path String + */ + public DesktopTarget(int typ, String path) + { + m_type = typ; + m_target = path; + } + + /** + * Return the target type + * + * @return int + */ + public final int isType() + { + return m_type; + } + + /** + * Return the target path/id + * + * @return String + */ + public final String getTarget() + { + return m_target; + } + + /** + * Check if the associated node is valid + * + * @return boolean + */ + public final boolean hasNodeRef() + { + return m_noderef != null ? true : false; + } + + /** + * Return the associated node + * + * @return NodeRef + */ + public final NodeRef getNode() + { + return m_noderef; + } + + /** + * Return the target type as a string + * + * @return String + */ + public final String getTypeAsString() + { + String str = null; + + switch( isType()) + { + case TargetFile: + str = "File"; + break; + case TargetFolder: + str = "Folder"; + break; + case TargetCopiedFile: + str = "File Copy"; + break; + case TargetCopiedFolder: + str = "Folder Copy"; + break; + case TargetNodeRef: + str = "NodeRef"; + break; + } + + return str; + } + + /** + * Set the associated node + * + * @param node NodeRef + */ + public final void setNode(NodeRef node) + { + m_noderef = node; + } + + /** + * Return the desktop target as a string + * + * @return String + */ + public String toString() + { + StringBuilder str = new StringBuilder(); + + str.append("["); + str.append(getTypeAsString()); + str.append(":"); + str.append(getTarget()); + + if ( hasNodeRef()) + { + str.append(":"); + str.append(getNode()); + } + str.append("]"); + + return str.toString(); + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/IOControl.java b/source/java/org/alfresco/filesys/smb/server/repo/IOControl.java index 1eac93cd41..75d7009328 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/IOControl.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/IOControl.java @@ -21,7 +21,7 @@ import org.alfresco.filesys.smb.NTIOCtl; /** * Content Disk Driver I/O Control Codes Class * - *

contains I/O control codes and status codes used by the content disk driver I/O control + *

Contains I/O control codes and status codes used by the content disk driver I/O control * implementation. * * @author gkspencer @@ -30,24 +30,20 @@ public class IOControl { // Custom I/O control codes - public static final int CmdProbe = NTIOCtl.FsCtlCustom; - public static final int CmdFileStatus = NTIOCtl.FsCtlCustom + 1; - public static final int CmdCheckOut = NTIOCtl.FsCtlCustom + 2; - public static final int CmdCheckIn = NTIOCtl.FsCtlCustom + 3; + public static final int CmdProbe = NTIOCtl.FsCtlCustom; + public static final int CmdFileStatus = NTIOCtl.FsCtlCustom + 1; + // Version 1 CmdCheckOut = NTIOCtl.FsCtlCustom + 2 + // Version 1 CmdCheckIn = NTIOCtl.FsCtlCustom + 3 + public static final int CmdGetActionInfo = NTIOCtl.FsCtlCustom + 4; + public static final int CmdRunAction = NTIOCtl.FsCtlCustom + 5; // I/O control request/response signature public static final String Signature = "ALFRESCO"; - // I/O control status codes + // I/O control interface version id - public static final int StsSuccess = 0; - - public static final int StsError = 1; - public static final int StsFileNotFound = 2; - public static final int StsAccessDenied = 3; - public static final int StsBadParameter = 4; - public static final int StsNotWorkingCopy = 5; + public static final int Version = 2; // Boolean field values diff --git a/source/java/org/alfresco/filesys/smb/server/repo/IOControlHandler.java b/source/java/org/alfresco/filesys/smb/server/repo/IOControlHandler.java index 39eafacc9f..255f0c5909 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/IOControlHandler.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/IOControlHandler.java @@ -18,7 +18,6 @@ package org.alfresco.filesys.smb.server.repo; import org.alfresco.filesys.server.filesys.IOCtlInterface; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.transaction.TransactionService; @@ -34,11 +33,7 @@ public interface IOControlHandler extends IOCtlInterface * * * @param contentDriver ContentDiskDriver - * @param cifsHelper CifsHelper - * @param transService TransactionService - * @param nodeService NodeService - * @param cociService CheckOutCheckInService + * @param contentContext ContentContext */ - public void initialize( ContentDiskDriver contentDriver, CifsHelper cifsHelper, - TransactionService transService, NodeService nodeService, CheckOutCheckInService cociService); + public void initialize( ContentDiskDriver contentDriver, ContentContext contentContext); } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java new file mode 100644 index 0000000000..0f98e425e3 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo.desk; + +import org.alfresco.filesys.server.filesys.FileName; +import org.alfresco.filesys.server.filesys.NotifyChange; +import org.alfresco.filesys.smb.server.repo.DesktopAction; +import org.alfresco.filesys.smb.server.repo.DesktopParams; +import org.alfresco.filesys.smb.server.repo.DesktopResponse; +import org.alfresco.filesys.smb.server.repo.DesktopTarget; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Check In/Out Desktop Action Class + * + *

Provides check in/out functionality via CIFS using the desktop action interface. + * + * @author gkspencer + */ +public class CheckInOutDesktopAction extends DesktopAction { + + /** + * Class constructor + */ + public CheckInOutDesktopAction() + { + super( DesktopAction.AttrAnyFiles, DesktopAction.PreConfirmAction + DesktopAction.PreCopyToTarget + DesktopAction.PreLocalToWorkingCopy); + } + + @Override + public String getConfirmationString() { + return "Run check in/out action"; + } + + /** + * Run the desktop action + * + * @param params DesktopParams + * @return DesktopResponse + */ + @Override + public DesktopResponse runAction(DesktopParams params) { + + // check if there are any files/folders to process + + if ( params.numberOfTargetNodes() == 0) + return new DesktopResponse(StsSuccess); + + // Start a transaction + + params.getSession().beginTransaction(getTransactionService(), false); + + // Process the list of target nodes + + DesktopResponse response = new DesktopResponse(StsSuccess); + + for ( int idx = 0; idx < params.numberOfTargetNodes(); idx++) + { + // Get the current target node + + DesktopTarget target = params.getTarget(idx); + + // Check if the node is a working copy + + if ( getNodeService().hasAspect( target.getNode(), ContentModel.ASPECT_WORKING_COPY)) + { + try + { + // Check in the file + + getCheckInOutService().checkin( target.getNode(), null, null, false); + + // Check if there are any file/directory change notify requests active + + if ( getContext().hasChangeHandler()) { + + // Build the relative path to the checked in file + + String fileName = null; + + if ( target.getTarget().startsWith(FileName.DOS_SEPERATOR_STR)) + { + // Path is already relative to filesystem root + + fileName = target.getTarget(); + } + else + { + // Build a root relative path for the file + + fileName = FileName.buildPath( params.getFolder().getFullName(), null, target.getTarget(), FileName.DOS_SEPERATOR); + } + + // Queue a file deleted change notification + + getContext().getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, fileName); + } + } + catch (Exception ex) + { + // Return an error status and message + + response.setStatus(StsError, "Checkin failed for " + target.getTarget() + ", " + ex.getMessage()); + } + } + else + { + try + { + // Check out the file + + NodeRef workingCopyNode = getCheckInOutService().checkout( target.getNode()); + + // Get the working copy file name + + String workingCopyName = (String) getNodeService().getProperty( workingCopyNode, ContentModel.PROP_NAME); + + // Check out was successful, pack the working copy name + + response.setStatus(StsSuccess, "Checked out working copy " + workingCopyName); + + // Check if there are any file/directory change notify requests active + + if ( getContext().hasChangeHandler()) { + + // Build the relative path to the checked in file + + String fileName = FileName.buildPath( params.getFolder().getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR); + + // Queue a file added change notification + + getContext().getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, fileName); + } + } + catch (Exception ex) + { + // Return an error status and message + + response.setStatus(StsError, "Failed to checkout " + target.getTarget() + ", " + ex.getMessage()); + } + } + } + + // Return a success status for now + + return response; + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java new file mode 100644 index 0000000000..a67b4768cf --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo.desk; + +import org.alfresco.filesys.smb.server.repo.DesktopAction; +import org.alfresco.filesys.smb.server.repo.DesktopParams; +import org.alfresco.filesys.smb.server.repo.DesktopResponse; + +/** + * Command Line Desktop Action Class + * + *

Simple desktop action that returns a test command line. + * + * @author gkspencer + */ +public class CmdLineDesktopAction extends DesktopAction { + + /** + * Class constructor + */ + public CmdLineDesktopAction() + { + super( 0, DesktopAction.PreConfirmAction); + } + + @Override + public String getConfirmationString() { + return "Run commandline action"; + } + + @Override + public DesktopResponse runAction(DesktopParams params) { + + // Return a URL in the status message + + return new DesktopResponse(StsCommandLine, "C:\\Windows\\notepad.exe"); + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/EchoDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/EchoDesktopAction.java new file mode 100644 index 0000000000..78a8bb9783 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/EchoDesktopAction.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo.desk; + +import java.util.Date; + +import org.alfresco.filesys.smb.server.repo.DesktopAction; +import org.alfresco.filesys.smb.server.repo.DesktopParams; +import org.alfresco.filesys.smb.server.repo.DesktopResponse; + +/** + * Echo Desktop Action Class + * + *

Simple desktop action that echoes back the received string. + * + * @author gkspencer + */ +public class EchoDesktopAction extends DesktopAction { + + /** + * Class constructor + */ + public EchoDesktopAction() + { + super( 0, DesktopAction.PreConfirmAction); + } + + @Override + public String getConfirmationString() { + return "Run echo action"; + } + + @Override + public DesktopResponse runAction(DesktopParams params) { + + // Return a text message + + return new DesktopResponse(StsSuccess, "Test message from echo action at " + new Date()); + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java new file mode 100644 index 0000000000..23129a5ac5 --- /dev/null +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.filesys.smb.server.repo.desk; + +import org.alfresco.filesys.smb.server.repo.DesktopAction; +import org.alfresco.filesys.smb.server.repo.DesktopParams; +import org.alfresco.filesys.smb.server.repo.DesktopResponse; + +/** + * URL Desktop Action Class + * + *

Simple desktop action that returns a test URL. + * + * @author gkspencer + */ +public class URLDesktopAction extends DesktopAction { + + /** + * Class constructor + */ + public URLDesktopAction() + { + super( 0, DesktopAction.PreConfirmAction); + } + + @Override + public String getConfirmationString() { + return "Run URL action"; + } + + @Override + public DesktopResponse runAction(DesktopParams params) { + + // Return a URL in the status message + + return new DesktopResponse(StsLaunchURL, "http://www.alfresco.com"); + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java index 3d90a835cc..24f8e08896 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java @@ -16,11 +16,15 @@ */ package org.alfresco.filesys.smb.server.repo.pseudo; +import java.util.Enumeration; + import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.filesys.FileName; import org.alfresco.filesys.server.filesys.TreeConnection; import org.alfresco.filesys.smb.server.SMBSrvSession; import org.alfresco.filesys.smb.server.repo.ContentContext; +import org.alfresco.filesys.smb.server.repo.DesktopAction; +import org.alfresco.filesys.smb.server.repo.DesktopActionTable; import org.alfresco.filesys.smb.server.repo.FileState; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -126,27 +130,40 @@ public class ContentPseudoFileImpl implements PseudoFileInterface boolean isCIFS = sess instanceof SMBSrvSession; - // Add the drag and drop pseudo file, if enabled + // Add the desktop action pseudo files - if ( isCIFS && ctx.hasDragAndDropApp()) + if ( isCIFS && ctx.numberOfDesktopActions() > 0) { // If the file state is null create a file state for the path if ( fstate == null) ctx.getStateTable().findFileState( path, true, true); - // Enable the drag and drop pseudo file + // Add the desktop action pseudo files - fstate.addPseudoFile( ctx.getDragAndDropApp()); + DesktopActionTable actions = ctx.getDesktopActions(); + Enumeration actionNames = actions.enumerateActionNames(); + + while(actionNames.hasMoreElements()) + { + // Get the current desktop action + + String name = actionNames.nextElement(); + DesktopAction action = actions.getAction(name); + + // Add the pseudo file for the desktop action + + if ( action.hasPseudoFile()) + { + fstate.addPseudoFile( action.getPseudoFile()); + pseudoCnt++; - // Update the count of pseudo files added - - pseudoCnt++; - - // DEBUG - - if ( logger.isInfoEnabled()) - logger.info("Added drag/drop pseudo file for " + path); + // DEBUG + + if ( logger.isInfoEnabled()) + logger.info("Added desktop action " + action.getName() + " for " + path); + } + } } // Add the URL link pseudo file, if enabled