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
This commit is contained in:
Gary Spencer
2008-01-06 16:44:00 +00:00
parent 0a7fef92aa
commit 6478d72321
396 changed files with 6336 additions and 118542 deletions

View File

@@ -0,0 +1,676 @@
/*
* 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 java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileExistsException;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.util.WildCard;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.SearchLanguageConversion;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class with supplying helper methods and potentially acting as a cache for
* queries.
*
* @author derekh
*/
public class CifsHelper
{
// Logging
private static Log logger = LogFactory.getLog(CifsHelper.class);
// Services
private DictionaryService dictionaryService;
private NodeService nodeService;
private FileFolderService fileFolderService;
private MimetypeService mimetypeService;
private PermissionService permissionService;
private boolean isReadOnly;
// Mark locked files as offline
private boolean lockedFilesAsOffline;
/**
* Class constructor
*/
public CifsHelper()
{
isReadOnly = false;
}
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* @return Returns true if all files/folders should be treated as read-only
*/
public boolean isReadOnly()
{
return isReadOnly;
}
/**
* Set whether the system allows files to be edited or not. The default is
* to allow writes.
* @param allowWrites true to allow writes, otherwise false for read-only mode
*/
public void setAllowWrites(boolean allowWrites)
{
this.isReadOnly = !allowWrites;
}
/**
* Enable marking of locked files as offline
*
* @param ena boolean
*/
public final void setMarkLockedFilesAsOffline(boolean ena)
{
lockedFilesAsOffline = ena;
}
/**
* Check if locked files should be marked as offline
*
* @return boolean
*/
public final boolean hasLockedFilesAsOffline()
{
return lockedFilesAsOffline;
}
/**
* @param serviceRegistry for repo connection
* @param nodeRef
* @return Returns true if the node is a subtype of {@link ContentModel#TYPE_FOLDER folder}
* @throws AlfrescoRuntimeException if the type is neither related to a folder or content
*/
public boolean isDirectory(NodeRef nodeRef)
{
QName nodeTypeQName = nodeService.getType(nodeRef);
if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER))
{
return true;
}
else if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT))
{
return false;
}
else
{
// it is not a directory, but what is it?
return false;
}
}
/**
* Extract a single node's file info, where the node is reference by
* a path relative to an ancestor node.
*
* @param pathRootNodeRef
* @param path
* @return Returns the existing node reference
* @throws FileNotFoundException
*/
public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
{
// get the node being referenced
NodeRef nodeRef = getNodeRef(pathRootNodeRef, path);
return getFileInformation(nodeRef);
}
/**
* Helper method to extract file info from a specific node.
* <p>
* This method goes direct to the repo for all information and no data is
* cached here.
*
* @param nodeRef the node that the path is relative to
* @param path the path to get info for
* @return Returns the file information pertinent to the node
* @throws FileNotFoundException if the path refers to a non-existent file
*/
public ContentFileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException
{
// get the file info
org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef);
// retrieve required properties and create file info
ContentFileInfo fileInfo = new ContentFileInfo();
fileInfo.setNodeRef(nodeRef);
// unset all attribute flags
int fileAttributes = 0;
fileInfo.setFileAttributes(fileAttributes);
if (fileFolderInfo.isFolder())
{
// add directory attribute
fileAttributes |= FileAttribute.Directory;
fileInfo.setFileAttributes(fileAttributes);
}
else
{
Map<QName, Serializable> nodeProperties = fileFolderInfo.getProperties();
// Get the file size from the content
ContentData contentData = (ContentData) nodeProperties.get(ContentModel.PROP_CONTENT);
long size = 0L;
if (contentData != null)
{
size = contentData.getSize();
}
fileInfo.setSize(size);
// Set the allocation size by rounding up the size to a 512 byte block boundary
if ( size > 0)
fileInfo.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L);
// Check the lock status of the file
String lockTypeStr = (String) nodeProperties.get(ContentModel.PROP_LOCK_TYPE);
if ( lockTypeStr != null )
{
// File is locked so mark it as read-only and offline
int attr = fileInfo.getFileAttributes();
if (( attr & FileAttribute.ReadOnly) == 0)
attr += FileAttribute.ReadOnly;
if ( hasLockedFilesAsOffline())
attr += FileAttribute.NTOffline;
fileInfo.setFileAttributes( attr);
}
// Check if it is a link node
if ( fileFolderInfo.isLink())
fileInfo.setLinkNodeRef( fileFolderInfo.getLinkNodeRef());
}
// created
Date createdDate = fileFolderInfo.getCreatedDate();
if (createdDate != null)
{
long created = DefaultTypeConverter.INSTANCE.longValue(createdDate);
fileInfo.setCreationDateTime(created);
}
// modified
Date modifiedDate = fileFolderInfo.getModifiedDate();
if (modifiedDate != null)
{
long modified = DefaultTypeConverter.INSTANCE.longValue(modifiedDate);
fileInfo.setModifyDateTime(modified);
fileInfo.setAccessDateTime(modified);
}
// name
String name = fileFolderInfo.getName();
if (name != null)
{
fileInfo.setFileName(name);
}
// Read/write access
boolean deniedPermission = permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED;
if (isReadOnly || deniedPermission)
{
int attr = fileInfo.getFileAttributes();
if (( attr & FileAttribute.ReadOnly) == 0)
{
attr += FileAttribute.ReadOnly;
fileInfo.setFileAttributes(attr);
}
}
// Set the normal file attribute if no other attributes are set
if ( fileInfo.getFileAttributes() == 0)
fileInfo.setFileAttributes(FileAttribute.NTNormal);
// Debug
if (logger.isDebugEnabled())
{
logger.debug("Fetched file info: \n" +
" info: " + fileInfo);
}
// Return the file information
return fileInfo;
}
/**
* Creates a file or directory using the given paths.
* <p>
* If the directory path doesn't exist, then all the parent directories will be created.
* If the file path is <code>null</code>, then the file will not be created
*
* @param rootNodeRef the root node of the path
* @param path the path to a node
* @param isFile true if the node to be created must be a file
* @return Returns a newly created file or folder node
* @throws FileExistsException if the file or folder already exists
*/
public NodeRef createNode(NodeRef rootNodeRef, String path, boolean isFile) throws FileExistsException
{
// split the path up into its constituents
StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false);
List<String> folderPathElements = new ArrayList<String>(10);
String name = null;
while (tokenizer.hasMoreTokens())
{
String pathElement = tokenizer.nextToken();
if (!tokenizer.hasMoreTokens())
{
// the last token becomes the name
name = pathElement;
}
else
{
// add the path element to the parent folder path
folderPathElements.add(pathElement);
}
}
// ensure that the folder path exists
NodeRef parentFolderNodeRef = rootNodeRef;
if (folderPathElements.size() > 0)
{
parentFolderNodeRef = fileFolderService.makeFolders(
rootNodeRef,
folderPathElements,
ContentModel.TYPE_FOLDER).getNodeRef();
}
// add the file or folder
QName typeQName = isFile ? ContentModel.TYPE_CONTENT : ContentModel.TYPE_FOLDER;
try
{
NodeRef nodeRef = fileFolderService.create(parentFolderNodeRef, name, typeQName).getNodeRef();
// done
if (logger.isDebugEnabled())
{
logger.debug("Created node: \n" +
" device root: " + rootNodeRef + "\n" +
" path: " + path + "\n" +
" is file: " + isFile + "\n" +
" new node: " + nodeRef);
}
return nodeRef;
}
catch (org.alfresco.service.cmr.model.FileExistsException e)
{
throw new FileExistsException(path);
}
}
private void addDescendents(List<NodeRef> pathRootNodeRefs, Stack<String> pathElements, List<NodeRef> results)
{
if (pathElements.isEmpty())
{
// if this method is called with an empty path element stack, then the
// current context nodes are the results to be added
results.addAll(pathRootNodeRefs);
return;
}
// take the first path element off the stack
String pathElement = pathElements.pop();
// iterate over each path root node
for (NodeRef pathRootNodeRef : pathRootNodeRefs)
{
// deal with cyclic relationships by not traversing down any node already in the results
if (results.contains(pathRootNodeRef))
{
continue;
}
// get direct descendents along the path
List<NodeRef> directDescendents = getDirectDescendents(pathRootNodeRef, pathElement);
// recurse onto the descendents
addDescendents(directDescendents, pathElements, results);
}
// restore the path element stack
pathElements.push(pathElement);
}
/**
* Searches for the node or nodes that match the path element for the given parent node
*/
private List<NodeRef> getDirectDescendents(NodeRef pathRootNodeRef, String pathElement)
{
if (logger.isDebugEnabled())
{
logger.debug("Getting direct descendents: \n" +
" Path Root: " + pathRootNodeRef + "\n" +
" Path Element: " + pathElement);
}
List<NodeRef> results = null;
// if this contains no wildcards, then we can fasttrack it
if (!WildCard.containsWildcards(pathElement))
{
// 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<org.alfresco.service.cmr.model.FileInfo> childInfos = fileFolderService.search(
pathRootNodeRef,
escapedPathElement,
false);
// convert to noderefs
results = new ArrayList<NodeRef>(childInfos.size());
for (org.alfresco.service.cmr.model.FileInfo info : childInfos)
{
results.add(info.getNodeRef());
}
}
// done
return results;
}
/**
* Finds the nodes being reference by the given directory and file paths.
* <p>
* Examples of the path are:
* <ul>
* <li>\New Folder\New Text Document.txt</li>
* <li>\New Folder\Sub Folder</li>
* </ul>
*
* @param searchRootNodeRef the node from which to start the path search
* @param path the search path to either a folder or file
* @return Returns references to all matching nodes
*/
public List<NodeRef> getNodeRefs(NodeRef pathRootNodeRef, String path)
{
// tokenize the path and push into a stack in reverse order so that
// the root directory gets popped first
StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false);
String[] tokens = new String[tokenizer.countTokens()];
int count = 0;
while(tokenizer.hasMoreTokens())
{
tokens[count] = tokenizer.nextToken();
count++;
}
Stack<String> pathElements = new Stack<String>();
for (int i = tokens.length - 1; i >= 0; i--)
{
pathElements.push(tokens[i]);
}
// start with a single parent node
List<NodeRef> pathRootNodeRefs = Collections.singletonList(pathRootNodeRef);
// result storage
List<NodeRef> results = new ArrayList<NodeRef>(5);
// kick off the path walking
addDescendents(pathRootNodeRefs, pathElements, results);
// done
if (logger.isDebugEnabled())
{
logger.debug("Retrieved node references for path: \n" +
" path root: " + pathRootNodeRef + "\n" +
" path: " + path + "\n" +
" results: " + results);
}
return results;
}
/**
* Attempts to fetch a specific single node at the given path.
*
* @throws FileNotFoundException if the path can't be resolved to a node
*
* @see #getNodeRefs(NodeRef, String)
*/
public NodeRef getNodeRef(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
{
// attempt to get the file/folder node using hierarchy walking
List<NodeRef> nodeRefs = getNodeRefs(pathRootNodeRef, path);
if (nodeRefs.size() == 0)
{
throw new FileNotFoundException(path);
}
else if (nodeRefs.size() > 1)
{
logger.warn("Multiple matching nodes: \n" +
" search root: " + pathRootNodeRef + "\n" +
" path: " + path);
}
// take the first one - not sure if it is possible for the path to refer to more than one
NodeRef nodeRef = nodeRefs.get(0);
// done
return nodeRef;
}
/**
* Relink the content data from a new node to an existing node to preserve the version history.
*
* @param oldNodeRef NodeRef
* @param newNodeRef NodeRef
*/
public void relinkNode(NodeRef tempNodeRef, NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName)
throws FileNotFoundException, FileExistsException
{
// Get the properties for the old and new nodes
org.alfresco.service.cmr.model.FileInfo tempFileInfo = fileFolderService.getFileInfo(tempNodeRef);
org.alfresco.service.cmr.model.FileInfo fileToMoveInfo = fileFolderService.getFileInfo(nodeToMoveRef);
// Save the current name of the old node
String tempName = tempFileInfo.getName();
try
{
// remove the tempory aspects from the nodes, this will be reapplied if the new name dictates it
nodeService.removeAspect(tempNodeRef, ContentModel.ASPECT_TEMPORARY);
// rename temp file to the new name
fileFolderService.rename(tempNodeRef, newName);
// rename new file to old name
fileFolderService.rename(nodeToMoveRef, tempName);
this.nodeService.addAspect(nodeToMoveRef, ContentModel.ASPECT_TEMPORARY, null);
}
catch (org.alfresco.service.cmr.model.FileNotFoundException e)
{
throw new FileNotFoundException(e.getMessage());
}
catch (org.alfresco.service.cmr.model.FileExistsException e)
{
throw new FileExistsException(e.getMessage());
}
if (!tempFileInfo.isFolder() && !fileToMoveInfo.isFolder())
{
// swap the content between the two
ContentData oldContentData = tempFileInfo.getContentData();
if (oldContentData == null)
{
String mimetype = mimetypeService.guessMimetype(tempName);
oldContentData = ContentData.setMimetype(null, mimetype);
}
ContentData newContentData = fileToMoveInfo.getContentData();
// Reset the mime type
String mimetype = mimetypeService.guessMimetype(newName);
newContentData = ContentData.setMimetype(newContentData, mimetype);
nodeService.setProperty(tempNodeRef, ContentModel.PROP_CONTENT, newContentData);
nodeService.setProperty(nodeToMoveRef, ContentModel.PROP_CONTENT, oldContentData);
}
}
/**
* Move a node
*
* @param nodeToMoveRef Node to be moved
* @param newParentNodeRef New parent folder node
* @param newName New name for the moved node
* @throws FileExistsException
*/
public void move(NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName) throws FileExistsException
{
try
{
fileFolderService.move(nodeToMoveRef, newParentNodeRef, newName);
}
catch (org.alfresco.service.cmr.model.FileExistsException e)
{
throw new FileExistsException(newName);
}
catch (Throwable e)
{
throw new AlfrescoRuntimeException("Move failed: \n" +
" node to move: " + nodeToMoveRef + "\n" +
" new parent: " + newParentNodeRef + "\n" +
" new name: " + newName,
e);
}
}
/**
* Rename a node
*
* @param nodeToRenameRef Node to be renamed
* @param newName New name for the node
* @throws FileExistsException
*/
public void rename(NodeRef nodeToRenameRef, String newName) throws FileExistsException
{
try
{
// Check if the new file name is a temporary file name
if ( newName.endsWith(".tmp") || newName.endsWith(".temp"))
nodeService.addAspect(nodeToRenameRef, ContentModel.ASPECT_TEMPORARY, null);
fileFolderService.rename(nodeToRenameRef, newName);
}
catch (org.alfresco.service.cmr.model.FileExistsException e)
{
throw new FileExistsException(newName);
}
catch (Throwable e)
{
throw new AlfrescoRuntimeException("Rename failed: \n" +
" node to rename: " + nodeToRenameRef + "\n" +
" new name: " + newName,
e);
}
}
/**
* Return the file name for a node
*
* @param node NodeRef
* @return String
* @throws FileNotFoundException
*/
public String getFileName(NodeRef node)
{
String fname = null;
try
{
fname = (String) nodeService.getProperty( node, ContentModel.PROP_NAME);
}
catch (InvalidNodeRefException ex)
{
}
return fname;
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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 org.alfresco.filesys.CIFSServerBean;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.jlan.server.filesys.FilesystemsConfigSection;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.util.BaseAlfrescoTestCase;
/**
* Checks that the required configuration details are obtainable from the CIFS components.
*
* @author Derek Hulley
*/
public class CifsIntegrationTest extends BaseAlfrescoTestCase
{
public void testGetServerName()
{
CIFSServerBean cifsServer = (CIFSServerBean) ctx.getBean("cifsServer");
assertNotNull("No CIFS server available", cifsServer);
// the server might, quite legitimately, not start
if (!cifsServer.isStarted())
{
return;
}
// get the server name
String serverName = cifsServer.getConfiguration().getServerName();
assertNotNull("No server name available", serverName);
assertTrue("No server name available (zero length)", serverName.length() > 0);
// Get the primary filesystem, might be null if the home folder mapper is configured
FilesystemsConfigSection filesysConfig = (FilesystemsConfigSection) cifsServer.getConfiguration().getConfigSection(FilesystemsConfigSection.SectionName);
DiskSharedDevice mainFilesys = (DiskSharedDevice) filesysConfig.getShares().enumerateShares().nextElement();
if ( mainFilesys != null)
{
// Check the share name
String shareName = mainFilesys.getName();
assertNotNull("No share name available", shareName);
assertTrue("No share name available (zero length)", shareName.length() > 0);
// Check that the context is valid
ContentContext filesysCtx = (ContentContext) mainFilesys.getContext();
assertNotNull("Content context is null", filesysCtx);
assertNotNull("Store id is null", filesysCtx.getStoreName());
assertNotNull("Root path is null", filesysCtx.getRootPath());
assertNotNull("Root node is null", filesysCtx.getRootNode());
// Check the root node
NodeService nodeService = (NodeService) ctx.getBean(ServiceRegistry.NODE_SERVICE.getLocalName());
// get the share root node and check that it exists
NodeRef shareNodeRef = filesysCtx.getRootNode();
assertNotNull("No share root node available", shareNodeRef);
assertTrue("Share root node doesn't exist", nodeService.exists(shareNodeRef));
}
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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 org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.IOControlHandler;
import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.FileSystem;
import org.alfresco.service.cmr.repository.*;
/**
* Content Filesystem Context Class
*
* <p>Contains per filesystem context.
*
* @author GKSpencer
*/
public class ContentContext extends AlfrescoContext
{
// Store and root path
private String m_storeName;
private String m_rootPath;
// Root node
private NodeRef m_rootNodeRef;
/**
* Class constructor
*
*@param filesysName String
* @param storeName String
* @param rootPath String
* @param rootNodeRef NodeRef
*/
public ContentContext(String filesysName, String storeName, String rootPath, NodeRef rootNodeRef)
{
super(filesysName, rootNodeRef.toString());
m_storeName = storeName;
m_rootPath = rootPath;
m_rootNodeRef = rootNodeRef;
// Create the I/O control handler
setIOHandler( createIOHandler( null));
}
/**
* Return the filesystem type, either FileSystem.TypeFAT or FileSystem.TypeNTFS.
*
* @return String
*/
public String getFilesystemType()
{
return FileSystem.TypeNTFS;
}
/**
* Return the store name
*
* @return String
*/
public final String getStoreName()
{
return m_storeName;
}
/**
* Return the root path
*
* @return String
*/
public final String getRootPath()
{
return m_rootPath;
}
/**
* Return the root node
*
* @return NodeRef
*/
public final NodeRef getRootNode()
{
return m_rootNodeRef;
}
/**
* Close the filesystem context
*/
public void CloseContext() {
// Call the base class
super.CloseContext();
}
/**
* Create the I/O control handler for this filesystem type
*
* @param filesysDriver DiskInterface
* @return IOControlHandler
*/
protected IOControlHandler createIOHandler( DiskInterface filesysDriver)
{
return new ContentIOControlHandler();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
/*
* 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 org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Extended {@link org.alfresco.filesys.server.filesys.DiskInterface disk interface} to
* allow access to some of the internal configuration properties.
*
* @author Derek Hulley
*/
public interface ContentDiskInterface extends DiskInterface
{
/**
* Get the name of the shared path within the server. The share name is
* equivalent in browse path to the {@link #getContextRootNodeRef() context root}.
*
* @return Returns the share name
*/
public String getShareName();
/**
* Get a reference to the node that all CIFS paths are relative to
*
* @return Returns a node acting as the CIFS root
*/
public NodeRef getContextRootNodeRef();
}

View File

@@ -0,0 +1,98 @@
/*
* 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 org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Content Disk Driver File Info Class
*
* <p>Adds fields for the file/folder NodeRef, and linked NodeRef for a link node.
*
* @author gkspencer
*/
public class ContentFileInfo extends FileInfo {
// Version id
private static final long serialVersionUID = 2518699645372408663L;
// File/folder node
private NodeRef m_nodeRef;
// Linked node
private NodeRef m_linkRef;
/**
* Return the file/folder node
*
* @return NodeRef
*/
public final NodeRef getNodeRef()
{
return m_nodeRef;
}
/**
* Check if this is a link node
*
* @return boolean
*/
public final boolean isLinkNode()
{
return m_linkRef != null ? true : false;
}
/**
* Return the link node, or null if this is not a link
*
* @return NodeRef
*/
public final NodeRef getLinkNodeRef()
{
return m_linkRef;
}
/**
* Set the node for this file/folder
*
* @param node NodeRef
*/
public final void setNodeRef(NodeRef node)
{
m_nodeRef = node;
}
/**
* Set the link node
*
* @param link NodeRef
*/
public final void setLinkNodeRef(NodeRef link)
{
m_linkRef = link;
}
}

View File

@@ -0,0 +1,794 @@
/*
* 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);
}
}
}

View File

@@ -0,0 +1,604 @@
/*
* 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.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.DiskFullException;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.smb.SeekType;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.encoding.ContentCharsetFinder;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.cmr.repository.ContentAccessor;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.usage.ContentQuotaException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Implementation of the <tt>NetworkFile</tt> for direct interaction
* with the channel repository.
* <p>
* This provides the interaction with the Alfresco Content Model file/folder structure.
*
* @author Derek Hulley
*/
public class ContentNetworkFile extends NodeRefNetworkFile
{
private static final Log logger = LogFactory.getLog(ContentNetworkFile.class);
private NodeService nodeService;
private ContentService contentService;
private MimetypeService mimetypeService;
// File channel to file content
private FileChannel channel;
// File content
private ContentAccessor content;
// Indicate if file has been written to or truncated/resized
private boolean modified;
// Flag to indicate if the file channel is writable
private boolean writableChannel;
/**
* Helper method to create a {@link NetworkFile network file} given a node reference.
*/
public static ContentNetworkFile createFile(
NodeService nodeService,
ContentService contentService,
MimetypeService mimetypeService,
CifsHelper cifsHelper,
NodeRef nodeRef,
FileOpenParams params)
{
String path = params.getPath();
// Check write access
// TODO: Check access writes and compare to write requirements
// Create the file
ContentNetworkFile netFile = new ContentNetworkFile(nodeService, contentService, mimetypeService, nodeRef, path);
// Set relevant parameters
if (params.isReadOnlyAccess())
{
netFile.setGrantedAccess(NetworkFile.READONLY);
}
else
{
netFile.setGrantedAccess(NetworkFile.READWRITE);
}
// Check the type
FileInfo fileInfo;
try
{
fileInfo = cifsHelper.getFileInformation(nodeRef, "");
}
catch (FileNotFoundException e)
{
throw new AlfrescoRuntimeException("File not found when creating network file: " + nodeRef, e);
}
if (fileInfo.isDirectory())
{
netFile.setAttributes(FileAttribute.Directory);
}
else
{
// Set the current size
netFile.setFileSize(fileInfo.getSize());
}
// Set the file timestamps
if ( fileInfo.hasCreationDateTime())
netFile.setCreationDate( fileInfo.getCreationDateTime());
if ( fileInfo.hasModifyDateTime())
netFile.setModifyDate(fileInfo.getModifyDateTime());
if ( fileInfo.hasAccessDateTime())
netFile.setAccessDate(fileInfo.getAccessDateTime());
// Set the file attributes
netFile.setAttributes(fileInfo.getFileAttributes());
// If the file is read-only then only allow read access
if ( netFile.isReadOnly())
netFile.setGrantedAccess(NetworkFile.READONLY);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Create file node=" + nodeRef + ", param=" + params + ", netfile=" + netFile);
// Return the network file
return netFile;
}
/**
* Class constructor
*
* @param transactionService TransactionService
* @param nodeService NodeService
* @param contentService ContentService
* @param nodeRef NodeRef
* @param name String
*/
private ContentNetworkFile(
NodeService nodeService,
ContentService contentService,
MimetypeService mimetypeService,
NodeRef nodeRef,
String name)
{
super(name, nodeRef);
setFullName(name);
this.nodeService = nodeService;
this.contentService = contentService;
this.mimetypeService = mimetypeService;
}
/**
* Return the file details as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append( "[");
str.append( getNodeRef().getId());
str.append( ",channel=");
str.append( channel);
if ( channel != null)
str.append( writableChannel ? "(Write)" : "(Read)");
if ( modified)
str.append( ",modified");
str.append( "]");
return str.toString();
}
/**
* @return Returns true if the channel should be writable
*
* @see NetworkFile#getGrantedAccess()
* @see NetworkFile#READONLY
* @see NetworkFile#WRITEONLY
* @see NetworkFile#READWRITE
*/
private boolean isWritable()
{
// Check that we are allowed to write
int access = getGrantedAccess();
return (access == NetworkFile.READWRITE || access == NetworkFile.WRITEONLY);
}
/**
* Determine if the file content data has been opened
*
* @return boolean
*/
public final boolean hasContent()
{
return content != null ? true : false;
}
/**
* Opens the channel for reading or writing depending on the access mode.
* <p>
* If the channel is already open, it is left.
*
* @param write true if the channel must be writable
* @param trunc true if the writable channel does not require the previous content data
* @throws AccessDeniedException if this network file is read only
* @throws AlfrescoRuntimeException if this network file represents a directory
*
* @see NetworkFile#getGrantedAccess()
* @see NetworkFile#READONLY
* @see NetworkFile#WRITEONLY
* @see NetworkFile#READWRITE
*/
private void openContent(boolean write, boolean trunc)
throws AccessDeniedException, AlfrescoRuntimeException
{
// Check if the file is a directory
if (isDirectory())
{
throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
}
// Check if write access is required and the current channel is read-only
else if ( write && writableChannel == false && channel != null)
{
// Close the existing read-only channel
try
{
channel.close();
channel = null;
}
catch (IOException ex)
{
logger.error("Error closing read-only channel", ex);
}
// Debug
if ( logger.isDebugEnabled())
logger.debug("Switching to writable channel for " + getName());
}
else if (channel != null)
{
// Already have channel open
return;
}
// We need to create the channel
if (write && !isWritable())
{
throw new AccessDeniedException("The network file was created for read-only: " + this);
}
content = null;
if (write)
{
// Get a writeable channel to the content
content = contentService.getWriter( getNodeRef(), ContentModel.PROP_CONTENT, false);
// Indicate that we have a writable channel to the file
writableChannel = true;
// Get the writable channel, do not copy existing content data if the file is to be truncated
channel = ((ContentWriter) content).getFileChannel( trunc);
}
else
{
// Get a read-only channel to the content
content = contentService.getReader( getNodeRef(), ContentModel.PROP_CONTENT);
// Ensure that the content we are going to read is valid
content = FileContentReader.getSafeContentReader(
(ContentReader) content,
I18NUtil.getMessage(FileContentReader.MSG_MISSING_CONTENT),
getNodeRef(), content);
// Indicate that we only have a read-only channel to the data
writableChannel = false;
// Get the read-only channel
channel = ((ContentReader) content).getFileChannel();
}
}
/**
* Close the file
*
* @exception IOException
*/
public void closeFile()
throws IOException
{
// Check if this is a directory
if (isDirectory())
{
// Nothing to do
return;
}
else if (channel == null)
{
// File was not read/written so channel was not opened
return;
}
// Check if the file has been modified
if (modified)
{
// Take a guess at the mimetype
channel.position(0);
InputStream is = new BufferedInputStream(Channels.newInputStream(channel));
ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder();
Charset charset = charsetFinder.getCharset(is, content.getMimetype());
content.setEncoding(charset.name());
// Close the channel
channel.close();
channel = null;
// Update node properties
ContentData contentData = content.getContentData();
try
{
nodeService.setProperty( getNodeRef(), ContentModel.PROP_CONTENT, contentData);
}
catch (ContentQuotaException qe)
{
throw new DiskFullException(qe.getMessage());
}
}
else
{
// Close it - it was not modified
channel.close();
channel = null;
}
}
/**
* Truncate or extend the file to the specified length
*
* @param size long
* @exception IOException
*/
public void truncateFile(long size)
throws IOException
{
// If the content data channel has not been opened yet and the requested size is zero
// then this is an open for overwrite so the existing content data is not copied
if ( hasContent() == false && size == 0L)
{
// Open content for overwrite, no need to copy existing content data
openContent(true, true);
}
else
{
// Normal open for write
openContent(true, false);
// Truncate or extend the channel
channel.truncate(size);
}
// Set modification flag
modified = true;
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Truncate file=" + this + ", size=" + size);
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @exception IOException
*/
public void writeFile(byte[] buffer, int length, int position, long fileOffset)
throws IOException
{
// Open the channel for writing
openContent(true, false);
// Write to the channel
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length);
int count = channel.write(byteBuffer, fileOffset);
// Set modification flag
modified = true;
// Update the current file size
setFileSize(channel.size());
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Write file=" + this + ", size=" + count);
}
/**
* Read from the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @return Length of data read.
* @exception IOException
*/
public int readFile(byte[] buffer, int length, int position, long fileOffset)
throws IOException
{
// Open the channel for reading
openContent(false, false);
// Read from the channel
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length);
int count = channel.read(byteBuffer, fileOffset);
if (count < 0)
{
count = 0; // doesn't obey the same rules, i.e. just returns the bytes read
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Read file=" + this + " read=" + count);
// Return the actual count of bytes read
return count;
}
/**
* Open the file
*
* @param createFlag boolean
* @exception IOException
*/
@Override
public void openFile(boolean createFlag)
throws IOException
{
// Wait for read/write before opening the content channel
}
/**
* Seek to a new position in the file
*
* @param pos long
* @param typ int
* @return long
*/
@Override
public long seekFile(long pos, int typ)
throws IOException
{
// Open the file, if not already open
openContent( false, false);
// Check if the current file position is the required file position
long curPos = channel.position();
switch (typ) {
// From start of file
case SeekType.StartOfFile :
if (curPos != pos)
channel.position( pos);
break;
// From current position
case SeekType.CurrentPos :
channel.position( curPos + pos);
break;
// From end of file
case SeekType.EndOfFile :
{
long newPos = channel.size() + pos;
channel.position(newPos);
}
break;
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Seek file=" + this + ", pos=" + pos + ", type=" + typ);
// Return the new file position
return channel.position();
}
/**
* Flush and buffered data for this file
*
* @exception IOException
*/
@Override
public void flushFile()
throws IOException
{
// Open the channel for writing
openContent(true, false);
// Flush the channel - metadata flushing is not important
channel.force(false);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Flush file=" + this);
}
}

View File

@@ -0,0 +1,438 @@
/*
* 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 java.util.List;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileType;
import org.alfresco.jlan.server.filesys.SearchContext;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Wrapper for simple XPath searche against the node service. The search is performed statically
* outside the context instance itself - this class merely maintains the state of the search
* results across client connections.
*
* @author Derek Hulley
*/
public class ContentSearchContext extends SearchContext
{
// Debug logging
private static final Log logger = LogFactory.getLog(ContentSearchContext.class);
// Constants
//
// Link file size, actual size will be set if/when the link is opened
public final static int LinkFileSize = 512;
// List of nodes returned from the folder search
private CifsHelper cifsHelper;
private List<NodeRef> results;
private int index = -1;
// Pseudo file list blended into a wildcard folder search
private PseudoFileList pseudoList;
private boolean donePseudoFiles = false;
// Resume id
private int resumeId;
// Relative path being searched
private String m_relPath;
/**
* Class constructor
*
* @param cifsHelper Filesystem helper class
* @param results List of file/folder nodes that match the search pattern
* @param searchStr Search path
* @param pseudoList List of pseudo files to be blended into the returned list of files
* @param relPath Relative path being searched
*/
protected ContentSearchContext(
CifsHelper cifsHelper,
List<NodeRef> results,
String searchStr,
PseudoFileList pseudoList,
String relPath)
{
super();
super.setSearchString(searchStr);
this.cifsHelper = cifsHelper;
this.results = results;
this.pseudoList = pseudoList;
m_relPath = relPath;
if ( m_relPath != null && m_relPath.endsWith( FileName.DOS_SEPERATOR_STR) == false)
m_relPath = m_relPath + FileName.DOS_SEPERATOR_STR;
}
/**
* Return the search as a string
*
* @return String
*/
public String toString()
{
StringBuilder sb = new StringBuilder(60);
sb.append("ContentSearchContext")
.append("[ searchStr=").append(getSearchString())
.append(", resultCount=").append(results.size())
.append("]");
return sb.toString();
}
/**
* Return the resume id for the current file/directory in the search.
*
* @return int
*/
public int getResumeId()
{
return resumeId;
}
/**
* Determine if there are more files for the active search.
*
* @return boolean
*/
public boolean hasMoreFiles()
{
// Pseudo files are returned first
if ( donePseudoFiles == false && pseudoList != null && index < (pseudoList.numberOfFiles() - 1))
return true;
return index < (results.size() - 1);
}
/**
* Return file information for the next file in the active search. Returns false if the search
* is complete.
*
* @param info FileInfo to return the file information.
* @return true if the file information is valid, else false
*/
public boolean nextFileInfo(FileInfo info)
{
// Check if there is anything else to return
if (!hasMoreFiles())
return false;
// Increment the index and resume id
index++;
resumeId++;
// If the pseudo file list is valid return the pseudo files first
if ( donePseudoFiles == false && pseudoList != null)
{
if ( index < pseudoList.numberOfFiles())
{
PseudoFile pfile = pseudoList.getFileAt( index);
if ( pfile != null)
{
// Get the file information for the pseudo file
FileInfo pinfo = pfile.getFileInfo();
// Copy the file information to the callers file info
info.copyFrom( pinfo);
// Generate a file id for the current file
if ( info != null && info.getFileId() == -1)
{
StringBuilder pathStr = new StringBuilder( m_relPath);
pathStr.append ( info.getFileName());
info.setFileId( pathStr.toString().hashCode());
}
// Check if we have finished with the pseudo file list, switch to the normal file list
if ( index == (pseudoList.numberOfFiles() - 1))
{
// Switch to the main file list
donePseudoFiles = true;
index = -1;
}
// Indicate that the file information is valid
return true;
}
}
}
// Get the next file info from the node search
NodeRef nextNodeRef = results.get(index);
try
{
// Get the file information and copy across to the callers file info
ContentFileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
info.copyFrom(nextInfo);
// Generate a file id for the current file
StringBuilder pathStr = new StringBuilder( m_relPath);
pathStr.append ( info.getFileName());
info.setFileId( pathStr.toString().hashCode());
// Check if this is a link node
if ( nextInfo.isLinkNode())
{
// Set a dummy file size for the link data that will be generated if/when the file is opened
info.setFileSize( LinkFileSize);
// Make the link read-only
if ( info.isReadOnly() == false)
info.setFileAttributes( info.getFileAttributes() + FileAttribute.ReadOnly);
// Set the file type to indicate a symbolic link
info.setFileType( FileType.SymbolicLink);
}
else
info.setFileType( FileType.RegularFile);
// Indicate that the file information is valid
return true;
}
catch (FileNotFoundException e)
{
}
// File information is not valid
return false;
}
/**
* Return the file name of the next file in the active search. Returns null is the search is
* complete.
*
* @return String
*/
public String nextFileName()
{
// Check if there is anything else to return
if (!hasMoreFiles())
return null;
// Increment the index and resume id
index++;
resumeId++;
// If the pseudo file list is valid return the pseudo files first
if ( donePseudoFiles == false && pseudoList != null)
{
if ( index < pseudoList.numberOfFiles())
{
PseudoFile pfile = pseudoList.getFileAt( index);
if ( pfile != null)
{
// Get the file information for the pseudo file
FileInfo pinfo = pfile.getFileInfo();
// Copy the file information to the callers file info
return pinfo.getFileName();
}
}
else
{
// Switch to the main file list
donePseudoFiles = true;
index = -1;
if ( results == null || results.size() == 0)
return null;
}
}
// Get the next file info from the node search
NodeRef nextNodeRef = results.get(index);
try
{
// Get the file information and copy across to the callers file info
FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
// Indicate that the file information is valid
return nextInfo.getFileName();
}
catch (FileNotFoundException e)
{
}
// No more files
return null;
}
/**
* Restart a search at the specified resume point.
*
* @param resumeId Resume point id.
* @return true if the search can be restarted, else false.
*/
public boolean restartAt(FileInfo info)
{
// Check if the resume point is in the pseudo file list
int resId = 0;
if (pseudoList != null)
{
while ( resId < pseudoList.numberOfFiles())
{
// Check if the current pseudo file matches the resume file name
PseudoFile pfile = pseudoList.getFileAt(resId);
if ( pfile.getFileName().equals(info.getFileName()))
{
// Found the restart point
donePseudoFiles = false;
index = resId - 1;
return true;
}
else
resId++;
}
}
// Check if the resume file is in the main file list
if ( results != null)
{
int idx = 0;
while ( idx < results.size())
{
// Get the file name for the node
String fname = cifsHelper.getFileName( results.get( idx));
if ( fname != null && fname.equals( info.getFileName()))
{
index = idx - 1;
resumeId = resId - 1;
donePseudoFiles = true;
return true;
}
else
{
idx++;
resId++;
}
}
}
// Failed to find resume file
return false;
}
/**
* Restart the current search at the specified file.
*
* @param info File to restart the search at.
* @return true if the search can be restarted, else false.
*/
public boolean restartAt(int resumeId)
{
// Check if the resume point is in the pseudo file list
if (pseudoList != null)
{
if ( resumeId < pseudoList.numberOfFiles())
{
// Resume at a pseudo file
index = resumeId;
donePseudoFiles = false;
return true;
}
else
{
// Adjust the resume id so that it is an index into the main file list
resumeId -= pseudoList.numberOfFiles();
}
}
// Check if the resume point is valid
if ( results != null && resumeId < results.size())
{
index = resumeId;
donePseudoFiles = true;
return true;
}
// Invalid resume point
return false;
}
}

View File

@@ -0,0 +1,383 @@
/*
* 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.util.Enumeration;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.auth.InvalidUserException;
import org.alfresco.jlan.server.config.InvalidConfigurationException;
import org.alfresco.jlan.server.config.ServerConfiguration;
import org.alfresco.jlan.server.core.InvalidDeviceInterfaceException;
import org.alfresco.jlan.server.core.ShareMapper;
import org.alfresco.jlan.server.core.ShareType;
import org.alfresco.jlan.server.core.SharedDevice;
import org.alfresco.jlan.server.core.SharedDeviceList;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.jlan.server.filesys.FilesystemsConfigSection;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Home Share Mapper Class
*
* <p>Maps disk share lookup requests to the list of shares defined in the server
* configuration and provides a dynamic home share mapped to the users home node.
*
* @author GKSpencer
*/
public class HomeShareMapper implements ShareMapper
{
// Logging
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
// Home folder share name
public static final String HOME_FOLDER_SHARE = "HOME";
// Server configuration and sections
private ServerConfiguration m_config;
private FilesystemsConfigSection m_filesysConfig;
// Filesystem driver to be used to create home shares
private ContentDiskDriver m_driver;
// Home folder share name
private String m_homeShareName = HOME_FOLDER_SHARE;
// Debug enable flag
private boolean m_debug;
/**
* Default constructor
*/
public HomeShareMapper()
{
}
/**
* Initialize the share mapper
*
* @param config ServerConfiguration
* @param params ConfigElement
* @exception InvalidConfigurationException
*/
public void initializeMapper(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException
{
// Save the server configuration
m_config = config;
m_filesysConfig = (FilesystemsConfigSection) m_config.getConfigSection(FilesystemsConfigSection.SectionName);
// Check if the home share name has been specified
String homeName = params.getAttribute("name");
if ( homeName != null && homeName.length() > 0)
m_homeShareName = homeName;
// Check if debug is enabled
if (params != null && params.getChild("debug") != null)
m_debug = true;
// Search for a filesystem that uses the content driver to use the driver when creating the home shares
SharedDeviceList shares = m_filesysConfig.getShares();
if ( shares != null)
{
Enumeration<SharedDevice> shrEnum = shares.enumerateShares();
while ( shrEnum.hasMoreElements() && m_driver == null)
{
try
{
SharedDevice curShare = shrEnum.nextElement();
if ( curShare.getInterface() instanceof ContentDiskDriver)
m_driver = (ContentDiskDriver) curShare.getInterface();
}
catch (InvalidDeviceInterfaceException ex)
{
}
}
}
}
/**
* Check if debug output is enabled
*
* @return boolean
*/
public final boolean hasDebug()
{
return m_debug;
}
/**
* Return the home folder share name
*
* @return String
*/
public final String getHomeFolderName()
{
return m_homeShareName;
}
/**
* Return the list of available shares.
*
* @param host String
* @param sess SrvSession
* @param allShares boolean
* @return SharedDeviceList
*/
public SharedDeviceList getShareList(String host, SrvSession sess, boolean allShares)
{
// Check if the user has a home folder, and the session does not currently have any
// dynamic shares defined
if ( sess != null && sess.hasClientInformation() && sess.hasDynamicShares() == false &&
sess.getClientInformation() instanceof AlfrescoClientInfo)
{
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) sess.getClientInformation();
if ( alfClient.hasHomeFolder())
{
// Create the home folder share
DiskSharedDevice homeShare = createHomeDiskShare( alfClient);
sess.addDynamicShare(homeShare);
// Debug
if ( logger.isDebugEnabled())
logger.debug("Added " + getHomeFolderName() + " share to list of shares for " + alfClient.getUserName());
}
}
// Make a copy of the global share list and add the per session dynamic shares
SharedDeviceList shrList = new SharedDeviceList(m_filesysConfig.getShares());
if ( sess != null && sess.hasDynamicShares()) {
// Add the per session dynamic shares
shrList.addShares(sess.getDynamicShareList());
}
// Remove unavailable shares from the list and return the list
if ( allShares == false)
shrList.removeUnavailableShares();
return shrList;
}
/**
* Find a share using the name and type for the specified client.
*
* @param host String
* @param name String
* @param typ int
* @param sess SrvSession
* @param create boolean
* @return SharedDevice
* @exception InvalidUserException
*/
public SharedDevice findShare(String tohost, String name, int typ, SrvSession sess, boolean create)
throws Exception
{
// Check for the special HOME disk share
SharedDevice share = null;
if (( typ == ShareType.DISK || typ == ShareType.UNKNOWN) && name.equalsIgnoreCase(getHomeFolderName()) &&
sess.getClientInformation() != null && m_driver != null) {
// Get the client details
ClientInfo client = sess.getClientInformation();
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Map share " + name + ", type=" + ShareType.TypeAsString(typ) + ", client=" + client);
// Check if the user has a home folder node
if ( client != null && client instanceof AlfrescoClientInfo) {
// Access the extended client information
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
if ( alfClient.hasHomeFolder()) {
// Check if the share has already been created for the session
if ( sess.hasDynamicShares()) {
// Check if the required share exists in the sessions dynamic share list
share = sess.getDynamicShareList().findShare(name, typ, false);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug(" Reusing existing dynamic share for " + name);
}
// Check if we found a share, if not then create a new dynamic share for the home directory
if ( share == null && create == true) {
// Create the home share mapped to the users home folder
DiskSharedDevice diskShare = createHomeDiskShare( alfClient);
// Add the new share to the sessions dynamic share list
sess.addDynamicShare(diskShare);
share = diskShare;
// DEBUG
if (logger.isDebugEnabled())
logger.debug(" Mapped share " + name + " to " + alfClient.getHomeFolder());
}
}
else
throw new InvalidUserException("No home directory");
}
}
else {
// Find the required share by name/type. Use a case sensitive search first, if that fails use a case
// insensitive search.
share = m_filesysConfig.getShares().findShare(name, typ, false);
if ( share == null) {
// Try a case insensitive search for the required share
share = m_filesysConfig.getShares().findShare(name, typ, true);
}
}
// Check if the share is available
if ( share != null && share.getContext() != null && share.getContext().isAvailable() == false)
share = null;
// Return the shared device, or null if no matching device was found
return share;
}
/**
* Delete temporary shares for the specified session
*
* @param sess SrvSession
*/
public void deleteShares(SrvSession sess)
{
// Check if the session has any dynamic shares
if ( sess.hasDynamicShares() == false)
return;
// Delete the dynamic shares
SharedDeviceList shares = sess.getDynamicShareList();
Enumeration<SharedDevice> enm = shares.enumerateShares();
while ( enm.hasMoreElements()) {
// Get the current share from the list
SharedDevice shr = enm.nextElement();
// Close the shared device
shr.getContext().CloseContext();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Deleted dynamic share " + shr);
}
// Clear the dynamic share list
shares.removeAllShares();
}
/**
* Close the share mapper, release any resources.
*/
public void closeMapper()
{
// TODO Auto-generated method stub
}
/**
* Create a disk share for the home folder
*
* @param alfClient AlfrescoClientInfo
* @return DiskSharedDevice
*/
private final DiskSharedDevice createHomeDiskShare(AlfrescoClientInfo alfClient)
{
// Make sure the client is an Alfresco client
if ( alfClient != null) {
// Create the disk driver and context
ContentContext diskCtx = new ContentContext( getHomeFolderName(), "", "", alfClient.getHomeFolder());
diskCtx.enableStateTable( true, m_driver.getStateReaper());
// Create a temporary shared device for the users home directory
return new DiskSharedDevice(getHomeFolderName(), m_driver, diskCtx, SharedDevice.Temporary);
}
// Invalid client type
return null;
}
}

View File

@@ -0,0 +1,256 @@
/*
* 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.IOException;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.smb.SeekType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Link Node In Memory Network File Class
*
* <p>In memory network file implementation that uses a memory buffer for the file data.
*
* @author gkspencer
*/
public class LinkMemoryNetworkFile extends NodeRefNetworkFile
{
// Current file position
private long m_filePos;
// File data
private byte[] m_data;
/**
* Class constructor.
*
* @param name String
* @param localPath String
* @param finfo FileInfo
* @param nodeRef NodeRef
*/
public LinkMemoryNetworkFile(String name, byte[] data, FileInfo finfo, NodeRef nodeRef)
{
super( name, nodeRef);
// Set the file data
m_data = data;
if ( m_data == null)
m_data = new byte[0];
// Set the file size
setFileSize( m_data.length);
// Set the creation and modification date/times
setModifyDate( finfo.getModifyDateTime());
setCreationDate( finfo.getCreationDateTime());
// Set the file id and relative path
if ( finfo.getPath() != null)
{
setFileId( finfo.getPath().hashCode());
setFullName( finfo.getPath());
}
}
/**
* Close the network file.
*/
public void closeFile() throws java.io.IOException
{
// Nothing to do
}
/**
* Return the current file position.
*
* @return long
*/
public long currentPosition()
{
return m_filePos;
}
/**
* Flush the file.
*
* @exception IOException
*/
public void flushFile() throws IOException
{
// Nothing to do
}
/**
* Determine if the end of file has been reached.
*
* @return boolean
*/
public boolean isEndOfFile() throws java.io.IOException
{
// Check if we reached end of file
if ( m_filePos == m_data.length)
return true;
return false;
}
/**
* Open the file.
*
* @param createFlag boolean
* @exception IOException
*/
public void openFile(boolean createFlag) throws java.io.IOException
{
// Indicate that the file is open
setClosed(false);
}
/**
* Read from the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param fileOff long
* @return Length of data read.
* @exception IOException
*/
public int readFile(byte[] buf, int len, int pos, long fileOff) throws java.io.IOException
{
// Check if the read is within the file data range
long fileLen = (long) m_data.length;
if ( fileOff >= fileLen)
return 0;
// Calculate the actual read length
if (( fileOff + len) > fileLen)
len = (int) ( fileLen - fileOff);
// Copy the data to the user buffer
System.arraycopy( m_data, (int) fileOff, buf, pos, len);
// Update the current file position
m_filePos = fileOff + len;
// Return the actual length of data read
return len;
}
/**
* Seek to the specified file position.
*
* @param pos long
* @param typ int
* @return long
* @exception IOException
*/
public long seekFile(long pos, int typ) throws IOException
{
// Seek to the required file position
switch (typ)
{
// From start of file
case SeekType.StartOfFile:
if (currentPosition() != pos)
m_filePos = pos;
break;
// From current position
case SeekType.CurrentPos:
m_filePos += pos;
break;
// From end of file
case SeekType.EndOfFile:
m_filePos += pos;
if ( m_filePos < 0)
m_filePos = 0L;
break;
}
// Return the new file position
return currentPosition();
}
/**
* Truncate the file
*
* @param siz long
* @exception IOException
*/
public void truncateFile(long siz) throws IOException
{
// Allow the truncate, do not alter the pseduo file data
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @exception IOException
*/
public void writeFile(byte[] buf, int len, int pos) throws java.io.IOException
{
// Allow the write, just do not do anything
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param offset long
* @exception IOException
*/
public void writeFile(byte[] buf, int len, int pos, long offset) throws java.io.IOException
{
// Allow the write, just do not do anything
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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 org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* NodeRef Based Network File Class
*
* @author gkspencer
*/
public abstract class NodeRefNetworkFile extends AlfrescoNetworkFile {
// Associated node ref
protected NodeRef m_nodeRef;
/**
* Create a network file object with the specified file/directory name.
*
* @param name File name string.
*/
public NodeRefNetworkFile(String name)
{
super( name);
}
/**
* Create a network file object with the specified file/directory name.
*
* @param name File name string.
* @param node NodeRef
*/
public NodeRefNetworkFile(String name, NodeRef node)
{
super( name);
m_nodeRef = node;
}
/**
* Return the node ref
*
* @return NodeRef
*/
public NodeRef getNodeRef()
{
return m_nodeRef;
}
/**
* set the node ref
*
* @param nodeRef NodeRef
*/
public void setNodeRef( NodeRef nodeRef)
{
m_nodeRef = nodeRef;
}
}

View File

@@ -0,0 +1,232 @@
/*
* 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.desk;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
import org.alfresco.filesys.alfresco.DesktopTarget;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.NotifyChange;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.transaction.TransactionService;
/**
* Check In/Out Desktop Action Class
*
* <p>Provides check in/out functionality via CIFS using the desktop action interface.
*
* @author gkspencer
*/
public class CheckInOutDesktopAction extends DesktopAction {
// Check in/out service
private CheckOutCheckInService m_checkInOutService;
/**
* Class constructor
*/
public CheckInOutDesktopAction()
{
super( DesktopAction.AttrAnyFiles, DesktopAction.PreConfirmAction + DesktopAction.PreCopyToTarget + DesktopAction.PreLocalToWorkingCopy);
}
/**
* Return the confirmation string to be displayed by the client
*
* @return String
*/
@Override
public String getConfirmationString() {
return "Run check in/out action";
}
/**
* Run the desktop action
*
* @param params DesktopParams
* @return DesktopResponse
*/
@Override
public DesktopResponse runAction(DesktopParams params) {
// Check if there are any files/folders to process
if ( params.numberOfTargetNodes() == 0)
return new DesktopResponse(StsSuccess);
// Get required services
NodeService nodeService = getServiceRegistry().getNodeService();
// Start a transaction
params.getDriver().beginWriteTransaction( params.getSession());
// Process the list of target nodes
DesktopResponse response = new DesktopResponse(StsSuccess);
for ( int idx = 0; idx < params.numberOfTargetNodes(); idx++)
{
// Get the current target node
DesktopTarget target = params.getTarget(idx);
// Check if the node is a working copy
if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_WORKING_COPY))
{
try
{
// Check in the file, pass an empty version properties so that veriosnable nodes create a new version
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>();
getCheckInOutService().checkin( target.getNode(), versionProperties, null, false);
// Check if there are any file/directory change notify requests active
if ( getContext().hasChangeHandler()) {
// Build the relative path to the checked in file
String fileName = null;
if ( target.getTarget().startsWith(FileName.DOS_SEPERATOR_STR))
{
// Path is already relative to filesystem root
fileName = target.getTarget();
}
else
{
// Build a root relative path for the file
fileName = FileName.buildPath( params.getFolder().getFullName(), null, target.getTarget(), FileName.DOS_SEPERATOR);
}
// Queue a file deleted change notification
getContext().getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, fileName);
}
}
catch (Exception ex)
{
// Dump the error
if ( logger.isErrorEnabled())
logger.error("Desktop action error", ex);
// Return an error status and message
response.setStatus(StsError, "Checkin failed for " + target.getTarget() + ", " + ex.getMessage());
}
}
else
{
try
{
// Check if the file is locked
if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_LOCKABLE)) {
// Get the lock type
String lockTypeStr = (String) nodeService.getProperty( target.getNode(), ContentModel.PROP_LOCK_TYPE);
if ( lockTypeStr != null) {
response.setStatus(StsError, "Checkout failed, file is locked");
return response;
}
}
// Check out the file
NodeRef workingCopyNode = getCheckInOutService().checkout( target.getNode());
// Get the working copy file name
String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME);
// Check out was successful, pack the working copy name
response.setStatus(StsSuccess, "Checked out working copy " + workingCopyName);
// Check if there are any file/directory change notify requests active
if ( getContext().hasChangeHandler()) {
// Build the relative path to the checked in file
String fileName = FileName.buildPath( params.getFolder().getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR);
// Queue a file added change notification
getContext().getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, fileName);
}
}
catch (Exception ex)
{
// Dump the error
if ( logger.isErrorEnabled())
logger.error("Desktop action error", ex);
// Return an error status and message
response.setStatus(StsError, "Failed to checkout " + target.getTarget() + ", " + ex.getMessage());
}
}
}
// Return a success status for now
return response;
}
/**
* Get the check in/out service
*
* @return CheckOutCheckInService
*/
protected final CheckOutCheckInService getCheckInOutService()
{
// Check if the service has been cached
if ( m_checkInOutService == null)
{
m_checkInOutService = getServiceRegistry().getCheckOutCheckInService();
}
// Return the check in/out service
return m_checkInOutService;
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.desk;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
/**
* Command Line Desktop Action Class
*
* <p>Simple desktop action that returns a test command line.
*
* @author gkspencer
*/
public class CmdLineDesktopAction extends DesktopAction {
/**
* Class constructor
*/
public CmdLineDesktopAction()
{
super( 0, PreConfirmAction);
}
@Override
public String getConfirmationString() {
return "Run commandline action";
}
@Override
public DesktopResponse runAction(DesktopParams params) {
// Return a URL in the status message
return new DesktopResponse(StsCommandLine, "%SystemRoot%\\notepad.exe");
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.desk;
import java.util.Date;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
/**
* Echo Desktop Action Class
*
* <p>Simple desktop action that echoes back the received string.
*
* @author gkspencer
*/
public class EchoDesktopAction extends DesktopAction {
/**
* Class constructor
*/
public EchoDesktopAction()
{
super( 0, PreConfirmAction);
}
@Override
public String getConfirmationString() {
return "Run echo action";
}
@Override
public DesktopResponse runAction(DesktopParams params) {
// Return a text message
return new DesktopResponse(StsSuccess, "Test message from echo action at " + new Date());
}
}

View File

@@ -0,0 +1,461 @@
/*
* 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.desk;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopActionException;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.service.cmr.repository.ScriptException;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.transaction.TransactionService;
/**
* Javascript Desktop Action Class
*
* <p>Run a server-side script against the target node(s).
*
* @author gkspencer
*/
public class JavaScriptDesktopAction extends DesktopAction {
// Script service
private ScriptService m_scriptService;
// Script name
private String m_scriptName;
// Script file details
private String m_scriptPath;
private long m_lastModified;
// Script string
private String m_script;
/**
* Class constructor
*/
public JavaScriptDesktopAction()
{
super( 0, 0);
}
/**
* Return the confirmation string to be displayed by the client
*
* @return String
*/
@Override
public String getConfirmationString()
{
return "Run Javascript action";
}
/**
* Initialize the action
*
* @param global ConfigElement
* @param config ConfigElement
* @param fileSys DiskSharedDevice
* @exception DesktopActionException
*/
@Override
public void initializeAction(ConfigElement global, ConfigElement config, DiskSharedDevice fileSys)
throws DesktopActionException
{
// Perform standard initialization
super.initializeAction(global, config, fileSys);
// Get the script file name and check that it exists
ConfigElement elem = config.getChild("script");
if ( elem != null && elem.getValue().length() > 0)
{
// Set the script name
setScriptName(elem.getValue());
// Check if the script exists on the classpath
URL scriptURL = this.getClass().getClassLoader().getResource(getScriptName());
if ( scriptURL == null)
throw new DesktopActionException("Failed to find script on classpath, " + getScriptName());
// Decode the URL path, it might contain escaped characters
String scriptURLPath = null;
try
{
scriptURLPath = URLDecoder.decode( scriptURL.getFile(), "UTF-8");
}
catch ( UnsupportedEncodingException ex)
{
throw new DesktopActionException("Failed to decode script path, " + ex.getMessage());
}
// Check that the script file exists
File scriptFile = new File(scriptURLPath);
if ( scriptFile.exists() == false)
throw new DesktopActionException("Script file not found, " + elem.getValue());
m_scriptPath = scriptFile.getAbsolutePath();
m_lastModified =scriptFile.lastModified();
// Load the script
try
{
loadScript( scriptFile);
}
catch ( IOException ex)
{
throw new DesktopActionException( "Failed to load script, " + ex.getMessage());
}
}
else
throw new DesktopActionException("Script name not specified");
// check if the desktop action attributes have been specified
elem = config.getChild("attributes");
if ( elem != null)
{
// Check if the attribute string is empty
if ( elem.getValue().length() == 0)
throw new DesktopActionException("Empty desktop action attributes");
// Parse the attribute string
int attr = 0;
StringTokenizer tokens = new StringTokenizer( elem.getValue(), ",");
while ( tokens.hasMoreTokens())
{
// Get the current attribute token and validate
String token = tokens.nextToken().trim();
if ( token.equalsIgnoreCase( "targetFiles"))
attr |= AttrTargetFiles;
else if ( token.equalsIgnoreCase( "targetFolders"))
attr |= AttrTargetFolders;
else if ( token.equalsIgnoreCase( "clientFiles"))
attr |= AttrClientFiles;
else if ( token.equalsIgnoreCase( "clientFolders"))
attr |= AttrClientFolders;
else if ( token.equalsIgnoreCase( "alfrescoFiles"))
attr |= AttrAlfrescoFiles;
else if ( token.equalsIgnoreCase( "alfrescoFolders"))
attr |= AttrAlfrescoFolders;
else if ( token.equalsIgnoreCase( "multiplePaths"))
attr |= AttrMultiplePaths;
else if ( token.equalsIgnoreCase( "allowNoParams"))
attr |= AttrAllowNoParams;
else if ( token.equalsIgnoreCase( "anyFiles"))
attr |= AttrAnyFiles;
else if ( token.equalsIgnoreCase( "anyFolders"))
attr |= AttrAnyFolders;
else if ( token.equalsIgnoreCase( "anyFilesFolders"))
attr |= AttrAnyFilesFolders;
else
throw new DesktopActionException("Unknown attribute, " + token);
}
// Set the action attributes
setAttributes( attr);
}
// Check if the desktop action pre-processing options have been specified
elem = config.getChild("preprocess");
if ( elem != null)
{
// Check if the pre-process string is empty
int pre = 0;
if ( elem.getValue() != null && elem.getValue().length() > 0)
{
// Parse the pre-process string
StringTokenizer tokens = new StringTokenizer( elem.getValue(), ",");
while ( tokens.hasMoreTokens())
{
// Get the current pre-process token and validate
String token = tokens.nextToken().trim();
if ( token.equalsIgnoreCase( "copyToTarget"))
pre |= PreCopyToTarget;
else if ( token.equalsIgnoreCase( "confirm"))
pre |= PreConfirmAction;
else if ( token.equalsIgnoreCase( "localToWorkingCopy"))
pre |= PreLocalToWorkingCopy;
else
throw new DesktopActionException("Unknown pre-processing flag, " + token);
}
}
// Set the action pre-processing flags
setPreProcessActions( pre);
}
}
/**
* Run the desktop action
*
* @param params DesktopParams
* @return DesktopResponse
*/
@Override
public DesktopResponse runAction(DesktopParams params)
throws DesktopActionException
{
// Check if the script file has been changed
DesktopResponse response = new DesktopResponse(StsSuccess);
File scriptFile = new File(m_scriptPath);
if ( scriptFile.lastModified() != m_lastModified)
{
// Reload the script
m_lastModified = scriptFile.lastModified();
try
{
loadScript( scriptFile);
}
catch ( IOException ex)
{
response.setStatus(StsError, "Failed to reload script file, " + getScriptName());
return response;
}
}
// Start a transaction
params.getDriver().beginWriteTransaction( params.getSession());
// Access the script service
if ( getScriptService() != null)
{
// Create the objects to be passed to the script
Map<String, Object> model = new HashMap<String, Object>();
model.put("deskParams", params);
model.put("out", System.out);
// Add the webapp URL, if valid
if ( hasWebappURL())
model.put("webURL", getWebappURL());
// Start a transaction
params.getDriver().beginWriteTransaction( params.getSession());
// Run the script
Object result = null;
try
{
// Run the script
result = getScriptService().executeScriptString( getScript(), model);
// Check the result
if ( result != null)
{
// Check for a full response object
if ( result instanceof DesktopResponse)
{
response = (DesktopResponse) result;
}
// Status code only response
else if ( result instanceof Double)
{
Double jsSts = (Double) result;
response.setStatus( jsSts.intValue(), "");
}
// Encoded response in the format '<stsCode>,<stsMessage>'
else if ( result instanceof String)
{
String responseMsg = (String) result;
// Parse the status message
StringTokenizer token = new StringTokenizer( responseMsg, ",");
String stsToken = token.nextToken();
String msgToken = token.nextToken();
int sts = -1;
try
{
sts = Integer.parseInt( stsToken);
}
catch ( NumberFormatException ex)
{
response.setStatus( StsError, "Bad response from script");
}
// Set the response
response.setStatus( sts, msgToken != null ? msgToken : "");
}
}
}
catch (ScriptException ex)
{
// Set the error response for the client
response.setStatus( StsError, ex.getMessage());
}
}
else
{
// Return an error response, script service not available
response.setStatus( StsError, "Script service not available");
}
// Return the response
return response;
}
/**
* Get the script service
*
* @return ScriptService
*/
protected final ScriptService getScriptService()
{
// Check if the script service has been initialized
if ( m_scriptService == null)
{
// Get the script service
m_scriptService = getServiceRegistry().getScriptService();
}
// Return the script service
return m_scriptService;
}
/**
* Get the script name
*
* @return String
*/
public final String getScriptName()
{
return m_scriptName;
}
/**
* Return the script data
*
* @return String
*/
public final String getScript()
{
return m_script;
}
/**
* Set the script name
*
* @param name String
*/
protected final void setScriptName(String name)
{
m_scriptName = name;
}
/**
* Load, or reload, the script
*
* @param scriptFile File
*/
private final void loadScript(File scriptFile)
throws IOException
{
// Open the script file
BufferedReader scriptIn = new BufferedReader(new FileReader( scriptFile));
StringBuilder scriptStr = new StringBuilder((int) scriptFile.length() + 256);
String inRec = scriptIn.readLine();
while ( inRec != null)
{
scriptStr.append( inRec);
scriptStr.append( "\n");
inRec = scriptIn.readLine();
}
// Close the script file
scriptIn.close();
// Update the script string
m_script = scriptStr.toString();
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.desk;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
/**
* URL Desktop Action Class
*
* <p>Simple desktop action that returns a test URL.
*
* @author gkspencer
*/
public class URLDesktopAction extends DesktopAction {
/**
* Class constructor
*/
public URLDesktopAction()
{
super( 0, PreConfirmAction);
}
@Override
public String getConfirmationString() {
return "Run URL action";
}
@Override
public DesktopResponse runAction(DesktopParams params) {
// Get the local IP address
String ipAddr = null;
try
{
ipAddr = InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException ex)
{
}
// Return a URL in the status message to browse to the folder node
StringBuilder urlStr = new StringBuilder();
urlStr.append( "http://");
urlStr.append(ipAddr);
urlStr.append(":8080/alfresco/navigate/browse/workspace/SpacesStore/");
urlStr.append( params.getFolderNode().getId());
urlStr.append("?ticket=");
urlStr.append(params.getTicket());
return new DesktopResponse(StsLaunchURL, urlStr.toString());
}
}