001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.webdav; 029 030import org.opencms.main.CmsLog; 031import org.opencms.main.OpenCms; 032import org.opencms.repository.A_CmsRepository; 033 034import java.io.IOException; 035import java.io.InputStream; 036import java.util.Collections; 037import java.util.Enumeration; 038import java.util.LinkedHashMap; 039 040import javax.servlet.ServletConfig; 041import javax.servlet.ServletContext; 042import javax.servlet.ServletException; 043import javax.servlet.http.HttpServletRequest; 044import javax.servlet.http.HttpServletResponse; 045 046import org.apache.commons.collections4.EnumerationUtils; 047import org.apache.commons.logging.Log; 048import org.apache.jackrabbit.webdav.DavLocatorFactory; 049import org.apache.jackrabbit.webdav.DavResource; 050import org.apache.jackrabbit.webdav.DavResourceFactory; 051import org.apache.jackrabbit.webdav.DavServletRequest; 052import org.apache.jackrabbit.webdav.DavSessionProvider; 053import org.apache.jackrabbit.webdav.WebdavRequest; 054import org.apache.jackrabbit.webdav.header.IfHeader; 055import org.apache.jackrabbit.webdav.io.InputContext; 056import org.apache.jackrabbit.webdav.lock.LockManager; 057import org.apache.jackrabbit.webdav.lock.SimpleLockManager; 058import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet; 059 060import com.google.common.collect.Iterators; 061 062/** 063 * Webdav access servlet for OpenCms, implemented using jackrabbit-webdav library. 064 */ 065public class CmsJackrabbitWebdavServlet extends AbstractWebdavServlet { 066 067 /** Logger instance for this class. */ 068 private static final Log LOG = CmsLog.getLog(CmsJackrabbitWebdavServlet.class); 069 070 /** Serial version id. */ 071 private static final long serialVersionUID = 1L; 072 073 /** The locator factory. */ 074 private CmsDavLocatorFactory m_locatorFactory = new CmsDavLocatorFactory(); 075 076 /** The session provider. */ 077 private CmsDavSessionProvider m_sessionProvider = new CmsDavSessionProvider(); 078 079 /** The resource factory. */ 080 private CmsDavResourceFactory m_resourceFactory = new CmsDavResourceFactory(); 081 082 /** The lock manager. */ 083 private LockManager m_lockManager = new SimpleLockManager(); 084 085 /** The repository. */ 086 private A_CmsRepository m_repository; 087 088 /** 089 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#getDavSessionProvider() 090 */ 091 @Override 092 public DavSessionProvider getDavSessionProvider() { 093 094 return m_sessionProvider; 095 } 096 097 /** 098 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#getLocatorFactory() 099 */ 100 @Override 101 public DavLocatorFactory getLocatorFactory() { 102 103 return m_locatorFactory; 104 } 105 106 /** 107 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#getResourceFactory() 108 */ 109 @Override 110 public DavResourceFactory getResourceFactory() { 111 112 return m_resourceFactory; 113 } 114 115 /** 116 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig) 117 */ 118 @Override 119 public void init(ServletConfig config) throws ServletException { 120 121 final LinkedHashMap<String, String> params = new LinkedHashMap<>(); 122 for (String name : Collections.list(config.getInitParameterNames())) { 123 params.put(name, config.getInitParameter(name)); 124 } 125 // Force relative URI 126 params.put(AbstractWebdavServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false"); 127 super.init(new ServletConfig() { 128 129 public String getInitParameter(String name) { 130 131 return params.get(name); 132 133 } 134 135 public Enumeration<String> getInitParameterNames() { 136 137 return Collections.enumeration(params.keySet()); 138 } 139 140 public ServletContext getServletContext() { 141 142 return config.getServletContext(); 143 } 144 145 public String getServletName() { 146 147 return config.getServletName(); 148 } 149 }); 150 String repName = config.getInitParameter(CmsDavUtil.PARAM_REPOSITORY); 151 A_CmsRepository repository = OpenCms.getRepositoryManager().getRepository(repName, A_CmsRepository.class); 152 m_repository = repository; 153 m_sessionProvider.setRepository(repository); 154 m_resourceFactory.setLockManager(m_lockManager); 155 } 156 157 /** 158 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#setDavSessionProvider(org.apache.jackrabbit.webdav.DavSessionProvider) 159 */ 160 @Override 161 public void setDavSessionProvider(DavSessionProvider davSessionProvider) { 162 163 throw new UnsupportedOperationException(); 164 } 165 166 /** 167 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#setLocatorFactory(org.apache.jackrabbit.webdav.DavLocatorFactory) 168 */ 169 @Override 170 public void setLocatorFactory(DavLocatorFactory locatorFactory) { 171 172 throw new UnsupportedOperationException(); 173 174 } 175 176 /** 177 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#setResourceFactory(org.apache.jackrabbit.webdav.DavResourceFactory) 178 */ 179 @Override 180 public void setResourceFactory(DavResourceFactory resourceFactory) { 181 182 throw new UnsupportedOperationException(); 183 } 184 185 /** 186 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#getInputContext(org.apache.jackrabbit.webdav.DavServletRequest, java.io.InputStream) 187 */ 188 @Override 189 protected InputContext getInputContext(DavServletRequest request, InputStream in) { 190 191 return new CmsDavInputContext(request, in); 192 } 193 194 /** 195 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#isPreconditionValid(org.apache.jackrabbit.webdav.WebdavRequest, org.apache.jackrabbit.webdav.DavResource) 196 */ 197 @Override 198 protected boolean isPreconditionValid(WebdavRequest request, DavResource resource) { 199 200 IfHeader header = new IfHeader(request); 201 String[] tokens = Iterators.toArray(header.getAllTokens(), String.class); 202 for (String token : tokens) { 203 if ("DAV:no-lock".equals(token)) { 204 return false; 205 } 206 } 207 return !resource.exists() || request.matchesIfHeader(resource); 208 } 209 210 /** 211 * @see org.apache.jackrabbit.webdav.server.AbstractWebdavServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 212 */ 213 @Override 214 protected void service(HttpServletRequest request, HttpServletResponse response) 215 throws ServletException, IOException { 216 217 LOG.debug("WEBDAV: " + request.getMethod() + " " + request.getRequestURI()); 218 //printHeaderInfo(request); 219 if (Boolean.parseBoolean(m_repository.getConfiguration().getString("failOnRangeHeader", "false"))) { 220 // The MacOS WebDav client uses range requests and seems to assume they work even if the server sends a HTTP status of 200 221 // in response (rather than 206 Partial Content). This can cause big files to be corrupted. To prevent this, 222 // we send an empty response with status 400 when detecting a range request. 223 String range = request.getHeader("range"); 224 if (range != null) { 225 response.setStatus(400); 226 return; 227 } 228 } 229 try { 230 super.service(request, response); 231 } catch (ServletException | IOException e) { 232 LOG.error(e.getLocalizedMessage(), e); 233 throw e; 234 } catch (Exception e) { 235 LOG.error(e.getLocalizedMessage(), e); 236 throw new RuntimeException(e); 237 } 238 } 239 240 /** 241 * Debug method for printing header information. 242 * 243 * @param request the current request 244 */ 245 private void printHeaderInfo(HttpServletRequest request) { 246 247 System.out.println("--------------- " + request.getMethod() + " " + request.getRequestURI()); 248 Enumeration<String> hnames = request.getHeaderNames(); 249 while (hnames.hasMoreElements()) { 250 String name = hnames.nextElement(); 251 System.out.println(name + ": " + EnumerationUtils.toList(request.getHeaders(name))); 252 } 253 } 254 255}