001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.configuration; 029 030import org.opencms.file.CmsObject; 031import org.opencms.letsencrypt.CmsLetsEncryptConfiguration; 032import org.opencms.letsencrypt.CmsLetsEncryptConfiguration.Trigger; 033import org.opencms.letsencrypt.CmsSiteConfigToLetsEncryptConfigConverter; 034import org.opencms.main.CmsLog; 035import org.opencms.main.OpenCms; 036import org.opencms.report.CmsLogReport; 037import org.opencms.site.CmsSSLMode; 038import org.opencms.site.CmsSite; 039import org.opencms.site.CmsSiteManagerImpl; 040import org.opencms.site.CmsSiteMatcher; 041 042import java.util.HashSet; 043import java.util.Iterator; 044import java.util.Locale; 045import java.util.Map; 046import java.util.concurrent.ScheduledFuture; 047import java.util.concurrent.TimeUnit; 048 049import org.apache.commons.digester3.Digester; 050 051import org.dom4j.Element; 052 053/** 054 * Class to read and write the OpenCms site configuration.<p> 055 */ 056public class CmsSitesConfiguration extends A_CmsXmlConfiguration implements I_CmsXmlConfigurationWithUpdateHandler { 057 058 /** The "error" attribute. */ 059 public static final String A_ERROR = "error"; 060 061 /** The "errorPage" attribute. */ 062 public static final String A_ERROR_PAGE = "errorPage"; 063 064 /** The "exclusive" attribute. */ 065 public static final String A_EXCLUSIVE = "exclusive"; 066 067 /** The attribute name for the alias offset. */ 068 public static final String A_OFFSET = "offset"; 069 070 /** The "position" attribute. */ 071 public static final String A_POSITION = "position"; 072 073 /** The "server" attribute. */ 074 public static final String A_SERVER = "server"; 075 076 /** The "redirect" attribute. */ 077 public static final String A_REDIRECT = "redirect"; 078 079 /** The "title" attribute. */ 080 public static final String A_TITLE = "title"; 081 082 /** The ssl mode attribute.*/ 083 public static final String A_SSL = "sslmode"; 084 085 /** The "usePermanentRedirects" attribute. */ 086 public static final String A_USE_PERMANENT_REDIRECTS = "usePermanentRedirects"; 087 088 /** The "webserver" attribute. */ 089 public static final String A_WEBSERVER = "webserver"; 090 091 /** The name of the DTD for this configuration. */ 092 public static final String CONFIGURATION_DTD_NAME = "opencms-sites.dtd"; 093 094 /** The name of the default XML file for this configuration. */ 095 public static final String DEFAULT_XML_FILE_NAME = "opencms-sites.xml"; 096 097 /** The node name for the alias node. */ 098 public static final String N_ALIAS = "alias"; 099 100 /** The node name for the default-uri node. */ 101 public static final String N_DEFAULT_URI = "default-uri"; 102 103 /** The node name for the parameters. */ 104 public static final String N_PARAMETERS = "parameters"; 105 106 /** The node name for the secure site. */ 107 public static final String N_SECURE = "secure"; 108 109 /** Shared folder node name. */ 110 public static final String N_SHARED_FOLDER = "shared-folder"; 111 112 /** New secure modes node. */ 113 public static final String N_OLD_STYLE_SECURE_SERVER = "oldStyleSecureServer"; 114 115 /** The node name for the sites node. */ 116 public static final String N_SITES = "sites"; 117 118 /** The node name which indicates if apache should be configurable in sitemanager. */ 119 public static final String N_WEBSERVERSCRIPTING = "webserver-scripting"; 120 121 /** Configuration node name. */ 122 public static final String N_WEBSERVERSCRIPTING_CONFIGTEMPLATE = "configtemplate"; 123 124 /** Configuration node name. */ 125 public static final String N_WEBSERVERSCRIPTING_FILENAMEPREFIX = "filenameprefix"; 126 127 /** Configuration node name. */ 128 public static final String N_WEBSERVERSCRIPTING_LOGGINGDIR = "loggingdir"; 129 130 /** Configuration node name. */ 131 public static final String N_WEBSERVERSCRIPTING_SECURETEMPLATE = "securetemplate"; 132 133 /** Configuration node name. */ 134 public static final String N_WEBSERVERSCRIPTING_TARGETPATH = "targetpath"; 135 136 /** Configuration node name. */ 137 public static final String N_WEBSERVERSCRIPTING_WEBSERVERSCRIPT = "webserverscript"; 138 139 /** The node name for the workplace-server node. */ 140 public static final String N_WORKPLACE_SERVER = "workplace-server"; 141 142 /** The CmsObject with admin privileges. */ 143 private CmsObject m_adminCms; 144 145 /** The configured site manager. */ 146 private CmsSiteManagerImpl m_siteManager; 147 148 /** Future for the LetsEncrypt async update. */ 149 private ScheduledFuture<?> m_updateFuture; 150 151 /** 152 * @see org.opencms.configuration.I_CmsXmlConfiguration#addXmlDigesterRules(org.apache.commons.digester3.Digester) 153 */ 154 public void addXmlDigesterRules(Digester digester) { 155 156 // add site configuration rule 157 digester.addObjectCreate("*/" + N_SITES, CmsSiteManagerImpl.class); 158 digester.addCallMethod("*/" + N_SITES + "/" + N_WORKPLACE_SERVER, "addWorkplaceServer", 2); 159 digester.addCallParam("*/" + N_SITES + "/" + N_WORKPLACE_SERVER, 0); 160 digester.addCallParam("*/" + N_SITES + "/" + N_WORKPLACE_SERVER, 1, A_SSL); 161 digester.addCallMethod("*/" + N_SITES + "/" + N_DEFAULT_URI, "setDefaultUri", 0); 162 digester.addCallMethod("*/" + N_SITES + "/" + N_OLD_STYLE_SECURE_SERVER, "setOldStyleSecureServerAllowed", 0); 163 164 String configApachePath = "*/" + N_SITES + "/" + N_WEBSERVERSCRIPTING; 165 digester.addCallMethod(configApachePath, "setWebServerScripting", 6); 166 digester.addCallParam(configApachePath + "/" + N_WEBSERVERSCRIPTING_WEBSERVERSCRIPT, 0); 167 digester.addCallParam(configApachePath + "/" + N_WEBSERVERSCRIPTING_TARGETPATH, 1); 168 digester.addCallParam(configApachePath + "/" + N_WEBSERVERSCRIPTING_CONFIGTEMPLATE, 2); 169 digester.addCallParam(configApachePath + "/" + N_WEBSERVERSCRIPTING_SECURETEMPLATE, 3); 170 digester.addCallParam(configApachePath + "/" + N_WEBSERVERSCRIPTING_FILENAMEPREFIX, 4); 171 digester.addCallParam(configApachePath + "/" + N_WEBSERVERSCRIPTING_LOGGINGDIR, 5); 172 173 digester.addSetNext("*/" + N_SITES, "setSiteManager"); 174 175 // add site configuration rule 176 String siteXpath = "*/" + N_SITES + "/" + N_SITE; 177 178 digester.addCallMethod(siteXpath, "addSiteInternally", 11); 179 digester.addCallParam(siteXpath, 0, A_SERVER); 180 digester.addCallParam(siteXpath, 1, A_URI); 181 digester.addCallParam(siteXpath, 2, A_TITLE); 182 digester.addCallParam(siteXpath, 3, A_POSITION); 183 digester.addCallParam(siteXpath, 4, A_ERROR_PAGE); 184 digester.addCallParam(siteXpath, 5, A_WEBSERVER); 185 digester.addCallParam(siteXpath, 6, A_SSL); 186 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_SECURE, 7, A_SERVER); 187 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_SECURE, 8, A_EXCLUSIVE); 188 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_SECURE, 9, A_ERROR); 189 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_SECURE, 10, A_USE_PERMANENT_REDIRECTS); 190 digester.addCallMethod(siteXpath + "/" + N_PARAMETERS + "/" + N_PARAM, "addParamToConfigSite", 2); 191 digester.addCallParam(siteXpath + "/" + N_PARAMETERS + "/" + N_PARAM, 0, A_NAME); 192 digester.addCallParam(siteXpath + "/" + N_PARAMETERS + "/" + N_PARAM, 1); 193 // add an alias to the currently configured site 194 digester.addCallMethod("*/" + N_SITES + "/" + N_SITE + "/" + N_ALIAS, "addAliasToConfigSite", 3); 195 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_ALIAS, 0, A_SERVER); 196 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_ALIAS, 1, A_REDIRECT); 197 digester.addCallParam("*/" + N_SITES + "/" + N_SITE + "/" + N_ALIAS, 2, A_OFFSET); 198 199 digester.addCallMethod("*/" + N_SITES + "/" + N_SHARED_FOLDER, "setSharedFolder", 0); 200 201 } 202 203 /** 204 * @see org.opencms.configuration.I_CmsXmlConfiguration#generateXml(org.dom4j.Element) 205 */ 206 public Element generateXml(Element parent) { 207 208 // create <sites> node 209 Element sitesElement = parent.addElement(N_SITES); 210 if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { 211 m_siteManager = OpenCms.getSiteManager(); 212 } 213 Map<String, CmsSSLMode> workplaceMap = m_siteManager.getWorkplaceServersMap(); 214 for (String server : workplaceMap.keySet()) { 215 Element workplaceElement = sitesElement.addElement(N_WORKPLACE_SERVER).addText(server); 216 workplaceElement.addAttribute(A_SSL, workplaceMap.get(server).getXMLValue()); 217 } 218 sitesElement.addElement(N_DEFAULT_URI).addText(m_siteManager.getDefaultUri()); 219 String sharedFolder = m_siteManager.getSharedFolder(); 220 if (sharedFolder != null) { 221 sitesElement.addElement(N_SHARED_FOLDER).addText(sharedFolder); 222 } 223 String oldStyleSecureAllowed = String.valueOf(m_siteManager.isOldStyleSecureServerAllowed()); 224 sitesElement.addElement(N_OLD_STYLE_SECURE_SERVER).addText(oldStyleSecureAllowed); 225 if (m_siteManager.isConfigurableWebServer()) { 226 Element configServer = sitesElement.addElement(N_WEBSERVERSCRIPTING); 227 Map<String, String> configServerMap = m_siteManager.getWebServerConfig(); 228 configServer.addElement(N_WEBSERVERSCRIPTING_WEBSERVERSCRIPT).addText( 229 configServerMap.get(CmsSiteManagerImpl.WEB_SERVER_CONFIG_WEBSERVERSCRIPT)); 230 configServer.addElement(N_WEBSERVERSCRIPTING_TARGETPATH).addText( 231 configServerMap.get(CmsSiteManagerImpl.WEB_SERVER_CONFIG_TARGETPATH)); 232 configServer.addElement(N_WEBSERVERSCRIPTING_CONFIGTEMPLATE).addText( 233 configServerMap.get(CmsSiteManagerImpl.WEB_SERVER_CONFIG_CONFIGTEMPLATE)); 234 configServer.addElement(N_WEBSERVERSCRIPTING_SECURETEMPLATE).addText( 235 configServerMap.get(CmsSiteManagerImpl.WEB_SERVER_CONFIG_SECURETEMPLATE)); 236 configServer.addElement(N_WEBSERVERSCRIPTING_FILENAMEPREFIX).addText( 237 configServerMap.get(CmsSiteManagerImpl.WEB_SERVER_CONFIG_FILENAMEPREFIX)); 238 configServer.addElement(N_WEBSERVERSCRIPTING_LOGGINGDIR).addText( 239 configServerMap.get(CmsSiteManagerImpl.WEB_SERVER_CONFIG_LOGGINGDIR)); 240 } 241 Iterator<CmsSite> siteIterator = new HashSet<CmsSite>(m_siteManager.getSites().values()).iterator(); 242 while (siteIterator.hasNext()) { 243 CmsSite site = siteIterator.next(); 244 // create <site server="" uri=""/> subnode(s) 245 Element siteElement = sitesElement.addElement(N_SITE); 246 247 siteElement.addAttribute(A_SERVER, site.getSiteMatcher().toString()); 248 siteElement.addAttribute(A_URI, site.getSiteRoot().concat("/")); 249 siteElement.addAttribute(A_TITLE, site.getTitle()); 250 siteElement.addAttribute(A_POSITION, Float.toString(site.getPosition())); 251 siteElement.addAttribute(A_ERROR_PAGE, site.getErrorPage()); 252 siteElement.addAttribute(A_WEBSERVER, String.valueOf(site.isWebserver())); 253 siteElement.addAttribute(A_SSL, site.getSSLMode().getXMLValue()); 254 255 // create <secure server=""/> subnode 256 if (site.hasSecureServer()) { 257 Element secureElem = siteElement.addElement(N_SECURE); 258 secureElem.addAttribute(A_SERVER, site.getSecureUrl()); 259 260 secureElem.addAttribute(A_EXCLUSIVE, String.valueOf(site.isExclusiveUrl())); 261 secureElem.addAttribute(A_ERROR, String.valueOf(site.isExclusiveError())); 262 if (site.usesPermanentRedirects()) { 263 secureElem.addAttribute(A_USE_PERMANENT_REDIRECTS, Boolean.TRUE.toString()); 264 } 265 } 266 267 if ((site.getParameters() != null) && !site.getParameters().isEmpty()) { 268 Element parametersElem = siteElement.addElement(N_PARAMETERS); 269 for (Map.Entry<String, String> entry : site.getParameters().entrySet()) { 270 Element paramElem = parametersElem.addElement(N_PARAM); 271 paramElem.addAttribute(A_NAME, entry.getKey()); 272 paramElem.addText(entry.getValue()); 273 } 274 } 275 276 // create <alias server=""/> subnode(s) 277 Iterator<CmsSiteMatcher> aliasIterator = site.getAliases().iterator(); 278 while (aliasIterator.hasNext()) { 279 CmsSiteMatcher matcher = aliasIterator.next(); 280 Element aliasElement = siteElement.addElement(N_ALIAS); 281 aliasElement.addAttribute(A_SERVER, matcher.getUrl()); 282 aliasElement.addAttribute(A_REDIRECT, String.valueOf(matcher.isRedirect())); 283 if (matcher.getTimeOffset() != 0) { 284 aliasElement.addAttribute(A_OFFSET, "" + matcher.getTimeOffset()); 285 } 286 } 287 } 288 return sitesElement; 289 } 290 291 /** 292 * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdFilename() 293 */ 294 public String getDtdFilename() { 295 296 return CONFIGURATION_DTD_NAME; 297 } 298 299 /** 300 * Returns the site manager.<p> 301 * 302 * @return the site manager 303 */ 304 public CmsSiteManagerImpl getSiteManager() { 305 306 return m_siteManager; 307 } 308 309 /** 310 * @see org.opencms.configuration.I_CmsXmlConfigurationWithUpdateHandler#handleUpdate() 311 */ 312 public synchronized void handleUpdate() throws Exception { 313 314 CmsLetsEncryptConfiguration config = OpenCms.getLetsEncryptConfig(); 315 if ((config != null) && config.isValidAndEnabled() && (config.getTrigger() == Trigger.siteConfig)) { 316 317 // the configuration may be written several times in quick succession. We want to update when this 318 // happens for the last time, not the first, so we use a scheduled task. 319 320 if (m_updateFuture != null) { 321 m_updateFuture.cancel(false); 322 m_updateFuture = null; 323 } 324 m_updateFuture = OpenCms.getExecutor().schedule(new Runnable() { 325 326 @SuppressWarnings("synthetic-access") 327 public void run() { 328 329 m_updateFuture = null; 330 CmsLogReport report = new CmsLogReport( 331 Locale.ENGLISH, 332 org.opencms.letsencrypt.CmsSiteConfigToLetsEncryptConfigConverter.class); 333 CmsSiteConfigToLetsEncryptConfigConverter converter = new CmsSiteConfigToLetsEncryptConfigConverter( 334 config); 335 converter.run(report, OpenCms.getSiteManager()); 336 337 // TODO Auto-generated method stub 338 339 } 340 }, 5, TimeUnit.SECONDS); 341 } 342 343 } 344 345 /** 346 * @see org.opencms.configuration.I_CmsXmlConfigurationWithUpdateHandler#setCmsObject(org.opencms.file.CmsObject) 347 */ 348 public void setCmsObject(CmsObject cms) { 349 350 m_adminCms = cms; 351 } 352 353 /** 354 * Sets the site manager.<p> 355 * 356 * @param siteManager the site manager to set 357 */ 358 public void setSiteManager(CmsSiteManagerImpl siteManager) { 359 360 m_siteManager = siteManager; 361 if (CmsLog.INIT.isInfoEnabled()) { 362 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SITE_CONFIG_FINISHED_0)); 363 } 364 } 365 366 /** 367 * @see org.opencms.configuration.A_CmsXmlConfiguration#initMembers() 368 */ 369 @Override 370 protected void initMembers() { 371 372 setXmlFileName(DEFAULT_XML_FILE_NAME); 373 } 374}