/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
* #L%
*/
package org.alfresco.repo.webdav;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.events.types.ContentEvent;
import org.alfresco.events.types.ContentEventImpl;
import org.alfresco.events.types.Event;
import org.alfresco.jlan.util.IPAddress;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.Client;
import org.alfresco.repo.Client.ClientType;
import org.alfresco.repo.events.EventPreparator;
import org.alfresco.repo.events.EventPublisher;
import org.alfresco.repo.lock.LockUtils;
import org.alfresco.repo.model.filefolder.HiddenAspect;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.activities.ActivityPoster;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.CopyService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.util.EqualsHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.URLDecoder;
import org.springframework.extensions.surf.util.URLEncoder;
import org.springframework.util.StringUtils;
import org.xml.sax.helpers.AttributesImpl;
/**
* WebDAV Protocol Helper Class
*
*
Provides helper methods for repository access using the WebDAV protocol.
*
* @author GKSpencer
*/
public class WebDAVHelper
{
// Constants
public static final String BEAN_NAME = "webDAVHelper";
private static final String HTTPS_SCHEME = "https://";
private static final String HTTP_SCHEME = "http://";
// Path seperator
public static final String PathSeperator = "/";
public static final char PathSeperatorChar = '/';
public static final String EMPTY_SITE_ID = "";
// Logging
protected static Log logger = LogFactory.getLog("org.alfresco.webdav.protocol");
// Service registry TODO: eliminate this - not dependency injection!
private ServiceRegistry m_serviceRegistry;
// Services
private NodeService m_nodeService;
private FileFolderService m_fileFolderService;
private SearchService m_searchService;
private NamespaceService m_namespaceService;
private DictionaryService m_dictionaryService;
private MimetypeService m_mimetypeService;
private WebDAVLockService m_lockService;
private ActionService m_actionService;
private AuthenticationService m_authService;
private PermissionService m_permissionService;
private TenantService m_tenantService;
private HiddenAspect m_hiddenAspect;
private EventPublisher eventPublisher;
private ActivityPoster poster;
// pattern is tested against full path after it has been lower cased.
private Pattern m_renameShufflePattern = Pattern.compile("(.*/\\..*)|(.*[a-f0-9]{8}+$)|(.*\\.tmp$)|(.*atmp[0-9]+$)|(.*\\.wbk$)|(.*\\.bak$)|(.*\\~$)|(.*backup.*\\.do[ct]{1}[x]?[m]?$)|(.*\\.sb\\-\\w{8}\\-\\w{6}$)");
// Empty XML attribute list
private final AttributesImpl m_nullAttribs = new AttributesImpl();
private BehaviourFilter m_policyBehaviourFilter;
private String m_urlPathPrefix;
private long sizeLimit = -1L;
/**
* This method sets a value for the limit. If the string does not {@link Long#parseLong(String) parse} to a
* java long.
*
* @param limit a String representing a valid Java long.
*/
public void setSizeLimitString(String limit)
{
// A string parameter is used here in order to not to require end users to provide a value for the limit in a property
// file. This results in the empty string being injected to this method.
long longLimit = -1L;
try
{
longLimit = Long.parseLong(limit);
} catch (NumberFormatException ignored)
{
// Intentionally empty
}
this.sizeLimit = longLimit;
}
/**
* Set the regular expression that will be applied to filenames during renames
* to detect whether clients are performing a renaming shuffle - common during
* file saving on various clients.
*
* ALF-3856, ALF-7079, MNT-181
*
* @param renameShufflePattern a regular expression filename match
*/
public void setRenameShufflePattern(Pattern renameShufflePattern)
{
this.m_renameShufflePattern = renameShufflePattern;
}
/**
* @return Return the limit size
*/
public long getSizeLimit()
{
return sizeLimit;
}
/**
* @return Return the authentication service
*/
public final AuthenticationService getAuthenticationService()
{
return m_authService;
}
/**
* @return Return the service registry
*/
public ServiceRegistry getServiceRegistry()
{
// TODO: eliminate this - not dependency injection!
return m_serviceRegistry;
}
/**
* @return Return the node service
*/
public final NodeService getNodeService()
{
return m_nodeService;
}
public FileFolderService getFileFolderService()
{
return m_fileFolderService;
}
/**
* @return Return the search service
*/
public final SearchService getSearchService()
{
return m_searchService;
}
/**
* @return Return the namespace service
*/
public final NamespaceService getNamespaceService()
{
return m_namespaceService;
}
/**
* @return Return the dictionary service
*/
public final DictionaryService getDictionaryService()
{
return m_dictionaryService;
}
/**
* @return Return the mimetype service
*/
public final MimetypeService getMimetypeService()
{
return m_mimetypeService;
}
/**
* @return Return the lock service
*/
public WebDAVLockService getLockService()
{
return m_lockService;
}
/**
* @return Return the action service
*/
public final ActionService getActionService()
{
return m_actionService;
}
/**
*
* @return Return the permission service
*/
public final PermissionService getPermissionService()
{
return m_permissionService;
}
/**
* @return the hidden aspect bean
*/
public final HiddenAspect getHiddenAspect()
{
return m_hiddenAspect;
}
/**
* Retrieve the {@link TenantService} held by the helper.
*
* @return TenantService
*/
public TenantService getTenantService()
{
return m_tenantService;
}
/**
* @return Return the copy service
*/
public final CopyService getCopyService()
{
return getServiceRegistry().getCopyService();
}
public void setTenantService(TenantService tenantService)
{
this.m_tenantService = tenantService;
}
/**
* @param serviceRegistry the service registry
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.m_serviceRegistry = serviceRegistry;
}
/**
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.m_nodeService = nodeService;
}
/**
* @param fileFolderService the fileFolder service
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.m_fileFolderService = fileFolderService;
}
/**
* @param searchService the search service
*/
public void setSearchService(SearchService searchService)
{
this.m_searchService = searchService;
}
/**
* @param namespaceService the namespace service
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.m_namespaceService = namespaceService;
}
/**
* @param eventPublisher the eventPublisher service
*/
public void setEventPublisher(EventPublisher eventPublisher)
{
this.eventPublisher = eventPublisher;
}
/**
* @param poster ActivityPoster
*/
public void setPoster(ActivityPoster poster)
{
this.poster = poster;
}
/**
* @param dictionaryService the dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.m_dictionaryService = dictionaryService;
}
/**
* @param mimetypeService the mimetype service
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
this.m_mimetypeService = mimetypeService;
}
/**
* @param lockService the lock service
*/
public void setLockService(WebDAVLockService lockService)
{
this.m_lockService = lockService;
}
/**
* @param actionService the action service
*/
public void setActionService(ActionService actionService)
{
this.m_actionService = actionService;
}
/**
* @param authService the authentication service
*/
public void setAuthenticationService(AuthenticationService authService)
{
this.m_authService = authService;
}
/**
* @param permissionService the permission service
*/
public void setPermissionService(PermissionService permissionService)
{
this.m_permissionService = permissionService;
}
/**
* @param hiddenAspect the hiddenAspect to set
*/
public void setHiddenAspect(HiddenAspect hiddenAspect)
{
this.m_hiddenAspect = hiddenAspect;
}
public BehaviourFilter getPolicyBehaviourFilter()
{
return m_policyBehaviourFilter;
}
public void setPolicyBehaviourFilter(BehaviourFilter behaviourFilter)
{
m_policyBehaviourFilter = behaviourFilter;
}
/**
* Checks a new path in a move operation to detect whether clients are starting a renaming shuffle - common during
* file saving on various clients.
*
* ALF-3856, ALF-7079, MNT-181
*/
public boolean isRenameShuffle(String newPath)
{
return m_renameShufflePattern.matcher(newPath.toLowerCase()).matches();
}
/**
* Split the path into seperate directory path and file name strings.
* If the path is not empty, then there will always be an entry for the filename
*
* @param path Full path string.
* @return Returns a String[2] with the folder path and file path.
*/
public final String[] splitPath(String path)
{
if (path == null)
throw new IllegalArgumentException("path may not be null");
// Create an array of strings to hold the path and file name strings
String[] pathStr = new String[] {"", ""};
// Check if the path has a trailing seperator, if so then there is no file name.
int pos = path.lastIndexOf(PathSeperatorChar);
if (pos == -1 || pos == (path.length() - 1))
{
// Set the path string in the returned string array
pathStr[1] = path;
}
else
{
pathStr[0] = path.substring(0, pos);
pathStr[1] = path.substring(pos + 1);
}
// Return the path strings
return pathStr;
}
/**
* Split the path into all the component directories and filename
*
* @param path the string to split
* @return an array of all the path components
*/
public List splitAllPaths(String path)
{
if (path == null || path.length() == 0)
{
return Collections.emptyList();
}
// split the path
StringTokenizer token = new StringTokenizer(path, PathSeperator);
List results = new ArrayList(10);
while (token.hasMoreTokens())
{
results.add(token.nextToken());
}
return results;
}
public String getURLForPath(HttpServletRequest request, String path, boolean isCollection)
{
return getURLForPath(request, path, isCollection, null);
}
public String getURLForPath(HttpServletRequest request, String path, boolean isCollection, String userAgent)
{
String urlPathPrefix = getUrlPathPrefix(request);
StringBuilder urlStr = new StringBuilder(urlPathPrefix);
if (path.equals(WebDAV.RootPath) == false)
{
// split the path and URL encode each path element
for (StringTokenizer t = new StringTokenizer(path, PathSeperator); t.hasMoreTokens(); /**/)
{
urlStr.append( WebDAVHelper.encodeURL(t.nextToken(), userAgent) );
if (t.hasMoreTokens())
{
urlStr.append(PathSeperator);
}
}
}
// If the URL is to a collection add a trailing slash
if (isCollection && urlStr.charAt( urlStr.length() - 1) != PathSeperatorChar)
{
urlStr.append( PathSeperator);
}
logger.debug("getURLForPath() path:" + path + " => url:" + urlStr);
// Return the URL string
return urlStr.toString();
}
/**
* Get the file info for the given paths
*
* @param rootNodeRef the acting webdav root
* @param path the path to search for
* @return Return the file info for the path
* @throws FileNotFoundException
* if the path doesn't refer to a valid node
*/
public FileInfo getNodeForPath(NodeRef rootNodeRef, String path) throws FileNotFoundException
{
if (rootNodeRef == null)
{
throw new IllegalArgumentException("Root node may not be null");
}
else if (path == null)
{
throw new IllegalArgumentException("Path may not be null");
}
FileFolderService fileFolderService = getFileFolderService();
// Check for the root path
if ( path.length() == 0 || path.equals(PathSeperator))
{
return fileFolderService.getFileInfo(rootNodeRef);
}
// split the paths up
List splitPath = splitAllPaths(path);
// find it
FileInfo fileInfo = m_fileFolderService.resolveNamePath(rootNodeRef, splitPath);
String fileName = splitPath.get(splitPath.size() - 1);
if (!fileInfo.getName().equals(fileName))
{
throw new FileNotFoundException("Requested filename " + fileName +
" does not match case of " + fileInfo.getName());
}
// done
if (logger.isDebugEnabled())
{
logger.debug("Fetched node for path: \n" +
" root: " + rootNodeRef + "\n" +
" path: " + path + "\n" +
" result: " + fileInfo);
}
return fileInfo;
}
public boolean isRootPath(String path, String servletPath)
{
// Check for the root path
return( path.length() == 0 || path.equals(PathSeperator) || EqualsHelper.nullSafeEquals(path, servletPath));
}
public final FileInfo getParentNodeForPath(NodeRef rootNodeRef, String path) throws FileNotFoundException
{
if (rootNodeRef == null)
{
throw new IllegalArgumentException("Root node may not be null");
}
else if (path == null)
{
throw new IllegalArgumentException("Path may not be null");
}
// shorten the path
String[] paths = splitPath(path);
return getNodeForPath(rootNodeRef, paths[0]);
}
/**
* Return the relative path for the node walking back to the specified root node
*
* @param rootNodeRef the root below which the path will be valid
* @param nodeRef the node's path to get
* @return Returns string of form /A/B/C where C represents the from node and
*/
public final String getPathFromNode(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
{
// Check if the nodes are valid, or equal
if (rootNodeRef == null || nodeRef == null)
throw new IllegalArgumentException("Invalid node(s) in getPathFromNode call");
// short cut if the path node is the root node
if (rootNodeRef.equals(nodeRef))
return "";
FileFolderService fileFolderService = getFileFolderService();
// get the path elements
List pathInfos = fileFolderService.getNameOnlyPath(rootNodeRef, nodeRef);
// build the path string
StringBuilder sb = new StringBuilder(pathInfos.size() * 20);
for (String fileInfo : pathInfos)
{
sb.append(WebDAVHelper.PathSeperatorChar);
sb.append(fileInfo);
}
// done
if (logger.isDebugEnabled())
{
logger.debug("Build name path for node: \n" +
" root: " + rootNodeRef + "\n" +
" target: " + nodeRef + "\n" +
" path: " + sb);
}
return sb.toString();
}
public FileInfo createFile(FileInfo parentNodeInfo, String path) throws WebDAVServerException
{
return m_fileFolderService.create(parentNodeInfo.getNodeRef(), path, ContentModel.TYPE_CONTENT);
}
public List getChildren(FileInfo fileInfo) throws WebDAVServerException
{
return m_fileFolderService.list(fileInfo.getNodeRef());
}
/**
* Make an ETag value for a node using the GUID and modify date/time
*/
public final String makeETag(FileInfo nodeInfo)
{
// Get the modify date/time property for the node
StringBuilder etag = new StringBuilder();
makeETagString(nodeInfo, etag);
return etag.toString();
}
/**
* Make an ETag value for a node using the GUID and modify date/time
*/
public final String makeQuotedETag(FileInfo nodeInfo)
{
StringBuilder etag = new StringBuilder();
etag.append("\"");
makeETagString(nodeInfo, etag);
etag.append("\"");
return etag.toString();
}
/**
* Make an ETag value for a node using the GUID and modify date/time
*/
protected final void makeETagString(FileInfo nodeInfo, StringBuilder etag)
{
// Get the modify date/time property for the node
Object modVal = nodeInfo.getProperties().get(ContentModel.PROP_MODIFIED);
etag.append(nodeInfo.getNodeRef().getId());
if ( modVal != null)
{
etag.append("_");
etag.append(DefaultTypeConverter.INSTANCE.longValue(modVal));
}
}
/**
* @return Return the null XML attribute list
*/
public final AttributesImpl getNullAttributes()
{
return m_nullAttribs;
}
/**
* Encodes the given string to valid URL format
*
* @param s the String to convert
*/
public final static String encodeURL(String s)
{
return encodeURL(s, null);
}
public final static String encodeURL(String s, String userAgent)
{
return URLEncoder.encode(s);
}
public final static String decodeURL(String s)
{
return URLDecoder.decode(s);
}
/**
* Encodes the given string to valid HTML format
*
* @param string the String to convert
*/
public final static String encodeHTML(String string)
{
if (string == null)
{
return "";
}
StringBuilder sb = null; //create on demand
String enc;
char c;
for (int i = 0; i < string.length(); i++)
{
enc = null;
c = string.charAt(i);
switch (c)
{
case '"': enc = """; break; //"
case '&': enc = "&"; break; //&
case '<': enc = "<"; break; //<
case '>': enc = ">"; break; //>
//misc
case '\u20AC': enc = "€"; break;
case '\u00AB': enc = "«"; break;
case '\u00BB': enc = "»"; break;
case '\u00A0': enc = " "; break;
default:
if (((int)c) >= 0x80)
{
//encode all non basic latin characters
enc = "" + ((int)c) + ";";
}
break;
}
if (enc != null)
{
if (sb == null)
{
String soFar = string.substring(0, i);
sb = new StringBuilder(i + 8);
sb.append(soFar);
}
sb.append(enc);
}
else
{
if (sb != null)
{
sb.append(c);
}
}
}
if (sb == null)
{
return string;
}
else
{
return sb.toString();
}
}
/**
* ALF-5333: Microsoft clients use ISO-8859-1 to decode WebDAV responses
* so this method should only be used for Microsoft user agents.
*
* @param string String
* @return The encoded string for Microsoft clients
* @throws UnsupportedEncodingException
*/
public final static String encodeUrlReservedSymbols(String string) throws UnsupportedEncodingException
{
if (string == null)
{
return "";
}
StringBuilder sb = null; // create on demand
String enc;
char c;
for (int i = 0; i < string.length(); i++)
{
enc = null;
c = string.charAt(i);
switch (c)
{
// reserved
case ';': enc = URLEncoder.encode(String.valueOf(c)); break;
case '/': enc = URLEncoder.encode(String.valueOf(c)); break;
case '?': enc = URLEncoder.encode(String.valueOf(c)); break;
case ':': enc = URLEncoder.encode(String.valueOf(c)); break;
case '@': enc = URLEncoder.encode(String.valueOf(c)); break;
case '&': enc = URLEncoder.encode(String.valueOf(c)); break;
case '=': enc = URLEncoder.encode(String.valueOf(c)); break;
case '+': enc = URLEncoder.encode(String.valueOf(c)); break;
// unsafe
case '\"': enc = URLEncoder.encode(String.valueOf(c)); break;
case '#': enc = URLEncoder.encode(String.valueOf(c)); break;
case '%': enc = URLEncoder.encode(String.valueOf(c)); break;
case '>': enc = URLEncoder.encode(String.valueOf(c)); break;
case '<': enc = URLEncoder.encode(String.valueOf(c)); break;
default: break;
}
if (enc != null)
{
if (sb == null)
{
String soFar = string.substring(0, i);
sb = new StringBuilder(i + 8);
sb.append(soFar);
}
sb.append(enc);
}
else
{
if (sb != null)
{
sb.append(c);
}
}
}
if (sb == null)
{
return string;
}
else
{
return sb.toString();
}
}
public String determineSiteId(WebDAVMethod method)
{
return determineSiteId(method.getRootNodeRef(), method.getPath());
}
public String determineSiteId(NodeRef rootNodeRef, String path)
{
SiteService siteService = getServiceRegistry().getSiteService();
String siteId;
try
{
FileInfo fileInfo = getNodeForPath(rootNodeRef, path);
siteId = siteService.getSiteShortName(fileInfo.getNodeRef());
if (siteId == null)
{
throw new RuntimeException("Node is not contained by a site: " + path);
}
}
catch (Exception error)
{
siteId = EMPTY_SITE_ID;
}
return siteId;
}
@Deprecated
public String determineTenantDomain(WebDAVMethod method)
{
return determineTenantDomain();
}
public String determineTenantDomain()
{
TenantService tenantService = getTenantService();
String tenantDomain = tenantService.getCurrentUserDomain();
if (tenantDomain == null)
{
return TenantService.DEFAULT_DOMAIN;
}
return tenantDomain;
}
/**
* Extract the destination path for MOVE or COPY commands from the
* supplied destination URL header.
*
* @param servletPath Path prefix of the WebDAV servlet.
* @param destURL The Destination header.
* @return The path to move/copy the file to.
*/
public String getDestinationPath(String contextPath, String servletPath, String destURL)
{
if (destURL != null && destURL.length() > 0)
{
int offset = -1;
if (destURL.startsWith(HTTP_SCHEME))
{
// Set the offset to the start of the host name
offset = HTTP_SCHEME.length();
}
else if (destURL.startsWith(HTTPS_SCHEME))
{
// Set the offset to the start of the host name
offset = HTTPS_SCHEME.length();
}
// Strip the start of the path if not a relative path
if (offset != -1)
{
offset = destURL.indexOf(WebDAV.PathSeperator, offset);
if (offset != -1)
{
// Strip the host from the beginning
String strPath = destURL.substring(offset);
// If it starts with /contextPath/servletPath/ (e.g. /alfresco/webdav/path/to/file) - then
// strip the servlet path from the start of the path.
String pathPrefix = contextPath + servletPath + WebDAV.PathSeperator;
if (strPath.startsWith(pathPrefix))
{
strPath = strPath.substring(pathPrefix.length());
}
return WebDAV.decodeURL(strPath);
}
}
}
// Unable to get the path.
return null;
}
/**
* Check that the destination path is on this server and is a valid WebDAV
* path for this server
*
* @param request The request made against the WebDAV server.
* @param urlStr String
* @exception WebDAVServerException
*/
public void checkDestinationURL(HttpServletRequest request, String urlStr) throws WebDAVServerException
{
try
{
// Parse the URL
URL url = new URL(urlStr);
// Check if the path is on this WebDAV server
boolean localPath = true;
if (url.getPort() != -1 && url.getPort() != request.getServerPort())
{
// Debug
if (logger.isDebugEnabled())
logger.debug("Destination path, different server port");
localPath = false;
}
else if (url.getHost().equalsIgnoreCase(request.getServerName()) == false
&& url.getHost().equals(request.getLocalAddr()) == false)
{
// The target host may contain a domain or be specified as a numeric IP address
String targetHost = url.getHost();
if ( IPAddress.isNumericAddress( targetHost) == false)
{
String localHost = request.getServerName();
int pos = targetHost.indexOf( ".");
if ( pos != -1)
targetHost = targetHost.substring( 0, pos);
pos = localHost.indexOf( ".");
if ( pos != -1)
localHost = localHost.substring( 0, pos);
// compare the host names
if ( targetHost.equalsIgnoreCase( localHost) == false)
localPath = false;
}
else
{
try
{
// Check if the target IP address is a local address
InetAddress targetAddr = InetAddress.getByName( targetHost);
if ( NetworkInterface.getByInetAddress( targetAddr) == null)
localPath = false;
}
catch (Exception ex)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Failed to check target IP address, " + targetHost);
localPath = false;
}
}
// Debug
if (localPath == false && logger.isDebugEnabled())
{
logger.debug("Destination path, different server name/address");
logger.debug(" URL host=" + url.getHost() + ", ServerName=" + request.getServerName() + ", localAddr=" + request.getLocalAddr());
}
}
else if (!url.getPath().startsWith(getUrlPathPrefix(request)))
{
// Debug
if (logger.isDebugEnabled())
logger.debug("Destination path, different serlet path");
localPath = false;
}
// If the URL does not refer to this WebDAV server throw an
// exception
if (localPath != true)
throw new WebDAVServerException(HttpServletResponse.SC_BAD_GATEWAY);
}
catch (MalformedURLException ex)
{
// Debug
if (logger.isDebugEnabled())
logger.debug("Bad destination path, " + urlStr);
throw new WebDAVServerException(HttpServletResponse.SC_BAD_GATEWAY);
}
}
public void setUrlPathPrefix(String urlPathPrefix)
{
m_urlPathPrefix = urlPathPrefix;
}
public String getUrlPathPrefix(HttpServletRequest request)
{
StringBuilder urlStr = null;
if (StringUtils.hasText(m_urlPathPrefix))
{
// A specific prefix has been configured in, so use it.
urlStr = new StringBuilder(m_urlPathPrefix);
}
else
{
// Extract the path prefix from the request, using the servlet path as a guide.
// e.g. "/preamble/servlet-mapping/folder/file.txt"
// with a servlet path of "/servlet-mapping"
// would result in a path prefix of "/preamble/servlet-mapping" being discovered.
urlStr = new StringBuilder(request.getRequestURI());
String servletPath = request.getServletPath();
int rootPos = urlStr.indexOf(servletPath);
if (rootPos != -1)
{
urlStr.setLength(rootPos + servletPath.length());
}
}
// Ensure the prefix ends in the path separator.
if (urlStr.length() == 0 || urlStr.charAt(urlStr.length() - 1) != PathSeperatorChar)
{
urlStr.append(PathSeperator);
}
return urlStr.toString();
}
/**
* Notifies listeners that a read has taken place
* @param realNodeInfo FileInfo
* @param mimetype String
* @param size Long
* @param contentEncoding String
* @param range String
*/
protected void publishReadEvent(final FileInfo realNodeInfo, final String mimetype, final Long size, final String contentEncoding, final String range)
{
if (!StringUtils.hasText(range))
{
// Its not a range request
eventPublisher.publishEvent(new EventPreparator()
{
@Override
public Event prepareEvent(String user, String networkId, String transactionId)
{
return new ContentEventImpl(ContentEvent.DOWNLOAD, user, networkId, transactionId, realNodeInfo.getNodeRef().getId(), null,
realNodeInfo.getType().toString(), Client.asType(ClientType.webdav), realNodeInfo.getName(), mimetype,
size, contentEncoding);
}
});
}
}
public String getRepositoryPath(HttpServletRequest request)
{
// Try and get the path
String strPath = null;
try
{
strPath = WebDAVHelper.decodeURL(request.getRequestURI());
}
catch (Exception ex) {}
// Find the servlet path and trim from the request path
String servletPath = request.getServletPath();
int rootPos = strPath.indexOf(servletPath);
if ( rootPos != -1)
{
strPath = strPath.substring( rootPos);
}
// If we failed to get the path from the request try and get the path from the servlet path
if (strPath == null)
{
strPath = request.getServletPath();
}
if (strPath == null || strPath.length() == 0)
{
// If we still have not got a path then default to the root directory
strPath = WebDAV.RootPath;
}
else if (strPath.startsWith(request.getServletPath()))
{
// Check if the path starts with the base servlet path
int len = request.getServletPath().length();
if (strPath.length() > len)
{
strPath = strPath.substring(len);
}
else
{
strPath = WebDAV.RootPath;
}
}
// Make sure there are no trailing slashes
if (strPath.length() > 1 && strPath.endsWith(WebDAV.PathSeperator))
{
strPath = strPath.substring(0, strPath.length() - 1);
}
// Return the path
return strPath;
}
/**
* Indicates if the node is unlocked or the current user has a WRITE_LOCK
*
* @see LockUtils#isLockedAndReadOnly(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.lock.LockService)
*
* @param nodeRef the node reference
*/
public boolean isLockedAndReadOnly(final NodeRef nodeRef)
{
return LockUtils.isLockedAndReadOnly(nodeRef, m_serviceRegistry.getLockService());
}
}