diff --git a/source/java/org/alfresco/repo/webdav/OptionsMethod.java b/source/java/org/alfresco/repo/webdav/OptionsMethod.java index b83547c20a..0eba3ab17f 100644 --- a/source/java/org/alfresco/repo/webdav/OptionsMethod.java +++ b/source/java/org/alfresco/repo/webdav/OptionsMethod.java @@ -16,6 +16,8 @@ */ package org.alfresco.repo.webdav; +import javax.servlet.http.HttpServletResponse; + import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; @@ -30,6 +32,7 @@ public class OptionsMethod extends WebDAVMethod private static final String DAV_HEADER_CONTENT = "1,2"; private static final String ALLOW_HEADER = "Allow"; private static final String MS_HEADER = "MS-Author-Via"; + private static final String CONTENT_LENGTH = "Content-Length"; private static final String FILE_METHODS = "OPTIONS, GET, HEAD, POST, DELETE, PROPFIND, COPY, MOVE, LOCK, UNLOCK"; private static final String COLLECTION_METHODS = FILE_METHODS + ", PUT"; @@ -77,15 +80,24 @@ public class OptionsMethod extends WebDAVMethod catch (FileNotFoundException e) { // Do nothing; just default to a folder + isFolder = true; } + // Add the header to advertise the level of support the server has + m_response.addHeader(DAV_HEADER, DAV_HEADER_CONTENT); // Add the proprietary Microsoft header to make Microsoft clients behave + m_response.addHeader(MS_HEADER, DAV_HEADER); // Add the header to show what methods are allowed + m_response.addHeader(ALLOW_HEADER, isFolder ? COLLECTION_METHODS : FILE_METHODS); + + // Indicate no content + + m_response.addHeader(CONTENT_LENGTH, "0"); } } diff --git a/source/java/org/alfresco/repo/webdav/WebDAVMethod.java b/source/java/org/alfresco/repo/webdav/WebDAVMethod.java index 6e6d91effe..3ebb6feb85 100644 --- a/source/java/org/alfresco/repo/webdav/WebDAVMethod.java +++ b/source/java/org/alfresco/repo/webdav/WebDAVMethod.java @@ -23,10 +23,13 @@ import java.util.Iterator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.transaction.Status; +import javax.transaction.UserTransaction; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.TransactionUtil; @@ -113,28 +116,37 @@ public abstract class WebDAVMethod public void execute() throws WebDAVServerException { // Parse the HTTP headers + parseRequestHeaders(); // Parse the HTTP body - parseRequestBody(); - TransactionWork executeWork = new TransactionWork() - { - public WebDAVServerException doWork() throws Exception - { - executeImpl(); - return null; - } - }; + parseRequestBody(); + + // Run the method wrapped in a transaction + + TransactionService transactionService = getTransactionService(); + UserTransaction tx = transactionService.getUserTransaction( false); + try { - // Execute the method - TransactionService transactionService = getTransactionService(); - TransactionUtil.executeInUserTransaction(transactionService, executeWork); + // Start the transaction + + tx.begin(); + + // Run the WebDAV method + + executeImpl(); + + // Commit the transaction + + tx.commit(); + tx = null; } catch (AccessDeniedException e) { // Return a forbidden status + throw new WebDAVServerException(HttpServletResponse.SC_UNAUTHORIZED, e); } catch (Throwable e) @@ -147,9 +159,39 @@ public abstract class WebDAVMethod else { // Convert error to a server error + throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } } + finally + { + if ( tx != null) + { + // Commit or rollback the transaction + + try + { + // Commit or rollback the transaction + + if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) + { + // Transaction is marked for rollback + + tx.rollback(); + } + else + { + // Commit the transaction + + tx.commit(); + } + } + catch ( Exception ex) + { + throw new AlfrescoRuntimeException("Failed to end transaction", ex); + } + } + } } /** diff --git a/source/java/org/alfresco/repo/webdav/WebDAVServlet.java b/source/java/org/alfresco/repo/webdav/WebDAVServlet.java index 5fd20f218d..b06cfffbb2 100644 --- a/source/java/org/alfresco/repo/webdav/WebDAVServlet.java +++ b/source/java/org/alfresco/repo/webdav/WebDAVServlet.java @@ -97,6 +97,7 @@ public class WebDAVServlet extends HttpServlet try { // Create the appropriate WebDAV method for the request and execute it + final WebDAVMethod method = createMethod(request, response); if (method == null) @@ -115,20 +116,14 @@ public class WebDAVServlet extends HttpServlet logger.error("No root node for request"); // Return an error status + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } - // Execute the WebDAV request, wrapped in a transaction - TransactionWork methodWork = new TransactionWork() - { - public Object doWork() throws Exception - { - method.execute(); - return null; - } - }; - TransactionUtil.executeInUserTransaction(m_transactionService, methodWork); + // Excecute the WebDAV request + + method.execute(); } catch (Throwable e) { @@ -139,13 +134,15 @@ public class WebDAVServlet extends HttpServlet e = e.getCause(); } } + // Work out how to handle the error + if (e instanceof WebDAVServerException) { WebDAVServerException error = (WebDAVServerException) e; if (error.getCause() != null) { - logger.error(INTERNAL_SERVER_ERROR, error.getCause()); + logger.debug( "WebDAV " + request.getMethod() + " error: " + error.getCause().getMessage()); } if (logger.isDebugEnabled()) @@ -166,7 +163,7 @@ public class WebDAVServlet extends HttpServlet } else { - logger.error(INTERNAL_SERVER_ERROR, e); + logger.debug( "WebDAV " + request.getMethod() + " error: " + e.getMessage()); if (response.isCommitted()) { diff --git a/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java index 0d489e5e25..d8cd696803 100644 --- a/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java +++ b/source/java/org/alfresco/repo/webdav/auth/NTLMAuthenticationFilter.java @@ -21,6 +21,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Hashtable; import java.util.List; import java.util.Random; @@ -126,6 +127,10 @@ public class NTLMAuthenticationFilter implements Filter private String m_srvName; + // In progress NTLM logons for clients that are not using cookies + + private Hashtable m_logonDetailsTable; + /** * Initialize the filter * @@ -209,6 +214,10 @@ public class NTLMAuthenticationFilter implements Filter if ( logger.isDebugEnabled() && m_allowGuest) logger.debug("NTLM filter guest access allowed"); } + + // Create the NTLM logon details hash table for clients that are not using cookies + + m_logonDetailsTable = new Hashtable(); } /** @@ -320,6 +329,7 @@ public class NTLMAuthenticationFilter implements Filter Type3NTLMMessage type3Msg = new Type3NTLMMessage(ntlmByts); processType3(type3Msg, req, resp, httpSess, chain); + return; } } @@ -445,8 +455,32 @@ public class NTLMAuthenticationFilter implements Filter ntlmDetails = new NTLMLogonDetails(); ntlmDetails.setType2Message( type2Msg); ntlmDetails.setAuthenticationToken(authToken); - - httpSess.setAttribute(NTLM_AUTH_DETAILS, ntlmDetails); + + // Check if the client supports cookies + + if ( req.getCookies() != null) + { + // Client appears to support cookies so we can store the NTLM details in the session + + httpSess.setAttribute(NTLM_AUTH_DETAILS, ntlmDetails); + } + else + { + // Client does not support cookies so store the NTLM details in the logon details table + + StringBuilder keyStr = new StringBuilder(); + + keyStr.append( req.getRemoteHost()); + keyStr.append( ":"); + keyStr.append( req.getRemotePort()); + + m_logonDetailsTable.put( keyStr.toString(), ntlmDetails); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "Storing NTLM details for " + keyStr.toString() + " in logon table"); + } // Debug @@ -495,7 +529,30 @@ public class NTLMAuthenticationFilter implements Filter ntlmDetails = (NTLMLogonDetails) httpSess.getAttribute(NTLM_AUTH_DETAILS); user = (WebDAVUser) httpSess.getAttribute(AUTHENTICATION_USER); } - + + // Check if the NTLM details are null and the client is not using cookies. The NTLM details will have been + // stored in the logon table. + + if ( ntlmDetails == null && req.getCookies() == null) + { + // Check if the NTLM details are in logon table + + StringBuilder keyStr = new StringBuilder(); + + keyStr.append( req.getRemoteHost()); + keyStr.append( ":"); + keyStr.append( req.getRemotePort()); + + // Remove the NTLM details from the logon table + + ntlmDetails = m_logonDetailsTable.remove( keyStr.toString()); + + // DEBUG + + if ( logger.isDebugEnabled() && ntlmDetails != null) + logger.debug( "Retrieved NTLM details for " + keyStr.toString() + " from logon table"); + } + // Get the NTLM logon details String userName = type3Msg.getUserName(); @@ -568,7 +625,7 @@ public class NTLMAuthenticationFilter implements Filter } else { - // Cehck if we are using local MD4 password hashes or passthru authentication + // Check if we are using local MD4 password hashes or passthru authentication if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) { @@ -664,13 +721,16 @@ public class NTLMAuthenticationFilter implements Filter tx.begin(); // Get user details for the authenticated user + m_authComponent.setCurrentUser(userName.toLowerCase()); // The user name used may be a different case to the NTLM supplied user name, read the current // user and use that name + userName = m_authComponent.getCurrentUserName(); // Setup User object and Home space ID etc. + NodeRef personNodeRef = m_personService.getPerson(userName); String currentTicket = m_authService.getCurrentTicket(); user = new WebDAVUser(userName, currentTicket, personNodeRef); @@ -678,7 +738,8 @@ public class NTLMAuthenticationFilter implements Filter NodeRef homeSpaceRef = (NodeRef) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); user.setHomeNode(homeSpaceRef); - // commit + // Commit the transaction + tx.commit(); } catch (Throwable ex) diff --git a/source/java/org/alfresco/repo/webservice/CMLUtil.java b/source/java/org/alfresco/repo/webservice/CMLUtil.java index 71d23d4fad..da8636ef21 100644 --- a/source/java/org/alfresco/repo/webservice/CMLUtil.java +++ b/source/java/org/alfresco/repo/webservice/CMLUtil.java @@ -593,7 +593,7 @@ public class CMLUtil List nodesToCopy = getNodeRefList(copy.getWhere_id(), copy.getWhere(), context); for (NodeRef nodeToCopy : nodesToCopy) { - NodeRef newNodeRef = this.copyService.copy(nodeToCopy, destinationNodeRef, assocType, assocName, copyChildren); + NodeRef newNodeRef = this.copyService.copyAndRename(nodeToCopy, destinationNodeRef, assocType, assocName, copyChildren); // Create the result results.add(createResult(COPY, null, nodeToCopy, newNodeRef));