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
*
@@ -2013,6 +2246,36 @@ public class ServerConfiguration implements ApplicationListener
return m_nbBindAddress;
}
+ /**
+ * Return the NetBIOS name server port
+ *
+ * @return int
+ */
+ public final int getNetBIOSNamePort()
+ {
+ return m_nbNamePort;
+ }
+
+ /**
+ * Return the NetBIOS session port
+ *
+ * @return int
+ */
+ public final int getNetBIOSSessionPort()
+ {
+ return m_nbSessPort;
+ }
+
+ /**
+ * Return the NetBIOS datagram port
+ *
+ * @return int
+ */
+ public final int getNetBIOSDatagramPort()
+ {
+ return m_nbDatagramPort;
+ }
+
/**
* Return the network broadcast mask to be used for broadcast datagrams.
*
@@ -2154,6 +2417,16 @@ public class ServerConfiguration implements ApplicationListener
return m_win32NBUseWinsock;
}
+ /**
+ * Return the native SMB port
+ *
+ * @return int
+ */
+ public final int getTcpipSMBPort()
+ {
+ return m_tcpSMBPort;
+ }
+
/**
* Return the timezone name
*
@@ -2716,6 +2989,36 @@ public class ServerConfiguration implements ApplicationListener
m_netBIOSEnable = ena;
}
+ /**
+ * Set the NetBIOS name server port
+ *
+ * @param port int
+ */
+ public final void setNetBIOSNamePort(int port)
+ {
+ m_nbNamePort = port;
+ }
+
+ /**
+ * Set the NetBIOS session port
+ *
+ * @param port int
+ */
+ public final void setNetBIOSSessionPort(int port)
+ {
+ m_nbSessPort = port;
+ }
+
+ /**
+ * Set the NetBIOS datagram port
+ *
+ * @param port int
+ */
+ public final void setNetBIOSDatagramPort(int port)
+ {
+ m_nbDatagramPort = port;
+ }
+
/**
* Enable/disable the TCP/IP SMB support
*
@@ -2726,6 +3029,16 @@ public class ServerConfiguration implements ApplicationListener
m_tcpSMBEnable = ena;
}
+ /**
+ * Set the TCP/IP SMB port
+ *
+ * @param port int
+ */
+ public final void setTcpipSMBPort( int port)
+ {
+ m_tcpSMBPort = port;
+ }
+
/**
* Enable/disable the Win32 NetBIOS SMB support
*
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/mailslot/TcpipNetBIOSHostAnnouncer.java b/source/java/org/alfresco/filesys/smb/mailslot/TcpipNetBIOSHostAnnouncer.java
index 7f29cf8075..afa02c99f0 100644
--- a/source/java/org/alfresco/filesys/smb/mailslot/TcpipNetBIOSHostAnnouncer.java
+++ b/source/java/org/alfresco/filesys/smb/mailslot/TcpipNetBIOSHostAnnouncer.java
@@ -46,7 +46,6 @@ public class TcpipNetBIOSHostAnnouncer extends HostAnnouncer
// Broadcast address and port
private InetAddress m_bcastAddr;
- private int m_bcastPort = RFCNetBIOSProtocol.DATAGRAM;
// NetBIOS datagram
@@ -140,7 +139,7 @@ public class TcpipNetBIOSHostAnnouncer extends HostAnnouncer
public final void setBroadcastAddress(String addr, int port) throws UnknownHostException
{
m_bcastAddr = InetAddress.getByName(addr);
- m_bcastPort = port;
+ m_port = port;
}
/**
@@ -198,11 +197,15 @@ public class TcpipNetBIOSHostAnnouncer extends HostAnnouncer
*/
protected void sendAnnouncement(String hostName, byte[] buf, int offset, int len) throws Exception
{
-
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Send NetBIOS host announcement to " + m_bcastAddr.getHostAddress() + ", port " + getPort());
+
// Send the host announce datagram
m_nbdgram.SendDatagram(NetBIOSDatagram.DIRECT_GROUP, hostName, NetBIOSName.FileServer, getDomain(),
- NetBIOSName.MasterBrowser, buf, len, offset);
+ NetBIOSName.MasterBrowser, buf, len, offset, m_bcastAddr, getPort());
}
/**
diff --git a/source/java/org/alfresco/filesys/smb/server/NetBIOSSessionSocketHandler.java b/source/java/org/alfresco/filesys/smb/server/NetBIOSSessionSocketHandler.java
index 9fa5b20a46..977819ad79 100644
--- a/source/java/org/alfresco/filesys/smb/server/NetBIOSSessionSocketHandler.java
+++ b/source/java/org/alfresco/filesys/smb/server/NetBIOSSessionSocketHandler.java
@@ -20,7 +20,6 @@ import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
-import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
import org.alfresco.filesys.server.config.ServerConfiguration;
import org.alfresco.filesys.smb.mailslot.TcpipNetBIOSHostAnnouncer;
@@ -154,7 +153,7 @@ public class NetBIOSSessionSocketHandler extends SessionSocketHandler
// Create the NetBIOS SMB handler
- SessionSocketHandler sessHandler = new NetBIOSSessionSocketHandler(server, RFCNetBIOSProtocol.PORT, config
+ SessionSocketHandler sessHandler = new NetBIOSSessionSocketHandler(server, config.getNetBIOSSessionPort(), config
.getSMBBindAddress(), sockDbg);
sessHandler.initialize();
@@ -171,7 +170,7 @@ public class NetBIOSSessionSocketHandler extends SessionSocketHandler
// DEBUG
if (logger.isDebugEnabled() && sockDbg)
- logger.debug("TCP NetBIOS session handler created");
+ logger.debug("TCP NetBIOS session handler created on port " + config.getNetBIOSSessionPort());
// Check if a host announcer should be created
@@ -188,6 +187,7 @@ public class NetBIOSSessionSocketHandler extends SessionSocketHandler
announcer.setDomain(config.getDomainName());
announcer.setComment(config.getComment());
announcer.setBindAddress(config.getSMBBindAddress());
+ announcer.setPort(config.getNetBIOSDatagramPort());
// Set the announcement interval
@@ -222,7 +222,7 @@ public class NetBIOSSessionSocketHandler extends SessionSocketHandler
// DEBUG
if (logger.isDebugEnabled() && sockDbg)
- logger.debug("TCP NetBIOS host announcer created");
+ logger.debug("TCP NetBIOS host announcer created on port " + config.getNetBIOSDatagramPort());
}
}
}
diff --git a/source/java/org/alfresco/filesys/smb/server/SMBServer.java b/source/java/org/alfresco/filesys/smb/server/SMBServer.java
index ce08072aec..d95f3df092 100644
--- a/source/java/org/alfresco/filesys/smb/server/SMBServer.java
+++ b/source/java/org/alfresco/filesys/smb/server/SMBServer.java
@@ -80,15 +80,6 @@ public class SMBServer extends NetworkFileServer implements Runnable
private int m_srvType = ServerType.WorkStation + ServerType.Server + ServerType.NTServer;
- // Next available session id
-
- private int m_sessId;
-
- // Server shutdown flag and server active flag
-
- private boolean m_shutdown = false;
- private boolean m_active = false;
-
// Server GUID
private UUID m_serverGUID;
@@ -472,7 +463,7 @@ public class SMBServer extends NetworkFileServer implements Runnable
// Clear the server shutdown flag
- m_shutdown = false;
+ setShutdown(false);
// Get the list of IP addresses the server is bound to
@@ -529,7 +520,7 @@ public class SMBServer extends NetworkFileServer implements Runnable
// Wait for incoming connection requests
- while (m_shutdown == false)
+ while (hasShutdown() == false)
{
// Sleep for a while
@@ -569,7 +560,7 @@ public class SMBServer extends NetworkFileServer implements Runnable
// Do not report an error if the server has shutdown, closing the server socket
// causes an exception to be thrown.
- if (m_shutdown == false)
+ if (hasShutdown() == false)
{
logger.error("Server error : ", ex);
@@ -654,7 +645,7 @@ public class SMBServer extends NetworkFileServer implements Runnable
// Indicate that the server is closing
- m_shutdown = true;
+ setShutdown(true);
try
{
diff --git a/source/java/org/alfresco/filesys/smb/server/TcpipSMBSessionSocketHandler.java b/source/java/org/alfresco/filesys/smb/server/TcpipSMBSessionSocketHandler.java
index a00f863407..f16db5ff03 100644
--- a/source/java/org/alfresco/filesys/smb/server/TcpipSMBSessionSocketHandler.java
+++ b/source/java/org/alfresco/filesys/smb/server/TcpipSMBSessionSocketHandler.java
@@ -21,7 +21,6 @@ import java.net.Socket;
import java.net.SocketException;
import org.alfresco.filesys.server.config.ServerConfiguration;
-import org.alfresco.filesys.smb.TcpipSMB;
/**
* Native SMB Session Socket Handler Class
@@ -153,7 +152,7 @@ public class TcpipSMBSessionSocketHandler extends SessionSocketHandler
// Create the NetBIOS SMB handler
- SessionSocketHandler sessHandler = new TcpipSMBSessionSocketHandler(server, TcpipSMB.PORT, config
+ SessionSocketHandler sessHandler = new TcpipSMBSessionSocketHandler(server, config.getTcpipSMBPort(), config
.getSMBBindAddress(), sockDbg);
sessHandler.initialize();
@@ -168,6 +167,6 @@ public class TcpipSMBSessionSocketHandler extends SessionSocketHandler
// DEBUG
if (logger.isDebugEnabled() && sockDbg)
- logger.debug("Native SMB TCP session handler created");
+ logger.debug("Native SMB TCP session handler created on port " + config.getTcpipSMBPort());
}
}
diff --git a/source/java/org/alfresco/filesys/smb/server/repo/CifsHelper.java b/source/java/org/alfresco/filesys/smb/server/repo/CifsHelper.java
index 84e0d080db..aa5a889f2d 100644
--- a/source/java/org/alfresco/filesys/smb/server/repo/CifsHelper.java
+++ b/source/java/org/alfresco/filesys/smb/server/repo/CifsHelper.java
@@ -31,6 +31,7 @@ import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.FileExistsException;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.server.filesys.FileName;
+import org.alfresco.filesys.util.WildCard;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
@@ -402,11 +403,7 @@ public class CifsHelper
}
/**
- * Performs an XPath query to get the first-level descendents matching the given path
- *
- * @param pathRootNodeRef
- * @param pathElement
- * @return
+ * Searches for the node or nodes that match the path element for the given parent node
*/
private List getDirectDescendents(NodeRef pathRootNodeRef, String pathElement)
{
@@ -416,18 +413,36 @@ public class CifsHelper
" Path Root: " + pathRootNodeRef + "\n" +
" Path Element: " + pathElement);
}
- // escape for the Lucene syntax search
- String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement);
- // do the lookup
- List childInfos = fileFolderService.search(
- pathRootNodeRef,
- escapedPathElement,
- false);
- // convert to noderefs
- List results = new ArrayList(childInfos.size());
- for (org.alfresco.service.cmr.model.FileInfo info : childInfos)
+ List results = null;
+ // if this contains no wildcards, then we can fasttrack it
+ if (!WildCard.containsWildcards(pathElement))
{
- results.add(info.getNodeRef());
+ // a specific name is required
+ NodeRef foundNodeRef = fileFolderService.searchSimple(pathRootNodeRef, pathElement);
+ if (foundNodeRef == null)
+ {
+ results = Collections.emptyList();
+ }
+ else
+ {
+ results = Collections.singletonList(foundNodeRef);
+ }
+ }
+ else
+ {
+ // escape for the Lucene syntax search
+ String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement);
+ // do the lookup
+ List childInfos = fileFolderService.search(
+ pathRootNodeRef,
+ escapedPathElement,
+ false);
+ // convert to noderefs
+ results = new ArrayList(childInfos.size());
+ for (org.alfresco.service.cmr.model.FileInfo info : childInfos)
+ {
+ results.add(info.getNodeRef());
+ }
}
// done
return results;
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 0d57ee05c3..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,10 +16,8 @@
*/
package org.alfresco.filesys.smb.server.repo;
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.net.URL;
import java.util.List;
import javax.transaction.UserTransaction;
@@ -50,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;
@@ -103,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
*
@@ -125,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
*/
@@ -156,7 +213,6 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface
{
this.searchService = searchService;
}
-
/**
* @param transactionService the transaction service
@@ -179,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;
}
/**
@@ -335,52 +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());
- File appFile = new File(appURL.getFile());
- 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");
@@ -411,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");
@@ -442,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();
}
/**
@@ -506,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
@@ -690,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
@@ -891,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
@@ -1408,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());
}
}
}
@@ -1681,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
@@ -1980,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/DesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopAction.java
new file mode 100644
index 0000000000..d17fca8b31
--- /dev/null
+++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopAction.java
@@ -0,0 +1,559 @@
+/*
+ * 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.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
+
+import org.alfresco.config.ConfigElement;
+import org.alfresco.filesys.server.filesys.DiskSharedDevice;
+import org.alfresco.filesys.smb.server.repo.pseudo.LocalPseudoFile;
+import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFile;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.coci.CheckOutCheckInService;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.search.SearchService;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Desktop Action Class
+ *
+ * @author gkspencer
+ *
+ */
+public abstract class DesktopAction {
+
+ // Logging
+
+ protected static final Log logger = LogFactory.getLog(DesktopAction.class);
+
+ // Constants
+ //
+ // Action attributes
+
+ public static final int AttrTargetFiles = 0x0001; // allow files from the same folder
+ public static final int AttrTargetFolders = 0x0002; // allow sub-folders from the same folder
+ public static final int AttrClientFiles = 0x0004; // allow files from a client drive
+ // File(s) will be copied to the target folder
+ public static final int AttrClientFolders = 0x0008; // allow folders from a client drive
+ // Folder(s) will be copied to the target folder
+ public static final int AttrAlfrescoFiles = 0x0010; // allow files from another path within the Alfresco share
+ public static final int AttrAlfrescoFolders = 0x0020; // allow folders from another path within the Alfresco share
+ public static final int AttrMultiplePaths = 0x0040; // run action using multiple paths
+ // default is to run the action against a single path with the client app calling the action
+ // multiple times
+ public static final int AttrAllowNoParams = 0x0080; // allow action to run without parameters
+ // used when files/folder parameters are optional
+
+ public static final int AttrAnyFiles = AttrTargetFiles + AttrClientFiles + AttrAlfrescoFiles;
+ public static final int AttrAnyFolders = AttrTargetFolders + AttrClientFolders + AttrAlfrescoFolders;
+ public static final int AttrAnyFilesFolders = AttrAnyFiles + AttrAnyFolders;
+
+ // Client side pre-processing actions
+
+ public static final int PreCopyToTarget = 0x0001; // copy files/folders from another Alfresco folder to the target folder
+ public static final int PreConfirmAction = 0x0002; // confirm action, allow user to abort
+ public static final int PreLocalToWorkingCopy = 0x0004; // local files must match a working copy in the target folder
+
+ // Desktop action 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;
+ public static final int StsNoSuchAction = 6;
+ public static final int StsLaunchURL = 7;
+ public static final int StsCommandLine = 8;
+
+ // Action name
+
+ private String m_name;
+
+ // Pseudo file details
+
+ private PseudoFile m_pseudoFile;
+
+ // Desktop action attributes
+
+ private int m_attributes;
+
+ // Desktop action client side pre-processing control
+
+ private int m_clientPreActions;
+
+ // Filesystem driver and context
+
+ private ContentDiskDriver m_contentDriver;
+ private ContentContext m_contentContext;
+
+ // Debug enable flag
+
+ private boolean m_debug;
+
+ /**
+ * Default constructor
+ */
+ protected DesktopAction()
+ {
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param attr int
+ * @param preActions int
+ */
+ protected DesktopAction(int attr, int preActions)
+ {
+ setAttributes(attr);
+ setPreProcessActions(preActions);
+ }
+
+ /**
+ * Class constructor
+ *
+ * @param name String
+ */
+ protected DesktopAction(String name)
+ {
+ m_name = name;
+ }
+
+ /**
+ * Return the desktop action attributes
+ *
+ * @return int
+ */
+ public final int getAttributes()
+ {
+ return m_attributes;
+ }
+
+ /**
+ * Check for a specified action attribute
+ *
+ * @param attr int
+ * @return boolean
+ */
+ public final boolean hasAttribute(int attr)
+ {
+ return ( m_attributes & attr) != 0 ? true : false;
+ }
+
+ /**
+ * Return the desktop action pore-processing actions
+ *
+ * @return int
+ */
+ public final int getPreProcessActions()
+ {
+ return m_clientPreActions;
+ }
+
+ /**
+ * Check for the specified pre-process action
+ *
+ * @param pre int
+ * @return boolean
+ */
+ public final boolean hasPreProcessAction(int pre)
+ {
+ return (m_clientPreActions & pre) != 0 ? true : false;
+ }
+
+ /**
+ * Return the action name
+ *
+ * @return String
+ */
+ public final String getName()
+ {
+ return m_name;
+ }
+
+ /**
+ * Check if the action has an associated pseudo file
+ *
+ * @return boolean
+ */
+ public final boolean hasPseudoFile()
+ {
+ return m_pseudoFile != null ? true : false;
+ }
+
+ /**
+ * Return the associated pseudo file
+ *
+ * @return PseudoFile
+ */
+ public final PseudoFile getPseudoFile()
+ {
+ return m_pseudoFile;
+ }
+
+ /**
+ * Return the content filesystem driver
+ *
+ * @return ContentDiskDriver
+ */
+ public final ContentDiskDriver getDriver()
+ {
+ return m_contentDriver;
+ }
+
+ /**
+ * Return the filesystem context
+ *
+ * @return ContentContext
+ */
+ public final ContentContext getContext()
+ {
+ return m_contentContext;
+ }
+
+ /**
+ * Return the action confirmation string to be displayed by the client application
+ *
+ * @return String
+ */
+ public String getConfirmationString()
+ {
+ return null;
+ }
+
+ /**
+ * Check if debug output is enabled
+ *
+ * @return boolean
+ */
+ public final boolean hasDebug()
+ {
+ return m_debug;
+ }
+
+ /**
+ * Initialize the desktop action
+ *
+ * @param global ConfigElement
+ * @param config ConfigElement
+ * @param fileSys DiskSharedDevice
+ * @exception DesktopActionException
+ */
+ public void initializeAction(ConfigElement global, ConfigElement config, DiskSharedDevice fileSys)
+ throws DesktopActionException
+ {
+ // Perform standard initialization
+
+ standardInitialize(global, config, fileSys);
+ }
+
+ /**
+ * Perform standard desktop action initialization
+ *
+ * @param global ConfigElement
+ * @param config ConfigElement
+ * @param fileSys DiskSharedDevice
+ * @exception DesktopActionException
+ */
+ public void standardInitialize(ConfigElement global, ConfigElement config, DiskSharedDevice fileSys)
+ throws DesktopActionException
+ {
+ // Save the filesystem device and I/O handler
+
+ if ( fileSys.getDiskInterface() instanceof ContentDiskDriver)
+ {
+ m_contentDriver = (ContentDiskDriver) fileSys.getDiskInterface();
+ m_contentContext = (ContentContext) fileSys.getDiskContext();
+ }
+ else
+ throw new DesktopActionException("Desktop action requires content filesystem driver");
+
+ // Check for standard config values
+
+ ConfigElement elem = config.getChild("name");
+ if ( elem != null && elem.getValue().length() > 0)
+ {
+ // Set the action name
+
+ setName(elem.getValue());
+ }
+ else
+ throw new DesktopActionException("Desktop action name not specified");
+
+ // Get the pseudo file name
+
+ ConfigElement name = config.getChild("filename");
+ if ( name == null || name.getValue() == null || name.getValue().length() == 0)
+ throw new DesktopActionException("Desktop action pseudo name not specified");
+
+ // Get the local path to the executable
+
+ ConfigElement path = findConfigElement("path", global, config);
+ if ( path == null || path.getValue() == null || path.getValue().length() == 0)
+ throw new DesktopActionException("Desktop action executable path not specified");
+
+ // Check that the application exists on the local filesystem
+
+ URL appURL = this.getClass().getClassLoader().getResource(path.getValue());
+ if ( appURL == null)
+ throw new DesktopActionException("Failed to find drag and drop application, " + path.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 DesktopActionException("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 DesktopActionException("Drag and drop application not found, " + path.getValue());
+
+ // Create the pseudo file for the action
+
+ PseudoFile pseudoFile = new LocalPseudoFile(name.getValue(), appFile.getAbsolutePath());
+ setPseudoFile(pseudoFile);
+
+ // Check if confirmations should be switched off for the action
+
+ if ( findConfigElement("noConfirm", global, config) != null && hasPreProcessAction(PreConfirmAction))
+ setPreProcessActions(getPreProcessActions() - PreConfirmAction);
+
+ // Check if debug output is enabled for the action
+
+ ConfigElement debug = findConfigElement("debug", global, config);
+ if ( debug != null)
+ setDebug(true);
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled() && hasDebug())
+ logger.debug("Initialized desktop action " + getName() + ", pseudo name " + name.getValue());
+ }
+
+ /**
+ * Find the required configuration element in the local or global config
+ *
+ * @param name String
+ * @param global ConfigElement
+ * @param local configElement
+ * @return ConfigElement
+ */
+ private final ConfigElement findConfigElement(String name, ConfigElement global, ConfigElement local)
+ {
+ // Check if the required setting is in the local config
+
+ ConfigElement elem = local.getChild(name);
+ if ( elem == null && global != null)
+ elem = global.getChild(name);
+
+ return elem;
+ }
+
+ /**
+ * Run the desktop action
+ *
+ * @param params DesktopParams
+ * @return DesktopResponse
+ * @exception
+ */
+ public abstract DesktopResponse runAction(DesktopParams params)
+ throws DesktopActionException;
+
+ /**
+ * Return the CIFS helper
+ *
+ * @return CifsHelper
+ */
+ protected final CifsHelper getCifsHelper()
+ {
+ return m_contentDriver.getCifsHelper();
+ }
+
+ /**
+ * Return the transaction service
+ *
+ * @return TransactionService
+ */
+ protected final TransactionService getTransactionService()
+ {
+ return m_contentDriver.getTransactionService();
+ }
+
+ /**
+ * Return the node service
+ *
+ * @return NodeService
+ */
+ protected final NodeService getNodeService()
+ {
+ return m_contentDriver.getNodeService();
+ }
+
+
+ /**
+ * Return the content service
+ *
+ * @return ContentService
+ */
+ public final ContentService getContentService()
+ {
+ return m_contentDriver.getContentService();
+ }
+
+ /**
+ * Return the namespace service
+ *
+ * @return NamespaceService
+ */
+ public final NamespaceService getNamespaceService()
+ {
+ return m_contentDriver.getNamespaceService();
+ }
+
+ /**
+ * Return the search service
+ *
+ * @return SearchService
+ */
+ public final SearchService getSearchService()
+ {
+ return m_contentDriver.getSearchService();
+ }
+
+ /**
+ * Return the check in/out service
+ *
+ * @return CheckOutInService
+ */
+ public final CheckOutCheckInService getCheckInOutService()
+ {
+ return m_contentDriver.getCheckInOutService();
+ }
+
+ /**
+ * Set the action attributes
+ *
+ * @param attr int
+ */
+ protected final void setAttributes(int attr)
+ {
+ m_attributes = attr;
+ }
+
+ /**
+ * Set the client side pre-processing actions
+ *
+ * @param pre int
+ */
+ protected final void setPreProcessActions(int pre)
+ {
+ m_clientPreActions = pre;
+ }
+
+ /**
+ * Set the action name
+ *
+ * @param name String
+ */
+ protected final void setName(String name)
+ {
+ m_name = name;
+ }
+
+ /**
+ * Set the associated pseudo file
+ *
+ * @param pseudoFile PseudoFile
+ */
+ protected final void setPseudoFile(PseudoFile pseudoFile)
+ {
+ m_pseudoFile = pseudoFile;
+ }
+
+ /**
+ * Enable debug output
+ *
+ * @param ena boolean
+ */
+ protected final void setDebug(boolean ena)
+ {
+ m_debug = ena;
+ }
+
+ /**
+ * Equality check
+ *
+ * @param obj Object
+ * @return boolean
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if ( obj instanceof DesktopAction)
+ {
+ DesktopAction action = (DesktopAction) obj;
+ return action.getName().equals(getName());
+ }
+ return false;
+ }
+
+ /**
+ * Return the desktop action details as a string
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+
+ str.append("[");
+ str.append(getName());
+ str.append(":Attr=0x");
+ str.append(Integer.toHexString(getAttributes()));
+ str.append(":Pre=0x");
+ str.append(Integer.toHexString(getPreProcessActions()));
+
+ if ( hasPseudoFile())
+ {
+ str.append(":Pseudo=");
+ str.append(getPseudoFile().getFileName());
+ }
+ str.append("]");
+
+ return str.toString();
+ }
+}
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