diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index 2700d5f0d3..8bffb5d24c 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -217,7 +217,7 @@ imconvert "${source}" ${options} "${target}" - imconvert ${source} ${options} ${target} + convert ${source} ${options} ${target} diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index fc2743c1ba..5989e204a8 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -76,6 +76,7 @@ alfresco.messages.template-service alfresco.messages.lock-service alfresco.messages.patch-service + alfresco.messages.webdav-messages diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index be8b13c6b0..0ed693e163 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -49,7 +49,7 @@ timeToLiveSeconds="0" overflowToDisk="false" /> - + - + - + - + - + - + - + - + - + - + Created d:datetime true + true Creator d:text true + true Modified d:datetime true + true Modifier d:text true + true Accessed diff --git a/source/java/org/alfresco/filesys/ftp/FTPDataSession.java b/source/java/org/alfresco/filesys/ftp/FTPDataSession.java index a0acf05f02..c502942ef5 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPDataSession.java +++ b/source/java/org/alfresco/filesys/ftp/FTPDataSession.java @@ -19,6 +19,19 @@ package org.alfresco.filesys.ftp; import java.net.*; import java.io.*; +import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.filesys.AccessMode; +import org.alfresco.filesys.server.filesys.DiskDeviceContext; +import org.alfresco.filesys.server.filesys.DiskInterface; +import org.alfresco.filesys.server.filesys.FileAction; +import org.alfresco.filesys.server.filesys.FileOpenParams; +import org.alfresco.filesys.server.filesys.FileStatus; +import org.alfresco.filesys.server.filesys.NetworkFile; +import org.alfresco.filesys.server.filesys.NotifyChange; +import org.alfresco.filesys.server.filesys.TreeConnection; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + /** * FTP Data Session Class *

@@ -30,9 +43,16 @@ import java.io.*; * * @author GKSpencer */ -public class FTPDataSession implements Runnable +public class FTPDataSession extends SrvSession implements Runnable { + // Debug logging + private static final Log logger = LogFactory.getLog("org.alfresco.ftp.protocol"); + + // Data session command types + + public enum DataCommand { StoreFile, ReturnFile }; + // FTP session that this data connection is associated with private FTPSrvSession m_cmdSess; @@ -54,10 +74,6 @@ public class FTPDataSession implements Runnable private ServerSocket m_passiveSock; - // Adapter to bind the passive socket to - - private InetAddress m_bindAddr; - // Transfer in progress and abort file transfer flags private boolean m_transfer; @@ -66,7 +82,27 @@ public class FTPDataSession implements Runnable // Send/receive data byte count private long m_bytCount; + + // Data command type + + private DataCommand m_dataCmd; + + // Requested file name + + private String m_reqFileName; + + // Path to the local file + + private FTPPath m_ftpPath; + // Restart position + + private long m_restartPos; + + // Thread that runs the data command + + private Thread m_dataThread; + /** * Class constructor *

@@ -77,7 +113,10 @@ public class FTPDataSession implements Runnable */ protected FTPDataSession(FTPSrvSession sess) throws IOException { - + // Setup the base class + + super( -1, sess.getServer(), "FTPDATA", null); + // Set the associated command session m_cmdSess = sess; @@ -100,6 +139,9 @@ public class FTPDataSession implements Runnable */ protected FTPDataSession(FTPSrvSession sess, int localPort, InetAddress bindAddr) throws IOException { + // Setup the base class + + super( -1, sess.getServer(), "FTPDATA", null); // Set the associated command session @@ -124,6 +166,9 @@ public class FTPDataSession implements Runnable */ protected FTPDataSession(FTPSrvSession sess, InetAddress bindAddr) throws IOException { + // Setup the base class + + super( -1, sess.getServer(), "FTPDATA", null); // Set the associated command session @@ -146,6 +191,9 @@ public class FTPDataSession implements Runnable */ protected FTPDataSession(FTPSrvSession sess, InetAddress addr, int port) { + // Setup the base class + + super( -1, sess.getServer(), "FTPDATA", null); // Set the associated command session @@ -171,6 +219,9 @@ public class FTPDataSession implements Runnable */ protected FTPDataSession(FTPSrvSession sess, int localPort, InetAddress addr, int port) { + // Setup the base class + + super( -1, sess.getServer(), "FTPDATA", null); // Set the associated command session @@ -271,6 +322,16 @@ public class FTPDataSession implements Runnable return m_transfer; } + /** + * Determine if the transfer has been aborted + * + * @return boolean + */ + public final boolean isTransferAborted() + { + return m_abort; + } + /** * Abort an in progress file transfer */ @@ -358,12 +419,520 @@ public class FTPDataSession implements Runnable } m_passiveSock = null; } + + // Commit, or rollback, any active user transaction + + try + { + // Commit or rollback the transaction + + endTransaction(); + } + catch ( Exception ex) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Error committing transaction", ex); + } } + /** + * Store a file using a seperate thread to receive the data and write the file + * + * @param ftpPath FTPPath + */ + public final void doStoreFile( FTPPath ftpPath, long restartPos, String reqFileName) + { + // Set the transfer details + + m_dataCmd = DataCommand.StoreFile; + m_ftpPath = ftpPath; + m_restartPos = restartPos; + m_reqFileName = reqFileName; + + // Run the transfer in a seperate thread + + m_dataThread = new Thread(this); + m_dataThread.setName(m_cmdSess.getUniqueId() + "_DATA_STORE"); + m_dataThread.start(); + } + + /** + * Return a file using a seperate thread to read the file and send the data + * + * @param ftpPath FTPPath + */ + public final void doReturnFile( FTPPath ftpPath, long restartPos, String reqFileName) + { + // Set the transfer details + + m_dataCmd = DataCommand.ReturnFile; + m_ftpPath = ftpPath; + m_restartPos = restartPos; + m_reqFileName = reqFileName; + + // Run the transfer in a seperate thread + + m_dataThread = new Thread(this); + m_dataThread.setName(m_cmdSess.getUniqueId() + "_DATA_RETURN"); + m_dataThread.start(); + } + /** * Run a file send/receive in a seperate thread */ public void run() { + // Setup the authentication context as we are running in a seperate thread from the main FTP session + + try + { + // Setup the authentication context for the thread + + m_cmdSess.authenticateDataSession(); + + // Run the required data command + + switch ( m_dataCmd) + { + // Store a file + + case StoreFile: + runStoreFile(); + break; + + // Return a file + + case ReturnFile: + runReturnFile(); + break; + } + } + catch ( org.alfresco.repo.security.authentication.AuthenticationException ex) + { + if ( logger.isErrorEnabled()) + logger.error("Failed to authenticate FTP data session", ex); + + // Close the data connection to the client + + m_cmdSess.getFTPServer().releaseDataSession(this); + closeSession(); + } } + + /** + * Return a file to the client + */ + private final void runReturnFile() + { + // Send the file to the client + + OutputStream os = null; + DiskInterface disk = null; + TreeConnection tree = null; + NetworkFile netFile = null; + Socket dataSock = null; + + try + { + + // Open an output stream to the client + + dataSock = getSocket(); + os = dataSock.getOutputStream(); + + // Create a temporary tree connection + + tree = m_cmdSess.getTreeConnection(m_ftpPath.getSharedDevice()); + + // Check if the file exists and it is a file, if so then open the + // file + + disk = (DiskInterface) m_ftpPath.getSharedDevice().getInterface(); + + // Create the file open parameters + + FileOpenParams params = new FileOpenParams(m_ftpPath.getSharePath(), FileAction.OpenIfExists, + AccessMode.ReadOnly, 0); + + // Check if the file exists and it is a file + + int sts = disk.fileExists( this, tree, m_ftpPath.getSharePath()); + + if (sts == FileStatus.FileExists) + { + + // Open the file + + netFile = disk.openFile( this, tree, params); + } + + // Check if the file has been opened + + if (netFile == null) + { + m_cmdSess.sendFTPResponse(550, "File " + m_reqFileName + " not available"); + return; + } + + // Allocate the buffer for the file data + + byte[] buf = new byte[FTPSrvSession.DEFAULT_BUFFERSIZE]; + long filePos = m_restartPos; + + int len = -1; + + while (filePos < netFile.getFileSize()) + { + + // Read another block of data from the file + + len = disk.readFile( this, tree, netFile, buf, 0, buf.length, filePos); + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_FILEIO)) + logger.debug(" Write len=" + len + " bytes"); + + // Write the current data block to the client, update the file position + + if (len > 0) + { + + // Write the data to the client + + os.write(buf, 0, len); + + // Update the file position + + filePos += len; + + // Update the transfer byte count + + m_bytCount += len; + } + + // Check if the transfer has been aborted + + if ( isTransferAborted()) + { + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_FILE)) + logger.debug(" Transfer aborted (RETR)"); + + // Send a status to the client + + sendFTPResponse( 226, "Aborted data connection"); + + // Finally block will cleanup + + return; + } + } + + // Close the output stream to the client + + os.close(); + os = null; + + // Indicate that the file has been transmitted + + sendFTPResponse(226, "Closing data connection"); + + // Close the data session + + m_cmdSess.getFTPServer().releaseDataSession(this); + + // Close the network file + + disk.closeFile( this, tree, netFile); + netFile = null; + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_FILEIO)) + logger.debug(" Transfer complete, file closed"); + } + catch (SocketException ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Data connection closed by client"); + } + catch (Exception ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Error during transmission"); + } + finally + { + try + { + // Close the network file + + if (netFile != null && disk != null && tree != null) + disk.closeFile(m_cmdSess, tree, netFile); + + // Close the output stream to the client + + if (os != null) + os.close(); + + // Close the data connection to the client + + m_cmdSess.getFTPServer().releaseDataSession( this); + closeSession(); + } + catch (Exception ex) + { + if ( logger.isErrorEnabled()) + logger.error( "Error during FTP data session close", ex); + } + } + } + + /** + * Store a file received from the client + */ + private final void runStoreFile() + { + // Store the file from the client + + InputStream is = null; + DiskInterface disk = null; + TreeConnection tree = null; + NetworkFile netFile = null; + Socket dataSock = null; + + try + { + + // Create a temporary tree connection + + tree = m_cmdSess.getTreeConnection(m_ftpPath.getSharedDevice()); + + // Check if the session has the required access to the filesystem + + if (tree == null || tree.hasWriteAccess() == false) + { + + // Session does not have write access to the filesystem + + sendFTPResponse(550, "Access denied"); + return; + } + + // Check if the file exists + + disk = (DiskInterface) m_ftpPath.getSharedDevice().getInterface(); + int sts = disk.fileExists(this, tree, m_ftpPath.getSharePath()); + + if (sts == FileStatus.DirectoryExists) + { + + // Return an error status + + sendFTPResponse(500, "Invalid path (existing directory)"); + return; + } + + // Create the file open parameters + + FileOpenParams params = new FileOpenParams(m_ftpPath.getSharePath(), + sts == FileStatus.FileExists ? FileAction.TruncateExisting : FileAction.CreateNotExist, + AccessMode.ReadWrite, 0); + + // Create a new file to receive the data + + if (sts == FileStatus.FileExists) + { + + // Overwrite the existing file + + netFile = disk.openFile(this, tree, params); + } + else + { + + // Create a new file + + netFile = disk.createFile(this, tree, params); + } + + // Notify change listeners that a new file has been created + + DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); + + if (diskCtx.hasChangeHandler()) + diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, m_ftpPath.getSharePath()); + + // Send the intermediate response + + sendFTPResponse(150, "File status okay, about to open data connection"); + + // Get the data connection socket + + try + { + dataSock = getSocket(); + } + catch (Exception ex) + { + } + + if (dataSock == null) + { + sendFTPResponse(426, "Connection closed; transfer aborted"); + return; + } + + // Open an input stream from the client + + is = dataSock.getInputStream(); + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_FILE)) + logger.debug("Storing ftp=" + + m_ftpPath.getFTPPath() + ", share=" + m_ftpPath.getShareName() + ", path=" + + m_ftpPath.getSharePath()); + + // Allocate the buffer for the file data + + byte[] buf = new byte[FTPSrvSession.DEFAULT_BUFFERSIZE]; + long filePos = 0; + int len = is.read(buf, 0, buf.length); + + while (len > 0) + { + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_FILEIO)) + logger.debug(" Receive len=" + len + " bytes"); + + // Write the current data block to the file, update the file + // position + + disk.writeFile(this, tree, netFile, buf, 0, len, filePos); + filePos += len; + + // Read another block of data from the client + + len = is.read(buf, 0, buf.length); + } + + // Close the input stream from the client + + is.close(); + is = null; + + // Close the network file + + disk.closeFile(this, tree, netFile); + netFile = null; + + // Indicate that the file has been received + + sendFTPResponse(226, "Closing data connection"); + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_FILEIO)) + logger.debug(" Transfer complete, file closed"); + } + catch (SocketException ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Indicate that there was an error during transmission of the file data + + sendFTPResponse(426, "Data connection closed by client"); + } + catch (Exception ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && m_cmdSess.hasDebug(FTPSrvSession.DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Error during transmission"); + } + finally + { + try + { + // Close the network file + + if (netFile != null && disk != null && tree != null) + disk.closeFile( this, tree, netFile); + + // Close the input stream to the client + + if (is != null) + is.close(); + + // Close the data connection to the client + + m_cmdSess.getFTPServer().releaseDataSession(this); + closeSession(); + } + catch (Exception ex) + { + if ( logger.isErrorEnabled()) + logger.error( "Error during FTP data session close", ex); + } + } + } + + /** + * Send an FTP response to the client via the command session + * + * @param stsCode int + * @param msg String + */ + protected final void sendFTPResponse(int stsCode, String msg) + { + try + { + m_cmdSess.sendFTPResponse( stsCode, msg); + } + catch (Exception ex) + { + } + } + + /** + * Return the client address + * + * @return InetAddress + */ + public InetAddress getRemoteAddress() { + return m_cmdSess.getRemoteAddress(); + } } diff --git a/source/java/org/alfresco/filesys/ftp/FTPDate.java b/source/java/org/alfresco/filesys/ftp/FTPDate.java index 6e68d691eb..7436de2880 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPDate.java +++ b/source/java/org/alfresco/filesys/ftp/FTPDate.java @@ -98,10 +98,10 @@ public class FTPDate buf.append(hr); buf.append(":"); - int sec = cal.get(Calendar.SECOND); - if (sec < 10) + int min = cal.get(Calendar.MINUTE); + if (min < 10) buf.append("0"); - buf.append(sec); + buf.append(min); } } } diff --git a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java index ee7993f36e..b47c2fb724 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java +++ b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java @@ -127,7 +127,7 @@ public class FTPSrvSession extends SrvSession implements Runnable // File transfer buffer size - private static final int DEFAULT_BUFFERSIZE = 64000; + public static final int DEFAULT_BUFFERSIZE = 64000; // Carriage return/line feed combination required for response messages @@ -137,6 +137,10 @@ public class FTPSrvSession extends SrvSession implements Runnable protected final static String LIST_OPTION_HIDDEN = "-a"; + // Flag to control whether data transfers use a seperate thread + + private static boolean UseThreadedDataTransfer = true; + // Session socket private Socket m_sock; @@ -235,14 +239,18 @@ public class FTPSrvSession extends SrvSession implements Runnable if (m_dataSess != null) { + // Abort any active transfer + + m_dataSess.abortTransfer(); + + // Remove the data session + getFTPServer().releaseDataSession(m_dataSess); m_dataSess = null; } // Close the socket first, if the client is still connected this should - // allow the - // input/output streams - // to be closed + // allow the input/output streams to be closed if (m_sock != null) { @@ -335,8 +343,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Set the default path for the session * - * @param rootPath - * FTPPath + * @param rootPath FTPPath */ public final void setRootPath(FTPPath rootPath) { @@ -353,10 +360,8 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Get the path details for the current request * - * @param req - * FTPRequest - * @param filePath - * boolean + * @param req FTPRequest + * @param filePath boolean * @return FTPPath */ protected final FTPPath generatePathForRequest(FTPRequest req, boolean filePath) @@ -367,12 +372,9 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Get the path details for the current request * - * @param req - * FTPRequest - * @param filePath - * boolean - * @param checkExists - * boolean + * @param req FTPRequest + * @param filePath boolean + * @param checkExists boolean * @return FTPPath */ protected final FTPPath generatePathForRequest(FTPRequest req, boolean filePath, boolean checkExists) @@ -583,8 +585,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Convert a path string from share path seperators to FTP path seperators * - * @param path - * String + * @param path String * @return String */ protected final String convertToFTPSeperators(String path) @@ -603,8 +604,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Find the required disk shared device * - * @param name - * String + * @param name String * @return DiskSharedDevice */ protected final DiskSharedDevice findShare(String name) @@ -630,8 +630,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Set the binary mode flag * - * @param bin - * boolean + * @param bin boolean */ protected final void setBinary(boolean bin) { @@ -641,10 +640,8 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Send an FTP command response * - * @param stsCode - * int - * @param msg - * String + * @param stsCode int + * @param msg String * @exception IOException */ protected final void sendFTPResponse(int stsCode, String msg) throws IOException @@ -680,8 +677,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Send an FTP command response * - * @param msg - * StringBuffer + * @param msg StringBuffer * @exception IOException */ protected final void sendFTPResponse(StringBuffer msg) throws IOException @@ -700,8 +696,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a user command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procUser(FTPRequest req) throws IOException @@ -750,8 +745,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a password command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procPassword(FTPRequest req) throws IOException @@ -827,6 +821,10 @@ public class FTPSrvSession extends SrvSession implements Runnable sendFTPResponse(230, "User logged in, proceed"); setLoggedOn(true); + // Save the client info + + setClientInformation( cInfo); + // DEBUG if (logger.isDebugEnabled() && hasDebug(DBG_STATE)) @@ -909,8 +907,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a port command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procPort(FTPRequest req) throws IOException @@ -990,8 +987,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a passive command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procPassive(FTPRequest req) throws IOException @@ -1049,8 +1045,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a print working directory command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procPrintWorkDir(FTPRequest req) throws IOException @@ -1078,8 +1073,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a change working directory command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procChangeWorkDir(FTPRequest req) throws IOException @@ -1128,8 +1122,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a change directory up command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procCdup(FTPRequest req) throws IOException @@ -1177,8 +1170,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a long directory listing command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procList(FTPRequest req) throws IOException @@ -1380,8 +1372,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a short directory listing command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procNList(FTPRequest req) throws IOException @@ -1532,8 +1523,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a system status command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procSystemStatus(FTPRequest req) throws IOException @@ -1547,8 +1537,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a server status command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procServerStatus(FTPRequest req) throws IOException @@ -1562,8 +1551,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a help command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procHelp(FTPRequest req) throws IOException @@ -1577,8 +1565,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a no-op command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procNoop(FTPRequest req) throws IOException @@ -1592,8 +1579,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a quit command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procQuit(FTPRequest req) throws IOException @@ -1616,8 +1602,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a type command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procType(FTPRequest req) throws IOException @@ -1660,8 +1645,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a restart command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procRestart(FTPRequest req) throws IOException @@ -1708,8 +1692,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a return file command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procReturnFile(FTPRequest req) throws IOException @@ -1760,198 +1743,215 @@ public class FTPSrvSession extends SrvSession implements Runnable return; } - // Get the data connection socket - - Socket dataSock = null; - - try + // Check if a seperate thread should be used for the data transfer + + if ( UseThreadedDataTransfer == true) { - dataSock = m_dataSess.getSocket(); + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILE)) + logger.debug("Returning (threaded) ftp=" + + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path=" + ftpPath.getSharePath()); + + // Start the transfer in a seperate thread + + m_dataSess.doReturnFile( ftpPath, m_restartPos, req.getArgument()); } - catch (Exception ex) + else { - } - - if (dataSock == null) - { - sendFTPResponse(426, "Connection closed; transfer aborted"); - return; - } - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_FILE)) - logger.debug("Returning ftp=" - + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path=" + ftpPath.getSharePath()); - - // Send the file to the client - - OutputStream os = null; - DiskInterface disk = null; - TreeConnection tree = null; - NetworkFile netFile = null; - - try - { - - // Open an output stream to the client - - os = dataSock.getOutputStream(); - - // Create a temporary tree connection - - tree = getTreeConnection(ftpPath.getSharedDevice()); - - // Check if the file exists and it is a file, if so then open the - // file - - disk = (DiskInterface) ftpPath.getSharedDevice().getInterface(); - - // Create the file open parameters - - FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(), FileAction.OpenIfExists, - AccessMode.ReadOnly, 0); - - // Check if the file exists and it is a file - - int sts = disk.fileExists(this, tree, ftpPath.getSharePath()); - - if (sts == FileStatus.FileExists) - { - - // Open the file - - netFile = disk.openFile(this, tree, params); - } - - // Check if the file has been opened - - if (netFile == null) - { - sendFTPResponse(550, "File " + req.getArgument() + " not available"); - return; - } - - // Allocate the buffer for the file data - - byte[] buf = new byte[DEFAULT_BUFFERSIZE]; - long filePos = m_restartPos; - - int len = -1; - - while (filePos < netFile.getFileSize()) - { - - // Read another block of data from the file - - len = disk.readFile(this, tree, netFile, buf, 0, buf.length, filePos); - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) - logger.debug(" Write len=" + len + " bytes"); - - // Write the current data block to the client, update the file - // position - - if (len > 0) - { - - // Write the data to the client - - os.write(buf, 0, len); - - // Update the file position - - filePos += len; - } - } - - // Close the output stream to the client - - os.close(); - os = null; - - // Indicate that the file has been transmitted - - sendFTPResponse(226, "Closing data connection"); - - // Close the data session - - getFTPServer().releaseDataSession(m_dataSess); - m_dataSess = null; - - // Close the network file - - disk.closeFile(this, tree, netFile); - netFile = null; - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) - logger.debug(" Transfer complete, file closed"); - } - catch (SocketException ex) - { - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) - logger.debug(" Error during transfer", ex); - - // Close the data socket to the client - - if (m_dataSess != null) - { - m_dataSess.closeSession(); - m_dataSess = null; - } - - // Indicate that there was an error during transmission of the file - // data - - sendFTPResponse(426, "Data connection closed by client"); - } - catch (Exception ex) - { - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) - logger.debug(" Error during transfer", ex); - - // Indicate that there was an error during transmission of the file - // data - - sendFTPResponse(426, "Error during transmission"); - } finally - { - - // Close the network file - - if (netFile != null && disk != null && tree != null) - disk.closeFile(this, tree, netFile); - - // Close the output stream to the client - - if (os != null) - os.close(); - - // Close the data connection to the client - - if (m_dataSess != null) - { - getFTPServer().releaseDataSession(m_dataSess); - m_dataSess = null; - } + // Get the data connection socket + + Socket dataSock = null; + + try + { + dataSock = m_dataSess.getSocket(); + } + catch (Exception ex) + { + } + + if (dataSock == null) + { + sendFTPResponse(426, "Connection closed; transfer aborted"); + return; + } + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILE)) + logger.debug("Returning ftp=" + + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path=" + ftpPath.getSharePath()); + + // Send the file to the client + + OutputStream os = null; + DiskInterface disk = null; + TreeConnection tree = null; + NetworkFile netFile = null; + + try + { + + // Open an output stream to the client + + os = dataSock.getOutputStream(); + + // Create a temporary tree connection + + tree = getTreeConnection(ftpPath.getSharedDevice()); + + // Check if the file exists and it is a file, if so then open the + // file + + disk = (DiskInterface) ftpPath.getSharedDevice().getInterface(); + + // Create the file open parameters + + FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(), FileAction.OpenIfExists, + AccessMode.ReadOnly, 0); + + // Check if the file exists and it is a file + + int sts = disk.fileExists(this, tree, ftpPath.getSharePath()); + + if (sts == FileStatus.FileExists) + { + + // Open the file + + netFile = disk.openFile(this, tree, params); + } + + // Check if the file has been opened + + if (netFile == null) + { + sendFTPResponse(550, "File " + req.getArgument() + " not available"); + return; + } + + // Allocate the buffer for the file data + + byte[] buf = new byte[DEFAULT_BUFFERSIZE]; + long filePos = m_restartPos; + + int len = -1; + + while (filePos < netFile.getFileSize()) + { + + // Read another block of data from the file + + len = disk.readFile(this, tree, netFile, buf, 0, buf.length, filePos); + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) + logger.debug(" Write len=" + len + " bytes"); + + // Write the current data block to the client, update the file + // position + + if (len > 0) + { + + // Write the data to the client + + os.write(buf, 0, len); + + // Update the file position + + filePos += len; + } + } + + // Close the output stream to the client + + os.close(); + os = null; + + // Indicate that the file has been transmitted + + sendFTPResponse(226, "Closing data connection"); + + // Close the data session + + getFTPServer().releaseDataSession(m_dataSess); + m_dataSess = null; + + // Close the network file + + disk.closeFile(this, tree, netFile); + netFile = null; + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) + logger.debug(" Transfer complete, file closed"); + } + catch (SocketException ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Close the data socket to the client + + if (m_dataSess != null) + { + m_dataSess.closeSession(); + m_dataSess = null; + } + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Data connection closed by client"); + } + catch (Exception ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Error during transmission"); + } + finally + { + + // Close the network file + + if (netFile != null && disk != null && tree != null) + disk.closeFile(this, tree, netFile); + + // Close the output stream to the client + + if (os != null) + os.close(); + + // Close the data connection to the client + + if (m_dataSess != null) + { + getFTPServer().releaseDataSession(m_dataSess); + m_dataSess = null; + } + } } } /** * Process a store file command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procStoreFile(FTPRequest req) throws IOException @@ -1982,252 +1982,269 @@ public class FTPSrvSession extends SrvSession implements Runnable return; } - // Send the file to the client - - InputStream is = null; - DiskInterface disk = null; - TreeConnection tree = null; - NetworkFile netFile = null; - - try + // Check if a seperate thread should be used for the data transfer + + if ( UseThreadedDataTransfer == true) { - - // Create a temporary tree connection - - tree = getTreeConnection(ftpPath.getSharedDevice()); - - // Check if the session has the required access to the filesystem - - if (tree == null || tree.hasWriteAccess() == false) - { - - // Session does not have write access to the filesystem - - sendFTPResponse(550, "Access denied"); - return; - } - - // Check if the file exists - - disk = (DiskInterface) ftpPath.getSharedDevice().getInterface(); - int sts = disk.fileExists(this, tree, ftpPath.getSharePath()); - - if (sts == FileStatus.DirectoryExists) - { - - // Return an error status - - sendFTPResponse(500, "Invalid path (existing directory)"); - return; - } - - // Create the file open parameters - - FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(), - sts == FileStatus.FileExists ? FileAction.TruncateExisting : FileAction.CreateNotExist, - AccessMode.ReadWrite, 0); - - // Create a new file to receive the data - - if (sts == FileStatus.FileExists) - { - - // Overwrite the existing file - - netFile = disk.openFile(this, tree, params); - } - else - { - - // Create a new file - - netFile = disk.createFile(this, tree, params); - } - - // Notify change listeners that a new file has been created - - DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); - - if (diskCtx.hasChangeHandler()) - diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, ftpPath.getSharePath()); - - // Send the intermediate response - - sendFTPResponse(150, "File status okay, about to open data connection"); - - // Check if there is an active data session - - if (m_dataSess == null) - { - sendFTPResponse(425, "Can't open data connection"); - return; - } - - // Get the data connection socket - - Socket dataSock = null; - - try - { - dataSock = m_dataSess.getSocket(); - } - catch (Exception ex) - { - } - - if (dataSock == null) - { - sendFTPResponse(426, "Connection closed; transfer aborted"); - return; - } - - // Open an input stream from the client - - is = dataSock.getInputStream(); - // DEBUG - + if (logger.isDebugEnabled() && hasDebug(DBG_FILE)) - logger.debug("Storing ftp=" + logger.debug("Storing (threaded) ftp=" + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path=" + ftpPath.getSharePath()); - // Allocate the buffer for the file data - - byte[] buf = new byte[DEFAULT_BUFFERSIZE]; - long filePos = 0; - int len = is.read(buf, 0, buf.length); - - while (len > 0) - { - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) - logger.debug(" Receive len=" + len + " bytes"); - - // Write the current data block to the file, update the file - // position - - disk.writeFile(this, tree, netFile, buf, 0, len, filePos); - filePos += len; - - // Read another block of data from the client - - len = is.read(buf, 0, buf.length); - } - - // Close the input stream from the client - - is.close(); - is = null; - - // Close the network file - - disk.closeFile(this, tree, netFile); - netFile = null; - - // Indicate that the file has been received - - sendFTPResponse(226, "Closing data connection"); - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) - logger.debug(" Transfer complete, file closed"); + // Start the transfer in a seperate thread + + m_dataSess.doStoreFile( ftpPath, m_restartPos, req.getArgument()); } - catch( AccessDeniedException ex) + else { - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) - logger.debug(" Access denied", ex); - - // Session does not have write access to the filesystem - - sendFTPResponse(550, "Access denied"); - } - catch (SocketException ex) - { - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) - logger.debug(" Error during transfer", ex); - - // Close the data socket to the client - - if (m_dataSess != null) - { - getFTPServer().releaseDataSession(m_dataSess); - m_dataSess = null; - } - - // Indicate that there was an error during transmission of the file - // data - - sendFTPResponse(426, "Data connection closed by client"); - } - catch (DiskFullException ex) - { - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) - logger.debug(" Error during transfer", ex); - - // Close the data socket to the client - - if (m_dataSess != null) - { - getFTPServer().releaseDataSession(m_dataSess); - m_dataSess = null; - } - - // Indicate that there was an error during writing of the file - - sendFTPResponse(451, "Disk full"); - } - catch (Exception ex) - { - - // DEBUG - - if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) - logger.debug(" Error during transfer", ex); - - // Indicate that there was an error during transmission of the file - // data - - sendFTPResponse(426, "Error during transmission"); - } - finally - { - - // Close the network file - - if (netFile != null && disk != null && tree != null) - disk.closeFile(this, tree, netFile); - - // Close the input stream to the client - - if (is != null) - is.close(); - - // Close the data connection to the client - - if (m_dataSess != null) - { - getFTPServer().releaseDataSession(m_dataSess); - m_dataSess = null; - } + // Send the file to the client + + InputStream is = null; + DiskInterface disk = null; + TreeConnection tree = null; + NetworkFile netFile = null; + + try + { + + // Create a temporary tree connection + + tree = getTreeConnection(ftpPath.getSharedDevice()); + + // Check if the session has the required access to the filesystem + + if (tree == null || tree.hasWriteAccess() == false) + { + + // Session does not have write access to the filesystem + + sendFTPResponse(550, "Access denied"); + return; + } + + // Check if the file exists + + disk = (DiskInterface) ftpPath.getSharedDevice().getInterface(); + int sts = disk.fileExists(this, tree, ftpPath.getSharePath()); + + if (sts == FileStatus.DirectoryExists) + { + + // Return an error status + + sendFTPResponse(500, "Invalid path (existing directory)"); + return; + } + + // Create the file open parameters + + FileOpenParams params = new FileOpenParams(ftpPath.getSharePath(), + sts == FileStatus.FileExists ? FileAction.TruncateExisting : FileAction.CreateNotExist, + AccessMode.ReadWrite, 0); + + // Create a new file to receive the data + + if (sts == FileStatus.FileExists) + { + + // Overwrite the existing file + + netFile = disk.openFile(this, tree, params); + } + else + { + + // Create a new file + + netFile = disk.createFile(this, tree, params); + } + + // Notify change listeners that a new file has been created + + DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext(); + + if (diskCtx.hasChangeHandler()) + diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, ftpPath.getSharePath()); + + // Send the intermediate response + + sendFTPResponse(150, "File status okay, about to open data connection"); + + // Check if there is an active data session + + if (m_dataSess == null) + { + sendFTPResponse(425, "Can't open data connection"); + return; + } + + // Get the data connection socket + + Socket dataSock = null; + + try + { + dataSock = m_dataSess.getSocket(); + } + catch (Exception ex) + { + } + + if (dataSock == null) + { + sendFTPResponse(426, "Connection closed; transfer aborted"); + return; + } + + // Open an input stream from the client + + is = dataSock.getInputStream(); + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILE)) + logger.debug("Storing ftp=" + + ftpPath.getFTPPath() + ", share=" + ftpPath.getShareName() + ", path=" + + ftpPath.getSharePath()); + + // Allocate the buffer for the file data + + byte[] buf = new byte[DEFAULT_BUFFERSIZE]; + long filePos = 0; + int len = is.read(buf, 0, buf.length); + + while (len > 0) + { + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) + logger.debug(" Receive len=" + len + " bytes"); + + // Write the current data block to the file, update the file + // position + + disk.writeFile(this, tree, netFile, buf, 0, len, filePos); + filePos += len; + + // Read another block of data from the client + + len = is.read(buf, 0, buf.length); + } + + // Close the input stream from the client + + is.close(); + is = null; + + // Close the network file + + disk.closeFile(this, tree, netFile); + netFile = null; + + // Indicate that the file has been received + + sendFTPResponse(226, "Closing data connection"); + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) + logger.debug(" Transfer complete, file closed"); + } + catch( AccessDeniedException ex) + { + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) + logger.debug(" Access denied", ex); + + // Session does not have write access to the filesystem + + sendFTPResponse(550, "Access denied"); + } + catch (SocketException ex) + { + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Close the data socket to the client + + if (m_dataSess != null) + { + getFTPServer().releaseDataSession(m_dataSess); + m_dataSess = null; + } + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Data connection closed by client"); + } + catch (DiskFullException ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Close the data socket to the client + + if (m_dataSess != null) + { + getFTPServer().releaseDataSession(m_dataSess); + m_dataSess = null; + } + + // Indicate that there was an error during writing of the file + + sendFTPResponse(451, "Disk full"); + } + catch (Exception ex) + { + + // DEBUG + + if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) + logger.debug(" Error during transfer", ex); + + // Indicate that there was an error during transmission of the file + // data + + sendFTPResponse(426, "Error during transmission"); + } + finally + { + + // Close the network file + + if (netFile != null && disk != null && tree != null) + disk.closeFile(this, tree, netFile); + + // Close the input stream to the client + + if (is != null) + is.close(); + + // Close the data connection to the client + + if (m_dataSess != null) + { + getFTPServer().releaseDataSession(m_dataSess); + m_dataSess = null; + } + } } } /** * Process a delete file command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procDeleteFile(FTPRequest req) throws IOException @@ -2342,8 +2359,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a rename from command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procRenameFrom(FTPRequest req) throws IOException @@ -2445,8 +2461,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a rename to command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procRenameTo(FTPRequest req) throws IOException @@ -2573,8 +2588,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a create directory command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procCreateDirectory(FTPRequest req) throws IOException @@ -2682,8 +2696,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a delete directory command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procRemoveDirectory(FTPRequest req) throws IOException @@ -2800,8 +2813,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a modify date/time command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procModifyDateTime(FTPRequest req) throws IOException @@ -2815,8 +2827,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a file size command * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procFileSize(FTPRequest req) throws IOException @@ -2892,8 +2903,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a structure command. This command is obsolete. * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procStructure(FTPRequest req) throws IOException @@ -2912,8 +2922,7 @@ public class FTPSrvSession extends SrvSession implements Runnable /** * Process a mode command. This command is obsolete. * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procMode(FTPRequest req) throws IOException @@ -2929,11 +2938,45 @@ public class FTPSrvSession extends SrvSession implements Runnable sendFTPResponse(504, "Obsolete"); } + /** + * Abort an active file transfer + * + * @param req FTPRequest + * @exception IOException + */ + protected final void procAbort(FTPRequest req) throws IOException + { + // Check if threaded transfers are enabled + + if ( UseThreadedDataTransfer == true) + { + // Check if there is an active data connection + + if ( m_dataSess != null) + { + // Abort the data transfer + + m_dataSess.abortTransfer(); + } + else + { + // Send an error status, no transfer in progress + + sendFTPResponse( 226, "Data connection not active"); + } + } + else + { + // Abort not implemented for inline transfers + + sendFTPResponse( 502, "Abort not implemented"); + } + } + /** * Process an allocate command. This command is obsolete. * - * @param req - * FTPRequest + * @param req FTPRequest * @exception IOException */ protected final void procAllocate(FTPRequest req) throws IOException @@ -2948,12 +2991,9 @@ public class FTPSrvSession extends SrvSession implements Runnable * Build a list of file name or file information objects for the specified * server path * - * @param path - * FTPPath - * @param nameOnly - * boolean - * @param hidden - * boolean + * @param path FTPPath + * @param nameOnly boolean + * @param hidden boolean * @return Vector */ protected final Vector listFilesForPath(FTPPath path, boolean nameOnly, boolean hidden) @@ -3144,8 +3184,7 @@ public class FTPSrvSession extends SrvSession implements Runnable * Get a tree connection for the specified shared device. Creates and caches * a new tree connection if required. * - * @param share - * SharedDevice + * @param share SharedDevice * @return TreeConnection */ protected final TreeConnection getTreeConnection(SharedDevice share) @@ -3338,7 +3377,6 @@ public class FTPSrvSession extends SrvSession implements Runnable switch (ftpReq.isCommand()) { - // User command case FTPCommand.User: @@ -3516,6 +3554,12 @@ public class FTPSrvSession extends SrvSession implements Runnable procAllocate(ftpReq); break; + // Abort an active file data transfer + + case FTPCommand.Abor: + procAbort(ftpReq); + break; + // Unknown/unimplemented command default: @@ -3600,4 +3644,33 @@ public class FTPSrvSession extends SrvSession implements Runnable if (hasDebug(DBG_STATE)) logger.debug("Server session closed"); } + + /** + * Authenticate an associated FTP data session using the same credentials as the main FTP session + * + * @exception AuthenticationException + */ + protected void authenticateDataSession() throws org.alfresco.repo.security.authentication.AuthenticationException + { + // Use the normal authentication service as we have the plaintext password + + AuthenticationService authService = getServer().getConfiguration().getAuthenticationService(); + + // Authenticate the user + + ClientInfo cInfo = getClientInformation(); + + if ( cInfo.isGuest()) + { + // Authenticate as the guest user + + authService.authenticateAsGuest(); + } + else + { + // Authenticate as a normal user + + authService.authenticate( cInfo.getUserName(), cInfo.getPasswordAsCharArray()); + } + } } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index b496d631dc..d11781753a 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -460,8 +460,6 @@ public class ServerConfiguration implements ApplicationListener throw new AlfrescoRuntimeException("Property 'configService' not set"); } - initialised = false; - // Create the configuration context ConfigLookupContext configCtx = new ConfigLookupContext(ConfigArea); @@ -470,59 +468,106 @@ public class ServerConfiguration implements ApplicationListener determinePlatformType(); + // Initialize the filesystems + + boolean filesysInitOK = false; + Config config = null; + try { - - // Process the CIFS server configuration - - Config config = configService.getConfig(ConfigCIFS, configCtx); - processCIFSServerConfig(config); - - // Process the FTP server configuration - - config = configService.getConfig(ConfigFTP, configCtx); - processFTPServerConfig(config); - - // Process the security configuration - - config = configService.getConfig(ConfigSecurity, configCtx); - processSecurityConfig(config); - // Process the filesystems configuration config = configService.getConfig(ConfigFilesystems, configCtx); processFilesystemsConfig(config); - - // Successful initialisation - initialised = true; + + // Indicate that the filesystems were initialized + + filesysInitOK = true; } - catch (UnsatisfiedLinkError ex) - { - // Error accessing the Win32NetBIOS DLL code - - logger.error("Error accessing Win32 NetBIOS, check DLL is on the path"); - - // Disable the CIFS server - - setNetBIOSSMB(false); - setTcpipSMB(false); - setWin32NetBIOS(false); - - setSMBServerEnabled(false); - } - catch (Throwable ex) + catch (Exception ex) { // Configuration error logger.error("File server configuration error, " + ex.getMessage(), ex); + } - // Disable the CIFS server + // Initialize the CIFS and FTP servers, if the filesystem(s) initialized successfully + + if ( filesysInitOK == true) + { + // Initialize the CIFS server - setNetBIOSSMB(false); - setTcpipSMB(false); - setWin32NetBIOS(false); + try + { + + // Process the CIFS server configuration + + config = configService.getConfig(ConfigCIFS, configCtx); + processCIFSServerConfig(config); + + // Process the security configuration + + config = configService.getConfig(ConfigSecurity, configCtx); + processSecurityConfig(config); + + // Log the successful startup + + logger.info("CIFS server started"); + } + catch (UnsatisfiedLinkError ex) + { + // Error accessing the Win32NetBIOS DLL code + + logger.error("Error accessing Win32 NetBIOS, check DLL is on the path"); + + // Disable the CIFS server + + setNetBIOSSMB(false); + setTcpipSMB(false); + setWin32NetBIOS(false); + + setSMBServerEnabled(false); + } + catch (Throwable ex) + { + // Configuration error + + logger.error("CIFS server configuration error, " + ex.getMessage(), ex); + + // Disable the CIFS server + + setNetBIOSSMB(false); + setTcpipSMB(false); + setWin32NetBIOS(false); + + setSMBServerEnabled(false); + } - setSMBServerEnabled(false); + // Initialize the FTP server + + try + { + // Process the FTP server configuration + + config = configService.getConfig(ConfigFTP, configCtx); + processFTPServerConfig(config); + + // Log the successful startup + + logger.info("FTP server started"); + } + catch (Exception ex) + { + // Configuration error + + logger.error("FTP server configuration error, " + ex.getMessage(), ex); + } + } + else + { + // Log the error + + logger.error("CIFS and FTP servers not started due to filesystem initialization error"); } } diff --git a/source/java/org/alfresco/filesys/smb/mailslot/Win32NetBIOSHostAnnouncer.java b/source/java/org/alfresco/filesys/smb/mailslot/Win32NetBIOSHostAnnouncer.java index 8a681ca48f..55ad8dedaa 100644 --- a/source/java/org/alfresco/filesys/smb/mailslot/Win32NetBIOSHostAnnouncer.java +++ b/source/java/org/alfresco/filesys/smb/mailslot/Win32NetBIOSHostAnnouncer.java @@ -120,6 +120,6 @@ public class Win32NetBIOSHostAnnouncer extends HostAnnouncer int sts = Win32NetBIOS.SendDatagram(getLana(), getNameNumber(), destName, buf, 0, len); if ( sts != NetBIOS.NRC_GoodRet) - logger.debug("Win32NetBIOS host announce error " + NetBIOS.getErrorString( -sts)); + logger.debug("Win32NetBIOS host announce error " + NetBIOS.getErrorString( -sts) + " (LANA " + getLana() + ")"); } } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/smb/server/win32/Win32NetBIOSLanaMonitor.java b/source/java/org/alfresco/filesys/smb/server/win32/Win32NetBIOSLanaMonitor.java index e7cec88150..bd9f9d90d4 100644 --- a/source/java/org/alfresco/filesys/smb/server/win32/Win32NetBIOSLanaMonitor.java +++ b/source/java/org/alfresco/filesys/smb/server/win32/Win32NetBIOSLanaMonitor.java @@ -39,7 +39,7 @@ public class Win32NetBIOSLanaMonitor extends Thread // // Initial LANA listener array size - private static final int LanaListenerArraySize = 16; + private static final int LanaListenerArraySize = 256; // Debug logging @@ -153,24 +153,7 @@ public class Win32NetBIOSLanaMonitor extends Thread // Check if the listener array has been allocated if ( m_listeners == null) - { - int len = LanaListenerArraySize; - if ( lana > len) - len = (lana + 3) & 0x00FC; - - m_listeners = new LanaListener[len]; - } - else if ( lana >= m_listeners.length) - { - // Extend the LANA listener array - - LanaListener[] newArray = new LanaListener[(lana + 3) & 0x00FC]; - - // Copy the existing array to the extended array - - System.arraycopy(m_listeners, 0, newArray, 0, m_listeners.length); - m_listeners = newArray; - } + m_listeners = new LanaListener[LanaListenerArraySize]; // Add the LANA listener @@ -343,6 +326,10 @@ public class Win32NetBIOSLanaMonitor extends Thread m_lanas.set(lana); m_lanaSts.set(lana, true); + + // Add a listener for the new LANA + + addLanaListener( sessHandler.getLANANumber(), sessHandler); } } else diff --git a/source/java/org/alfresco/license/AlfrescoKeyStore.java b/source/java/org/alfresco/license/AlfrescoKeyStore.java new file mode 100644 index 0000000000..7ae382a056 --- /dev/null +++ b/source/java/org/alfresco/license/AlfrescoKeyStore.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.license; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import de.schlichtherle.license.KeyStoreParam; + + +/** + * Alfresco Public KeyStore Parameters + * + * @author davidc + */ +public class AlfrescoKeyStore implements KeyStoreParam +{ + // location of alfresco public keystore + private final static String KEYSTORE = "/org/alfresco/license/alfresco.keystore"; + + + /* (non-Javadoc) + * @see de.schlichtherle.license.KeyStoreParam#getStream() + */ + public InputStream getStream() throws IOException + { + final InputStream in = getClass().getResourceAsStream(KEYSTORE); + if (in == null) + { + throw new FileNotFoundException(KEYSTORE); + } + return in; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.KeyStoreParam#getStorePwd() + */ + public String getStorePwd() + { + return "ocs3rf1a"; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.KeyStoreParam#getAlias() + */ + public String getAlias() + { + return "alfresco"; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.KeyStoreParam#getKeyPwd() + */ + public String getKeyPwd() + { + // Note: not required for public key + return null; + } + +} diff --git a/source/java/org/alfresco/license/AlfrescoLicenseParam.java b/source/java/org/alfresco/license/AlfrescoLicenseParam.java new file mode 100644 index 0000000000..f1910c534e --- /dev/null +++ b/source/java/org/alfresco/license/AlfrescoLicenseParam.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.license; + +import java.util.prefs.Preferences; + +import javax.security.auth.x500.X500Principal; + +import org.alfresco.service.descriptor.DescriptorService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import de.schlichtherle.license.CipherParam; +import de.schlichtherle.license.KeyStoreParam; +import de.schlichtherle.license.LicenseContent; +import de.schlichtherle.license.ftp.LicenseParam; + + +/** + * Alfresco License Parameters + * + * @author davidc + */ +public class AlfrescoLicenseParam implements LicenseParam +{ + private static final Log logger = LogFactory.getLog(DescriptorService.class); + + private KeyStoreParam alfrescoStore = new AlfrescoKeyStore(); + private KeyStoreParam trialStore = new TrialKeyStore(); + private CipherParam cipherParam = new CipherParamImpl(); + private boolean createTrialLicense = true; + private int days = 30; + + + /** + * Construct + * + * @param createTrialLicense allow the creation of trial license + */ + public AlfrescoLicenseParam(boolean createTrialLicense) + { + this.createTrialLicense = createTrialLicense; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.ftp.LicenseParam#getFTPKeyStoreParam() + */ + public KeyStoreParam getFTPKeyStoreParam() + { + return trialStore; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.ftp.LicenseParam#getFTPDays() + */ + public int getFTPDays() + { + return days; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.ftp.LicenseParam#isFTPEligible() + */ + public boolean isFTPEligible() + { + return createTrialLicense; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.ftp.LicenseParam#createFTPLicenseContent() + */ + public LicenseContent createFTPLicenseContent() + { + if (logger.isInfoEnabled()) + logger.info("Alfresco license: Creating time limited trial license"); + + LicenseContent result = new LicenseContent(); + X500Principal holder = new X500Principal("O=Trial User"); + result.setHolder(holder); + X500Principal issuer = new X500Principal("CN=Unknown, OU=Unknown, O=Alfresco, L=Maidenhead, ST=Berkshire, C=UK"); + result.setIssuer(issuer); + result.setConsumerType("System"); + result.setConsumerAmount(1); + return result; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.ftp.LicenseParam#removeFTPEligibility() + */ + public void removeFTPEligibility() + { + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.ftp.LicenseParam#ftpGranted(de.schlichtherle.license.LicenseContent) + */ + public void ftpGranted(LicenseContent content) + { + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.LicenseParam#getSubject() + */ + public String getSubject() + { + return "Enterprise Network"; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.LicenseParam#getPreferences() + */ + public Preferences getPreferences() + { + // note: Alfresco license manager does not store licenses in Preferences + return null; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.LicenseParam#getKeyStoreParam() + */ + public KeyStoreParam getKeyStoreParam() + { + return alfrescoStore; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.LicenseParam#getCipherParam() + */ + public CipherParam getCipherParam() + { + return cipherParam; + } + +} diff --git a/source/java/org/alfresco/license/CipherParamImpl.java b/source/java/org/alfresco/license/CipherParamImpl.java new file mode 100644 index 0000000000..179eb618df --- /dev/null +++ b/source/java/org/alfresco/license/CipherParamImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.license; + + +import de.schlichtherle.license.CipherParam; + + +/** + * Password used for license encryption + * + * @author davidc + */ +public class CipherParamImpl implements CipherParam +{ + + /* (non-Javadoc) + * @see de.schlichtherle.license.CipherParam#getKeyPwd() + */ + public String getKeyPwd() + { + return "j3ss13"; + }; + +} diff --git a/source/java/org/alfresco/license/LicenseComponent.java b/source/java/org/alfresco/license/LicenseComponent.java new file mode 100644 index 0000000000..63fd017ff1 --- /dev/null +++ b/source/java/org/alfresco/license/LicenseComponent.java @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.license; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Properties; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.admin.patch.PatchDaoService; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.domain.AppliedPatch; +import org.alfresco.repo.importer.ImporterBootstrap; +import org.alfresco.repo.transaction.TransactionComponent; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.ServiceRegistry; +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.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.license.LicenseDescriptor; +import org.alfresco.service.license.LicenseException; +import org.alfresco.service.license.LicenseService; +import org.alfresco.service.namespace.NamespaceService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SchedulerFactory; +import org.quartz.Trigger; +import org.quartz.helpers.TriggerUtils; +import org.quartz.impl.StdSchedulerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import de.schlichtherle.license.LicenseContent; +import de.schlichtherle.license.ftp.LicenseManager; +import de.schlichtherle.license.ftp.LicenseParam; + + +/** + * Alfresco Enterprise Network implementation of License Service + * + * @author davidc + */ +public class LicenseComponent implements LicenseService +{ + protected AlfrescoLicenseManager licenseManager; + private LicenseDescriptor licenseDescriptor = null; + + // dependencies + private TransactionComponent transactionComponent; + private ImporterBootstrap systemBootstrap; + private SearchService searchService; + private NamespaceService namespaceService; + protected NodeService nodeService; + protected ContentService contentService; + private Scheduler verifyScheduler = null; + private boolean failed = false; + + // logger + private static final Log logger = LogFactory.getLog(DescriptorService.class); + private static final Log loggerInternal = LogFactory.getLog(LicenseComponent.class); + + + /** + * Construct + * + * @param context application context + */ + public LicenseComponent(ApplicationContext context) + { + transactionComponent = (TransactionComponent)context.getBean("transactionComponent"); + systemBootstrap = (ImporterBootstrap)context.getBean("systemBootstrap"); + nodeService = (NodeService)context.getBean("nodeService"); + searchService = (SearchService)context.getBean("searchService"); + contentService = (ContentService)context.getBean("contentService"); + namespaceService = (NamespaceService)context.getBean(ServiceRegistry.NAMESPACE_SERVICE.getLocalName()); + + // construct license manager + boolean trialEligibility = getTrialEligibility(context); + licenseManager = new AlfrescoLicenseManager(new AlfrescoLicenseParam(trialEligibility)); + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseService#verifyLicense() + */ + public void verifyLicense() + { + // check to see if there's a license to install + File licenseFile = getLicenseFile(); + if (licenseFile != null) + { + if (logger.isInfoEnabled()) + logger.info("Alfresco license: Installing license file " + licenseFile.getName()); + + try + { + LicenseContent licenseContent = licenseManager.install(licenseFile); + renameLicenseFile(licenseFile); + licenseDescriptor = new LicenseContentDescriptor(licenseContent); + } + catch(Exception e) + { + throw new LicenseException("Failed to install license file " + licenseFile.getName(), e); + } + } + + // verify existing license + if (licenseDescriptor == null) + { + verify(); + + // construct scheduler for period license verify + SchedulerFactory schedulerFactory = new StdSchedulerFactory(); + try + { + verifyScheduler = schedulerFactory.getScheduler(); + JobDetail jobDetail = new JobDetail("vlj", Scheduler.DEFAULT_GROUP, VerifyLicenseJob.class); + jobDetail.getJobDataMap().put("licenseComponent", this); + Trigger trigger = TriggerUtils.makeHourlyTrigger(); + trigger.setStartTime(new Date(System.currentTimeMillis() + (60L * 1000L))); // one minute from now + trigger.setName("vlt"); + trigger.setGroup(Scheduler.DEFAULT_GROUP); + verifyScheduler.scheduleJob(jobDetail, trigger); + verifyScheduler.start(); + } + catch(SchedulerException e) + { + throw new LicenseException("Failed to initialise License Component"); + } + } + } + + /** + * Verify License + */ + protected void verify() + { + // note: if a license hasn't been already been installed then install + // a free trial period + TransactionWork verifyLicense = new TransactionUtil.TransactionWork() + { + public LicenseDescriptor doWork() + { + LicenseDescriptor descriptor = null; + try + { + LicenseContent licenseContent = licenseManager.verify(); + descriptor = new LicenseContentDescriptor(licenseContent); + } + catch(Exception e) + { + // handle license failure case + licenseFailed(e); + } + return descriptor; + } + }; + licenseDescriptor = TransactionUtil.executeInUserTransaction(transactionComponent, verifyLicense); + } + + /** + * Handle case where license is found to be invalid + */ + protected void licenseFailed(Exception e) + throws LicenseException + { + if (!failed) + { + // Mark transactions as read-only + transactionComponent.setAllowWrite(false); + + if (logger.isWarnEnabled()) + { + logger.warn("Alfresco license: Failed to verify license - Invalid License!"); + logger.warn("Alfresco license: Restricted Alfresco Repository to read-only capability"); + } + if (loggerInternal.isDebugEnabled()) + { + loggerInternal.debug("Alfresco license: Failed due to " + e.toString()); + } + failed = true; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseService#getLicense() + */ + public LicenseDescriptor getLicense() + { + return licenseDescriptor; + } + + /** + * Determine if a license file is to be installed + * + * @return the license file (or null, if one is not to be installed) + */ + private File getLicenseFile() + { + File licenseFile = null; + + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + try + { + Resource[] resources = resolver.getResources("classpath*:/alfresco/extension/license/*.lic"); + if (resources.length > 1) + { + String resMsg = ""; + for (Resource resource : resources) + { + resMsg += "[" + resource.getURL().toExternalForm() + "] "; + } + throw new LicenseException("Found more than one license file to install. The licenses found are: " + resMsg); + } + if (resources.length > 0) + { + licenseFile = resources[0].getFile(); + } + } + catch (IOException e) + { + // Note: Ignore: license not found + } + + return licenseFile; + } + + /** + * Rename installed license file + * + * @param licenseFile license file to rename + */ + private void renameLicenseFile(File licenseFile) + { + File dest = new File(licenseFile.getAbsolutePath() + ".installed"); + boolean success = false; + try + { + success = licenseFile.renameTo(dest); + } + catch(Exception e) + { + } + + if (!success) + { + if (logger.isWarnEnabled()) + logger.warn("Alfresco license: Failed to rename installed license file " + licenseFile.getName() + " to " + dest.getName()); + } + } + + /** + * Determine if eligible for trial license creation + * + * @param context application context + * @return true => trial license may be created + */ + private boolean getTrialEligibility(ApplicationContext context) + { + // from clean (open) + // from clean (enterprise) + // ==> systemBootstrap == true + + // upgrade from 1.2.1 open to 1.2.1 enterprise + // ==> patch = true, schema >= 12, versionEdition = open + + // upgrade from 1.2.1 open to 1.3.0 enterprise + // ==> patch = true, schema >= 12, versionEdition = open + + // upgrade from 1.2.1 enterprise to 1.3.0 enterprise + // ==> patch = true, schema >= 12, versionEdition = license + + // upgrade from 1.2 open to 1.2.1+ enterprise + // ==> patch = false, schema < 12, versionEdition = null + + // upgrade from 1.2 enterprise to 1.2.1+ enterprise + // ==> patch = false, schema < 12, versionEdition = null + + + // first determine if the system store has been bootstrapped in this startup sequence + // if so, a trial license may be created + boolean trialEligibility = systemBootstrap.hasPerformedBootstrap(); + + if (loggerInternal.isDebugEnabled()) + loggerInternal.debug("Alfresco license: System store bootstrapped: " + trialEligibility); + + // if not, then this could be a pre-installed repository that has yet to be patched with a license + if (!trialEligibility) + { + NodeRef descriptorRef = getDescriptor(); + if (descriptorRef != null) + { + PatchDaoService patchDao = (PatchDaoService)context.getBean("patchDaoComponent"); + AppliedPatch patch = patchDao.getAppliedPatch("patch.descriptorUpdate"); + + // versionEdition = open + // patch = true, schema >= 12 + + // versionEdition = null + // patch = false, schema < 12 + + // versionEdition = license + // not eligible + + int schema = (Integer)nodeService.getProperty(descriptorRef, ContentModel.PROP_SYS_VERSION_SCHEMA); + Serializable value = nodeService.getProperty(descriptorRef, ContentModel.PROP_SYS_VERSION_EDITION); + + if (loggerInternal.isDebugEnabled()) + { + loggerInternal.debug("Alfresco license: patch applied: " + (patch != null)); + loggerInternal.debug("Alfresco license: schema: " + schema); + loggerInternal.debug("Alfresco license: edition: " + value); + } + + if (value == null) + { + trialEligibility = (patch == null) && schema < 12; + } + else if (value instanceof Collection) + { + Collection editions = (Collection)value; + Object editionsValue = (editions.size() > 0) ? editions.iterator().next() : null; + String edition = (editionsValue instanceof String) ? (String)editionsValue : ""; + trialEligibility = (patch != null) && schema >=12 && edition.equals("Community Network"); + } + } + } + + if (loggerInternal.isDebugEnabled()) + loggerInternal.debug("Alfresco license: trial eligibility: " + trialEligibility); + + return trialEligibility; + } + + /** + * Get System Store Descriptor + * + * @return node reference of system store descriptor + */ + protected NodeRef getDescriptor() + { + StoreRef storeRef = systemBootstrap.getStoreRef(); + List nodeRefs = null; + + if (nodeService.exists(storeRef)) + { + Properties systemProperties = systemBootstrap.getConfiguration(); + String path = systemProperties.getProperty("system.descriptor.current.childname"); + String searchPath = "/" + path; + NodeRef rootNodeRef = nodeService.getRootNode(storeRef); + nodeRefs = searchService.selectNodes(rootNodeRef, searchPath, null, namespaceService, false); + } + + return (nodeRefs == null || nodeRefs.size() == 0) ? null : nodeRefs.get(0); + } + + + /** + * Job for period license verification + * + * @author davidc + */ + public static class VerifyLicenseJob implements Job + { + public void execute(JobExecutionContext context) throws JobExecutionException + { + LicenseComponent license = (LicenseComponent)context.getJobDetail().getJobDataMap().get("licenseComponent"); + license.verify(); + } + } + + + /** + * Alfresco implementation of License Manager + * + * Note: Stores verified license files in Alfresco Repository + * + * @author davidc + */ + public class AlfrescoLicenseManager extends LicenseManager + { + /** + * Construct + * + * @param context application context + * @param param license parameters + */ + public AlfrescoLicenseManager(LicenseParam param) + { + super(param); + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.LicenseManager#getLicenseKey() + */ + protected byte[] getLicenseKey() + { + byte[] key = null; + + try + { + NodeRef descriptorRef = getDescriptor(); + if (descriptorRef == null) + { + throw new LicenseException("Failed to find system descriptor"); + } + ContentReader reader = contentService.getReader(descriptorRef, ContentModel.PROP_SYS_VERSION_EDITION); + if (reader != null && reader.exists()) + { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + reader.getContent(os); + key = os.toByteArray(); + } + } + catch(Exception e) + { + throw new LicenseException("Failed to load license", e); + } + return key; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.LicenseManager#setLicenseKey(byte[]) + */ + protected synchronized void setLicenseKey(final byte[] key) + { + try + { + NodeRef descriptorRef = getDescriptor(); + if (descriptorRef == null) + { + throw new LicenseException("Failed to find system descriptor"); + } + if (key == null) + { + nodeService.setProperty(descriptorRef, ContentModel.PROP_SYS_VERSION_EDITION, null); + } + else + { + ContentWriter writer = contentService.getWriter(descriptorRef, ContentModel.PROP_SYS_VERSION_EDITION, true); + InputStream is = new ByteArrayInputStream(key); + writer.setMimetype(MimetypeMap.MIMETYPE_BINARY); + writer.putContent(is); + } + } + catch(Exception e) + { + throw new LicenseException("Failed to save license", e); + } + } + + } +} diff --git a/source/java/org/alfresco/license/LicenseContentDescriptor.java b/source/java/org/alfresco/license/LicenseContentDescriptor.java new file mode 100644 index 0000000000..776d3da1e5 --- /dev/null +++ b/source/java/org/alfresco/license/LicenseContentDescriptor.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.license; + +import java.security.Principal; +import java.util.Date; + +import org.alfresco.service.license.LicenseDescriptor; +import org.joda.time.DateMidnight; +import org.joda.time.Interval; +import org.joda.time.Period; +import org.joda.time.PeriodType; + +import de.schlichtherle.license.LicenseContent; + + +/** + * License Descriptor + * + * @author davidc + */ +public class LicenseContentDescriptor implements LicenseDescriptor +{ + private LicenseContent licenseContent = null; + + /** + * Construct + * + * @param licenseContent + */ + public LicenseContentDescriptor(LicenseContent licenseContent) + { + this.licenseContent = licenseContent; + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getIssued() + */ + public Date getIssued() + { + return licenseContent.getIssued(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getValidUntil() + */ + public Date getValidUntil() + { + return licenseContent.getNotAfter(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getSubject() + */ + public String getSubject() + { + return licenseContent.getSubject(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getHolder() + */ + public Principal getHolder() + { + return licenseContent.getHolder(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getIssuer() + */ + public Principal getIssuer() + { + return licenseContent.getIssuer(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getDays() + */ + public Integer getDays() + { + Integer days = null; + Date validUntil = getValidUntil(); + if (validUntil != null) + { + Date issued = getIssued(); + days = new Integer(calcDays(issued, validUntil)); + } + return days; + } + + /* (non-Javadoc) + * @see org.alfresco.service.license.LicenseDescriptor#getRemainingDays() + */ + public Integer getRemainingDays() + { + Integer days = null; + Date validUntil = getValidUntil(); + if (validUntil != null) + { + Date now = new Date(); + days = new Integer(calcDays(now, validUntil)); + } + return days; + } + + /** + * Calculate number of days between start and end date + * + * @param start start date + * @param end end date + * @return number days between + */ + private int calcDays(Date start, Date end) + { + DateMidnight startMidnight = new DateMidnight(start); + DateMidnight endMidnight = new DateMidnight(end); + + int days; + if (endMidnight.isBefore(startMidnight)) + { + Interval interval = new Interval(endMidnight, startMidnight); + Period period = interval.toPeriod(PeriodType.days()); + days = 0 - period.getDays(); + } + else + { + Interval interval = new Interval(startMidnight, endMidnight); + Period period = interval.toPeriod(PeriodType.days()); + days = period.getDays(); + } + return days; + } + +} diff --git a/source/java/org/alfresco/license/TrialKeyStore.java b/source/java/org/alfresco/license/TrialKeyStore.java new file mode 100644 index 0000000000..1065aa39c7 --- /dev/null +++ b/source/java/org/alfresco/license/TrialKeyStore.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.license; + + +/** + * Alfresco Trial KeyStore Parameters + * + * @author davidc + */ +public class TrialKeyStore extends AlfrescoKeyStore +{ + /* (non-Javadoc) + * @see de.schlichtherle.license.KeyStoreParam#getAlias() + */ + public String getAlias() + { + return "alfrescoTrial"; + } + + /* (non-Javadoc) + * @see de.schlichtherle.license.KeyStoreParam#getKeyPwd() + */ + public String getKeyPwd() + { + return "la1rtocs3rf1a"; + } + +} diff --git a/source/java/org/alfresco/license/alfresco.keystore b/source/java/org/alfresco/license/alfresco.keystore new file mode 100644 index 0000000000..18ba225ae3 Binary files /dev/null and b/source/java/org/alfresco/license/alfresco.keystore differ diff --git a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java index 8400ef1bbf..3840764d24 100644 --- a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java @@ -18,7 +18,6 @@ package org.alfresco.repo.action.executer; import java.util.List; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.service.cmr.action.Action; @@ -212,58 +211,48 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase destinationAssocQName, false); newCopy = true; - } + } - // Get the content reader - ContentReader contentReader = this.contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT); - if (contentReader == null) - { - // for some reason, this action is premature - throw new AlfrescoRuntimeException( - "Attempting to execute content transformation rule " + - "but content has not finished writing, i.e. no URL is available."); - } - String originalMimetype = contentReader.getMimetype(); - - // get the writer and set it up - ContentWriter contentWriter = this.contentService.getWriter(copyNodeRef, ContentModel.PROP_CONTENT, true); - contentWriter.setMimetype(mimeType); // new mimetype - contentWriter.setEncoding(contentReader.getEncoding()); // original encoding - if (newCopy == true) { // Adjust the name of the copy String originalName = (String)nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_NAME); - String newName = transformName(originalName, originalMimetype, mimeType); + String newName = transformName(originalName, mimeType); nodeService.setProperty(copyNodeRef, ContentModel.PROP_NAME, newName); String originalTitle = (String)nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_TITLE); if (originalTitle != null && originalTitle.length() > 0) { - String newTitle = transformName(originalTitle, originalMimetype, mimeType); + String newTitle = transformName(originalTitle, mimeType); nodeService.setProperty(copyNodeRef, ContentModel.PROP_TITLE, newTitle); } } - - // Try and transform the content - try + + // Get the content reader + ContentReader contentReader = this.contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT); + // Only do the transformation if some content is available + if (contentReader != null) { - doTransform(ruleAction, contentReader, contentWriter); - } - catch(NoTransformerException e) - { - if (logger.isDebugEnabled()) + // get the writer and set it up + ContentWriter contentWriter = this.contentService.getWriter(copyNodeRef, ContentModel.PROP_CONTENT, true); + contentWriter.setMimetype(mimeType); // new mimetype + contentWriter.setEncoding(contentReader.getEncoding()); // original encoding + + // Try and transform the content + try { - logger.debug("No transformer found to execute rule: \n" + - " reader: " + contentReader + "\n" + - " writer: " + contentWriter + "\n" + - " action: " + this); + doTransform(ruleAction, contentReader, contentWriter); } - //if (newCopy == true) - //{ - // TODO: Revisit this for alternative solutions - // nodeService.deleteNode(copyNodeRef); - // } - } + catch(NoTransformerException e) + { + if (logger.isDebugEnabled()) + { + logger.debug("No transformer found to execute rule: \n" + + " reader: " + contentReader + "\n" + + " writer: " + contentWriter + "\n" + + " action: " + this); + } + } + } } protected void doTransform(Action ruleAction, ContentReader contentReader, ContentWriter contentWriter) @@ -279,7 +268,7 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase * @param newMimetype * @return */ - private String transformName(String original, String originalMimetype, String newMimetype) + private String transformName(String original, String newMimetype) { // get the current extension int dotIndex = original.lastIndexOf('.');