From 3c4c3d357c79ede2cdc6aafffec0a2912cf8a82a Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Fri, 8 Nov 2013 10:42:18 +0100 Subject: [PATCH] OO-798, OO-797, OO-796: update the webdav implementation based on Tomcat, make OpenOLAt work under root context, small refactoring of the dispatchers, push raw files dispatcher to its own servlet, implements Digest authentication... --- pom.xml | 15 +- .../bps/olat/user/ChangeEMailController.java | 12 +- .../org/olat/admin/AdminModuleDispatcher.java | 8 +- .../datasources/ThreadAndControllerInfo.java | 4 +- .../site/ui/SitesConfigurationController.java | 3 +- .../sysinfo/CoreFunctionsController.java | 5 +- .../admin/sysinfo/MRTGStatsDispatcher.java | 28 +- .../olat/admin/sysinfo/SysinfoController.java | 3 +- .../admin/sysinfo/_spring/sysinfoContext.xml | 4 +- .../sysinfo/manager/SessionStatsManager.java | 17 + .../user/UsermanagerUserSearchController.java | 4 +- .../org/olat/basesecurity/AuthHelper.java | 6 +- .../basesecurity/BaseSecurityManager.java | 4 +- .../olat/catalog/ui/CatalogController.java | 4 +- .../olat/commons/servlets/ICalServlet.java | 51 +- .../org/olat/commons/servlets/RSSServlet.java | 45 +- .../servlets/StaticsLegacyDispatcher.java | 13 +- .../contextHelp/ContextHelpDispatcher.java | 9 +- .../ContextHelpMainController.java | 4 +- .../fullWebApp/DefaultTopNavController.java | 4 +- .../modules/bc/FolderRunController.java | 5 +- .../services/webdav/DefaultServlet.java | 2353 --------------- .../services/webdav/WebDAVDispatcher.java} | 44 +- .../services/webdav/WebDAVManager.java | 27 +- .../commons/services/webdav/WebDAVModule.java | 136 + .../webdav/WebDAVProviderFactory.java | 91 - .../services/webdav/_spring/webdavContext.xml | 72 +- .../webdav/manager/DigestAuthentication.java | 122 + .../webdav/manager/EmptyWebResource.java | 100 + .../webdav/manager/VFSDirContext.java | 1236 -------- .../services/webdav/manager/VFSResource.java | 121 + .../webdav/manager/VFSResourceRoot.java | 262 ++ .../webdav/manager/WebDAVAuthManager.java | 85 +- .../webdav/manager/WebDAVManagerImpl.java | 213 +- .../WebDAVProviderNamedContainer.java | 3 +- .../webdav/servlets/ConcurrentDateFormat.java | 75 + .../services/webdav/servlets/DOMWriter.java | 239 ++ .../webdav/servlets/DefaultDispatcher.java | 1314 ++++++++ .../webdav/servlets/FastHttpDateFormat.java | 229 ++ .../webdav}/servlets/RequestUtil.java | 13 +- .../services/webdav/servlets/WebResource.java | 99 + .../webdav/servlets/WebResourceRoot.java | 89 + .../WebdavDispatcher.java} | 2630 ++++++++--------- .../services/webdav}/servlets/XMLWriter.java | 75 +- .../webdav/ui/WebDAVAdminController.java | 86 + .../webdav/ui/_chelp/webdavconfig.html | 1 + .../ui/_i18n/LocalStrings_de.properties | 9 + .../org/olat/core/dispatcher/Dispatcher.java | 5 +- ...tcherAction.java => DispatcherModule.java} | 214 +- .../core/dispatcher/ErrorFeedbackMailer.java | 5 +- .../dispatcher/_spring/dispatcherContext.xml | 33 +- .../impl/RedirectToDefaultDispatcher.java | 7 +- .../impl/StaticMediaDispatcher.java | 159 +- .../mapper/GlobalMapperRegistry.java | 13 +- .../dispatcher/mapper/MapperDispatcher.java | 22 +- .../mapper/manager/MapperServiceImpl.java | 12 +- .../org/olat/core/gui/UserRequestImpl.java | 3 +- .../form/flexible/elements/TextElement.java | 3 +- .../components/form/flexible/impl/Form.java | 46 +- .../gui/control/winmgr/AjaxController.java | 4 +- .../org/olat/core/gui/media/ServletUtil.java | 68 +- .../java/org/olat/core/helpers/Settings.java | 12 + .../org/olat/core/servlets/OLATServlet.java | 168 -- .../olat/core/servlets/OpenOLATServlet.java | 265 ++ .../core/servlets/ServletWrapperFilter.java | 4 +- .../org/olat/core/servlets/StaticServlet.java | 137 + .../java/org/olat/core/util/CodeHelper.java | 2 +- src/main/java/org/olat/core/util/Encoder.java | 19 +- .../java/org/olat/core/util/FileUtils.java | 1 - .../java/org/olat/core/util/URIHelper.java | 36 - .../java/org/olat/core/util/UserSession.java | 10 + .../core/util/ValidationStatusHelper.java | 73 - .../core/util/_spring/utilCorecontext.xml | 3 +- .../util/httpclient/EasySSLSocketFactory.java | 4 +- .../ui/I18nConfigSubNewLangController.java | 4 +- .../olat/core/util/servlets/DOMWriter.java | 316 -- .../util/servlets/FastHttpDateFormat.java | 122 - .../org/olat/core/util/servlets/Globals.java | 253 -- .../olat/core/util/servlets/MIME2Java.java | 612 ---- .../olat/core/util/servlets/ServerInfo.java | 82 - .../olat/core/util/servlets/URLEncoder.java | 2 +- .../org/olat/core/util/vfs/VFSStatus.java | 7 +- .../dispatcher/AuthenticatedDispatcher.java | 122 +- .../CatalogExportModuleDispatcher.java | 4 +- .../org/olat/dispatcher/DMZDispatcher.java | 86 +- .../org/olat/dispatcher/RESTDispatcher.java | 38 +- .../RedirectToAutoGuestLoginDispatcher.java | 12 +- .../dispatcher/RemoteLoginformDispatcher.java | 26 +- .../gui/control/OlatTopNavController.java | 4 +- .../ldap/ui/LDAPAuthenticationController.java | 4 +- .../login/LoginAuthprovidersController.java | 4 +- .../login/OLATAuthenticationController.java | 63 +- .../login/_i18n/LocalStrings_fr.properties | 2 +- .../olat/login/auth/AuthenticationSPI.java | 2 + .../org/olat/login/auth/OLATAuthManager.java | 19 +- .../dispatching/FeedMediaDispatcher.java | 12 +- .../olat/registration/PwChangeController.java | 4 +- .../RepositoryDetailsController.java | 4 +- .../controllers/RepositoryMainController.java | 1 - .../RepositorySearchController.java | 4 +- .../restapi/security/RestApiLoginFilter.java | 4 +- .../system/OpenOLATStatisticsWebService.java | 4 +- ...ultShibbolethAuthenticationController.java | 6 +- .../ShibbolethAuthenticationController.java | 4 +- .../olat/shibboleth/ShibbolethDispatcher.java | 20 +- .../ShibbolethRegistrationController.java | 4 +- .../olat/user/PersonalSettingsController.java | 6 +- .../resources/serviceconfig/olat.properties | 4 + src/main/webapp-gae/WEB-INF/web.xml | 53 +- src/main/webapp-jbossas7/WEB-INF/web.xml | 49 +- src/main/webapp-tomcat/WEB-INF/web.xml | 68 +- .../webdav/HttpPropFind.java | 2 +- .../services/webdav/WebDAVCommandsTest.java | 67 + .../webdav/WebDAVConnection.java | 37 +- .../webdav/WebDAVTestCase.java | 39 +- .../webdav/manager/WebDAVManagerTest.java | 17 +- .../java/org/olat/test/AllTestsJunit4.java | 2 +- 117 files changed, 5579 insertions(+), 7916 deletions(-) delete mode 100644 src/main/java/org/olat/core/commons/services/webdav/DefaultServlet.java rename src/main/java/org/olat/core/{util/filter/ChainedFilter.java => commons/services/webdav/WebDAVDispatcher.java} (53%) create mode 100644 src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java delete mode 100644 src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderFactory.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/manager/DigestAuthentication.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/manager/EmptyWebResource.java delete mode 100644 src/main/java/org/olat/core/commons/services/webdav/manager/VFSDirContext.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/manager/VFSResource.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/manager/VFSResourceRoot.java rename src/main/java/org/olat/core/commons/services/webdav/{ => manager}/WebDAVProviderNamedContainer.java (94%) create mode 100644 src/main/java/org/olat/core/commons/services/webdav/servlets/ConcurrentDateFormat.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/servlets/DOMWriter.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/servlets/FastHttpDateFormat.java rename src/main/java/org/olat/core/{util => commons/services/webdav}/servlets/RequestUtil.java (96%) create mode 100644 src/main/java/org/olat/core/commons/services/webdav/servlets/WebResource.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/servlets/WebResourceRoot.java rename src/main/java/org/olat/core/commons/services/webdav/{SecureWebdavServlet.java => servlets/WebdavDispatcher.java} (50%) rename src/main/java/org/olat/core/{util => commons/services/webdav}/servlets/XMLWriter.java (70%) create mode 100644 src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java create mode 100644 src/main/java/org/olat/core/commons/services/webdav/ui/_chelp/webdavconfig.html create mode 100644 src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties rename src/main/java/org/olat/core/dispatcher/{DispatcherAction.java => DispatcherModule.java} (51%) delete mode 100644 src/main/java/org/olat/core/servlets/OLATServlet.java create mode 100644 src/main/java/org/olat/core/servlets/OpenOLATServlet.java create mode 100644 src/main/java/org/olat/core/servlets/StaticServlet.java delete mode 100644 src/main/java/org/olat/core/util/ValidationStatusHelper.java delete mode 100644 src/main/java/org/olat/core/util/servlets/DOMWriter.java delete mode 100644 src/main/java/org/olat/core/util/servlets/FastHttpDateFormat.java delete mode 100644 src/main/java/org/olat/core/util/servlets/Globals.java delete mode 100644 src/main/java/org/olat/core/util/servlets/MIME2Java.java delete mode 100644 src/main/java/org/olat/core/util/servlets/ServerInfo.java rename src/test/java/org/olat/core/commons/{service => services}/webdav/HttpPropFind.java (97%) create mode 100644 src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java rename src/test/java/org/olat/core/commons/{service => services}/webdav/WebDAVConnection.java (72%) rename src/test/java/org/olat/core/commons/{service => services}/webdav/WebDAVTestCase.java (69%) diff --git a/pom.xml b/pom.xml index 37c2019606f..00d2720e091 100644 --- a/pom.xml +++ b/pom.xml @@ -1755,8 +1755,8 @@ <!-- J2EE dependencies but provided --> <dependency> <groupId>javax.servlet</groupId> - <artifactId>servlet-api</artifactId> - <version>2.5</version> + <artifactId>javax.servlet-api</artifactId> + <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> @@ -1766,17 +1766,6 @@ <scope>test</scope> </dependency> <!-- core dependencies --> - <dependency> - <groupId>tomcat</groupId> - <artifactId>naming-resources</artifactId> - <version>5.5.15</version> - <exclusions> - <exclusion> - <groupId>commons-logging</groupId> - <artifactId>commons-logging-api</artifactId> - </exclusion> - </exclusions> - </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> diff --git a/src/main/java/de/bps/olat/user/ChangeEMailController.java b/src/main/java/de/bps/olat/user/ChangeEMailController.java index e90be1c4604..8d830a4978c 100644 --- a/src/main/java/de/bps/olat/user/ChangeEMailController.java +++ b/src/main/java/de/bps/olat/user/ChangeEMailController.java @@ -23,7 +23,7 @@ import java.util.Calendar; import java.util.Date; import java.util.HashMap; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.control.DefaultController; @@ -85,7 +85,7 @@ public class ChangeEMailController extends DefaultController { if (tempKey == null) { // registration key not available userRequest.getUserSession().putEntryInNonClearedStore("error.change.email", pT.translate("error.change.email")); - DispatcherAction.redirectToDefaultDispatcher(userRequest.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(userRequest.getHttpResp()); return; } else { if (!isLinkTimeUp()) { @@ -93,16 +93,16 @@ public class ChangeEMailController extends DefaultController { if ((userRequest.getUserSession().getEntry(CHANGE_EMAIL_ENTRY) == null) || (!userRequest.getUserSession().getEntry(CHANGE_EMAIL_ENTRY).equals(CHANGE_EMAIL_ENTRY))) { userRequest.getUserSession().putEntryInNonClearedStore(CHANGE_EMAIL_ENTRY, CHANGE_EMAIL_ENTRY); - DispatcherAction.redirectToDefaultDispatcher(userRequest.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(userRequest.getHttpResp()); return; } else { if (userRequest.getIdentity() == null) { - DispatcherAction.redirectToDefaultDispatcher(userRequest.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(userRequest.getHttpResp()); return; } } } catch (ClassCastException e) { - DispatcherAction.redirectToDefaultDispatcher(userRequest.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(userRequest.getHttpResp()); return; } } else { @@ -117,7 +117,7 @@ public class ChangeEMailController extends DefaultController { } // delete registration key rm.deleteTemporaryKeyWithId(tempKey.getRegistrationKey()); - DispatcherAction.redirectToDefaultDispatcher(userRequest.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(userRequest.getHttpResp()); return; } } diff --git a/src/main/java/org/olat/admin/AdminModuleDispatcher.java b/src/main/java/org/olat/admin/AdminModuleDispatcher.java index f6914302df8..2ddd2ecb8a5 100644 --- a/src/main/java/org/olat/admin/AdminModuleDispatcher.java +++ b/src/main/java/org/olat/admin/AdminModuleDispatcher.java @@ -31,7 +31,7 @@ import javax.servlet.http.HttpServletResponse; import org.olat.admin.sysinfo.InfoMessageManager; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.media.ServletUtil; import org.olat.core.util.session.UserSessionManager; @@ -67,7 +67,7 @@ public class AdminModuleDispatcher implements Dispatcher { * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) */ @Override - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + public void execute(HttpServletRequest request, HttpServletResponse response) { String cmd = request.getParameter(PARAMETER_CMD); if (cmd.equalsIgnoreCase(CMD_SET_MAINTENANCE_MESSAGE) || cmd.equalsIgnoreCase(CMD_SET_INFO_MESSAGE)) { handleSetMaintenanceOrInfoMessage(request, response, cmd); @@ -75,7 +75,7 @@ public class AdminModuleDispatcher implements Dispatcher { if (CoreSpringFactory.getImpl(AdminModule.class).checkSessionAdminToken(request, response)) { handleSessionsCommand(request, response, cmd); } else { - DispatcherAction.sendForbidden(request.getPathInfo(), response); + DispatcherModule.sendForbidden(request.getPathInfo(), response); } } } @@ -179,7 +179,7 @@ public class AdminModuleDispatcher implements Dispatcher { ServletUtil.serveStringResource(request, response, "Ok, new maintenanceMessage is::" + message); } } else { - DispatcherAction.sendForbidden(request.getPathInfo(), response); + DispatcherModule.sendForbidden(request.getPathInfo(), response); } } diff --git a/src/main/java/org/olat/admin/jmx/datasources/ThreadAndControllerInfo.java b/src/main/java/org/olat/admin/jmx/datasources/ThreadAndControllerInfo.java index 1d9f6e12bcc..ed6dfab9c52 100644 --- a/src/main/java/org/olat/admin/jmx/datasources/ThreadAndControllerInfo.java +++ b/src/main/java/org/olat/admin/jmx/datasources/ThreadAndControllerInfo.java @@ -27,8 +27,8 @@ package org.olat.admin.jmx.datasources; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; +import org.olat.admin.sysinfo.manager.SessionStatsManager; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; import org.olat.core.gui.control.DefaultController; import org.olat.core.util.session.UserSessionManager; @@ -44,7 +44,7 @@ public class ThreadAndControllerInfo { private static MemoryMXBean mbean = ManagementFactory.getMemoryMXBean(); public int getConcurrentThreads() { - return DispatcherAction.getConcurrentCounter(); + return CoreSpringFactory.getImpl(SessionStatsManager.class).getConcurrentCounter(); } public int getControllerCount() { diff --git a/src/main/java/org/olat/admin/site/ui/SitesConfigurationController.java b/src/main/java/org/olat/admin/site/ui/SitesConfigurationController.java index e15ea21f9e1..1a8e3defd73 100644 --- a/src/main/java/org/olat/admin/site/ui/SitesConfigurationController.java +++ b/src/main/java/org/olat/admin/site/ui/SitesConfigurationController.java @@ -20,6 +20,7 @@ package org.olat.admin.site.ui; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -52,8 +53,6 @@ import org.olat.core.gui.control.navigation.SiteSecurityCallback; import org.olat.core.gui.control.navigation.SiteViewSecurityCallback; import org.olat.core.util.StringHelper; -import edu.emory.mathcs.backport.java.util.Collections; - /** * Configuration of the list of sites: order, security callback, alternative controllers... * diff --git a/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java b/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java index 26c3dbe1ecd..f5392de3630 100644 --- a/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java +++ b/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java @@ -21,7 +21,7 @@ package org.olat.admin.sysinfo; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.chiefcontrollers.BaseChiefController; -import org.olat.core.commons.services.webdav.WebDAVManager; +import org.olat.core.commons.services.webdav.WebDAVModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; @@ -45,6 +45,7 @@ public class CoreFunctionsController extends FormBasicController { */ public CoreFunctionsController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl, "coreinfo"); + initForm(ureq); } @@ -60,7 +61,7 @@ public class CoreFunctionsController extends FormBasicController { MultipleSelectionElement clusterEl = uifactory.addCheckboxesHorizontal("webdav", "core.webdav", serverCont, new String[]{"xx"}, new String[]{""}, null); clusterEl.setEnabled(false); - clusterEl.select("xx", WebDAVManager.getInstance().isEnabled()); + clusterEl.select("xx", CoreSpringFactory.getImpl(WebDAVModule.class).isEnabled()); MultipleSelectionElement jsMathEl = uifactory.addCheckboxesHorizontal("jsmath", "core.jsMath", serverCont, new String[]{"xx"}, new String[]{""}, null); diff --git a/src/main/java/org/olat/admin/sysinfo/MRTGStatsDispatcher.java b/src/main/java/org/olat/admin/sysinfo/MRTGStatsDispatcher.java index 7cd90f8eb86..c892bb8a4d5 100644 --- a/src/main/java/org/olat/admin/sysinfo/MRTGStatsDispatcher.java +++ b/src/main/java/org/olat/admin/sysinfo/MRTGStatsDispatcher.java @@ -42,6 +42,7 @@ import java.util.logging.Level; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.olat.admin.sysinfo.manager.SessionStatsManager; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.Constants; @@ -51,10 +52,11 @@ import org.olat.commons.coordinate.cluster.jms.SimpleProbe; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBQueryImpl; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.control.DefaultController; import org.olat.core.gui.media.ServletUtil; import org.olat.core.id.Identity; +import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.SessionInfo; import org.olat.core.util.UserSession; @@ -79,6 +81,9 @@ import org.olat.testutils.codepoints.server.Codepoint; * @author patrickb */ public class MRTGStatsDispatcher implements Dispatcher { + + private static final OLog log = Tracing.createLoggerFor(MRTGStatsDispatcher.class); + // default allows monitoring only from localhost // "*" means allow from any host (not recommended in real world setups) private String monitoringHost = "127.0.0.1"; @@ -117,18 +122,20 @@ public class MRTGStatsDispatcher implements Dispatcher { doInSyncInsideProbe_.logifSlowerThan(1000, Level.WARNING); dbQueryListProbe_.logifSlowerThan(1300, Level.WARNING); } catch(RuntimeException re) { - Tracing.logInfo("Certain MRTG Statistics will not be available since Codepoints are disabled", getClass()); + log.info("Certain MRTG Statistics will not be available since Codepoints are disabled"); } catch (CommunicationException e) { - Tracing.logInfo("Certain MRTG Statistics will not be available since Codepoints are disabled", getClass()); + log.info("Certain MRTG Statistics will not be available since Codepoints are disabled"); } } /** * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { - if(Tracing.isDebugEnabled(MRTGStatsDispatcher.class)){ - Tracing.logDebug("serving MRTGStats on uriPrefix [[["+uriPrefix+"]]]", MRTGStatsDispatcher.class); + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { + if(log.isDebug()){ + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); + log.debug("serving MRTGStats on uriPrefix [[["+uriPrefix+"]]]"); } returnMRTGStats(request, response); } @@ -162,8 +169,8 @@ public class MRTGStatsDispatcher implements Dispatcher { private void returnMRTGStats(HttpServletRequest request, HttpServletResponse response) { if (!request.getRemoteAddr().equals(monitoringHost) && !monitoringHost.equals("*")) { // limit to allowed hosts - Tracing.logAudit("Trying to access stats from other host than configured (" + monitoringHost + ") : " + request.getRemoteAddr(), SysinfoController.class); - DispatcherAction.sendForbidden(request.getPathInfo(), response); + log.audit("Trying to access stats from other host than configured (" + monitoringHost + ") : " + request.getRemoteAddr()); + DispatcherModule.sendForbidden(request.getPathInfo(), response); } String command = request.getParameter("cmd"); if (command == null) command = "users"; @@ -234,7 +241,8 @@ public class MRTGStatsDispatcher implements Dispatcher { } else if (command.equals("proc")) { // get VM process stats // Number of concurrent dispatching OLAT threads (concurrent user requests) - result.append(DispatcherAction.getConcurrentCounter()).append("\n"); + SessionStatsManager statsManager = CoreSpringFactory.getImpl(SessionStatsManager.class); + result.append(statsManager.getConcurrentCounter()).append("\n"); // Number of active threads ThreadGroup group = Thread.currentThread().getThreadGroup(); Thread[] threads = new Thread[ group.activeCount() ]; @@ -528,7 +536,7 @@ public class MRTGStatsDispatcher implements Dispatcher { }); for (Iterator<Entry<String, org.olat.core.commons.persistence.SimpleProbe>> it = list.iterator(); it.hasNext();) { Entry<String, org.olat.core.commons.persistence.SimpleProbe> entry = it.next(); - Tracing.logInfo("MRTGStats: table '"+entry.getKey()+"' uses up "+entry.getValue().getSum()+"ms of a total of "+sum+"ms, which is "+Math.round(1000.0*entry.getValue().getSum()/sum)/10+"%", getClass()); + log.info("MRTGStats: table '"+entry.getKey()+"' uses up "+entry.getValue().getSum()+"ms of a total of "+sum+"ms, which is "+Math.round(1000.0*entry.getValue().getSum()/sum)/10+"%"); entry.getValue().reset(); } } else if (command.equals("LifeCycleEntry")) { // LifeCycleEntry diff --git a/src/main/java/org/olat/admin/sysinfo/SysinfoController.java b/src/main/java/org/olat/admin/sysinfo/SysinfoController.java index 8f92857d365..a65efa965f4 100644 --- a/src/main/java/org/olat/admin/sysinfo/SysinfoController.java +++ b/src/main/java/org/olat/admin/sysinfo/SysinfoController.java @@ -36,7 +36,6 @@ import org.olat.admin.sysinfo.manager.SessionStatsManager; import org.olat.admin.sysinfo.model.SessionsStats; import org.olat.basesecurity.BaseSecurity; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; @@ -122,7 +121,7 @@ public class SysinfoController extends FormBasicController { //controllers int controllerCnt = DefaultController.getControllerCount(); uifactory.addStaticTextElement("controllercount", "runtime.controllercount", Integer.toString(controllerCnt), runtimeCont); - int numOfDispatchingThreads = DispatcherAction.getConcurrentCounter(); + int numOfDispatchingThreads = sessionStatsManager.getConcurrentCounter(); uifactory.addStaticTextElement("dispatchingthreads", "runtime.dispatchingthreads", Integer.toString(numOfDispatchingThreads), runtimeCont); //sessions and clicks diff --git a/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml b/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml index 635808a1238..7c3f0250e48 100644 --- a/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml +++ b/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml @@ -4,9 +4,9 @@ xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.olat.admin.sysinfo" /> diff --git a/src/main/java/org/olat/admin/sysinfo/manager/SessionStatsManager.java b/src/main/java/org/olat/admin/sysinfo/manager/SessionStatsManager.java index abc4f70e192..23360828dc8 100644 --- a/src/main/java/org/olat/admin/sysinfo/manager/SessionStatsManager.java +++ b/src/main/java/org/olat/admin/sysinfo/manager/SessionStatsManager.java @@ -21,6 +21,7 @@ package org.olat.admin.sysinfo.manager; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import org.olat.admin.sysinfo.model.SessionStatsSample; import org.olat.admin.sysinfo.model.SessionsStats; @@ -45,6 +46,7 @@ public class SessionStatsManager implements Sampler { private UserSessionManager sessionManager; private SessionStatsSample currentSample; + private AtomicInteger concurrentCounter = new AtomicInteger(0); private List<SessionStatsSample> sessionStatsSamples = new ArrayList<SessionStatsSample>(); public List<SessionStatsSample> getSessionViews() { @@ -72,6 +74,21 @@ public class SessionStatsManager implements Sampler { currentSample.incrementRequest(); } + /** + * @return the number of requests currently handled + */ + public int getConcurrentCounter() { + return concurrentCounter.intValue(); + } + + public void incrementConcurrentCounter() { + concurrentCounter.incrementAndGet(); + } + + public void decrementConcurrentCounter() { + concurrentCounter.decrementAndGet(); + } + public synchronized long getNumOfSessions() { if(currentSample != null) { return currentSample.getNumOfSessions(); diff --git a/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java b/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java index cef7c6258b4..43fa5920ced 100644 --- a/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java +++ b/src/main/java/org/olat/admin/user/UsermanagerUserSearchController.java @@ -46,7 +46,7 @@ import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.persistence.PersistenceHelper; -import org.olat.core.commons.services.webdav.WebDAVManager; +import org.olat.core.commons.services.webdav.WebDAVModule; import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -691,7 +691,7 @@ class UsermanagerUserSearchForm extends FormBasicController { )); } } - if(WebDAVManager.getInstance().isEnabled()) { + if(CoreSpringFactory.getImpl(WebDAVModule.class).isEnabled()) { authKeyList.add(WebDAVAuthManager.PROVIDER_WEBDAV); authValueList.add(translate("search.form.constraint.auth.WEBDAV")); } diff --git a/src/main/java/org/olat/basesecurity/AuthHelper.java b/src/main/java/org/olat/basesecurity/AuthHelper.java index 81c2efda55a..bb23b752f51 100644 --- a/src/main/java/org/olat/basesecurity/AuthHelper.java +++ b/src/main/java/org/olat/basesecurity/AuthHelper.java @@ -43,7 +43,7 @@ import org.olat.core.commons.chiefcontrollers.BaseChiefControllerCreator; import org.olat.core.commons.fullWebApp.BaseFullWebappController; import org.olat.core.commons.fullWebApp.BaseFullWebappControllerParts; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.GUIInterna; import org.olat.core.gui.UserRequest; import org.olat.core.gui.WindowManager; @@ -136,12 +136,12 @@ public class AuthHelper { } Window currentWindow = occ.getWindow(); - currentWindow.setUriPrefix(WebappHelper.getServletContextPath() + DispatcherAction.PATH_AUTHENTICATED); + currentWindow.setUriPrefix(WebappHelper.getServletContextPath() + DispatcherModule.PATH_AUTHENTICATED); Windows.getWindows(ureq).registerWindow(currentWindow); // redirect to AuthenticatedDispatcher // IMPORTANT: windowID has changed due to re-registering current window -> do not use ureq.getWindowID() to build new URLBuilder. - URLBuilder ubu = new URLBuilder(WebappHelper.getServletContextPath() + DispatcherAction.PATH_AUTHENTICATED, currentWindow.getInstanceId(), "1", null); + URLBuilder ubu = new URLBuilder(WebappHelper.getServletContextPath() + DispatcherModule.PATH_AUTHENTICATED, currentWindow.getInstanceId(), "1", null); StringOutput sout = new StringOutput(30); ubu.buildURI(sout, null, null); ureq.getDispatchResult().setResultingMediaResource(new RedirectMediaResource(sout.toString())); diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 8debfc9ac14..d8e5fea3712 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -1445,7 +1445,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { Authentication auth = findAuthentication(ident, provider); if(auth == null) { if(algorithm != null) { - String salt = Encoder.getSalt(); + String salt = algorithm.isSalted() ? Encoder.getSalt() : null; String hash = Encoder.encrypt(credentials, salt, algorithm); auth = new AuthenticationImpl(ident, provider, authUserName, hash, salt, algorithm.name()); } else { @@ -1534,7 +1534,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { @Override public Authentication updateCredentials(Authentication authentication, String password, Algorithm algorithm) { - String salt = Encoder.getSalt(); + String salt = algorithm.isSalted() ? Encoder.getSalt() : null; String newCredentials = Encoder.encrypt(password, salt, algorithm); authentication.setSalt(salt); authentication.setCredential(newCredentials); diff --git a/src/main/java/org/olat/catalog/ui/CatalogController.java b/src/main/java/org/olat/catalog/ui/CatalogController.java index 383b8496f91..9a041c7753a 100644 --- a/src/main/java/org/olat/catalog/ui/CatalogController.java +++ b/src/main/java/org/olat/catalog/ui/CatalogController.java @@ -44,7 +44,7 @@ import org.olat.catalog.CatalogManager; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.services.mark.MarkManager; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.image.ImageComponent; @@ -402,7 +402,7 @@ public class CatalogController extends BasicController implements Activateable2 * login link clicked */ else if (source == loginLink){ - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } diff --git a/src/main/java/org/olat/commons/servlets/ICalServlet.java b/src/main/java/org/olat/commons/servlets/ICalServlet.java index d3d3c789dfc..59928fc40ec 100644 --- a/src/main/java/org/olat/commons/servlets/ICalServlet.java +++ b/src/main/java/org/olat/commons/servlets/ICalServlet.java @@ -35,17 +35,19 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.data.CalendarOutputter; +import net.fortuna.ical4j.model.Calendar; import net.fortuna.ical4j.model.ValidationException; - +import org.apache.poi.util.IOUtils; import org.olat.commons.calendar.CalendarManager; import org.olat.commons.calendar.CalendarManagerFactory; import org.olat.commons.calendar.ICalTokenGenerator; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; +import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.i18n.I18nManager; /** @@ -57,6 +59,9 @@ import org.olat.core.logging.Tracing; * @author Udit Sajjanhar */ public class ICalServlet extends HttpServlet { + + private static final long serialVersionUID = -155266285395912535L; + private static final OLog log = Tracing.createLoggerFor(ICalServlet.class); private static int outputBufferSize = 2048; private static int inputBufferSize = 2048; @@ -71,17 +76,17 @@ public class ICalServlet extends HttpServlet { */ public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); - Tracing.logInfo("init statics servlet", ICalServlet.class); + log.info("init statics servlet"); try { String bufSize = servletConfig.getInitParameter("input"); inputBufferSize = Integer.parseInt(bufSize); bufSize = servletConfig.getInitParameter("output"); inputBufferSize = Integer.parseInt(bufSize); } catch (Exception e) { - Tracing.logWarn("problem with config parameters for ical servlets:", e, ICalServlet.class); + log.warn("problem with config parameters for ical servlets:", e); } - Tracing.logInfo("input buffer size: " + inputBufferSize, ICalServlet.class); - Tracing.logInfo("output buffer size: " + inputBufferSize, ICalServlet.class); + log.info("input buffer size: " + inputBufferSize); + log.info("output buffer size: " + inputBufferSize); } /** @@ -99,6 +104,7 @@ public class ICalServlet extends HttpServlet { }finally { //consume the userrequest. Tracing.setUreq(null); + I18nManager.remove18nInfoFromThread(); } } @@ -109,9 +115,10 @@ public class ICalServlet extends HttpServlet { Calendar icalDoc = null; ServletOutputStream ostream = null; + final boolean debug = log.isDebug(); try { String pathInfo = request.getPathInfo(); - Tracing.logDebug("doGet pathInfo=" + pathInfo, ICalServlet.class); + if(debug) log.debug("doGet pathInfo=" + pathInfo); if ((pathInfo == null) || (pathInfo.equals(""))) { return; // error } @@ -119,11 +126,11 @@ public class ICalServlet extends HttpServlet { if (checkPath(pathInfo)) { icalDoc = getIcalDocument(pathInfo); if (icalDoc == null) { - DispatcherAction.sendNotFound(pathInfo, response); + DispatcherModule.sendNotFound(pathInfo, response); return; } } else { - DispatcherAction.sendNotFound(pathInfo, response); + DispatcherModule.sendNotFound(pathInfo, response); return; } @@ -141,23 +148,17 @@ public class ICalServlet extends HttpServlet { calOut.output(icalDoc, ostream); } catch (ValidationException e) { // throw olat exception for nice logging - Tracing.logWarn("Validation Error when generate iCal stream for path::" + request.getPathInfo(), e, this.getClass()); - DispatcherAction.sendNotFound("none", response); + log.warn("Validation Error when generate iCal stream for path::" + request.getPathInfo(), e); + DispatcherModule.sendNotFound("none", response); } catch (IOException e) { // throw olat exception for nice logging - Tracing.logWarn("IOException Error when generate iCal stream for path::" + request.getPathInfo(), e, this.getClass()); - DispatcherAction.sendNotFound("none", response); + log.warn("IOException Error when generate iCal stream for path::" + request.getPathInfo(), e); + DispatcherModule.sendNotFound("none", response); } catch (Exception e) { - Tracing.logWarn("Unknown Error in icalservlet", e, this.getClass()); - DispatcherAction.sendNotFound("none", response); - } - finally { - try{ - ostream.close(); - } catch (Exception e) { - // ignore - } - + log.warn("Unknown Error in icalservlet", e); + DispatcherModule.sendNotFound("none", response); + } finally { + IOUtils.closeQuietly(ostream); DBFactory.getInstance(false).commitAndCloseSession(); } } @@ -228,7 +229,7 @@ public class ICalServlet extends HttpServlet { // check the authentication token if (!checkPathAuthenticity(calendarType, userName, authToken, calendarID)) { - Tracing.logWarn("Authenticity Check failed for the ical feed path: " + pathInfo, this.getClass()); + log.warn("Authenticity Check failed for the ical feed path: " + pathInfo); return null; } diff --git a/src/main/java/org/olat/commons/servlets/RSSServlet.java b/src/main/java/org/olat/commons/servlets/RSSServlet.java index 9e6c45126fa..99a623c1c75 100644 --- a/src/main/java/org/olat/commons/servlets/RSSServlet.java +++ b/src/main/java/org/olat/commons/servlets/RSSServlet.java @@ -25,7 +25,6 @@ package org.olat.commons.servlets; -import java.io.IOException; import java.io.Writer; import java.util.Date; @@ -35,11 +34,12 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.pdfbox.io.IOUtils; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurityManager; import org.olat.commons.rss.RSSUtil; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; @@ -59,14 +59,13 @@ import com.sun.syndication.io.SyndFeedOutput; * @author Florian Gnägi */ public class RSSServlet extends HttpServlet { - + + private static final long serialVersionUID = -674630331334472714L; + private static final OLog log = Tracing.createLoggerFor(RSSServlet.class); public static final String DEFAULT_ENCODING = "UTF-8"; - private static int outputBufferSize = 2048; private static int inputBufferSize = 2048; - // TODO:GW How shall we cache the feed? - // private CacheWrapper timerCache; - private static final OLog log = Tracing.createLoggerFor(RSSServlet.class); + /** * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) @@ -126,11 +125,11 @@ public class RSSServlet extends HttpServlet { if (pathInfo.indexOf(RSSUtil.RSS_PREFIX_PERSONAL) == 0) { feed = getPersonalFeed(pathInfo); if (feed == null) { - DispatcherAction.sendNotFound(pathInfo, response); + DispatcherModule.sendNotFound(pathInfo, response); return; } } else { - DispatcherAction.sendNotFound(pathInfo, response); + DispatcherModule.sendNotFound(pathInfo, response); return; } @@ -166,20 +165,15 @@ public class RSSServlet extends HttpServlet { } catch (FeedException e) { // throw olat exception for nice logging log.warn("Error when generating RSS stream for path::" + request.getPathInfo(), e); - DispatcherAction.sendNotFound("none", response); + DispatcherModule.sendNotFound("none", response); } catch (Exception e) { log.warn("Unknown Exception in rssservlet", e); - DispatcherAction.sendNotFound("none", response); + DispatcherModule.sendNotFound("none", response); } catch (Error e) { log.warn("Unknown Error in rssservlet", e); - DispatcherAction.sendNotFound("none", response); + DispatcherModule.sendNotFound("none", response); } finally { - try { - if (writer != null) writer.close(); - } catch (IOException e) { - // writer didn't close properly – ignore - } - + IOUtils.closeQuietly(writer); DBFactory.getInstance(false).commitAndCloseSession(); } } @@ -217,16 +211,9 @@ public class RSSServlet extends HttpServlet { return null; } - // FIXME:GW Make some sensible verifications here (expire date etc.) and - // return a cached file if it exists - SyndFeed cachedFeed = null; - if (cachedFeed != null) { - // return the cache entry - return cachedFeed; - } else { - // create rss feed for user notifications - SyndFeed feed = new PersonalRSSFeed(identity, idToken); - return feed; - } + // create rss feed for user notifications + SyndFeed feed = new PersonalRSSFeed(identity, idToken); + //TODO implements some caching + return feed; } } \ No newline at end of file diff --git a/src/main/java/org/olat/commons/servlets/StaticsLegacyDispatcher.java b/src/main/java/org/olat/commons/servlets/StaticsLegacyDispatcher.java index cc3f41e2d39..b55bb04fda2 100644 --- a/src/main/java/org/olat/commons/servlets/StaticsLegacyDispatcher.java +++ b/src/main/java/org/olat/commons/servlets/StaticsLegacyDispatcher.java @@ -34,8 +34,8 @@ import javax.servlet.http.HttpServletResponse; import org.olat.commons.servlets.pathhandlers.PathHandler; import org.olat.commons.servlets.util.ResourceDescriptor; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; -import org.olat.core.dispatcher.impl.StaticMediaDispatcher; +import org.olat.core.dispatcher.DispatcherModule; +import org.olat.core.gui.media.ServletUtil; import org.olat.core.logging.Tracing; /** @@ -80,7 +80,8 @@ public class StaticsLegacyDispatcher implements Dispatcher { /** * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest req, HttpServletResponse resp, String uriPrefix) { + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) { try { String method = req.getMethod(); if (method.equals("GET")) { @@ -88,14 +89,14 @@ public class StaticsLegacyDispatcher implements Dispatcher { } else if (method.equals("HEAD")) { doHead(req, resp); } else { - DispatcherAction.sendNotFound(req.getRequestURI(), resp); + DispatcherModule.sendNotFound(req.getRequestURI(), resp); } } catch (IOException e) { /* * silently ignore forward errors (except in debug mode), since IE * causes tons of such messages by its double GET request */ - if (Tracing.isDebugEnabled(DispatcherAction.class)) { + if (Tracing.isDebugEnabled(DispatcherModule.class)) { Tracing.logDebug("could not execute legacy statics method:" + e.toString() + ",msg:" + e.getMessage(), StaticsLegacyDispatcher.class); } @@ -294,7 +295,7 @@ public class StaticsLegacyDispatcher implements Dispatcher { if ((result == null) || (result.equals(""))) { result = "/"; } - return StaticMediaDispatcher.normalizePath(result); + return ServletUtil.normalizePath(result); } diff --git a/src/main/java/org/olat/core/commons/contextHelp/ContextHelpDispatcher.java b/src/main/java/org/olat/core/commons/contextHelp/ContextHelpDispatcher.java index 54a7fda4b70..dadade3e241 100644 --- a/src/main/java/org/olat/core/commons/contextHelp/ContextHelpDispatcher.java +++ b/src/main/java/org/olat/core/commons/contextHelp/ContextHelpDispatcher.java @@ -29,7 +29,7 @@ import org.olat.core.commons.chiefcontrollers.BaseChiefControllerCreator; import org.olat.core.commons.fullWebApp.BaseFullWebappPopupBrowserWindow; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.Windows; @@ -67,15 +67,16 @@ public class ContextHelpDispatcher extends LogDelegator implements Dispatcher { * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest request, - HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { UserRequest ureq = null; try { + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); ureq = new UserRequestImpl(uriPrefix, request, response); if (!ContextHelpModule.isContextHelpEnabled()) { // disabled context help - redirect immediately - DispatcherAction.sendNotFound(ureq.getNonParsedUri(), response); + DispatcherModule.sendNotFound(ureq.getNonParsedUri(), response); return; } diff --git a/src/main/java/org/olat/core/commons/contextHelp/ContextHelpMainController.java b/src/main/java/org/olat/core/commons/contextHelp/ContextHelpMainController.java index 26bcd1bd499..e106f1a0bac 100644 --- a/src/main/java/org/olat/core/commons/contextHelp/ContextHelpMainController.java +++ b/src/main/java/org/olat/core/commons/contextHelp/ContextHelpMainController.java @@ -25,7 +25,7 @@ import java.util.Locale; import org.apache.commons.lang.ArrayUtils; import org.olat.core.commons.chiefcontrollers.LanguageChangedEvent; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.dispatcher.impl.StaticMediaDispatcher; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -161,7 +161,7 @@ class ContextHelpMainController extends MainLayoutBasicController implements Gen if (bundleName == null) { // Help page not found, try it with static legacy help files String legacyHelpUrl = StaticMediaDispatcher.createStaticURIFor("help/" + ureq.getNonParsedUri()); - DispatcherAction.redirectTo(ureq.getHttpResp(), legacyHelpUrl); + DispatcherModule.redirectTo(ureq.getHttpResp(), legacyHelpUrl); return; } diff --git a/src/main/java/org/olat/core/commons/fullWebApp/DefaultTopNavController.java b/src/main/java/org/olat/core/commons/fullWebApp/DefaultTopNavController.java index ef1d359f09e..1360cc86560 100644 --- a/src/main/java/org/olat/core/commons/fullWebApp/DefaultTopNavController.java +++ b/src/main/java/org/olat/core/commons/fullWebApp/DefaultTopNavController.java @@ -20,7 +20,7 @@ package org.olat.core.commons.fullWebApp; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.velocity.VelocityContainer; @@ -66,7 +66,7 @@ public class DefaultTopNavController extends BasicController { if (command.equals("logout")) { CoreSpringFactory.getImpl(UserSessionManager.class).signOffAndClear(ureq.getUserSession()); } else if (command.equals("login")) { - DispatcherAction + DispatcherModule .redirectToDefaultDispatcher(ureq.getHttpResp()); } } diff --git a/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java b/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java index 3bb49e41e08..ae7f3027fed 100644 --- a/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java +++ b/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java @@ -43,7 +43,7 @@ import org.olat.core.commons.modules.bc.commands.FolderCommand; import org.olat.core.commons.modules.bc.commands.FolderCommandFactory; import org.olat.core.commons.modules.bc.commands.FolderCommandStatus; import org.olat.core.commons.modules.bc.components.FolderComponent; -import org.olat.core.commons.services.webdav.WebDAVManager; +import org.olat.core.commons.services.webdav.WebDAVModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.download.DisplayOrDownloadComponent; @@ -238,8 +238,9 @@ public class FolderRunController extends BasicController implements Activateable folderComponent.setCanMail(ureq.getUserSession().getRoles().isGuestOnly() ? false : canMail); // guests can never send mail folderComponent.addListener(this); folderContainer.put("foldercomp", folderComponent); - if (WebDAVManager.getInstance().isEnabled() && displayWebDAVLink) + if (CoreSpringFactory.getImpl(WebDAVModule.class).isEnabled() && displayWebDAVLink) { folderContainer.contextPut("webdavlink", FolderManager.getWebDAVLink()); + } selTree = new SelectionTree("seltree", getTranslator()); selTree.addListener(this); diff --git a/src/main/java/org/olat/core/commons/services/webdav/DefaultServlet.java b/src/main/java/org/olat/core/commons/services/webdav/DefaultServlet.java deleted file mode 100644 index babd1ac6f29..00000000000 --- a/src/main/java/org/olat/core/commons/services/webdav/DefaultServlet.java +++ /dev/null @@ -1,2353 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -* <p> -*/ - -package org.olat.core.commons.services.webdav; -/* - * ==================================================================== - * - * The Apache Software License, Version 1.1 - * - * Copyright (c) 1999 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - * [Additional notices, if required by prior licensing conditions] - * - */ - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.RandomAccessFile; -import java.io.Reader; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.sql.Timestamp; -import java.text.Normalizer; -import java.text.Normalizer.Form; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Enumeration; -import java.util.Locale; -import java.util.StringTokenizer; -import java.util.TimeZone; -import java.util.Vector; - -import javax.naming.InitialContext; -import javax.naming.NameClassPair; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.naming.resources.Resource; -import org.apache.naming.resources.ResourceAttributes; -import org.olat.core.helpers.Settings; -import org.olat.core.util.MD5Encoder; -import org.olat.core.util.servlets.FastHttpDateFormat; -import org.olat.core.util.servlets.Globals; -import org.olat.core.util.servlets.URLEncoder; - - -/** - * The default resource-serving servlet for most web applications, - * used to serve static resources such as HTML pages and images. - * - * @author Craig R. McClanahan - * @author Remy Maucherat - */ - -public class DefaultServlet - extends HttpServlet { - - /** - * The debugging detail level for this servlet. - */ - protected int debug = 0; - - - /** - * The input buffer size to use when serving resources. - */ - protected int input = 2048; - - - /** - * Should we generate directory listings when no welcome file is present? - */ - protected boolean listings = true; - - - /** - * Read only flag. By default, it's set to true. - */ - protected boolean readOnly = true; - - - /** - * The output buffer size to use when serving resources. - */ - protected int output = 2048; - - - /** - * The set of welcome files for this web application - */ - protected String welcomes[] = new String[0]; - - - /** - * MD5 message digest provider. - */ - protected static MessageDigest md5Helper; - - - /** - * The MD5 helper object for this class. - */ - protected static final MD5Encoder md5Encoder = new MD5Encoder(); - - - /** - * The set of SimpleDateFormat formats to use in getDateHeader(). - */ - protected static final SimpleDateFormat formats[] = { - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), - new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), - new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) - }; - - - protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT"); - - /** - * Array containing the safe characters set. - */ - protected static URLEncoder urlEncoder; - - - /** - * GMT timezone - all HTTP dates are on GMT - */ - // ----------------------------------------------------- Static Initializer - static { - formats[0].setTimeZone(gmtZone); - formats[1].setTimeZone(gmtZone); - formats[2].setTimeZone(gmtZone); - - urlEncoder = new URLEncoder(); - urlEncoder.addSafeCharacter('-'); - urlEncoder.addSafeCharacter('_'); - urlEncoder.addSafeCharacter('.'); - urlEncoder.addSafeCharacter('*'); - urlEncoder.addSafeCharacter('/'); - } - - - /** - * MIME multipart separation string - */ - protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY"; - - - /** - * JNDI resources name. - */ - protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources"; - - - /** - * Size of file transfer buffer in bytes. - */ - private static final int BUFFER_SIZE = 4096; - - - // --------------------------------------------------------- Public Methods - - - /** - * Finalize this servlet. - */ - public void destroy() { - - // No actions necessary - - } - - - /** - * Initialize this servlet. - */ - public void init() throws ServletException { - - // Set our properties from the initialization parameters - String value = null; - try { - value = getServletConfig().getInitParameter("debug"); - debug = Integer.parseInt(value); - } catch (Throwable t) { - - } - try { - value = getServletConfig().getInitParameter("input"); - input = Integer.parseInt(value); - } catch (Throwable t) { - - } - try { - value = getServletConfig().getInitParameter("listings"); - listings = (new Boolean(value)).booleanValue(); - } catch (Throwable t) { - - } - try { - value = getServletConfig().getInitParameter("readonly"); - if (value != null) - readOnly = (new Boolean(value)).booleanValue(); - } catch (Throwable t) { - - } - try { - value = getServletConfig().getInitParameter("output"); - output = Integer.parseInt(value); - } catch (Throwable t) { - - } - - // Sanity check on the specified buffer sizes - if (input < 256) - input = 256; - if (output < 256) - output = 256; - - // Initialize the set of welcome files for this application - welcomes = (String[]) getServletContext().getAttribute - (Globals.WELCOME_FILES_ATTR); - if (welcomes == null) - welcomes = new String[0]; - - if (debug > 0) { - log("DefaultServlet.init: input buffer size=" + input + - ", output buffer size=" + output); - for (int i = 0; i < welcomes.length; i++) - log("DefaultServlet.init: welcome file=" + - welcomes[i]); - } - - // Load the MD5 helper used to calculate signatures. - try { - md5Helper = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(); - } - - } - - - - // ------------------------------------------------------ Protected Methods - - - /** - * Get resources. This method will try to retrieve the resources through - * JNDI first, then in the servlet context if JNDI has failed (it could be - * disabled). It will return null. - * - * @return A JNDI DirContext, or null. - */ - protected DirContext getResources(HttpServletRequest hreq) { - - DirContext result = null; - // Try the servlet context - try { - result = (DirContext) getServletContext() - .getAttribute(Globals.RESOURCES_ATTR); - } catch (ClassCastException e) { - // Failed : Not the right type - } - - if (result != null) - return result; - - // Try JNDI - try { - result = - (DirContext) new InitialContext().lookup(RESOURCES_JNDI_NAME); - } catch (NamingException e) { - // Failed - } catch (ClassCastException e) { - // Failed : Not the right type - } - - return result; - - } - - - /** - * Show HTTP header information. - */ - protected void showRequestInfo(HttpServletRequest req) { - - System.out.println(); - System.out.println("SlideDAV Request Info"); - System.out.println(); - - // Show generic info - System.out.println("Encoding : " + req.getCharacterEncoding()); - System.out.println("Length : " + req.getContentLength()); - System.out.println("Type : " + req.getContentType()); - - System.out.println(); - System.out.println("Parameters"); - - Enumeration parameters = req.getParameterNames(); - - while (parameters.hasMoreElements()) { - String paramName = (String) parameters.nextElement(); - String[] values = req.getParameterValues(paramName); - System.out.print(paramName + " : "); - for (int i = 0; i < values.length; i++) { - System.out.print(values[i] + ", "); - } - System.out.println(); - } - - System.out.println(); - - System.out.println("Protocol : " + req.getProtocol()); - System.out.println("Address : " + req.getRemoteAddr()); - System.out.println("Host : " + req.getRemoteHost()); - System.out.println("Scheme : " + req.getScheme()); - System.out.println("Server Name : " + req.getServerName()); - System.out.println("Server Port : " + req.getServerPort()); - - System.out.println(); - System.out.println("Attributes"); - - Enumeration attributes = req.getAttributeNames(); - - while (attributes.hasMoreElements()) { - String attributeName = (String) attributes.nextElement(); - System.out.print(attributeName + " : "); - System.out.println(req.getAttribute(attributeName).toString()); - } - - System.out.println(); - - // Show HTTP info - System.out.println(); - System.out.println("HTTP Header Info"); - System.out.println(); - - System.out.println("Authentication Type : " + req.getAuthType()); - System.out.println("HTTP Method : " + req.getMethod()); - System.out.println("Path Info : " + req.getPathInfo()); - System.out.println("Path translated : " + req.getPathTranslated()); - System.out.println("Query string : " + req.getQueryString()); - System.out.println("Remote user : " + req.getRemoteUser()); - System.out.println("Requested session id : " - + req.getRequestedSessionId()); - System.out.println("Request URI : " + req.getRequestURI()); - System.out.println("Context path : " + req.getContextPath()); - System.out.println("Servlet path : " + req.getServletPath()); - System.out.println("User principal : " + req.getUserPrincipal()); - - - System.out.println(); - System.out.println("Headers : "); - - Enumeration headers = req.getHeaderNames(); - - while (headers.hasMoreElements()) { - String headerName = (String) headers.nextElement(); - System.out.print(headerName + " : "); - System.out.println(req.getHeader(headerName)); - } - - System.out.println(); - System.out.println(); - - } - - - /** - * Return the relative path associated with this servlet. - * - * @param request The servlet request we are processing - */ - protected String getRelativePath(HttpServletRequest request) { - - // Are we being processed by a RequestDispatcher.include()? - if (request.getAttribute("javax.servlet.include.request_uri")!=null) { - String result = (String) - request.getAttribute("javax.servlet.include.path_info"); - if (result == null) - result = (String) - request.getAttribute("javax.servlet.include.servlet_path"); - if ((result == null) || (result.equals(""))) - result = "/"; - return (result); - } - - // No, extract the desired path directly from the request - String result = request.getPathInfo(); - if (result == null) { - result = request.getServletPath(); - } - if ((result == null) || (result.equals(""))) { - result = "/"; - } - return normalize(result); - - } - - - /** - * Process a GET request for the specified resource. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs - */ - protected void doGet(HttpServletRequest request, - HttpServletResponse response) - throws IOException, ServletException { - - if (debug > 999) - showRequestInfo(request); - - // Serve the requested resource, including the data content - serveResource(request, response, true); - - } - - - /** - * Process a HEAD request for the specified resource. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs - */ - protected void doHead(HttpServletRequest request, - HttpServletResponse response) - throws IOException, ServletException { - - // Serve the requested resource, without the data content - serveResource(request, response, false); - - } - - - /** - * Process a POST request for the specified resource. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs - */ - protected void doPost(HttpServletRequest request, - HttpServletResponse response) - throws IOException, ServletException { - doGet(request, response); - } - - - /** - * Process a POST request for the specified resource. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs - */ - protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - - if (readOnly) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - String path = getRelativePath(req); - - // Retrieve the resources - DirContext resources = getResources(req); - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - boolean exists = true; - try { - resources.lookup(path); - } catch (NamingException e) { - exists = false; - } - - boolean result = true; - - // Temp. content file used to support partial PUT - File contentFile = null; - - // Input stream for temp. content file used to support partial PUT - //FileInputStream contentFileInStream = null; - - //ResourceInfo resourceInfo = new ResourceInfo(path, resources); - Range range = parseContentRange(req, resp); - - InputStream resourceInputStream = null; - - // Append data specified in ranges to existing content for this - // resource - create a temp. file on the local filesystem to - // perform this operation - // Assume just one range is specified for now - if (range != null) { - contentFile = executePartialPut(req, range, path); - resourceInputStream = new FileInputStream(contentFile); - } else { - resourceInputStream = req.getInputStream(); - } - - try { - Resource newResource = new Resource(resourceInputStream); - if (exists) { - resources.rebind(path, newResource); - } else { - resources.bind(path, newResource); - } - } catch(NamingException e) { - result = false; - } - - if (result) { - if (exists) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - } else { - resp.setStatus(HttpServletResponse.SC_CREATED); - } - } else { - resp.sendError(HttpServletResponse.SC_CONFLICT); - } - - } - - - /** - * Handle a partial PUT. New content specified in request is appended to - * existing content in oldRevisionContent (if present). This code does - * not support simultaneous partial updates to the same resource. - */ - protected File executePartialPut(HttpServletRequest req, Range range, - String path) - throws IOException { - - // Append data specified in ranges to existing content for this - // resource - create a temp. file on the local filesystem to - // perform this operation - File tempDir = (File) getServletContext().getAttribute - ("javax.servlet.context.tempdir"); - // Convert all '/' characters to '.' in resourcePath - String convertedResourcePath = path.replace('/', '.'); - File contentFile = new File(tempDir, convertedResourcePath); - if (contentFile.createNewFile()) { - // Clean up contentFile when Tomcat is terminated - contentFile.deleteOnExit(); - } - - RandomAccessFile randAccessContentFile = - new RandomAccessFile(contentFile, "rw"); - - Resource oldResource = null; - try { - Object obj = getResources(req).lookup(path); - if (obj instanceof Resource) - oldResource = (Resource) obj; - } catch (NamingException e) { - } - - // Copy data in oldRevisionContent to contentFile - if (oldResource != null) { - BufferedInputStream bufOldRevStream = - new BufferedInputStream(oldResource.streamContent(), - BUFFER_SIZE); - - int numBytesRead; - byte[] copyBuffer = new byte[BUFFER_SIZE]; - while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) { - randAccessContentFile.write(copyBuffer, 0, numBytesRead); - } - - bufOldRevStream.close(); - } - - randAccessContentFile.setLength(range.length); - - // Append data in request input stream to contentFile - randAccessContentFile.seek(range.start); - int numBytesRead; - byte[] transferBuffer = new byte[BUFFER_SIZE]; - BufferedInputStream requestBufInStream = - new BufferedInputStream(req.getInputStream(), BUFFER_SIZE); - while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { - randAccessContentFile.write(transferBuffer, 0, numBytesRead); - } - randAccessContentFile.close(); - requestBufInStream.close(); - - return contentFile; - - } - - - /** - * Process a POST request for the specified resource. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs - */ - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - - if (readOnly) { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - String path = getRelativePath(req); - - // Retrieve the Catalina context - // Retrieve the resources - DirContext resources = getResources(req); - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - boolean exists = true; - try { - resources.lookup(path); - } catch (NamingException e) { - exists = false; - } - - if (exists) { - boolean result = true; - try { - resources.unbind(path); - } catch (NamingException e) { - result = false; - } - if (result) { - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - } else { - resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); - } - } else { - resp.sendError(HttpServletResponse.SC_NOT_FOUND); - } - - } - - - /** - * Check if the conditions specified in the optional If headers are - * satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resourceInfo File object - * @return boolean true if the resource meets all the specified conditions, - * and false if any of the conditions is not satisfied, in which case - * request processing is stopped - */ - protected boolean checkIfHeaders(HttpServletRequest request, - HttpServletResponse response, - ResourceInfo resourceInfo) - throws IOException { - - return checkIfMatch(request, response, resourceInfo) - && checkIfModifiedSince(request, response, resourceInfo) - && checkIfNoneMatch(request, response, resourceInfo) - && checkIfUnmodifiedSince(request, response, resourceInfo); - - } - - - /** - * Get the ETag associated with a file. - * - * @param resourceInfo File object - * @param strong True if we want a strong ETag, in which case a checksum - * of the file has to be calculated - */ - protected String getETag(ResourceInfo resourceInfo) { - if (resourceInfo.strongETag != null) { - return resourceInfo.strongETag; - } else if (resourceInfo.weakETag != null) { - return resourceInfo.weakETag; - } else { - return "W/\"" + resourceInfo.length + "-" - + resourceInfo.date + "\""; - } - } - - protected String normalize(String path) { - return normalize(path, true); - } - - /** - * Return a context-relative path, beginning with a "/", that represents - * the canonical version of the specified path after ".." and "." elements - * are resolved out. If the specified path attempts to go outside the - * boundaries of the current context (i.e. too many ".." path elements - * are present), return <code>null</code> instead. - * - * @param path Path to be normalized - */ - protected String normalize(String path, boolean decode) { - - if (path == null) - return "/"; - - // Create a place for the normalized path - String normalized = path; - - /* - * Commented out -- already URL-decoded in StandardContextMapper - * Decoding twice leaves the container vulnerable to %25 --> '%' - * attacks. - * - * if (normalized.indexOf('%') >= 0) - * normalized = RequestUtil.URLDecode(normalized, "UTF8"); - */ - - if (normalized == null) - return (null); - - ///// - // OLAT-6294 Commented decoding code block - // Never decode URL as URL encoding must be set to UTF-8 on - // the connector level which leads to a double decoding which breaks - // umlaute in WebDAV -// if (decode) { -// try { // we need to decode potential UTF-8 characters in the URL -// normalized = new String(normalized.getBytes(), "UTF-8"); -// } catch (UnsupportedEncodingException e) {} -// } - ///// - // Normalize the unicode characters for comparison with file system - normalized = Normalizer.normalize(normalized, Form.NFC); - - if (normalized.equals("/.")) return "/"; - - // Normalize the slashes and add leading slash if necessary - if (normalized.indexOf('\\') >= 0) - normalized = normalized.replace('\\', '/'); - if (!normalized.startsWith("/")) - normalized = "/" + normalized; - - // Resolve occurrences of "//" in the normalized path - while (true) { - int index = normalized.indexOf("//"); - if (index < 0) - break; - normalized = normalized.substring(0, index) + - normalized.substring(index + 1); - } - - // Resolve occurrences of "/./" in the normalized path - while (true) { - int index = normalized.indexOf("/./"); - if (index < 0) - break; - normalized = normalized.substring(0, index) + - normalized.substring(index + 2); - } - - // Resolve occurrences of "/../" in the normalized path - while (true) { - int index = normalized.indexOf("/../"); - if (index < 0) - break; - if (index == 0) - return (null); // Trying to go outside our context - int index2 = normalized.lastIndexOf('/', index - 1); - normalized = normalized.substring(0, index2) + - normalized.substring(index + 3); - } - - if (normalized.charAt(normalized.length() - 1) == '/') normalized = normalized.substring(0, normalized.length() - 1); - // Return the normalized path that we have completed - return (normalized); - - } - - - /** - * URL rewriter. - * - * @param path Path which has to be rewiten - */ - protected String rewriteUrl(String path) { - return urlEncoder.encode( path ); - } - - - /** - * Display the size of a file. - */ - protected void displaySize(StringBuilder buf, int filesize) { - - int leftside = filesize / 1024; - int rightside = (filesize % 1024) / 103; // makes 1 digit - // To avoid 0.0 for non-zero file, we bump to 0.1 - if (leftside == 0 && rightside == 0 && filesize != 0) - rightside = 1; - buf.append(leftside).append(".").append(rightside); - buf.append(" KB"); - - } - - - /** - * Serve the specified resource, optionally including the data content. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param content Should the content be included? - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs - */ - protected void serveResource(HttpServletRequest request, - HttpServletResponse response, - boolean content) - throws IOException { - - // Identify the requested resource path - String path = getRelativePath(request); - if (debug > 0) { - if (content) - log("DefaultServlet.serveResource: Serving resource '" + - path + "' headers and data"); - else - log("DefaultServlet.serveResource: Serving resource '" + - path + "' headers only"); - } - - // Retrieve the Catalina context and Resources implementation - DirContext resources = getResources(request); - ResourceInfo resourceInfo = new ResourceInfo(path, resources); - - if (!resourceInfo.exists) { - response.sendError(HttpServletResponse.SC_NOT_FOUND, - request.getRequestURI()); - return; - } - - // If the resource is not a collection, and the resource path - // ends with "/" or "\", return NOT FOUND - if (!resourceInfo.collection) { - if (path.endsWith("/") || (path.endsWith("\\"))) { - response.sendError(HttpServletResponse.SC_NOT_FOUND, - request.getRequestURI()); - return; - } - } - - // If the resource is a collection (aka a directory), we check - // the welcome files list. - if (resourceInfo.collection) { - - if (!request.getRequestURI().endsWith("/")) { - String redirectPath = path; - String contextPath = request.getContextPath(); - if ((contextPath != null) && (!contextPath.equals("/"))) { - redirectPath = contextPath + redirectPath; - } - if (!(redirectPath.endsWith("/"))) - redirectPath = redirectPath + "/"; - redirectPath = appendParameters(request, redirectPath); - response.sendRedirect(redirectPath); - return; - } - - ResourceInfo welcomeFileInfo = checkWelcomeFiles(path, resources); - if (welcomeFileInfo != null) { - String redirectPath = welcomeFileInfo.path; - String contextPath = request.getContextPath(); - if ((contextPath != null) && (!contextPath.equals("/"))) { - redirectPath = contextPath + redirectPath; - } - redirectPath = appendParameters(request, redirectPath); - response.sendRedirect(redirectPath); - return; - } - - } else { - - // Checking If headers - boolean included = - (request.getAttribute(Globals.CONTEXT_PATH_ATTR) != null); - if (!included - && !checkIfHeaders(request, response, resourceInfo)) { - return; - } - - } - - // Find content type. - String contentType = - getServletContext().getMimeType(resourceInfo.path); - - Vector ranges = null; - - if (resourceInfo.collection) { - - // Skip directory listings if we have been configured to - // suppress them - if (!listings) { - response.sendError(HttpServletResponse.SC_NOT_FOUND, - request.getRequestURI()); - return; - } - contentType = "text/html;charset=UTF-8"; - - } else { - - // Parse range specifier - - ranges = parseRange(request, response, resourceInfo); - - // ETag header - response.setHeader("ETag", getETag(resourceInfo)); - - // Last-Modified header - if (debug > 0) - log("DefaultServlet.serveFile: lastModified='" + - (new Timestamp(resourceInfo.date)).toString() + "'"); - response.setHeader("Last-Modified", resourceInfo.httpDate); - - } - - ServletOutputStream ostream = null; - PrintWriter writer = null; - - if (content) { - - // Trying to retrieve the servlet output stream - - try { - ostream = response.getOutputStream(); - } catch (IllegalStateException e) { - // If it fails, we try to get a Writer instead if we're - // trying to serve a text file - if ( (contentType == null) - || (contentType.startsWith("text")) ) { - writer = response.getWriter(); - } else { - throw e; - } - } - - } - - if ( (resourceInfo.collection) || - ( ((ranges == null) || (ranges.isEmpty())) - && (request.getHeader("Range") == null) ) ) { - - // Set the appropriate output headers - if (contentType != null) { - if (debug > 0) - log("DefaultServlet.serveFile: contentType='" + - contentType + "'"); - response.setContentType(contentType); - } - long contentLength = resourceInfo.length; - if ((!resourceInfo.collection) && (contentLength >= 0)) { - if (debug > 0) - log("DefaultServlet.serveFile: contentLength=" + - contentLength); - response.setContentLength((int) contentLength); - } - - if (resourceInfo.collection) { - - if (content) { - // Serve the directory browser - resourceInfo.setStream(render(request.getContextPath() + "/webdav", resourceInfo)); - } - - } - - // Copy the input stream to our output stream (if requested) - if (content) { - try { - response.setBufferSize(output); - } catch (IllegalStateException e) { - // Silent catch - } - if (ostream != null) { - copy(resourceInfo, ostream); - } else { - copy(resourceInfo, writer); - } - } - - } else { - - if ((ranges == null) || (ranges.isEmpty())) - return; - - // Partial content response. - - response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); - - if (ranges.size() == 1) { - - Range range = (Range) ranges.elementAt(0); - response.addHeader("Content-Range", "bytes " - + range.start - + "-" + range.end + "/" - + range.length); - response.setContentLength((int) (range.end - range.start + 1)); - - if (contentType != null) { - if (debug > 0) - log("DefaultServlet.serveFile: contentType='" + - contentType + "'"); - response.setContentType(contentType); - } - - if (content) { - try { - response.setBufferSize(output); - } catch (IllegalStateException e) { - // Silent catch - } - if (ostream != null) { - copy(resourceInfo, ostream, range); - } else { - copy(resourceInfo, writer, range); - } - } - - } else { - - response.setContentType("multipart/byteranges; boundary=" - + mimeSeparation); - - if (content) { - try { - response.setBufferSize(output); - } catch (IllegalStateException e) { - // Silent catch - } - if (ostream != null) { - copy(resourceInfo, ostream, ranges.elements(), - contentType); - } else { - copy(resourceInfo, writer, ranges.elements(), - contentType); - } - } - - } - - } - - } - - - /** - * Parse the content-range header. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @return Range - */ - protected Range parseContentRange(HttpServletRequest request, - HttpServletResponse response) - throws IOException { - - // Retrieving the content-range header (if any is specified - String rangeHeader = request.getHeader("Content-Range"); - - if (rangeHeader == null) - return null; - - // bytes is the only range unit supported - if (!rangeHeader.startsWith("bytes")) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return null; - } - - rangeHeader = rangeHeader.substring(6).trim(); - - int dashPos = rangeHeader.indexOf('-'); - int slashPos = rangeHeader.indexOf('/'); - - if (dashPos == -1) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return null; - } - - if (slashPos == -1) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return null; - } - - Range range = new Range(); - - try { - range.start = Long.parseLong(rangeHeader.substring(0, dashPos)); - range.end = - Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos)); - range.length = Long.parseLong - (rangeHeader.substring(slashPos + 1, rangeHeader.length())); - } catch (NumberFormatException e) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return null; - } - - if (!range.validate()) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return null; - } - - return range; - - } - - - /** - * Parse the range header. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @return Vector of ranges - */ - protected Vector parseRange(HttpServletRequest request, - HttpServletResponse response, - ResourceInfo resourceInfo) - throws IOException { - - // Checking If-Range - String headerValue = request.getHeader("If-Range"); - if (headerValue != null) { - - String eTag = getETag(resourceInfo); - long lastModified = resourceInfo.date; - - Date date = null; - - // Parsing the HTTP Date - for (int i = 0; (date == null) && (i < formats.length); i++) { - try { - date = formats[i].parse(headerValue); - } catch (ParseException e) { - - } - } - - if (date == null) { - - // If the ETag the client gave does not match the entity - // etag, then the entire entity is returned. - if (!eTag.equals(headerValue.trim())) - return null; - - } else { - - // If the timestamp of the entity the client got is older than - // the last modification date of the entity, the entire entity - // is returned. - if (lastModified > (date.getTime() + 1000)) - return null; - - } - - } - - long fileLength = resourceInfo.length; - - if (fileLength == 0) - return null; - - // Retrieving the range header (if any is specified - String rangeHeader = request.getHeader("Range"); - - if (rangeHeader == null) - return null; - // bytes is the only range unit supported (and I don't see the point - // of adding new ones). - if (!rangeHeader.startsWith("bytes")) { - response.addHeader("Content-Range", "bytes */" + fileLength); - response.sendError - (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); - return null; - } - - rangeHeader = rangeHeader.substring(6); - - // Vector which will contain all the ranges which are successfully - // parsed. - Vector result = new Vector(); - StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ","); - - // Parsing the range list - while (commaTokenizer.hasMoreTokens()) { - String rangeDefinition = commaTokenizer.nextToken(); - - Range currentRange = new Range(); - currentRange.length = fileLength; - - int dashPos = rangeDefinition.indexOf('-'); - - if (dashPos == -1) { - response.addHeader("Content-Range", "bytes */" + fileLength); - response.sendError - (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); - return null; - } - - if (dashPos == 0) { - - try { - long offset = Long.parseLong(rangeDefinition); - currentRange.start = fileLength + offset; - currentRange.end = fileLength - 1; - } catch (NumberFormatException e) { - response.addHeader("Content-Range", - "bytes */" + fileLength); - response.sendError - (HttpServletResponse - .SC_REQUESTED_RANGE_NOT_SATISFIABLE); - return null; - } - - } else { - - try { - currentRange.start = Long.parseLong - (rangeDefinition.substring(0, dashPos)); - if (dashPos < rangeDefinition.length() - 1) - currentRange.end = Long.parseLong - (rangeDefinition.substring - (dashPos + 1, rangeDefinition.length())); - else - currentRange.end = fileLength - 1; - } catch (NumberFormatException e) { - response.addHeader("Content-Range", - "bytes */" + fileLength); - response.sendError - (HttpServletResponse - .SC_REQUESTED_RANGE_NOT_SATISFIABLE); - return null; - } - - } - - if (!currentRange.validate()) { - response.addHeader("Content-Range", "bytes */" + fileLength); - response.sendError - (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); - return null; - } - - result.addElement(currentRange); - } - - return result; - } - - - /** - * Append the request parameters to the redirection string before calling - * sendRedirect. - */ - protected String appendParameters(HttpServletRequest request, - String redirectPath) { - - StringBuilder result = new StringBuilder(rewriteUrl(redirectPath)); - - String query = request.getQueryString (); - if (query != null) - result.append ("?").append (query); - - return result.toString(); - - } - - - /** - * Return an InputStream to an HTML representation of the contents - * of this directory. - * - * @param contextPath Context path to which our internal paths are - * relative - */ - protected InputStream render - (String contextPath, ResourceInfo resourceInfo) { - - String name = resourceInfo.path; - - // Prepare a writer to a buffered area - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - OutputStreamWriter osWriter = null; - try { - osWriter = new OutputStreamWriter(stream, "UTF8"); - } catch (Exception e) { - // Should never happen - osWriter = new OutputStreamWriter(stream); - } - PrintWriter writer = new PrintWriter(osWriter); - - StringBuilder sb = new StringBuilder(); - - // Render the page header - sb.append("<html>\r\n"); - sb.append("<head>\r\n"); - sb.append("<title>"); - sb.append(name); - sb.append("</title>\r\n"); - sb.append("<STYLE><!--"); - sb.append("H1{font-family : Verdana, Tahoma, Arial, Geneva, Helvetica, sans-serif; color : white;background-color : #96A4BA;} "); - sb.append("H3{font-family : Verdana, Tahoma, Arial, Geneva, Helvetica, sans-serif; color : white;background-color : #96A4BA;} "); - sb.append("BODY{font-family : Verdana, Tahoma, Arial, Geneva, Helvetica, sans-serif; color : black;background-color : white;} "); - sb.append("A{color : #2A518D;} "); - sb.append("HR{color : #0086b2;} "); - sb.append("--></STYLE> "); - sb.append("</head>\r\n"); - sb.append("<body>"); - sb.append("<h1>"); - sb.append(name); - sb.append(" "); - sb.append("</h1>"); - - // Render the link to our parent (if required) - String parentDirectory = name; - if (parentDirectory.endsWith("/")) { - parentDirectory = - parentDirectory.substring(0, parentDirectory.length() - 1); - } - int slash = parentDirectory.lastIndexOf('/'); - if (slash >= 0) { - String parent = name.substring(0, slash); - sb.append("<p><a href=\""); - sb.append(rewriteUrl(contextPath)); - if (parent.equals("")) parent = "/"; - sb.append(rewriteUrl(parent)); - if (!parent.endsWith("/")) sb.append("/"); - sb.append("\">"); - sb.append("Back to "); - sb.append(parent); - sb.append("</a></p>"); - } - - sb.append("<HR size=\"1\" noshade>"); - - sb.append("<table width=\"100%\" cellspacing=\"0\"" + - " cellpadding=\"5\" align=\"center\">\r\n"); - - // Render the column headings - sb.append("<tr>\r\n"); - sb.append("<td align=\"left\"><font size=\"+1\"><strong>"); - sb.append("Directory filename"); - sb.append("</strong></font></td>\r\n"); - sb.append("<td align=\"center\"><font size=\"+1\"><strong>"); - sb.append("Directory size"); - sb.append("</strong></font></td>\r\n"); - sb.append("<td align=\"right\"><font size=\"+1\"><strong>"); - sb.append("Directory last modified"); - sb.append("</strong></font></td>\r\n"); - sb.append("</tr>"); - - try { - - // Render the directory entries within this directory - DirContext directory = resourceInfo.directory; - NamingEnumeration enumeration = - resourceInfo.resources.list(resourceInfo.path); - boolean shade = false; - while (enumeration.hasMoreElements()) { - - NameClassPair ncPair = (NameClassPair) enumeration.nextElement(); - String resourceName = ncPair.getName(); - ResourceInfo childResourceInfo = - new ResourceInfo(resourceName, directory); - - String trimmed = resourceName/*.substring(trim)*/; - if (trimmed.equalsIgnoreCase("WEB-INF") || - trimmed.equalsIgnoreCase("META-INF")) - continue; - - sb.append("<tr"); - if (shade) - sb.append(" bgcolor=\"eeeeee\""); - sb.append(">\r\n"); - shade = !shade; - - sb.append("<td align=\"left\"> \r\n"); - sb.append("<a href=\""); - sb.append(rewriteUrl(contextPath)); - resourceName = rewriteUrl(name + "/" + resourceName); - sb.append(resourceName); - if (childResourceInfo.collection) sb.append("/"); - sb.append("\"><tt>"); - sb.append(trimmed); - if (childResourceInfo.collection) - sb.append("/"); - sb.append("</tt></a></td>\r\n"); - - sb.append("<td align=\"right\"><tt>"); - if (childResourceInfo.collection) - sb.append(" "); - else - sb.append(renderSize(childResourceInfo.length)); - sb.append("</tt></td>\r\n"); - - sb.append("<td align=\"right\"><tt>"); - sb.append(childResourceInfo.httpDate); - sb.append("</tt></td>\r\n"); - - sb.append("</tr>\r\n"); - } - - } catch (NamingException e) { - // Something went wrong - } - - // Render the page footer - sb.append("</table>\r\n"); - - sb.append("<HR size=\"1\" noshade>"); - sb.append("<h3>").append(Settings.getFullVersionInfo()).append("</h3>"); - sb.append("</body>\r\n"); - sb.append("</html>\r\n"); - - // Return an input stream to the underlying bytes - writer.write(sb.toString()); - writer.flush(); - return (new ByteArrayInputStream(stream.toByteArray())); - - } - - - /** - * Render the specified file size (in bytes). - * - * @param size File size (in bytes) - */ - protected String renderSize(long size) { - - long leftSide = size / 1024; - long rightSide = (size % 1024) / 103; // Makes 1 digit - if ((leftSide == 0) && (rightSide == 0) && (size > 0)) - rightSide = 1; - - return ("" + leftSide + "." + rightSide + " kb"); - - } - - - // -------------------------------------------------------- Private Methods - - - /** - * Check if the if-match condition is satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resourceInfo File object - * @return boolean true if the resource meets the specified condition, - * and false if the condition is not satisfied, in which case request - * processing is stopped - */ - private boolean checkIfMatch(HttpServletRequest request, - HttpServletResponse response, - ResourceInfo resourceInfo) - throws IOException { - - String eTag = getETag(resourceInfo); - String headerValue = request.getHeader("If-Match"); - if (headerValue != null) { - if (headerValue.indexOf('*') == -1) { - - StringTokenizer commaTokenizer = new StringTokenizer - (headerValue, ","); - boolean conditionSatisfied = false; - - while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) { - String currentToken = commaTokenizer.nextToken(); - if (currentToken.trim().equals(eTag)) - conditionSatisfied = true; - } - - // If none of the given ETags match, 412 Precodition failed is - // sent back - if (!conditionSatisfied) { - response.sendError - (HttpServletResponse.SC_PRECONDITION_FAILED); - return false; - } - - } - } - return true; - - } - - - /** - * Check if the if-modified-since condition is satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resourceInfo File object - * @return boolean true if the resource meets the specified condition, - * and false if the condition is not satisfied, in which case request - * processing is stopped - */ - private boolean checkIfModifiedSince(HttpServletRequest request, - HttpServletResponse response, - ResourceInfo resourceInfo) { - try { - long headerValue = request.getDateHeader("If-Modified-Since"); - long lastModified = resourceInfo.date; - if (headerValue != -1) { - - // If an If-None-Match header has been specified, if modified since - // is ignored. - if ((request.getHeader("If-None-Match") == null) - && (lastModified <= headerValue + 1000)) { - // The entity has not been modified since the date - // specified by the client. This is not an error case. - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - return false; - } - } - } catch(IllegalArgumentException illegalArgument) { - return false; - } - return true; - - } - - - /** - * Check if the if-none-match condition is satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resourceInfo File object - * @return boolean true if the resource meets the specified condition, - * and false if the condition is not satisfied, in which case request - * processing is stopped - */ - private boolean checkIfNoneMatch(HttpServletRequest request, - HttpServletResponse response, - ResourceInfo resourceInfo) - throws IOException { - - String eTag = getETag(resourceInfo); - String headerValue = request.getHeader("If-None-Match"); - if (headerValue != null) { - - boolean conditionSatisfied = false; - - if (!headerValue.equals("*")) { - - StringTokenizer commaTokenizer = - new StringTokenizer(headerValue, ","); - - while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) { - String currentToken = commaTokenizer.nextToken(); - if (currentToken.trim().equals(eTag)) - conditionSatisfied = true; - } - - } else { - conditionSatisfied = true; - } - - if (conditionSatisfied) { - - // For GET and HEAD, we should respond with - // 304 Not Modified. - // For every other method, 412 Precondition Failed is sent - // back. - if ( ("GET".equals(request.getMethod())) - || ("HEAD".equals(request.getMethod())) ) { - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - return false; - } else { - response.sendError - (HttpServletResponse.SC_PRECONDITION_FAILED); - return false; - } - } - } - return true; - - } - - - /** - * Check if the if-unmodified-since condition is satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resourceInfo File object - * @return boolean true if the resource meets the specified condition, - * and false if the condition is not satisfied, in which case request - * processing is stopped - */ - private boolean checkIfUnmodifiedSince(HttpServletRequest request, - HttpServletResponse response, - ResourceInfo resourceInfo) - throws IOException { - try { - long lastModified = resourceInfo.date; - long headerValue = request.getDateHeader("If-Unmodified-Since"); - if (headerValue != -1) { - if ( lastModified > headerValue ) { - // The entity has not been modified since the date - // specified by the client. This is not an error case. - response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); - return false; - } - } - } catch(IllegalArgumentException illegalArgument) { - return false; - } - return true; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param istream The input stream to read from - * @param ostream The output stream to write to - * - * @exception IOException if an input/output error occurs - */ - private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream) - throws IOException { - - IOException exception = null; - - // FIXME:ms: i18n ? - InputStream resourceInputStream = resourceInfo.getStream(); - InputStream istream = new BufferedInputStream - (resourceInputStream, input); - - // Copy the input stream to the output stream - exception = copyRange(istream, ostream); - - // Clean up the input stream - try { - istream.close(); - } catch (Throwable t) { - - } - - // Rethrow any exception that has occurred - if (exception != null) - throw exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param istream The input stream to read from - * @param writer The writer to write to - * - * @exception IOException if an input/output error occurs - */ - private void copy(ResourceInfo resourceInfo, PrintWriter writer) - throws IOException { - - IOException exception = null; - - InputStream resourceInputStream = resourceInfo.getStream(); - // FIXME:ms: i18n ? - Reader reader = new InputStreamReader(resourceInputStream); - - // Copy the input stream to the output stream - exception = copyRange(reader, writer); - - // Clean up the reader - try { - reader.close(); - } catch (Throwable t) { - - } - - // Rethrow any exception that has occurred - if (exception != null) - throw exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param resourceInfo The ResourceInfo object - * @param ostream The output stream to write to - * @param range Range the client wanted to retrieve - * @exception IOException if an input/output error occurs - */ - private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream, - Range range) - throws IOException { - - IOException exception = null; - - InputStream resourceInputStream = resourceInfo.getStream(); - InputStream istream = - new BufferedInputStream(resourceInputStream, input); - exception = copyRange(istream, ostream, range.start, range.end); - - // Clean up the input stream - try { - istream.close(); - } catch (Throwable t) { - - } - - // Rethrow any exception that has occurred - if (exception != null) - throw exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param resourceInfo The ResourceInfo object - * @param writer The writer to write to - * @param range Range the client wanted to retrieve - * @exception IOException if an input/output error occurs - */ - private void copy(ResourceInfo resourceInfo, PrintWriter writer, - Range range) - throws IOException { - - IOException exception = null; - - InputStream resourceInputStream = resourceInfo.getStream(); - Reader reader = new InputStreamReader(resourceInputStream); - exception = copyRange(reader, writer, range.start, range.end); - - // Clean up the input stream - try { - reader.close(); - } catch (Throwable t) { - - } - - // Rethrow any exception that has occurred - if (exception != null) - throw exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param resourceInfo The ResourceInfo object - * @param ostream The output stream to write to - * @param ranges Enumeration of the ranges the client wanted to retrieve - * @param contentType Content type of the resource - * @exception IOException if an input/output error occurs - */ - private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream, - Enumeration ranges, String contentType) - throws IOException { - - IOException exception = null; - - while ( (exception == null) && (ranges.hasMoreElements()) ) { - - InputStream resourceInputStream = resourceInfo.getStream(); - InputStream istream = // FIXME:ms: internationalization??????? - new BufferedInputStream(resourceInputStream, input); - - Range currentRange = (Range) ranges.nextElement(); - - // Writing MIME header. - ostream.println("--" + mimeSeparation); - if (contentType != null) - ostream.println("Content-Type: " + contentType); - ostream.println("Content-Range: bytes " + currentRange.start - + "-" + currentRange.end + "/" - + currentRange.length); - ostream.println(); - - // Printing content - exception = copyRange(istream, ostream, currentRange.start, - currentRange.end); - - try { - istream.close(); - } catch (Throwable t) { - - } - - } - - ostream.print("--" + mimeSeparation + "--"); - - // Rethrow any exception that has occurred - if (exception != null) - throw exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param resourceInfo The ResourceInfo object - * @param writer The writer to write to - * @param ranges Enumeration of the ranges the client wanted to retrieve - * @param contentType Content type of the resource - * @exception IOException if an input/output error occurs - */ - private void copy(ResourceInfo resourceInfo, PrintWriter writer, - Enumeration ranges, String contentType) - throws IOException { - - IOException exception = null; - - while ( (exception == null) && (ranges.hasMoreElements()) ) { - - InputStream resourceInputStream = resourceInfo.getStream(); - Reader reader = new InputStreamReader(resourceInputStream); - - Range currentRange = (Range) ranges.nextElement(); - - // Writing MIME header. - writer.println("--" + mimeSeparation); - if (contentType != null) - writer.println("Content-Type: " + contentType); - writer.println("Content-Range: bytes " + currentRange.start - + "-" + currentRange.end + "/" - + currentRange.length); - writer.println(); - - // Printing content - exception = copyRange(reader, writer, currentRange.start, - currentRange.end); - - try { - reader.close(); - } catch (Throwable t) { - - } - - } - - writer.print("--" + mimeSeparation + "--"); - - // Rethrow any exception that has occurred - if (exception != null) - throw exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param istream The input stream to read from - * @param ostream The output stream to write to - * @return Exception which occurred during processing - */ - private IOException copyRange(InputStream istream, - ServletOutputStream ostream) { - - // Copy the input stream to the output stream - IOException exception = null; - byte buffer[] = new byte[input]; - int len = buffer.length; - while (true) { - try { - len = istream.read(buffer); - if (len == -1) - break; - ostream.write(buffer, 0, len); - } catch (IOException e) { - exception = e; - len = -1; - break; - } - } - return exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param reader The reader to read from - * @param writer The writer to write to - * @return Exception which occurred during processing - */ - private IOException copyRange(Reader reader, PrintWriter writer) { - - // Copy the input stream to the output stream - IOException exception = null; - char buffer[] = new char[input]; - int len = buffer.length; - while (true) { - try { - len = reader.read(buffer); - if (len == -1) - break; - writer.write(buffer, 0, len); - } catch (IOException e) { - exception = e; - len = -1; - break; - } - } - return exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param istream The input stream to read from - * @param ostream The output stream to write to - * @param start Start of the range which will be copied - * @param end End of the range which will be copied - * @return Exception which occurred during processing - */ - private IOException copyRange(InputStream istream, - ServletOutputStream ostream, - long start, long end) { - - if (debug > 10) - System.out.println("Serving bytes:" + start + "-" + end); - - try { - istream.skip(start); - } catch (IOException e) { - return e; - } - - IOException exception = null; - long bytesToRead = end - start + 1; - - byte buffer[] = new byte[input]; - int len = buffer.length; - while ( (bytesToRead > 0) && (len >= buffer.length)) { - try { - len = istream.read(buffer); - if (bytesToRead >= len) { - ostream.write(buffer, 0, len); - bytesToRead -= len; - } else { - ostream.write(buffer, 0, (int) bytesToRead); - bytesToRead = 0; - } - } catch (IOException e) { - exception = e; - len = -1; - } - if (len < buffer.length) - break; - } - - return exception; - - } - - - /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param reader The reader to read from - * @param writer The writer to write to - * @param start Start of the range which will be copied - * @param end End of the range which will be copied - * @return Exception which occurred during processing - */ - private IOException copyRange(Reader reader, PrintWriter writer, - long start, long end) { - - try { - reader.skip(start); - } catch (IOException e) { - return e; - } - - IOException exception = null; - long bytesToRead = end - start + 1; - - char buffer[] = new char[input]; - int len = buffer.length; - while ( (bytesToRead > 0) && (len >= buffer.length)) { - try { - len = reader.read(buffer); - if (bytesToRead >= len) { - writer.write(buffer, 0, len); - bytesToRead -= len; - } else { - writer.write(buffer, 0, (int) bytesToRead); - bytesToRead = 0; - } - } catch (IOException e) { - exception = e; - len = -1; - } - if (len < buffer.length) - break; - } - - return exception; - - } - - - /** - * Check to see if a default page exists. - * - * @param pathname Pathname of the file to be served - */ - private ResourceInfo checkWelcomeFiles(String pathname, - DirContext resources) { - - String collectionName = pathname; - if (!pathname.endsWith("/")) { - collectionName += "/"; - } - - // Refresh our currently defined set of welcome files - synchronized (welcomes) { //o_clusterOK by:fj - welcomes = (String[]) getServletContext().getAttribute - (Globals.WELCOME_FILES_ATTR); - if (welcomes == null) - welcomes = new String[0]; - } - - // Serve a welcome resource or file if one exists - for (int i = 0; i < welcomes.length; i++) { - - // Does the specified resource exist? - String resourceName = collectionName + welcomes[i]; - ResourceInfo resourceInfo = - new ResourceInfo(resourceName, resources); - if (resourceInfo.exists()) { - return resourceInfo; - } - - } - - return null; - - } - - - // ------------------------------------------------------ Range Inner Class - - - private class Range { - - public long start; - public long end; - public long length; - - /** - * Validate range. - */ - public boolean validate() { - if (end >= length) - end = length - 1; - return ( (start >= 0) && (end >= 0) && (start <= end) - && (length > 0) ); - } - - public void recycle() { - start = 0; - end = 0; - length = 0; - } - - } - - - // ---------------------------------------------- ResourceInfo Inner Class - - - protected class ResourceInfo { - - - /** - * Constructor. - * - * @param pathname Path name of the file - */ - public ResourceInfo(String path, DirContext resources) { - set(path, resources); - } - - - public Object object; - public DirContext directory; - public Resource file; - public Attributes attributes; - public String path; - public long creationDate; - public String httpDate; - public long date; - public long length; - public boolean collection; - public String weakETag; - public String strongETag; - public boolean exists; - public DirContext resources; - protected InputStream is; - - - public void recycle() { - object = null; - directory = null; - file = null; - attributes = null; - path = null; - creationDate = 0; - httpDate = null; - date = 0; - length = -1; - collection = true; - weakETag = null; - strongETag = null; - exists = false; - resources = null; - is = null; - } - - - public void set(String path, DirContext resources) { - - recycle(); - - this.path = path; - this.resources = resources; - exists = true; - try { - object = resources.lookup(path); - if (object instanceof Resource) { - file = (Resource) object; - collection = false; - } else if (object instanceof DirContext) { - directory = (DirContext) object; - collection = true; - } else { - // Don't know how to serve another object type - exists = false; - } - } catch (NamingException e) { - exists = false; - } - if (exists) { - try { - attributes = resources.getAttributes(path); - if (attributes instanceof ResourceAttributes) { - ResourceAttributes tempAttrs = - (ResourceAttributes) attributes; - Date tempDate = tempAttrs.getCreationDate(); - if (tempDate != null) - creationDate = tempDate.getTime(); - tempDate = tempAttrs.getLastModifiedDate(); - if (tempDate != null) { - httpDate = FastHttpDateFormat.getDate(tempDate); - date = tempDate.getTime(); - } else { - httpDate = FastHttpDateFormat.getCurrentDate(); - } - weakETag = tempAttrs.getETag(); - strongETag = tempAttrs.getETag(true); - length = tempAttrs.getContentLength(); - } - } catch (NamingException e) { - // Shouldn't happen, the implementation of the DirContext - // is probably broken - exists = false; - } - } - - } - - - /** - * Test if the associated resource exists. - */ - public boolean exists() { - return exists; - } - - - /** - * String representation. - */ - public String toString() { - return path; - } - - - /** - * Set IS. - */ - public void setStream(InputStream is) { - this.is = is; - } - - - /** - * Get IS from resource. - */ - public InputStream getStream() - throws IOException { - if (is != null) - return is; - if (file != null) - return (file.streamContent()); - else - return null; - } - - - } - - -} diff --git a/src/main/java/org/olat/core/util/filter/ChainedFilter.java b/src/main/java/org/olat/core/commons/services/webdav/WebDAVDispatcher.java similarity index 53% rename from src/main/java/org/olat/core/util/filter/ChainedFilter.java rename to src/main/java/org/olat/core/commons/services/webdav/WebDAVDispatcher.java index 17fcf71bfbd..9b02d791d3d 100644 --- a/src/main/java/org/olat/core/util/filter/ChainedFilter.java +++ b/src/main/java/org/olat/core/commons/services/webdav/WebDAVDispatcher.java @@ -17,41 +17,27 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.core.util.filter; +package org.olat.core.commons.services.webdav; -import java.util.ArrayList; -import java.util.List; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.olat.core.dispatcher.Dispatcher; /** * - * Description:<br> - * The filter chain allows you to use muliple filters in a chain. - * <P> - * Initial Date: 13.07.2009 <br> + * Hide the actual implementation under this interface * - * @author gnaegi + * @author srosse, stephane.rosse@frentix.com, http://www.frentix + * */ -public class ChainedFilter implements Filter { - private final List<Filter> chain = new ArrayList<Filter>(); - - /** - * @see org.olat.core.util.filter.Filter#filter(java.lang.String) - */ - public String filter(final String original) { - String filteredValue = original; - for (Filter filter : chain) { - filteredValue = filter.filter(filteredValue); - } - return filteredValue; - } +public interface WebDAVDispatcher extends Dispatcher { - /** - * Add a filter to the filter chain - * - * @param filter - */ - public void addFilter(final Filter filter) { - chain.add(filter); - } + public void doRootOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException; + + public void doWebdavOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException; } diff --git a/src/main/java/org/olat/core/commons/services/webdav/WebDAVManager.java b/src/main/java/org/olat/core/commons/services/webdav/WebDAVManager.java index f6f9527010e..67997ae2ce4 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/WebDAVManager.java +++ b/src/main/java/org/olat/core/commons/services/webdav/WebDAVManager.java @@ -22,8 +22,7 @@ package org.olat.core.commons.services.webdav; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.olat.core.configuration.ConfigOnOff; -import org.olat.core.manager.BasicManager; +import org.olat.core.commons.services.webdav.servlets.WebResourceRoot; import org.olat.core.util.UserSession; /** @@ -36,18 +35,10 @@ import org.olat.core.util.UserSession; * * @author Florian Gnaegi, frentix GmbH, http://www.frentix.com */ -public abstract class WebDAVManager extends BasicManager implements ConfigOnOff{ +public interface WebDAVManager { - protected static WebDAVManager INSTANCE; - /** - * Factory method - * - * @return - */ - public static final WebDAVManager getInstance() { - return INSTANCE; - } + public static final String REQUEST_USERSESSION_KEY = "__usess"; /** * Handles authentication of OLAT users for the WevDAV servlet. @@ -57,8 +48,7 @@ public abstract class WebDAVManager extends BasicManager implements ConfigOnOff{ * @return True if user is successfully authenticated as a valid OLAT user, * false otherwise. */ - protected abstract boolean handleAuthentication(HttpServletRequest req, - HttpServletResponse resp); + public boolean handleAuthentication(HttpServletRequest req, HttpServletResponse resp); /** * Calculate the user session object for a request @@ -66,11 +56,8 @@ public abstract class WebDAVManager extends BasicManager implements ConfigOnOff{ * @param req * @return */ - protected abstract UserSession getUserSession(HttpServletRequest req); - - /** - * @return true: webDAV support is enabled; false: webDAV is disabled - */ - public abstract boolean isEnabled(); + public UserSession getUserSession(HttpServletRequest req); + + public WebResourceRoot getWebDAVRoot(HttpServletRequest req); } \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java b/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java new file mode 100644 index 00000000000..93e2743e623 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java @@ -0,0 +1,136 @@ +/** +* OLAT - Online Learning and Training<br> +* http://www.olat.org +* <p> +* Licensed under the Apache License, Version 2.0 (the "License"); <br> +* you may not use this file except in compliance with the License.<br> +* You may obtain a copy of the License at +* <p> +* http://www.apache.org/licenses/LICENSE-2.0 +* <p> +* Unless required by applicable law or agreed to in writing,<br> +* software distributed under the License is distributed on an "AS IS" BASIS, <br> +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> +* See the License for the specific language governing permissions and <br> +* limitations under the License. +* <p> +* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> +* University of Zurich, Switzerland. +* <hr> +* <a href="http://www.openolat.org"> +* OpenOLAT - Online Learning and Training</a><br> +* This file has been modified by the OpenOLAT community. Changes are licensed +* under the Apache 2.0 license as the original file. +* <p> +*/ + +package org.olat.core.commons.services.webdav; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.olat.core.configuration.AbstractOLATModule; +import org.olat.core.configuration.ConfigOnOff; +import org.olat.core.configuration.PersistedProperties; +import org.olat.core.logging.AssertException; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; + +public class WebDAVModule extends AbstractOLATModule implements ConfigOnOff { + + private static final OLog log = Tracing.createLoggerFor(WebDAVModule.class); + + private static final String WEBDAV_ENABLED = "webdav.enabled"; + private static final String DIGEST_AUTH_ENABLED = "auth.digest.enabled"; + + private Map<String, WebDAVProvider> webdavProviders; + + private boolean enabled; + private boolean digestAuthenticationEnabled; + + @Override + public void init() { + //module enabled/disabled + String enabledObj = getStringPropertyValue(WEBDAV_ENABLED, true); + if(StringHelper.containsNonWhitespace(enabledObj)) { + enabled = "true".equals(enabledObj); + } + + String digestEnabledObj = getStringPropertyValue(DIGEST_AUTH_ENABLED, true); + if(StringHelper.containsNonWhitespace(digestEnabledObj)) { + digestAuthenticationEnabled = "true".equals(digestEnabledObj); + } + } + + @Override + protected void initDefaultProperties() { + enabled = getBooleanConfigParameter(WEBDAV_ENABLED, true); + digestAuthenticationEnabled = getBooleanConfigParameter(DIGEST_AUTH_ENABLED, true); + } + + @Override + protected void initFromChangedProperties() { + init(); + } + + @Override + public void setPersistedProperties(PersistedProperties persistedProperties) { + this.moduleConfigProperties = persistedProperties; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + String enabledStr = enabled ? "true" : "false"; + setStringProperty(WEBDAV_ENABLED, enabledStr, true); + } + + public boolean isDigestAuthenticationEnabled() { + return digestAuthenticationEnabled; + } + + public void setDigestAuthenticationEnabled(boolean enabled) { + String enabledStr = enabled ? "true" : "false"; + setStringProperty(DIGEST_AUTH_ENABLED, enabledStr, true); + } + + /** + * Return an unmodifiable map + * @return + */ + public Map<String, WebDAVProvider> getWebDAVProviders() { + return Collections.unmodifiableMap(webdavProviders); + } + + /** + * Set the list of webdav providers. + * @param webdavProviders + */ + public void setWebdavProviderList(List<WebDAVProvider> webdavProviders) { + if (webdavProviders == null) return;//nothing to do + + for (WebDAVProvider provider : webdavProviders) { + addWebdavProvider(provider); + } + } + + /** + * Add a new webdav provider. + * @param provider + */ + public void addWebdavProvider(WebDAVProvider provider) { + if (webdavProviders == null) { + webdavProviders = new HashMap<String, WebDAVProvider>(); + } + if (webdavProviders.containsKey(provider.getMountPoint())) + throw new AssertException("May not add two providers with the same mount point."); + webdavProviders.put(provider.getMountPoint(), provider); + log.info("Adding webdav mountpoint '" + provider.getMountPoint() + "'."); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderFactory.java b/src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderFactory.java deleted file mode 100644 index b55ebb6e08e..00000000000 --- a/src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderFactory.java +++ /dev/null @@ -1,91 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -* <p> -*/ - -package org.olat.core.commons.services.webdav; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.olat.core.id.Identity; -import org.olat.core.logging.AssertException; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; -import org.olat.core.util.vfs.MergeSource; -import org.olat.core.util.vfs.VFSItem; - -public class WebDAVProviderFactory { - - private OLog log = Tracing.createLoggerFor(WebDAVProviderFactory.class); - - private static final WebDAVProviderFactory INSTANCE = new WebDAVProviderFactory(); - private static Map<String, WebDAVProvider> webdavProviders; - - private WebDAVProviderFactory() { - // singleton - } - - public static final WebDAVProviderFactory getInstance() { return INSTANCE; } - - /** - * Returns a mountable root containing all entries which will be exposed to the webdav mount. - * @return - */ - public VFSItem getMountableRoot(Identity identity) { - MergeSource vfsRoot = new MergeSource(null, "webdavroot"); - for (Iterator<String> iter = webdavProviders.keySet().iterator(); iter.hasNext();) { - WebDAVProvider provider = (WebDAVProvider)webdavProviders.get(iter.next()); - vfsRoot.addContainer(new WebDAVProviderNamedContainer(identity, provider)); - } - return vfsRoot; - } - - /** - * Set the list of webdav providers. - * @param webdavProviders - */ - public void setWebdavProviderList(List<WebDAVProvider> webdavProviders) { - if (webdavProviders == null) - throw new AssertException("null value for webdavProviders not allowed."); - - for (WebDAVProvider provider : webdavProviders) { - addWebdavProvider(provider); - } - } - - /** - * Add a new webdav provider. - * @param provider - */ - public void addWebdavProvider(WebDAVProvider provider) { - if (webdavProviders == null) webdavProviders = new HashMap<String, WebDAVProvider>(); - if (webdavProviders.containsKey(provider.getMountPoint())) - throw new AssertException("May not add two providers with the same mount point."); - webdavProviders.put(provider.getMountPoint(), provider); - log.info("Adding webdav mountpoint '" + provider.getMountPoint() + "'."); - } -} \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml b/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml index bd9493d46b1..7b80da51b7e 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml +++ b/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml @@ -4,23 +4,16 @@ xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - - <bean id="webDAVManager" class="org.olat.core.commons.services.webdav.manager.WebDAVManagerImpl" > - <constructor-arg ref="coordinatorManager"/> - <!-- set to false to disable the WebDAV support and remove the WebDAV Link from the GUI --> - <property name="enabled" value="${webdav.links.enabled}" /> - <property name="sessionManager" ref="userSessionManager" /> - <property name="webDAVAuthManager" ref="webDAVAuthenticationSpi" /> - </bean> - - <bean id="webDAVAuthenticationSpi" class="org.olat.core.commons.services.webdav.manager.WebDAVAuthManager" > - <property name="securityManager" ref="baseSecurityManager" /> - <property name="olatAuthenticationSpi" ref="olatAuthenticationSpi" /> - </bean> - - - <!-- WebDAV provider factory --> - <bean id="webdavfactory" class="org.olat.core.commons.services.webdav.WebDAVProviderFactory" > + + <!-- WebDAV module --> + <bean id="webdavModule" class="org.olat.core.commons.services.webdav.WebDAVModule" depends-on="org.olat.core.util.WebappHelper"> + <property name="persistedProperties"> + <bean class="org.olat.core.configuration.PersistedProperties" scope="prototype" init-method="init" destroy-method="destroy" + depends-on="coordinatorManager,org.olat.core.util.WebappHelper"> + <constructor-arg index="0" ref="coordinatorManager"/> + <constructor-arg index="1" ref="webdavModule" /> + </bean> + </property> <property name="webdavProviderList"> <list> <ref bean="webdav_briefcase"/> @@ -31,6 +24,31 @@ </property> </bean> + <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> + <property name="targetObject" ref="webdavModule" /> + <property name="targetMethod" value="init" /> + <property name="arguments"> + <value> + webdav.enabled=${webdav.links.enabled} + auth.digest.enabled=${auth.digest.enabled} + </value> + </property> + </bean> + + <bean id="webDAVManager" class="org.olat.core.commons.services.webdav.manager.WebDAVManagerImpl" init-method="init"> + <constructor-arg ref="coordinatorManager"/> + <property name="sessionManager" ref="userSessionManager" /> + <property name="webDAVAuthManager" ref="webDAVAuthenticationSpi" /> + <property name="webDAVModule" ref="webdavModule" /> + </bean> + + <bean id="webDAVAuthenticationSpi" class="org.olat.core.commons.services.webdav.manager.WebDAVAuthManager" > + <property name="securityManager" ref="baseSecurityManager" /> + <property name="olatAuthenticationSpi" ref="olatAuthenticationSpi" /> + <property name="webDAVModule" ref="webdavModule" /> + </bean> + + <bean id="webdav_briefcase" class="org.olat.core.commons.modules.bc.BriefcaseWebDAVProvider" scope="prototype" /> <bean id="webdav_coursefolders" class="org.olat.course.CoursefolderWebDAVProvider" scope="prototype" /> <bean id="webdav_sharedfolders" class="org.olat.modules.sharedfolder.SharedFolderWebDAVProvider" scope="prototype" > @@ -62,5 +80,25 @@ <bean id="webdav_groupfolders" class="org.olat.group.GroupfoldersWebDAVProvider" scope="prototype"> <property name="collaborationManager" ref="collaborationManager" /> </bean> + + <!-- WebDAV admin. panel --> + <bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints"> + <property name="order" value="7219" /> + <property name="actionController"> + <bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype"> + <property name="className" value="org.olat.core.commons.services.webdav.ui.WebDAVAdminController"/> + </bean> + </property> + <property name="navigationKey" value="webdavadmin" /> + <property name="i18nActionKey" value="admin.menu.title"/> + <property name="i18nDescriptionKey" value="admin.menu.title.alt"/> + <property name="translationPackage" value="org.olat.core.commons.services.webdav.ui"/> + <property name="parentTreeNodeIdentifier" value="sysconfigParent" /> + <property name="extensionPoints"> + <list> + <value>org.olat.admin.SystemAdminMainController</value> + </list> + </property> + </bean> </beans> \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/DigestAuthentication.java b/src/main/java/org/olat/core/commons/services/webdav/manager/DigestAuthentication.java new file mode 100644 index 00000000000..ac2fbe52b76 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/DigestAuthentication.java @@ -0,0 +1,122 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.commons.services.webdav.manager; + +import java.util.StringTokenizer; + +/** + * + * Contains the informations needed to test the authentication with + * the Digest algorithm + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class DigestAuthentication { + + private final String username; + private final String realm; + private final String nonce; + private final String uri; + private final String cnonce; + private final String nc; + private final String response; + private final String qop; + + public DigestAuthentication(String username, String realm, String nonce, String uri, String cnonce, String nc, String response, String qop) { + this.username = username; + this.realm = realm; + this.nonce = nonce; + this.uri = uri; + this.cnonce = cnonce; + this.nc = nc; + this.response = response; + this.qop = qop; + } + + public String getUsername() { + return username; + } + + public String getRealm() { + return realm; + } + + public String getNonce() { + return nonce; + } + + public String getUri() { + return uri; + } + + public String getCnonce() { + return cnonce; + } + + public String getNc() { + return nc; + } + + public String getResponse() { + return response; + } + + public String getQop() { + return qop; + } + + public static DigestAuthentication parse(String request) { + String username = null; + String realm = null; + String nonce = null; + String uri = null; + String cnonce = null; + String nc = null; + String response = null; + String qop = null; + + StringTokenizer tokenizer = new StringTokenizer(request, ",\n"); + for(; tokenizer.hasMoreTokens(); ) { + String token=tokenizer.nextToken().trim(); + int index = token.indexOf('='); + String key = token.substring(0, index); + String val = token.substring(index + 1, token.length()).replace("\"", ""); + if("username".equals(key)) { + username = val; + } else if("realm".equals(key)) { + realm = val; + } else if("nonce".equals(key)) { + nonce = val; + } else if("uri".equals(key)) { + uri = val; + } else if("cnonce".equals(key)) { + cnonce = val; + } else if("nc".equals(key)) { + nc = val; + } else if("response".equals(key)) { + response = val; + } else if("qop".equals(key)) { + qop = val; + } + } + return new DigestAuthentication(username, realm, nonce, uri, cnonce, nc, response, qop); + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/EmptyWebResource.java b/src/main/java/org/olat/core/commons/services/webdav/manager/EmptyWebResource.java new file mode 100644 index 00000000000..79e1d7621d9 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/EmptyWebResource.java @@ -0,0 +1,100 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.commons.services.webdav.manager; + +import java.io.InputStream; + +import org.olat.core.commons.services.webdav.servlets.WebResource; + +/** + * An empty resource + * + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class EmptyWebResource implements WebResource { + + public static final EmptyWebResource EMPTY_WEB_RESOURCE = new EmptyWebResource(); + + public EmptyWebResource() { + // + } + + @Override + public long getLastModified() { + return -1; + } + + @Override + public String getLastModifiedHttp() { + return null; + } + + @Override + public boolean exists() { + return false; + } + + @Override + public boolean isDirectory() { + return false; + } + + @Override + public boolean isFile() { + return false; + } + + @Override + public String getName() { + return null; + } + + @Override + public long getContentLength() { + return 0; + } + + @Override + public String getETag() { + return null; + } + + @Override + public void setMimeType(String mimeType) { + // + } + + @Override + public String getMimeType() { + return null; + } + + @Override + public InputStream getInputStream() { + return null; + } + + @Override + public byte[] getContent() { + return null;//use the input stream instead + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/VFSDirContext.java b/src/main/java/org/olat/core/commons/services/webdav/manager/VFSDirContext.java deleted file mode 100644 index c8395b0143e..00000000000 --- a/src/main/java/org/olat/core/commons/services/webdav/manager/VFSDirContext.java +++ /dev/null @@ -1,1236 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -* <p> -*/ - -package org.olat.core.commons.services.webdav.manager; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; - -import javax.naming.Binding; -import javax.naming.Context; -import javax.naming.NameAlreadyBoundException; -import javax.naming.NameClassPair; -import javax.naming.NameNotFoundException; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.NoPermissionException; -import javax.naming.NotContextException; -import javax.naming.OperationNotSupportedException; -import javax.naming.directory.AttributeModificationException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.InvalidAttributesException; -import javax.naming.directory.InvalidSearchControlsException; -import javax.naming.directory.InvalidSearchFilterException; -import javax.naming.directory.ModificationItem; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; - -import org.apache.naming.resources.BaseDirContext; -import org.apache.naming.resources.Resource; -import org.apache.naming.resources.ResourceAttributes; -import org.olat.core.commons.modules.bc.meta.MetaInfo; -import org.olat.core.commons.modules.bc.meta.MetaInfoHelper; -import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged; -import org.olat.core.id.Identity; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; -import org.olat.core.util.FileUtils; -import org.olat.core.util.UserSession; -import org.olat.core.util.notifications.NotificationsManager; -import org.olat.core.util.notifications.SubscriptionContext; -import org.olat.core.util.vfs.Quota; -import org.olat.core.util.vfs.VFSConstants; -import org.olat.core.util.vfs.VFSContainer; -import org.olat.core.util.vfs.VFSItem; -import org.olat.core.util.vfs.VFSLeaf; -import org.olat.core.util.vfs.VFSManager; -import org.olat.core.util.vfs.VFSStatus; -import org.olat.core.util.vfs.callbacks.VFSSecurityCallback; -import org.olat.core.util.vfs.version.Versionable; -import org.olat.core.util.vfs.version.VersionsManager; - - -/** - * Filesystem Directory Context implementation helper class. - * - * @author Remy Maucherat - */ - -public class VFSDirContext extends BaseDirContext { - - private OLog log = Tracing.createLoggerFor(VFSDirContext.class); - - // -------------------------------------------------------------- Constants - - /** - * The descriptive information string for this implementation. - */ - public static int bufferSize = 2048; - - - private Identity identity; - private UserSession userSession; - - // ----------------------------------------------------------- Constructors - - /** - * Builds a file directory context using the given environment. - */ - public VFSDirContext() { - super(); - } - - /** - * Builds a file directory context using the given environment. - */ - public VFSDirContext(Hashtable env) { - super(env); - } - - // ----------------------------------------------------- Instance Variables - - /** - * The document base directory. - */ - protected VFSItem base = null; - - /** - * Absolute normalized filename of the base. - */ - protected String absoluteBase = null; - - /** - * Case sensitivity. - */ - protected boolean caseSensitive = true; - - /** - * Allow linking. - */ - protected boolean allowLinking = false; - - // ------------------------------------------------------------- Properties - - - - /** - * Set the document root. - * - * @param vfsItem The new document root - * @exception IllegalArgumentException if the specified value is not supported - * by this implementation - */ - public void setVirtualDocBase(VFSItem vfsItem) { - base = vfsItem; - } - - public Identity getIdentity() { - return identity; - } - - public void setIdentity(Identity identity) { - this.identity = identity; - } - - public UserSession getUserSession() { - return userSession; - } - - public void setUserSession(UserSession userSession) { - this.userSession = userSession; - } - - public VFSItem getVirtualDocBase() { - return base; - } - - /** - * Set the document root. - * - * @param docBase The new document root - * @exception IllegalArgumentException if the specified value is not supported - * by this implementation - * @exception IllegalArgumentException if this would create a malformed URL - */ - public void setDocBase(String docBase) { - - // disabled for VFSDirContext implementation... - throw new IllegalArgumentException("setDocBase(String) not supported by VFSDirCOntext."); - } - - /** - * Set case sensitivity. - */ - public void setCaseSensitive(boolean caseSensitive) { - this.caseSensitive = caseSensitive; - } - - /** - * Is case sensitive ? - */ - public boolean isCaseSensitive() { - return caseSensitive; - } - - /** - * Set allow linking. - */ - public void setAllowLinking(boolean allowLinking) { - this.allowLinking = allowLinking; - } - - /** - * Is linking allowed. - */ - public boolean getAllowLinking() { - return allowLinking; - } - - public void setBuffer(int bytes) { - bufferSize = bytes; - } - // --------------------------------------------------------- Public Methods - - /** - * Release any resources allocated for this directory context. - */ - public void release() { - - caseSensitive = true; - allowLinking = false; - absoluteBase = null; - base = null; - super.release(); - - } - - // -------------------------------------------------------- Context Methods - - /** - * Retrieves the named object. - * - * @param name the name of the object to look up - * @return the object bound to name - * @exception NamingException if a naming exception is encountered - */ - public Object lookup(String name) throws NamingException { - - VFSItem item = resolveFile(name); - if (item == null) throw new NamingException("Resources not found: " + name); - - if (item instanceof VFSContainer) { - VFSDirContext tempContext = new VFSDirContext(env); - tempContext.setVirtualDocBase(item); - return tempContext; - } else { - return new VFSResource(item); - } - } - - /** - * Check if resource can be copied (delegation to VFS item) - * @param name - * @return true: can copy; false: can not copy - */ - public boolean canCopy(String name) { - VFSItem item = resolveFile(name); - if (item != null && VFSConstants.YES.equals(item.canCopy())) return true; - else return false; - } - - /** - * Check if resource can be written (delegation to VFS item) - * @param name - * @return true: can write; false: can not write - */ - public boolean canWrite(String name) { - // resolve item if it already exists - VFSItem item = resolveFile(name); - if (item == null) { - // try to resolve parent in case the item does not yet exist - int lastSlash = name.lastIndexOf("/"); - if (lastSlash > 0) { - String containerName = name.substring(0, lastSlash); - item = resolveFile(containerName); - } - } - if (item == null) return false; - - VFSStatus status; - if (item instanceof VFSContainer) { - status = item.canWrite(); - } else { - // read/write is not defined on item level, only on directory level - status = item.getParentContainer().canWrite(); - } - return VFSConstants.YES.equals(status); - } - - /** - * Check if resource can be deleted (delegation to VFS item) - * @param name - * @return true: can delete; false: can not delete - */ - public boolean canDelete(String name) { - VFSItem item = resolveFile(name); - if (item != null && VFSConstants.YES.equals(item.canDelete())) { - return !MetaInfoHelper.isLocked(item, userSession); - } - else return false; - } - - /** - * Check if resource can be renamed (delegation to VFS item) - * @param name - * @return true: can rename; false: can not rename - */ - public boolean canRename(String name) { - VFSItem item = resolveFile(name); - if (item != null && VFSConstants.YES.equals(item.canRename())) { - return !MetaInfoHelper.isLocked(item, userSession); - } - else return false; - } - - /** - * Unbinds the named object. Removes the terminal atomic name in name from the - * target context--that named by all but the terminal atomic part of name. - * <p> - * This method is idempotent. It succeeds even if the terminal atomic name is - * not bound in the target context, but throws NameNotFoundException if any of - * the intermediate contexts do not exist. - * - * @param name the name to bind; may not be empty - * @exception NameNotFoundException if an intermediate context does not exist - * @exception NamingException if a naming exception is encountered - */ - public void unbind(String name) throws NamingException { - VFSItem item = resolveFile(name); - if (item == null) { - throw new NamingException("Resources not found" + name); - } - - if (item != null && VFSConstants.YES.equals(item.canDelete())) { - if(MetaInfoHelper.isLocked(item, userSession)) { - throw new NamingException("File locked: " + name); - } - } - - VFSStatus status = item.delete(); - if (status == VFSConstants.NO) { - throw new NamingException("resources unbind failed" + name); - } - } - - /** - * Binds a new name to the object bound to an old name, and unbinds the old - * name. Both names are relative to this context. Any attributes associated - * with the old name become associated with the new name. Intermediate - * contexts of the old name are not changed. - * - * @param oldName the name of the existing binding; may not be empty - * @param newName the name of the new binding; may not be empty - * @exception NameAlreadyBoundException if newName is already bound - * @exception NamingException if a naming exception is encountered - */ - public void rename(String oldName, String newName) throws NamingException { - - VFSItem oldFile = resolveFile(oldName); - if (oldFile == null) { - throw new NamingException("Resources not found: " + oldName); - } - if(MetaInfoHelper.isLocked(oldFile, userSession)) { - throw new NoPermissionException("Locked"); - } - - VFSItem newFile = resolveFile(newName); - if (newFile != null) { - throw new NameAlreadyBoundException(); - } - - VFSStatus status = oldFile.rename(newName); - if (status == VFSConstants.NO) { - throw new NameAlreadyBoundException(); - } - } - - /** - * Enumerates the names bound in the named context, along with the class names - * of objects bound to them. The contents of any subcontexts are not included. - * <p> - * If a binding is added to or removed from this context, its effect on an - * enumeration previously returned is undefined. - * - * @param name the name of the context to list - * @return an enumeration of the names and class names of the bindings in this - * context. Each element of the enumeration is of type NameClassPair. - * @exception NamingException if a naming exception is encountered - */ - public NamingEnumeration<NameClassPair> list(String name) throws NamingException { - - VFSItem file = resolveFile(name); - if (file == null) throw new NamingException("Resources not found: " + name); - return new NamingContextEnumeration(list(file).iterator()); - - } - - /** - * Enumerates the names bound in the named context, along with the objects - * bound to them. The contents of any subcontexts are not included. - * <p> - * If a binding is added to or removed from this context, its effect on an - * enumeration previously returned is undefined. - * - * @param name the name of the context to list - * @return an enumeration of the bindings in this context. Each element of the - * enumeration is of type Binding. - * @exception NamingException if a naming exception is encountered - */ - public NamingEnumeration<Binding> listBindings(String name) throws NamingException { - return new NamingContextEnumeration2(list(name)); - } - - /** - * Destroys the named context and removes it from the namespace. Any - * attributes associated with the name are also removed. Intermediate contexts - * are not destroyed. - * <p> - * This method is idempotent. It succeeds even if the terminal atomic name is - * not bound in the target context, but throws NameNotFoundException if any of - * the intermediate contexts do not exist. In a federated naming system, a - * context from one naming system may be bound to a name in another. One can - * subsequently look up and perform operations on the foreign context using a - * composite name. However, an attempt destroy the context using this - * composite name will fail with NotContextException, because the foreign - * context is not a "subcontext" of the context in which it is bound. Instead, - * use unbind() to remove the binding of the foreign context. Destroying the - * foreign context requires that the destroySubcontext() be performed on a - * context from the foreign context's "native" naming system. - * - * @param name the name of the context to be destroyed; may not be empty - * @exception NameNotFoundException if an intermediate context does not exist - * @exception NotContextException if the name is bound but does not name a - * context, or does not name a context of the appropriate type - */ - public void destroySubcontext(String name) throws NamingException { - unbind(name); - } - - /** - * Retrieves the named object, following links except for the terminal atomic - * component of the name. If the object bound to name is not a link, returns - * the object itself. - * - * @param name the name of the object to look up - * @return the object bound to name, not following the terminal link (if any). - * @exception NamingException if a naming exception is encountered - */ - public Object lookupLink(String name) throws NamingException { - // Note : Links are not supported - return lookup(name); - } - - /** - * Retrieves the full name of this context within its own namespace. - * <p> - * Many naming services have a notion of a "full name" for objects in their - * respective namespaces. For example, an LDAP entry has a distinguished name, - * and a DNS record has a fully qualified name. This method allows the client - * application to retrieve this name. The string returned by this method is - * not a JNDI composite name and should not be passed directly to context - * methods. In naming systems for which the notion of full name does not make - * sense, OperationNotSupportedException is thrown. - * - * @return this context's name in its own namespace; never null - * @exception OperationNotSupportedException if the naming system does not - * have the notion of a full name - * @exception NamingException if a naming exception is encountered - */ - public String getNameInNamespace() { - return docBase; - } - - // ----------------------------------------------------- DirContext Methods - - /** - * Retrieves selected attributes associated with a named object. See the class - * description regarding attribute models, attribute type names, and - * operational attributes. - * - * @return the requested attributes; never null - * @param name the name of the object from which to retrieve attributes - * @param attrIds the identifiers of the attributes to retrieve. null - * indicates that all attributes should be retrieved; an empty array - * indicates that none should be retrieved - * @exception NamingException if a naming exception is encountered - */ - public Attributes getAttributes(String name, String[] attrIds) throws NamingException { - - // Building attribute list - VFSItem file = resolveFile(name); - if (file == null) throw new NamingException("Resources not found" + name); - return new VFSResourceAttributes(file); - - } - - /** - * Modifies the attributes associated with a named object. The order of the - * modifications is not specified. Where possible, the modifications are - * performed atomically. - * - * @param name the name of the object whose attributes will be updated - * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, - * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE - * @param attrs the attributes to be used for the modification; may not be - * null - * @exception AttributeModificationException if the modification cannot be - * completed successfully - * @exception NamingException if a naming exception is encountered - */ - public void modifyAttributes(String name, int mod_op, Attributes attrs) { - // not implemented - } - - /** - * Modifies the attributes associated with a named object using an an ordered - * list of modifications. The modifications are performed in the order - * specified. Each modification specifies a modification operation code and an - * attribute on which to operate. Where possible, the modifications are - * performed atomically. - * - * @param name the name of the object whose attributes will be updated - * @param mods an ordered sequence of modifications to be performed; may not - * be null - * @exception AttributeModificationException if the modification cannot be - * completed successfully - * @exception NamingException if a naming exception is encountered - */ - public void modifyAttributes(String name, ModificationItem[] mods) { - // not implemented - } - - /** - * @see javax.naming.Context#bind(java.lang.String, java.lang.Object) - */ - public void bind(String name, Object obj) throws NamingException { - bind(name, obj, null); - } - - /** - * Binds a name to an object, along with associated attributes. If attrs is - * null, the resulting binding will have the attributes associated with obj if - * obj is a DirContext, and no attributes otherwise. If attrs is non-null, the - * resulting binding will have attrs as its attributes; any attributes - * associated with obj are ignored. - * - * @param name the name to bind; may not be empty - * @param obj the object to bind; possibly null - * @param attrs the attributes to associate with the binding - * @exception NameAlreadyBoundException if name is already bound - * @exception InvalidAttributesException if some "mandatory" attributes of the - * binding are not supplied - * @exception NamingException if a naming exception is encountered - */ - public void bind(String name, Object obj, Attributes attrs) throws NamingException { - - // Note: No custom attributes allowed - VFSItem file = resolveFile(name); - if (file != null) throw new NameAlreadyBoundException("Resources already bound" + name); - - int lastSlash = name.lastIndexOf('/'); - if (lastSlash == -1) throw new NamingException(); - String parent = name.substring(0, lastSlash); - VFSItem folder = resolveFile(parent); - - if (folder == null || (!(folder instanceof VFSContainer))) - throw new NamingException("Resources bind failed: " + name); - String newName = name.substring(lastSlash + 1); - VFSLeaf childLeaf = ((VFSContainer)folder).createChildLeaf(newName); - if (childLeaf == null) - throw new NamingException("Resources bind failed: " + name); - copyVFS(childLeaf, name, obj, attrs); - - VFSSecurityCallback callback = folder.getLocalSecurityCallback(); - if(callback != null && callback.getSubscriptionContext() != null) { - SubscriptionContext subContext = callback.getSubscriptionContext(); - NotificationsManager.getInstance().markPublisherNews(subContext, null, true); - } - - if(childLeaf instanceof MetaTagged) { - MetaInfo infos = ((MetaTagged)childLeaf).getMetaInfo(); - if(infos != null && infos.getAuthorIdentity() == null) { - infos.setAuthor(userSession.getIdentity()); - infos.clearThumbnails(); - infos.write(); - } - } - - //used by move operations - if(obj instanceof VFSResource) { - VFSResource vfsResource = (VFSResource)obj; - if(vfsResource.vfsItem instanceof Versionable - && ((Versionable)vfsResource.vfsItem).getVersions().isVersioned()) { - VFSLeaf currentVersion = (VFSLeaf)vfsResource.vfsItem; - VersionsManager.getInstance().move(currentVersion, childLeaf, identity); - } - } - } - - /** - * Binds a name to an object, along with associated attributes, overwriting - * any existing binding. If attrs is null and obj is a DirContext, the - * attributes from obj are used. If attrs is null and obj is not a DirContext, - * any existing attributes associated with the object already bound in the - * directory remain unchanged. If attrs is non-null, any existing attributes - * associated with the object already bound in the directory are removed and - * attrs is associated with the named object. If obj is a DirContext and attrs - * is non-null, the attributes of obj are ignored. - * - * @param name the name to bind; may not be empty - * @param obj the object to bind; possibly null - * @param attrs the attributes to associate with the binding - * @exception InvalidAttributesException if some "mandatory" attributes of the - * binding are not supplied - * @exception NamingException if a naming exception is encountered - */ - public void rebind(String name, Object obj, Attributes attrs) throws NamingException { - - // Note: No custom attributes allowed - // Check obj type - - VFSItem vfsItem = resolveFile(name); - if (vfsItem == null || (!(vfsItem instanceof VFSLeaf))) { - throw new NamingException("Resources bind failed" + name); - } - if(MetaInfoHelper.isLocked(vfsItem, userSession)) { - throw new NoPermissionException("Locked"); - } - - VFSLeaf file = (VFSLeaf)vfsItem; - if(file instanceof Versionable && ((Versionable)file).getVersions().isVersioned()) { - if(file.getSize() == 0) { - VersionsManager.getInstance().createVersionsFor(file, true); - } else { - VersionsManager.getInstance().addToRevisions((Versionable)file, identity, ""); - } - } - - copyVFS(file, name, obj, attrs); - if(file instanceof MetaTagged) { - MetaInfo infos = ((MetaTagged)file).getMetaInfo(); - if(infos != null && infos.getAuthorIdentity() == null) { - infos.setAuthor(userSession.getIdentity()); - infos.clearThumbnails(); - infos.write(); - } - } - - //used by move operations - if(obj instanceof VFSResource) { - VFSResource vfsResource = (VFSResource)obj; - if(vfsResource.vfsItem instanceof Versionable - && ((Versionable)vfsResource.vfsItem).getVersions().isVersioned()) { - Versionable currentVersion = (Versionable)vfsResource.vfsItem; - VersionsManager.getInstance().move(currentVersion, file.getParentContainer()); - } - } - } - - private void copyVFS(VFSLeaf file, String name, Object obj, Attributes attrs) throws NamingException { - InputStream is = null; - if (obj instanceof Resource) { - try { - is = ((Resource) obj).streamContent(); - } catch (IOException e) { - // ignore, check further - } - } else if (obj instanceof InputStream) { - is = (InputStream) obj; - } else if (obj instanceof DirContext) { - createSubcontext(name, attrs); - return; - } - if (is == null) { - throw new NamingException("Resources bind failed: " + name); - } - - // Try to get Quota - long quotaLeft = -1; - boolean withQuotaCheck = false; - VFSContainer parentContainer = file.getParentContainer(); - if (parentContainer != null) { - quotaLeft = VFSManager.getQuotaLeftKB(parentContainer); - if (quotaLeft != Quota.UNLIMITED) { - quotaLeft = quotaLeft * 1024; // convert from kB - withQuotaCheck = true; - } else { - withQuotaCheck = false; - } - } - // Open os - OutputStream os = null; - byte buffer[] = new byte[bufferSize]; - int len = -1; - try { - os = file.getOutputStream(false); - while (true) { - len = is.read(buffer); - if (len == -1) break; - if (withQuotaCheck) { - // re-calculate quota and check - quotaLeft = quotaLeft - len; - if (quotaLeft < 0) { - log.info("Quota exceeded: " + file); - throw new NamingException("Quota exceeded."); - } - } - os.write(buffer, 0, len); - } - } catch (Exception e) { - FileUtils.closeSafely(os); // close first, in order to be able to delete any reamins of the file - file.delete(); - if (e instanceof NamingException) { - throw (NamingException)e; - } - throw new NamingException("Resources bind failed"); - } finally { - FileUtils.closeSafely(os); - FileUtils.closeSafely(is); - } - } - - /** - * @see javax.naming.Context#createSubcontext(java.lang.String) - */ - public Context createSubcontext(String name) throws NamingException { - return createSubcontext(name, null); - } - - /** - * Creates and binds a new context, along with associated attributes. This - * method creates a new subcontext with the given name, binds it in the target - * context (that named by all but terminal atomic component of the name), and - * associates the supplied attributes with the newly created object. All - * intermediate and target contexts must already exist. If attrs is null, this - * method is equivalent to Context.createSubcontext(). - * - * @param name the name of the context to create; may not be empty - * @param attrs the attributes to associate with the newly created context - * @return the newly created context - * @exception NameAlreadyBoundException if the name is already bound - * @exception InvalidAttributesException if attrs does not contain all the - * mandatory attributes required for creation - * @exception NamingException if a naming exception is encountered - */ - public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { - - VFSItem file = resolveFile(name); - if (file != null) throw new NameAlreadyBoundException("Resources already bound" + name); - - int lastSlash = name.lastIndexOf('/'); - if (lastSlash == -1) throw new NamingException(); - String parent = name.substring(0, lastSlash); - VFSItem folder = resolveFile(parent); - if (folder == null || (!(folder instanceof VFSContainer))) - throw new NamingException("Resources bind failed" + name); - String newName = name.substring(lastSlash + 1); - VFSItem childContainer = ((VFSContainer)folder).createChildContainer(newName); - if (childContainer == null) - throw new NamingException("Resources bind failed" + name); - return (DirContext)lookup(name); - - } - - /** - * Retrieves the schema associated with the named object. The schema describes - * rules regarding the structure of the namespace and the attributes stored - * within it. The schema specifies what types of objects can be added to the - * directory and where they can be added; what mandatory and optional - * attributes an object can have. The range of support for schemas is - * directory-specific. - * - * @param name the name of the object whose schema is to be retrieved - * @return the schema associated with the context; never null - * @exception OperationNotSupportedException if schema not supported - * @exception NamingException if a naming exception is encountered - */ - public DirContext getSchema(String name) throws NamingException { - throw new OperationNotSupportedException(); - } - - /** - * Retrieves a context containing the schema objects of the named object's - * class definitions. - * - * @param name the name of the object whose object class definition is to be - * retrieved - * @return the DirContext containing the named object's class definitions; - * never null - * @exception OperationNotSupportedException if schema not supported - * @exception NamingException if a naming exception is encountered - */ - public DirContext getSchemaClassDefinition(String name) throws NamingException { - throw new OperationNotSupportedException(); - } - - /** - * Searches in a single context for objects that contain a specified set of - * attributes, and retrieves selected attributes. The search is performed - * using the default SearchControls settings. - * - * @param name the name of the context to search - * @param matchingAttributes the attributes to search for. If empty or null, - * all objects in the target context are returned. - * @param attributesToReturn the attributes to return. null indicates that all - * attributes are to be returned; an empty array indicates that none - * are to be returned. - * @return a non-null enumeration of SearchResult objects. Each SearchResult - * contains the attributes identified by attributesToReturn and the - * name of the corresponding object, named relative to the context - * named by name. - * @exception NamingException if a naming exception is encountered - */ - public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes, String[] attributesToReturn) { - return null; - } - - /** - * Searches in a single context for objects that contain a specified set of - * attributes. This method returns all the attributes of such objects. It is - * equivalent to supplying null as the atributesToReturn parameter to the - * method search(Name, Attributes, String[]). - * - * @param name the name of the context to search - * @param matchingAttributes the attributes to search for. If empty or null, - * all objects in the target context are returned. - * @return a non-null enumeration of SearchResult objects. Each SearchResult - * contains the attributes identified by attributesToReturn and the - * name of the corresponding object, named relative to the context - * named by name. - * @exception NamingException if a naming exception is encountered - */ - public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes) { - return null; - } - - /** - * Searches in the named context or object for entries that satisfy the given - * search filter. Performs the search as specified by the search controls. - * - * @param name the name of the context or object to search - * @param filter the filter expression to use for the search; may not be null - * @param cons the search controls that control the search. If null, the - * default search controls are used (equivalent to (new - * SearchControls())). - * @return an enumeration of SearchResults of the objects that satisfy the - * filter; never null - * @exception InvalidSearchFilterException if the search filter specified is - * not supported or understood by the underlying directory - * @exception InvalidSearchControlsException if the search controls contain - * invalid settings - * @exception NamingException if a naming exception is encountered - */ - public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls cons) { - return null; - } - - /** - * Searches in the named context or object for entries that satisfy the given - * search filter. Performs the search as specified by the search controls. - * - * @param name the name of the context or object to search - * @param filterExpr the filter expression to use for the search. The - * expression may contain variables of the form "{i}" where i is a - * nonnegative integer. May not be null. - * @param filterArgs the array of arguments to substitute for the variables in - * filterExpr. The value of filterArgs[i] will replace each - * occurrence of "{i}". If null, equivalent to an empty array. - * @param cons the search controls that control the search. If null, the - * default search controls are used (equivalent to (new - * SearchControls())). - * @return an enumeration of SearchResults of the objects that satisy the - * filter; never null - * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} - * expressions where i is outside the bounds of the array - * filterArgs - * @exception InvalidSearchControlsException if cons contains invalid settings - * @exception InvalidSearchFilterException if filterExpr with filterArgs - * represents an invalid search filter - * @exception NamingException if a naming exception is encountered - */ - public NamingEnumeration<SearchResult> search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) { - return null; - } - - // ------------------------------------------------------ Protected Methods - - /** - * Resolve a file relative to this base. - * Make sure, paths are relative - */ - protected VFSItem resolveFile(String name) { - if (name == null) name = ""; - if (name.length() > 0 && name.charAt(0) == '/') name = name.substring(1); - return base.resolve(name); - } - - /** - * List the resources which are members of a collection. - * - * @param file Collection - * @return Vector containg NamingEntry objects - */ - protected List<NamingEntry> list(VFSItem file) { - - List<NamingEntry> entries = new ArrayList<NamingEntry>(); - - if (!(file instanceof VFSContainer)) return entries; - List<VFSItem> children = ((VFSContainer)file).getItems(); - - NamingEntry entry = null; - for (VFSItem currentFile:children) { - Object object; - if (currentFile instanceof VFSContainer) { - VFSDirContext tempContext = new VFSDirContext(env); - tempContext.setVirtualDocBase(currentFile); - object = tempContext; - } else { - object = new VFSResource(currentFile); - } - entry = new NamingEntry(currentFile.getName(), object, NamingEntry.ENTRY); - entries.add(entry); - } - return entries; - } - - // ----------------------------------------------- FileResource Inner Class - - /** - * This specialized resource implementation avoids opening the IputStream to - * the file right away (which would put a lock on the file). - */ - protected class VFSResource extends Resource { - - // -------------------------------------------------------- Constructor - - public VFSResource(VFSItem fileObject) { - this.vfsItem = fileObject; - } - - // --------------------------------------------------- Member Variables - - /** - * Associated file object. - */ - protected VFSItem vfsItem; - - /** - * File length. - */ - protected long length = -1L; - - // --------------------------------------------------- Resource Methods - - /** - * Content accessor. - * - * @return InputStream - */ - public InputStream streamContent() throws IOException { - if (!(vfsItem instanceof VFSLeaf)) throw new IOException("Can't get stream for VFSItem."); - VFSLeaf vfsLeaf = (VFSLeaf)vfsItem; - return vfsLeaf.getInputStream(); - } - - } - - // ------------------------------------- FileResourceAttributes Inner Class - - /** - * This specialized resource attribute implementation does some lazy reading - * (to speed up simple checks, like checking the last modified date). - */ - protected class VFSResourceAttributes extends ResourceAttributes { - - private static final long serialVersionUID = 4144775626100809634L; - - // -------------------------------------------------------- Constructor - - public VFSResourceAttributes(VFSItem vfsItem) { - this.vfsItem = vfsItem; - } - - // --------------------------------------------------- Member Variables - - protected VFSItem vfsItem; - - protected boolean accessed = false; - - protected String canonicalPath = null; - - // ----------------------------------------- ResourceAttributes Methods - - /** - * Is collection. - */ - public boolean isCollection() { - if (!accessed) { - collection = (vfsItem instanceof VFSContainer); - } - return collection; - } - - /** - * Get content length. - * - * @return content length value - */ - public long getContentLength() { - if (contentLength != -1L) return contentLength; - if (isCollection()) contentLength = 0; - else { - VFSLeaf leaf = (VFSLeaf)vfsItem; - contentLength = leaf.getSize(); - } - return contentLength; - } - - /** - * Get creation time. - * - * @return creation time value - */ - public long getCreation() { - if (creation != -1L) return creation; - if (isCollection()) creation = 0; - else { - creation = vfsItem.getLastModified(); - } - return creation; - } - - /** - * Get creation date. - * - * @return Creation date value - */ - public Date getCreationDate() { - return new Date(getCreation()); - } - - /** - * Get last modified time. - * - * @return lastModified time value - */ - public long getLastModified() { - if (lastModified != -1L) return lastModified; - if (isCollection()) lastModified = 0; - else { - lastModified = vfsItem.getLastModified(); - } - return lastModified; - } - - /** - * Get lastModified date. - * - * @return LastModified date value - */ - public Date getLastModifiedDate() { - return new Date(getLastModified()); - } - - /** - * Get name. - * - * @return Name value - */ - public String getName() { - if (name == null) name = vfsItem.getName(); - return name; - } - - /** - * Get resource type. - * - * @return String resource type - */ - public String getResourceType() { - if (!accessed) isCollection(); // needed to initialize - return super.getResourceType(); - } - - } - - /** - * Represents a binding in a NamingContext. - * - * @author Remy Maucherat - */ - - public class NamingEntry { - - // -------------------------------------------------------------- - // Constants - - public static final int ENTRY = 0; - public static final int LINK_REF = 1; - public static final int REFERENCE = 2; - - public static final int CONTEXT = 10; - - // ----------------------------------------------------------- - // Constructors - - public NamingEntry(String name, Object value, int type) { - this.name = name; - this.value = value; - this.type = type; - } - - // ----------------------------------------------------- Instance Variables - - /** - * The type instance variable is used to avoid unsing RTTI when doing - * lookups. - */ - public int type; - public String name; - public Object value; - - // --------------------------------------------------------- Object Methods - - public boolean equals(Object obj) { - if ((obj != null) && (obj instanceof NamingEntry)) { - return name.equals(((NamingEntry) obj).name); - } else { - return false; - } - } - - public int hashCode() { - return name.hashCode(); - } - - } - - /** - * Naming enumeration implementation. - * - * @author Remy Maucherat - */ - - public class NamingContextEnumeration implements NamingEnumeration<NameClassPair> { - - // ----------------------------------------------------------- Constructors - - public NamingContextEnumeration(Iterator<NamingEntry> entries) { - iterator = entries; - } - - // -------------------------------------------------------------- Variables - - /** - * Underlying enumeration. - */ - protected Iterator<NamingEntry> iterator; - - // --------------------------------------------------------- Public Methods - - /** - * Retrieves the next element in the enumeration. - */ - public NameClassPair next() { - return nextElement(); - } - - /** - * Determines whether there are any more elements in the enumeration. - */ - public boolean hasMore() { - return iterator.hasNext(); - } - - /** - * Closes this enumeration. - */ - public void close() { - //nothing to do - } - - public boolean hasMoreElements() { - return iterator.hasNext(); - } - - public NameClassPair nextElement() { - NamingEntry entry = iterator.next(); - return new NameClassPair(entry.name, entry.value.getClass().getName()); - } - } - - public class NamingContextEnumeration2 implements NamingEnumeration<Binding> { - - private final NamingEnumeration<NameClassPair> entries; - - public NamingContextEnumeration2(NamingEnumeration<NameClassPair> entries) { - this.entries = entries; - } - - public void close() { - //nothing to do - } - - public boolean hasMore() throws NamingException { - return entries.hasMore(); - } - - public Binding next() throws NamingException { - NameClassPair pair = entries.next(); - return new Binding(pair.getName(), pair.getClassName(), null); - } - - public boolean hasMoreElements() { - return entries.hasMoreElements(); - } - - public Binding nextElement() { - try { - return next(); - } catch (NamingException e) { - e.printStackTrace(); - return null; - } - } - } -} diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/VFSResource.java b/src/main/java/org/olat/core/commons/services/webdav/manager/VFSResource.java new file mode 100644 index 00000000000..d1823765c7d --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/VFSResource.java @@ -0,0 +1,121 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.commons.services.webdav.manager; + +import java.io.InputStream; +import java.util.Date; + +import org.olat.core.commons.services.webdav.servlets.ConcurrentDateFormat; +import org.olat.core.commons.services.webdav.servlets.WebResource; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VFSItem; +import org.olat.core.util.vfs.VFSLeaf; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class VFSResource implements WebResource { + + private final VFSItem item; + private String mimeType; + private volatile String weakETag; + + public VFSResource(VFSItem item) { + this.item = item; + } + + protected VFSItem getItem() { + return item; + } + + @Override + public long getLastModified() { + return item.getLastModified(); + } + + @Override + public String getLastModifiedHttp() { + return ConcurrentDateFormat.formatRfc1123(new Date(getLastModified())); + } + + @Override + public boolean exists() { + return item != null && item.exists(); + } + + @Override + public boolean isDirectory() { + return (item instanceof VFSContainer); + } + + @Override + public boolean isFile() { + return (item instanceof VFSLeaf); + } + + @Override + public String getName() { + return item.getName(); + } + + @Override + public long getContentLength() { + return (item instanceof VFSLeaf ? ((VFSLeaf)item).getSize() : null); + } + + @Override + public String getETag() { + if (weakETag == null) { + synchronized (this) { + if (weakETag == null) { + long contentLength = getContentLength(); + long lastModified = getLastModified(); + if ((contentLength >= 0) || (lastModified >= 0)) { + weakETag = "W/\"" + contentLength + "-" + + lastModified + "\""; + } + } + } + } + return weakETag; + } + + @Override + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + @Override + public String getMimeType() { + return mimeType; + } + + @Override + public InputStream getInputStream() { + return (item instanceof VFSLeaf ? ((VFSLeaf)item).getInputStream() : null); + } + + @Override + public byte[] getContent() { + return null;//use the input stream instead + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/VFSResourceRoot.java b/src/main/java/org/olat/core/commons/services/webdav/manager/VFSResourceRoot.java new file mode 100644 index 00000000000..ad1d07ed8ae --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/VFSResourceRoot.java @@ -0,0 +1,262 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.commons.services.webdav.manager; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.pdfbox.io.IOUtils; +import org.olat.core.commons.modules.bc.meta.MetaInfo; +import org.olat.core.commons.modules.bc.meta.MetaInfoHelper; +import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged; +import org.olat.core.commons.services.webdav.servlets.WebResource; +import org.olat.core.commons.services.webdav.servlets.WebResourceRoot; +import org.olat.core.id.Identity; +import org.olat.core.util.FileUtils; +import org.olat.core.util.UserSession; +import org.olat.core.util.notifications.NotificationsManager; +import org.olat.core.util.notifications.SubscriptionContext; +import org.olat.core.util.vfs.VFSConstants; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VFSItem; +import org.olat.core.util.vfs.VFSLeaf; +import org.olat.core.util.vfs.VFSStatus; +import org.olat.core.util.vfs.callbacks.VFSSecurityCallback; +import org.olat.core.util.vfs.version.Versionable; +import org.olat.core.util.vfs.version.VersionsManager; + + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class VFSResourceRoot implements WebResourceRoot { + + private final Identity identity; + private final VFSContainer base; + private UserSession userSession; + + public VFSResourceRoot(Identity identity, VFSContainer root) { + this.identity = identity; + this.base = root; + } + + public Identity getIdentity() { + return identity; + } + + public VFSContainer getRoot() { + return base; + } + + public void setUserSession(UserSession userSession) { + this.userSession = userSession; + } + + @Override + public boolean canWrite(String name) { + // resolve item if it already exists + VFSItem item = resolveFile(name); + if (item == null) { + // try to resolve parent in case the item does not yet exist + int lastSlash = name.lastIndexOf("/"); + if (lastSlash > 0) { + String containerName = name.substring(0, lastSlash); + item = resolveFile(containerName); + } + } + if (item == null) { + return false; + } + + VFSStatus status; + if (item instanceof VFSContainer) { + status = item.canWrite(); + } else { + // read/write is not defined on item level, only on directory level + status = item.getParentContainer().canWrite(); + } + return VFSConstants.YES.equals(status); + } + + @Override + public boolean canRename(String name) { + VFSItem item = resolveFile(name); + if (item != null && VFSConstants.YES.equals(item.canRename())) { + return !MetaInfoHelper.isLocked(item, userSession); + } else { + return false; + } + } + + @Override + public boolean canDelete(String path) { + VFSItem item = resolveFile(path); + if (item != null && VFSConstants.YES.equals(item.canDelete())) { + return !MetaInfoHelper.isLocked(item, userSession); + } else { + return false; + } + } + + @Override + public WebResource getResource(String path) { + VFSItem file = resolveFile(path); + if(file == null) { + return EmptyWebResource.EMPTY_WEB_RESOURCE; + } + return new VFSResource(file); + } + + @Override + public Collection<VFSItem> list(String path) { + VFSItem file = resolveFile(path); + if(file instanceof VFSContainer) { + VFSContainer container = (VFSContainer)file; + List<VFSItem> children = container.getItems(); + return children; + } else { + return Collections.emptyList(); + } + } + + @Override + public boolean mkdir(String path) { + //remove trailing / + if(path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + int lastSlash = path.lastIndexOf('/'); + if (lastSlash == -1) return false; + + String parentPath = path.substring(0, lastSlash); + VFSItem parentItem = resolveFile(parentPath); + if (parentItem instanceof VFSLeaf) { + return false; + } else if (parentItem instanceof VFSContainer) { + String name = path.substring(lastSlash + 1); + VFSContainer folder = (VFSContainer)parentItem; + VFSContainer dir = folder.createChildContainer(name); + return dir != null && dir.exists(); + } + return false; + } + + @Override + public boolean write(String path, InputStream is, boolean overwrite) { + VFSLeaf childLeaf; + VFSItem file = resolveFile(path); + if (file instanceof VFSLeaf) { + if(overwrite) { + //overwrite the file + childLeaf = (VFSLeaf)file; + + //versioning + if(childLeaf instanceof Versionable && ((Versionable)childLeaf).getVersions().isVersioned()) { + if(childLeaf.getSize() == 0) { + VersionsManager.getInstance().createVersionsFor(childLeaf, true); + } else { + VersionsManager.getInstance().addToRevisions((Versionable)childLeaf, identity, ""); + } + } + } else { + return false; + } + } else if (file instanceof VFSContainer) { + return false; + } else { + //create a new file + int lastSlash = path.lastIndexOf('/'); + if (lastSlash == -1) return false; + String parentPath = path.substring(0, lastSlash); + VFSItem parentItem = resolveFile(parentPath); + if(parentItem instanceof VFSContainer) { + VFSContainer folder = (VFSContainer)parentItem; + String name = path.substring(lastSlash + 1); + childLeaf = folder.createChildLeaf(name); + } else { + return false; + } + } + + if(childLeaf == null) { + return false; + } + + try { + byte[] content = IOUtils.toByteArray(is); + System.out.println("Write: " + path + " -> " + content.length); + ByteArrayInputStream in = new ByteArrayInputStream(content); + + FileUtils.copy(in, childLeaf.getOutputStream(false)); + } catch (IOException e) { + e.printStackTrace(); + } + + VFSContainer folder = childLeaf.getParentContainer(); + VFSSecurityCallback callback = folder.getLocalSecurityCallback(); + if(callback != null && callback.getSubscriptionContext() != null) { + SubscriptionContext subContext = callback.getSubscriptionContext(); + NotificationsManager.getInstance().markPublisherNews(subContext, null, true); + } + + if(childLeaf instanceof MetaTagged && identity != null) { + MetaInfo infos = ((MetaTagged)childLeaf).getMetaInfo(); + if(infos != null && infos.getAuthorIdentity() == null) { + infos.setAuthor(identity); + infos.clearThumbnails(); + infos.write(); + } + } + + return true; + } + + @Override + public boolean delete(WebResource resource) { + boolean deleted = false; + if(resource instanceof VFSResource) { + VFSResource vfsResource = (VFSResource)resource; + VFSItem item = vfsResource.getItem(); + if (item != null && VFSConstants.YES.equals(item.canDelete()) + && !MetaInfoHelper.isLocked(item, userSession)) { + VFSStatus status = item.delete(); + deleted = (status == VFSConstants.YES || status == VFSConstants.SUCCESS); + } + } + return deleted; + } + + /** + * Resolve a file relative to this base. + * Make sure, paths are relative + */ + private VFSItem resolveFile(String name) { + if (name == null) name = ""; + if (name.length() > 0 && name.charAt(0) == '/') name = name.substring(1); + return base.resolve(name); + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java index a9b84b57432..458ff3ceaf1 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java @@ -22,10 +22,12 @@ package org.olat.core.commons.services.webdav.manager; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; +import org.olat.core.commons.services.webdav.WebDAVModule; import org.olat.core.id.Identity; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.Encoder; import org.olat.core.util.Encoder.Algorithm; import org.olat.login.LoginModule; import org.olat.login.auth.AuthenticationSPI; @@ -44,12 +46,22 @@ import org.olat.login.auth.OLATAuthManager; public class WebDAVAuthManager implements AuthenticationSPI { public static final String PROVIDER_WEBDAV = "WEBDAV"; + public static final String PROVIDER_HA1 = "HA1"; private static final OLog log = Tracing.createLoggerFor(WebDAVAuthManager.class); - + + private WebDAVModule webDAVModule; private BaseSecurity securityManager; private OLATAuthManager olatAuthenticationSpi; + /** + * [used by Spring] + * @param webDAVModule + */ + public void setWebDAVModule(WebDAVModule webDAVModule) { + this.webDAVModule = webDAVModule; + } + /** * [used by Spring] * @param securityManager @@ -65,6 +77,31 @@ public class WebDAVAuthManager implements AuthenticationSPI { public void setOlatAuthenticationSpi(OLATAuthManager olatAuthenticationSpi) { this.olatAuthenticationSpi = olatAuthenticationSpi; } + + public Identity digestAuthentication(String httpMethod, DigestAuthentication digestAuth) { + String username = digestAuth.getUsername(); + + Authentication olatAuth = securityManager.findAuthenticationByAuthusername(username, WebDAVAuthManager.PROVIDER_HA1); + if(olatAuth != null) { + if("auth".equals(digestAuth.getQop())) { + String nonce = digestAuth.getNonce(); + String response = digestAuth.getResponse(); + + String ha1 = olatAuth.getCredential(); + + String a2 = httpMethod + ":" + digestAuth.getUri(); + String ha2 = Encoder.md5hash(a2); + + String ver = ha1 + ":" + nonce + ":" + digestAuth.getNc() + ":" + digestAuth.getCnonce() + ":" + digestAuth.getQop() + ":" + ha2; + String verity = Encoder.md5hash(ver); + if(verity.equals(response)) { + Identity identity = olatAuth.getIdentity(); + return identity; + } + } + } + return null; + } @Override public Identity authenticate(Identity identity, String login, String password) { @@ -95,6 +132,18 @@ public class WebDAVAuthManager implements AuthenticationSPI { } return null; } + + @Override + public void upgradePassword(Identity identity, String login, String password) { + if(webDAVModule.isEnabled() && webDAVModule.isDigestAuthenticationEnabled()) { + Authentication digestAuth = securityManager.findAuthentication(identity, PROVIDER_HA1); + if(digestAuth == null) { + String digestToken = identity.getName() + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + password; + Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); + securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_HA1, identity.getName(), digestToken, Encoder.Algorithm.md5_noSalt); + } + } + } /** * Change the WEBDAV-Password of an identity @@ -108,15 +157,33 @@ public class WebDAVAuthManager implements AuthenticationSPI { if (identity == null || identity.getKey() == null) throw new AssertException("cannot change password on a nonpersisted identity"); - Authentication auth = securityManager.findAuthentication(identity, PROVIDER_WEBDAV); - if (auth == null) { // create new authentication for provider OLAT - Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); - auth = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_WEBDAV, identity.getName(), newPwd, LoginModule.getDefaultHashAlgorithm()); - log.audit(doer.getName() + " created new WebDAV authenticatin for identity: " + identity.getName()); - } else { - auth = securityManager.updateCredentials(auth, newPwd, LoginModule.getDefaultHashAlgorithm()); - log.audit(doer.getName() + " set new WebDAV password for identity: " +identity.getName()); + + {//For Basic + Authentication auth = securityManager.findAuthentication(identity, PROVIDER_WEBDAV); + if (auth == null) { // create new authentication for provider OLAT + Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); + auth = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_WEBDAV, identity.getName(), newPwd, LoginModule.getDefaultHashAlgorithm()); + log.audit(doer.getName() + " created new WebDAV authenticatin for identity: " + identity.getName()); + } else { + auth = securityManager.updateCredentials(auth, newPwd, LoginModule.getDefaultHashAlgorithm()); + log.audit(doer.getName() + " set new WebDAV password for identity: " +identity.getName()); + } + } + + //For Digest + if(webDAVModule.isDigestAuthenticationEnabled()) { + Authentication authHa1 = securityManager.findAuthentication(identity, PROVIDER_HA1); + String digestToken = identity.getName() + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + newPwd; + if (authHa1 == null) { // create new authentication for provider OLAT + Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); + authHa1 = securityManager.createAndPersistAuthentication(reloadedIdentity, PROVIDER_HA1, identity.getName(), digestToken, Encoder.Algorithm.md5_noSalt); + log.audit(doer.getName() + " created new WebDAV authenticatin for identity: " + identity.getName()); + } else { + authHa1 = securityManager.updateCredentials(authHa1, digestToken, Encoder.Algorithm.md5_noSalt); + log.audit(doer.getName() + " set new WebDAV password for identity: " +identity.getName()); + } } + return true; } } diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java index cfa2499dba6..87195e08090 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java @@ -27,7 +27,9 @@ package org.olat.core.commons.services.webdav.manager; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Map; import java.util.StringTokenizer; +import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,8 +37,10 @@ import javax.servlet.http.HttpServletResponse; import org.olat.admin.user.delete.service.UserDeletionManager; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.BaseSecurityModule; -import org.olat.core.commons.services.webdav.SecureWebdavServlet; import org.olat.core.commons.services.webdav.WebDAVManager; +import org.olat.core.commons.services.webdav.WebDAVModule; +import org.olat.core.commons.services.webdav.WebDAVProvider; +import org.olat.core.commons.services.webdav.servlets.WebResourceRoot; import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.id.User; @@ -46,6 +50,10 @@ import org.olat.core.util.UserSession; import org.olat.core.util.cache.CacheWrapper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.session.UserSessionManager; +import org.olat.core.util.vfs.MergeSource; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VirtualContainer; +import org.olat.core.util.vfs.callbacks.ReadOnlyCallback; import com.oreilly.servlet.Base64Decoder; @@ -58,22 +66,23 @@ import com.oreilly.servlet.Base64Decoder; * Comment: * */ -public class WebDAVManagerImpl extends WebDAVManager { +public class WebDAVManagerImpl implements WebDAVManager { private static boolean enabled = true; - private static final String BASIC_AUTH_REALM = "OLAT WebDAV Access"; + public static final String BASIC_AUTH_REALM = "OLAT WebDAV Access"; private CoordinatorManager coordinatorManager; private CacheWrapper<String,UserSession> timedSessionCache; + private UserSessionManager sessionManager; private WebDAVAuthManager webDAVAuthManager; + private WebDAVModule webdavModule; /** * [spring] */ private WebDAVManagerImpl(CoordinatorManager coordinatorManager) { this.coordinatorManager = coordinatorManager; - INSTANCE = this; } /** @@ -91,18 +100,93 @@ public class WebDAVManagerImpl extends WebDAVManager { public void setWebDAVAuthManager(WebDAVAuthManager webDAVAuthManager) { this.webDAVAuthManager = webDAVAuthManager; } + + /** + * [used by Spring] + * @param webdavModule + */ + public void setWebDAVModule(WebDAVModule webdavModule) { + this.webdavModule = webdavModule; + } + + public void init() { + timedSessionCache = coordinatorManager.getCoordinator().getCacher().getCache(WebDAVManager.class.getSimpleName(), "webdav"); + } + + @Override + public WebResourceRoot getWebDAVRoot(HttpServletRequest req) { + UserSession usess = getUserSession(req); + if (usess == null || usess.getIdentity() == null) { + return createEmptyRoot(usess); + } + + usess.getSessionInfo().setLastClickTime(); + VFSResourceRoot fdc = (VFSResourceRoot)usess.getEntry("_DIRCTX"); + if (fdc != null) { + fdc.setUserSession(usess); + return fdc; + } + + Identity identity = usess.getIdentity(); + VFSContainer webdavContainer = getMountableRoot(identity); + + //create the / folder + VirtualContainer rootContainer = new VirtualContainer(""); + rootContainer.addItem(webdavContainer); + rootContainer.setLocalSecurityCallback(new ReadOnlyCallback()); + + fdc = new VFSResourceRoot(identity, rootContainer); + fdc.setUserSession(usess); + usess.putEntry("_DIRCTX", fdc); + return fdc; + } + + /** + * Returns a mountable root containing all entries which will be exposed to the webdav mount. + * @return + */ + private VFSContainer getMountableRoot(Identity identity) { + MergeSource vfsRoot = new MergeSource(null, "webdav"); + for (Map.Entry<String, WebDAVProvider> entry : webdavModule.getWebDAVProviders().entrySet()) { + WebDAVProvider provider = entry.getValue(); + vfsRoot.addContainer(new WebDAVProviderNamedContainer(identity, provider)); + } + return vfsRoot; + } + + private WebResourceRoot createEmptyRoot(UserSession usess) { + //create the / folder + VirtualContainer rootContainer = new VirtualContainer(""); + rootContainer.setLocalSecurityCallback(new ReadOnlyCallback()); + + VFSResourceRoot fdc = new VFSResourceRoot(null, rootContainer); + return fdc; + } /** * @see org.olat.core.commons.services.webdav.WebDAVManager#handleAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override - protected boolean handleAuthentication(HttpServletRequest req, HttpServletResponse resp) { - UserSession usess = handleBasicAuthentication(req, resp); - if (usess == null) return false; + public boolean handleAuthentication(HttpServletRequest req, HttpServletResponse resp) { + //manger not started + if(timedSessionCache == null) { + return false; + } + + UserSession usess = sessionManager.getUserSession(req); + if(usess != null && usess.isAuthenticated()) { + req.setAttribute(REQUEST_USERSESSION_KEY, usess); + return true; + } + + usess = doAuthentication(req, resp); + if (usess == null) { + return false; + } // register usersession in REQUEST, not session !! // see SecureWebDAVServlet.setAuthor() and checkQuota() - req.setAttribute(SecureWebdavServlet.REQUEST_USERSESSION_KEY, usess); + req.setAttribute(REQUEST_USERSESSION_KEY, usess); return true; } @@ -110,17 +194,11 @@ public class WebDAVManagerImpl extends WebDAVManager { * @see org.olat.core.commons.services.webdav.WebDAVManager#getUserSession(javax.servlet.http.HttpServletRequest) */ @Override - protected UserSession getUserSession(HttpServletRequest req) { - return (UserSession)req.getAttribute(SecureWebdavServlet.REQUEST_USERSESSION_KEY); + public UserSession getUserSession(HttpServletRequest req) { + return (UserSession)req.getAttribute(REQUEST_USERSESSION_KEY); } - private UserSession handleBasicAuthentication(HttpServletRequest request, HttpServletResponse response) { - if (timedSessionCache == null) { - synchronized (this) { - timedSessionCache = coordinatorManager.getCoordinator().getCacher().getCache(WebDAVManager.class.getSimpleName(), "webdav"); - } - } - + private UserSession doAuthentication(HttpServletRequest request, HttpServletResponse response) { // Get the Authorization header, if one was supplied String authHeader = request.getHeader("Authorization"); if (authHeader != null) { @@ -138,6 +216,10 @@ public class WebDAVManagerImpl extends WebDAVManager { if (basic.equalsIgnoreCase("Basic")) { String credentials = st.nextToken(); usess = handleBasicAuthentication(credentials, request); + } else if (basic.equalsIgnoreCase("Digest")) { + int digestIndex = authHeader.indexOf("Digest"); + String digestInfos = authHeader.substring(digestIndex + 7); + usess = handleDigestAuthentication(digestInfos, request); } } @@ -158,10 +240,21 @@ public class WebDAVManagerImpl extends WebDAVManager { // and cache them so that it doesn't have to // prompt you again. - response.setHeader("WWW-Authenticate", "Basic realm=\"" + BASIC_AUTH_REALM + "\""); + response.addHeader("WWW-Authenticate", "Basic realm=\"" + BASIC_AUTH_REALM + "\""); + String nonce = UUID.randomUUID().toString().replace("-", ""); + response.addHeader("WWW-Authenticate", "Digest realm=\"" + BASIC_AUTH_REALM + "\", qop=\"auth\", nonce=\"" + nonce + "\""); response.setStatus(401); return null; } + + protected UserSession handleDigestAuthentication(String credentials, HttpServletRequest request) { + DigestAuthentication digestAuth = DigestAuthentication.parse(credentials); + Identity identity = webDAVAuthManager.digestAuthentication(request.getMethod(), digestAuth); + if(identity != null) { + return afterAuthorization(identity, request); + } + return null; + } protected UserSession handleBasicAuthentication(String credentials, HttpServletRequest request) { // This example uses sun.misc.* classes. @@ -182,51 +275,55 @@ public class WebDAVManagerImpl extends WebDAVManager { // that neither field is blank Identity identity = webDAVAuthManager.authenticate(null, userID, password); if (identity != null) { - UserSession usess = sessionManager.getUserSession(request); - synchronized(usess) { - //double check to prevent severals concurrent login - if(usess.isAuthenticated()) { - return usess; - } - - sessionManager.signOffAndClear(usess); - usess.setIdentity(identity); - UserDeletionManager.getInstance().setIdentityAsActiv(identity); - // set the roles (admin, author, guest) - Roles roles = BaseSecurityManager.getInstance().getRoles(identity); - usess.setRoles(roles); - // set authprovider - //usess.getIdentityEnvironment().setAuthProvider(OLATAuthenticationController.PROVIDER_OLAT); - - // set session info - SessionInfo sinfo = new SessionInfo(identity.getKey(), identity.getName(), request.getSession()); - User usr = identity.getUser(); - sinfo.setFirstname(usr.getProperty(UserConstants.FIRSTNAME, null)); - sinfo.setLastname(usr.getProperty(UserConstants.LASTNAME, null)); - sinfo.setFromIP(request.getRemoteAddr()); - sinfo.setFromFQN(request.getRemoteAddr()); - try { - InetAddress[] iaddr = InetAddress.getAllByName(request.getRemoteAddr()); - if (iaddr.length > 0) sinfo.setFromFQN(iaddr[0].getHostName()); - } catch (UnknownHostException e) { - // ok, already set IP as FQDN - } - sinfo.setAuthProvider(BaseSecurityModule.getDefaultAuthProviderIdentifier()); - sinfo.setUserAgent(request.getHeader("User-Agent")); - sinfo.setSecure(request.isSecure()); - sinfo.setWebDAV(true); - sinfo.setWebModeFromUreq(null); - // set session info for this session - usess.setSessionInfo(sinfo); - // - sessionManager.signOn(usess); - return usess; - } + return afterAuthorization(identity, request); } } return null; } + private UserSession afterAuthorization(Identity identity, HttpServletRequest request) { + UserSession usess = sessionManager.getUserSession(request); + synchronized(usess) { + //double check to prevent severals concurrent login + if(usess.isAuthenticated()) { + return usess; + } + + sessionManager.signOffAndClear(usess); + usess.setIdentity(identity); + UserDeletionManager.getInstance().setIdentityAsActiv(identity); + // set the roles (admin, author, guest) + Roles roles = BaseSecurityManager.getInstance().getRoles(identity); + usess.setRoles(roles); + // set authprovider + //usess.getIdentityEnvironment().setAuthProvider(OLATAuthenticationController.PROVIDER_OLAT); + + // set session info + SessionInfo sinfo = new SessionInfo(identity.getKey(), identity.getName(), request.getSession()); + User usr = identity.getUser(); + sinfo.setFirstname(usr.getProperty(UserConstants.FIRSTNAME, null)); + sinfo.setLastname(usr.getProperty(UserConstants.LASTNAME, null)); + sinfo.setFromIP(request.getRemoteAddr()); + sinfo.setFromFQN(request.getRemoteAddr()); + try { + InetAddress[] iaddr = InetAddress.getAllByName(request.getRemoteAddr()); + if (iaddr.length > 0) sinfo.setFromFQN(iaddr[0].getHostName()); + } catch (UnknownHostException e) { + // ok, already set IP as FQDN + } + sinfo.setAuthProvider(BaseSecurityModule.getDefaultAuthProviderIdentifier()); + sinfo.setUserAgent(request.getHeader("User-Agent")); + sinfo.setSecure(request.isSecure()); + sinfo.setWebDAV(true); + sinfo.setWebModeFromUreq(null); + // set session info for this session + usess.setSessionInfo(sinfo); + // + sessionManager.signOn(usess); + return usess; + } + } + /** * @see org.olat.core.commons.services.webdav.WebDAVManager#isEnabled() */ diff --git a/src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderNamedContainer.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java similarity index 94% rename from src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderNamedContainer.java rename to src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java index b0b130d40a1..d1d9589fdaf 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/WebDAVProviderNamedContainer.java +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java @@ -17,8 +17,9 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.core.commons.services.webdav; +package org.olat.core.commons.services.webdav.manager; +import org.olat.core.commons.services.webdav.WebDAVProvider; import org.olat.core.id.Identity; import org.olat.core.util.vfs.NamedContainerImpl; import org.olat.core.util.vfs.VFSContainer; diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/ConcurrentDateFormat.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/ConcurrentDateFormat.java new file mode 100644 index 00000000000..164def40d7c --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/ConcurrentDateFormat.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Queue; +import java.util.TimeZone; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * A thread safe wrapper around {@link SimpleDateFormat} that does not make use + * of ThreadLocal and - broadly - only creates enough SimpleDateFormat objects + * to satisfy the concurrency requirements. + */ +public class ConcurrentDateFormat { + + private final String format; + private final Locale locale; + private final TimeZone timezone; + private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<SimpleDateFormat>(); + + public static final String RFC1123_DATE = "EEE, dd MMM yyyy HH:mm:ss zzz"; + public static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + private static final ConcurrentDateFormat FORMAT_RFC1123; + + static { + FORMAT_RFC1123 = new ConcurrentDateFormat(RFC1123_DATE, Locale.US, GMT); + } + + public static String formatRfc1123(Date date) { + return FORMAT_RFC1123.format(date); + } + + public ConcurrentDateFormat(String format, Locale locale, + TimeZone timezone) { + this.format = format; + this.locale = locale; + this.timezone = timezone; + SimpleDateFormat initial = createInstance(); + queue.add(initial); + } + + public String format(Date date) { + SimpleDateFormat sdf = queue.poll(); + if (sdf == null) { + sdf = createInstance(); + } + String result = sdf.format(date); + queue.add(sdf); + return result; + } + + private SimpleDateFormat createInstance() { + SimpleDateFormat sdf = new SimpleDateFormat(format, locale); + sdf.setTimeZone(timezone); + return sdf; + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/DOMWriter.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/DOMWriter.java new file mode 100644 index 00000000000..ac1b022b7bc --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/DOMWriter.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; + +import java.io.PrintWriter; +import java.io.Writer; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * A sample DOM writer. This sample program illustrates how to traverse a DOM + * tree in order to print a document that is parsed. + */ +public class DOMWriter { + + /** Default Encoding */ + private static final String PRINTWRITER_ENCODING = "UTF8"; + + /** Print writer. */ + protected final PrintWriter out; + + /** Canonical output. */ + protected final boolean canonical; + + + public DOMWriter(Writer writer, boolean canonical) { + out = new PrintWriter(writer); + this.canonical = canonical; + } + + + public static String getWriterEncoding() { + return (PRINTWRITER_ENCODING); + } + + + /** Prints the specified node, recursively. */ + public void print(Node node) { + + // is there anything to do? + if (node == null) { + return; + } + + int type = node.getNodeType(); + switch (type) { + // print document + case Node.DOCUMENT_NODE: + if (!canonical) { + String Encoding = getWriterEncoding(); + if (Encoding.equalsIgnoreCase("DEFAULT")) + Encoding = "UTF-8"; + else if (Encoding.equalsIgnoreCase("Unicode")) + Encoding = "UTF-16"; + else + Encoding = null;//TODO webdav MIME2Java.reverse(Encoding); + + out.println("<?xml version=\"1.0\" encoding=\"" + Encoding + + "\"?>"); + } + print(((Document) node).getDocumentElement()); + out.flush(); + break; + + // print element with attributes + case Node.ELEMENT_NODE: + out.print('<'); + out.print(node.getLocalName()); + Attr attrs[] = sortAttributes(node.getAttributes()); + for (int i = 0; i < attrs.length; i++) { + Attr attr = attrs[i]; + out.print(' '); + out.print(attr.getLocalName()); + + out.print("=\""); + out.print(normalize(attr.getNodeValue())); + out.print('"'); + } + out.print('>'); + printChildren(node); + break; + + // handle entity reference nodes + case Node.ENTITY_REFERENCE_NODE: + if (canonical) { + printChildren(node); + } else { + out.print('&'); + out.print(node.getLocalName()); + out.print(';'); + } + break; + + // print cdata sections + case Node.CDATA_SECTION_NODE: + if (canonical) { + out.print(normalize(node.getNodeValue())); + } else { + out.print("<![CDATA["); + out.print(node.getNodeValue()); + out.print("]]>"); + } + break; + + // print text + case Node.TEXT_NODE: + out.print(normalize(node.getNodeValue())); + break; + + // print processing instruction + case Node.PROCESSING_INSTRUCTION_NODE: + out.print("<?"); + out.print(node.getLocalName()); + + String data = node.getNodeValue(); + if (data != null && data.length() > 0) { + out.print(' '); + out.print(data); + } + out.print("?>"); + break; + } + + if (type == Node.ELEMENT_NODE) { + out.print("</"); + out.print(node.getLocalName()); + out.print('>'); + } + + out.flush(); + + } // print(Node) + + + private void printChildren(Node node) { + NodeList children = node.getChildNodes(); + if (children != null) { + int len = children.getLength(); + for (int i = 0; i < len; i++) { + print(children.item(i)); + } + } + } + + + /** Returns a sorted list of attributes. */ + protected Attr[] sortAttributes(NamedNodeMap attrs) { + if (attrs == null) { + return new Attr[0]; + } + + int len = attrs.getLength(); + Attr array[] = new Attr[len]; + for (int i = 0; i < len; i++) { + array[i] = (Attr) attrs.item(i); + } + for (int i = 0; i < len - 1; i++) { + String name = null; + name = array[i].getLocalName(); + int index = i; + for (int j = i + 1; j < len; j++) { + String curName = null; + curName = array[j].getLocalName(); + if (curName.compareTo(name) < 0) { + name = curName; + index = j; + } + } + if (index != i) { + Attr temp = array[i]; + array[i] = array[index]; + array[index] = temp; + } + } + + return (array); + + } + + /** Normalizes the given string. */ + protected String normalize(String s) { + if (s == null) { + return ""; + } + + StringBuilder str = new StringBuilder(); + + int len = s.length(); + for (int i = 0; i < len; i++) { + char ch = s.charAt(i); + switch (ch) { + case '<': + str.append("<"); + break; + case '>': + str.append(">"); + break; + case '&': + str.append("&"); + break; + case '"': + str.append("""); + break; + case '\r': + case '\n': + if (canonical) { + str.append("&#"); + str.append(Integer.toString(ch)); + str.append(';'); + break; + } + // else, default append char + //$FALL-THROUGH$ + default: + str.append(ch); + } + } + + return (str.toString()); + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java new file mode 100644 index 00000000000..4b6cd66eea8 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java @@ -0,0 +1,1314 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; + +import java.io.BufferedInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.StringTokenizer; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletResponse; +import javax.servlet.ServletResponseWrapper; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.servlets.URLEncoder; + + +/** + * <p>The default resource-serving servlet for most web applications, + * used to serve static resources such as HTML pages and images. + * </p> + * <p> + * This servlet is intended to be mapped to <em>/</em> e.g.: + * </p> + * <pre> + * <servlet-mapping> + * <servlet-name>default</servlet-name> + * <url-pattern>/</url-pattern> + * </servlet-mapping> + * </pre> + * <p>It can be mapped to sub-paths, however in all cases resources are served + * from the web appplication resource root using the full path from the root + * of the web application context. + * <br/>e.g. given a web application structure: + *</p> + * <pre> + * /context + * /images + * tomcat2.jpg + * /static + * /images + * tomcat.jpg + * </pre> + * <p> + * ... and a servlet mapping that maps only <code>/static/*</code> to the default servlet: + * </p> + * <pre> + * <servlet-mapping> + * <servlet-name>default</servlet-name> + * <url-pattern>/static/*</url-pattern> + * </servlet-mapping> + * </pre> + * <p> + * Then a request to <code>/context/static/images/tomcat.jpg</code> will succeed + * while a request to <code>/context/images/tomcat2.jpg</code> will fail. + * </p> + * @author Craig R. McClanahan + * @author Remy Maucherat + * @version $Id$ + */ + +public abstract class DefaultDispatcher implements Serializable { + + private static final long serialVersionUID = 1L; + private static final OLog log = Tracing.createLoggerFor(DefaultDispatcher.class); + + // ----------------------------------------------------- Instance Variables + + + /** + * The debugging detail level for this servlet. + */ + protected final int debug = 0; + + + /** + * The input buffer size to use when serving resources. + */ + private int input = 2048; + + + /** + * Should be serve gzip versions of files. By default, it's set to false. + */ + private boolean gzip = false; + + + /** + * The output buffer size to use when serving resources. + */ + private int output = 2048; + + + /** + * Array containing the safe characters set. + */ + private static final URLEncoder urlEncoder; + + + /** + * File encoding to be used when reading static files. If none is specified + * the platform default is used. + */ + protected String fileEncoding = null; + + /** + * Should the Accept-Ranges: bytes header be send with static resources? + */ + private boolean useAcceptRanges = true; + + /** + * Full range marker. + */ + private static final ArrayList<Range> FULL = new ArrayList<Range>(); + + + // ----------------------------------------------------- Static Initializer + + + /** + * GMT timezone - all HTTP dates are on GMT + */ + static { + urlEncoder = new URLEncoder(); + urlEncoder.addSafeCharacter('-'); + urlEncoder.addSafeCharacter('_'); + urlEncoder.addSafeCharacter('.'); + urlEncoder.addSafeCharacter('*'); + urlEncoder.addSafeCharacter('/'); + } + + + /** + * MIME multipart separation string + */ + protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY"; + + + /** + * Size of file transfer buffer in bytes. + */ + protected static final int BUFFER_SIZE = 4096; + + + // --------------------------------------------------------- Public Methods + + + protected abstract WebResourceRoot getResources(HttpServletRequest req); + + + + + // ------------------------------------------------------ Protected Methods + + + /** + * Return the relative path associated with this servlet. + * + * @param request The servlet request we are processing + */ + protected String getRelativePath(HttpServletRequest request) { + // IMPORTANT: DefaultServlet can be mapped to '/' or '/path/*' but always + // serves resources from the web app root with context rooted paths. + // i.e. it can not be used to mount the web app root under a sub-path + // This method must construct a complete context rooted path, although + // subclasses can change this behaviour. + + // Are we being processed by a RequestDispatcher.include()? + if (request.getAttribute( + RequestDispatcher.INCLUDE_REQUEST_URI) != null) { + String result = (String) request.getAttribute( + RequestDispatcher.INCLUDE_PATH_INFO); + if (result == null) { + result = (String) request.getAttribute( + RequestDispatcher.INCLUDE_SERVLET_PATH); + } else { + result = (String) request.getAttribute( + RequestDispatcher.INCLUDE_SERVLET_PATH) + result; + } + if ((result == null) || (result.equals(""))) { + result = "/"; + } + return (result); + } + + // No, extract the desired path directly from the request + String result = request.getPathInfo(); + if (result == null) { + result = request.getServletPath(); + } else { + result = request.getServletPath() + result; + } + if ((result == null) || (result.equals(""))) { + result = "/"; + } + return (result); + + } + + + /** + * Determines the appropriate path to prepend resources with + * when generating directory listings. Depending on the behaviour of + * {@link #getRelativePath(HttpServletRequest)} this will change. + * @param request the request to determine the path for + * @return the prefix to apply to all resources in the listing. + */ + protected String getPathPrefix(final HttpServletRequest request) { + return request.getContextPath(); + } + + + + + /** + * Check if the conditions specified in the optional If headers are + * satisfied. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * @return boolean true if the resource meets all the specified conditions, + * and false if any of the conditions is not satisfied, in which case + * request processing is stopped + */ + protected boolean checkIfHeaders(HttpServletRequest request, + HttpServletResponse response, + WebResource resource) + throws IOException { + + return checkIfMatch(request, response, resource) + && checkIfModifiedSince(request, response, resource) + && checkIfNoneMatch(request, response, resource) + && checkIfUnmodifiedSince(request, response, resource); + + } + + + /** + * URL rewriter. + * + * @param path Path which has to be rewritten + */ + protected String rewriteUrl(String path) { + return urlEncoder.encode( path ); + } + + + /** + * Serve the specified resource, optionally including the data content. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param content Should the content be included? + * @param encoding The encoding to use if it is necessary to access the + * source as characters rather than as bytes + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet-specified error occurs + */ + protected void serveResource(HttpServletRequest request, + HttpServletResponse response, + boolean content, + String encoding) + throws IOException, ServletException { + + boolean serveContent = content; + boolean debug = log.isDebug(); + + // Identify the requested resource path + String path = getRelativePath(request); + if (debug) { + if (serveContent) + log.debug("DefaultServlet.serveResource: Serving resource '" + + path + "' headers and data"); + else + log.debug("DefaultServlet.serveResource: Serving resource '" + + path + "' headers only"); + } + + WebResourceRoot resources = getResources(request); + WebResource resource = resources.getResource(path); + + if (!resource.exists()) { + // Check if we're included so we can return the appropriate + // missing resource name in the error + String requestUri = (String) request.getAttribute( + RequestDispatcher.INCLUDE_REQUEST_URI); + if (requestUri == null) { + requestUri = request.getRequestURI(); + } else { + // We're included + // SRV.9.3 says we must throw a FNFE + throw new FileNotFoundException( + "defaultServlet.missingResource"); + } + + response.sendError(HttpServletResponse.SC_NOT_FOUND, + requestUri); + return; + } + + // If the resource is not a collection, and the resource path + // ends with "/" or "\", return NOT FOUND + if (resource.isFile()) { + if (path.endsWith("/") || (path.endsWith("\\"))) { + // Check if we're included so we can return the appropriate + // missing resource name in the error + String requestUri = (String) request.getAttribute( + RequestDispatcher.INCLUDE_REQUEST_URI); + if (requestUri == null) { + requestUri = request.getRequestURI(); + } + response.sendError(HttpServletResponse.SC_NOT_FOUND, + requestUri); + return; + } + } + + boolean isError = + response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST; + + boolean included = false; + // Check if the conditions specified in the optional If headers are + // satisfied. + if (resource.isFile()) { + // Checking If headers + included = (request.getAttribute( + RequestDispatcher.INCLUDE_CONTEXT_PATH) != null); + if (!included && !isError && + !checkIfHeaders(request, response, resource)) { + return; + } + + } + + // Find content type. + String contentType = resource.getMimeType(); + if (contentType == null) { + contentType = request.getServletContext().getMimeType(resource.getName()); + resource.setMimeType(contentType); + } + + // These need to reflect the original resource, not the potentially + // gzip'd version of the resource so get them now if they are going to + // be needed later + String eTag = null; + String lastModifiedHttp = null; + if (resource.isFile() && !isError) { + eTag = resource.getETag(); + lastModifiedHttp = resource.getLastModifiedHttp(); + } + + + // Serve a gzipped version of the file if present + boolean usingGzippedVersion = false; + if (gzip && + resource.isFile() && + !included && + !path.endsWith(".gz") && + checkIfGzip(request)) { + WebResource gzipResource = resources.getResource(path + ".gz"); + if (gzipResource.exists() && gzipResource.isFile()) { + response.addHeader("Content-Encoding", "gzip"); + resource = gzipResource; + usingGzippedVersion = true; + } + } + + ArrayList<Range> ranges = null; + long contentLength = -1L; + + if (resource.isDirectory()) { + contentType = "text/html;charset=UTF-8"; + } else { + if (!isError) { + if (useAcceptRanges) { + // Accept ranges header + response.setHeader("Accept-Ranges", "bytes"); + } + + // Parse range specifier + ranges = parseRange(request, response, resource); + + // ETag header + response.setHeader("ETag", eTag); + + // Last-Modified header + response.setHeader("Last-Modified", lastModifiedHttp); + } + + // Get content length + contentLength = resource.getContentLength(); + // Special case for zero length files, which would cause a + // (silent) ISE when setting the output buffer size + if (contentLength == 0L) { + serveContent = false; + } + + } + + ServletOutputStream ostream = null; + PrintWriter writer = null; + + if (serveContent) { + + // Trying to retrieve the servlet output stream + + try { + ostream = response.getOutputStream(); + } catch (IllegalStateException e) { + // If it fails, we try to get a Writer instead if we're + // trying to serve a text file + if (!usingGzippedVersion && + ((contentType == null) || + (contentType.startsWith("text")) || + (contentType.endsWith("xml")) || + (contentType.contains("/javascript"))) + ) { + writer = response.getWriter(); + // Cannot reliably serve partial content with a Writer + ranges = FULL; + } else { + throw e; + } + } + + } + + // Check to see if a Filter, Valve of wrapper has written some content. + // If it has, disable range requests and setting of a content length + // since neither can be done reliably. + ServletResponse r = response; + long contentWritten = 0; + while (r instanceof ServletResponseWrapper) { + r = ((ServletResponseWrapper) r).getResponse(); + } + + if (contentWritten > 0) { + ranges = FULL; + } + + if (resource.isDirectory() || + isError || + ( (ranges == null || ranges.isEmpty()) + && request.getHeader("Range") == null ) || + ranges == FULL ) { + + // Set the appropriate output headers + if (contentType != null) { + if (debug) + log.debug("DefaultServlet.serveFile: contentType='" + + contentType + "'"); + response.setContentType(contentType); + } + if (resource.isFile() && contentLength >= 0 && + (!serveContent || ostream != null)) { + if (debug) + log.debug("DefaultServlet.serveFile: contentLength=" + + contentLength); + // Don't set a content length if something else has already + // written to the response. + if (contentWritten == 0) { + response.setContentLength((int)contentLength);//TODO tomcat + } + } + + InputStream renderResult = null; + if (resource.isDirectory()) { + if (serveContent) { + // Serve the directory browser + renderResult = null;//TODO tomcat render(getPathPrefix(request), resource); + } + } + + // Copy the input stream to our output stream (if requested) + if (serveContent) { + try { + response.setBufferSize(output); + } catch (IllegalStateException e) { + // Silent catch + } + if (ostream != null) { + if (!checkSendfile(request, response, resource, + contentLength, null)) + copy(resource, renderResult, ostream); + } else { + copy(resource, renderResult, writer, encoding); + } + } + + } else { + + if ((ranges == null) || (ranges.isEmpty())) + return; + + // Partial content response. + + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); + + if (ranges.size() == 1) { + + Range range = ranges.get(0); + response.addHeader("Content-Range", "bytes " + + range.start + + "-" + range.end + "/" + + range.length); + long length = range.end - range.start + 1; + response.setContentLength((int)length);//TODO tomcat + + if (contentType != null) { + if (debug) + log.debug("DefaultServlet.serveFile: contentType='" + + contentType + "'"); + response.setContentType(contentType); + } + + if (serveContent) { + try { + response.setBufferSize(output); + } catch (IllegalStateException e) { + // Silent catch + } + if (ostream != null) { + if (!checkSendfile(request, response, resource, + range.end - range.start + 1, range)) + copy(resource, ostream, range); + } else { + // we should not get here + throw new IllegalStateException(); + } + } + + } else { + + response.setContentType("multipart/byteranges; boundary=" + + mimeSeparation); + + if (serveContent) { + try { + response.setBufferSize(output); + } catch (IllegalStateException e) { + // Silent catch + } + if (ostream != null) { + copy(resource, ostream, ranges.iterator(), + contentType); + } else { + // we should not get here + throw new IllegalStateException(); + } + } + } + } + } + + + /** + * Parse the content-range header. + * + * @param request The servlet request we a)re processing + * @param response The servlet response we are creating + * @return Range + */ + protected Range parseContentRange(HttpServletRequest request, + HttpServletResponse response) + throws IOException { + + // Retrieving the content-range header (if any is specified + String rangeHeader = request.getHeader("Content-Range"); + + if (rangeHeader == null) + return null; + + // bytes is the only range unit supported + if (!rangeHeader.startsWith("bytes")) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + rangeHeader = rangeHeader.substring(6).trim(); + + int dashPos = rangeHeader.indexOf('-'); + int slashPos = rangeHeader.indexOf('/'); + + if (dashPos == -1) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + if (slashPos == -1) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + Range range = new Range(); + + try { + range.start = Long.parseLong(rangeHeader.substring(0, dashPos)); + range.end = + Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos)); + range.length = Long.parseLong + (rangeHeader.substring(slashPos + 1, rangeHeader.length())); + } catch (NumberFormatException e) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + if (!range.validate()) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + + return range; + + } + + + /** + * Parse the range header. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * @return Vector of ranges + */ + private ArrayList<Range> parseRange(HttpServletRequest request, + HttpServletResponse response, + WebResource resource) throws IOException { + + // Checking If-Range + String headerValue = request.getHeader("If-Range"); + + if (headerValue != null) { + + long headerValueTime = (-1L); + try { + headerValueTime = request.getDateHeader("If-Range"); + } catch (IllegalArgumentException e) { + // Ignore + } + + String eTag = resource.getETag(); + long lastModified = resource.getLastModified(); + + if (headerValueTime == (-1L)) { + + // If the ETag the client gave does not match the entity + // etag, then the entire entity is returned. + if (!eTag.equals(headerValue.trim())) + return FULL; + + } else { + + // If the timestamp of the entity the client got is older than + // the last modification date of the entity, the entire entity + // is returned. + if (lastModified > (headerValueTime + 1000)) + return FULL; + + } + + } + + long fileLength = resource.getContentLength(); + + if (fileLength == 0) + return null; + + // Retrieving the range header (if any is specified + String rangeHeader = request.getHeader("Range"); + + if (rangeHeader == null) + return null; + // bytes is the only range unit supported (and I don't see the point + // of adding new ones). + if (!rangeHeader.startsWith("bytes")) { + response.addHeader("Content-Range", "bytes */" + fileLength); + response.sendError + (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return null; + } + + rangeHeader = rangeHeader.substring(6); + + // Vector which will contain all the ranges which are successfully + // parsed. + ArrayList<Range> result = new ArrayList<Range>(); + StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ","); + + // Parsing the range list + while (commaTokenizer.hasMoreTokens()) { + String rangeDefinition = commaTokenizer.nextToken().trim(); + + Range currentRange = new Range(); + currentRange.length = fileLength; + + int dashPos = rangeDefinition.indexOf('-'); + + if (dashPos == -1) { + response.addHeader("Content-Range", "bytes */" + fileLength); + response.sendError + (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return null; + } + + if (dashPos == 0) { + + try { + long offset = Long.parseLong(rangeDefinition); + currentRange.start = fileLength + offset; + currentRange.end = fileLength - 1; + } catch (NumberFormatException e) { + response.addHeader("Content-Range", + "bytes */" + fileLength); + response.sendError + (HttpServletResponse + .SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return null; + } + + } else { + + try { + currentRange.start = Long.parseLong + (rangeDefinition.substring(0, dashPos)); + if (dashPos < rangeDefinition.length() - 1) + currentRange.end = Long.parseLong + (rangeDefinition.substring + (dashPos + 1, rangeDefinition.length())); + else + currentRange.end = fileLength - 1; + } catch (NumberFormatException e) { + response.addHeader("Content-Range", + "bytes */" + fileLength); + response.sendError + (HttpServletResponse + .SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return null; + } + + } + + if (!currentRange.validate()) { + response.addHeader("Content-Range", "bytes */" + fileLength); + response.sendError + (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + return null; + } + + result.add(currentRange); + } + + return result; + } + + // -------------------------------------------------------- protected Methods + + + /** + * Check if sendfile can be used. + */ + private boolean checkSendfile(HttpServletRequest request, + HttpServletResponse response, + WebResource resource, + long length, Range range) { + /* if (sendfileSize > 0 + && resource.isFile() + && length > sendfileSize + && (resource.getCanonicalPath() != null) + && (Boolean.TRUE == request.getAttribute(Globals.SENDFILE_SUPPORTED_ATTR)) + && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade")) + && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) { + request.setAttribute(Globals.SENDFILE_FILENAME_ATTR, resource.getCanonicalPath()); + if (range == null) { + request.setAttribute(Globals.SENDFILE_FILE_START_ATTR, Long.valueOf(0L)); + request.setAttribute(Globals.SENDFILE_FILE_END_ATTR, Long.valueOf(length)); + } else { + request.setAttribute(Globals.SENDFILE_FILE_START_ATTR, Long.valueOf(range.start)); + request.setAttribute(Globals.SENDFILE_FILE_END_ATTR, Long.valueOf(range.end + 1)); + } + return true; + }*/ + return false; + } + + + /** + * Check if the if-match condition is satisfied. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * @return boolean true if the resource meets the specified condition, + * and false if the condition is not satisfied, in which case request + * processing is stopped + */ + private boolean checkIfMatch(HttpServletRequest request, + HttpServletResponse response, WebResource resource) + throws IOException { + + String eTag = resource.getETag(); + String headerValue = request.getHeader("If-Match"); + if (headerValue != null) { + if (headerValue.indexOf('*') == -1) { + + StringTokenizer commaTokenizer = new StringTokenizer + (headerValue, ","); + boolean conditionSatisfied = false; + + while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) { + String currentToken = commaTokenizer.nextToken(); + if (currentToken.trim().equals(eTag)) + conditionSatisfied = true; + } + + // If none of the given ETags match, 412 Precodition failed is + // sent back + if (!conditionSatisfied) { + response.sendError + (HttpServletResponse.SC_PRECONDITION_FAILED); + return false; + } + + } + } + return true; + } + + + /** + * Check if the if-modified-since condition is satisfied. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * @return boolean true if the resource meets the specified condition, + * and false if the condition is not satisfied, in which case request + * processing is stopped + */ + private boolean checkIfModifiedSince(HttpServletRequest request, + HttpServletResponse response, WebResource resource) { + try { + long headerValue = request.getDateHeader("If-Modified-Since"); + long lastModified = resource.getLastModified(); + if (headerValue != -1) { + + // If an If-None-Match header has been specified, if modified since + // is ignored. + if ((request.getHeader("If-None-Match") == null) + && (lastModified < headerValue + 1000)) { + // The entity has not been modified since the date + // specified by the client. This is not an error case. + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setHeader("ETag", resource.getETag()); + + return false; + } + } + } catch (IllegalArgumentException illegalArgument) { + return true; + } + return true; + } + + + /** + * Check if the if-none-match condition is satisfied. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * @return boolean true if the resource meets the specified condition, + * and false if the condition is not satisfied, in which case request + * processing is stopped + */ + private boolean checkIfNoneMatch(HttpServletRequest request, + HttpServletResponse response, WebResource resource) + throws IOException { + + String eTag = resource.getETag(); + String headerValue = request.getHeader("If-None-Match"); + if (headerValue != null) { + + boolean conditionSatisfied = false; + + if (!headerValue.equals("*")) { + + StringTokenizer commaTokenizer = + new StringTokenizer(headerValue, ","); + + while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) { + String currentToken = commaTokenizer.nextToken(); + if (currentToken.trim().equals(eTag)) + conditionSatisfied = true; + } + + } else { + conditionSatisfied = true; + } + + if (conditionSatisfied) { + + // For GET and HEAD, we should respond with + // 304 Not Modified. + // For every other method, 412 Precondition Failed is sent + // back. + if ( ("GET".equals(request.getMethod())) + || ("HEAD".equals(request.getMethod())) ) { + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setHeader("ETag", eTag); + + return false; + } + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return false; + } + } + return true; + } + + /** + * Check if the user agent supports gzip encoding. + * + * @param request The servlet request we are processing + * @return boolean true if the user agent supports gzip encoding, + * and false if the user agent does not support gzip encoding + */ + private boolean checkIfGzip(HttpServletRequest request) { + Enumeration<String> headers = request.getHeaders("Accept-Encoding"); + while (headers.hasMoreElements()) { + String header = headers.nextElement(); + if (header.indexOf("gzip") != -1) { + return true; + } + } + return false; + } + + + /** + * Check if the if-unmodified-since condition is satisfied. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * @return boolean true if the resource meets the specified condition, + * and false if the condition is not satisfied, in which case request + * processing is stopped + */ + private boolean checkIfUnmodifiedSince(HttpServletRequest request, + HttpServletResponse response, WebResource resource) + throws IOException { + try { + long lastModified = resource.getLastModified(); + long headerValue = request.getDateHeader("If-Unmodified-Since"); + if (headerValue != -1) { + if ( lastModified >= (headerValue + 1000)) { + // The entity has not been modified since the date + // specified by the client. This is not an error case. + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return false; + } + } + } catch(IllegalArgumentException illegalArgument) { + return true; + } + return true; + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param resource The source resource + * @param is The input stream to read the source resource from + * @param ostream The output stream to write to + * + * @exception IOException if an input/output error occurs + */ + private void copy(WebResource resource, InputStream is, + ServletOutputStream ostream) + throws IOException { + + IOException exception = null; + InputStream resourceInputStream = null; + + // Optimization: If the binary content has already been loaded, send + // it directly + if (resource.isFile()) { + byte buffer[] = resource.getContent(); + if (buffer != null) { + ostream.write(buffer, 0, buffer.length); + return; + } + resourceInputStream = resource.getInputStream(); + } else { + resourceInputStream = is; + } + + InputStream istream = new BufferedInputStream + (resourceInputStream, input); + + // Copy the input stream to the output stream + exception = copyRange(istream, ostream); + + // Clean up the input stream + istream.close(); + + // Rethrow any exception that has occurred + if (exception != null) + throw exception; + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param resource The source resource + * @param is The input stream to read the source resource from + * @param writer The writer to write to + * @param encoding The encoding to use when reading the source input stream + * + * @exception IOException if an input/output error occurs + */ + protected void copy(WebResource resource, InputStream is, PrintWriter writer, + String encoding) throws IOException { + + IOException exception = null; + + InputStream resourceInputStream = null; + if (resource.isFile()) { + resourceInputStream = resource.getInputStream(); + } else { + resourceInputStream = is; + } + + Reader reader; + if (encoding == null) { + reader = new InputStreamReader(resourceInputStream); + } else { + reader = new InputStreamReader(resourceInputStream, encoding); + } + + // Copy the input stream to the output stream + exception = copyRange(reader, writer); + + // Clean up the reader + reader.close(); + + // Rethrow any exception that has occurred + if (exception != null) + throw exception; + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param resource The source resource + * @param ostream The output stream to write to + * @param range Range the client wanted to retrieve + * @exception IOException if an input/output error occurs + */ + private void copy(WebResource resource, ServletOutputStream ostream, + Range range) + throws IOException { + + IOException exception = null; + + InputStream resourceInputStream = resource.getInputStream(); + InputStream istream = + new BufferedInputStream(resourceInputStream, input); + exception = copyRange(istream, ostream, range.start, range.end); + + // Clean up the input stream + istream.close(); + + // Rethrow any exception that has occurred + if (exception != null) + throw exception; + + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param resource The source resource + * @param ostream The output stream to write to + * @param ranges Enumeration of the ranges the client wanted to + * retrieve + * @param contentType Content type of the resource + * @exception IOException if an input/output error occurs + */ + private void copy(WebResource resource, ServletOutputStream ostream, + Iterator<Range> ranges, String contentType) + throws IOException { + + IOException exception = null; + + while ( (exception == null) && (ranges.hasNext()) ) { + + InputStream resourceInputStream = resource.getInputStream(); + InputStream istream = + new BufferedInputStream(resourceInputStream, input); + + Range currentRange = ranges.next(); + + // Writing MIME header. + ostream.println(); + ostream.println("--" + mimeSeparation); + if (contentType != null) + ostream.println("Content-Type: " + contentType); + ostream.println("Content-Range: bytes " + currentRange.start + + "-" + currentRange.end + "/" + + currentRange.length); + ostream.println(); + + // Printing content + exception = copyRange(istream, ostream, currentRange.start, + currentRange.end); + + istream.close(); + + } + + ostream.println(); + ostream.print("--" + mimeSeparation + "--"); + + // Rethrow any exception that has occurred + if (exception != null) + throw exception; + + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param istream The input stream to read from + * @param ostream The output stream to write to + * @return Exception which occurred during processing + */ + private IOException copyRange(InputStream istream, + ServletOutputStream ostream) { + + // Copy the input stream to the output stream + IOException exception = null; + byte buffer[] = new byte[input]; + int len = buffer.length; + while (true) { + try { + len = istream.read(buffer); + if (len == -1) + break; + ostream.write(buffer, 0, len); + } catch (IOException e) { + exception = e; + len = -1; + break; + } + } + return exception; + + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param reader The reader to read from + * @param writer The writer to write to + * @return Exception which occurred during processing + */ + private IOException copyRange(Reader reader, PrintWriter writer) { + + // Copy the input stream to the output stream + IOException exception = null; + char buffer[] = new char[input]; + int len = buffer.length; + while (true) { + try { + len = reader.read(buffer); + if (len == -1) + break; + writer.write(buffer, 0, len); + } catch (IOException e) { + exception = e; + len = -1; + break; + } + } + return exception; + + } + + + /** + * Copy the contents of the specified input stream to the specified + * output stream, and ensure that both streams are closed before returning + * (even in the face of an exception). + * + * @param istream The input stream to read from + * @param ostream The output stream to write to + * @param start Start of the range which will be copied + * @param end End of the range which will be copied + * @return Exception which occurred during processing + */ + private IOException copyRange(InputStream istream, + ServletOutputStream ostream, + long start, long end) { + + if (log.isDebug()) { + log.debug("Serving bytes:" + start + "-" + end); + } + + long skipped = 0; + try { + skipped = istream.skip(start); + } catch (IOException e) { + return e; + } + if (skipped < start) { + return new IOException("defaultservlet.skipfail" + + Long.valueOf(skipped) + Long.valueOf(start)); + } + + IOException exception = null; + long bytesToRead = end - start + 1; + + byte buffer[] = new byte[input]; + int len = buffer.length; + while ( (bytesToRead > 0) && (len >= buffer.length)) { + try { + len = istream.read(buffer); + if (bytesToRead >= len) { + ostream.write(buffer, 0, len); + bytesToRead -= len; + } else { + ostream.write(buffer, 0, (int) bytesToRead); + bytesToRead = 0; + } + } catch (IOException e) { + exception = e; + len = -1; + } + if (len < buffer.length) + break; + } + + return exception; + + } + + + // ------------------------------------------------------ Range Inner Class + + + protected static class Range { + + public long start; + public long end; + public long length; + + /** + * Validate range. + */ + public boolean validate() { + if (end >= length) + end = length - 1; + return (start >= 0) && (end >= 0) && (start <= end) && (length > 0); + } + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/FastHttpDateFormat.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/FastHttpDateFormat.java new file mode 100644 index 00000000000..07b399569c3 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/FastHttpDateFormat.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Utility class to generate HTTP dates. + * + * @author Remy Maucherat + */ +public final class FastHttpDateFormat { + + + // -------------------------------------------------------------- Variables + + + private static final int CACHE_SIZE = + Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000")); + + + /** + * The only date format permitted when generating HTTP headers. + */ + public static final String RFC1123_DATE = + "EEE, dd MMM yyyy HH:mm:ss zzz"; + + private static final SimpleDateFormat format = + new SimpleDateFormat(RFC1123_DATE, Locale.US); + + + /** + * The set of SimpleDateFormat formats to use in getDateHeader(). + */ + private static final SimpleDateFormat formats[] = { + new SimpleDateFormat(RFC1123_DATE, Locale.US), + new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), + new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) + }; + + + private static final TimeZone gmtZone = TimeZone.getTimeZone("GMT"); + + + /** + * GMT timezone - all HTTP dates are on GMT + */ + static { + + format.setTimeZone(gmtZone); + + formats[0].setTimeZone(gmtZone); + formats[1].setTimeZone(gmtZone); + formats[2].setTimeZone(gmtZone); + + } + + + /** + * Instant on which the currentDate object was generated. + */ + private static volatile long currentDateGenerated = 0L; + + + /** + * Current formatted date. + */ + private static String currentDate = null; + + + /** + * Formatter cache. + */ + private static final ConcurrentHashMap<Long, String> formatCache = + new ConcurrentHashMap<Long, String>(CACHE_SIZE); + + + /** + * Parser cache. + */ + private static final ConcurrentHashMap<String, Long> parseCache = + new ConcurrentHashMap<String, Long>(CACHE_SIZE); + + + // --------------------------------------------------------- Public Methods + + + /** + * Get the current date in HTTP format. + */ + public static final String getCurrentDate() { + + long now = System.currentTimeMillis(); + if ((now - currentDateGenerated) > 1000) { + synchronized (format) { + if ((now - currentDateGenerated) > 1000) { + currentDate = format.format(new Date(now)); + currentDateGenerated = now; + } + } + } + return currentDate; + + } + + + /** + * Get the HTTP format of the specified date. + */ + public static final String formatDate + (long value, DateFormat threadLocalformat) { + + Long longValue = new Long(value); + String cachedDate = formatCache.get(longValue); + if (cachedDate != null) { + return cachedDate; + } + + String newDate = null; + Date dateValue = new Date(value); + if (threadLocalformat != null) { + newDate = threadLocalformat.format(dateValue); + updateFormatCache(longValue, newDate); + } else { + synchronized (format) { + newDate = format.format(dateValue); + } + updateFormatCache(longValue, newDate); + } + return newDate; + } + + + /** + * Try to parse the given date as a HTTP date. + */ + public static final long parseDate(String value, + DateFormat[] threadLocalformats) { + + Long cachedDate = parseCache.get(value); + if (cachedDate != null) { + return cachedDate.longValue(); + } + + Long date = null; + if (threadLocalformats != null) { + date = internalParseDate(value, threadLocalformats); + updateParseCache(value, date); + } else { + date = internalParseDate(value, formats); + updateParseCache(value, date); + } + if (date == null) { + return (-1L); + } + + return date.longValue(); + } + + + /** + * Parse date with given formatters. + */ + private static final Long internalParseDate + (String value, DateFormat[] formats) { + Date date = null; + for (int i = 0; (date == null) && (i < formats.length); i++) { + try { + date = formats[i].parse(value); + } catch (ParseException e) { + // Ignore + } + } + if (date == null) { + return null; + } + return new Long(date.getTime()); + } + + + /** + * Update cache. + */ + private static void updateFormatCache(Long key, String value) { + if (value == null) { + return; + } + if (formatCache.size() > CACHE_SIZE) { + formatCache.clear(); + } + formatCache.put(key, value); + } + + + /** + * Update cache. + */ + private static void updateParseCache(String key, Long value) { + if (value == null) { + return; + } + if (parseCache.size() > CACHE_SIZE) { + parseCache.clear(); + } + parseCache.put(key, value); + } + + +} diff --git a/src/main/java/org/olat/core/util/servlets/RequestUtil.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/RequestUtil.java similarity index 96% rename from src/main/java/org/olat/core/util/servlets/RequestUtil.java rename to src/main/java/org/olat/core/commons/services/webdav/servlets/RequestUtil.java index f785d60f217..25a7301b82a 100644 --- a/src/main/java/org/olat/core/util/servlets/RequestUtil.java +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/RequestUtil.java @@ -22,12 +22,13 @@ * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ -package org.olat.core.util.servlets; +package org.olat.core.commons.services.webdav.servlets; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.TimeZone; @@ -240,7 +241,7 @@ public final class RequestUtil { if ((header == null) || (header.length() < 1)) return (new Cookie[0]); - ArrayList cookies = new ArrayList(); + List<Cookie> cookies = new ArrayList<Cookie>(); while (header.length() > 0) { int semicolon = header.indexOf(';'); if (semicolon < 0) @@ -264,7 +265,7 @@ public final class RequestUtil { } } - return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()])); + return cookies.toArray(new Cookie[cookies.size()]); } @@ -286,7 +287,7 @@ public final class RequestUtil { * * @exception IllegalArgumentException if the data is malformed */ - public static void parseParameters(Map map, String data, String encoding) + public static void parseParameters(Map<String,String[]> map, String data, String encoding) throws UnsupportedEncodingException { if ((data != null) && (data.length() > 0)) { @@ -396,7 +397,7 @@ public final class RequestUtil { * Put name and value pair in map. When name already exist, add value * to array of values. */ - private static void putMapEntry( Map map, String name, String value) { + private static void putMapEntry( Map<String,String[]> map, String name, String value) { String[] newValues = null; String[] oldValues = (String[]) map.get(name); if (oldValues == null) { @@ -430,7 +431,7 @@ public final class RequestUtil { * * @exception UnsupportedEncodingException if the data is malformed */ - public static void parseParameters(Map map, byte[] data, String encoding) + public static void parseParameters(Map<String,String[]> map, byte[] data, String encoding) throws UnsupportedEncodingException { if (data != null && data.length > 0) { diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/WebResource.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebResource.java new file mode 100644 index 00000000000..f62fdf5764d --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebResource.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; + +import java.io.InputStream; + +/** + * Represents a file or directory within a web application. It borrows heavily + * from {@link java.io.File}. + */ +public interface WebResource { + /** + * See {@link java.io.File#lastModified()}. + */ + long getLastModified(); + + /** + * Return the last modified time of this resource in the correct format for + * the HTTP Last-Modified header as specified by RFC 2616. + */ + String getLastModifiedHttp(); + + /** + * See {@link java.io.File#exists()}. + */ + boolean exists(); + + /** + * See {@link java.io.File#isDirectory()}. + */ + boolean isDirectory(); + + /** + * See {@link java.io.File#isFile()}. + */ + boolean isFile(); + + /** + * See {@link java.io.File#delete()}. + */ + //boolean delete(); + + /** + * See {@link java.io.File#getName()}. + */ + String getName(); + + /** + * See {@link java.io.File#length()}. + */ + long getContentLength(); + + /** + * Return the strong ETag if available (currently not supported) else return + * the weak ETag calculated from the content length and last modified. + * + * @return The ETag for this resource + */ + String getETag(); + + /** + * Set the MIME type for this Resource. + */ + void setMimeType(String mimeType); + + /** + * Get the MIME type for this Resource. + */ + String getMimeType(); + + /** + * Obtain an InputStream based on the contents of this resource. + * + * @return An InputStream based on the contents of this resource or + * <code>null</code> if the resource does not exist or does not + * represent a file + */ + InputStream getInputStream(); + + /** + * Obtain the cached binary content of this resource. + */ + byte[] getContent(); + +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/WebResourceRoot.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebResourceRoot.java new file mode 100644 index 00000000000..9a700945bb3 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebResourceRoot.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; + +import java.io.InputStream; +import java.util.Collection; + +import org.olat.core.util.vfs.VFSItem; + + +public interface WebResourceRoot { + + public boolean canWrite(String path); + + public boolean canRename(String name); + + public boolean canDelete(String path); + + + /** + * Obtain the object that represents the resource at the given path. Note + * that the resource at that path may not exist. If the path does not + * exist, the WebResource returned will be associated with the main + * WebResourceSet. + * + * @param path The path for the resource of interest relative to the root + * of the web application. It must start with '/'. + * + * @return The object that represents the resource at the given path + */ + WebResource getResource(String path); + + /** + * Obtain the list of the names of all of the files and directories located + * in the specified directory. + * + * @param path The path for the resource of interest relative to the root + * of the web application. It must start with '/'. + * + * @return The list of resources. If path does not refer to a directory + * then a zero length array will be returned. + */ + Collection<VFSItem> list(String path); + + /** + * Create a new directory at the given path. + * + * @param path The path for the new resource to create relative to the root + * of the web application. It must start with '/'. + * + * @return <code>true</code> if the directory was created, otherwise + * <code>false</code> + */ + boolean mkdir(String path); + + /** + * Create a new resource at the requested path using the provided + * InputStream. + * + * @param path The path to be used for the new Resource. It is relative + * to the root of the web application and must start with + * '/'. + * @param is The InputStream that will provide the content for the + * new Resource. + * @param overwrite If <code>true</code> and the resource already exists it + * will be overwritten. If <code>false</code> and the + * resource already exists the write will fail. + * + * @return <code>true</code> if and only if the new Resource is written + */ + boolean write(String path, InputStream is, boolean overwrite); + + public boolean delete(WebResource resourceo); + +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/SecureWebdavServlet.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebdavDispatcher.java similarity index 50% rename from src/main/java/org/olat/core/commons/services/webdav/SecureWebdavServlet.java rename to src/main/java/org/olat/core/commons/services/webdav/servlets/WebdavDispatcher.java index dccdfd3870c..987ee8c0dc0 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/SecureWebdavServlet.java +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebdavDispatcher.java @@ -1,51 +1,42 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -* <p> -*/ - -package org.olat.core.commons.services.webdav; - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.RandomAccessFile; +import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; -import java.net.URLDecoder; -import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.Locale; +import java.util.Map; import java.util.Stack; import java.util.TimeZone; import java.util.Vector; -import javax.naming.NameAlreadyBoundException; -import javax.naming.NameClassPair; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -53,65 +44,112 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.apache.naming.resources.Resource; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.commons.services.webdav.manager.VFSDirContext; -import org.olat.core.helpers.Settings; -import org.olat.core.id.Identity; -import org.olat.core.logging.OLATRuntimeException; +import org.olat.core.commons.services.webdav.WebDAVDispatcher; +import org.olat.core.commons.services.webdav.WebDAVManager; +import org.olat.core.dispatcher.Dispatcher; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.ThreadLocalUserActivityLoggerInstaller; -import org.olat.core.util.UserSession; +import org.olat.core.util.Encoder; import org.olat.core.util.WorkThreadInformations; import org.olat.core.util.i18n.I18nManager; -import org.olat.core.util.servlets.DOMWriter; -import org.olat.core.util.servlets.RequestUtil; -import org.olat.core.util.servlets.XMLWriter; -import org.olat.core.util.vfs.VFSConstants; import org.olat.core.util.vfs.VFSItem; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; /** * Servlet which adds support for WebDAV level 2. All the basic HTTP requests - * are handled by the DefaultServlet. + * are handled by the DefaultServlet. The WebDAVServlet must not be used as the + * default servlet (ie mapped to '/') as it will not work in this configuration. + * <p/> + * Mapping a subpath (e.g. <code>/webdav/*</code> to this servlet has the effect + * of re-mounting the entire web application under that sub-path, with WebDAV + * access to all the resources. This <code>WEB-INF</code> and <code>META-INF</code> + * directories are protected in this re-mounted resource tree. + * <p/> + * To enable WebDAV for a context add the following to web.xml: + * <pre> + * <servlet> + * <servlet-name>webdav</servlet-name> + * <servlet-class>org.apache.catalina.servlets.WebdavServlet</servlet-class> + * <init-param> + * <param-name>debug</param-name> + * <param-value>0</param-value> + * </init-param> + * <init-param> + * <param-name>listings</param-name> + * <param-value>false</param-value> + * </init-param> + * </servlet> + * <servlet-mapping> + * <servlet-name>webdav</servlet-name> + * <url-pattern>/*</url-pattern> + * </servlet-mapping> + * </pre> + * This will enable read only access. To enable read-write access add: + * <pre> + * <init-param> + * <param-name>readonly</param-name> + * <param-value>false</param-value> + * </init-param> + * </pre> + * To make the content editable via a different URL, use the following + * mapping: + * <pre> + * <servlet-mapping> + * <servlet-name>webdav</servlet-name> + * <url-pattern>/webdavedit/*</url-pattern> + * </servlet-mapping> + * </pre> + * By default access to /WEB-INF and META-INF are not available via WebDAV. To + * enable access to these URLs, use add: + * <pre> + * <init-param> + * <param-name>allowSpecialPaths</param-name> + * <param-value>true</param-value> + * </init-param> + * </pre> + * Don't forget to secure access appropriately to the editing URLs, especially + * if allowSpecialPaths is used. With the mapping configuration above, the + * context will be accessible to normal users as before. Those users with the + * necessary access will be able to edit content available via + * http://host:port/context/content using + * http://host:port/context/webdavedit/content * * @author Remy Maucherat + * @version $Id$ */ -public class SecureWebdavServlet - extends DefaultServlet { +public class WebdavDispatcher + extends DefaultDispatcher implements WebDAVDispatcher, Dispatcher { - private static final long serialVersionUID = -4935604508424445093L; - private static final OLog log = Tracing.createLoggerFor(SecureWebdavServlet.class); + private static final long serialVersionUID = 1L; + private static final OLog log = Tracing.createLoggerFor(DefaultDispatcher.class); - // -------------------------------------------------------------- Constants - public static final String REQUEST_USERSESSION_KEY = "__usess"; - - protected static final String METHOD_HEAD = "HEAD"; - protected static final String METHOD_PROPFIND = "PROPFIND"; - protected static final String METHOD_PROPPATCH = "PROPPATCH"; - protected static final String METHOD_MKCOL = "MKCOL"; - protected static final String METHOD_COPY = "COPY"; - protected static final String METHOD_MOVE = "MOVE"; - protected static final String METHOD_DELETE = "DELETE"; - protected static final String METHOD_LOCK = "LOCK"; - protected static final String METHOD_UNLOCK = "UNLOCK"; - protected static final String METHOD_GET = "GET"; - protected static final String METHOD_PUT = "PUT"; - protected static final String METHOD_OPTIONS = "OPTIONS"; - - /** - * Default depth is infite. - */ - private static final int INFINITY = 3; // To limit tree browsing a bit + // -------------------------------------------------------------- Constants + private static final String METHOD_PROPFIND = "PROPFIND"; + private static final String METHOD_PROPPATCH = "PROPPATCH"; + private static final String METHOD_MKCOL = "MKCOL"; + private static final String METHOD_COPY = "COPY"; + private static final String METHOD_MOVE = "MOVE"; + private static final String METHOD_LOCK = "LOCK"; + private static final String METHOD_UNLOCK = "UNLOCK"; + private static final String METHOD_DELETE = "DELETE"; + private static final String METHOD_HEAD = "HEAD"; + private static final String METHOD_GET = "GET"; + private static final String METHOD_OPTIONS = "OPTIONS"; + private static final String METHOD_POST = "POST"; + private static final String METHOD_PUT = "PUT"; /** * PROPFIND - Specify a property mask. @@ -164,30 +202,20 @@ public class SecureWebdavServlet /** * Simple date format for the creation date ISO representation (partial). */ - protected static final SimpleDateFormat creationDateFormat = - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + protected static final ConcurrentDateFormat creationDateFormat = + new ConcurrentDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US, + TimeZone.getTimeZone("GMT")); - static { - creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - } - - /** - * Manager initialized in init via spring bean - */ - private static WebDAVManager webDAVManager; - // ----------------------------------------------------- Instance Variables - /** * Repository of the locks put on single resources. * <p> * Key : path <br> * Value : LockInfo */ - private Hashtable resourceLocks = new Hashtable(); - + private final Hashtable<String,LockInfo> resourceLocks = new Hashtable<String,LockInfo>(); /** * Repository of the lock-null resources. @@ -197,7 +225,7 @@ public class SecureWebdavServlet * collection. Each element of the Vector is the path associated with * the lock-null resource. */ - private Hashtable lockNullResources = new Hashtable(); + private final Map<String,Vector<String>> lockNullResources = new Hashtable<String,Vector<String>>(); /** @@ -206,7 +234,7 @@ public class SecureWebdavServlet * Key : path <br> * Value : LockInfo */ - private Vector collectionLocks = new Vector(); + private final Vector<LockInfo> collectionLocks = new Vector<LockInfo>(); /** @@ -214,78 +242,74 @@ public class SecureWebdavServlet */ private String secret = "catalina"; - // --------------------------------------------------------- Public Methods + + /** + * Default depth in spec is infinite. Limit depth to 3 by default as + * infinite depth makes operations very expensive. + */ + private int maxDepth = 3; /** - * Initialize this servlet. + * Is access allowed via WebDAV to the special paths (/WEB-INF and + * /META-INF)? */ - public void init() - throws ServletException { + private boolean allowSpecialPaths = false; - super.init(); - String value = null; - try { - value = getServletConfig().getInitParameter("secret"); - if (value != null) - secret = value; - } catch (Throwable t) { - // - } + // --------------------------------------------------------- Public Methods - webDAVManager = WebDAVManager.getInstance(); - if (webDAVManager != null && webDAVManager.isEnabled()) { - // - } else { - // disabled by configuration - webDAVManager = null; - Tracing.logInfo("WebDAV support disabled by WebDAVManager spring configuration", SecureWebdavServlet.class); - } + public WebdavDispatcher() { + // } - /** - * Finalize this servlet. - */ - public void destroy() { - //webdav manager gets destroyed by spring container - } - // ------------------------------------------------------ Protected Methods + @Override + protected WebResourceRoot getResources(HttpServletRequest req) { + return CoreSpringFactory.getImpl(WebDAVManager.class).getWebDAVRoot(req); + } - /** + /** * Return JAXP document builder instance. */ - protected DocumentBuilder getDocumentBuilder() + protected DocumentBuilder getDocumentBuilder(HttpServletRequest req) throws ServletException { DocumentBuilder documentBuilder = null; + DocumentBuilderFactory documentBuilderFactory = null; try { - documentBuilder = - DocumentBuilderFactory.newInstance().newDocumentBuilder(); + documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + documentBuilderFactory.setExpandEntityReferences(false); + documentBuilder = documentBuilderFactory.newDocumentBuilder(); + documentBuilder.setEntityResolver( + new WebdavResolver(req.getServletContext())); } catch(ParserConfigurationException e) { throw new ServletException ("webdavservlet.jaxpfailed"); } return documentBuilder; } - - /** - * Wrap the request around the same calls as the OLATServlet - */ - protected void service(HttpServletRequest request, HttpServletResponse resp) - throws ServletException, IOException { + + public void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { try { - Tracing.setUreq(request); - I18nManager.attachI18nInfoToThread(request); - ThreadLocalUserActivityLoggerInstaller.initUserActivityLogger(request); - //fxdiff FXOLAT-97: high CPU load tracker - WorkThreadInformations.set("Serve request: " + request.getRequestURI()); - - secureService(request, resp); + Tracing.setUreq(req); + I18nManager.attachI18nInfoToThread(req); + ThreadLocalUserActivityLoggerInstaller.initUserActivityLogger(req); + WorkThreadInformations.set("Serve request: " + req.getRequestURI()); + resp.setCharacterEncoding("UTF-8"); + + WebDAVManager webDAVManager = CoreSpringFactory.getImpl(WebDAVManager.class); + if (webDAVManager == null) { + resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); + } else if (webDAVManager.handleAuthentication(req, resp)) { + webdavService(req, resp); + } else { + //the method handleAuthentication will send the challenges for authentication + } } finally { - //fxdiff FXOLAT-97: high CPU load tracker WorkThreadInformations.unset(); ThreadLocalUserActivityLoggerInstaller.resetUserActivityLogger(); I18nManager.remove18nInfoFromThread(); @@ -293,340 +317,266 @@ public class SecureWebdavServlet DBFactory.getInstanceForClosing().closeSession(); } } - - /** - * Handles the special WebDAV methods. - */ - protected void secureService(HttpServletRequest req, HttpServletResponse resp) { - try { - String method = req.getMethod(); - String path = getRelativePath(req); - // OLAT-6294 alsways set encoding to UTF-8, overwritten later when a - // resource is different - resp.setCharacterEncoding("UTF-8"); - if (debug > 0) { - log.debug("[" + method + "] " + path); - } - - // security check; response header will be set appropriately - // returns false if security check failed. + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + WebDAVManager webDAVManager = CoreSpringFactory.getImpl(WebDAVManager.class); + if (webDAVManager == null) { + resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); + } else if (webDAVManager.handleAuthentication(req, resp)) { + webdavService(req, resp); + } else { + //the method handleAuthentication will send the challenges for authentication + } + } - if (webDAVManager == null) { - // is not initialized properly - return; - } - boolean authenticated = webDAVManager.handleAuthentication(req, resp); - if (!authenticated) { - return; - } - if (method.equals(METHOD_PROPFIND)) { - doPropfind(req, resp); - } else if (method.equals(METHOD_PROPPATCH)) { - doProppatch(req, resp); - } else if (method.equals(METHOD_OPTIONS)) { - doOptions(req, resp); - } else if (method.equals(METHOD_MKCOL)) { - doMkcol(req, resp); - } else if (method.equals(METHOD_COPY)) { - doCopy(req, resp); - } else if (method.equals(METHOD_MOVE)) { - doMove(req, resp); - } else if (method.equals(METHOD_LOCK)) { - doLock(req, resp); - } else if (method.equals(METHOD_UNLOCK)) { - doUnlock(req, resp); - } else if (method.equals(METHOD_HEAD)) { - String decodedPath = URLDecoder.decode(path, "UTF-8"); - String headerCD = "attachment; filename=" + decodedPath.substring(decodedPath.lastIndexOf("/") + 1); - resp.setHeader("Content-Disposition", headerCD); - super.service(req, resp); - } else if (method.equals(METHOD_PUT)) { - doPut(req, resp); - } else if (method.equals(METHOD_DELETE)) { - doDelete(req, resp); - } else { - // DefaultServlet processing - super.service(req, resp); - } - } catch (Exception e) { - log.error("Exception in WebDAV request", e); - throw new OLATRuntimeException(this.getClass(), "Exception in SecureWebDavServlet.", e); - } catch (Error er) { - log.error("Error in WebDAV request", er); - throw new OLATRuntimeException(this.getClass(), "Error in SecureWebDavServlet.", er); - } catch (Throwable er) { - log.error("Throwable in WebDAV request", er); - throw new OLATRuntimeException(this.getClass(), "Throwable in SecureWebDavServlet.", er); - } - } - ///////////////////////////////////////////// - // Start of additions to Tomcat WebdavServlet - ///////////////////////////////////////////// - - /** - * Return the relative path associated with this servlet. - * - * @param request The servlet request we are processing + /** + * Handles the special WebDAV methods. */ - protected String getRelativePath(HttpServletRequest request) { + protected void webdavService(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { - // No, extract the desired path directly from the request - String path = request.getPathInfo(); - return normalize(path, true); + final String path = getRelativePath(req); + + // Block access to special subdirectories. + // DefaultServlet assumes it services resources from the root of the web app + // and doesn't add any special path protection + // WebdavServlet remounts the webapp under a new path, so this check is + // necessary on all methods (including GET). + if (isSpecialPath(path)) { + resp.sendError(WebdavStatus.SC_NOT_FOUND); + return; + } + + final String method = req.getMethod(); + + if (log.isDebug()) { + log.debug("[" + method + "] " + path); + } + + if (method.equals(METHOD_PROPFIND)) { + doPropfind(req, resp); + } else if (method.equals(METHOD_PROPPATCH)) { + doProppatch(req, resp); + } else if (method.equals(METHOD_MKCOL)) { + doMkcol(req, resp); + } else if (method.equals(METHOD_COPY)) { + doCopy(req, resp); + } else if (method.equals(METHOD_MOVE)) { + doMove(req, resp); + } else if (method.equals(METHOD_LOCK)) { + doLock(req, resp); + } else if (method.equals(METHOD_UNLOCK)) { + doUnlock(req, resp); + } else if (method.equals(METHOD_GET)) { + doGet(req, resp); + } else if (method.equals(METHOD_HEAD)) { + doHead(req, resp); + } else if (method.equals(METHOD_POST)) { + doPost(req, resp); + } else if (method.equals(METHOD_PUT)) { + doPut(req, resp); + } else if (method.equals(METHOD_DELETE)) { + doDelete(req, resp); + } else if (method.equals(METHOD_OPTIONS)) { + doOptions(req,resp); + } } - /** - * Special case needed here: - * Use the bcRoot directly since the servlet mapping gets - * prepended by the request path already. - */ - protected DirContext getResources(HttpServletRequest hreq) { - // try to get cached dir context - if (webDAVManager == null) { - // not initialized properly - return null; - } - UserSession usess = webDAVManager.getUserSession(hreq); - if (usess == null) return null; - try{ - usess.getSessionInfo().setLastClickTime(); - }catch(Throwable th){ - Tracing.logError("getResource failure: ", th, SecureWebdavServlet.class); - } - VFSDirContext fdc = (VFSDirContext)usess.getEntry("_DIRCTX"); - if (fdc != null) { - fdc.setUserSession(usess); - return fdc; - } - - Identity identity = usess.getIdentity(); - // we need an identity... - if (identity == null) return null; - - - fdc = new VFSDirContext(); - fdc.setIdentity(identity); - fdc.setUserSession(usess); - fdc.setVirtualDocBase(WebDAVProviderFactory.getInstance().getMountableRoot(identity)); - fdc.setBuffer(super.input); // take inBuffer from DefaultServlet (configured via web.xml) - // do not cache while debugging - if (!Settings.isDebuging()) { - usess.putEntry("_DIRCTX", fdc); - } - - // OLAT-5243 related: sending back the reply can take arbitrary long, - // considering slow end-user connections for example - or a sudden death of the connection - // on the client-side which remains unnoticed (network partitioning) - DBFactory.getInstance().intermediateCommit(); - - return fdc; - } - /** - * Process a PUT request for the specified resource. - * Method modified by Mike Stock. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet-specified error occurs + * Checks whether a given path refers to a resource under + * <code>WEB-INF</code> or <code>META-INF</code>. + * @param path the full path of the resource being accessed + * @return <code>true</code> if the resource specified is under a special path */ - protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - -// if (isLocked(req)) { -// resp.sendError(WebdavStatus.SC_LOCKED); -// return; -// } - - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; - String path = getRelativePath(req); - if (!vfsContext.canWrite(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - super.doPut(req, resp); - - // Removing any lock-null resource which would be present - lockNullResources.remove(path); - -// if (!WebDAVManager.checkQuota(usess, bcPath)) { -// // quota exceeded. Delete file and send error. -// File toDelete = new File(bcPath.getCanonicalPath()); -// if (toDelete.exists()) { toDelete.delete(); } -// resp.sendError(WebdavStatus.SC_INSUFFICIENT_SPACE_ON_RESOURCE); -// log.warn("WebDAV access for briefcase '" + bcPath.getFullPath() + "' failed: quota exceeded."); -// return; -// } - // TODO: Modified WebDAV + private final boolean isSpecialPath(final String path) { + return !allowSpecialPaths && ( + path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") || + path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")); } - /////////////////////////////////////////// - // End of additions to Tomcat WebdavServlet - /////////////////////////////////////////// - /** - * Check if the conditions specified in the optional If headers are - * satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resourceInfo File object - * @return boolean true if the resource meets all the specified conditions, - * and false if any of the conditions is not satisfied, in which case - * request processing is stopped - */ + @Override protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse response, - ResourceInfo resourceInfo) + WebResource resource) throws IOException { - if (!super.checkIfHeaders(request, response, resourceInfo)) + if (!super.checkIfHeaders(request, response, resource)) return false; // TODO : Checking the WebDAV If header return true; - } /** - * OPTIONS Method. + * Override the DefaultServlet implementation and only use the PathInfo. If + * the ServletPath is non-null, it will be because the WebDAV servlet has + * been mapped to a url other than /* to configure editing at different url + * than normal viewing. + * + * @param request The servlet request we are processing */ - protected void doOptions(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + @Override + protected String getRelativePath(HttpServletRequest request) { + // Are we being processed by a RequestDispatcher.include()? + if (request.getAttribute( + RequestDispatcher.INCLUDE_REQUEST_URI) != null) { + String result = (String) request.getAttribute( + RequestDispatcher.INCLUDE_PATH_INFO); + if ((result == null) || (result.equals(""))) + result = "/"; + return (result); + } - String path = getRelativePath(req); + // No, extract the desired path directly from the request + String result = request.getPathInfo(); + if ((result == null) || (result.equals(""))) { + result = "/"; + } + return (result); - resp.addHeader("DAV", "1,2"); - String methodsAllowed = null; + } - // Retrieve the resources - DirContext resources = getResources(req); - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; + /** + * Determines the prefix for standard directory GET listings. + */ + @Override + protected String getPathPrefix(final HttpServletRequest request) { + // Repeat the servlet path (e.g. /webdav/) in the listing path + String contextPath = request.getContextPath(); + if (request.getServletPath() != null) { + contextPath = contextPath + request.getServletPath(); } + return contextPath; + } - boolean exists = true; - Object object = null; - try { - object = resources.lookup(path); - } catch (NamingException e) { - exists = false; - } - if (!exists) { - methodsAllowed = "OPTIONS, MKCOL, PUT, LOCK"; - resp.addHeader("Allow", methodsAllowed); - return; - } + /** + * OPTIONS Method. + * + * @param req The request + * @param resp The response + * @throws ServletException If an error occurs + * @throws IOException If an IO error occurs + */ + public void doOptions(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { - methodsAllowed = "OPTIONS, GET, HEAD, POST, DELETE, TRACE, " - + "PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK"; - if (!(object instanceof DirContext)) { - methodsAllowed += ", PUT"; - } + resp.addHeader("DAV", "1,2"); - resp.addHeader("Allow", methodsAllowed); + StringBuilder methodsAllowed = determineMethodsAllowed(req); + resp.addHeader("Allow", methodsAllowed.toString()); resp.addHeader("MS-Author-Via", "DAV"); - } + @Override + public void doRootOptions(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.addHeader("DAV", "1,2"); + resp.setHeader("Allow", "OPTIONS, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, LOCK, UNLOCK"); + + resp.addHeader("MS-Author-Via", "DAV"); + resp.setDateHeader("Date", new Date().getTime()); + resp.setHeader("Content-Length", "0"); + resp.setContentLength(0); + resp.setStatus(200); + } + + @Override + public void doWebdavOptions(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.addHeader("DAV", "1,2"); + resp.setHeader("Allow", "PROPFIND, OPTIONS"); + resp.addHeader("MS-Author-Via", "DAV"); + resp.setDateHeader("Date", new Date().getTime()); + resp.setHeader("Content-Length", "0"); + resp.setContentLength(0); + resp.setStatus(200); + } - /** + /** * PROPFIND Method. */ - protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) + public void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (!listings) { - resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); - return; - } - String path = getRelativePath(req); if (path.endsWith("/")) path = path.substring(0, path.length() - 1); - if ((path.toUpperCase().startsWith("/WEB-INF")) || - (path.toUpperCase().startsWith("/META-INF"))) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - // Properties which are to be displayed. - Vector properties = null; + Vector<String> properties = null; // Propfind depth - int depth = INFINITY; + int depth = maxDepth; // Propfind type int type = FIND_ALL_PROP; String depthStr = req.getHeader("Depth"); if (depthStr == null) { - depth = INFINITY; + depth = maxDepth; } else { if (depthStr.equals("0")) { depth = 0; } else if (depthStr.equals("1")) { depth = 1; } else if (depthStr.equals("infinity")) { - depth = INFINITY; + depth = maxDepth; } } Node propNode = null; - DocumentBuilder documentBuilder = getDocumentBuilder(); + if (req.getContentLength() > 0) { + DocumentBuilder documentBuilder = getDocumentBuilder(req); - try { - InputStream in = req.getInputStream(); - if (in.available() > 0) { - Document document = documentBuilder.parse - (new InputSource(in)); - - // Get the root element of the document - Element rootElement = document.getDocumentElement(); - NodeList childList = rootElement.getChildNodes(); - - for (int i=0; i < childList.getLength(); i++) { - Node currentNode = childList.item(i); - switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - break; - case Node.ELEMENT_NODE: - if (currentNode.getNodeName().endsWith("prop")) { - type = FIND_BY_PROPERTY; - propNode = currentNode; - } - if (currentNode.getNodeName().endsWith("propname")) { - type = FIND_PROPERTY_NAMES; - } - if (currentNode.getNodeName().endsWith("allprop")) { - type = FIND_ALL_PROP; - } - break; - } - } - } - } catch (Exception e) { - // Most likely there was no content : we use the defaults. - // TODO : Enhance that ! + try { + Document document = documentBuilder.parse + (new InputSource(req.getInputStream())); + + // Get the root element of the document + Element rootElement = document.getDocumentElement(); + NodeList childList = rootElement.getChildNodes(); + + for (int i=0; i < childList.getLength(); i++) { + Node currentNode = childList.item(i); + switch (currentNode.getNodeType()) { + case Node.TEXT_NODE: + break; + case Node.ELEMENT_NODE: + if (currentNode.getNodeName().endsWith("prop")) { + type = FIND_BY_PROPERTY; + propNode = currentNode; + } + if (currentNode.getNodeName().endsWith("propname")) { + type = FIND_PROPERTY_NAMES; + } + if (currentNode.getNodeName().endsWith("allprop")) { + type = FIND_ALL_PROP; + } + break; + } + } + } catch (SAXException e) { + // Something went wrong - bad request + resp.sendError(WebdavStatus.SC_BAD_REQUEST); + } catch (IOException e) { + // Something went wrong - bad request + resp.sendError(WebdavStatus.SC_BAD_REQUEST); + } } if (type == FIND_BY_PROPERTY) { - properties = new Vector(); + properties = new Vector<String>(); + // propNode must be non-null if type == FIND_BY_PROPERTY NodeList childList = propNode.getChildNodes(); for (int i=0; i < childList.getLength(); i++) { @@ -651,47 +601,28 @@ public class SecureWebdavServlet } - // Retrieve the resources - DirContext resources = getResources(req); - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } + WebResourceRoot resources = getResources(req); + WebResource resource = resources.getResource(path); - boolean exists = true; - Object object = null; - try { - object = resources.lookup(path); - } catch (NamingException e) { - exists = false; + if (!resource.exists()) { int slash = path.lastIndexOf('/'); if (slash != -1) { String parentPath = path.substring(0, slash); - Vector currentLockNullResources = - (Vector) lockNullResources.get(parentPath); + Vector<String> currentLockNullResources = lockNullResources.get(parentPath); if (currentLockNullResources != null) { - Enumeration lockNullResourcesList = - currentLockNullResources.elements(); + Enumeration<String> lockNullResourcesList = currentLockNullResources.elements(); while (lockNullResourcesList.hasMoreElements()) { - String lockNullPath = (String) + String lockNullPath = lockNullResourcesList.nextElement(); if (lockNullPath.equals(path)) { resp.setStatus(WebdavStatus.SC_MULTI_STATUS); resp.setContentType("text/xml; charset=UTF-8"); // Create multistatus object - XMLWriter generatedXML = - new XMLWriter(resp.getWriter()); + XMLWriter generatedXML = new XMLWriter(resp.getWriter()); generatedXML.writeXMLHeader(); - generatedXML.writeElement - (null, "multistatus" - + generateNamespaceDeclarations(), - XMLWriter.OPENING); - parseLockNullProperties - (req, generatedXML, lockNullPath, type, - properties); - generatedXML.writeElement(null, "multistatus", - XMLWriter.CLOSING); + generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING); + parseLockNullProperties(req, generatedXML, lockNullPath, type, properties); + generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); generatedXML.sendData(); return; } @@ -700,7 +631,7 @@ public class SecureWebdavServlet } } - if (!exists) { + if (!resource.exists()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, path); return; } @@ -713,68 +644,51 @@ public class SecureWebdavServlet XMLWriter generatedXML = new XMLWriter(resp.getWriter()); generatedXML.writeXMLHeader(); - generatedXML.writeElement(null, "multistatus" - + generateNamespaceDeclarations(), - XMLWriter.OPENING); + generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING); if (depth == 0) { - parseProperties(req, resources, generatedXML, path, type, + parseProperties(req, generatedXML, path, type, properties); } else { // The stack always contains the object of the current level - Stack stack = new Stack(); + Stack<String> stack = new Stack<String>(); stack.push(path); // Stack of the objects one level below - Stack stackBelow = new Stack(); - - int count = 0; + Stack<String> stackBelow = new Stack<String>(); while ((!stack.isEmpty()) && (depth >= 0)) { - String currentPath = (String) stack.pop(); - parseProperties(req, resources, generatedXML, currentPath, + String currentPath = stack.pop(); + parseProperties(req, generatedXML, currentPath, type, properties); - try { - object = resources.lookup(currentPath); - } catch (NamingException e) { - continue; - } + resource = resources.getResource(currentPath); - if ((object instanceof DirContext) && (depth > 0)) { + if (resource.isDirectory() && (depth > 0)) { - try { - NamingEnumeration enumeration = resources.list(currentPath); - while (enumeration.hasMoreElements()) { - NameClassPair ncPair = - (NameClassPair) enumeration.nextElement(); - String newPath = currentPath; - if (!(newPath.endsWith("/"))) + Collection<VFSItem> entries = resources.list(currentPath); + for (VFSItem entry : entries) { + String newPath = currentPath; + if (!(newPath.endsWith("/"))) newPath += "/"; - newPath += ncPair.getName(); - stackBelow.push(newPath); - } - } catch (NamingException e) { - resp.sendError - (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - path); - return; + newPath += entry.getName(); + stackBelow.push(newPath); } // Displaying the lock-null resources present in that // collection String lockPath = currentPath; if (lockPath.endsWith("/")) - lockPath = + lockPath = lockPath.substring(0, lockPath.length() - 1); - Vector currentLockNullResources = - (Vector) lockNullResources.get(lockPath); + Vector<String> currentLockNullResources = + lockNullResources.get(lockPath); if (currentLockNullResources != null) { - Enumeration lockNullResourcesList = + Enumeration<String> lockNullResourcesList = currentLockNullResources.elements(); while (lockNullResourcesList.hasMoreElements()) { - String lockNullPath = (String) + String lockNullPath = lockNullResourcesList.nextElement(); parseLockNullProperties (req, generatedXML, lockNullPath, type, @@ -787,19 +701,15 @@ public class SecureWebdavServlet if (stack.isEmpty()) { depth--; stack = stackBelow; - stackBelow = new Stack(); + stackBelow = new Stack<String>(); } generatedXML.sendData(); - //fast every iteration make 1 call to the database - if(count++ % 50 == 0) { - DBFactory.getInstance().intermediateCommit(); - } + } } - generatedXML.writeElement(null, "multistatus", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); generatedXML.sendData(); @@ -809,203 +719,314 @@ public class SecureWebdavServlet /** * PROPPATCH Method. */ - protected void doProppatch(HttpServletRequest req, - HttpServletResponse resp) - throws IOException { + public void doProppatch(HttpServletRequest req, HttpServletResponse resp) + throws IOException { - if (readOnly) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); + if (isLocked(req)) { + resp.sendError(WebdavStatus.SC_LOCKED); return; } - - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; + String path = getRelativePath(req); - if (!vfsContext.canWrite(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; + WebResourceRoot resources = getResources(req); + if(!resources.canWrite(path)) { + resp.sendError(WebdavStatus.SC_FORBIDDEN); + return; } - - if (isLocked(req)) { - resp.sendError(WebdavStatus.SC_LOCKED); - return; + + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); } - resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + resp.setStatus(WebdavStatus.SC_MULTI_STATUS); + resp.setContentType("text/xml; charset=UTF-8"); + + /* + <?xml version="1.0" encoding="utf-8" ?> + <D:multistatus xmlns:D="DAV:" + xmlns:Z="http://www.w3.com/standards/z39.50"> + <D:response> + <D:href>http://www.foo.com/bar.html</D:href> + <D:propstat> + <D:prop><Z:Authors/></D:prop> + <D:status>HTTP/1.1 424 Failed Dependency</D:status> + </D:propstat> + <D:propstat> + <D:prop><Z:Copyright-Owner/></D:prop> + <D:status>HTTP/1.1 409 Conflict</D:status> + </D:propstat> + <D:responsedescription> Copyright Owner can not be deleted or altered.</D:responsedescription> + </D:response> + </D:multistatus> + */ + + XMLWriter generatedXML = new XMLWriter(resp.getWriter()); + generatedXML.writeXMLHeader(); + generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING); + + parseProperties( req, generatedXML, path, 32, new Vector<String>()); + generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); + generatedXML.sendData(); } /** * MKCOL Method. */ - protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - - if (readOnly) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; - String path = getRelativePath(req); - if (!vfsContext.canWrite(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } + public void doMkcol(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { if (isLocked(req)) { resp.sendError(WebdavStatus.SC_LOCKED); return; } - path = getRelativePath(req); - - if ((path.toUpperCase().startsWith("/WEB-INF")) || - (path.toUpperCase().startsWith("/META-INF"))) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - // Retrieve the resources - resources = getResources(req); - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - // Check if operation allowed by OLAT VFS security callback - vfsContext = (VFSDirContext) resources; - if (!vfsContext.canWrite(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - boolean exists = true; - try { - resources.lookup(path); - } catch (NamingException e) { - exists = false; - } + String path = getRelativePath(req); + WebResourceRoot resources = getResources(req); + WebResource resource = resources.getResource(path); // Can't create a collection if a resource already exists at the given // path - if (exists) { + if (resource.exists()) { + // Get allowed methods + StringBuilder methodsAllowed = determineMethodsAllowed(req); + + resp.addHeader("Allow", methodsAllowed.toString()); + resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); return; } - boolean result = true; - try { - resources.createSubcontext(path); - } catch (NameAlreadyBoundException e) { - result = false; - } catch (NamingException e) { - result = false; + if (req.getContentLength() > 0) { + DocumentBuilder documentBuilder = getDocumentBuilder(req); + try { + // Document document = + documentBuilder.parse(new InputSource(req.getInputStream())); + // TODO : Process this request body + resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED); + return; + + } catch(SAXException saxe) { + // Parse error - assume invalid content + resp.sendError(WebdavStatus.SC_UNSUPPORTED_MEDIA_TYPE); + return; + } } - if (!result) { - resp.sendError(WebdavStatus.SC_FORBIDDEN, - WebdavStatus.getStatusText - (WebdavStatus.SC_FORBIDDEN)); - } else { + if (resources.mkdir(path)) { resp.setStatus(WebdavStatus.SC_CREATED); // Removing any lock-null resource which would be present lockNullResources.remove(path); + } else { + resp.sendError(WebdavStatus.SC_CONFLICT, + WebdavStatus.getStatusText + (WebdavStatus.SC_CONFLICT)); } - } /** * DELETE Method. */ - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) + public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (readOnly) { + if (isLocked(req)) { + resp.sendError(WebdavStatus.SC_LOCKED); + return; + } + + String path = this.getRelativePath(req); + WebResourceRoot resources = this.getResources(req); + if(!resources.canDelete(path)) { resp.sendError(WebdavStatus.SC_FORBIDDEN); return; } - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; - String path = getRelativePath(req); - if (!vfsContext.canDelete(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } + deleteResource(req, resp); + } + + /** + * Process a HEAD request for the specified resource. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet-specified error occurs + */ + protected void doHead(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + // Serve the requested resource, without the data content + serveResource(request, response, false, fileEncoding); + } + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + serveResource(request, response, true, fileEncoding); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + serveResource(request, response, true, fileEncoding); + } + + /** + * Process a PUT request for the specified resource. + * + * @param req The servlet request we are processing + * @param resp The servlet response we are creating + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet-specified error occurs + */ + public void doPut(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { if (isLocked(req)) { resp.sendError(WebdavStatus.SC_LOCKED); return; } - deleteResource(req, resp); + String path = getRelativePath(req); + WebResourceRoot resources = getResources(req); + if (!resources.canWrite(path)) { + resp.sendError(WebdavStatus.SC_FORBIDDEN); + return; + } + + WebResource resource = resources.getResource(path); + Range range = parseContentRange(req, resp); + InputStream resourceInputStream = null; - } + try { + // Append data specified in ranges to existing content for this + // resource - create a temp. file on the local filesystem to + // perform this operation + // Assume just one range is specified for now + if (range != null) { + File contentFile = executePartialPut(req, range, path); + resourceInputStream = new FileInputStream(contentFile); + } else { + resourceInputStream = req.getInputStream(); + } + if (resources.write(path, resourceInputStream, true)) { + if (resource.exists()) { + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + } else { + resp.setStatus(HttpServletResponse.SC_CREATED); + } + } else { + resp.sendError(HttpServletResponse.SC_CONFLICT); + } + } finally { + if (resourceInputStream != null) { + try { + resourceInputStream.close(); + } catch (IOException ioe) { + // Ignore + } + } + } + // Removing any lock-null resource which would be present + lockNullResources.remove(path); + } + /** - * COPY Method. + * Handle a partial PUT. New content specified in request is appended to + * existing content in oldRevisionContent (if present). This code does + * not support simultaneous partial updates to the same resource. */ - protected void doCopy(HttpServletRequest req, HttpServletResponse resp) - throws IOException { + private File executePartialPut(HttpServletRequest req, Range range, String path) + throws IOException { - if (readOnly) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; + // Append data specified in ranges to existing content for this + // resource - create a temp. file on the local filesystem to + // perform this operation + File tempDir = (File) req.getServletContext().getAttribute(ServletContext.TEMPDIR); + + // Convert all '/' characters to '.' in resourcePath + String convertedResourcePath = path.replace('/', '.'); + File contentFile = new File(tempDir, convertedResourcePath); + if (contentFile.createNewFile()) { + // Clean up contentFile when Tomcat is terminated + contentFile.deleteOnExit(); } - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; - String destinationPath = req.getHeader("Destination"); - // First decode URL - destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8"); - // Then normalize to NFC form for comparison - destinationPath = normalize(destinationPath); - if (!vfsContext.canWrite(destinationPath)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; + RandomAccessFile randAccessContentFile = + new RandomAccessFile(contentFile, "rw"); + + WebResource oldResource = getResources(req).getResource(path); + + // Copy data in oldRevisionContent to contentFile + if (oldResource.isFile()) { + BufferedInputStream bufOldRevStream = + new BufferedInputStream(oldResource.getInputStream(), + BUFFER_SIZE); + + int numBytesRead; + byte[] copyBuffer = new byte[BUFFER_SIZE]; + while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) { + randAccessContentFile.write(copyBuffer, 0, numBytesRead); + } + + bufOldRevStream.close(); + } + + randAccessContentFile.setLength(range.length); + + // Append data in request input stream to contentFile + randAccessContentFile.seek(range.start); + int numBytesRead; + byte[] transferBuffer = new byte[BUFFER_SIZE]; + BufferedInputStream requestBufInStream = + new BufferedInputStream(req.getInputStream(), BUFFER_SIZE); + while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { + randAccessContentFile.write(transferBuffer, 0, numBytesRead); } + randAccessContentFile.close(); + requestBufInStream.close(); - copyResource(req, resp); + return contentFile; + } + + /** + * COPY Method. + */ + public void doCopy(HttpServletRequest req, HttpServletResponse resp) + throws IOException { + + String path = getRelativePath(req); + WebResourceRoot resources = getResources(req); + if (resources.canWrite(path)) { + copyResource(req, resp, false); + } else { + resp.sendError(WebdavStatus.SC_FORBIDDEN); + } } /** * MOVE Method. */ - protected void doMove(HttpServletRequest req, HttpServletResponse resp) - throws IOException { + public void doMove(HttpServletRequest req, HttpServletResponse resp) + throws IOException { - if (readOnly) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); + if (isLocked(req)) { + resp.sendError(WebdavStatus.SC_LOCKED); return; } - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; String path = getRelativePath(req); - if (!vfsContext.canRename(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - if (isLocked(req)) { - resp.sendError(WebdavStatus.SC_LOCKED); + WebResourceRoot resources = this.getResources(req); + if(!resources.canRename(path)) { + resp.sendError(WebdavStatus.SC_FORBIDDEN); return; } - - if (copyResource(req, resp)) { - deleteResource(path, req, resp); + + if (copyResource(req, resp, true)) { + deleteResource(path, req, resp, false); } } @@ -1013,27 +1034,19 @@ public class SecureWebdavServlet /** * LOCK Method. */ - protected void doLock(HttpServletRequest req, HttpServletResponse resp) + public void doLock(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (readOnly) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); + if(isLocked(req)) { + resp.sendError(WebdavStatus.SC_LOCKED); return; } - - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; - String path = getRelativePath(req); - if (!vfsContext.canWrite(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - if (isLocked(req)) { - resp.sendError(WebdavStatus.SC_LOCKED); - return; + final String path = getRelativePath(req); + final WebResourceRoot resources = getResources(req); + if(!resources.canWrite(path)) { + resp.sendError(WebdavStatus.SC_FORBIDDEN); + return; } LockInfo lock = new LockInfo(); @@ -1045,12 +1058,12 @@ public class SecureWebdavServlet String depthStr = req.getHeader("Depth"); if (depthStr == null) { - lock.depth = INFINITY; + lock.depth = maxDepth; } else { if (depthStr.equals("0")) { lock.depth = 0; } else { - lock.depth = INFINITY; + lock.depth = maxDepth; } } @@ -1061,6 +1074,11 @@ public class SecureWebdavServlet if (lockDurationStr == null) { lockDuration = DEFAULT_TIMEOUT; } else { + int commaPos = lockDurationStr.indexOf(","); + // If multiple timeouts, just use the first + if (commaPos != -1) { + lockDurationStr = lockDurationStr.substring(0,commaPos); + } if (lockDurationStr.startsWith("Second-")) { lockDuration = (new Integer(lockDurationStr.substring(7))).intValue(); @@ -1089,7 +1107,7 @@ public class SecureWebdavServlet Node lockInfoNode = null; - DocumentBuilder documentBuilder = getDocumentBuilder(); + DocumentBuilder documentBuilder = getDocumentBuilder(req); try { Document document = documentBuilder.parse(new InputSource @@ -1098,7 +1116,9 @@ public class SecureWebdavServlet // Get the root element of the document Element rootElement = document.getDocumentElement(); lockInfoNode = rootElement; - } catch(Exception e) { + } catch (IOException e) { + lockRequestType = LOCK_REFRESH; + } catch (SAXException e) { lockRequestType = LOCK_REFRESH; } @@ -1218,27 +1238,16 @@ public class SecureWebdavServlet } } else { - lock.owner = new String(); + lock.owner = ""; } } - lock.path = path; - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - boolean exists = true; - Object object = null; - try { - object = resources.lookup(path); - } catch (NamingException e) { - exists = false; - } + lock.path = path; + WebResource resource = resources.getResource(path); - Enumeration locksList = null; + Enumeration<LockInfo> locksList = null; if (lockRequestType == LOCK_CREATION) { @@ -1248,20 +1257,18 @@ public class SecureWebdavServlet + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-" + lock.expiresAt + "-" + System.currentTimeMillis() + "-" + secret; - String lockToken = - md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes())); + String lockToken = Encoder.md5hash(lockTokenStr); - if ( (exists) && (object instanceof DirContext) && - (lock.depth == INFINITY) ) { + if (resource.isDirectory() && lock.depth == maxDepth) { // Locking a collection (and all its member resources) // Checking if a child resource of this collection is // already locked - Vector lockPaths = new Vector(); + Vector<String> lockPaths = new Vector<String>(); locksList = collectionLocks.elements(); while (locksList.hasMoreElements()) { - LockInfo currentLock = (LockInfo) locksList.nextElement(); + LockInfo currentLock = locksList.nextElement(); if (currentLock.hasExpired()) { resourceLocks.remove(currentLock.path); continue; @@ -1275,7 +1282,7 @@ public class SecureWebdavServlet } locksList = resourceLocks.elements(); while (locksList.hasMoreElements()) { - LockInfo currentLock = (LockInfo) locksList.nextElement(); + LockInfo currentLock = locksList.nextElement(); if (currentLock.hasExpired()) { resourceLocks.remove(currentLock.path); continue; @@ -1293,48 +1300,31 @@ public class SecureWebdavServlet // One of the child paths was locked // We generate a multistatus error report - Enumeration lockPathsList = lockPaths.elements(); + Enumeration<String> lockPathsList = lockPaths.elements(); resp.setStatus(WebdavStatus.SC_CONFLICT); XMLWriter generatedXML = new XMLWriter(); generatedXML.writeXMLHeader(); - - generatedXML.writeElement - (null, "multistatus" + generateNamespaceDeclarations(), - XMLWriter.OPENING); + generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING); while (lockPathsList.hasMoreElements()) { - generatedXML.writeElement(null, "response", - XMLWriter.OPENING); - generatedXML.writeElement(null, "href", - XMLWriter.OPENING); - generatedXML - .writeText((String) lockPathsList.nextElement()); - generatedXML.writeElement(null, "href", - XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", - XMLWriter.OPENING); - generatedXML - .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED - + " " + WebdavStatus - .getStatusText(WebdavStatus.SC_LOCKED)); - generatedXML.writeElement(null, "status", - XMLWriter.CLOSING); - - generatedXML.writeElement(null, "response", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "response", XMLWriter.OPENING); + generatedXML.writeElement("D", "href", XMLWriter.OPENING); + generatedXML.writeText(lockPathsList.nextElement()); + generatedXML.writeElement("D", "href", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); + generatedXML.writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED + " " + WebdavStatus.getStatusText(WebdavStatus.SC_LOCKED)); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "response", XMLWriter.CLOSING); } - generatedXML.writeElement(null, "multistatus", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); Writer writer = resp.getWriter(); writer.write(generatedXML.toString()); writer.close(); - return; - } boolean addLock = true; @@ -1343,7 +1333,7 @@ public class SecureWebdavServlet locksList = collectionLocks.elements(); while (locksList.hasMoreElements()) { - LockInfo currentLock = (LockInfo) locksList.nextElement(); + LockInfo currentLock = locksList.nextElement(); if (currentLock.path.equals(lock.path)) { if (currentLock.isExclusive()) { @@ -1374,7 +1364,7 @@ public class SecureWebdavServlet // Locking a single resource // Retrieving an already existing lock on that resource - LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path); + LockInfo presentLock = resourceLocks.get(lock.path); if (presentLock != null) { if ((presentLock.isExclusive()) || (lock.isExclusive())) { @@ -1393,29 +1383,26 @@ public class SecureWebdavServlet resourceLocks.put(lock.path, lock); // Checking if a resource exists at this path - exists = true; - try { - object = resources.lookup(path); - } catch (NamingException e) { - exists = false; - } - if (!exists) { + if (!resource.exists()) { // "Creating" a lock-null resource int slash = lock.path.lastIndexOf('/'); String parentPath = lock.path.substring(0, slash); - Vector lockNulls = - (Vector) lockNullResources.get(parentPath); + Vector<String> lockNulls = + lockNullResources.get(parentPath); if (lockNulls == null) { - lockNulls = new Vector(); + lockNulls = new Vector<String>(); lockNullResources.put(parentPath, lockNulls); } lockNulls.addElement(lock.path); } - + // Add the Lock-Token header as by RFC 2518 8.10.1 + // - only do this for newly created locks + resp.addHeader("Lock-Token", "<opaquelocktoken:" + + lockToken + ">"); } } @@ -1430,33 +1417,32 @@ public class SecureWebdavServlet // Checking resource locks - LockInfo toRenew = (LockInfo) resourceLocks.get(path); - Enumeration tokenList = null; - if (lock != null) { + LockInfo toRenew = resourceLocks.get(path); + Enumeration<String> tokenList = null; + if (toRenew != null) { // At least one of the tokens of the locks must have been given - tokenList = toRenew.tokens.elements(); while (tokenList.hasMoreElements()) { - String token = (String) tokenList.nextElement(); + String token = tokenList.nextElement(); if (ifHeader.indexOf(token) != -1) { toRenew.expiresAt = lock.expiresAt; lock = toRenew; } } - } // Checking inheritable collection locks - Enumeration collectionLocksList = collectionLocks.elements(); + Enumeration<LockInfo> collectionLocksList = + collectionLocks.elements(); while (collectionLocksList.hasMoreElements()) { - toRenew = (LockInfo) collectionLocksList.nextElement(); + toRenew = collectionLocksList.nextElement(); if (path.equals(toRenew.path)) { tokenList = toRenew.tokens.elements(); while (tokenList.hasMoreElements()) { - String token = (String) tokenList.nextElement(); + String token = tokenList.nextElement(); if (ifHeader.indexOf(token) != -1) { toRenew.expiresAt = lock.expiresAt; lock = toRenew; @@ -1472,69 +1458,51 @@ public class SecureWebdavServlet // the lock information XMLWriter generatedXML = new XMLWriter(); generatedXML.writeXMLHeader(); - generatedXML.writeElement(null, "prop" - + generateNamespaceDeclarations(), - XMLWriter.OPENING); - - generatedXML.writeElement(null, "lockdiscovery", - XMLWriter.OPENING); + generatedXML.writeElement("D", DEFAULT_NAMESPACE, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.OPENING); - lock.toXML(generatedXML, true); + lock.toXML(generatedXML); - generatedXML.writeElement(null, "lockdiscovery", - XMLWriter.CLOSING); - - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.CLOSING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); resp.setStatus(WebdavStatus.SC_OK); resp.setContentType("text/xml; charset=UTF-8"); Writer writer = resp.getWriter(); writer.write(generatedXML.toString()); writer.close(); - } /** * UNLOCK Method. */ - protected void doUnlock(HttpServletRequest req, HttpServletResponse resp) - throws IOException { + public void doUnlock(HttpServletRequest req, HttpServletResponse resp) + throws IOException { - if (readOnly) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - - // Check if operation allowed by OLAT VFS security callback - DirContext resources = getResources(req); - VFSDirContext vfsContext = (VFSDirContext) resources; - String path = getRelativePath(req); - if (!vfsContext.canWrite(path)) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return; - } - if (isLocked(req)) { resp.sendError(WebdavStatus.SC_LOCKED); return; } + String path = getRelativePath(req); + String lockTokenHeader = req.getHeader("Lock-Token"); - if (lockTokenHeader == null) - lockTokenHeader = ""; + if (lockTokenHeader == null) { + lockTokenHeader = ""; + } // Checking resource locks - LockInfo lock = (LockInfo) resourceLocks.get(path); - Enumeration tokenList = null; + LockInfo lock = resourceLocks.get(path); + Enumeration<String> tokenList = null; if (lock != null) { // At least one of the tokens of the locks must have been given tokenList = lock.tokens.elements(); while (tokenList.hasMoreElements()) { - String token = (String) tokenList.nextElement(); + String token = tokenList.nextElement(); if (lockTokenHeader.indexOf(token) != -1) { lock.tokens.removeElement(token); } @@ -1550,14 +1518,14 @@ public class SecureWebdavServlet // Checking inheritable collection locks - Enumeration collectionLocksList = collectionLocks.elements(); + Enumeration<LockInfo> collectionLocksList = collectionLocks.elements(); while (collectionLocksList.hasMoreElements()) { - lock = (LockInfo) collectionLocksList.nextElement(); + lock = collectionLocksList.nextElement(); if (path.equals(lock.path)) { tokenList = lock.tokens.elements(); while (tokenList.hasMoreElements()) { - String token = (String) tokenList.nextElement(); + String token = tokenList.nextElement(); if (lockTokenHeader.indexOf(token) != -1) { lock.tokens.removeElement(token); break; @@ -1577,23 +1545,8 @@ public class SecureWebdavServlet } - // -------------------------------------------------------- Private Methods - - protected String getETagValue(ResourceInfo resourceInfo) { - return resourceInfo.length + "-" + resourceInfo.date; - } - - - /** - * Generate the namespace declarations. - */ - private String generateNamespaceDeclarations() { - return " xmlns=\"" + DEFAULT_NAMESPACE + "\""; - } - - /** * Check to see if a resource is currently write locked. The method * will look at the "If" header to make sure the client @@ -1613,25 +1566,15 @@ public class SecureWebdavServlet ifHeader = ""; String lockTokenHeader = req.getHeader("Lock-Token"); - if (lockTokenHeader == null) - lockTokenHeader = ""; + if (lockTokenHeader == null) { + lockTokenHeader = ""; + } return isLocked(path, ifHeader + lockTokenHeader); - } /** - * Patched version of isLockedOriginal. - * @param path - * @param ifHeader - * @return - */ - private boolean isLockedDummy(String path, String ifHeader) { - return false; - } - - /** * Check to see if a resource is currently write locked. * * @param path Path of the resource @@ -1643,9 +1586,11 @@ public class SecureWebdavServlet private boolean isLocked(String path, String ifHeader) { // Checking resource locks + + System.out.println("isLocked: " + path + " :: " + ifHeader); - LockInfo lock = (LockInfo) resourceLocks.get(path); - Enumeration tokenList = null; + LockInfo lock = resourceLocks.get(path); + Enumeration<String> tokenList = null; if ((lock != null) && (lock.hasExpired())) { resourceLocks.remove(path); } else if (lock != null) { @@ -1655,9 +1600,11 @@ public class SecureWebdavServlet tokenList = lock.tokens.elements(); boolean tokenMatch = false; while (tokenList.hasMoreElements()) { - String token = (String) tokenList.nextElement(); - if (ifHeader.indexOf(token) != -1) + String token = tokenList.nextElement(); + if (ifHeader.indexOf(token) != -1) { tokenMatch = true; + break; + } } if (!tokenMatch) return true; @@ -1666,9 +1613,9 @@ public class SecureWebdavServlet // Checking inheritable collection locks - Enumeration collectionLocksList = collectionLocks.elements(); + Enumeration<LockInfo> collectionLocksList = collectionLocks.elements(); while (collectionLocksList.hasMoreElements()) { - lock = (LockInfo) collectionLocksList.nextElement(); + lock = collectionLocksList.nextElement(); if (lock.hasExpired()) { collectionLocks.removeElement(lock); } else if (path.startsWith(lock.path)) { @@ -1676,9 +1623,11 @@ public class SecureWebdavServlet tokenList = lock.tokens.elements(); boolean tokenMatch = false; while (tokenList.hasMoreElements()) { - String token = (String) tokenList.nextElement(); - if (ifHeader.indexOf(token) != -1) + String token = tokenList.nextElement(); + if (ifHeader.indexOf(token) != -1) { tokenMatch = true; + break; + } } if (!tokenMatch) return true; @@ -1698,58 +1647,16 @@ public class SecureWebdavServlet * @param resp Servlet response * @return boolean true if the copy is successful */ - private boolean copyResource(HttpServletRequest req, - HttpServletResponse resp) - throws IOException { + private boolean copyResource(HttpServletRequest req, HttpServletResponse resp, boolean move) + throws IOException { // Parsing destination header - - String destinationPath = req.getHeader("Destination"); - - if (destinationPath == null) { + String destinationPath = this.getDestinationPath(req); + if (destinationPath == null) { resp.sendError(WebdavStatus.SC_BAD_REQUEST); return false; } - int protocolIndex = destinationPath.indexOf("://"); - if (protocolIndex >= 0) { - // if the Destination URL contains the protocol, we can safely - // trim everything upto the first "/" character after "://" - int firstSeparator = - destinationPath.indexOf("/", protocolIndex + 4); - if (firstSeparator < 0) { - destinationPath = "/"; - } else { - destinationPath = destinationPath.substring(firstSeparator); - } - } else { - String hostName = req.getServerName(); - if ((hostName != null) && (destinationPath.startsWith(hostName))) { - destinationPath = destinationPath.substring(hostName.length()); - } - - int portIndex = destinationPath.indexOf(":"); - if (portIndex >= 0) { - destinationPath = destinationPath.substring(portIndex); - } - - if (destinationPath.startsWith(":")) { - int firstSeparator = destinationPath.indexOf("/"); - if (firstSeparator < 0) { - destinationPath = "/"; - } else { - destinationPath = - destinationPath.substring(firstSeparator); - } - } - } - - String contextPath = req.getContextPath(); - if ((contextPath != null) && - (destinationPath.startsWith(contextPath))) { - destinationPath = destinationPath.substring(contextPath.length()); - } - String pathInfo = req.getPathInfo(); if (pathInfo != null) { String servletPath = req.getServletPath(); @@ -1759,29 +1666,17 @@ public class SecureWebdavServlet .substring(servletPath.length()); } } - - // First decode URL - destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8"); - // Then normalize to NFC form for comparison - destinationPath = normalize(destinationPath); - if (debug > 0) - Tracing.logDebug("Dest path :" + destinationPath, SecureWebdavServlet.class); + if (log.isDebug()) log.debug("Dest path :" + destinationPath); - if ((destinationPath.toUpperCase().startsWith("/WEB-INF")) || - (destinationPath.toUpperCase().startsWith("/META-INF"))) { + // Check destination path to protect special subdirectories + if (isSpecialPath(destinationPath)) { resp.sendError(WebdavStatus.SC_FORBIDDEN); return false; } String path = getRelativePath(req); - if ((path.toUpperCase().startsWith("/WEB-INF")) || - (path.toUpperCase().startsWith("/META-INF"))) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return false; - } - if (destinationPath.equals(path)) { resp.sendError(WebdavStatus.SC_FORBIDDEN); return false; @@ -1801,65 +1696,46 @@ public class SecureWebdavServlet } // Overwriting the destination + WebResourceRoot resources = getResources(req); + WebResource destination = resources.getResource(destinationPath); - // Retrieve the resources - DirContext resources = getResources(req); - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return false; - } - - boolean exists = true; - Object object = null; - try { - object = resources.lookup(destinationPath); - } catch (NamingException e) { - exists = false; - } - - if (object instanceof VFSItem) { - if (((VFSItem)object).canDelete() != VFSConstants.YES) { - resp.setStatus(WebdavStatus.SC_METHOD_NOT_ALLOWED); - return false; - } - } - if (overwrite) { - // Delete destination resource, if it exists - if (exists) { - if (!deleteResource(destinationPath, req, resp)) { + if (destination.exists()) { + if (!deleteResource(destinationPath, req, resp, true)) { return false; - } else { - resp.setStatus(WebdavStatus.SC_NO_CONTENT); } } else { resp.setStatus(WebdavStatus.SC_CREATED); } - } else { - // If the destination exists, then it's a conflict - if (exists) { + if (destination.exists()) { resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED); return false; } - } // Copying source to destination - Hashtable errorList = new Hashtable(); + Hashtable<String,Integer> errorList = new Hashtable<String,Integer>(); - boolean result = copyResource(resources, errorList, - path, destinationPath); + boolean result = copyResource(req, errorList, path, destinationPath); if ((!result) || (!errorList.isEmpty())) { - - sendReport(req, resp, errorList); + if (errorList.size() == 1) { + resp.sendError(errorList.elements().nextElement().intValue()); + } else { + sendReport(req, resp, errorList); + } return false; + } + // Copy was successful + if (destination.exists()) { + resp.setStatus(WebdavStatus.SC_NO_CONTENT); + } else { + resp.setStatus(WebdavStatus.SC_CREATED); } // Removing any lock-null resource which would be present at @@ -1867,82 +1743,112 @@ public class SecureWebdavServlet lockNullResources.remove(destinationPath); return true; + } + + private String getDestinationPath(HttpServletRequest req) { + String destinationPath = req.getHeader("Destination"); + if (destinationPath == null) { + return null; + } + + // Remove url encoding from destination + destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8"); + + int protocolIndex = destinationPath.indexOf("://"); + if (protocolIndex >= 0) { + // if the Destination URL contains the protocol, we can safely + // trim everything upto the first "/" character after "://" + int firstSeparator = + destinationPath.indexOf("/", protocolIndex + 4); + if (firstSeparator < 0) { + destinationPath = "/"; + } else { + destinationPath = destinationPath.substring(firstSeparator); + } + } else { + String hostName = req.getServerName(); + if ((hostName != null) && (destinationPath.startsWith(hostName))) { + destinationPath = destinationPath.substring(hostName.length()); + } + + int portIndex = destinationPath.indexOf(":"); + if (portIndex >= 0) { + destinationPath = destinationPath.substring(portIndex); + } + + if (destinationPath.startsWith(":")) { + int firstSeparator = destinationPath.indexOf("/"); + if (firstSeparator < 0) { + destinationPath = "/"; + } else { + destinationPath = + destinationPath.substring(firstSeparator); + } + } + } + // Normalise destination path (remove '.' and '..') + destinationPath = RequestUtil.normalize(destinationPath); + + String contextPath = req.getContextPath(); + if ((contextPath != null) && + (destinationPath.startsWith(contextPath))) { + destinationPath = destinationPath.substring(contextPath.length()); + } + + return destinationPath; } /** * Copy a collection. * - * @param resources Resources implementation to be used * @param errorList Hashtable containing the list of errors which occurred * during the copy operation * @param source Path of the resource to be copied * @param dest Destination path */ - private boolean copyResource(DirContext resources, Hashtable errorList, - String source, String dest) { - - if (debug > 1) - System.out.println("Copy: " + source + " To: " + dest); - - Object object = null; - try { - object = resources.lookup(source); - } catch (NamingException e) { - } - - if (object instanceof DirContext) { + private boolean copyResource(HttpServletRequest req, Hashtable<String,Integer> errorList, + String source, String dest) { - try { - resources.createSubcontext(dest); - } catch (NamingException e) { - errorList.put - (dest, new Integer(WebdavStatus.SC_CONFLICT)); - return false; - } - try { - NamingEnumeration<NameClassPair> enumeration = resources.list(source); - while (enumeration.hasMoreElements()) { - NameClassPair ncPair = enumeration.nextElement(); - String childDest = dest; - if (!childDest.equals("/")) - childDest += "/"; - childDest += ncPair.getName(); - String childSrc = source; - if (!childSrc.equals("/")) - childSrc += "/"; - childSrc += ncPair.getName(); - copyResource(resources, errorList, childSrc, childDest); + if (log.isDebug()) log.debug("Copy: " + source + " To: " + dest); + + WebResourceRoot resources = getResources(req); + WebResource sourceResource = resources.getResource(source); + + if (sourceResource.isDirectory()) { + if (!resources.mkdir(dest)) { + WebResource destResource = resources.getResource(dest); + if (!destResource.isDirectory()) { + errorList.put(dest, new Integer(WebdavStatus.SC_CONFLICT)); + return false; } - } catch (NamingException e) { - errorList.put - (dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); - return false; } - } else { - - if (object instanceof Resource) { - try { - resources.bind(dest, object); - } catch (NamingException e) { - errorList.put - (source, - new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); - return false; + Collection<VFSItem> entries = resources.list(source); + for (VFSItem entry : entries) { + String childDest = dest; + if (!childDest.equals("/")) { + childDest += "/"; } - } else { - errorList.put - (source, - new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); + childDest += entry.getName(); + String childSrc = source; + if (!childSrc.equals("/")) { + childSrc += "/"; + } + childSrc += entry.getName(); + copyResource(req, errorList, childSrc, childDest); + } + } else if (sourceResource.isFile()) { + if (!resources.write(dest, sourceResource.getInputStream(), false)) { + errorList.put(source, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); return false; } - + } else { + errorList.put(source, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); + return false; } - return true; - } @@ -1955,11 +1861,12 @@ public class SecureWebdavServlet */ private boolean deleteResource(HttpServletRequest req, HttpServletResponse resp) - throws IOException { + throws IOException { String path = getRelativePath(req); - return deleteResource(path, req, resp); + return deleteResource(path, req, resp, true); + } @@ -1969,16 +1876,12 @@ public class SecureWebdavServlet * @param path Path of the resource which is to be deleted * @param req Servlet request * @param resp Servlet response + * @param setStatus Should the response status be set on successful + * completion */ private boolean deleteResource(String path, HttpServletRequest req, - HttpServletResponse resp) - throws IOException { - - if ((path.toUpperCase().startsWith("/WEB-INF")) || - (path.toUpperCase().startsWith("/META-INF"))) { - resp.sendError(WebdavStatus.SC_FORBIDDEN); - return false; - } + HttpServletResponse resp, boolean setStatus) + throws IOException { String ifHeader = req.getHeader("If"); if (ifHeader == null) @@ -1993,87 +1896,55 @@ public class SecureWebdavServlet return false; } - // Retrieve the resources - DirContext resources = getResources(req); - - if (resources == null) { - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return false; - } - - boolean exists = true; - Object object = null; - try { - object = resources.lookup(path); - } catch (NamingException e) { - exists = false; - } + WebResourceRoot resources = getResources(req); + WebResource resource = resources.getResource(path); - if (!exists) { + if (!resource.exists()) { resp.sendError(WebdavStatus.SC_NOT_FOUND); return false; } - // do not delete junctions attached to VirtualFileSystems ... - if (object instanceof VFSDirContext) { - if (((VFSDirContext)object).getVirtualDocBase().canDelete() != VFSConstants.YES) { - resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); - return false; - } - } - - boolean collection = (object instanceof DirContext); - - if (!collection) { - try { - resources.unbind(path); - } catch (NamingException e) { + if (!resource.isDirectory()) { + if (!resources.delete(resource)) { resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); return false; } } else { - Hashtable errorList = new Hashtable(); + Hashtable<String,Integer> errorList = new Hashtable<String,Integer>(); - deleteCollection(req, resources, path, errorList); - try { - resources.unbind(path); - } catch (NamingException e) { + deleteCollection(req, path, errorList); + if (!resources.delete(resource)) { errorList.put(path, new Integer (WebdavStatus.SC_INTERNAL_SERVER_ERROR)); } if (!errorList.isEmpty()) { - sendReport(req, resp, errorList); return false; - } - } - - resp.setStatus(WebdavStatus.SC_NO_CONTENT); + if (setStatus) { + resp.setStatus(WebdavStatus.SC_NO_CONTENT); + } return true; - } /** * Deletes a collection. * - * @param resources Resources implementation associated with the context * @param path Path to the collection to be deleted * @param errorList Contains the list of the errors which occurred */ private void deleteCollection(HttpServletRequest req, - DirContext resources, - String path, Hashtable errorList) { + String path, + Map<String,Integer> errorList) { - if (debug > 1) - System.out.println("Delete:" + path); + if (log.isDebug()) log.debug("Delete:" + path); - if ((path.toUpperCase().startsWith("/WEB-INF")) || - (path.toUpperCase().startsWith("/META-INF"))) { + // Prevent deletion of special subdirectories + if (isSpecialPath(path)) { errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN)); return; } @@ -2086,54 +1957,33 @@ public class SecureWebdavServlet if (lockTokenHeader == null) lockTokenHeader = ""; - Enumeration enumeration = null; - try { - enumeration = resources.list(path); - } catch (NamingException e) { - errorList.put(path, new Integer - (WebdavStatus.SC_INTERNAL_SERVER_ERROR)); - return; - } + WebResourceRoot resources = getResources(req); + Collection<VFSItem> entries = resources.list(path); - while (enumeration.hasMoreElements()) { - NameClassPair ncPair = (NameClassPair) enumeration.nextElement(); + for (VFSItem entry : entries) { String childName = path; if (!childName.equals("/")) childName += "/"; - childName += ncPair.getName(); + childName += entry.getName(); if (isLocked(childName, ifHeader + lockTokenHeader)) { errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED)); } else { + WebResource childResource = resources.getResource(childName); + if (childResource.isDirectory()) { + deleteCollection(req, childName, errorList); + } - try { - Object object = resources.lookup(childName); - if (object instanceof DirContext) { - deleteCollection(req, resources, childName, errorList); - } - - try { - resources.unbind(childName); - } catch (NamingException e) { - if (!(object instanceof DirContext)) { - // If it's not a collection, then it's an unknown - // error - errorList.put - (childName, new Integer - (WebdavStatus.SC_INTERNAL_SERVER_ERROR)); - } + if (!resources.delete(childResource)) { + if (!childResource.isDirectory()) { + // If it's not a collection, then it's an unknown error + errorList.put(childName, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); } - } catch (NamingException e) { - errorList.put - (childName, new Integer - (WebdavStatus.SC_INTERNAL_SERVER_ERROR)); } } - } - } @@ -2146,8 +1996,8 @@ public class SecureWebdavServlet * @param errorList List of error to be displayed */ private void sendReport(HttpServletRequest req, HttpServletResponse resp, - Hashtable errorList) - throws IOException { + Hashtable<String,Integer> errorList) + throws IOException { resp.setStatus(WebdavStatus.SC_MULTI_STATUS); @@ -2157,35 +2007,33 @@ public class SecureWebdavServlet XMLWriter generatedXML = new XMLWriter(); generatedXML.writeXMLHeader(); - generatedXML.writeElement(null, "multistatus" - + generateNamespaceDeclarations(), - XMLWriter.OPENING); + generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", + XMLWriter.OPENING); - Enumeration pathList = errorList.keys(); + Enumeration<String> pathList = errorList.keys(); while (pathList.hasMoreElements()) { - String errorPath = (String) pathList.nextElement(); - int errorCode = ((Integer) errorList.get(errorPath)).intValue(); + String errorPath = pathList.nextElement(); + int errorCode = errorList.get(errorPath).intValue(); - generatedXML.writeElement(null, "response", XMLWriter.OPENING); + generatedXML.writeElement("D", "response", XMLWriter.OPENING); - generatedXML.writeElement(null, "href", XMLWriter.OPENING); + generatedXML.writeElement("D", "href", XMLWriter.OPENING); String toAppend = errorPath.substring(relativePath.length()); if (!toAppend.startsWith("/")) toAppend = "/" + toAppend; generatedXML.writeText(absoluteUri + toAppend); - generatedXML.writeElement(null, "href", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); - generatedXML - .writeText("HTTP/1.1 " + errorCode + " " - + WebdavStatus.getStatusText(errorCode)); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "href", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); + generatedXML.writeText("HTTP/1.1 " + errorCode + " " + + WebdavStatus.getStatusText(errorCode)); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "response", XMLWriter.CLOSING); + generatedXML.writeElement("D", "response", XMLWriter.CLOSING); } - generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING); + generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); Writer writer = resp.getWriter(); writer.write(generatedXML.toString()); @@ -2197,6 +2045,7 @@ public class SecureWebdavServlet /** * Propfind helper method. * + * @param req The servlet request * @param resources Resources object associated with this context * @param generatedXML XML response to the Propfind request * @param path Path of the current resource @@ -2205,43 +2054,40 @@ public class SecureWebdavServlet * name, then this Vector contains those properties */ private void parseProperties(HttpServletRequest req, - DirContext resources, XMLWriter generatedXML, + XMLWriter generatedXML, String path, int type, - Vector propertiesVector) { + Vector<String> propertiesVector) { // Exclude any resource in the /WEB-INF and /META-INF subdirectories - // (the "toUpperCase()" avoids problems on Windows systems) - if (path.toUpperCase().startsWith("/WEB-INF") || - path.toUpperCase().startsWith("/META-INF")) + if (isSpecialPath(path)) return; - ResourceInfo resourceInfo = new ResourceInfo(path, resources); + WebResourceRoot resources = getResources(req); + WebResource resource = resources.getResource(path); + if (!resource.exists()) { + // File is in directory listing but doesn't appear to exist + // Broken symlink or odd permission settings? + return; + } - generatedXML.writeElement(null, "response", XMLWriter.OPENING); - String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " " - + WebdavStatus.getStatusText - (WebdavStatus.SC_OK)); + generatedXML.writeElement("D", "response", XMLWriter.OPENING); + String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " " + + WebdavStatus.getStatusText(WebdavStatus.SC_OK); // Generating href element - generatedXML.writeElement(null, "href", XMLWriter.OPENING); - - String href = req.getContextPath(); - String servletPath = req.getServletPath(); - // append servletPath - if ((href.endsWith("/")) && (servletPath.startsWith("/"))) - href += servletPath.substring(1); - else - href += servletPath; - // append resource path + generatedXML.writeElement("D", "href", XMLWriter.OPENING); + + String href = req.getContextPath() + req.getServletPath(); if ((href.endsWith("/")) && (path.startsWith("/"))) href += path.substring(1); else href += path; - if ((resourceInfo.collection) && (!href.endsWith("/"))) + if (resource.isDirectory() && (!href.endsWith("/"))) href += "/"; - generatedXML.writeText(rewriteUrl(href)); - generatedXML.writeElement(null, "href", XMLWriter.CLOSING); + generatedXML.writeText(rewriteUrl(href)); + + generatedXML.writeElement("D", "href", XMLWriter.CLOSING); String resourceName = path; int lastSlash = path.lastIndexOf('/'); @@ -2252,190 +2098,186 @@ public class SecureWebdavServlet case FIND_ALL_PROP : - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - generatedXML.writeProperty - (null, "creationdate", - getISOCreationDate(resourceInfo.creationDate)); - generatedXML.writeElement(null, "displayname", XMLWriter.OPENING); + //TODO jdk 1.7 generatedXML.writeProperty("D", "creationdate", + // getISOCreationDate(resource.getCreation())); + generatedXML.writeElement("D", "displayname", XMLWriter.OPENING); generatedXML.writeData(resourceName); - generatedXML.writeElement(null, "displayname", XMLWriter.CLOSING); - generatedXML.writeProperty(null, "getcontentlanguage", - Locale.getDefault().toString()); - if (!resourceInfo.collection) { + generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING); + if (resource.isFile()) { generatedXML.writeProperty - (null, "getlastmodified", resourceInfo.httpDate); + ("D", "getlastmodified", FastHttpDateFormat.formatDate + (resource.getLastModified(), null)); generatedXML.writeProperty - (null, "getcontentlength", - String.valueOf(resourceInfo.length)); - generatedXML.writeProperty - (null, "getcontenttype", - getServletContext().getMimeType(resourceInfo.path)); - generatedXML.writeProperty(null, "getetag", - getETagValue(resourceInfo)); - generatedXML.writeElement(null, "resourcetype", - XMLWriter.NO_CONTENT); + ("D", "getcontentlength", + String.valueOf(resource.getContentLength())); + String contentType = req.getServletContext().getMimeType( + resource.getName()); + if (contentType != null) { + generatedXML.writeProperty("D", "getcontenttype", + contentType); + } + generatedXML.writeProperty("D", "getetag",resource.getETag()); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.NO_CONTENT); } else { - generatedXML.writeElement(null, "resourcetype", - XMLWriter.OPENING); - generatedXML.writeElement(null, "collection", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "resourcetype", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.OPENING); + generatedXML.writeElement("D", "collection", + XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.CLOSING); } - generatedXML.writeProperty(null, "source", ""); - - String supportedLocks = "<lockentry>" - + "<lockscope><exclusive/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>" + "<lockentry>" - + "<lockscope><shared/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>"; - generatedXML.writeElement(null, "supportedlock", - XMLWriter.OPENING); + generatedXML.writeProperty("D", "source", ""); + + String supportedLocks = "<D:lockentry>" + + "<D:lockscope><D:exclusive/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>" + "<D:lockentry>" + + "<D:lockscope><D:shared/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>"; + generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING); generatedXML.writeText(supportedLocks); - generatedXML.writeElement(null, "supportedlock", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING); generateLockDiscovery(path, generatedXML); - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); break; case FIND_PROPERTY_NAMES : - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - generatedXML.writeElement(null, "creationdate", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "displayname", + generatedXML.writeElement("D", "creationdate", XMLWriter.NO_CONTENT); - if (!resourceInfo.collection) { - generatedXML.writeElement(null, "getcontentlanguage", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getcontentlength", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getcontenttype", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getetag", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getlastmodified", - XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "displayname", XMLWriter.NO_CONTENT); + if (resource.isFile()) { + generatedXML.writeElement("D", "getcontentlanguage", + XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getcontentlength", + XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getcontenttype", + XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getlastmodified", + XMLWriter.NO_CONTENT); } - generatedXML.writeElement(null, "resourcetype", + generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "lockdiscovery", + generatedXML.writeElement("D", "source", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); break; case FIND_BY_PROPERTY : - Vector propertiesNotFound = new Vector(); + Vector<String> propertiesNotFound = new Vector<String>(); // Parse the list of properties - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - Enumeration properties = propertiesVector.elements(); + Enumeration<String> properties = propertiesVector.elements(); while (properties.hasMoreElements()) { - String property = (String) properties.nextElement(); + String property = properties.nextElement(); if (property.equals("creationdate")) { - generatedXML.writeProperty - (null, "creationdate", - getISOCreationDate(resourceInfo.creationDate)); + //TODO jdk 1.7generatedXML.writeProperty + // ("D", "creationdate", + // getISOCreationDate(resource.getCreation())); } else if (property.equals("displayname")) { generatedXML.writeElement - (null, "displayname", XMLWriter.OPENING); + ("D", "displayname", XMLWriter.OPENING); generatedXML.writeData(resourceName); generatedXML.writeElement - (null, "displayname", XMLWriter.CLOSING); + ("D", "displayname", XMLWriter.CLOSING); } else if (property.equals("getcontentlanguage")) { - if (resourceInfo.collection) { + if (resource.isDirectory()) { propertiesNotFound.addElement(property); } else { - generatedXML.writeProperty - (null, "getcontentlanguage", - Locale.getDefault().toString()); + generatedXML.writeElement("D", "getcontentlanguage", + XMLWriter.NO_CONTENT); } } else if (property.equals("getcontentlength")) { - if (resourceInfo.collection) { + if (resource.isDirectory()) { propertiesNotFound.addElement(property); } else { generatedXML.writeProperty - (null, "getcontentlength", - (String.valueOf(resourceInfo.length))); + ("D", "getcontentlength", + (String.valueOf(resource.getContentLength()))); } } else if (property.equals("getcontenttype")) { - if (resourceInfo.collection) { + if (resource.isDirectory()) { propertiesNotFound.addElement(property); } else { generatedXML.writeProperty - (null, "getcontenttype", - getServletContext().getMimeType - (resourceInfo.path)); + ("D", "getcontenttype", + req.getServletContext().getMimeType + (resource.getName())); } } else if (property.equals("getetag")) { - if (resourceInfo.collection) { + if (resource.isDirectory()) { propertiesNotFound.addElement(property); } else { generatedXML.writeProperty - (null, "getetag", getETagValue(resourceInfo)); + ("D", "getetag", resource.getETag()); } } else if (property.equals("getlastmodified")) { - if (resourceInfo.collection) { + if (resource.isDirectory()) { propertiesNotFound.addElement(property); } else { generatedXML.writeProperty - (null, "getlastmodified", resourceInfo.httpDate); + ("D", "getlastmodified", FastHttpDateFormat.formatDate + (resource.getLastModified(), null)); } } else if (property.equals("resourcetype")) { - if (resourceInfo.collection) { - generatedXML.writeElement(null, "resourcetype", - XMLWriter.OPENING); - generatedXML.writeElement(null, "collection", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "resourcetype", - XMLWriter.CLOSING); + if (resource.isDirectory()) { + generatedXML.writeElement("D", "resourcetype", + XMLWriter.OPENING); + generatedXML.writeElement("D", "collection", + XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.CLOSING); } else { - generatedXML.writeElement(null, "resourcetype", - XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.NO_CONTENT); } } else if (property.equals("source")) { - generatedXML.writeProperty(null, "source", ""); + generatedXML.writeProperty("D", "source", ""); } else if (property.equals("supportedlock")) { - supportedLocks = "<lockentry>" - + "<lockscope><exclusive/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>" + "<lockentry>" - + "<lockscope><shared/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>"; - generatedXML.writeElement(null, "supportedlock", - XMLWriter.OPENING); + supportedLocks = "<D:lockentry>" + + "<D:lockscope><D:exclusive/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>" + "<D:lockentry>" + + "<D:lockscope><D:shared/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>"; + generatedXML.writeElement("D", "supportedlock", + XMLWriter.OPENING); generatedXML.writeText(supportedLocks); - generatedXML.writeElement(null, "supportedlock", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "supportedlock", + XMLWriter.CLOSING); } else if (property.equals("lockdiscovery")) { if (!generateLockDiscovery(path, generatedXML)) propertiesNotFound.addElement(property); @@ -2445,34 +2287,34 @@ public class SecureWebdavServlet } - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); - Enumeration propertiesNotFoundList = propertiesNotFound.elements(); + Enumeration<String> propertiesNotFoundList = + propertiesNotFound.elements(); if (propertiesNotFoundList.hasMoreElements()) { - status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND - + " " + WebdavStatus.getStatusText - (WebdavStatus.SC_NOT_FOUND)); + status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " " + + WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND); - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); while (propertiesNotFoundList.hasMoreElements()) { generatedXML.writeElement - (null, (String) propertiesNotFoundList.nextElement(), + ("D", propertiesNotFoundList.nextElement(), XMLWriter.NO_CONTENT); } - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); } @@ -2480,13 +2322,13 @@ public class SecureWebdavServlet } - generatedXML.writeElement(null, "response", XMLWriter.CLOSING); + generatedXML.writeElement("D", "response", XMLWriter.CLOSING); } /** - * Propfind helper method. Dispays the properties of a lock-null resource. + * Propfind helper method. Displays the properties of a lock-null resource. * * @param resources Resources object associated with this context * @param generatedXML XML response to the Propfind request @@ -2498,27 +2340,24 @@ public class SecureWebdavServlet private void parseLockNullProperties(HttpServletRequest req, XMLWriter generatedXML, String path, int type, - Vector propertiesVector) { + Vector<String> propertiesVector) { // Exclude any resource in the /WEB-INF and /META-INF subdirectories - // (the "toUpperCase()" avoids problems on Windows systems) - if (path.toUpperCase().startsWith("/WEB-INF") || - path.toUpperCase().startsWith("/META-INF")) + if (isSpecialPath(path)) return; // Retrieving the lock associated with the lock-null resource - LockInfo lock = (LockInfo) resourceLocks.get(path); + LockInfo lock = resourceLocks.get(path); if (lock == null) return; - generatedXML.writeElement(null, "response", XMLWriter.OPENING); - String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " " - + WebdavStatus.getStatusText - (WebdavStatus.SC_OK)); + generatedXML.writeElement("D", "response", XMLWriter.OPENING); + String status = "HTTP/1.1 " + WebdavStatus.SC_OK + " " + + WebdavStatus.getStatusText(WebdavStatus.SC_OK); // Generating href element - generatedXML.writeElement(null, "href", XMLWriter.OPENING); + generatedXML.writeElement("D", "href", XMLWriter.OPENING); String absoluteUri = req.getRequestURI(); String relativePath = getRelativePath(req); @@ -2526,9 +2365,9 @@ public class SecureWebdavServlet if (!toAppend.startsWith("/")) toAppend = "/" + toAppend; - generatedXML.writeText(rewriteUrl(normalize(absoluteUri + toAppend))); + generatedXML.writeText(rewriteUrl(RequestUtil.normalize(absoluteUri + toAppend))); - generatedXML.writeElement(null, "href", XMLWriter.CLOSING); + generatedXML.writeElement("D", "href", XMLWriter.CLOSING); String resourceName = path; int lastSlash = path.lastIndexOf('/'); @@ -2539,153 +2378,140 @@ public class SecureWebdavServlet case FIND_ALL_PROP : - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - generatedXML.writeProperty - (null, "creationdate", - getISOCreationDate(lock.creationDate.getTime())); - generatedXML.writeElement - (null, "displayname", XMLWriter.OPENING); + generatedXML.writeProperty("D", "creationdate", + getISOCreationDate(lock.creationDate.getTime())); + generatedXML.writeElement("D", "displayname", XMLWriter.OPENING); generatedXML.writeData(resourceName); - generatedXML.writeElement - (null, "displayname", XMLWriter.CLOSING); - generatedXML.writeProperty(null, "getcontentlanguage", - Locale.getDefault().toString()); - generatedXML.writeProperty(null, "getlastmodified", - formats[0].format(lock.creationDate)); - generatedXML.writeProperty - (null, "getcontentlength", String.valueOf(0)); - generatedXML.writeProperty(null, "getcontenttype", ""); - generatedXML.writeProperty(null, "getetag", ""); - generatedXML.writeElement(null, "resourcetype", - XMLWriter.OPENING); - generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "resourcetype", - XMLWriter.CLOSING); - - generatedXML.writeProperty(null, "source", ""); - - String supportedLocks = "<lockentry>" - + "<lockscope><exclusive/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>" + "<lockentry>" - + "<lockscope><shared/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>"; - generatedXML.writeElement(null, "supportedlock", - XMLWriter.OPENING); + generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING); + generatedXML.writeProperty("D", "getlastmodified", + FastHttpDateFormat.formatDate + (lock.creationDate.getTime(), null)); + generatedXML.writeProperty("D", "getcontentlength", + String.valueOf(0)); + generatedXML.writeProperty("D", "getcontenttype", ""); + generatedXML.writeProperty("D", "getetag", ""); + generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); + generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING); + + generatedXML.writeProperty("D", "source", ""); + + String supportedLocks = "<D:lockentry>" + + "<D:lockscope><D:exclusive/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>" + "<D:lockentry>" + + "<D:lockscope><D:shared/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>"; + generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING); generatedXML.writeText(supportedLocks); - generatedXML.writeElement(null, "supportedlock", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING); generateLockDiscovery(path, generatedXML); - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); break; case FIND_PROPERTY_NAMES : - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - generatedXML.writeElement(null, "creationdate", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "displayname", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getcontentlanguage", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getcontentlength", + generatedXML.writeElement("D", "creationdate", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getcontenttype", + generatedXML.writeElement("D", "displayname", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getcontentlanguage", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getetag", + generatedXML.writeElement("D", "getcontentlength", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "getlastmodified", + generatedXML.writeElement("D", "getcontenttype", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "resourcetype", + generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getlastmodified", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "source", + generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "lockdiscovery", + generatedXML.writeElement("D", "source", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); break; case FIND_BY_PROPERTY : - Vector propertiesNotFound = new Vector(); + Vector<String> propertiesNotFound = new Vector<String>(); // Parse the list of properties - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - Enumeration properties = propertiesVector.elements(); + Enumeration<String> properties = propertiesVector.elements(); while (properties.hasMoreElements()) { - String property = (String) properties.nextElement(); + String property = properties.nextElement(); if (property.equals("creationdate")) { - generatedXML.writeProperty - (null, "creationdate", - getISOCreationDate(lock.creationDate.getTime())); + generatedXML.writeProperty("D", "creationdate", + getISOCreationDate(lock.creationDate.getTime())); } else if (property.equals("displayname")) { - generatedXML.writeElement - (null, "displayname", XMLWriter.OPENING); + generatedXML.writeElement("D", "displayname", + XMLWriter.OPENING); generatedXML.writeData(resourceName); - generatedXML.writeElement - (null, "displayname", XMLWriter.CLOSING); + generatedXML.writeElement("D", "displayname", + XMLWriter.CLOSING); } else if (property.equals("getcontentlanguage")) { - generatedXML.writeProperty - (null, "getcontentlanguage", - Locale.getDefault().toString()); + generatedXML.writeElement("D", "getcontentlanguage", + XMLWriter.NO_CONTENT); } else if (property.equals("getcontentlength")) { - generatedXML.writeProperty - (null, "getcontentlength", (String.valueOf(0))); + generatedXML.writeProperty("D", "getcontentlength", + (String.valueOf(0))); } else if (property.equals("getcontenttype")) { - generatedXML.writeProperty - (null, "getcontenttype", ""); + generatedXML.writeProperty("D", "getcontenttype", ""); } else if (property.equals("getetag")) { - generatedXML.writeProperty(null, "getetag", ""); + generatedXML.writeProperty("D", "getetag", ""); } else if (property.equals("getlastmodified")) { generatedXML.writeProperty - (null, "getlastmodified", - formats[0].format(lock.creationDate)); + ("D", "getlastmodified", + FastHttpDateFormat.formatDate + (lock.creationDate.getTime(), null)); } else if (property.equals("resourcetype")) { - generatedXML.writeElement(null, "resourcetype", - XMLWriter.OPENING); - generatedXML.writeElement(null, "lock-null", - XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "resourcetype", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.OPENING); + generatedXML.writeElement("D", "lock-null", + XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", + XMLWriter.CLOSING); } else if (property.equals("source")) { - generatedXML.writeProperty(null, "source", ""); + generatedXML.writeProperty("D", "source", ""); } else if (property.equals("supportedlock")) { - supportedLocks = "<lockentry>" - + "<lockscope><exclusive/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>" + "<lockentry>" - + "<lockscope><shared/></lockscope>" - + "<locktype><write/></locktype>" - + "</lockentry>"; - generatedXML.writeElement(null, "supportedlock", - XMLWriter.OPENING); + supportedLocks = "<D:lockentry>" + + "<D:lockscope><D:exclusive/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>" + "<D:lockentry>" + + "<D:lockscope><D:shared/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>"; + generatedXML.writeElement("D", "supportedlock", + XMLWriter.OPENING); generatedXML.writeText(supportedLocks); - generatedXML.writeElement(null, "supportedlock", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "supportedlock", + XMLWriter.CLOSING); } else if (property.equals("lockdiscovery")) { if (!generateLockDiscovery(path, generatedXML)) propertiesNotFound.addElement(property); @@ -2695,34 +2521,33 @@ public class SecureWebdavServlet } - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); - Enumeration propertiesNotFoundList = propertiesNotFound.elements(); + Enumeration<String> propertiesNotFoundList = propertiesNotFound.elements(); if (propertiesNotFoundList.hasMoreElements()) { - status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND - + " " + WebdavStatus.getStatusText - (WebdavStatus.SC_NOT_FOUND)); + status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " " + + WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND); - generatedXML.writeElement(null, "propstat", XMLWriter.OPENING); - generatedXML.writeElement(null, "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); while (propertiesNotFoundList.hasMoreElements()) { generatedXML.writeElement - (null, (String) propertiesNotFoundList.nextElement(), + ("D", propertiesNotFoundList.nextElement(), XMLWriter.NO_CONTENT); } - generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); - generatedXML.writeElement(null, "status", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); generatedXML.writeText(status); - generatedXML.writeElement(null, "status", XMLWriter.CLOSING); - generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); } @@ -2730,7 +2555,7 @@ public class SecureWebdavServlet } - generatedXML.writeElement(null, "response", XMLWriter.CLOSING); + generatedXML.writeElement("D", "response", XMLWriter.CLOSING); } @@ -2745,34 +2570,31 @@ public class SecureWebdavServlet private boolean generateLockDiscovery (String path, XMLWriter generatedXML) { - LockInfo resourceLock = (LockInfo) resourceLocks.get(path); - Enumeration collectionLocksList = collectionLocks.elements(); + LockInfo resourceLock = resourceLocks.get(path); + Enumeration<LockInfo> collectionLocksList = collectionLocks.elements(); boolean wroteStart = false; if (resourceLock != null) { wroteStart = true; - generatedXML.writeElement(null, "lockdiscovery", - XMLWriter.OPENING); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.OPENING); resourceLock.toXML(generatedXML); } while (collectionLocksList.hasMoreElements()) { - LockInfo currentLock = - (LockInfo) collectionLocksList.nextElement(); + LockInfo currentLock = collectionLocksList.nextElement(); if (path.startsWith(currentLock.path)) { if (!wroteStart) { wroteStart = true; - generatedXML.writeElement(null, "lockdiscovery", - XMLWriter.OPENING); + generatedXML.writeElement("D", "lockdiscovery", + XMLWriter.OPENING); } currentLock.toXML(generatedXML); } } if (wroteStart) { - generatedXML.writeElement(null, "lockdiscovery", - XMLWriter.CLOSING); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.CLOSING); } else { return false; } @@ -2786,32 +2608,37 @@ public class SecureWebdavServlet * Get creation date in ISO format. */ private String getISOCreationDate(long creationDate) { - StringBuilder creationDateValue = new StringBuilder - (creationDateFormat.format - (new Date(creationDate))); - /* - int offset = Calendar.getInstance().getTimeZone().getRawOffset() - / 3600000; // - if (offset < 0) { - creationDateValue.append("-"); - offset = -offset; - } else if (offset > 0) { - creationDateValue.append("+"); - } - if (offset != 0) { - if (offset < 10) - creationDateValue.append("0"); - creationDateValue.append(offset + ":00"); - } else { - creationDateValue.append("Z"); - } - */ - return creationDateValue.toString(); + return creationDateFormat.format(new Date(creationDate)); } + /** + * Determines the methods normally allowed for the resource. + * + */ + private StringBuilder determineMethodsAllowed(HttpServletRequest req) { + + StringBuilder methodsAllowed = new StringBuilder(); - // -------------------------------------------------- LockInfo Inner Class + WebResourceRoot resources = getResources(req); + WebResource resource = resources.getResource(getRelativePath(req)); + + if (!resource.exists()) { + methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK"); + return methodsAllowed; + } + + methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE"); + methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK"); + methodsAllowed.append(", PROPFIND"); + + if (resource.isFile()) { + methodsAllowed.append(", PUT"); + } + return methodsAllowed; + } + + // -------------------------------------------------- LockInfo Inner Class /** * Holds a lock information. @@ -2824,11 +2651,9 @@ public class SecureWebdavServlet /** * Constructor. - * - * @param pathname Path name of the file */ public LockInfo() { - + // Ignore } @@ -2840,7 +2665,7 @@ public class SecureWebdavServlet String scope = "exclusive"; int depth = 0; String owner = ""; - Vector tokens = new Vector(); + Vector<String> tokens = new Vector<String>(); long expiresAt = 0; Date creationDate = new Date(); @@ -2851,20 +2676,26 @@ public class SecureWebdavServlet /** * Get a String representation of this lock token. */ + @Override public String toString() { - String result = "Type:" + type + "\n"; - result += "Scope:" + scope + "\n"; - result += "Depth:" + depth + "\n"; - result += "Owner:" + owner + "\n"; - result += "Expiration:" + - formats[0].format(new Date(expiresAt)) + "\n"; - Enumeration tokensList = tokens.elements(); + StringBuilder result = new StringBuilder("Type:"); + result.append(type); + result.append("\nScope:"); + result.append(scope); + result.append("\nDepth:"); + result.append(depth); + result.append("\nOwner:"); + result.append(owner); + result.append("\nExpiration:"); + result.append(FastHttpDateFormat.formatDate(expiresAt, null)); + Enumeration<String> tokensList = tokens.elements(); while (tokensList.hasMoreElements()) { - result += "Token:" + tokensList.nextElement() + "\n"; + result.append("\nToken:"); + result.append(tokensList.nextElement()); } - return result; - + result.append("\n"); + return result.toString(); } @@ -2891,60 +2722,45 @@ public class SecureWebdavServlet * append an XML fragment to the given XML writer. */ public void toXML(XMLWriter generatedXML) { - toXML(generatedXML, true); - } - - - /** - * Get an XML representation of this lock token. This method will - * append an XML fragment to the given XML writer. - */ - public void toXML(XMLWriter generatedXML, boolean showToken) { - generatedXML.writeElement(null, "activelock", XMLWriter.OPENING); + generatedXML.writeElement("D", "activelock", XMLWriter.OPENING); - generatedXML.writeElement(null, "locktype", XMLWriter.OPENING); - generatedXML.writeElement(null, type, XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "locktype", XMLWriter.CLOSING); + generatedXML.writeElement("D", "locktype", XMLWriter.OPENING); + generatedXML.writeElement("D", type, XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "locktype", XMLWriter.CLOSING); - generatedXML.writeElement(null, "lockscope", XMLWriter.OPENING); - generatedXML.writeElement(null, scope, XMLWriter.NO_CONTENT); - generatedXML.writeElement(null, "lockscope", XMLWriter.CLOSING); + generatedXML.writeElement("D", "lockscope", XMLWriter.OPENING); + generatedXML.writeElement("D", scope, XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "lockscope", XMLWriter.CLOSING); - generatedXML.writeElement(null, "depth", XMLWriter.OPENING); - if (depth == INFINITY) { + generatedXML.writeElement("D", "depth", XMLWriter.OPENING); + if (depth == maxDepth) { generatedXML.writeText("Infinity"); } else { generatedXML.writeText("0"); } - generatedXML.writeElement(null, "depth", XMLWriter.CLOSING); + generatedXML.writeElement("D", "depth", XMLWriter.CLOSING); - generatedXML.writeElement(null, "owner", XMLWriter.OPENING); + generatedXML.writeElement("D", "owner", XMLWriter.OPENING); generatedXML.writeText(owner); - generatedXML.writeElement(null, "owner", XMLWriter.CLOSING); + generatedXML.writeElement("D", "owner", XMLWriter.CLOSING); - generatedXML.writeElement(null, "timeout", XMLWriter.OPENING); + generatedXML.writeElement("D", "timeout", XMLWriter.OPENING); long timeout = (expiresAt - System.currentTimeMillis()) / 1000; generatedXML.writeText("Second-" + timeout); - generatedXML.writeElement(null, "timeout", XMLWriter.CLOSING); - - generatedXML.writeElement(null, "locktoken", XMLWriter.OPENING); - if (showToken) { - Enumeration tokensList = tokens.elements(); - while (tokensList.hasMoreElements()) { - generatedXML.writeElement(null, "href", XMLWriter.OPENING); - generatedXML.writeText("opaquelocktoken:" - + tokensList.nextElement()); - generatedXML.writeElement(null, "href", XMLWriter.CLOSING); - } - } else { - generatedXML.writeElement(null, "href", XMLWriter.OPENING); - generatedXML.writeText("opaquelocktoken:dummytoken"); - generatedXML.writeElement(null, "href", XMLWriter.CLOSING); + generatedXML.writeElement("D", "timeout", XMLWriter.CLOSING); + + generatedXML.writeElement("D", "locktoken", XMLWriter.OPENING); + Enumeration<String> tokensList = tokens.elements(); + while (tokensList.hasMoreElements()) { + generatedXML.writeElement("D", "href", XMLWriter.OPENING); + generatedXML.writeText("opaquelocktoken:" + + tokensList.nextElement()); + generatedXML.writeElement("D", "href", XMLWriter.CLOSING); } - generatedXML.writeElement(null, "locktoken", XMLWriter.CLOSING); + generatedXML.writeElement("D", "locktoken", XMLWriter.CLOSING); - generatedXML.writeElement(null, "activelock", XMLWriter.CLOSING); + generatedXML.writeElement("D", "activelock", XMLWriter.CLOSING); } @@ -2952,20 +2768,28 @@ public class SecureWebdavServlet } - // --------------------------------------------------- Property Inner Class - - - /*private class Property { - - public String name; - public String value; - public String namespace; - public String namespaceAbbrev; - public int status = WebdavStatus.SC_OK; - - }*/ + // --------------------------------------------- WebdavResolver Inner Class + /** + * Work around for XML parsers that don't fully respect + * {@link DocumentBuilderFactory#setExpandEntityReferences(boolean)} when + * called with <code>false</code>. External references are filtered out for + * security reasons. See CVE-2007-5461. + */ + private static class WebdavResolver implements EntityResolver { + private ServletContext context; + public WebdavResolver(ServletContext theContext) { + context = theContext; + } + @Override + public InputSource resolveEntity (String publicId, String systemId) { + context.log("webdavservlet.enternalEntityIgnored" + + publicId + " : " + systemId); + return new InputSource( + new StringReader("Ignored external entity")); + } + } } @@ -2992,7 +2816,8 @@ class WebdavStatus { * status codes to descriptive text. This is a static * variable. */ - private static Hashtable mapStatusCodes = new Hashtable(); + private static final Hashtable<Integer,String> mapStatusCodes = + new Hashtable<Integer,String>(); // ------------------------------------------------------ HTTP Status Codes @@ -3171,8 +2996,8 @@ class WebdavStatus { * providing status for multiple independent operations. */ public static final int SC_MULTI_STATUS = 207; - // This one colides with HTTP 1.1 - // "207 Parital Update OK" + // This one collides with HTTP 1.1 + // "207 Partial Update OK" /** @@ -3180,7 +3005,7 @@ class WebdavStatus { * the PATCH method was not understood by the resource. */ public static final int SC_UNPROCESSABLE_ENTITY = 418; - // This one colides with HTTP 1.1 + // This one collides with HTTP 1.1 // "418 Reauthentication Required" @@ -3190,7 +3015,7 @@ class WebdavStatus { * execution of this method. */ public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419; - // This one colides with HTTP 1.1 + // This one collides with HTTP 1.1 // "419 Proxy Reauthentication Required" @@ -3216,7 +3041,7 @@ class WebdavStatus { static { - // HTTP 1.0 tatus Code + // HTTP 1.0 status Code addStatusCodeMap(SC_OK, "OK"); addStatusCodeMap(SC_CREATED, "Created"); addStatusCodeMap(SC_ACCEPTED, "Accepted"); @@ -3261,12 +3086,12 @@ class WebdavStatus { * HTTP status code (e.g., "OK"). */ public static String getStatusText(int nHttpStatusCode) { - Integer intKey = new Integer(nHttpStatusCode); + Integer intKey = Integer.valueOf(nHttpStatusCode); if (!mapStatusCodes.containsKey(intKey)) { return ""; } else { - return (String) mapStatusCodes.get(intKey); + return mapStatusCodes.get(intKey); } } @@ -3282,8 +3107,9 @@ class WebdavStatus { * @param strVal [IN] HTTP status text */ private static void addStatusCodeMap(int nKey, String strVal) { - mapStatusCodes.put(new Integer(nKey), strVal); + mapStatusCodes.put(Integer.valueOf(nKey), strVal); } - } + + diff --git a/src/main/java/org/olat/core/util/servlets/XMLWriter.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/XMLWriter.java similarity index 70% rename from src/main/java/org/olat/core/util/servlets/XMLWriter.java rename to src/main/java/org/olat/core/commons/services/webdav/servlets/XMLWriter.java index 9d36af5ff0d..89a696e3951 100644 --- a/src/main/java/org/olat/core/util/servlets/XMLWriter.java +++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/XMLWriter.java @@ -1,29 +1,21 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.core.util.servlets; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.olat.core.commons.services.webdav.servlets; import java.io.IOException; import java.io.Writer; @@ -69,7 +61,7 @@ public class XMLWriter { /** * Writer. */ - protected Writer writer = null; + protected final Writer writer; // ----------------------------------------------------------- Constructors @@ -79,6 +71,7 @@ public class XMLWriter { * Constructor. */ public XMLWriter() { + this(null); } @@ -98,28 +91,12 @@ public class XMLWriter { * * @return String containing the generated XML */ + @Override public String toString() { return buffer.toString(); } - /** - * Write property to the XML. - * - * @param namespace Namespace - * @param namespaceInfo Namespace info - * @param name Property name - * @param value Property value - */ - public void writeProperty(String namespace, String namespaceInfo, - String name, String value) { - writeElement(namespace, namespaceInfo, name, OPENING); - buffer.append(value); - writeElement(namespace, namespaceInfo, name, CLOSING); - - } - - /** * Write property to the XML. * @@ -134,17 +111,6 @@ public class XMLWriter { } - /** - * Write property to the XML. - * - * @param namespace Namespace - * @param name Property name - */ - public void writeProperty(String namespace, String name) { - writeElement(namespace, name, NO_CONTENT); - } - - /** * Write an element. * @@ -243,6 +209,7 @@ public class XMLWriter { public void sendData() throws IOException { if (writer != null) { + //System.out.println(buffer.toString()); writer.write(buffer.toString()); buffer = new StringBuilder(); } diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java b/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java new file mode 100644 index 00000000000..4c12f268a55 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java @@ -0,0 +1,86 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.commons.services.webdav.ui; + +import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.services.webdav.WebDAVModule; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class WebDAVAdminController extends FormBasicController { + + private MultipleSelectionElement enableEl, enableDigestEl; + private final WebDAVModule webDAVModule; + + public WebDAVAdminController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl); + webDAVModule = CoreSpringFactory.getImpl(WebDAVModule.class); + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + setFormTitle("admin.menu.title.alt"); + setFormDescription("admin.webdav.description"); + setFormContextHelp(WebDAVAdminController.class.getPackage().getName(), "webdavconfig.html", "help.hover.webdavconfig"); + + enableEl = uifactory.addCheckboxesHorizontal("webdavLink", "webdav.link", formLayout, new String[]{"xx"}, new String[]{""}, null); + enableEl.select("xx", webDAVModule.isEnabled()); + enableEl.addActionListener(this, FormEvent.ONCHANGE); + + enableDigestEl = uifactory.addCheckboxesHorizontal("webdavDigest", "webdav.digest", formLayout, new String[]{"xx"}, new String[]{""}, null); + enableDigestEl.select("xx", webDAVModule.isDigestAuthenticationEnabled()); + enableDigestEl.addActionListener(this, FormEvent.ONCHANGE); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(source == enableEl) { + boolean enabled = enableEl.isAtLeastSelected(1); + webDAVModule.setEnabled(enabled); + } else if(source == enableDigestEl) { + boolean enabled = enableDigestEl.isAtLeastSelected(1); + webDAVModule.setDigestAuthenticationEnabled(enabled); + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void formOK(UserRequest ureq) { + // + } +} diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_chelp/webdavconfig.html b/src/main/java/org/olat/core/commons/services/webdav/ui/_chelp/webdavconfig.html new file mode 100644 index 00000000000..0858cf9bf5a --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_chelp/webdavconfig.html @@ -0,0 +1 @@ +<p>$r.translate("chelp.webdav.intro")</p> \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties new file mode 100644 index 00000000000..7a1f878c067 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties @@ -0,0 +1,9 @@ +admin.menu.title=WebDAV +admin.menu.title.alt=WebDAV Konfiguration +admin.webdav.description=WebDAV Konfiguration Beschreibung +chelp.webdav.intro=WebDAV Hilfe Text +chelp.webdavconfig.title=WebDAV Konfiguration +help.hover.webdavconfig=Hilfe zur WebDAV Konfiguration +core.webdav=WebDAV +webdav.link=WebDAV links +webdav.digest=Digest Authentifizierung \ No newline at end of file diff --git a/src/main/java/org/olat/core/dispatcher/Dispatcher.java b/src/main/java/org/olat/core/dispatcher/Dispatcher.java index 42eb4830735..abb81ea2c6b 100644 --- a/src/main/java/org/olat/core/dispatcher/Dispatcher.java +++ b/src/main/java/org/olat/core/dispatcher/Dispatcher.java @@ -28,6 +28,9 @@ */ package org.olat.core.dispatcher; +import java.io.IOException; + +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -51,5 +54,5 @@ public interface Dispatcher { * @param response * @param uriPrefix */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix); + public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; } diff --git a/src/main/java/org/olat/core/dispatcher/DispatcherAction.java b/src/main/java/org/olat/core/dispatcher/DispatcherModule.java similarity index 51% rename from src/main/java/org/olat/core/dispatcher/DispatcherAction.java rename to src/main/java/org/olat/core/dispatcher/DispatcherModule.java index 4bcf86e0858..e0980468588 100644 --- a/src/main/java/org/olat/core/dispatcher/DispatcherAction.java +++ b/src/main/java/org/olat/core/dispatcher/DispatcherModule.java @@ -30,168 +30,66 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.dispatcher.mapper.GlobalMapperRegistry; -import org.olat.core.dispatcher.mapper.MapperDispatcher; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.WebappHelper; -import org.olat.testutils.codepoints.server.Codepoint; + /** * Initial Date: 28.11.2003 * * @author Felix Jost */ -public class DispatcherAction implements Dispatcher { +public class DispatcherModule { + + private static final OLog log = Tracing.createLoggerFor(DispatcherModule.class); - private static OLog log = Tracing.createLoggerFor(DispatcherAction.class); - // important: add trailing slashes for PATH_XXX - private static String PATH_DEFAULT; - /** Identifies requests for the mappingregistry */ + /** Identifies requests for the DMZ */ + private static String PATH_DEFAULT = "/dmz/"; + /** Identifies requests for the mapper registry */ public static final String PATH_MAPPED = "/m/"; - /** Identifies requests for the global mappingregistry */ + /** Identifies requests for the global mapper registry */ public static final String PATH_GLOBAL_MAPPED = "/g/"; - /* default encoding */ + /** Identifies requests for webdav */ + public static final String WEBDAV_PATH = "/webdav/"; + /** Identifies requests for auth */ + public static final String PATH_AUTHENTICATED = "/auth/"; + /** default encoding */ private static final String UTF8_ENCODING = "utf-8"; - - /** - * incrementing an int is not atomar which needs to be synchronized - * AtomicInt provides the same without the need for syncing. - */ - private static AtomicInteger concurrentCounter = new AtomicInteger(0); - - private GlobalMapperRegistry gmr; /** * set by spring */ private Map<String, Dispatcher> dispatchers; - // brasato::remove!! - // ............used by BusinessGroupMainRunController - // ............used by DropboxScoringViewController - // used by olat.basesecurity.AuthHelper.doLogin - // used by olat.repository.RepoJumpInHandlerFactory.builRepositoryDispatchURI - public static final String PATH_AUTHENTICATED = "/auth/"; private static final String DOUBLE_SLASH = "//"; - // brasato::remove!! - // olat.login.DMZController - // olat.shibboleth13.ShibbolethDispatcher public static String getPathDefault(){ - return "/dmz/"; + return PATH_DEFAULT; } - - /** - * The main dispatcher. - */ - public DispatcherAction() { - // Initialize global mapper for dynamically generated, globally named mappers - gmr = GlobalMapperRegistry.getInstance(); + public static String getLegacyUriPrefix(HttpServletRequest request) { + return request.getContextPath() + getFirstPath(request); } - - /** - * Main method, called by OLATServlet. - * - * @param request - * @param response - */ - public void execute(HttpServletRequest request, HttpServletResponse response, String notusedhere) { - Codepoint.codepoint(DispatcherAction.class, "execute-start"); - - try { - concurrentCounter.incrementAndGet(); - String pathInfo = request.getPathInfo(); - - if (pathInfo == null) { - // seems to depend on the servlet container if null or "/" is returned - pathInfo = "/"; - } - - int sl = pathInfo.indexOf('/', 1); - String sub; - if (sl > 1) { - //e.g. something like /dmz/ or /auth/ - sub = pathInfo.substring(0, sl + 1); - } else { - //e.g. something like /maintenance.html - sub = pathInfo; - } - /* - * GLOBAL MAPPED and MAPPED PATHS - */ - if (pathInfo.startsWith(PATH_MAPPED)) { - - // OLAT-5368: an intermediate commit is necessary here to close open transactions which could otherwise - // run into the 2 min timeout and cause errors. The code does a return after serving the file anyway - // and would do a commit right there as well - so this doesn't break the transaction semantics. - DBFactory.getInstance(false).intermediateCommit(); - - // Session specific file mappers. When registered as cacheable - // mappers the browser might cache the delivered ressources - // using the last modified date. - // mapped paths (per usersession) -> /m/... - new MapperDispatcher().execute(request, response, subtractContextPath(request, pathInfo)); - return; - } else if (pathInfo.startsWith(PATH_GLOBAL_MAPPED)) { - // Dynamic files that can be cached by browsers based on last modified - // date, but are dynamically created by the application - gmr.execute(request, response, subtractContextPath(request, pathInfo)); - return; - } else { - - // Dispatch to defaultconfig.xml and extconfig.xml configured paths - // and also to named mappers -> /n/name... - Dispatcher d = dispatchers.get(sub); - if (d != null) { - d.execute(request, response, request.getContextPath() + sub);// e.g. /olat/ + sub - return; - } else { - // check if we have a root dispatcher that takes all - // remaining requests - d = dispatchers.get("/"); - if (d != null) { - // the root context dispatcher takes everything else - d.execute(request, response, request.getContextPath() + "/"); - return; - } - } - } - - // no dispatcher found that matches - send a 404 - sendNotFound(sub, response); - return; - - } catch (Throwable e) { - log.error("Exception in DispatcherAction", e); - handleError(); - sendBadRequest(request.getPathInfo(), response); - } finally { - try{ - concurrentCounter.decrementAndGet(); - - DBFactory.getInstance(false).commitAndCloseSession(); - - // reset user request for tracing - do this after db closing to provide logging to db closing - Codepoint.codepoint(DispatcherAction.class, "execute-end"); - } catch(Error er) { - log.error("Uncaught Error in DispatcherAction.execute.finally.", er); - throw er; - } catch(RuntimeException re) { - log.error("Uncaught RuntimeException in DispatcherAction.execute.finally.", re); - throw re; - } catch(Throwable th) { - log.error("Uncaught Throwable in DispatcherAction.execute.finally.", th); - } + public static String getFirstPath(HttpServletRequest request) { + String pathInfo = request.getPathInfo(); + if (pathInfo == null) return "/"; + + int sl = pathInfo.indexOf('/', 1); + String sub; + if (sl > 1) { + //e.g. something like /dmz/ or /auth/ + sub = pathInfo.substring(0, sl + 1); + } else { + //e.g. something like /maintenance.html + sub = pathInfo; } - + return sub; } /** @@ -202,7 +100,13 @@ public class DispatcherAction implements Dispatcher { * @return * @throws UnsupportedEncodingException */ - private String subtractContextPath(HttpServletRequest request, String pathInfo) throws UnsupportedEncodingException { + public static String subtractContextPath(HttpServletRequest request) throws UnsupportedEncodingException { + String pathInfo = request.getPathInfo(); + if (pathInfo == null) { + // seems to depend on the servlet container if null or "/" is returned + pathInfo = "/"; + } + String requestUri = request.getRequestURI(); //context path set - normal case if (WebappHelper.getServletContextPath().length() > 0) { @@ -292,27 +196,27 @@ public class DispatcherAction implements Dispatcher { log.error("Send 400 failed: url=" + url, e); } } - + /** - * @return the number of requests currently handled + * Sent to standard 503 if not available + * @param response */ - public static int getConcurrentCounter() { - return concurrentCounter.intValue(); + public static void redirectToServiceNotAvailable(HttpServletResponse response) { + try { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + } catch (IOException e) { + log.error("Send 503 failed", e); + } } public static void handleError() { if (log.isDebug()) log.debug("handleError : do rollback"); DBFactory.getInstance().rollbackAndCloseSession(); } - - - /** - * @return - */ - /*public static String getPathDefault() { - return PATH_DEFAULT; - }*/ + public Map<String, Dispatcher> getDispatchers() { + return dispatchers; + } /** * [key, value] pairs<br> @@ -327,26 +231,4 @@ public class DispatcherAction implements Dispatcher { public void setDispatchers(Map<String, Dispatcher> dispatchers) { this.dispatchers = dispatchers; } - - /** - * used by spring - * @param defaultDispatcherName The defaultDispatcherName to set. - */ - public void setDefaultDispatcherName(String defaultDispatcherName) { - PATH_DEFAULT = defaultDispatcherName; - } - - - /** - * Sent to standard 503 if not available - * @param response - */ - public static void redirectToServiceNotAvailable(HttpServletResponse response) { - try { - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); - } catch (IOException e) { - log.error("Send 503 failed", e); - } - } - } diff --git a/src/main/java/org/olat/core/dispatcher/ErrorFeedbackMailer.java b/src/main/java/org/olat/core/dispatcher/ErrorFeedbackMailer.java index 2b6f558b210..eb3b55ea1dd 100644 --- a/src/main/java/org/olat/core/dispatcher/ErrorFeedbackMailer.java +++ b/src/main/java/org/olat/core/dispatcher/ErrorFeedbackMailer.java @@ -118,9 +118,10 @@ public class ErrorFeedbackMailer implements Dispatcher { * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { sendMail(request); - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); } } diff --git a/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml b/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml index 1ac10498916..211620fdeaa 100644 --- a/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml +++ b/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml @@ -8,11 +8,9 @@ <!-- Configure the main url dispatcher: here: url http://host:port/webappname/ leads to http://host:port/webappname/go configures DispatcherActions to be called on certain paths --> <bean id="mainUrlDispatcher" - class="org.olat.core.dispatcher.DispatcherAction" > - <property name="defaultDispatcherName" value="/dmz/" /> + class="org.olat.core.dispatcher.DispatcherModule" > <property name="dispatchers"> <map> - <!-- <entry key="/go/"><ref bean="demodispatcher" /></entry>--> <entry key="/dmz/"> <ref bean="dmzbean" /> </entry> @@ -20,9 +18,6 @@ <!-- if you change /url make sure you also modify olatcore/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/_static/js/BTinyHelper.js accordingly --> <ref bean="restdispatcher" /> </entry> - <entry key="/"> - <ref bean="redirect2defaultbean" /> - </entry> <entry key="/remotelogin/"> <ref bean="remoteloginbean" /> </entry> @@ -42,9 +37,6 @@ <entry key="/admin.html"> <ref bean="adminbean" /> </entry> - <entry key="/raw/"> - <ref bean="staticsrawbean" /> - </entry> <entry key="/help/"> <ref bean="contexthelpbean" /> </entry> @@ -63,12 +55,6 @@ <entry key="/blog/"> <ref bean="blogMediaBean" /> </entry> - <!-- uncomment this if the xml catolog export should be enabled - <entry key="/catalog.xml"> - <ref bean="catexport" /> - </entry> - --> - </map> </property> </bean> @@ -201,19 +187,6 @@ <bean id="adminbean" class="org.olat.admin.AdminModuleDispatcher" /> - <!-- uncomment this if the xml catolog export should be enabled --> - <!-- - <bean id ="catexport" class = "org.olat.dispatcher.CatalogExportModuleDispatcher" > - <constructor-arg index="0">5</constructor-arg> - </bean> - --> - - <bean id="redirect2defaultbean" class = "org.olat.core.dispatcher.impl.RedirectToDefaultDispatcher" /> - <!-- uncomment this and comment above line if automatic guest login should be enabled --> - <!-- - <bean id="redirect2defaultbean" class = "org.olat.dispatcher.RedirectToAutoGuestLoginDispatcher" /> - --> - <!-- podcast media dispatcher --> <bean id="podcastMediaBean" class="org.olat.modules.webFeed.dispatching.FeedMediaDispatcher" /> @@ -221,8 +194,4 @@ <!-- blog media dispatcher --> <bean id="blogMediaBean" class="org.olat.modules.webFeed.dispatching.FeedMediaDispatcher" /> - - - - </beans> diff --git a/src/main/java/org/olat/core/dispatcher/impl/RedirectToDefaultDispatcher.java b/src/main/java/org/olat/core/dispatcher/impl/RedirectToDefaultDispatcher.java index 00b50976148..cd2e42e8e65 100644 --- a/src/main/java/org/olat/core/dispatcher/impl/RedirectToDefaultDispatcher.java +++ b/src/main/java/org/olat/core/dispatcher/impl/RedirectToDefaultDispatcher.java @@ -30,7 +30,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; /** * Description:<br> @@ -48,8 +48,9 @@ public class RedirectToDefaultDispatcher implements Dispatcher { /** * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { - DispatcherAction.redirectToDefaultDispatcher(response); + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { + DispatcherModule.redirectToDefaultDispatcher(response); } } diff --git a/src/main/java/org/olat/core/dispatcher/impl/StaticMediaDispatcher.java b/src/main/java/org/olat/core/dispatcher/impl/StaticMediaDispatcher.java index 6bcad5f18a5..15c3c16e898 100644 --- a/src/main/java/org/olat/core/dispatcher/impl/StaticMediaDispatcher.java +++ b/src/main/java/org/olat/core/dispatcher/impl/StaticMediaDispatcher.java @@ -19,20 +19,8 @@ */ package org.olat.core.dispatcher.impl; -import java.io.File; -import java.io.UnsupportedEncodingException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.gui.media.FileMediaResource; -import org.olat.core.gui.media.MediaResource; -import org.olat.core.gui.media.NotFoundMediaResource; -import org.olat.core.gui.media.ServletUtil; import org.olat.core.gui.render.StringOutput; import org.olat.core.helpers.Settings; -import org.olat.core.logging.AssertException; import org.olat.core.logging.LogDelegator; import org.olat.core.util.WebappHelper; @@ -54,7 +42,7 @@ import org.olat.core.util.WebappHelper; * * @author Florian Gnaegi, frentix GmbH, http://www.frentix.com */ -public class StaticMediaDispatcher extends LogDelegator implements Dispatcher { +public class StaticMediaDispatcher extends LogDelegator { public static String STATIC_DIR_NAME = "/static"; public static String NOVERSION = "_noversion_"; private static String mapperPath; @@ -68,151 +56,6 @@ public class StaticMediaDispatcher extends LogDelegator implements Dispatcher { mapperPath = mapperPathFromConfig; } - /** - * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, - * javax.servlet.http.HttpServletResponse, java.lang.String) - */ - @Override - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { - String pathInfo = request.getPathInfo(); - if (pathInfo == null) { - // huh? What's this, send not found, don't know what to do here - if (isLogDebugEnabled()) { - logDebug("PathInfo is null for static request URI::" + request.getRequestURI(), null); - } - ServletUtil.serveResource(request, response, new NotFoundMediaResource("error")); - return; - } - // remove uri prefix and version from request if available - String staticRelPath = null; - if (pathInfo.indexOf(NOVERSION) != -1) { - // no version provided - only remove mapper - staticRelPath = pathInfo.substring(mapperPath.length() + 1 + NOVERSION.length(), pathInfo.length()); - } - else { - // version provided - remove it - String version = Settings.getBuildIdentifier(); - int start = mapperPath.length() + 1 + version.length(); - int end = pathInfo.length(); - if(start <= end) { - staticRelPath = pathInfo.substring(start, end); - } else { - ServletUtil.serveResource(request, response, new NotFoundMediaResource("error")); - return; - } - } - - // remove any .. in the path - String normalizedRelPath = normalizePath(staticRelPath); - if (normalizedRelPath == null) { - if (isLogDebugEnabled()) { - logDebug("Path is null after noralizing for static request URI::" + request.getRequestURI(), null); - } - ServletUtil.serveResource(request, response, new NotFoundMediaResource("error")); - return; - } - // create the file from the path - String staticAbsPath; - if(Settings.isDebuging() && WebappHelper.getWebappSourcePath() != null) { - staticAbsPath = WebappHelper.getWebappSourcePath() + STATIC_DIR_NAME; - } else { - staticAbsPath = WebappHelper.getContextRoot() + STATIC_DIR_NAME; - } - File staticFile = new File(staticAbsPath, normalizedRelPath); - - // try loading themes from custom themes folder if configured - if (!staticFile.exists() && normalizedRelPath.contains("/themes/") && Settings.getGuiCustomThemePath() != null) { - File customThemesDir = Settings.getGuiCustomThemePath(); - String path = staticFile.getAbsolutePath(); - path = path.substring(path.indexOf("/static/themes/") + 15); - staticFile = new File(customThemesDir, path); - } - - // only serve if file exists - if (!staticFile.exists()) { - if (isLogDebugEnabled()) { - logDebug("File does not exist for URI::" + request.getRequestURI(), null); - } - // try fallback without version ID - staticRelPath = pathInfo.substring(mapperPath.length() , pathInfo.length()); - normalizedRelPath = normalizePath(staticRelPath); - staticAbsPath = WebappHelper.getContextRoot() + STATIC_DIR_NAME + normalizedRelPath; - staticFile = new File(staticAbsPath); - if (!staticFile.exists()) { - ServletUtil.serveResource(request, response, new NotFoundMediaResource("error")); - return; - } - // log as error, file exists but wrongly mapped - logWarn("File exists but not mapped using version - use StaticMediaDispatch methods to create URL of static files! invalid URI::" + request.getRequestURI(), null); - } - - if (isLogDebugEnabled()) { - logDebug("Serving resource URI::" + request.getRequestURI(), null); - } - // Everything is ok, serve resource - MediaResource resource = new FileMediaResource(staticFile); - ServletUtil.serveResource(request, response, resource); - } - - /** - * Return a context-relative path, beginning with a "/", that represents the - * canonical version of the specified path - * <p> - * ".." and "." elements are resolved out. If the specified path attempts to - * go outside the boundaries of the current context (i.e. too many ".." path - * elements are present), return <code>null</code> instead. - * <p> - * - * @author Mike Stock - * - * @param path Path to be normalized - * @return the normalized path - */ - public static String normalizePath(String path) { - if (path == null) return null; - - // Create a place for the normalized path - String normalized = path; - - try { // we need to decode potential UTF-8 characters in the URL - normalized = new String(normalized.getBytes(), "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new AssertException("utf-8 encoding must be supported on all java platforms..."); - } - - if (normalized.equals("/.")) return "/"; - - // Normalize the slashes and add leading slash if necessary - if (normalized.indexOf('\\') >= 0) normalized = normalized.replace('\\', '/'); - if (!normalized.startsWith("/")) normalized = "/" + normalized; - - // Resolve occurrences of "//" in the normalized path - while (true) { - int index = normalized.indexOf("//"); - if (index < 0) break; - normalized = normalized.substring(0, index) + normalized.substring(index + 1); - } - - // Resolve occurrences of "/./" in the normalized path - while (true) { - int index = normalized.indexOf("/./"); - if (index < 0) break; - normalized = normalized.substring(0, index) + normalized.substring(index + 2); - } - - // Resolve occurrences of "/../" in the normalized path - while (true) { - int index = normalized.indexOf("/../"); - if (index < 0) break; - if (index == 0) return (null); // Trying to go outside our context - int index2 = normalized.lastIndexOf('/', index - 1); - normalized = normalized.substring(0, index2) + normalized.substring(index + 3); - } - - // Return the normalized path that we have completed - return (normalized); - } - /** * Note: use only rarely - all non-generic js libs and css classes should be * included using JsAndCssComponent, and all images should be referenced with diff --git a/src/main/java/org/olat/core/dispatcher/mapper/GlobalMapperRegistry.java b/src/main/java/org/olat/core/dispatcher/mapper/GlobalMapperRegistry.java index 245641ad623..087f2f35ba1 100644 --- a/src/main/java/org/olat/core/dispatcher/mapper/GlobalMapperRegistry.java +++ b/src/main/java/org/olat/core/dispatcher/mapper/GlobalMapperRegistry.java @@ -26,6 +26,7 @@ package org.olat.core.dispatcher.mapper; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -33,7 +34,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.NotFoundMediaResource; import org.olat.core.gui.media.ServletUtil; @@ -87,7 +88,7 @@ public class GlobalMapperRegistry implements Dispatcher { throw new AssertException("Could not register global named mapper, name already used::" + globalName); } pathToMapper.put(globalName, mapper); - return WebappHelper.getServletContextPath() + DispatcherAction.PATH_GLOBAL_MAPPED + globalName ; + return WebappHelper.getServletContextPath() + DispatcherModule.PATH_GLOBAL_MAPPED + globalName ; } @@ -95,12 +96,14 @@ public class GlobalMapperRegistry implements Dispatcher { * @param hreq * @param hres */ - public void execute(HttpServletRequest hreq, HttpServletResponse hres, String pathInfo) { + @Override + public void execute(HttpServletRequest hreq, HttpServletResponse hres) throws IOException { + String pathInfo = DispatcherModule.subtractContextPath(hreq); // e.g. 23423/bla/blu.html - String subInfo = pathInfo.substring(DispatcherAction.PATH_GLOBAL_MAPPED.length()); + String subInfo = pathInfo.substring(DispatcherModule.PATH_GLOBAL_MAPPED.length()); int slashPos = subInfo.indexOf('/'); if (slashPos == -1) { - DispatcherAction.sendNotFound("not found", hres); + DispatcherModule.sendNotFound("not found", hres); return; } diff --git a/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java b/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java index f20311ca234..d72ee487b72 100644 --- a/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java +++ b/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java @@ -26,12 +26,15 @@ package org.olat.core.dispatcher.mapper; +import java.io.IOException; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.persistence.DBFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.ServletUtil; import org.olat.core.logging.LogDelegator; @@ -63,7 +66,10 @@ public class MapperDispatcher extends LogDelegator implements Dispatcher { * @param hreq * @param hres */ - public void execute(HttpServletRequest hreq, HttpServletResponse hres, String pathInfo) { + @Override + public void execute(HttpServletRequest hreq, HttpServletResponse hres) throws IOException { + + String pathInfo = DispatcherModule.subtractContextPath(hreq); final boolean isDebugLog = isLogDebugEnabled(); StringBuilder debugMsg = null; long debug_start = 0; @@ -74,7 +80,7 @@ public class MapperDispatcher extends LogDelegator implements Dispatcher { // e.g. non-cacheable: 23423/bla/blu.html // e.g. cacheable: my.mapper.path/bla/blu.html - String subInfo = pathInfo.substring(DispatcherAction.PATH_MAPPED.length()); + String subInfo = pathInfo.substring(DispatcherModule.PATH_MAPPED.length()); int slashPos = subInfo.indexOf('/'); String smappath; @@ -83,6 +89,9 @@ public class MapperDispatcher extends LogDelegator implements Dispatcher { } else { smappath = subInfo.substring(0, slashPos); } + + //legacy??? + DBFactory.getInstance().commitAndCloseSession(); // e.g. non-cacheable: 23423 // e.g. cacheable: my.mapper.path @@ -92,14 +101,13 @@ public class MapperDispatcher extends LogDelegator implements Dispatcher { logWarn( "Call to mapped resource, but mapper does not exist for path::" + pathInfo, null); - DispatcherAction.sendNotFound(pathInfo, hres); + DispatcherModule.sendNotFound(pathInfo, hres); return; } String mod = slashPos > 0 ? subInfo.substring(slashPos) : ""; if (mod.indexOf("..") != -1) { - logWarn("Illegal mapper path::" + mod + " contains '..'", - null); - DispatcherAction.sendForbidden(pathInfo, hres); + logWarn("Illegal mapper path::" + mod + " contains '..'", null); + DispatcherModule.sendForbidden(pathInfo, hres); return; } // /bla/blu.html diff --git a/src/main/java/org/olat/core/dispatcher/mapper/manager/MapperServiceImpl.java b/src/main/java/org/olat/core/dispatcher/mapper/manager/MapperServiceImpl.java index 4f5746d2c80..4cdca6862f1 100644 --- a/src/main/java/org/olat/core/dispatcher/mapper/manager/MapperServiceImpl.java +++ b/src/main/java/org/olat/core/dispatcher/mapper/manager/MapperServiceImpl.java @@ -27,7 +27,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.dispatcher.mapper.MapperService; import org.olat.core.dispatcher.mapper.model.PersistedMapper; @@ -90,7 +90,7 @@ public class MapperServiceImpl implements MapperService { mapperKeyToMapper.put(mapperKey, mapper); mapperToMapperKey.put(mapper, mapperKey); if(session == null || session.getSessionInfo() == null) { - return WebappHelper.getServletContextPath() + DispatcherAction.PATH_MAPPED + mapid; + return WebappHelper.getServletContextPath() + DispatcherModule.PATH_MAPPED + mapid; } String sessionId = session.getSessionInfo().getSession().getId(); @@ -105,7 +105,7 @@ public class MapperServiceImpl implements MapperService { if(mapper instanceof Serializable) { mapperDao.persistMapper(sessionId, mapid, (Serializable)mapper, -1); } - return WebappHelper.getServletContextPath() + DispatcherAction.PATH_MAPPED + mapid; + return WebappHelper.getServletContextPath() + DispatcherModule.PATH_MAPPED + mapid; } /** @@ -138,7 +138,7 @@ public class MapperServiceImpl implements MapperService { mapperKeyToMapper.put(mapperKey, mapper); mapperToMapperKey.put(mapper, mapperKey); - return WebappHelper.getServletContextPath() + DispatcherAction.PATH_MAPPED + encryptedMapId; + return WebappHelper.getServletContextPath() + DispatcherModule.PATH_MAPPED + encryptedMapId; } @Override @@ -147,9 +147,9 @@ public class MapperServiceImpl implements MapperService { return null; } - int index = id.indexOf(DispatcherAction.PATH_MAPPED); + int index = id.indexOf(DispatcherModule.PATH_MAPPED); if(index >= 0) { - id = id.substring(index + DispatcherAction.PATH_MAPPED.length(), id.length()); + id = id.substring(index + DispatcherModule.PATH_MAPPED.length(), id.length()); } MapperKey mapperKey = new MapperKey(session, id); diff --git a/src/main/java/org/olat/core/gui/UserRequestImpl.java b/src/main/java/org/olat/core/gui/UserRequestImpl.java index e6e5aa16e85..40b9a053212 100644 --- a/src/main/java/org/olat/core/gui/UserRequestImpl.java +++ b/src/main/java/org/olat/core/gui/UserRequestImpl.java @@ -264,8 +264,7 @@ public class UserRequestImpl implements UserRequest { } nonParsedUri = decodedUri.substring(uriPrefix.length()); // guaranteed to - // exist by - // DispatcherAction + // exist by OpenOLATServlet // parse parameters int nextSlash = nonParsedUri.indexOf('/'); diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/elements/TextElement.java b/src/main/java/org/olat/core/gui/components/form/flexible/elements/TextElement.java index 6f01d931651..c89ed1d294a 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/elements/TextElement.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/elements/TextElement.java @@ -47,8 +47,7 @@ public interface TextElement extends FormItem{ public String getValue(); /** - * Get the value and filter it using the given filter. To use multiple - * filters, use the ChainedFilter instead of a single filter. + * Get the value and filter it using the given filter. * * @param filter * @return diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java index 4cb4f584f3a..4d63f9933ca 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java @@ -29,6 +29,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -59,7 +61,6 @@ import org.olat.core.util.ArrayHelper; import org.olat.core.util.CodeHelper; import org.olat.core.util.FileUtils; import org.olat.core.util.ValidationStatus; -import org.olat.core.util.ValidationStatusHelper; import org.olat.core.util.WebappHelper; import org.olat.core.util.component.FormComponentTraverser; import org.olat.core.util.component.FormComponentVisitor; @@ -720,9 +721,10 @@ public class Form extends LogDelegator { final List<ValidationStatus> tmp = new ArrayList<ValidationStatus>(); public ValidationStatus[] getStatus() { - return ValidationStatusHelper.sort(tmp); + return sort(tmp); } + @Override public boolean visit(FormItem comp, UserRequest ureq) { if(comp.isVisible() && comp.isEnabled()){ //validate only if form item is visible and enabled @@ -730,6 +732,35 @@ public class Form extends LogDelegator { } return true; } + + private ValidationStatus[] sort(List<ValidationStatus> statusList) { + Collections.sort(statusList, new ErrorsGtWarningGtInfo()); + /* + * remove NOERRORS size bigger 1; NOERROR -> Level == OFF > SEVERE > WARNING > + * INFO + */ + if (statusList.size() > 1) { + for(int i=0;i<statusList.size();i++) { + if(statusList.get(i)==ValidationStatus.NOERROR) { + //aha found one to remove. Remove shifts all elements to the left, e.g. subtracts one of the indices + statusList.remove(i); + //thus we have to loop on place to remove all NOERRORS which shift to the ith place. + while(statusList.get(i)==ValidationStatus.NOERROR) { + statusList.remove(i); + } + } + } + } + + return statusList.toArray(new ValidationStatus[statusList.size()]); + } + + private static class ErrorsGtWarningGtInfo implements Comparator<ValidationStatus> { + @Override + public int compare(ValidationStatus s1, ValidationStatus s2) { + return s2.getLevel().intValue() - s1.getLevel().intValue(); + } + } } private static class ResettingFormComponentVisitor implements FormComponentVisitor { @@ -836,17 +867,14 @@ public class Form extends LogDelegator { private void initReplayIdCounter(Controller listener) { Integer formNum = null; - Object o; - Map m; - - o = GUIInterna.getReplayModeData().get("formsSeen"); + Object o = GUIInterna.getReplayModeData().get("formsSeen"); if (o == null) { - o = new HashMap(); + o = new HashMap<String,Integer>(); GUIInterna.getReplayModeData().put("formsSeen", o); } - m = (Map) o; + Map<String,Integer> m = (Map<String,Integer>) o; String k = listener.getClass().getName(); if (m.containsKey(k)) { formNum = (Integer) m.get(k); @@ -856,7 +884,7 @@ public class Form extends LogDelegator { o = GUIInterna.getReplayModeData().get("formNum"); formNum = (o == null) ? 1 : (Integer) o + 1; GUIInterna.getReplayModeData().put("formNum", formNum); - m = (Map) GUIInterna.getReplayModeData().get("formsSeen"); + m = (Map<String,Integer>) GUIInterna.getReplayModeData().get("formsSeen"); m.put(listener.getClass().getName(), formNum); } diff --git a/src/main/java/org/olat/core/gui/control/winmgr/AjaxController.java b/src/main/java/org/olat/core/gui/control/winmgr/AjaxController.java index 7df85f670fd..745266a5dc7 100644 --- a/src/main/java/org/olat/core/gui/control/winmgr/AjaxController.java +++ b/src/main/java/org/olat/core/gui/control/winmgr/AjaxController.java @@ -40,7 +40,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.olat.admin.sysinfo.manager.SessionStatsManager; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.dispatcher.impl.StaticMediaDispatcher; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.dispatcher.mapper.MapperService; @@ -171,7 +171,7 @@ public class AjaxController extends DefaultController { StaticMediaDispatcher.renderStaticURI(slink, null); //slink now holds static url base like /olat/raw/700/ - URLBuilder ubu = new URLBuilder(WebappHelper.getServletContextPath() + DispatcherAction.PATH_AUTHENTICATED, "1", "1", null); + URLBuilder ubu = new URLBuilder(WebappHelper.getServletContextPath() + DispatcherModule.PATH_AUTHENTICATED, "1", "1", null); StringOutput blink = new StringOutput(50); ubu.buildURI(blink, null, null); //blink holds the link back to olat like /olat/auth/1%3A1%3A0%3A0%3A0/ diff --git a/src/main/java/org/olat/core/gui/media/ServletUtil.java b/src/main/java/org/olat/core/gui/media/ServletUtil.java index e2b7726d4da..4bdd4073afb 100644 --- a/src/main/java/org/olat/core/gui/media/ServletUtil.java +++ b/src/main/java/org/olat/core/gui/media/ServletUtil.java @@ -34,6 +34,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Reader; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; @@ -49,6 +50,7 @@ import org.olat.core.gui.Windows; import org.olat.core.gui.render.StringOutput; import org.olat.core.gui.util.bandwidth.SlowBandWidthSimulator; import org.olat.core.helpers.Settings; +import org.olat.core.logging.AssertException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.FileUtils; @@ -70,7 +72,12 @@ public class ServletUtil { } } - + public static void printOutRequestHeaders(HttpServletRequest request) { + for(Enumeration<String> headers=request.getHeaderNames(); headers.hasMoreElements(); ) { + String header = headers.nextElement(); + log.info(header + " :: " + request.getHeader(header)); + } + } /** * @param httpReq @@ -546,6 +553,65 @@ public class ServletUtil { response.setDateHeader("Expires", 0); } + /** + * Return a context-relative path, beginning with a "/", that represents the + * canonical version of the specified path + * <p> + * ".." and "." elements are resolved out. If the specified path attempts to + * go outside the boundaries of the current context (i.e. too many ".." path + * elements are present), return <code>null</code> instead. + * <p> + * + * @author Mike Stock + * + * @param path Path to be normalized + * @return the normalized path + */ + public static String normalizePath(String path) { + if (path == null) return null; + + // Create a place for the normalized path + String normalized = path; + + try { // we need to decode potential UTF-8 characters in the URL + normalized = new String(normalized.getBytes(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new AssertException("utf-8 encoding must be supported on all java platforms..."); + } + + if (normalized.equals("/.")) return "/"; + + // Normalize the slashes and add leading slash if necessary + if (normalized.indexOf('\\') >= 0) normalized = normalized.replace('\\', '/'); + if (!normalized.startsWith("/")) normalized = "/" + normalized; + + // Resolve occurrences of "//" in the normalized path + while (true) { + int index = normalized.indexOf("//"); + if (index < 0) break; + normalized = normalized.substring(0, index) + normalized.substring(index + 1); + } + + // Resolve occurrences of "/./" in the normalized path + while (true) { + int index = normalized.indexOf("/./"); + if (index < 0) break; + normalized = normalized.substring(0, index) + normalized.substring(index + 2); + } + + // Resolve occurrences of "/../" in the normalized path + while (true) { + int index = normalized.indexOf("/../"); + if (index < 0) break; + if (index == 0) return (null); // Trying to go outside our context + int index2 = normalized.lastIndexOf('/', index - 1); + normalized = normalized.substring(0, index2) + normalized.substring(index + 3); + } + + // Return the normalized path that we have completed + return (normalized); + } + //fxdiff FXOLAT-118: accept range to deliver videos for iPad protected static class Range { public long start; diff --git a/src/main/java/org/olat/core/helpers/Settings.java b/src/main/java/org/olat/core/helpers/Settings.java index 24abdf97c92..9bcb90407be 100644 --- a/src/main/java/org/olat/core/helpers/Settings.java +++ b/src/main/java/org/olat/core/helpers/Settings.java @@ -88,6 +88,8 @@ public class Settings implements Initializable, Destroyable, GenericEventListene private static String crossOriginFilter; private static File guiCustomThemePath; + private String legacyContextPath; + /** * [used by spring] */ @@ -317,6 +319,14 @@ public class Settings implements Initializable, Destroyable, GenericEventListene Settings.serverconfig = serverconfig; } + public String getLegacyContext() { + return legacyContextPath; + } + + public void setLegacyContext(String legacyContextPath) { + this.legacyContextPath = legacyContextPath; + } + /** * [spring] * @param debug @@ -328,6 +338,7 @@ public class Settings implements Initializable, Destroyable, GenericEventListene /** * @see org.olat.core.configuration.ServiceLifeCycle#init() */ + @Override public void init() { // Initialize the user configuration and the spring default configuration // @@ -340,6 +351,7 @@ public class Settings implements Initializable, Destroyable, GenericEventListene /** * @see org.olat.core.configuration.ServiceLifeCycle#destroy() */ + @Override public void destroy() { if (persistedProperties != null) { persistedProperties.destroy(); diff --git a/src/main/java/org/olat/core/servlets/OLATServlet.java b/src/main/java/org/olat/core/servlets/OLATServlet.java deleted file mode 100644 index d3f47db9239..00000000000 --- a/src/main/java/org/olat/core/servlets/OLATServlet.java +++ /dev/null @@ -1,168 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -* <p> -*/ - -package org.olat.core.servlets; - -import java.io.IOException; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.olat.admin.sysinfo.manager.SessionStatsManager; -import org.olat.core.CoreSpringFactory; -import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; -import org.olat.core.gui.GUIInterna; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; -import org.olat.core.logging.activity.ThreadLocalUserActivityLoggerInstaller; -import org.olat.core.util.WorkThreadInformations; -import org.olat.core.util.event.FrameworkStartupEventChannel; -import org.olat.core.util.i18n.I18nManager; -import org.olat.core.util.threadlog.RequestBasedLogLevelManager; -import org.olat.core.util.threadlog.UserBasedLogLevelManager; - -/** - * Initial Date: Apr 28, 2004 - * - * @author Mike Stock - * - * Comment: - * - */ -public class OLATServlet extends HttpServlet { - - private static final long serialVersionUID = 4146352020009404834L; - private static OLog log = Tracing.createLoggerFor(OLATServlet.class); - private Dispatcher dispatcher; - private SessionStatsManager sessionStatsManager; - private RequestBasedLogLevelManager requestBasedLogLevelManager; - - /** - * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) - */ - public void init(ServletConfig servletConfig) throws ServletException { - super.init(servletConfig); - - requestBasedLogLevelManager = RequestBasedLogLevelManager.getInstance(); - if (requestBasedLogLevelManager==null) { - log.info("init: RequestBasedLogLevelManager is not configured on this system."); - } else { - log.info("init: RequestBasedLogLevelManager is configured and will be used."); - } - if (UserBasedLogLevelManager.getInstance()==null) { - log.info("init: UserBasedLogLevelManager is not configured on this system."); - } else { - log.info("init: UserBasedLogLevelManager is configured and will be used."); - } - - //the servlet.init method gets called after the spring stuff and all the stuff in web.xml is done - log.info("Framework has started, sending event to listeners of FrameworkStartupEventChannel"); - FrameworkStartupEventChannel.fireEvent(); - log.info("FrameworkStartupEvent processed by alle listeners. Webapp has started."); - sessionStatsManager = CoreSpringFactory.getImpl(SessionStatsManager.class); - } - - /** - * @see javax.servlet.Servlet#destroy() - */ - public void destroy() { - // - } - - - /** - * Filter BitKinex at the root - */ - @Override - public void service(HttpServletRequest req, HttpServletResponse res) - throws ServletException, IOException { - String userAgent = req.getHeader("User-Agent"); - if(userAgent != null && userAgent.indexOf("BitKinex") >= 0) { - //BitKinex isn't allow to see this context - res.sendError(HttpServletResponse.SC_FORBIDDEN); - } else { - super.service(req, res); - } - } - - /** - * Called when the HTTP request method is GET. This method just calls the - * doPost() method. - * - * @param request - * The HTTP request - * @param response - * The HTTP response - * @throws ServletException - * @throws IOException - */ - public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - doPost(request, response); - } - - /** - * Called when the HTTP request method is POST. This method provides the main - * control logic. - * - * @param request - * The HTTP request - * @param response - * The HTTP response - * @throws ServletException - * @throws IOException - */ - public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - GUIInterna.begin(request); - // initalize tracing with request, this allows debugging information as IP, User-Agent. - Tracing.setUreq(request); - I18nManager.attachI18nInfoToThread(request); - ThreadLocalUserActivityLoggerInstaller.initUserActivityLogger(request); - //fxdiff FXOLAT-97: high CPU load tracker - WorkThreadInformations.set("Serve request: " + request.getRequestURI()); - - try{ - if(sessionStatsManager != null) sessionStatsManager.incrementRequest(); - if (requestBasedLogLevelManager!=null) requestBasedLogLevelManager.activateRequestBasedLogLevel(request); - if (dispatcher == null) dispatcher = (Dispatcher) CoreSpringFactory.getBean(DispatcherAction.class); - dispatcher.execute(request, response, null); - } finally { - if (requestBasedLogLevelManager!=null) requestBasedLogLevelManager.deactivateRequestBasedLogLevel(); - //fxdiff FXOLAT-97: high CPU load tracker - WorkThreadInformations.unset(); - ThreadLocalUserActivityLoggerInstaller.resetUserActivityLogger(); - I18nManager.remove18nInfoFromThread(); - Tracing.setUreq(null); - GUIInterna.end(request); - DBFactory.getInstanceForClosing().closeSession(); - } - } - -} diff --git a/src/main/java/org/olat/core/servlets/OpenOLATServlet.java b/src/main/java/org/olat/core/servlets/OpenOLATServlet.java new file mode 100644 index 00000000000..8a5b1fad039 --- /dev/null +++ b/src/main/java/org/olat/core/servlets/OpenOLATServlet.java @@ -0,0 +1,265 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.servlets; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.olat.admin.sysinfo.manager.SessionStatsManager; +import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.commons.services.webdav.WebDAVDispatcher; +import org.olat.core.commons.services.webdav.servlets.WebdavDispatcher; +import org.olat.core.dispatcher.Dispatcher; +import org.olat.core.dispatcher.DispatcherModule; +import org.olat.core.dispatcher.mapper.GlobalMapperRegistry; +import org.olat.core.dispatcher.mapper.MapperDispatcher; +import org.olat.core.gui.GUIInterna; +import org.olat.core.helpers.Settings; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.logging.activity.ThreadLocalUserActivityLoggerInstaller; +import org.olat.core.util.StringHelper; +import org.olat.core.util.WebappHelper; +import org.olat.core.util.WorkThreadInformations; +import org.olat.core.util.event.FrameworkStartupEventChannel; +import org.olat.core.util.i18n.I18nManager; +import org.olat.core.util.threadlog.RequestBasedLogLevelManager; +import org.olat.core.util.threadlog.UserBasedLogLevelManager; + +public class OpenOLATServlet extends HttpServlet { + + private static final long serialVersionUID = -2777749229549683775L; + private static final OLog log = Tracing.createLoggerFor(OpenOLATServlet.class); + + private static final String METHOD_PROPFIND = "PROPFIND"; + private static final String METHOD_PROPPATCH = "PROPPATCH"; + private static final String METHOD_MKCOL = "MKCOL"; + private static final String METHOD_COPY = "COPY"; + private static final String METHOD_MOVE = "MOVE"; + private static final String METHOD_LOCK = "LOCK"; + private static final String METHOD_UNLOCK = "UNLOCK"; + + private String legacyContext; + + private DispatcherModule dispatcher; + private SessionStatsManager sessionStatsManager; + private RequestBasedLogLevelManager requestBasedLogLevelManager; + + private WebDAVDispatcher webDAVDispatcher; + private Map<String, Dispatcher> dispatchers; + + /** + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + @Override + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + + requestBasedLogLevelManager = RequestBasedLogLevelManager.getInstance(); + if (requestBasedLogLevelManager==null) { + log.info("init: RequestBasedLogLevelManager is not configured on this system."); + } else { + log.info("init: RequestBasedLogLevelManager is configured and will be used."); + } + if (UserBasedLogLevelManager.getInstance()==null) { + log.info("init: UserBasedLogLevelManager is not configured on this system."); + } else { + log.info("init: UserBasedLogLevelManager is configured and will be used."); + } + + //the servlet.init method gets called after the spring stuff and all the stuff in web.xml is done + log.info("Framework has started, sending event to listeners of FrameworkStartupEventChannel"); + FrameworkStartupEventChannel.fireEvent(); + log.info("FrameworkStartupEvent processed by alle listeners. Webapp has started."); + sessionStatsManager = CoreSpringFactory.getImpl(SessionStatsManager.class); + dispatcher = CoreSpringFactory.getImpl(DispatcherModule.class); + + dispatchers = new HashMap<String, Dispatcher>(dispatcher.getDispatchers()); + dispatchers.put(DispatcherModule.PATH_MAPPED, new MapperDispatcher()); + dispatchers.put(DispatcherModule.PATH_GLOBAL_MAPPED, GlobalMapperRegistry.getInstance()); + + webDAVDispatcher = new WebdavDispatcher(); + dispatchers.put(DispatcherModule.WEBDAV_PATH, webDAVDispatcher); + + Settings settings = CoreSpringFactory.getImpl(Settings.class); + if(StringHelper.containsNonWhitespace(settings.getLegacyContext())) { + legacyContext = settings.getLegacyContext(); + // same pattern as dispatcher: /olat/ + if(!legacyContext.startsWith("/")) { + legacyContext = "/" + legacyContext; + } + if(!legacyContext.endsWith("/")) { + legacyContext += "/"; + } + } + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + //log.info(req.getMethod() + " :: " + req.getPathInfo()); + + GUIInterna.begin(req); + Tracing.setUreq(req); + I18nManager.attachI18nInfoToThread(req); + ThreadLocalUserActivityLoggerInstaller.initUserActivityLogger(req); + WorkThreadInformations.set("Serve request: " + req.getRequestURI()); + if(sessionStatsManager != null) { + sessionStatsManager.incrementRequest(); + sessionStatsManager.incrementConcurrentCounter(); + } + try{ + + final String method = req.getMethod(); + if (method.equals(METHOD_PROPFIND)) { + webDAVDispatcher.execute(req, resp); + } else if (method.equals(METHOD_PROPPATCH)) { + webDAVDispatcher.execute(req, resp); + } else if (method.equals(METHOD_MKCOL)) { + webDAVDispatcher.execute(req, resp); + } else if (method.equals(METHOD_COPY)) { + webDAVDispatcher.execute(req, resp); + } else if (method.equals(METHOD_MOVE)) { + webDAVDispatcher.execute(req, resp); + } else if (method.equals(METHOD_LOCK)) { + webDAVDispatcher.execute(req, resp); + } else if (method.equals(METHOD_UNLOCK)) { + webDAVDispatcher.execute(req, resp); + } else { + super.service(req, resp); + } + + } finally { + if (requestBasedLogLevelManager != null) { + requestBasedLogLevelManager.deactivateRequestBasedLogLevel(); + } + if(sessionStatsManager != null) { + sessionStatsManager.decrementConcurrentCounter(); + } + WorkThreadInformations.unset(); + ThreadLocalUserActivityLoggerInstaller.resetUserActivityLogger(); + I18nManager.remove18nInfoFromThread(); + Tracing.setUreq(null); + GUIInterna.end(req); + //let it at the end + DBFactory.getInstance().commitAndCloseSession(); + } + } + + @Override + protected void doOptions(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + String subContext = DispatcherModule.getFirstPath(req); + if("/".equals(subContext)) { + webDAVDispatcher.doRootOptions(req, resp); + } else if("webdav".equals(subContext)) { + webDAVDispatcher.doWebdavOptions(req, resp); + } else { + super.doOptions(req, resp); + } + } + + /** + * Called when the HTTP request method is GET. This method just calls the + * doPost() method. + * + * @param request The HTTP request + * @param response The HTTP response + * @throws ServletException + * @throws IOException + */ + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + executeUserRequest(request, response); + } + + /** + * Called when the HTTP request method is POST. This method provides the main + * control logic. + * + * @param request The HTTP request + * @param response The HTTP response + * @throws ServletException + * @throws IOException + */ + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + executeUserRequest(request, response); + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + webDAVDispatcher.execute(req, resp); + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + webDAVDispatcher.execute(req, resp); + } + + @Override + protected void doHead(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + webDAVDispatcher.execute(req, resp); + } + + /** + * Initialize tracing with request, this allows debugging information as IP, User-Agent. + * @param request + * @param response + */ + private void executeUserRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + if (requestBasedLogLevelManager != null) { + requestBasedLogLevelManager.activateRequestBasedLogLevel(request); + } + + final String dispatcherName = DispatcherModule.getFirstPath(request); + if(legacyContext != null && legacyContext.equals(dispatcherName)) { + String uri = request.getRequestURI(); + String redirectUri = uri.substring(legacyContext.length() - 1, uri.length()); + RequestDispatcher dispatcher = request.getRequestDispatcher(redirectUri); + dispatcher.forward(request, response); + } else if(dispatchers.containsKey(dispatcherName)) { + Dispatcher dispatcher = dispatchers.get(dispatcherName); + dispatcher.execute(request, response); + } else { + String uri = request.getRequestURI(); + //root -> redirect to dmz + if("/".equals(uri)) { + String dmzUri = WebappHelper.getServletContextPath() + DispatcherModule.getPathDefault(); + response.sendRedirect(dmzUri); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + } +} diff --git a/src/main/java/org/olat/core/servlets/ServletWrapperFilter.java b/src/main/java/org/olat/core/servlets/ServletWrapperFilter.java index 79a1001d2e6..09ca2b33ffb 100644 --- a/src/main/java/org/olat/core/servlets/ServletWrapperFilter.java +++ b/src/main/java/org/olat/core/servlets/ServletWrapperFilter.java @@ -31,7 +31,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; @@ -86,7 +86,7 @@ public class ServletWrapperFilter implements Filter { success = true; } catch (Throwable e) { log.error("Exception in ServletWrapperFilter", e); - DispatcherAction.sendBadRequest(request.getPathInfo(), response); + DispatcherModule.sendBadRequest(request.getPathInfo(), response); } finally { // execute the cleanup code for this request Tracing.setUreq(null); diff --git a/src/main/java/org/olat/core/servlets/StaticServlet.java b/src/main/java/org/olat/core/servlets/StaticServlet.java new file mode 100644 index 00000000000..028293db3eb --- /dev/null +++ b/src/main/java/org/olat/core/servlets/StaticServlet.java @@ -0,0 +1,137 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.servlets; + +import java.io.File; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.olat.core.gui.media.FileMediaResource; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.gui.media.ServletUtil; +import org.olat.core.helpers.Settings; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.WebappHelper; + +/** + * + * Deliver the file in /raw/ + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class StaticServlet extends HttpServlet { + + private static final long serialVersionUID = -2430002903299685192L; + private static final OLog log = Tracing.createLoggerFor(StaticServlet.class); + + public static String STATIC_DIR_NAME = "/static"; + public static String NOVERSION = "_noversion_"; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + final boolean debug = log.isDebug(); + final String pathInfo = request.getPathInfo(); + if (pathInfo == null) { + // huh? What's this, send not found, don't know what to do here + if (debug) { + log.debug("PathInfo is null for static request URI::" + request.getRequestURI(), null); + } + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + // remove uri prefix and version from request if available + String staticRelPath; + if (pathInfo.indexOf(NOVERSION) != -1) { + // no version provided - only remove mapper + staticRelPath = pathInfo.substring(NOVERSION.length() + 1, pathInfo.length()); + } else { + // version provided - remove it + String version = Settings.getBuildIdentifier(); + int start = version.length() + 1; + int end = pathInfo.length(); + if(start <= end) { + staticRelPath = pathInfo.substring(start, end); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + } + + // remove any .. in the path + String normalizedRelPath = ServletUtil.normalizePath(staticRelPath); + if (normalizedRelPath == null) { + if (debug) { + log.debug("Path is null after noralizing for static request URI::" + request.getRequestURI(), null); + } + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + // create the file from the path + String staticAbsPath; + if(Settings.isDebuging() && WebappHelper.getWebappSourcePath() != null) { + staticAbsPath = WebappHelper.getWebappSourcePath() + STATIC_DIR_NAME; + } else { + staticAbsPath = WebappHelper.getContextRoot() + STATIC_DIR_NAME; + } + + File staticFile = new File(staticAbsPath, normalizedRelPath); + if (!staticFile.exists()) { + // try loading themes from custom themes folder if configured + if(normalizedRelPath.contains("/themes/") && Settings.getGuiCustomThemePath() != null) { + File customThemesDir = Settings.getGuiCustomThemePath(); + String path = staticFile.getAbsolutePath(); + path = path.substring(path.indexOf("/static/themes/") + 15); + staticFile = new File(customThemesDir, path); + } + + // only serve if file exists + if (!staticFile.exists()) { + if (debug) { + log.debug("File does not exist for URI::" + request.getRequestURI(), null); + } + // try fallback without version ID + String fallbackPath = pathInfo.substring(1, pathInfo.length()); + fallbackPath = ServletUtil.normalizePath(fallbackPath); + String fallbackAbsPath = WebappHelper.getContextRoot() + STATIC_DIR_NAME + fallbackPath; + staticFile = new File(fallbackAbsPath); + if (!staticFile.exists()) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + // log as error, file exists but wrongly mapped + log.warn("File exists but not mapped using version - use StaticMediaDispatch methods to create URL of static files! invalid URI::" + request.getRequestURI(), null); + } + } + + if (debug) { + log.debug("Serving resource URI::" + request.getRequestURI(), null); + } + + MediaResource resource = new FileMediaResource(staticFile); + ServletUtil.serveResource(request, response, resource); + } +} diff --git a/src/main/java/org/olat/core/util/CodeHelper.java b/src/main/java/org/olat/core/util/CodeHelper.java index a4708878d4f..7e75e72703a 100644 --- a/src/main/java/org/olat/core/util/CodeHelper.java +++ b/src/main/java/org/olat/core/util/CodeHelper.java @@ -44,7 +44,7 @@ public class CodeHelper { private static Integer nodeId; private CodeHelper(Integer nodeId) { - this.nodeId = nodeId; + CodeHelper.nodeId = nodeId; ramid = new AtomicLong( ((long)nodeId) * 1000000000 ); } diff --git a/src/main/java/org/olat/core/util/Encoder.java b/src/main/java/org/olat/core/util/Encoder.java index e5370a00942..a79136a1f42 100644 --- a/src/main/java/org/olat/core/util/Encoder.java +++ b/src/main/java/org/olat/core/util/Encoder.java @@ -54,18 +54,25 @@ public class Encoder { private static final OLog log = Tracing.createLoggerFor(Encoder.class); public enum Algorithm { - md5("MD5", 1), - sha1("SHA-1", 100), - sha256("SHA-256", 100), - sha512("SHA-512", 100), - pbkdf2("PBKDF2WithHmacSHA1", 20000); + md5("MD5", 1, true), + md5_noSalt("MD5", 1, false), + sha1("SHA-1", 100, true), + sha256("SHA-256", 100, true), + sha512("SHA-512", 100, true), + pbkdf2("PBKDF2WithHmacSHA1", 20000, true); + private final boolean salted; private final int iterations; private final String algorithm; - private Algorithm(String algorithm, int iterations) { + private Algorithm(String algorithm, int iterations, boolean salted) { this.algorithm = algorithm; this.iterations = iterations; + this.salted = salted; + } + + public boolean isSalted() { + return salted; } public String getAlgorithm() { diff --git a/src/main/java/org/olat/core/util/FileUtils.java b/src/main/java/org/olat/core/util/FileUtils.java index b88751c9551..da9ad175027 100644 --- a/src/main/java/org/olat/core/util/FileUtils.java +++ b/src/main/java/org/olat/core/util/FileUtils.java @@ -480,7 +480,6 @@ public class FileUtils { * @param target OutputStream, left open. * @return true if the copy was successful. */ - @Deprecated public static boolean copy(InputStream source, OutputStream target) { try { diff --git a/src/main/java/org/olat/core/util/URIHelper.java b/src/main/java/org/olat/core/util/URIHelper.java index 532d96d0c47..fe8324fe6d5 100644 --- a/src/main/java/org/olat/core/util/URIHelper.java +++ b/src/main/java/org/olat/core/util/URIHelper.java @@ -64,42 +64,6 @@ public class URIHelper { parseQuery(); } - /** - * Add a single parameter. - */ - public URIHelper addParameter(String name, String value) { - // ignore undefined values - if (name == null || value == null) return this; - - modified = true; - params.put(name, value); - return this; - } - - /** - * Add parameters from a map. - */ - public URIHelper addParameters(Map newParams) { - // nothing to do if there are no parameters - if (newParams == null || newParams.isEmpty()) return this; - - modified = true; - for (Iterator i = newParams.keySet().iterator(); i.hasNext();) { - String name = (String) i.next(); - Object value = newParams.get(name); - - if (value == null) continue; - - if (value instanceof Object[]) { - // add the first array element - params.put(name, ((Object[])value)[0].toString()); - } else { - params.put(name, value.toString()); - } - } - return this; - } - /** * Remove a single parameter, if exists. */ diff --git a/src/main/java/org/olat/core/util/UserSession.java b/src/main/java/org/olat/core/util/UserSession.java index 5e7091c5bef..2901aaa3b7f 100644 --- a/src/main/java/org/olat/core/util/UserSession.java +++ b/src/main/java/org/olat/core/util/UserSession.java @@ -82,6 +82,7 @@ public class UserSession implements HttpSessionBindingListener, GenericEventList */ private transient Map<String,Object> nonClearedStore = new HashMap<String,Object>(); private boolean authenticated = false; + private boolean savedSession = false; private transient Preferences guiPreferences; private transient EventBus singleUserSystemBus; private List<String> chats; @@ -103,6 +104,7 @@ public class UserSession implements HttpSessionBindingListener, GenericEventList store = new HashMap<String,Object>(4); nonClearedStore = new HashMap<String,Object>(); singleUserSystemBus = CoordinatorManager.getInstance().getCoordinator().createSingleUserInstance(); + savedSession = true; return this; } @@ -117,6 +119,14 @@ public class UserSession implements HttpSessionBindingListener, GenericEventList this.authenticated = authenticated; } + public boolean isSavedSession() { + return savedSession; + } + + public void setSavedSession(boolean savedSession) { + this.savedSession = savedSession; + } + public Map<String,Object> getStore() { return store; } diff --git a/src/main/java/org/olat/core/util/ValidationStatusHelper.java b/src/main/java/org/olat/core/util/ValidationStatusHelper.java deleted file mode 100644 index eaabf6a168c..00000000000 --- a/src/main/java/org/olat/core/util/ValidationStatusHelper.java +++ /dev/null @@ -1,73 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -* <p> -*/ -package org.olat.core.util; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * Description:<br> - * TODO: patrickb Class Description for ValidationStatusHelper - * - * <P> - * Initial Date: 04.12.2006 <br> - * @author patrickb - */ -public class ValidationStatusHelper { - - public static ValidationStatus[] sort(List statusList) { - Collections.sort(statusList, new ErrorsGtWarningGtInfo()); - /* - * remove NOERRORS size bigger 1; NOERROR -> Level == OFF > SEVERE > WARNING > - * INFO - */ - if (statusList.size() > 1) { - for(int i=0;i<statusList.size();i++) { - if(statusList.get(i)==ValidationStatus.NOERROR) { - //aha found one to remove. Remove shifts all elements to the left, e.g. subtracts one of the indices - statusList.remove(i); - //thus we have to loop on place to remove all NOERRORS which shift to the ith place. - while(statusList.get(i)==ValidationStatus.NOERROR) { - statusList.remove(i); - } - } - } - } - ValidationStatus[] retVal = new ValidationStatus[statusList.size()]; - retVal = (ValidationStatus[]) statusList.toArray(retVal); - return retVal; - } - - private static class ErrorsGtWarningGtInfo implements Comparator { - public int compare(Object o1, Object o2) { - ValidationStatus s1 = (ValidationStatus) o1; - ValidationStatus s2 = (ValidationStatus) o2; - return s2.getLevel().intValue() - s1.getLevel().intValue(); - } - } - -} diff --git a/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml b/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml index 64fb55b9a2d..f779f7a0bac 100644 --- a/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml +++ b/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml @@ -60,7 +60,8 @@ <entry key="server_securePort" value="${server.port.ssl}" /> <entry key="server_insecurePort" value="${server.port}" /> </map> - </property> + </property> + <property name="legacyContext" value="${server.legacy.context}" /> <!-- true enable debug mode, not for production mode but for productive hours ;) --> <property name="debug" value="${olat.debug}" /> <!-- if readOnlyDebug, then no file changes can be made in the debug mode --> diff --git a/src/main/java/org/olat/core/util/httpclient/EasySSLSocketFactory.java b/src/main/java/org/olat/core/util/httpclient/EasySSLSocketFactory.java index 182401dfe64..88b65cbd2e6 100644 --- a/src/main/java/org/olat/core/util/httpclient/EasySSLSocketFactory.java +++ b/src/main/java/org/olat/core/util/httpclient/EasySSLSocketFactory.java @@ -1,5 +1,3 @@ -package org.olat.core.util.httpclient; - /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +16,8 @@ package org.olat.core.util.httpclient; * specific language governing permissions and limitations * under the License. */ +package org.olat.core.util.httpclient; + import java.io.IOException; import java.net.InetSocketAddress; diff --git a/src/main/java/org/olat/core/util/i18n/ui/I18nConfigSubNewLangController.java b/src/main/java/org/olat/core/util/i18n/ui/I18nConfigSubNewLangController.java index e937514971f..21a7120d8cc 100644 --- a/src/main/java/org/olat/core/util/i18n/ui/I18nConfigSubNewLangController.java +++ b/src/main/java/org/olat/core/util/i18n/ui/I18nConfigSubNewLangController.java @@ -28,7 +28,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; @@ -215,7 +215,7 @@ class I18nConfigSubNewLangController extends FormBasicController { currUser = UserManager.getInstance().loadUserByKey(currUser.getKey()); currUser.getPreferences().setLanguage(localeKey); UserManager.getInstance().updateUser(currUser); - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } @Override diff --git a/src/main/java/org/olat/core/util/servlets/DOMWriter.java b/src/main/java/org/olat/core/util/servlets/DOMWriter.java deleted file mode 100644 index 9839dcd0874..00000000000 --- a/src/main/java/org/olat/core/util/servlets/DOMWriter.java +++ /dev/null @@ -1,316 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.core.util.servlets; - - -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -/** - * A sample DOM writer. This sample program illustrates how to - * traverse a DOM tree in order to print a document that is parsed. - */ -public class DOMWriter { - - // - // Data - // - - /** Default Encoding */ - private static String - PRINTWRITER_ENCODING = "UTF8"; - - private static String MIME2JAVA_ENCODINGS[] = - { "Default", "UTF-8", "US-ASCII", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", - "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-2022-JP", - "SHIFT_JIS", "EUC-JP","GB2312", "BIG5", "EUC-KR", "ISO-2022-KR", "KOI8-R", "EBCDIC-CP-US", - "EBCDIC-CP-CA", "EBCDIC-CP-NL", "EBCDIC-CP-DK", "EBCDIC-CP-NO", "EBCDIC-CP-FI", "EBCDIC-CP-SE", - "EBCDIC-CP-IT", "EBCDIC-CP-ES", "EBCDIC-CP-GB", "EBCDIC-CP-FR", "EBCDIC-CP-AR1", - "EBCDIC-CP-HE", "EBCDIC-CP-CH", "EBCDIC-CP-ROECE","EBCDIC-CP-YU", - "EBCDIC-CP-IS", "EBCDIC-CP-AR2", "UTF-16" - }; - - - /** Print writer. */ - protected PrintWriter out; - - /** Canonical output. */ - protected boolean canonical; - - - public DOMWriter(String encoding, boolean canonical) - throws UnsupportedEncodingException { - out = new PrintWriter(new OutputStreamWriter(System.out, encoding)); - this.canonical = canonical; - } // <init>(String,boolean) - - // - // Constructors - // - - /** Default constructor. */ - public DOMWriter(boolean canonical) throws UnsupportedEncodingException { - this( getWriterEncoding(), canonical); - } - - public DOMWriter(Writer writer, boolean canonical) { - out = new PrintWriter(writer); - this.canonical = canonical; - } - - public static String getWriterEncoding( ) { - return (PRINTWRITER_ENCODING); - }// getWriterEncoding - - public static void setWriterEncoding( String encoding ) { - if( encoding.equalsIgnoreCase( "DEFAULT" ) ) - PRINTWRITER_ENCODING = "UTF8"; - else if( encoding.equalsIgnoreCase( "UTF-16" ) ) - PRINTWRITER_ENCODING = "Unicode"; - else - PRINTWRITER_ENCODING = MIME2Java.convert( encoding ); - }// setWriterEncoding - - - public static boolean isValidJavaEncoding( String encoding ) { - for ( int i = 0; i < MIME2JAVA_ENCODINGS.length; i++ ) - if ( encoding.equals( MIME2JAVA_ENCODINGS[i] ) ) - return (true); - - return (false); - }// isValidJavaEncoding - - - /** Prints the specified node, recursively. */ - public void print(Node node) { - - // is there anything to do? - if ( node == null ) { - return; - } - - int type = node.getNodeType(); - switch ( type ) { - // print document - case Node.DOCUMENT_NODE: { - if ( !canonical ) { - String Encoding = getWriterEncoding(); - if( Encoding.equalsIgnoreCase( "DEFAULT" ) ) - Encoding = "UTF-8"; - else if( Encoding.equalsIgnoreCase( "Unicode" ) ) - Encoding = "UTF-16"; - else - Encoding = MIME2Java.reverse( Encoding ); - - out.println("<?xml version=\"1.0\" encoding=\""+ - Encoding + "\"?>"); - } - print(((Document)node).getDocumentElement()); - out.flush(); - break; - } - - // print element with attributes - case Node.ELEMENT_NODE: { - out.print('<'); - out.print(node.getNodeName()); - Attr attrs[] = sortAttributes(node.getAttributes()); - for ( int i = 0; i < attrs.length; i++ ) { - Attr attr = attrs[i]; - out.print(' '); - out.print(attr.getNodeName()); - out.print("=\""); - out.print(normalize(attr.getNodeValue())); - out.print('"'); - } - out.print('>'); - NodeList children = node.getChildNodes(); - if ( children != null ) { - int len = children.getLength(); - for ( int i = 0; i < len; i++ ) { - print(children.item(i)); - } - } - break; - } - - // handle entity reference nodes - case Node.ENTITY_REFERENCE_NODE: { - if ( canonical ) { - NodeList children = node.getChildNodes(); - if ( children != null ) { - int len = children.getLength(); - for ( int i = 0; i < len; i++ ) { - print(children.item(i)); - } - } - } else { - out.print('&'); - out.print(node.getNodeName()); - out.print(';'); - } - break; - } - - // print cdata sections - case Node.CDATA_SECTION_NODE: { - if ( canonical ) { - out.print(normalize(node.getNodeValue())); - } else { - out.print("<![CDATA["); - out.print(node.getNodeValue()); - out.print("]]>"); - } - break; - } - - // print text - case Node.TEXT_NODE: { - out.print(normalize(node.getNodeValue())); - break; - } - - // print processing instruction - case Node.PROCESSING_INSTRUCTION_NODE: { - out.print("<?"); - out.print(node.getNodeName()); - String data = node.getNodeValue(); - if ( data != null && data.length() > 0 ) { - out.print(' '); - out.print(data); - } - out.print("?>"); - break; - } - } - - if ( type == Node.ELEMENT_NODE ) { - out.print("</"); - out.print(node.getNodeName()); - out.print('>'); - } - - out.flush(); - - } // print(Node) - - /** Returns a sorted list of attributes. */ - protected Attr[] sortAttributes(NamedNodeMap attrs) { - - int len = (attrs != null) ? attrs.getLength() : 0; - Attr array[] = new Attr[len]; - for ( int i = 0; i < len; i++ ) { - array[i] = (Attr)attrs.item(i); - } - for ( int i = 0; i < len - 1; i++ ) { - String name = array[i].getNodeName(); - int index = i; - for ( int j = i + 1; j < len; j++ ) { - String curName = array[j].getNodeName(); - if ( curName.compareTo(name) < 0 ) { - name = curName; - index = j; - } - } - if ( index != i ) { - Attr temp = array[i]; - array[i] = array[index]; - array[index] = temp; - } - } - - return (array); - - } // sortAttributes(NamedNodeMap):Attr[] - - - /** Normalizes the given string. */ - protected String normalize(String s) { - StringBuilder str = new StringBuilder(); - - int len = (s != null) ? s.length() : 0; - for ( int i = 0; i < len; i++ ) { - char ch = s.charAt(i); - switch ( ch ) { - case '<': { - str.append("<"); - break; - } - case '>': { - str.append(">"); - break; - } - case '&': { - str.append("&"); - break; - } - case '"': { - str.append("""); - break; - } - case '\r': - case '\n': { - if ( canonical ) { - str.append("&#"); - str.append(Integer.toString(ch)); - str.append(';'); - break; - } - // else, default append char - } - default: { - str.append(ch); - } - } - } - - return (str.toString()); - - } // normalize(String):String - - /*private static void printValidJavaEncoding() { - System.err.println( " ENCODINGS:" ); - System.err.print( " " ); - for( int i = 0; - i < MIME2JAVA_ENCODINGS.length; i++) { - System.err.print( MIME2JAVA_ENCODINGS[i] + " " ); - if( (i % 7 ) == 0 ){ - System.err.println(); - System.err.print( " " ); - } - } - - } // printJavaEncoding() - */ - -} diff --git a/src/main/java/org/olat/core/util/servlets/FastHttpDateFormat.java b/src/main/java/org/olat/core/util/servlets/FastHttpDateFormat.java deleted file mode 100644 index 7b9244fb5a0..00000000000 --- a/src/main/java/org/olat/core/util/servlets/FastHttpDateFormat.java +++ /dev/null @@ -1,122 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.core.util.servlets; - - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Utility class to generate HTTP dates. - * - * @author Remy Maucherat - */ -public final class FastHttpDateFormat { - - - // -------------------------------------------------------------- Variables - - - /** - * HTTP date format. - */ - protected static SimpleDateFormat format = - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); - - - protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT"); - - - /** - * GMT timezone - all HTTP dates are on GMT - */ - static { - format.setTimeZone(gmtZone); - } - - - /** - * Instant on which the currentDate object was generated. - */ - protected static long currentDateGenerated = 0L; - - - /** - * Current formatted date. - */ - protected static String currentDate = null; - - - /** - * Date cache. - */ - protected static HashMap dateCache = new HashMap(); - - - // --------------------------------------------------------- Public Methods - - - /** - * Get the current date in HTTP format. - */ - public static String getCurrentDate() { - - long now = System.currentTimeMillis(); - if ((now - currentDateGenerated) > 1000) { - synchronized (format) { //o_clusterOK by:fj - if ((now - currentDateGenerated) > 1000) { - currentDateGenerated = now; - currentDate = format.format(new Date(now)); - } - } - } - return currentDate; - - } - - - /** - * Get the HTTP format of the specified date. - */ - public static String getDate(Date date) { - - String cachedDate = (String) dateCache.get(date); - if (cachedDate != null) - return cachedDate; - - String newDate = null; - synchronized (format) { //o_clusterOK by:fj - newDate = format.format(date); - dateCache.put(date, newDate); - } - return newDate; - - } - - -} diff --git a/src/main/java/org/olat/core/util/servlets/Globals.java b/src/main/java/org/olat/core/util/servlets/Globals.java deleted file mode 100644 index fc5019d65a3..00000000000 --- a/src/main/java/org/olat/core/util/servlets/Globals.java +++ /dev/null @@ -1,253 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.core.util.servlets; - - -/** - * Global constants that are applicable to multiple packages within Catalina. - * - * @author Craig R. McClanahan - */ - -public final class Globals { - - - /** - * The request attribute under which we store the array of X509Certificate - * objects representing the certificate chain presented by our client, - * if any. - */ - public static final String CERTIFICATES_ATTR = - "javax.servlet.request.X509Certificate"; - - /** - * SSL Certificate Request Attributite. - */ - public static final String SSL_CERTIFICATE_ATTR = "org.apache.coyote.request.X509Certificate"; - - /** - * The request attribute under which we store the name of the cipher suite - * being used on an SSL connection (as an object of type - * java.lang.String). - */ - public static final String CIPHER_SUITE_ATTR = - "javax.servlet.request.cipher_suite"; - - - /** - * The servlet context attribute under which we store the class loader - * used for loading servlets (as an object of type java.lang.ClassLoader). - */ - public static final String CLASS_LOADER_ATTR = - "org.apache.catalina.classloader"; - - - /** - * The JNDI directory context which is associated with the context. This - * context can be used to manipulate static files. - */ - public static final String RESOURCES_ATTR = - "org.apache.catalina.resources"; - - - /** - * The servlet context attribute under which we store the class path - * for our application class loader (as an object of type String), - * delimited with the appropriate path delimiter for this platform. - */ - public static final String CLASS_PATH_ATTR = - "org.apache.catalina.jsp_classpath"; - - - /** - * The request attribute under which the original context path is stored - * on an included dispatcher request. - */ - public static final String CONTEXT_PATH_ATTR = - "javax.servlet.include.context_path"; - - - /** - * The request attribute under which we forward a Java exception - * (as an object of type Throwable) to an error page. - */ - public static final String EXCEPTION_ATTR = - "javax.servlet.error.exception"; - - - /** - * The request attribute under which we forward the request URI - * (as an object of type String) of the page on which an error occurred. - */ - public static final String EXCEPTION_PAGE_ATTR = - "javax.servlet.error.request_uri"; - - - /** - * The request attribute under which we forward a Java exception type - * (as an object of type Class) to an error page. - */ - public static final String EXCEPTION_TYPE_ATTR = - "javax.servlet.error.exception_type"; - - - /** - * The request attribute under which we forward an HTTP status message - * (as an object of type STring) to an error page. - */ - public static final String ERROR_MESSAGE_ATTR = - "javax.servlet.error.message"; - - - /** - * The request attribute under which the Invoker servlet will store - * the invoking servlet path, if it was used to execute a servlet - * indirectly instead of through a servlet mapping. - */ - public static final String INVOKED_ATTR = - "org.apache.catalina.INVOKED"; - - - /** - * The request attribute under which we expose the value of the - * <code><jsp-file></code> value associated with this servlet, - * if any. - */ - public static final String JSP_FILE_ATTR = - "org.apache.catalina.jsp_file"; - - - /** - * The request attribute under which we store the key size being used for - * this SSL connection (as an object of type java.lang.Integer). - */ - public static final String KEY_SIZE_ATTR = - "javax.servlet.request.key_size"; - - - /** - * The servlet context attribute under which the managed bean Registry - * will be stored for privileged contexts (if enabled). - */ - public static final String MBEAN_REGISTRY_ATTR = - "org.apache.catalina.Registry"; - - - /** - * The servlet context attribute under which the MBeanServer will be stored - * for privileged contexts (if enabled). - */ - public static final String MBEAN_SERVER_ATTR = - "org.apache.catalina.MBeanServer"; - - - /** - * The request attribute under which we store the servlet name on a - * named dispatcher request. - */ - public static final String NAMED_DISPATCHER_ATTR = - "org.apache.catalina.NAMED"; - - - /** - * The request attribute under which the original path info is stored - * on an included dispatcher request. - */ - public static final String PATH_INFO_ATTR = - "javax.servlet.include.path_info"; - - - /** - * The request attribute under which the original query string is stored - * on an included dispatcher request. - */ - public static final String QUERY_STRING_ATTR = - "javax.servlet.include.query_string"; - - - /** - * The request attribute under which the original request URI is stored - * on an included dispatcher request. - */ - public static final String REQUEST_URI_ATTR = - "javax.servlet.include.request_uri"; - - - /** - * The request attribute under which we forward a servlet name to - * an error page. - */ - public static final String SERVLET_NAME_ATTR = - "javax.servlet.error.servlet_name"; - - - /** - * The request attribute under which the original servlet path is stored - * on an included dispatcher request. - */ - public static final String SERVLET_PATH_ATTR = - "javax.servlet.include.servlet_path"; - - - /** - * The name of the cookie used to pass the session identifier back - * and forth with the client. - */ - public static final String SESSION_COOKIE_NAME = "JSESSIONID"; - - - /** - * The name of the path parameter used to pass the session identifier - * back and forth with the client. - */ - public static final String SESSION_PARAMETER_NAME = "jsessionid"; - - - /** - * The request attribute under which we forward an HTTP status code - * (as an object of type Integer) to an error page. - */ - public static final String STATUS_CODE_ATTR = - "javax.servlet.error.status_code"; - - - /** - * The servlet context attribute under which we record the set of - * welcome files (as an object of type String[]) for this application. - */ - public static final String WELCOME_FILES_ATTR = - "org.apache.catalina.WELCOME_FILES"; - - - /** - * The servlet context attribute under which we store a temporary - * working directory (as an object of type File) for use by servlets - * within this web application. - */ - public static final String WORK_DIR_ATTR = - "javax.servlet.context.tempdir"; - - -} diff --git a/src/main/java/org/olat/core/util/servlets/MIME2Java.java b/src/main/java/org/olat/core/util/servlets/MIME2Java.java deleted file mode 100644 index 09d3c4291f7..00000000000 --- a/src/main/java/org/olat/core/util/servlets/MIME2Java.java +++ /dev/null @@ -1,612 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.core.util.servlets; - - -import java.util.Hashtable; -import java.util.Map; - -/** - * MIME2Java is a convenience class which handles conversions between MIME charset names - * and Java encoding names. - * <p>The supported XML encodings are the intersection of XML-supported code sets and those - * supported in JDK 1.1. - * <p>MIME charset names are used on <var>xmlEncoding</var> parameters to methods such - * as <code>TXDocument#setEncoding</code> and <code>DTD#setEncoding</code>. - * <p>Java encoding names are used on <var>encoding</var> parameters to - * methods such as <code>TXDocument#printWithFormat</code> and <code>DTD#printExternal</code>. - * <P> - * <TABLE BORDER="0" WIDTH="100%"> - * <TR> - * <TD WIDTH="33%"> - * <P ALIGN="CENTER"><B>Common Name</B> - * </TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER"><B>Use this name in XML files</B> - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER"><B>Name Type</B> - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER"><B>Xerces converts to this Java Encoder Name</B> - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">8 bit Unicode</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">UTF-8 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">UTF8 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin 1</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-1 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-1 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin 2</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-2 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-2 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin 3</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-3 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-3 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin 4</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-4 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-4 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin Cyrillic</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-5 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-5 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin Arabic</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-6 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-6 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin Greek</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-7 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-7 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin Hebrew</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-8 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-8 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">ISO Latin 5</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ISO-8859-9 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">ISO-8859-9 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: US</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-us - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp037 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Canada</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-ca - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp037 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Netherlands</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-nl - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp037 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Denmark</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-dk - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp277 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Norway</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-no - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp277 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Finland</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-fi - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp278 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Sweden</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-se - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp278 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Italy</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-it - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp280 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Spain, Latin America</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-es - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp284 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Great Britain</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-gb - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp285 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: France</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-fr - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp297 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Arabic</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-ar1 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp420 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Hebrew</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-he - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp424 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Switzerland</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-ch - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp500 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Roece</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-roece - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp870 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Yogoslavia</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-yu - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp870 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Iceland</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-is - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp871 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">EBCDIC: Urdu</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">ebcdic-cp-ar2 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">IANA - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">cp918 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Chinese for PRC, mixed 1/2 byte</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">gb2312 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">GB2312 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Extended Unix Code, packed for Japanese</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">euc-jp - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">eucjis - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Japanese: iso-2022-jp</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">iso-2020-jp - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">JIS - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Japanese: Shift JIS</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">Shift_JIS - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">SJIS - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Chinese: Big5</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">Big5 - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">Big5 - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Extended Unix Code, packed for Korean</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">euc-kr - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">iso2022kr - * </TD> - * </TR> - * <TR> - * <TD WIDTH="33%">Cyrillic</TD> - * <TD WIDTH="15%"> - * <P ALIGN="CENTER">koi8-r - * </TD> - * <TD WIDTH="12%"> - * <P ALIGN="CENTER">MIME - * </TD> - * <TD WIDTH="31%"> - * <P ALIGN="CENTER">koi8-r - * </TD> - * </TR> - * </TABLE> - * - * @author TAMURA Kent <kent@trl.ibm.co.jp> - */ -public class MIME2Java { - - static private Map<String,String> s_enchash; - static private Map<String,String> s_revhash; - - static { - s_enchash = new Hashtable<String,String>(); - // <preferred MIME name>, <Java encoding name> - s_enchash.put("UTF-8", "UTF8"); - s_enchash.put("US-ASCII", "8859_1"); // ? - s_enchash.put("ISO-8859-1", "8859_1"); - s_enchash.put("ISO-8859-2", "8859_2"); - s_enchash.put("ISO-8859-3", "8859_3"); - s_enchash.put("ISO-8859-4", "8859_4"); - s_enchash.put("ISO-8859-5", "8859_5"); - s_enchash.put("ISO-8859-6", "8859_6"); - s_enchash.put("ISO-8859-7", "8859_7"); - s_enchash.put("ISO-8859-8", "8859_8"); - s_enchash.put("ISO-8859-9", "8859_9"); - s_enchash.put("ISO-2022-JP", "JIS"); - s_enchash.put("SHIFT_JIS", "SJIS"); - s_enchash.put("EUC-JP", "EUCJIS"); - s_enchash.put("GB2312", "GB2312"); - s_enchash.put("BIG5", "Big5"); - s_enchash.put("EUC-KR", "KSC5601"); - s_enchash.put("ISO-2022-KR", "ISO2022KR"); - s_enchash.put("KOI8-R", "KOI8_R"); - - s_enchash.put("EBCDIC-CP-US", "CP037"); - s_enchash.put("EBCDIC-CP-CA", "CP037"); - s_enchash.put("EBCDIC-CP-NL", "CP037"); - s_enchash.put("EBCDIC-CP-DK", "CP277"); - s_enchash.put("EBCDIC-CP-NO", "CP277"); - s_enchash.put("EBCDIC-CP-FI", "CP278"); - s_enchash.put("EBCDIC-CP-SE", "CP278"); - s_enchash.put("EBCDIC-CP-IT", "CP280"); - s_enchash.put("EBCDIC-CP-ES", "CP284"); - s_enchash.put("EBCDIC-CP-GB", "CP285"); - s_enchash.put("EBCDIC-CP-FR", "CP297"); - s_enchash.put("EBCDIC-CP-AR1", "CP420"); - s_enchash.put("EBCDIC-CP-HE", "CP424"); - s_enchash.put("EBCDIC-CP-CH", "CP500"); - s_enchash.put("EBCDIC-CP-ROECE", "CP870"); - s_enchash.put("EBCDIC-CP-YU", "CP870"); - s_enchash.put("EBCDIC-CP-IS", "CP871"); - s_enchash.put("EBCDIC-CP-AR2", "CP918"); - - // j:CNS11643 -> EUC-TW? - // ISO-2022-CN? ISO-2022-CN-EXT? - - s_revhash = new Hashtable<String,String>(); - // <Java encoding name>, <preferred MIME name> - s_revhash.put("UTF8", "UTF-8"); - //s_revhash.put("8859_1", "US-ASCII"); // ? - s_revhash.put("8859_1", "ISO-8859-1"); - s_revhash.put("8859_2", "ISO-8859-2"); - s_revhash.put("8859_3", "ISO-8859-3"); - s_revhash.put("8859_4", "ISO-8859-4"); - s_revhash.put("8859_5", "ISO-8859-5"); - s_revhash.put("8859_6", "ISO-8859-6"); - s_revhash.put("8859_7", "ISO-8859-7"); - s_revhash.put("8859_8", "ISO-8859-8"); - s_revhash.put("8859_9", "ISO-8859-9"); - s_revhash.put("JIS", "ISO-2022-JP"); - s_revhash.put("SJIS", "Shift_JIS"); - s_revhash.put("EUCJIS", "EUC-JP"); - s_revhash.put("GB2312", "GB2312"); - s_revhash.put("BIG5", "Big5"); - s_revhash.put("KSC5601", "EUC-KR"); - s_revhash.put("ISO2022KR", "ISO-2022-KR"); - s_revhash.put("KOI8_R", "KOI8-R"); - - s_revhash.put("CP037", "EBCDIC-CP-US"); - s_revhash.put("CP037", "EBCDIC-CP-CA"); - s_revhash.put("CP037", "EBCDIC-CP-NL"); - s_revhash.put("CP277", "EBCDIC-CP-DK"); - s_revhash.put("CP277", "EBCDIC-CP-NO"); - s_revhash.put("CP278", "EBCDIC-CP-FI"); - s_revhash.put("CP278", "EBCDIC-CP-SE"); - s_revhash.put("CP280", "EBCDIC-CP-IT"); - s_revhash.put("CP284", "EBCDIC-CP-ES"); - s_revhash.put("CP285", "EBCDIC-CP-GB"); - s_revhash.put("CP297", "EBCDIC-CP-FR"); - s_revhash.put("CP420", "EBCDIC-CP-AR1"); - s_revhash.put("CP424", "EBCDIC-CP-HE"); - s_revhash.put("CP500", "EBCDIC-CP-CH"); - s_revhash.put("CP870", "EBCDIC-CP-ROECE"); - s_revhash.put("CP870", "EBCDIC-CP-YU"); - s_revhash.put("CP871", "EBCDIC-CP-IS"); - s_revhash.put("CP918", "EBCDIC-CP-AR2"); - } - - private MIME2Java() { - super(); - } - - /** - * Convert a MIME charset name, also known as an XML encoding name, to a Java encoding name. - * @param mimeCharsetName Case insensitive MIME charset name: <code>UTF-8, US-ASCII, ISO-8859-1, - * ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6, - * ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-2022-JP, Shift_JIS, - * EUC-JP, GB2312, Big5, EUC-KR, ISO-2022-KR, KOI8-R, - * EBCDIC-CP-US, EBCDIC-CP-CA, EBCDIC-CP-NL, EBCDIC-CP-DK, - * EBCDIC-CP-NO, EBCDIC-CP-FI, EBCDIC-CP-SE, EBCDIC-CP-IT, - * EBCDIC-CP-ES, EBCDIC-CP-GB, EBCDIC-CP-FR, EBCDIC-CP-AR1, - * EBCDIC-CP-HE, EBCDIC-CP-CH, EBCDIC-CP-ROECE, EBCDIC-CP-YU, - * EBCDIC-CP-IS and EBCDIC-CP-AR2</code>. - * @return Java encoding name, or <var>null</var> if <var>mimeCharsetName</var> - * is unknown. - * @see #reverse - */ - public static String convert(String mimeCharsetName) { - return (String)s_enchash.get(mimeCharsetName.toUpperCase()); - } - - /** - * Convert a Java encoding name to MIME charset name. - * Available values of <i>encoding</i> are "UTF8", "8859_1", "8859_2", "8859_3", "8859_4", - * "8859_5", "8859_6", "8859_7", "8859_8", "8859_9", "JIS", "SJIS", "EUCJIS", - * "GB2312", "BIG5", "KSC5601", "ISO2022KR", "KOI8_R", "CP037", "CP277", "CP278", - * "CP280", "CP284", "CP285", "CP297", "CP420", "CP424", "CP500", "CP870", "CP871" and "CP918". - * @param encoding Case insensitive Java encoding name: <code>UTF8, 8859_1, 8859_2, 8859_3, - * 8859_4, 8859_5, 8859_6, 8859_7, 8859_8, 8859_9, JIS, SJIS, EUCJIS, - * GB2312, BIG5, KSC5601, ISO2022KR, KOI8_R, CP037, CP277, CP278, - * CP280, CP284, CP285, CP297, CP420, CP424, CP500, CP870, CP871 - * and CP918</code>. - * @return MIME charset name, or <var>null</var> if <var>encoding</var> is unknown. - * @see #convert - */ - public static String reverse(String encoding) { - return (String)s_revhash.get(encoding.toUpperCase()); - } -} diff --git a/src/main/java/org/olat/core/util/servlets/ServerInfo.java b/src/main/java/org/olat/core/util/servlets/ServerInfo.java deleted file mode 100644 index 8e850b33d2a..00000000000 --- a/src/main/java/org/olat/core/util/servlets/ServerInfo.java +++ /dev/null @@ -1,82 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <p> -* Licensed under the Apache License, Version 2.0 (the "License"); <br> -* you may not use this file except in compliance with the License.<br> -* You may obtain a copy of the License at -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <p> -* Unless required by applicable law or agreed to in writing,<br> -* software distributed under the License is distributed on an "AS IS" BASIS, <br> -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> -* See the License for the specific language governing permissions and <br> -* limitations under the License. -* <p> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.core.util.servlets; - - - -import java.io.InputStream; -import java.util.Properties; - - -/** - * Simple utility module to make it easy to plug in the server identifier - * when integrating Tomcat. - * - * @author Craig R. McClanahan - */ - -public class ServerInfo { - - - // ------------------------------------------------------- Static Variables - - - /** - * The server information String with which we identify ourselves. - */ - private static String serverInfo = null; - - static { - - try { - InputStream is = ServerInfo.class.getResourceAsStream - ("/org/apache/catalina/util/ServerInfo.properties"); - Properties props = new Properties(); - props.load(is); - is.close(); - serverInfo = props.getProperty("server.info"); - } catch (Throwable t) { - - } - if (serverInfo == null) - serverInfo = "Apache Tomcat"; - - } - - - // --------------------------------------------------------- Public Methods - - - /** - * Return the server identification for this version of Tomcat. - */ - public static String getServerInfo() { - - return (serverInfo); - - } - - -} diff --git a/src/main/java/org/olat/core/util/servlets/URLEncoder.java b/src/main/java/org/olat/core/util/servlets/URLEncoder.java index 86648f9e885..572f87b6547 100644 --- a/src/main/java/org/olat/core/util/servlets/URLEncoder.java +++ b/src/main/java/org/olat/core/util/servlets/URLEncoder.java @@ -121,7 +121,7 @@ public class URLEncoder { } public void addSafeCharacter( char c ) { - safeCharacters.set( c ); + safeCharacters.set( c ); } public String encode( String path ) { diff --git a/src/main/java/org/olat/core/util/vfs/VFSStatus.java b/src/main/java/org/olat/core/util/vfs/VFSStatus.java index 0d806fc2df2..bcc451164b5 100644 --- a/src/main/java/org/olat/core/util/vfs/VFSStatus.java +++ b/src/main/java/org/olat/core/util/vfs/VFSStatus.java @@ -37,13 +37,8 @@ package org.olat.core.util.vfs; */ public class VFSStatus { - /** - * - */ public VFSStatus() { - super(); - // TODO Auto-generated constructor stub + // } - } diff --git a/src/main/java/org/olat/dispatcher/AuthenticatedDispatcher.java b/src/main/java/org/olat/dispatcher/AuthenticatedDispatcher.java index 9907ccbba40..3a7901c0e92 100644 --- a/src/main/java/org/olat/dispatcher/AuthenticatedDispatcher.java +++ b/src/main/java/org/olat/dispatcher/AuthenticatedDispatcher.java @@ -35,7 +35,7 @@ import org.olat.NewControllerFactory; import org.olat.basesecurity.AuthHelper; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.WindowSettings; @@ -81,14 +81,16 @@ public class AuthenticatedDispatcher implements Dispatcher { } /** - * Main method called by DispatcherAction. This processess all requests for + * Main method called by OpenOLATServlet. This processess all requests for * authenticated users. * * @param request * @param response * @param uriPrefix */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); long startExecute = 0; if ( log.isDebug() ) { startExecute = System.currentTimeMillis(); @@ -108,7 +110,7 @@ public class AuthenticatedDispatcher implements Dispatcher { if(log.isDebug()){ log.debug("Bad Request "+request.getPathInfo()); } - DispatcherAction.sendBadRequest(request.getPathInfo(), response); + DispatcherModule.sendBadRequest(request.getPathInfo(), response); return; } @@ -124,7 +126,7 @@ public class AuthenticatedDispatcher implements Dispatcher { } String guestAccess = ureq.getParameter(GUEST); if (guestAccess == null || !LoginModule.isGuestLoginLinksEnabled()) { - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } else if (guestAccess.equals(TRUE)) { // try to log in as anonymous @@ -139,9 +141,9 @@ public class AuthenticatedDispatcher implements Dispatcher { int loginStatus = AuthHelper.doAnonymousLogin(ureq, guestLoc); if ( loginStatus != AuthHelper.LOGIN_OK) { if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) { - DispatcherAction.redirectToServiceNotAvailable(response); + DispatcherModule.redirectToServiceNotAvailable(response); } - DispatcherAction.redirectToDefaultDispatcher(response); // error, redirect to login screen + DispatcherModule.redirectToDefaultDispatcher(response); // error, redirect to login screen return; } // else now logged in as anonymous user, continue @@ -164,13 +166,13 @@ public class AuthenticatedDispatcher implements Dispatcher { } } } - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } SessionInfo sessionInfo = usess.getSessionInfo(); if (sessionInfo==null) { - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } @@ -183,60 +185,22 @@ public class AuthenticatedDispatcher implements Dispatcher { // to avoid a endless redirect, remove the guest parameter if any // this can happen if a guest has cookies disabled String url = new URIHelper(origUrl).removeParameter(GUEST).toString(); - DispatcherAction.redirectTo(response, url); - return; - } - String businessPath = (String) usess.removeEntryFromNonClearedStore(AUTHDISPATCHER_BUSINESSPATH); - if (businessPath != null) { - BusinessControl bc = BusinessControlFactory.getInstance().createFromString(businessPath); - ChiefController cc = (ChiefController) Windows.getWindows(usess).getAttribute("AUTHCHIEFCONTROLLER"); - WindowControl wControl = cc.getWindowControl(); - - String wSettings = (String) usess.removeEntryFromNonClearedStore(WINDOW_SETTINGS); - if(wSettings != null) { - WindowSettings settings = WindowSettings.parse(wSettings); - wControl.getWindowBackOffice().setWindowSettings(settings); - } - - WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, wControl); - NewControllerFactory.getInstance().launch(ureq, bwControl); - // render the window - Window w = cc.getWindow(); - w.dispatchRequest(ureq, true); // renderOnly - return; - } - - if (ureq.isValidDispatchURI()) { - // valid uri for dispatching (has timestamp, componentid and windowid) - Windows ws = Windows.getWindows(ureq); - Window window = ws.getWindow(ureq); - if (window == null) { - // If no window, this is probably a stale link. send not - // found - // note: do not redirect to login since this wastes a new - // window each time since we are in an authenticated session - // -> a content packaging with wrong links e.g. /css/my.css - // wastes all the windows - DispatcherAction.sendNotFound(request.getRequestURI(), response); - return; - } - long startDispatchRequest = 0; - if (log.isDebug()) { - startDispatchRequest = System.currentTimeMillis(); - } - window.dispatchRequest(ureq); - if ( log.isDebug() ) { - long durationDispatchRequest = System.currentTimeMillis() - startDispatchRequest; - log.debug("Perf-Test: window=" + window); - log.debug("Perf-Test: durationDispatchRequest=" + durationDispatchRequest); - } + DispatcherModule.redirectTo(response, url); } else { - System.out.println("ERROR"); + String businessPath = (String) usess.removeEntryFromNonClearedStore(AUTHDISPATCHER_BUSINESSPATH); + if (businessPath != null) { + processBusinessPath(businessPath, ureq, usess); + } else if (ureq.isValidDispatchURI()) { + // valid uri for dispatching (has timestamp, componentid and windowid) + processValidDispatchURI(ureq, usess, request, response); + } else { + log.error("Invalid URI in AuthenticatedDispatcher: " + request.getRequestURI()); + } } } catch (Throwable th) { // Do not log as Warn or Error here, log as ERROR in MsgFactory => ExceptionWindowController throws an OLATRuntimeException log.debug("handleError in AuthenticatedDispatcher throwable=" + th); - DispatcherAction.handleError(); + DispatcherModule.handleError(); ChiefController msgcc = MsgFactory.createMessageChiefController(ureq, th); // the controller's window must be failsafe also msgcc.getWindow().dispatchRequest(ureq, true); @@ -250,4 +214,46 @@ public class AuthenticatedDispatcher implements Dispatcher { } } } + + private void processValidDispatchURI(UserRequest ureq, UserSession usess, HttpServletRequest request, HttpServletResponse response) { + Windows ws = Windows.getWindows(ureq); + Window window = ws.getWindow(ureq); + if (window == null) { + //probably a + if(usess.isSavedSession() && !usess.getHistoryStack().isEmpty()) { + DispatcherModule.redirectToDefaultDispatcher(response); + } else { + DispatcherModule.sendNotFound(request.getRequestURI(), response); + } + } else { + long startDispatchRequest = 0; + if (log.isDebug()) { + startDispatchRequest = System.currentTimeMillis(); + } + window.dispatchRequest(ureq); + if ( log.isDebug() ) { + long durationDispatchRequest = System.currentTimeMillis() - startDispatchRequest; + log.debug("Perf-Test: window=" + window); + log.debug("Perf-Test: durationDispatchRequest=" + durationDispatchRequest); + } + } + } + + private void processBusinessPath(String businessPath, UserRequest ureq, UserSession usess) { + BusinessControl bc = BusinessControlFactory.getInstance().createFromString(businessPath); + ChiefController cc = (ChiefController) Windows.getWindows(usess).getAttribute("AUTHCHIEFCONTROLLER"); + WindowControl wControl = cc.getWindowControl(); + + String wSettings = (String) usess.removeEntryFromNonClearedStore(WINDOW_SETTINGS); + if(wSettings != null) { + WindowSettings settings = WindowSettings.parse(wSettings); + wControl.getWindowBackOffice().setWindowSettings(settings); + } + + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, wControl); + NewControllerFactory.getInstance().launch(ureq, bwControl); + // render the window + Window w = cc.getWindow(); + w.dispatchRequest(ureq, true); // renderOnly + } } \ No newline at end of file diff --git a/src/main/java/org/olat/dispatcher/CatalogExportModuleDispatcher.java b/src/main/java/org/olat/dispatcher/CatalogExportModuleDispatcher.java index e5eec43e202..ef017ee40aa 100644 --- a/src/main/java/org/olat/dispatcher/CatalogExportModuleDispatcher.java +++ b/src/main/java/org/olat/dispatcher/CatalogExportModuleDispatcher.java @@ -59,6 +59,7 @@ import org.olat.basesecurity.SecurityGroup; import org.olat.catalog.CatalogEntry; import org.olat.catalog.CatalogManager; import org.olat.core.dispatcher.Dispatcher; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.media.FileMediaResource; import org.olat.core.gui.media.NotFoundMediaResource; import org.olat.core.gui.media.ServletUtil; @@ -336,7 +337,8 @@ public class CatalogExportModuleDispatcher implements Dispatcher { return f; } - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { if(!this.reInitialize()) Tracing.logError("Some Failsaves in reInitialization needed !", CatalogExportModuleDispatcher.class); try { diff --git a/src/main/java/org/olat/dispatcher/DMZDispatcher.java b/src/main/java/org/olat/dispatcher/DMZDispatcher.java index db5bcd034b4..8954bb8e7a9 100644 --- a/src/main/java/org/olat/dispatcher/DMZDispatcher.java +++ b/src/main/java/org/olat/dispatcher/DMZDispatcher.java @@ -35,7 +35,7 @@ import javax.servlet.http.HttpServletResponse; import org.olat.basesecurity.AuthHelper; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.Windows; @@ -113,7 +113,7 @@ public class DMZDispatcher implements Dispatcher { String newSessionId = sessionCookie.getValue().substring(0, sessionCookie.getValue().length()-2); response.setHeader("Set-Cookie", "JSESSIONID="+newSessionId+"; Path="+request.getContextPath()+(request.isSecure()?"":"; Secure")); } - DispatcherAction.redirectTo(response, rejectUrl); + DispatcherModule.redirectTo(response, rejectUrl); return true; } } @@ -121,60 +121,23 @@ public class DMZDispatcher implements Dispatcher { } /** - * Main method called by DispatcherAction. This processess all requests for + * Main method called by OpenOLATServlet. This processess all requests for * users who are not authenticated. * * @param request * @param response * @param uriPrefix */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { if (rejectRequest(request, response)) { return; } - - - /* - * (here it was assumed that an url containing /m/ is that of a - * mapper of olat, which is anyway not a good assumption. - * - * Removing this check can not create a security issue because a crafted - * request just needs to have referer null to bypass this check. - * - * - * - - String referer = request.getHeader("referer"); - if(referer != null && referer.indexOf(DispatcherAction.PATH_MAPPED) > -1) { - //TODO:gs may no longer needed as bad rel links are catched in dispatcherAction - //OLAT-3334 - //ignore /dmz/ requests issued from "content" delivered by - // /m/98129834/folder0/folder1/folder3/bla.hmtl - // this can happen if for example a CP contains a relative link pointing back like - // ../../../../../../presentation/cool.js where the "up navigation" exceeds the - // the /folder0/folder1/folder3 path and even jumps over /m/98129834. - //The DMZ is reached, the session invalidated and next click shows login screen. - // - //Because /g/ mapped content is considered to be save against such errors, there - // is no check for PATH_GLOBAL_MAPPED. Typically /g/ mapped paths are - // application wide defined and not brought in by users. Hence it should - // be discovered during developing or testing. - // - String msg = "BAD LINK IN [["+referer+"]]"; - Tracing.logWarn(msg, DMZDispatcher.class); - DispatcherAction.sendNotFound(msg, response); - return; - } - - * - * - */ UserRequest ureq = null; - + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); try { // upon creation URL is checked for - ureq = new UserRequestImpl(uriPrefix, request, response); } catch (NumberFormatException nfe) { // MODE could not be decoded @@ -186,7 +149,7 @@ public class DMZDispatcher implements Dispatcher { if (log.isDebug()) { log.debug("Bad Request " + request.getPathInfo()); } - DispatcherAction.sendBadRequest(request.getPathInfo(), response); + DispatcherModule.sendBadRequest(request.getPathInfo(), response); return; } //set load performance mode depending on logged in user or global parameter @@ -214,13 +177,11 @@ public class DMZDispatcher implements Dispatcher { } // chief controller creator for sub path, e.g. subPathccc = dmzServicesByPath.get(sub); - - UserSession usess = ureq.getUserSession(); - Windows ws = Windows.getWindows(usess); - synchronized (ws) { //o_clusterOK by:fj per user session - ChiefController occ; - if(subPathccc != null){ - occ = subPathccc.createChiefController(ureq); + if(subPathccc != null) { + UserSession usess = ureq.getUserSession(); + Windows ws = Windows.getWindows(usess); + synchronized (ws) { //o_clusterOK by:fj per user session + ChiefController occ = subPathccc.createChiefController(ureq); Window window = occ.getWindow(); window.setUriPrefix(uriPrefix); ws.registerWindow(window); @@ -277,18 +238,19 @@ public class DMZDispatcher implements Dispatcher { // request new windows since it is a new usersession, the old one was purged ws = Windows.getWindows(usess); - } else { - if (validDispatchUri) { + } else if (validDispatchUri) { window = ws.getWindow(ureq); - } else { - // e.g. /dmz/ -> start screen, clear previous session data - window = null; - CoreSpringFactory.getImpl(UserSessionManager.class).signOffAndClear(usess); - usess.setLocale(LocaleNegotiator.getPreferedLocale(ureq)); - I18nManager.updateLocaleInfoToThread(usess);//update locale infos - // request new windows since it is a new usersession, the old one was purged - ws = Windows.getWindows(usess); - } + } else if (dmzOnly) { + // e.g. /dmz/ -> start screen, clear previous session data + window = null; + CoreSpringFactory.getImpl(UserSessionManager.class).signOffAndClear(usess); + usess.setLocale(LocaleNegotiator.getPreferedLocale(ureq)); + I18nManager.updateLocaleInfoToThread(usess);//update locale infos + // request new windows since it is a new usersession, the old one was purged + ws = Windows.getWindows(usess); + } else { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; } if (window == null) { diff --git a/src/main/java/org/olat/dispatcher/RESTDispatcher.java b/src/main/java/org/olat/dispatcher/RESTDispatcher.java index 05e1ddd412a..38da54bad09 100644 --- a/src/main/java/org/olat/dispatcher/RESTDispatcher.java +++ b/src/main/java/org/olat/dispatcher/RESTDispatcher.java @@ -35,7 +35,7 @@ import org.olat.admin.user.delete.service.UserDeletionManager; import org.olat.basesecurity.AuthHelper; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.Windows; @@ -83,10 +83,12 @@ import org.olat.restapi.security.RestSecurityHelper; public class RESTDispatcher implements Dispatcher { private static final OLog log = Tracing.createLoggerFor(RESTDispatcher.class); - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { // // create a ContextEntries String which can be used to create a BusinessControl -> move to // + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); final String origUri = request.getRequestURI(); String restPart = origUri.substring(uriPrefix.length()); try { @@ -99,7 +101,7 @@ public class RESTDispatcher implements Dispatcher { if (split.length % 2 != 0) { // assert(split.length % 2 == 0); //The URL is not a valid business path - DispatcherAction.sendBadRequest(origUri, response); + DispatcherModule.sendBadRequest(origUri, response); log.warn("URL is not valid: "+restPart); return; } @@ -123,11 +125,11 @@ public class RESTDispatcher implements Dispatcher { BusinessControl bc = BusinessControlFactory.getInstance().createFromString(businessPath); if(!bc.hasContextEntry()) { //The URL is not a valid business path - DispatcherAction.sendBadRequest(origUri, response); + DispatcherModule.sendBadRequest(origUri, response); return; } } catch (Exception e) { - DispatcherAction.sendBadRequest(origUri, response); + DispatcherModule.sendBadRequest(origUri, response); log.warn("Error with business path: " + origUri, e); return; } @@ -150,7 +152,7 @@ public class RESTDispatcher implements Dispatcher { if(log.isDebug()){ log.debug("Bad Request "+request.getPathInfo()); } - DispatcherAction.sendBadRequest(request.getPathInfo(), response); + DispatcherModule.sendBadRequest(request.getPathInfo(), response); return; } //XX:GUIInterna.setLoadPerformanceMode(ureq); @@ -187,14 +189,14 @@ public class RESTDispatcher implements Dispatcher { UserDeletionManager.getInstance().setIdentityAsActiv(restIdentity); } else { //error, redirect to login screen - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); } } else if (Windows.getWindows(usess).getAttribute("AUTHCHIEFCONTROLLER") == null) { // Session is already available, but no main window (Head-less REST // session). Only create the base chief controller and the window Window currentWindow = AuthHelper.createAuthHome(ureq).getWindow(); //the user is authenticated successfully with a security token, we can set the authenticated path - currentWindow.setUriPrefix(WebappHelper.getServletContextPath() + DispatcherAction.PATH_AUTHENTICATED); + currentWindow.setUriPrefix(WebappHelper.getServletContextPath() + DispatcherModule.PATH_AUTHENTICATED); Windows ws = Windows.getWindows(ureq); ws.registerWindow(currentWindow); // no need to call setIdentityAsActive as this was already done by RestApiLoginFilter... @@ -213,10 +215,10 @@ public class RESTDispatcher implements Dispatcher { // session). Only create the base chief controller and the window AuthHelper.createAuthHome(ureq); String url = getRedirectToURL(usess) + ";jsessionid=" + usess.getSessionInfo().getSession().getId(); - DispatcherAction.redirectTo(response, url); + DispatcherModule.redirectTo(response, url); } else { String url = getRedirectToURL(usess); - DispatcherAction.redirectTo(response, url); + DispatcherModule.redirectTo(response, url); } } else { //prepare for redirect @@ -234,17 +236,17 @@ public class RESTDispatcher implements Dispatcher { UserDeletionManager.getInstance().setIdentityAsActiv(invite); //logged in as invited user, continue String url = getRedirectToURL(usess); - DispatcherAction.redirectTo(response, url); + DispatcherModule.redirectTo(response, url); } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) { - DispatcherAction.redirectToServiceNotAvailable(response); + DispatcherModule.redirectToServiceNotAvailable(response); } else { //error, redirect to login screen - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); } } else { String guestAccess = ureq.getParameter(AuthenticatedDispatcher.GUEST); if (guestAccess == null || !LoginModule.isGuestLoginLinksEnabled()) { - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } else if (guestAccess.equals(AuthenticatedDispatcher.TRUE)) { // try to log in as anonymous @@ -254,12 +256,12 @@ public class RESTDispatcher implements Dispatcher { if ( loginStatus == AuthHelper.LOGIN_OK) { //logged in as anonymous user, continue String url = getRedirectToURL(usess); - DispatcherAction.redirectTo(response, url); + DispatcherModule.redirectTo(response, url); } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) { - DispatcherAction.redirectToServiceNotAvailable(response); + DispatcherModule.redirectToServiceNotAvailable(response); } else { //error, redirect to login screen - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); } } } @@ -303,7 +305,7 @@ public class RESTDispatcher implements Dispatcher { ChiefController cc = (ChiefController) Windows.getWindows(usess).getAttribute("AUTHCHIEFCONTROLLER"); Window w = cc.getWindow(); - URLBuilder ubu = new URLBuilder(WebappHelper.getServletContextPath() + DispatcherAction.PATH_AUTHENTICATED, w.getInstanceId(), String.valueOf(w.getTimestamp()), null); + URLBuilder ubu = new URLBuilder(WebappHelper.getServletContextPath() + DispatcherModule.PATH_AUTHENTICATED, w.getInstanceId(), String.valueOf(w.getTimestamp()), null); StringOutput sout = new StringOutput(30); ubu.buildURI(sout, null, null); diff --git a/src/main/java/org/olat/dispatcher/RedirectToAutoGuestLoginDispatcher.java b/src/main/java/org/olat/dispatcher/RedirectToAutoGuestLoginDispatcher.java index 888603f4542..d681f55b158 100644 --- a/src/main/java/org/olat/dispatcher/RedirectToAutoGuestLoginDispatcher.java +++ b/src/main/java/org/olat/dispatcher/RedirectToAutoGuestLoginDispatcher.java @@ -30,7 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.olat.basesecurity.AuthHelper; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.Windows; @@ -63,11 +63,13 @@ public class RedirectToAutoGuestLoginDispatcher implements Dispatcher { /** * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { UserSession usess = CoreSpringFactory.getImpl(UserSessionManager.class).getUserSession(request); UserRequest ureq = null; try{ //upon creation URL is checked for + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); ureq = new UserRequestImpl(uriPrefix, request, response); }catch(NumberFormatException nfe){ //MODE could not be decoded @@ -79,15 +81,15 @@ public class RedirectToAutoGuestLoginDispatcher implements Dispatcher { if(log.isDebug()){ log.debug("Bad Request "+request.getPathInfo()); } - DispatcherAction.sendBadRequest(request.getPathInfo(), response); + DispatcherModule.sendBadRequest(request.getPathInfo(), response); return; } int loginStatus = AuthHelper.doAnonymousLogin(ureq,I18nManager.getInstance().getLocaleOrDefault(ureq.getParameter("lang")) ); if ( loginStatus != AuthHelper.LOGIN_OK) { if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) { - DispatcherAction.redirectToServiceNotAvailable(response); + DispatcherModule.redirectToServiceNotAvailable(response); } - DispatcherAction.redirectToDefaultDispatcher(response); // error, redirect to login screen + DispatcherModule.redirectToDefaultDispatcher(response); // error, redirect to login screen return; } diff --git a/src/main/java/org/olat/dispatcher/RemoteLoginformDispatcher.java b/src/main/java/org/olat/dispatcher/RemoteLoginformDispatcher.java index b858bd29774..4b737d2ea32 100644 --- a/src/main/java/org/olat/dispatcher/RemoteLoginformDispatcher.java +++ b/src/main/java/org/olat/dispatcher/RemoteLoginformDispatcher.java @@ -30,7 +30,7 @@ import org.olat.basesecurity.AuthHelper; import org.olat.basesecurity.BaseSecurityModule; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.Windows; @@ -100,27 +100,29 @@ public class RemoteLoginformDispatcher implements Dispatcher { * @param response * @param uriPrefix */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { UserRequest ureq = null; try { + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); ureq = new UserRequestImpl(uriPrefix, request, response); if (! request.getMethod().equals(METHOD_POST)) { log.warn("Wrong HTTP method, only POST allowed, but current method::" + request.getMethod()); - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } String userName = ureq.getParameter(PARAM_USERNAME); if (! StringHelper.containsNonWhitespace(userName)) { log.warn("Missing username parameter, use '" + PARAM_USERNAME + "' to submit the login name"); - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } String pwd = ureq.getParameter(PARAM_PASSWORD); if ( ! StringHelper.containsNonWhitespace(pwd)) { log.warn("Missing password parameter, use '" + PARAM_PASSWORD + "' to submit the password"); - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); return; } @@ -130,8 +132,8 @@ public class RemoteLoginformDispatcher implements Dispatcher { if (identity == null) { log.info("Could not authenticate user '" + userName + "', wrong password or user name"); // redirect to OLAT loginscreen, add error parameter so that the loginform can mark itself as errorfull - String loginUrl = WebappHelper.getServletContextPath() + DispatcherAction.getPathDefault() + "?" + OLATAuthenticationController.PARAM_LOGINERROR + "=true"; - DispatcherAction.redirectTo(response, loginUrl); + String loginUrl = WebappHelper.getServletContextPath() + DispatcherModule.getPathDefault() + "?" + OLATAuthenticationController.PARAM_LOGINERROR + "=true"; + DispatcherModule.redirectTo(response, loginUrl); return; } @@ -149,7 +151,7 @@ public class RemoteLoginformDispatcher implements Dispatcher { if(request.getParameter("redirect") != null) { //redirect parameter like: /olat/url/RepositoryEntry/917504/CourseNode/81254724902921 String redirect = request.getParameter("redirect"); - DispatcherAction.redirectTo(response, redirect); + DispatcherModule.redirectTo(response, redirect); } else if(StringHelper.containsNonWhitespace(restPart)) { //redirect like: http://www.frentix.com/olat/remotelogin/RepositoryEntry/917504/CourseNode/81254724902921 try { @@ -173,16 +175,16 @@ public class RemoteLoginformDispatcher implements Dispatcher { //UserSession usess = UserSession.getUserSession(request); usess.putEntryInNonClearedStore(AuthenticatedDispatcher.AUTHDISPATCHER_BUSINESSPATH, businessPath); String url = getRedirectToURL(usess); - DispatcherAction.redirectTo(response, url); + DispatcherModule.redirectTo(response, url); } else { //redirect ServletUtil.serveResource(request, response, ureq.getDispatchResult().getResultingMediaResource()); } } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) { - DispatcherAction.redirectToServiceNotAvailable(response); + DispatcherModule.redirectToServiceNotAvailable(response); } else { // error, redirect to login screen - DispatcherAction.redirectToDefaultDispatcher(response); + DispatcherModule.redirectToDefaultDispatcher(response); } } } catch (Throwable th) { @@ -206,6 +208,6 @@ public class RemoteLoginformDispatcher implements Dispatcher { StringOutput sout = new StringOutput(30); ubu.buildURI(sout, null, null); - return WebappHelper.getServletContextPath() + DispatcherAction.PATH_AUTHENTICATED + sout.toString(); + return WebappHelper.getServletContextPath() + DispatcherModule.PATH_AUTHENTICATED + sout.toString(); } } diff --git a/src/main/java/org/olat/gui/control/OlatTopNavController.java b/src/main/java/org/olat/gui/control/OlatTopNavController.java index 7ce5a26aca0..0cb351239b2 100644 --- a/src/main/java/org/olat/gui/control/OlatTopNavController.java +++ b/src/main/java/org/olat/gui/control/OlatTopNavController.java @@ -29,7 +29,7 @@ import org.olat.basesecurity.AuthHelper; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.controllers.impressum.ImpressumMainController; import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.Windows; import org.olat.core.gui.components.Component; @@ -151,7 +151,7 @@ public class OlatTopNavController extends BasicController implements GenericEven openInNewBrowserWindow(ureq, layoutCtrlr); // } else if (source == loginLink) { - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } else if (source == topNavVC) { if (command.equals(ACTION_LOGOUT)) { AuthHelper.doLogout(ureq); diff --git a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java index 637c401a5f9..55bc4fe68af 100644 --- a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java +++ b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java @@ -32,7 +32,7 @@ import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.BaseSecurityModule; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; @@ -319,7 +319,7 @@ protected void event(UserRequest ureq, Component source, Event event) { //update last login date and register active user UserDeletionManager.getInstance().setIdentityAsActiv(authenticatedIdentity); } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){ - DispatcherAction.redirectToServiceNotAvailable( ureq.getHttpResp() ); + DispatcherModule.redirectToServiceNotAvailable( ureq.getHttpResp() ); } else { getWindowControl().setError(translate("login.error", WebappHelper.getMailConfig("mailSupport"))); } diff --git a/src/main/java/org/olat/login/LoginAuthprovidersController.java b/src/main/java/org/olat/login/LoginAuthprovidersController.java index 34c5d7495fd..5e2fefbe743 100644 --- a/src/main/java/org/olat/login/LoginAuthprovidersController.java +++ b/src/main/java/org/olat/login/LoginAuthprovidersController.java @@ -38,7 +38,7 @@ import org.olat.basesecurity.BaseSecurityModule; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.BaseFullWebappController; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.panel.Panel; @@ -336,7 +336,7 @@ public class LoginAuthprovidersController extends MainLayoutBasicController impl if (loginStatus == AuthHelper.LOGIN_OK) { return; } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){ - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } else { // fxdiff: show useradmin-mail for pw-requests getWindowControl().setError(translate("login.error", WebappHelper.getMailConfig("mailReplyTo"))); diff --git a/src/main/java/org/olat/login/OLATAuthenticationController.java b/src/main/java/org/olat/login/OLATAuthenticationController.java index 1e0809b32d8..9a704f6d7ff 100644 --- a/src/main/java/org/olat/login/OLATAuthenticationController.java +++ b/src/main/java/org/olat/login/OLATAuthenticationController.java @@ -257,7 +257,6 @@ public class OLATAuthenticationController extends AuthenticationController imple } @Override - //fxdiff FXOLAT-113: business path in DMZ public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if(entries == null || entries.isEmpty()) return; @@ -278,70 +277,10 @@ public class OLATAuthenticationController extends AuthenticationController imple } } - /** - * @param login - * @param pass - * @return Identity if authentication was successfull, null otherwise. - * @deprecated should not be part of the controller - *//* - public static Identity authenticate(String login, String pass) { - if (pass == null) return null; // do never accept empty passwords - Identity ident = BaseSecurityManager.getInstance().findIdentityByName(login); - return authenticate(ident, login, pass); - }*/ - - /** - * @param login - * @param pass - * @return Identity if authentication was successfull, null otherwise. - * @deprecated should not be part of the controller - *//* - public static Identity authenticate(Identity ident, String login, String pass) { - // check for email instead of username if ident is null - if (ident == null && LoginModule.allowLoginUsingEmail()) { - if (MailHelper.isValidEmailAddress(login)){ - ident = UserManager.getInstance().findIdentityByEmail(login); - } - // check for email changed with verification workflow - if (ident == null) { - ident = findIdentInChangingEmailWorkflow(login); - } - } - - if (ident == null) { - return null; - } - - // find OLAT authentication provider - String auth = BaseSecurityManager.getInstance().findCredentials(ident, BaseSecurityModule.getDefaultAuthProviderIdentifier()); - if (auth != null && auth.equals(Encoder.encrypt(pass))) return ident; - - Tracing.createLoggerFor(OLATAuthenticationController.class).audit( - "Error authenticating user "+login+" via provider OLAT", - OLATAuthenticationController.class.getName() - ); - - return null; - } - - private static Identity findIdentInChangingEmailWorkflow(String login){ - RegistrationManager rm = RegistrationManager.getInstance(); - List<TemporaryKey> tk = rm.loadTemporaryKeyByAction(RegistrationManager.EMAIL_CHANGE); - if (tk != null) { - for (TemporaryKey temporaryKey : tk) { - XStream xml = new XStream(); - HashMap<String, String> mails = (HashMap<String, String>) xml.fromXML(temporaryKey.getEmailAddress()); - if (login.equals(mails.get("changedEMail"))) { - return BaseSecurityManager.getInstance().findIdentityByName(mails.get("currentEMail")); - } - } - } - return null; - }*/ - /** * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) */ + @Override protected void doDispose() { // } diff --git a/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties index 3743effcc90..ff19ffa882f 100644 --- a/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties @@ -114,7 +114,7 @@ menu.root.alt=infinite learning runonce.forced=Veuillez compl\u00E9ter toutes les indications et enregistrez le formulaire, s.v.p. . runonce.title=Modifications r\u00E9currentes / R\u00E9glages \u00E0 la premi\u00E8re connexion shib.redirect=Shibboleth Login -timeout.message=Votre session a expir\u00E9 ou le serveur a \u00E9t\u00E9 red\u00E9marr\u00E9. Pour vous connecter \u00E0 nouveau, s\u00E9lectionner ce <b><a href\='reload.html'>lien</ a></ b> ou recharger la page.<br>S'il vous pla\u00EEt, noter que tous les changements dans la fen\u00EAtre de votre navigateur seront perdus lorsque vous vous connecterez \u00E0 nouveau. Copier les posts non enregistr\u00E9es ou \u00E9quivalent dans le presse-papiers de votre navigateur apr\u00E8s avoir ferm\u00E9 ce dialogue puis rechargez la page. +timeout.message=Votre session a expir\u00E9 ou le serveur a \u00E9t\u00E9 red\u00E9marr\u00E9. Pour vous connecter \u00E0 nouveau, s\u00E9lectionner ce <b><a href='reload.html'>lien</ a></ b> ou recharger la page.<br>S'il vous pla\u00EEt, noter que tous les changements dans la fen\u00EAtre de votre navigateur seront perdus lorsque vous vous connecterez \u00E0 nouveau. Copier les posts non enregistr\u00E9es ou \u00E9quivalent dans le presse-papiers de votre navigateur apr\u00E8s avoir ferm\u00E9 ce dialogue puis rechargez la page. timeout.title=Expiration de la session topnav.help=Aide topnav.help.alt=D\u00E9marrer l'aide de OpenOLAT diff --git a/src/main/java/org/olat/login/auth/AuthenticationSPI.java b/src/main/java/org/olat/login/auth/AuthenticationSPI.java index 2d838872a25..a1f8b8a69c8 100644 --- a/src/main/java/org/olat/login/auth/AuthenticationSPI.java +++ b/src/main/java/org/olat/login/auth/AuthenticationSPI.java @@ -30,5 +30,7 @@ import org.olat.core.id.Identity; public interface AuthenticationSPI { public Identity authenticate(Identity identity, String login, String password); + + public void upgradePassword(Identity identity, String login, String password); } diff --git a/src/main/java/org/olat/login/auth/OLATAuthManager.java b/src/main/java/org/olat/login/auth/OLATAuthManager.java index 22d467b95fe..07dbced265d 100644 --- a/src/main/java/org/olat/login/auth/OLATAuthManager.java +++ b/src/main/java/org/olat/login/auth/OLATAuthManager.java @@ -33,6 +33,7 @@ import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.UserConstants; @@ -84,6 +85,8 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { private BaseSecurity securityManager; @Autowired private MailManager mailManager; + @Autowired + private WebDAVAuthManager webDAVAuthManager; /** * @@ -125,14 +128,24 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { if (securityManager.checkCredentials(authentication, password)) { Algorithm algorithm = Algorithm.find(authentication.getAlgorithm()); if(Algorithm.md5.equals(algorithm)) { - authentication = securityManager.updateCredentials(authentication, password, LoginModule.getDefaultHashAlgorithm()); + Algorithm defAlgorithm = LoginModule.getDefaultHashAlgorithm(); + authentication = securityManager.updateCredentials(authentication, password, defAlgorithm); + } + Identity identity = authentication.getIdentity(); + if(identity != null && webDAVAuthManager != null) { + webDAVAuthManager.upgradePassword(identity, login, password); } - return authentication.getIdentity(); + return identity; } log.audit("Error authenticating user "+login+" via provider OLAT", OLATAuthenticationController.class.getName()); return null; } - + + @Override + public void upgradePassword(Identity identity, String login, String password) { + //nothing to do + } + private Identity findIdentInChangingEmailWorkflow(String login){ XStream xml = XStreamHelper.createXStreamInstance(); diff --git a/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java b/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java index b76ffd4c0ea..065908c5f6d 100644 --- a/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java +++ b/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java @@ -32,7 +32,7 @@ import org.olat.basesecurity.BaseSecurityManager; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.ServletUtil; import org.olat.core.id.Identity; @@ -96,7 +96,9 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher { * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse, java.lang.String) */ - public void execute(HttpServletRequest request, HttpServletResponse response, String uriPrefix) { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) { + String uriPrefix = DispatcherModule.getLegacyUriPrefix(request); String requestedPath = getPath(request, uriPrefix); Path path = null; @@ -132,15 +134,15 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher { } else { // Deny access log.info("Access was denied. Path::" + path); - DispatcherAction.sendForbidden(request.getRequestURI(), response); + DispatcherModule.sendForbidden(request.getRequestURI(), response); } } } catch (InvalidPathException e) { logWarn("The requested path is invalid. path::" + path, e); - DispatcherAction.sendBadRequest(request.getRequestURI(), response); + DispatcherModule.sendBadRequest(request.getRequestURI(), response); } catch (Throwable t) { logWarn("Nothing was delivered. Path::" + path, t); - DispatcherAction.sendNotFound(request.getRequestURI(), response); + DispatcherModule.sendNotFound(request.getRequestURI(), response); } } diff --git a/src/main/java/org/olat/registration/PwChangeController.java b/src/main/java/org/olat/registration/PwChangeController.java index f5f0498d905..f8754258a1f 100644 --- a/src/main/java/org/olat/registration/PwChangeController.java +++ b/src/main/java/org/olat/registration/PwChangeController.java @@ -34,7 +34,7 @@ import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.BaseSecurityModule; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; @@ -156,7 +156,7 @@ public class PwChangeController extends BasicController { */ public void event(UserRequest ureq, Component source, Event event) { if (source == pwchangeHomelink) { - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } } diff --git a/src/main/java/org/olat/repository/controllers/RepositoryDetailsController.java b/src/main/java/org/olat/repository/controllers/RepositoryDetailsController.java index b124894e161..34ea3be19c0 100644 --- a/src/main/java/org/olat/repository/controllers/RepositoryDetailsController.java +++ b/src/main/java/org/olat/repository/controllers/RepositoryDetailsController.java @@ -43,7 +43,7 @@ import org.olat.catalog.ui.RepoEntryCategoriesTableController; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.services.mark.MarkManager; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.Windows; import org.olat.core.gui.components.Component; @@ -662,7 +662,7 @@ public class RepositoryDetailsController extends BasicController implements Gene } else if (sourceLink == launchButton){ doLaunch(ureq, repositoryEntry); } else if (sourceLink == loginLink){ - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } else if (sourceLink.getUserObject() instanceof IdentityShort) { IdentityShort author = (IdentityShort)sourceLink.getUserObject(); String businessPath = "[Identity:" + author.getKey() + "]"; diff --git a/src/main/java/org/olat/repository/controllers/RepositoryMainController.java b/src/main/java/org/olat/repository/controllers/RepositoryMainController.java index 90399007a72..aa1b03271c4 100644 --- a/src/main/java/org/olat/repository/controllers/RepositoryMainController.java +++ b/src/main/java/org/olat/repository/controllers/RepositoryMainController.java @@ -391,7 +391,6 @@ public class RepositoryMainController extends MainLayoutBasicController implemen * @param subViewIdentifyer optional view identifyer for a sub controller */ private void activateContent(UserRequest ureq, Object userObject, List<ContextEntry> entries, StateEntry state) { - log.info("activateContent userObject=" + userObject); if (userObject.equals("search.home")) { // the // home main.setPage(VELOCITY_ROOT + "/index.html"); diff --git a/src/main/java/org/olat/repository/controllers/RepositorySearchController.java b/src/main/java/org/olat/repository/controllers/RepositorySearchController.java index 655b1826008..d432211cabb 100644 --- a/src/main/java/org/olat/repository/controllers/RepositorySearchController.java +++ b/src/main/java/org/olat/repository/controllers/RepositorySearchController.java @@ -33,7 +33,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.ShortName; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; @@ -227,7 +227,7 @@ public class RepositorySearchController extends BasicController implements Activ displaySearchForm(); return; }else if (source == loginLink){ - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); } } diff --git a/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java b/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java index aa83547c82b..9f8a300fb56 100644 --- a/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java +++ b/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java @@ -95,6 +95,8 @@ public class RestApiLoginFilter implements Filter { public void destroy() { // } + + @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) @@ -152,7 +154,7 @@ public class RestApiLoginFilter implements Filter { throw new ServletException("Only accept HTTP Request"); } } - //fxdiff Basic Authentication + private boolean isBasicAuthenticated(HttpServletRequest request, HttpServletResponse response, String requestURI) { String authHeader = request.getHeader("Authorization"); if (authHeader != null) { diff --git a/src/main/java/org/olat/restapi/system/OpenOLATStatisticsWebService.java b/src/main/java/org/olat/restapi/system/OpenOLATStatisticsWebService.java index 4a3f013b335..ba440fddaff 100644 --- a/src/main/java/org/olat/restapi/system/OpenOLATStatisticsWebService.java +++ b/src/main/java/org/olat/restapi/system/OpenOLATStatisticsWebService.java @@ -34,7 +34,6 @@ import org.olat.admin.sysinfo.manager.SessionStatsManager; import org.olat.admin.sysinfo.model.SessionsStats; import org.olat.basesecurity.BaseSecurity; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; import org.olat.core.util.SessionInfo; import org.olat.core.util.UserSession; import org.olat.core.util.WorkThreadInformations; @@ -247,8 +246,7 @@ public class OpenOLATStatisticsWebService implements Sampler { vo.setAuthenticatedPollCountLastFiveMinutes(statsLast5Minutes.getAuthenticatedPollerCalls()); vo.setRequestLastMinute(statsLastMinute.getRequests()); vo.setRequestLastFiveMinutes(statsLast5Minutes.getRequests()); - long dispatchThreads = DispatcherAction.getConcurrentCounter(); - vo.setConcurrentDispatchThreads(dispatchThreads); + vo.setConcurrentDispatchThreads(sessionStatsManager.getConcurrentCounter()); return vo; } diff --git a/src/main/java/org/olat/shibboleth/DefaultShibbolethAuthenticationController.java b/src/main/java/org/olat/shibboleth/DefaultShibbolethAuthenticationController.java index a7835c1f8c1..20bf2edf424 100644 --- a/src/main/java/org/olat/shibboleth/DefaultShibbolethAuthenticationController.java +++ b/src/main/java/org/olat/shibboleth/DefaultShibbolethAuthenticationController.java @@ -27,7 +27,7 @@ package org.olat.shibboleth; import java.util.Locale; import org.olat.basesecurity.AuthHelper; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; @@ -96,14 +96,14 @@ public class DefaultShibbolethAuthenticationController extends AuthenticationCon @Override protected void event(UserRequest ureq, Component source, Event event) { if (source == shibLink) { - DispatcherAction.redirectTo(ureq.getHttpResp(), WebappHelper.getServletContextPath() + "/shib/"); + DispatcherModule.redirectTo(ureq.getHttpResp(), WebappHelper.getServletContextPath() + "/shib/"); } else if (source == guestLink) { int loginStatus = AuthHelper.doAnonymousLogin(ureq, ureq.getLocale()); if (loginStatus == AuthHelper.LOGIN_OK) { return; } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){ //getWindowControl().setError(translate("login.notavailable", OLATContext.getSupportaddress())); - DispatcherAction.redirectToServiceNotAvailable( ureq.getHttpResp() ); + DispatcherModule.redirectToServiceNotAvailable( ureq.getHttpResp() ); } else { getWindowControl().setError(translate("login.error", WebappHelper.getMailConfig("mailSupport"))); } diff --git a/src/main/java/org/olat/shibboleth/ShibbolethAuthenticationController.java b/src/main/java/org/olat/shibboleth/ShibbolethAuthenticationController.java index e63470e3457..faae17edc4b 100644 --- a/src/main/java/org/olat/shibboleth/ShibbolethAuthenticationController.java +++ b/src/main/java/org/olat/shibboleth/ShibbolethAuthenticationController.java @@ -34,7 +34,7 @@ import javax.servlet.http.Cookie; import org.olat.basesecurity.AuthHelper; import org.olat.core.CoreSpringFactory; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; @@ -154,7 +154,7 @@ public class ShibbolethAuthenticationController extends AuthenticationController return; } else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){ //getWindowControl().setError(translate("login.notavailable", OLATContext.getSupportaddress())); - DispatcherAction.redirectToServiceNotAvailable( ureq.getHttpResp() ); + DispatcherModule.redirectToServiceNotAvailable( ureq.getHttpResp() ); } else { getWindowControl().setError(translate("login.error", WebappHelper.getMailConfig("mailSupport"))); } diff --git a/src/main/java/org/olat/shibboleth/ShibbolethDispatcher.java b/src/main/java/org/olat/shibboleth/ShibbolethDispatcher.java index 5c59763680b..40412c27494 100644 --- a/src/main/java/org/olat/shibboleth/ShibbolethDispatcher.java +++ b/src/main/java/org/olat/shibboleth/ShibbolethDispatcher.java @@ -42,7 +42,7 @@ import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurityManager; import org.olat.core.CoreSpringFactory; import org.olat.core.dispatcher.Dispatcher; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.control.ChiefController; @@ -89,14 +89,15 @@ public class ShibbolethDispatcher implements Dispatcher{ } /** - * Main method called by DIspatcherAction. + * Main method called by OpenOLATServlet. * This processess all shibboleth requests. * * @param req * @param resp * @param uriPrefix */ - public void execute(HttpServletRequest req, HttpServletResponse resp, String uriPrefix) { + @Override + public void execute(HttpServletRequest req, HttpServletResponse resp) { if(translator==null) { translator = Util.createPackageTranslator(ShibbolethDispatcher.class, I18nModule.getDefaultLocale()); } @@ -109,6 +110,7 @@ public class ShibbolethDispatcher implements Dispatcher{ } catch (UnsupportedEncodingException e) { throw new AssertException("UTF-8 encoding not supported!!!!"); } + String uriPrefix = DispatcherModule.getLegacyUriPrefix(req); uri = uri.substring(uriPrefix.length()); // guaranteed to exist by DispatcherAction Map<String, String> attributesMap = getShibbolethAttributesFromRequest(req); @@ -131,7 +133,7 @@ public class ShibbolethDispatcher implements Dispatcher{ if(log.isDebug()){ log.debug("Bad Request "+req.getPathInfo()); } - DispatcherAction.sendBadRequest(req.getPathInfo(), resp); + DispatcherModule.sendBadRequest(req.getPathInfo(), resp); return; } @@ -145,9 +147,9 @@ public class ShibbolethDispatcher implements Dispatcher{ int loginStatus = AuthHelper.doLogin(auth.getIdentity(), ShibbolethDispatcher.PROVIDER_SHIB, ureq); if (loginStatus != AuthHelper.LOGIN_OK) { if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) { - DispatcherAction.redirectToServiceNotAvailable(resp); + DispatcherModule.redirectToServiceNotAvailable(resp); } else { - DispatcherAction.redirectToDefaultDispatcher(resp); // error, redirect to login screen + DispatcherModule.redirectToDefaultDispatcher(resp); // error, redirect to login screen } return; } @@ -172,7 +174,7 @@ public class ShibbolethDispatcher implements Dispatcher{ RedirectMediaResource rmr = (RedirectMediaResource)mr; rmr.prepare(resp); } else { - DispatcherAction.redirectToDefaultDispatcher(resp); // error, redirect to login screen + DispatcherModule.redirectToDefaultDispatcher(resp); // error, redirect to login screen } } } @@ -238,9 +240,9 @@ public class ShibbolethDispatcher implements Dispatcher{ private final void redirectToShibbolethRegistration(HttpServletResponse response) { try { - response.sendRedirect(WebappHelper.getServletContextPath() + DispatcherAction.getPathDefault() + ShibbolethModule.PATH_REGISTER_SHIBBOLETH + "/"); + response.sendRedirect(WebappHelper.getServletContextPath() + DispatcherModule.getPathDefault() + ShibbolethModule.PATH_REGISTER_SHIBBOLETH + "/"); } catch (IOException e) { - log.error("Redirect failed: url=" + WebappHelper.getServletContextPath() + DispatcherAction.getPathDefault(),e); + log.error("Redirect failed: url=" + WebappHelper.getServletContextPath() + DispatcherModule.getPathDefault(),e); } } diff --git a/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java b/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java index 9d7d9c6499b..02cbee0014f 100644 --- a/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java +++ b/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java @@ -40,7 +40,7 @@ import org.olat.basesecurity.SecurityGroup; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.chiefcontrollers.LanguageChangedEvent; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; -import org.olat.core.dispatcher.DispatcherAction; +import org.olat.core.dispatcher.DispatcherModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.velocity.VelocityContainer; @@ -398,7 +398,7 @@ public class ShibbolethRegistrationController extends DefaultController implemen //instead set the media resource accordingly //pb -> provide a DispatcherAction.getDefaultDispatcherRedirectMediaresource(); //to be used here. (and some more places like CatalogController. - DispatcherAction.redirectToDefaultDispatcher(ureq.getHttpResp()); // error, redirect to login screen + DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); // error, redirect to login screen return; } // successfull login diff --git a/src/main/java/org/olat/user/PersonalSettingsController.java b/src/main/java/org/olat/user/PersonalSettingsController.java index 6bf54e418ec..a11e0829e21 100644 --- a/src/main/java/org/olat/user/PersonalSettingsController.java +++ b/src/main/java/org/olat/user/PersonalSettingsController.java @@ -33,7 +33,7 @@ import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.Constants; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; -import org.olat.core.commons.services.webdav.WebDAVManager; +import org.olat.core.commons.services.webdav.WebDAVModule; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.tabbedpane.TabbedPane; @@ -103,8 +103,8 @@ public class PersonalSettingsController extends BasicController implements Activ listenTo(pwdc); userConfig.addTab(translate("tab.pwd"), pwdc.getInitialComponent()); } - - if(WebDAVManager.getInstance().isEnabled()) { + + if(CoreSpringFactory.getImpl(WebDAVModule.class).isEnabled()) { pwdav = new WebDAVPasswordController(ureq, getWindowControl()); userConfig.addTab(translate("tab.pwdav"), pwdav.getInitialComponent()); listenTo(pwdav); diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index 34ac978296d..ea51b80b54b 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -423,6 +423,7 @@ topnav.search=true #webdav manager (show or don't webdav links in GIU) webdav.links.enabled=true +auth.digest.enabled=true ######################################################################## # Image and PDF scale/thumbnail options @@ -443,6 +444,9 @@ thumbnail.provider.values=java,magick # Eg. 8443, 443 (must not be empty, even if standard port 443 is used). Use 0 to disable SSL server.port.ssl=0 +#if migrating from a context as /olat to the ROOT context in tomcat +server.legacy.context= + # mobile context used to redirect mobile.context=/mobile diff --git a/src/main/webapp-gae/WEB-INF/web.xml b/src/main/webapp-gae/WEB-INF/web.xml index 3a3614dc661..161c623d880 100644 --- a/src/main/webapp-gae/WEB-INF/web.xml +++ b/src/main/webapp-gae/WEB-INF/web.xml @@ -152,9 +152,14 @@ <!-- OLAT servlet --> <servlet> - <servlet-name>olatservlet</servlet-name> - <servlet-class>org.olat.core.servlets.OLATServlet</servlet-class> - + <servlet-name>openolatservlet</servlet-name> + <servlet-class>org.olat.core.servlets.OpenOLATServlet</servlet-class> + <!-- Set the load order --> + <load-on-startup>1</load-on-startup> + </servlet> + <servlet> + <servlet-name>rawservlet</servlet-name> + <servlet-class>org.olat.core.servlets.StaticServlet</servlet-class> <!-- Set the load order --> <load-on-startup>1</load-on-startup> </servlet> @@ -178,39 +183,6 @@ <load-on-startup>100</load-on-startup> </servlet> <!-- /axis --> - - <!-- WebDAV servlet --> - <servlet> - <servlet-name>webdav</servlet-name> - <servlet-class>org.olat.core.commons.services.webdav.SecureWebdavServlet</servlet-class> - - <init-param> - <param-name>debug</param-name> - <param-value>0</param-value> - </init-param> - - <init-param> - <param-name>input</param-name> - <param-value>32768</param-value> - </init-param> - - <init-param> - <param-name>output</param-name> - <param-value>32768</param-value> - </init-param> - - <init-param> - <param-name>listings</param-name> - <param-value>true</param-value> - </init-param> - - <!-- Uncomment this to enable read and write access --> - <init-param> - <param-name>readonly</param-name> - <param-value>false</param-value> - </init-param> - <load-on-startup>3</load-on-startup> - </servlet> <!-- RSS feed requests --> <servlet> @@ -251,19 +223,18 @@ <!-- 5.Servlet-Mapping --> <!-- The mapping for the OLAT servlet --> <servlet-mapping> - <servlet-name>olatservlet</servlet-name> + <servlet-name>openolatservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet-mapping> - <servlet-name>olatservlet</servlet-name> + <servlet-name>openolatservlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> - <!-- The mapping for the webdav servlet --> <servlet-mapping> - <servlet-name>webdav</servlet-name> - <url-pattern>/webdav/*</url-pattern> + <servlet-name>rawservlet</servlet-name> + <url-pattern>/raw/*</url-pattern> </servlet-mapping> <!-- The mapping for the RSS servlet --> diff --git a/src/main/webapp-jbossas7/WEB-INF/web.xml b/src/main/webapp-jbossas7/WEB-INF/web.xml index 5abbd64bbb4..4093350caeb 100644 --- a/src/main/webapp-jbossas7/WEB-INF/web.xml +++ b/src/main/webapp-jbossas7/WEB-INF/web.xml @@ -126,44 +126,16 @@ <!-- OLAT servlet --> <servlet> - <servlet-name>olatservlet</servlet-name> - <servlet-class>org.olat.core.servlets.OLATServlet</servlet-class> - + <servlet-name>openolatservlet</servlet-name> + <servlet-class>org.olat.core.servlets.OpenOLATServlet</servlet-class> <!-- Set the load order --> <load-on-startup>1</load-on-startup> </servlet> - - <!-- WebDAV servlet --> <servlet> - <servlet-name>webdav</servlet-name> - <servlet-class>org.olat.core.commons.services.webdav.SecureWebdavServlet</servlet-class> - - <init-param> - <param-name>debug</param-name> - <param-value>0</param-value> - </init-param> - - <init-param> - <param-name>input</param-name> - <param-value>32768</param-value> - </init-param> - - <init-param> - <param-name>output</param-name> - <param-value>32768</param-value> - </init-param> - - <init-param> - <param-name>listings</param-name> - <param-value>true</param-value> - </init-param> - - <!-- Uncomment this to enable read and write access --> - <init-param> - <param-name>readonly</param-name> - <param-value>false</param-value> - </init-param> - <load-on-startup>3</load-on-startup> + <servlet-name>rawservlet</servlet-name> + <servlet-class>org.olat.core.servlets.StaticServlet</servlet-class> + <!-- Set the load order --> + <load-on-startup>1</load-on-startup> </servlet> <!-- RSS feed requests --> @@ -205,19 +177,18 @@ <!-- 5.Servlet-Mapping --> <!-- The mapping for the OLAT servlet --> <servlet-mapping> - <servlet-name>olatservlet</servlet-name> + <servlet-name>openolatservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet-mapping> - <servlet-name>olatservlet</servlet-name> + <servlet-name>openolatservlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> - <!-- The mapping for the webdav servlet --> <servlet-mapping> - <servlet-name>webdav</servlet-name> - <url-pattern>/webdav/*</url-pattern> + <servlet-name>rawservlet</servlet-name> + <url-pattern>/raw/*</url-pattern> </servlet-mapping> <!-- The mapping for the RSS servlet --> diff --git a/src/main/webapp-tomcat/WEB-INF/web.xml b/src/main/webapp-tomcat/WEB-INF/web.xml index 5b568096731..46d23ce2813 100644 --- a/src/main/webapp-tomcat/WEB-INF/web.xml +++ b/src/main/webapp-tomcat/WEB-INF/web.xml @@ -32,8 +32,13 @@ <param-name>contextInitializerClasses</param-name> <param-value>org.olat.core.CoreSpringInitializer</param-value> </context-param> - - + <!-- + <filter> + <filter-name>SpyFilter</filter-name> + <filter-class>org.olat.core.servlets.SpyFilter</filter-class> + </filter> + --> + <!-- 2. Filters --> <filter> @@ -55,6 +60,12 @@ <filter-name>RESTApiLoginFilter</filter-name> <filter-class>org.olat.restapi.security.RestApiLoginFilter</filter-class> </filter> + <!-- + <filter-mapping> + <filter-name>SpyFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + --> <!-- Use FakeHTTPSFilter for requests to WSService and TraineeStatusService (dynamic WSDL for ONYX) --> <filter-mapping> @@ -141,9 +152,14 @@ <!-- OLAT servlet --> <servlet> - <servlet-name>olatservlet</servlet-name> - <servlet-class>org.olat.core.servlets.OLATServlet</servlet-class> - + <servlet-name>openolatservlet</servlet-name> + <servlet-class>org.olat.core.servlets.OpenOLATServlet</servlet-class> + <!-- Set the load order --> + <load-on-startup>1</load-on-startup> + </servlet> + <servlet> + <servlet-name>rawservlet</servlet-name> + <servlet-class>org.olat.core.servlets.StaticServlet</servlet-class> <!-- Set the load order --> <load-on-startup>1</load-on-startup> </servlet> @@ -159,39 +175,6 @@ <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> - - <!-- WebDAV servlet --> - <servlet> - <servlet-name>webdav</servlet-name> - <servlet-class>org.olat.core.commons.services.webdav.SecureWebdavServlet</servlet-class> - - <init-param> - <param-name>debug</param-name> - <param-value>0</param-value> - </init-param> - - <init-param> - <param-name>input</param-name> - <param-value>32768</param-value> - </init-param> - - <init-param> - <param-name>output</param-name> - <param-value>32768</param-value> - </init-param> - - <init-param> - <param-name>listings</param-name> - <param-value>true</param-value> - </init-param> - - <!-- Uncomment this to enable read and write access --> - <init-param> - <param-name>readonly</param-name> - <param-value>false</param-value> - </init-param> - <load-on-startup>3</load-on-startup> - </servlet> <!-- Jersey REST Servlet --> <servlet> @@ -247,19 +230,18 @@ <!-- 5.Servlet-Mapping --> <!-- The mapping for the OLAT servlet --> <servlet-mapping> - <servlet-name>olatservlet</servlet-name> + <servlet-name>openolatservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet-mapping> - <servlet-name>olatservlet</servlet-name> + <servlet-name>openolatservlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> - <!-- The mapping for the webdav servlet --> <servlet-mapping> - <servlet-name>webdav</servlet-name> - <url-pattern>/webdav/*</url-pattern> + <servlet-name>rawservlet</servlet-name> + <url-pattern>/raw/*</url-pattern> </servlet-mapping> <!-- The mapping to the REST API --> diff --git a/src/test/java/org/olat/core/commons/service/webdav/HttpPropFind.java b/src/test/java/org/olat/core/commons/services/webdav/HttpPropFind.java similarity index 97% rename from src/test/java/org/olat/core/commons/service/webdav/HttpPropFind.java rename to src/test/java/org/olat/core/commons/services/webdav/HttpPropFind.java index 2cb61bef420..c99d8a5aa4d 100644 --- a/src/test/java/org/olat/core/commons/service/webdav/HttpPropFind.java +++ b/src/test/java/org/olat/core/commons/services/webdav/HttpPropFind.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.core.commons.service.webdav; +package org.olat.core.commons.services.webdav; import java.net.URI; diff --git a/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java b/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java new file mode 100644 index 00000000000..9a097ca9186 --- /dev/null +++ b/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java @@ -0,0 +1,67 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.commons.services.webdav; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.UUID; + +import junit.framework.Assert; + +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.apache.poi.util.IOUtils; +import org.junit.Test; +import org.olat.core.id.Identity; +import org.olat.test.JunitTestHelper; + +/** + * + * Test the commands against the WedDAV implementation of OpenOLAT + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class WebDAVCommandsTest extends WebDAVTestCase { + + + @Test + public void testPropFind() + throws IOException, URISyntaxException { + //create a user + Identity user = JunitTestHelper.createAndPersistIdentityAsUser("webdav-1-" + UUID.randomUUID().toString()); + + //list root content of its webdav folder + WebDAVConnection conn = new WebDAVConnection(); + conn.setCredentials(user.getName(), "A6B7C8"); + URI uri = conn.getContextURI().build(); + HttpResponse response = conn.propfind(uri); + assertEquals(207, response.getStatusLine().getStatusCode()); + + String xml = EntityUtils.toString(response.getEntity()); + Assert.assertTrue(xml.indexOf("/webdav/coursefolders/") > 0); + + IOUtils.closeQuietly(conn); + } + +} diff --git a/src/test/java/org/olat/core/commons/service/webdav/WebDAVConnection.java b/src/test/java/org/olat/core/commons/services/webdav/WebDAVConnection.java similarity index 72% rename from src/test/java/org/olat/core/commons/service/webdav/WebDAVConnection.java rename to src/test/java/org/olat/core/commons/services/webdav/WebDAVConnection.java index f08ffe586ad..1cb4f20fec4 100644 --- a/src/test/java/org/olat/core/commons/service/webdav/WebDAVConnection.java +++ b/src/test/java/org/olat/core/commons/services/webdav/WebDAVConnection.java @@ -17,22 +17,26 @@ * 12.10.2011 by frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.core.commons.service.webdav; +package org.olat.core.commons.services.webdav; +import java.io.Closeable; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import javax.ws.rs.core.UriBuilder; +import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CookieStore; -import org.apache.http.client.params.CookiePolicy; -import org.apache.http.client.params.HttpClientParams; -import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.olat.restapi.security.RestSecurityHelper; /** @@ -46,22 +50,27 @@ import org.olat.restapi.security.RestSecurityHelper; * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class WebDAVConnection { +public class WebDAVConnection implements Closeable { private int port = WebDAVTestCase.PORT; private String host = WebDAVTestCase.HOST; private String protocol = WebDAVTestCase.PROTOCOL; private String contextPath = WebDAVTestCase.CONTEXT_PATH; + + private final BasicCookieStore cookieStore = new BasicCookieStore(); + private final CredentialsProvider provider = new BasicCredentialsProvider(); - private final DefaultHttpClient httpclient; + private final CloseableHttpClient httpclient; public WebDAVConnection() { - httpclient = new DefaultHttpClient(); - HttpClientParams.setCookiePolicy(httpclient.getParams(), CookiePolicy.RFC_2109); + httpclient = HttpClientBuilder.create() + .setDefaultCookieStore(cookieStore) + .setDefaultCredentialsProvider(provider) + .build(); } public CookieStore getCookieStore() { - return httpclient.getCookieStore(); + return cookieStore; } public String getSecurityToken(HttpResponse response) { @@ -71,14 +80,14 @@ public class WebDAVConnection { return header == null ? null : header.getValue(); } - public void shutdown() { - httpclient.getConnectionManager().shutdown(); + public void close() { + IOUtils.closeQuietly(httpclient); } public void setCredentials(String username, String password) { - httpclient.getCredentialsProvider().setCredentials( - new AuthScope("localhost", port), - new UsernamePasswordCredentials(username, password)); + provider.setCredentials( + new AuthScope(host, port, "OLAT WebDAV Access", "Basic"), + new UsernamePasswordCredentials(username, password)); } public HttpResponse propfind(URI uri) throws IOException, URISyntaxException { diff --git a/src/test/java/org/olat/core/commons/service/webdav/WebDAVTestCase.java b/src/test/java/org/olat/core/commons/services/webdav/WebDAVTestCase.java similarity index 69% rename from src/test/java/org/olat/core/commons/service/webdav/WebDAVTestCase.java rename to src/test/java/org/olat/core/commons/services/webdav/WebDAVTestCase.java index 7d3254cad8d..f5fada09399 100644 --- a/src/test/java/org/olat/core/commons/service/webdav/WebDAVTestCase.java +++ b/src/test/java/org/olat/core/commons/services/webdav/WebDAVTestCase.java @@ -24,28 +24,16 @@ * <p> */ -package org.olat.core.commons.service.webdav; - -import static org.junit.Assert.assertEquals; +package org.olat.core.commons.services.webdav; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.UUID; import javax.servlet.Servlet; -import junit.framework.Assert; - -import org.apache.http.HttpResponse; -import org.apache.http.util.EntityUtils; import org.junit.BeforeClass; -import org.junit.Test; -import org.olat.core.commons.services.webdav.SecureWebdavServlet; -import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; -import org.olat.test.JunitTestHelper; +import org.olat.core.servlets.OpenOLATServlet; import org.olat.test.OlatTestCase; import com.sun.grizzly.http.embed.GrizzlyWebServer; @@ -54,13 +42,13 @@ import com.sun.grizzly.http.servlet.ServletAdapter; /** * * Description:<br> - * Abstract class which start and stop a grizzly server for every test + * Abstract class which start and stop a grizzly server for every test run * * <P> * Initial Date: 14 apr. 2010 <br> * @author srosse, stephane.rosse@frentix.com */ -public class WebDAVTestCase extends OlatTestCase { +public abstract class WebDAVTestCase extends OlatTestCase { private static final OLog log = Tracing.createLoggerFor(WebDAVTestCase.class); public final static int PORT = 9997; @@ -79,7 +67,7 @@ public class WebDAVTestCase extends OlatTestCase { ServletAdapter sa = new ServletAdapter(); Servlet servletInstance = null; try { - servletInstance = new SecureWebdavServlet(); + servletInstance = new OpenOLATServlet(); } catch (Exception ex) { log.error("Cannot instantiate the Grizzly Servlet Container", ex); } @@ -99,21 +87,4 @@ public class WebDAVTestCase extends OlatTestCase { log.error("Cannot start the Grizzly Web Container for WebDAV"); } } - - @Test - public void testPropFind() - throws IOException, URISyntaxException { - //create a user - Identity user = JunitTestHelper.createAndPersistIdentityAsUser("webdav-1-" + UUID.randomUUID().toString()); - - //list root content of its webdav folder - WebDAVConnection conn = new WebDAVConnection(); - conn.setCredentials(user.getName(), "A6B7C8"); - URI uri = conn.getContextURI().build(); - HttpResponse response = conn.propfind(uri); - assertEquals(207, response.getStatusLine().getStatusCode()); - - String xml = EntityUtils.toString(response.getEntity()); - Assert.assertTrue(xml.indexOf("/webdav/coursefolders/") > 0); - } } \ No newline at end of file diff --git a/src/test/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerTest.java b/src/test/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerTest.java index b3bb9ada2eb..f4a1be096ef 100644 --- a/src/test/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerTest.java +++ b/src/test/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerTest.java @@ -30,8 +30,7 @@ import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; -import junit.framework.Assert; - +import org.junit.Assert; import org.junit.Test; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.DBFactory; @@ -61,6 +60,20 @@ public class WebDAVManagerTest extends OlatTestCase { @Autowired private WebDAVManagerImpl webDAVManager; + @Test + public void parseDigestAuthentication() { + String request = "username=\"kanu\",realm=\"OLAT WebDAV Access\",nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",uri=\"/webdav\",cnonce=\"195d0d15b31ee8f0a7f243b9bfcd881d\",nc=00000001,response=\"671742b00fae8d4c8ceb6a5bcf2b36fa\",qop=\"auth\""; + DigestAuthentication auth = DigestAuthentication.parse(request); + Assert.assertEquals("kanu", auth.getUsername()); + Assert.assertEquals("OLAT WebDAV Access", auth.getRealm()); + Assert.assertEquals("dcd98b7102dd2f0e8b11d0f600bfb0c093", auth.getNonce()); + Assert.assertEquals("/webdav", auth.getUri()); + Assert.assertEquals("195d0d15b31ee8f0a7f243b9bfcd881d", auth.getCnonce()); + Assert.assertEquals("00000001", auth.getNc()); + Assert.assertEquals("671742b00fae8d4c8ceb6a5bcf2b36fa", auth.getResponse()); + Assert.assertEquals("auth", auth.getQop()); + } + @Test public void handleBasicAuthentication() { Identity id = JunitTestHelper.createAndPersistIdentityAsUser("dav-user-" + UUID.randomUUID().toString()); diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 351e1bd695e..a2a1ed717dc 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -77,7 +77,7 @@ import org.junit.runners.Suite; org.olat.commons.coordinate.cluster.jms.JMSTest.class,//ok org.olat.commons.coordinate.cluster.lock.LockTest.class,//ok org.olat.commons.coordinate.CoordinatorTest.class,//ok - org.olat.core.commons.service.webdav.WebDAVTestCase.class,//ok + org.olat.core.commons.services.webdav.WebDAVTestCase.class,//ok org.olat.core.commons.services.webdav.manager.WebDAVManagerTest.class,//ok org.olat.core.commons.taskExecutor.PersistentTaskDAOTest.class,//ok org.olat.core.commons.taskExecutor.TaskExecutorManagerTest.class,//ok -- GitLab