alfresco-community-repo/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
Gary Spencer 6478d72321 Replaced the file server code with the Alfresco JLAN project.
Restructured the file server code packages.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7757 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2008-01-06 16:44:00 +00:00

795 lines
26 KiB
Java

/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.filesys.repo;
import java.io.FileNotFoundException;
import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
import org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopActionTable;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
import org.alfresco.filesys.alfresco.DesktopTarget;
import org.alfresco.filesys.alfresco.IOControl;
import org.alfresco.filesys.alfresco.IOControlHandler;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.filesys.IOControlNotImplementedException;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.TreeConnection;
import org.alfresco.jlan.smb.SMBException;
import org.alfresco.jlan.smb.SMBStatus;
import org.alfresco.jlan.smb.nt.NTIOCtl;
import org.alfresco.jlan.util.DataBuffer;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationException;
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.cmr.security.AuthenticationService;
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
*
* <p>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);
// Filesystem driver and context
private ContentDiskDriver contentDriver;
private ContentContext contentContext;
/**
* Default constructor
*/
public ContentIOControlHandler()
{
}
/**
* Initalize the I/O control handler
*
* @param filesysDriver AlfrescoDiskDriver
* @param context AlfrescoContext
*/
public void initialize( AlfrescoDiskDriver filesysDriver, AlfrescoContext context)
{
this.contentDriver = (ContentDiskDriver) filesysDriver;
this.contentContext = (ContentContext) context;
}
/**
* Return the CIFS helper
*
* @return CifsHelper
*/
public final CifsHelper getCifsHelper()
{
return contentDriver.getCifsHelper();
}
/**
* Return the authentication service
*
* @return AuthenticationService
*/
public final AuthenticationService getAuthenticationService()
{
return contentDriver.getAuthenticationService();
}
/**
* 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
*
* @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 org.alfresco.jlan.util.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);
// Check for I/O controls that require a success status
if ( devType == NTIOCtl.DeviceFileSystem)
{
// I/O control requests that require a success status
//
// Create or get object id
if ( ioFunc == NTIOCtl.FsCtlCreateOrGetObjectId)
return null;
}
// Check if the I/O control looks like a custom I/O control request
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 ( getCifsHelper().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.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
DataBuffer retBuffer = null;
switch ( ioFunc)
{
// 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;
// Return the authentication ticket
case IOControl.CmdGetAuthTicket:
// Process the get auth ticket request
retBuffer = procGetAuthTicket(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
contentDriver.beginReadTransaction( sess);
// Get the file name from the request
String fName = reqBuf.getString( true);
if ( logger.isDebugEnabled())
logger.debug(" 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 = getCifsHelper().getNodeRef( folderNode, fName);
}
catch (FileNotFoundException ex)
{
}
// Check if the file/folder was found
if ( childNode == null)
{
// Return an error response
respBuf.putInt(DesktopAction.StsFileNotFound);
return respBuf;
}
// Check if this is a file or folder node
if ( getCifsHelper().isDirectory( childNode))
{
// Only return the status and node type for folders
respBuf.putInt(DesktopAction.StsSuccess);
respBuf.putInt(IOControl.TypeFolder);
}
else
{
// Indicate that this is a file node
respBuf.putInt(DesktopAction.StsSuccess);
respBuf.putInt(IOControl.TypeFile);
// Check if this file is a working copy
if ( getNodeService().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) getNodeService().getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER);
String copiedFrom = null;
if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM))
{
// Get the path of the file the working copy was generated from
NodeRef fromNode = (NodeRef) getNodeService().getProperty( childNode, ContentModel.PROP_COPY_REFERENCE);
if ( fromNode != null)
copiedFrom = (String) getNodeService().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 ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_LOCKABLE))
{
// Get the lock type and owner
String lockTypeStr = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_TYPE);
String lockOwner = null;
if ( lockTypeStr != null)
lockOwner = (String) getNodeService().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) getNodeService().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 get action information 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 procGetActionInfo( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
NetworkFile netFile)
{
// Get the executable file name from the request
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 desktop actions list
DesktopActionTable deskActions = contentContext.getDesktopActions();
if ( deskActions == null)
{
respBuf.putInt(DesktopAction.StsNoSuchAction);
return respBuf;
}
// 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;
}
// Start a transaction
contentDriver.beginReadTransaction( sess);
// Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when
// generating URLs for the client-side application so that the user does not have to re-authenticate
getTicketForClient( sess);
// Get the list of targets for the action
int targetCnt = reqBuf.getInt();
DesktopParams deskParams = new DesktopParams(sess, contentDriver, folderNode, netFile);
while ( reqBuf.getAvailableLength() > 4 && targetCnt > 0)
{
// 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;
try
{
// 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)
{
}
// 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
{
// Build an error response
respBuf.putInt(DesktopAction.StsFileNotFound);
respBuf.putString("Cannot find noderef for path " + path, true);
return respBuf;
}
// Update the target count
targetCnt--;
}
// DEBUG
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);
}
}
// Run the desktop action
DesktopResponse deskResponse = null;
try
{
// Run the desktop action
deskResponse = action.runAction(deskParams);
}
catch (Exception ex)
{
// Create an error response
deskResponse = new DesktopResponse(DesktopAction.StsError, ex.getMessage());
}
// Pack the action response
if ( deskResponse != null)
{
// Pack the status
respBuf.putInt(deskResponse.getStatus());
respBuf.putString(deskResponse.hasStatusMessage() ? deskResponse.getStatusMessage() : "", true);
}
else
{
// Pack an error response
respBuf.putInt(DesktopAction.StsError);
respBuf.putString("Action did not return response", true);
}
// Return the response
return respBuf;
}
/**
* Process the get authentication ticket 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 procGetAuthTicket( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
NetworkFile netFile)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug(" Get Auth Ticket");
// Create a response buffer
DataBuffer respBuf = new DataBuffer(256);
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
// Start a transaction
contentDriver.beginReadTransaction( sess);
// Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when
// generating URLs for the client-side application so that the user does not have to re-authenticate
getTicketForClient( sess);
// Pack the response
AlfrescoClientInfo cInfo = (AlfrescoClientInfo) sess.getClientInformation();
if ( cInfo != null && cInfo.getAuthenticationTicket() != null) {
respBuf.putInt(DesktopAction.StsAuthTicket);
respBuf.putString( cInfo.getAuthenticationTicket(), true);
}
else {
respBuf.putInt(DesktopAction.StsError);
respBuf.putString( "Client information invalid", true);
}
// Return the response
return respBuf;
}
/**
* Get, or validate, an authentication ticket for the client
*
* @param sess SrvSession
*/
private final void getTicketForClient(SrvSession sess)
{
// Get the client information and check if there is a ticket allocated
AlfrescoClientInfo cInfo = (AlfrescoClientInfo) sess.getClientInformation();
if ( cInfo == null)
return;
boolean needTicket = true;
if ( cInfo.hasAuthenticationTicket())
{
// Validate the existing ticket, it may have expired
try
{
// Validate the existing ticket
getAuthenticationService().validate( cInfo.getAuthenticationTicket());
needTicket = false;
}
catch ( AuthenticationException ex)
{
// Invalidate the current ticket
try
{
getAuthenticationService().invalidateTicket( cInfo.getAuthenticationTicket());
cInfo.setAuthenticationTicket( null);
}
catch (Exception ex2)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Error during invalidate ticket", ex2);
}
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Auth ticket expired or invalid");
}
}
// Check if a ticket needs to be allocated
if ( needTicket == true)
{
// Allocate a new ticket and store in the client information for this session
String ticket = getAuthenticationService().getCurrentTicket();
cInfo.setAuthenticationTicket( ticket);
}
}
}