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 GmbH & Co. KG, 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.site; 029 030import com.alkacon.simapi.CmykJpegReader.StringUtil; 031 032import org.opencms.configuration.CmsConfigurationException; 033import org.opencms.configuration.CmsSitesConfiguration; 034import org.opencms.db.CmsPublishedResource; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsProject; 037import org.opencms.file.CmsPropertyDefinition; 038import org.opencms.file.CmsResource; 039import org.opencms.file.CmsResourceFilter; 040import org.opencms.main.CmsContextInfo; 041import org.opencms.main.CmsEvent; 042import org.opencms.main.CmsException; 043import org.opencms.main.CmsLog; 044import org.opencms.main.CmsRuntimeException; 045import org.opencms.main.I_CmsEventListener; 046import org.opencms.main.OpenCms; 047import org.opencms.security.CmsOrganizationalUnit; 048import org.opencms.security.CmsPermissionSet; 049import org.opencms.security.CmsRole; 050import org.opencms.util.CmsFileUtil; 051import org.opencms.util.CmsStringUtil; 052import org.opencms.util.CmsUUID; 053 054import java.util.ArrayList; 055import java.util.Collections; 056import java.util.Comparator; 057import java.util.HashMap; 058import java.util.Iterator; 059import java.util.LinkedHashMap; 060import java.util.List; 061import java.util.Map; 062import java.util.Set; 063import java.util.SortedMap; 064import java.util.TreeMap; 065 066import javax.servlet.http.HttpServletRequest; 067import javax.servlet.http.HttpSession; 068 069import org.apache.commons.logging.Log; 070 071import com.google.common.base.Optional; 072import com.google.common.collect.Lists; 073import com.google.common.collect.Maps; 074 075/** 076 * Manages all configured sites in OpenCms.<p> 077 * 078 * To obtain the configured site manager instance, use {@link OpenCms#getSiteManager()}.<p> 079 * 080 * @since 7.0.2 081 */ 082public final class CmsSiteManagerImpl implements I_CmsEventListener { 083 084 /** The default shared folder name. */ 085 public static final String DEFAULT_SHARED_FOLDER = "shared"; 086 087 /** 088 * The VFS root path to the system shared folder, where shared content that belongs to modules, 089 * and that should not be edited by normal editors can be stored. 090 * The folder is searched in the gallery search when shared folders should be searched. 091 */ 092 public static final String PATH_SYSTEM_SHARED_FOLDER = "/system/shared/"; 093 094 /** A placeholder for the title of the shared folder. */ 095 public static final String SHARED_FOLDER_TITLE = "%SHARED_FOLDER%"; 096 097 /** Path to config template. */ 098 public static final String WEB_SERVER_CONFIG_CONFIGTEMPLATE = "configtemplate"; 099 100 /**prefix for files. */ 101 public static final String WEB_SERVER_CONFIG_FILENAMEPREFIX = "filenameprefix"; 102 103 /**Path to write logs to. */ 104 public static final String WEB_SERVER_CONFIG_LOGGINGDIR = "loggingdir"; 105 106 /** Path to secure template. */ 107 public static final String WEB_SERVER_CONFIG_SECURETEMPLATE = "securetemplate"; 108 109 /** Path to target. */ 110 public static final String WEB_SERVER_CONFIG_TARGETPATH = "targetpath"; 111 112 /** Path of webserver script.*/ 113 public static final String WEB_SERVER_CONFIG_WEBSERVERSCRIPT = "webserverscript"; 114 115 /** The static log object for this class. */ 116 private static final Log LOG = CmsLog.getLog(CmsSiteManagerImpl.class); 117 118 /** The path to the "/sites/" folder. */ 119 private static final String SITES_FOLDER = "/sites/"; 120 121 /** The length of the "/sites/" folder plus 1. */ 122 private static final int SITES_FOLDER_POS = SITES_FOLDER.length() + 1; 123 124 /** A list of additional site roots, that is site roots that are not below the "/sites/" folder. */ 125 private List<String> m_additionalSiteRoots; 126 127 /** 128 * The list of aliases for the site that is configured at the moment, 129 * needed for the sites added during configuration. */ 130 private List<CmsSiteMatcher> m_aliases; 131 132 /**Map with webserver scripting parameter. */ 133 private Map<String, String> m_apacheConfig; 134 135 /**CmsObject.*/ 136 private CmsObject m_clone; 137 138 /** The default site root. */ 139 private CmsSite m_defaultSite; 140 141 /** The default URI. */ 142 private String m_defaultUri; 143 144 /** Indicates if the configuration is finalized (frozen). */ 145 private boolean m_frozen; 146 147 /**Is the publish listener already set? */ 148 private boolean m_isListenerSet; 149 150 /**Old style secure server allowed? */ 151 private boolean m_oldStyleSecureServer; 152 153 /**Site which are only available for offline project. */ 154 private List<CmsSite> m_onlyOfflineSites; 155 156 /** The shared folder name. */ 157 private String m_sharedFolder; 158 159 /** Contains all configured site matchers in a list for direct access. */ 160 private List<CmsSiteMatcher> m_siteMatchers; 161 162 /** Maps site matchers to sites. */ 163 private Map<CmsSiteMatcher, CmsSite> m_siteMatcherSites; 164 165 /** Temporary store for site parameter values. */ 166 private SortedMap<String, String> m_siteParams; 167 168 /** Maps site roots to sites. */ 169 private Map<String, CmsSite> m_siteRootSites; 170 171 /**Map from CmsUUID to CmsSite.*/ 172 private Map<CmsUUID, CmsSite> m_siteUUIDs; 173 174 /** The workplace site matchers. */ 175 private List<CmsSiteMatcher> m_workplaceMatchers; 176 177 /** The workplace servers. */ 178 private Map<String, CmsSSLMode> m_workplaceServers; 179 180 /** 181 * Creates a new CmsSiteManager.<p> 182 * 183 */ 184 public CmsSiteManagerImpl() { 185 186 m_siteMatcherSites = new HashMap<CmsSiteMatcher, CmsSite>(); 187 m_siteRootSites = new HashMap<String, CmsSite>(); 188 m_aliases = new ArrayList<CmsSiteMatcher>(); 189 m_siteParams = new TreeMap<String, String>(); 190 m_additionalSiteRoots = new ArrayList<String>(); 191 m_workplaceServers = new LinkedHashMap<String, CmsSSLMode>(); 192 m_workplaceMatchers = new ArrayList<CmsSiteMatcher>(); 193 m_oldStyleSecureServer = true; 194 195 if (CmsLog.INIT.isInfoEnabled()) { 196 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_START_SITE_CONFIG_0)); 197 } 198 } 199 200 /** 201 * Adds an alias to the currently configured site. 202 * 203 * @param alias the URL of the alias server 204 * @param redirect <code>true</code> to always redirect to main URL 205 * @param offset the optional time offset for this alias 206 */ 207 public void addAliasToConfigSite(String alias, String redirect, String offset) { 208 209 long timeOffset = 0; 210 try { 211 timeOffset = Long.parseLong(offset); 212 } catch (Throwable e) { 213 // ignore 214 } 215 CmsSiteMatcher siteMatcher = new CmsSiteMatcher(alias, timeOffset); 216 boolean redirectVal = new Boolean(redirect).booleanValue(); 217 siteMatcher.setRedirect(redirectVal); 218 m_aliases.add(siteMatcher); 219 } 220 221 /** 222 * Adds a parameter to the currently configured site.<p> 223 * 224 * @param name the parameter name 225 * @param value the parameter value 226 */ 227 public void addParamToConfigSite(String name, String value) { 228 229 m_siteParams.put(name, value); 230 } 231 232 /** 233 * Adds a site.<p> 234 * 235 * @param cms the CMS object 236 * @param site the site to add 237 * 238 * @throws CmsException if something goes wrong 239 */ 240 public void addSite(CmsObject cms, CmsSite site) throws CmsException { 241 242 // check permissions 243 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { 244 // simple unit tests will have runlevel 1 and no CmsObject 245 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 246 } 247 248 // un-freeze 249 m_frozen = false; 250 251 // set aliases and parameters, they will be used in the addSite method 252 // this is necessary because of a digester workaround 253 m_siteParams = site.getParameters(); 254 m_aliases = site.getAliases(); 255 256 String secureUrl = null; 257 if (site.hasSecureServer()) { 258 secureUrl = site.getSecureUrl(); 259 } 260 261 // add the site 262 addSite( 263 site.getUrl(), 264 site.getSiteRoot(), 265 site.getTitle(), 266 Float.toString(site.getPosition()), 267 site.getErrorPage(), 268 Boolean.toString(site.isWebserver()), 269 site.getSSLMode().getXMLValue(), 270 secureUrl, 271 Boolean.toString(site.isExclusiveUrl()), 272 Boolean.toString(site.isExclusiveError()), 273 Boolean.toString(site.usesPermanentRedirects())); 274 275 // re-initialize, will freeze the state when finished 276 initialize(cms); 277 OpenCms.writeConfiguration(CmsSitesConfiguration.class); 278 } 279 280 /** 281 * Adds a new CmsSite to the list of configured sites, 282 * this is only allowed during configuration.<p> 283 * 284 * If this method is called after the configuration is finished, 285 * a <code>RuntimeException</code> is thrown.<p> 286 * 287 * @param server the Server 288 * @param uri the VFS path 289 * @param title the display title for this site 290 * @param position the display order for this site 291 * @param errorPage the URI to use as error page for this site 292 * @param sslMode the SSLMode of the site 293 * @param webserver indicates whether to write the web server configuration for this site or not 294 * @param secureServer a secure server, can be <code>null</code> 295 * @param exclusive if set to <code>true</code>, secure resources will only be available using the configured secure url 296 * @param error if exclusive, and set to <code>true</code> will generate a 404 error, 297 * if set to <code>false</code> will redirect to secure URL 298 * @param usePermanentRedirects if set to "true", permanent redirects should be used when redirecting to the secure URL 299 * 300 * @throws CmsConfigurationException if the site contains a server name, that is already assigned 301 */ 302 public void addSite( 303 String server, 304 String uri, 305 String title, 306 String position, 307 String errorPage, 308 String webserver, 309 String sslMode, 310 String secureServer, 311 String exclusive, 312 String error, 313 String usePermanentRedirects) 314 throws CmsConfigurationException { 315 316 if (m_frozen) { 317 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 318 } 319 320 if (getSiteRoots().contains(uri)) { 321 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_SITE_ALREADY_CONFIGURED_1, uri)); 322 } 323 324 if (CmsStringUtil.isEmptyOrWhitespaceOnly(server)) { 325 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_EMPTY_SERVER_URL_0)); 326 } 327 328 // create a new site object 329 CmsSiteMatcher matcher = new CmsSiteMatcher(server); 330 CmsSite site = new CmsSite(uri, matcher); 331 // set the title 332 site.setTitle(title); 333 // set the position 334 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(position)) { 335 float pos = Float.MAX_VALUE; 336 try { 337 pos = Float.parseFloat(position); 338 } catch (Throwable e) { 339 // m_position will have Float.MAX_VALUE, so this site will appear last 340 } 341 site.setPosition(pos); 342 } 343 // set the error page 344 site.setErrorPage(errorPage); 345 site.setWebserver(Boolean.valueOf(webserver).booleanValue()); 346 site.setSSLMode(CmsSSLMode.getModeFromXML(sslMode)); 347 if (CmsStringUtil.isNotEmpty(secureServer)) { 348 matcher = new CmsSiteMatcher(secureServer); 349 site.setSecureServer(matcher); 350 site.setExclusiveUrl(Boolean.valueOf(exclusive).booleanValue()); 351 site.setExclusiveError(Boolean.valueOf(error).booleanValue()); 352 site.setUsePermanentRedirects(Boolean.valueOf(usePermanentRedirects).booleanValue()); 353 } 354 355 // note that Digester first calls the addAliasToConfigSite method. 356 // therefore, the aliases are already set 357 site.setAliases(m_aliases); 358 359 boolean valid = true; 360 List<CmsSiteMatcher> toAdd = new ArrayList<CmsSiteMatcher>(); 361 for (CmsSiteMatcher matcherToAdd : site.getAllMatchers()) { 362 valid = valid & isServerValid(matcherToAdd) & !toAdd.contains(matcherToAdd); 363 toAdd.add(matcherToAdd); 364 } 365 366 if (!valid) { 367 throw new CmsConfigurationException( 368 Messages.get().container(Messages.ERR_DUPLICATE_SERVER_NAME_1, matcher.getUrl())); 369 } 370 371 for (CmsSiteMatcher matcherToAdd : site.getAllMatchers()) { 372 addServer(matcherToAdd, site); 373 } 374 375 m_aliases = new ArrayList<CmsSiteMatcher>(); 376 site.setParameters(m_siteParams); 377 m_siteParams = new TreeMap<String, String>(); 378 m_siteRootSites = new HashMap<String, CmsSite>(m_siteRootSites); 379 m_siteRootSites.put(site.getSiteRoot(), site); 380 if (CmsLog.INIT.isInfoEnabled()) { 381 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SITE_ROOT_ADDED_1, site.toString())); 382 } 383 } 384 385 /** 386 * Adds a new CmsSite to the list of configured sites, 387 * this is only allowed during configuration.<p> 388 * 389 * If this method is called after the configuration is finished, 390 * a <code>RuntimeException</code> is thrown.<p> 391 * 392 * @param server the Server 393 * @param uri the VFS path 394 * @param title the display title for this site 395 * @param position the display order for this site 396 * @param errorPage the URI to use as error page for this site 397 * @param sslMode the SSLMode of the site 398 * @param webserver indicates whether to write the web server configuration for this site or not 399 * @param secureServer a secure server, can be <code>null</code> 400 * @param exclusive if set to <code>true</code>, secure resources will only be available using the configured secure url 401 * @param error if exclusive, and set to <code>true</code> will generate a 404 error, 402 * if set to <code>false</code> will redirect to secure URL 403 * @param usePermanentRedirects if set to "true", permanent redirects should be used when redirecting to the secure URL 404 * 405 * @throws CmsConfigurationException in case the site was not configured correctly 406 * 407 */ 408 409 public void addSiteInternally( 410 String server, 411 String uri, 412 String title, 413 String position, 414 String errorPage, 415 String webserver, 416 String sslMode, 417 String secureServer, 418 String exclusive, 419 String error, 420 String usePermanentRedirects) 421 throws CmsConfigurationException { 422 423 try { 424 addSite( 425 server, 426 uri, 427 title, 428 position, 429 errorPage, 430 webserver, 431 sslMode, 432 secureServer, 433 exclusive, 434 error, 435 usePermanentRedirects); 436 437 } catch (CmsConfigurationException e) { 438 LOG.error("Error reading definitions. Try to read without aliase..", e); 439 440 //If the aliases are making problems, just remove them 441 m_aliases.clear(); 442 443 //If this fails, the webserver was defined before ->throw exception 444 addSite( 445 server, 446 uri, 447 title, 448 position, 449 errorPage, 450 webserver, 451 sslMode, 452 secureServer, 453 exclusive, 454 error, 455 usePermanentRedirects); 456 457 } 458 } 459 460 /** 461 * Adds a workplace server, this is only allowed during configuration.<p> 462 * 463 * @param workplaceServer the workplace server 464 * @param sslmode CmsSSLMode of workplace server 465 */ 466 public void addWorkplaceServer(String workplaceServer, String sslmode) { 467 468 if (m_frozen) { 469 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 470 } 471 if (!m_workplaceServers.containsKey(workplaceServer)) { 472 m_workplaceServers.put(workplaceServer, CmsSSLMode.getModeFromXML(sslmode)); 473 } 474 } 475 476 /** 477 * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) 478 */ 479 public void cmsEvent(CmsEvent event) { 480 481 try { 482 CmsProject project = getOfflineProject(); 483 m_clone.getRequestContext().setCurrentProject(project); 484 List<CmsPublishedResource> res = null; 485 486 List<CmsPublishedResource> foundSites = new ArrayList<CmsPublishedResource>(); 487 488 res = m_clone.readPublishedResources( 489 new CmsUUID((String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID))); 490 491 if (res != null) { 492 for (CmsPublishedResource r : res) { 493 if (!foundSites.contains(r)) { 494 if (m_siteUUIDs.containsKey(r.getStructureId())) { 495 foundSites.add(r); 496 } 497 } 498 } 499 } 500 project = m_clone.readProject(CmsProject.ONLINE_PROJECT_ID); 501 m_clone.getRequestContext().setCurrentProject(project); 502 Map<CmsSite, CmsSite> updateMap = new HashMap<CmsSite, CmsSite>(); 503 504 for (CmsPublishedResource r : foundSites) { 505 if (m_clone.existsResource(r.getStructureId())) { 506 //Resource was not deleted 507 CmsResource siteRoot = m_clone.readResource(r.getStructureId()); 508 if (!m_siteRootSites.containsKey(CmsFileUtil.removeTrailingSeparator(siteRoot.getRootPath())) 509 | m_onlyOfflineSites.contains(m_siteUUIDs.get(r.getStructureId()))) { 510 //Site was moved or site root was renamed.. or site was published the first time 511 CmsSite oldSite = m_siteUUIDs.get(siteRoot.getStructureId()); 512 CmsSite newSite = oldSite.clone(); 513 newSite.setSiteRoot(siteRoot.getRootPath()); 514 updateMap.put(oldSite, newSite); 515 } 516 } 517 } 518 519 for (CmsSite site : updateMap.keySet()) { 520 updateSite(m_clone, site, updateMap.get(site)); 521 } 522 } catch (CmsException e) { 523 LOG.error("Unable to handle publish event", e); 524 } 525 526 } 527 528 /** 529 * Returns all wrong configured sites.<p> 530 * 531 * @param cms CmsObject 532 * @param workplaceMode workplace mode 533 * @return List of CmsSite 534 */ 535 public List<CmsSite> getAvailableCorruptedSites(CmsObject cms, boolean workplaceMode) { 536 537 List<CmsSite> res = new ArrayList<CmsSite>(); 538 List<CmsSite> visSites = getAvailableSites(cms, workplaceMode); 539 Map<CmsSiteMatcher, CmsSite> allsites = getSites(); 540 for (CmsSiteMatcher matcher : allsites.keySet()) { 541 CmsSite site = allsites.get(matcher); 542 if (!visSites.contains(site) & !res.contains(site)) { 543 res.add(site); 544 } 545 } 546 return res; 547 } 548 549 /** 550 * Returns a list of all sites available (visible) for the current user.<p> 551 * 552 * @param cms the current OpenCms user context 553 * @param workplaceMode if true, the root and current site is included for the admin user 554 * and the view permission is required to see the site root 555 * 556 * @return a list of all sites available for the current user 557 */ 558 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode) { 559 560 return getAvailableSites(cms, workplaceMode, cms.getRequestContext().getOuFqn()); 561 } 562 563 /** 564 * Returns a list of all {@link CmsSite} instances that are compatible to the given organizational unit.<p> 565 * 566 * @param cms the current OpenCms user context 567 * @param workplaceMode if true, the root and current site is included for the admin user 568 * and the view permission is required to see the site root 569 * @param showShared if the shared folder should be shown 570 * @param ouFqn the organizational unit 571 * 572 * @return a list of all site available for the current user 573 */ 574 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode, boolean showShared, String ouFqn) { 575 576 return getAvailableSites(cms, workplaceMode, showShared, ouFqn, null); 577 } 578 579 /** 580 * Returns a list of all {@link CmsSite} instances that are compatible to the given organizational unit.<p> 581 * 582 * @param cms the current OpenCms user context 583 * @param workplaceMode if true, the root and current site is included for the admin user 584 * and the view permission is required to see the site root 585 * @param showShared if the shared folder should be shown 586 * @param ouFqn the organizational unit 587 * @param filterMode The CmsSLLMode to filter, null if no filter 588 * 589 * @return a list of all site available for the current user 590 */ 591 public List<CmsSite> getAvailableSites( 592 CmsObject cms, 593 boolean workplaceMode, 594 boolean showShared, 595 String ouFqn, 596 CmsSSLMode filterMode) { 597 598 List<String> siteroots = new ArrayList<String>(m_siteMatcherSites.size() + 1); 599 Map<String, CmsSiteMatcher> siteServers = new HashMap<String, CmsSiteMatcher>(m_siteMatcherSites.size() + 1); 600 List<CmsSite> result = new ArrayList<CmsSite>(m_siteMatcherSites.size() + 1); 601 602 Iterator<CmsSiteMatcher> i; 603 // add site list 604 i = m_siteMatcherSites.keySet().iterator(); 605 while (i.hasNext()) { 606 CmsSite site = m_siteMatcherSites.get(i.next()); 607 String folder = CmsFileUtil.addTrailingSeparator(site.getSiteRoot()); 608 if (!siteroots.contains(folder)) { 609 siteroots.add(folder); 610 siteServers.put(folder, site.getSiteMatcher()); 611 } 612 } 613 // add default site 614 if (workplaceMode && (m_defaultSite != null)) { 615 String folder = CmsFileUtil.addTrailingSeparator(m_defaultSite.getSiteRoot()); 616 if (!siteroots.contains(folder)) { 617 siteroots.add(folder); 618 } 619 } 620 621 String storedSiteRoot = cms.getRequestContext().getSiteRoot(); 622 try { 623 // for all operations here we need no context 624 cms.getRequestContext().setSiteRoot("/"); 625 if (workplaceMode && OpenCms.getRoleManager().hasRole(cms, CmsRole.VFS_MANAGER)) { 626 if (!siteroots.contains("/")) { 627 // add the root site if the user is in the workplace and has the required role 628 siteroots.add("/"); 629 } 630 if (!siteroots.contains(CmsFileUtil.addTrailingSeparator(storedSiteRoot))) { 631 siteroots.add(CmsFileUtil.addTrailingSeparator(storedSiteRoot)); 632 } 633 } 634 // add the shared site 635 String shared = OpenCms.getSiteManager().getSharedFolder(); 636 if (showShared && (shared != null) && !siteroots.contains(shared)) { 637 siteroots.add(shared); 638 } 639 // all sites are compatible for root admins in the root OU, skip unnecessary tests 640 boolean allCompatible = OpenCms.getRoleManager().hasRole(cms, CmsRole.ROOT_ADMIN) 641 && (ouFqn.isEmpty() || ouFqn.equals(CmsOrganizationalUnit.SEPARATOR)); 642 List<CmsResource> resources = Collections.emptyList(); 643 if (!allCompatible) { 644 try { 645 resources = OpenCms.getOrgUnitManager().getResourcesForOrganizationalUnit(cms, ouFqn); 646 } catch (CmsException e) { 647 return Collections.emptyList(); 648 } 649 } 650 Collections.sort(siteroots); // sort by resource name 651 Iterator<String> roots = siteroots.iterator(); 652 while (roots.hasNext()) { 653 String folder = roots.next(); 654 boolean compatible = allCompatible; 655 if (!compatible) { 656 Iterator<CmsResource> itResources = resources.iterator(); 657 while (itResources.hasNext()) { 658 CmsResource resource = itResources.next(); 659 if (resource.getRootPath().startsWith(folder) || folder.startsWith(resource.getRootPath())) { 660 compatible = true; 661 break; 662 } 663 } 664 } 665 // select only sites compatibles to the given organizational unit 666 if (compatible) { 667 try { 668 CmsResource res = cms.readResource(folder); 669 if (!workplaceMode 670 || cms.hasPermissions( 671 res, 672 CmsPermissionSet.ACCESS_VIEW, 673 false, 674 CmsResourceFilter.ONLY_VISIBLE)) { 675 676 // get the title and the position from the system configuration first 677 CmsSite configuredSite = m_siteRootSites.get(CmsFileUtil.removeTrailingSeparator(folder)); 678 679 // get the title 680 String title = null; 681 if ((configuredSite != null) 682 && CmsStringUtil.isNotEmptyOrWhitespaceOnly(configuredSite.getTitle())) { 683 title = configuredSite.getTitle(); 684 } 685 if (title == null) { 686 title = getSiteTitle(cms, res); 687 } 688 689 // get the position 690 String position = null; 691 if ((configuredSite != null) && (configuredSite.getPosition() != Float.MAX_VALUE)) { 692 position = Float.toString(configuredSite.getPosition()); 693 } 694 if (position == null) { 695 // not found, use the 'NavPos' property 696 position = cms.readPropertyObject( 697 res, 698 CmsPropertyDefinition.PROPERTY_NAVPOS, 699 false).getValue(); 700 } 701 if (configuredSite != null) { 702 float pos = Float.MAX_VALUE; 703 try { 704 pos = Float.parseFloat(position); 705 } catch (Throwable e) { 706 // m_position will have Float.MAX_VALUE, so this site will appear last 707 } 708 CmsSite clone = configuredSite.clone(); 709 clone.setPosition(pos); 710 clone.setTitle(title); 711 if (filterMode == null) { 712 result.add(clone); 713 } else { 714 if (filterMode.equals(clone.getSSLMode())) { 715 result.add(clone); 716 } 717 } 718 } else { 719 // add the site to the result 720 721 result.add( 722 new CmsSite( 723 folder, 724 res.getStructureId(), 725 title, 726 siteServers.get(folder), 727 position)); 728 } 729 } 730 } catch (CmsException e) { 731 // user probably has no read access to the folder, ignore and continue iterating 732 } 733 } 734 } 735 736 // sort and ensure that the shared folder is the last element in the list 737 Collections.sort(result, new Comparator<CmsSite>() { 738 739 public int compare(CmsSite o1, CmsSite o2) { 740 741 if (isSharedFolder(o1.getSiteRoot())) { 742 return +1; 743 } 744 if (isSharedFolder(o2.getSiteRoot())) { 745 return -1; 746 } 747 return o1.compareTo(o2); 748 } 749 }); 750 } catch (Throwable t) { 751 LOG.error(Messages.get().getBundle().key(Messages.LOG_READ_SITE_PROP_FAILED_0), t); 752 } finally { 753 // restore the user's current context 754 cms.getRequestContext().setSiteRoot(storedSiteRoot); 755 } 756 return result; 757 758 } 759 760 /** 761 * Returns a list of all sites available (visible) for the current user.<p> 762 * 763 * @param cms the current OpenCms user context 764 * @param workplaceMode if true, the root and current site is included for the admin user 765 * and the view permission is required to see the site root 766 * @param filterMode The CmsSLLMode to filter, null if no filter 767 * 768 * @return a list of all sites available for the current user 769 */ 770 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode, CmsSSLMode filterMode) { 771 772 return getAvailableSites(cms, workplaceMode, workplaceMode, cms.getRequestContext().getOuFqn(), filterMode); 773 } 774 775 /** 776 * Returns a list of all {@link CmsSite} instances that are compatible to the given organizational unit.<p> 777 * 778 * @param cms the current OpenCms user context 779 * @param workplaceMode if true, the root and current site is included for the admin user 780 * and the view permission is required to see the site root 781 * @param ouFqn the organizational unit 782 * 783 * @return a list of all site available for the current user 784 */ 785 public List<CmsSite> getAvailableSites(CmsObject cms, boolean workplaceMode, String ouFqn) { 786 787 return getAvailableSites(cms, workplaceMode, workplaceMode, ouFqn); 788 } 789 790 /** 791 * Returns the current site for the provided OpenCms user context object.<p> 792 * 793 * In the unlikely case that no site matches with the provided OpenCms user context, 794 * the default site is returned.<p> 795 * 796 * @param cms the OpenCms user context object to check for the site 797 * 798 * @return the current site for the provided OpenCms user context object 799 */ 800 public CmsSite getCurrentSite(CmsObject cms) { 801 802 CmsSite site = getSiteForSiteRoot(cms.getRequestContext().getSiteRoot()); 803 return (site == null) ? m_defaultSite : site; 804 } 805 806 /** 807 * Returns the default site.<p> 808 * 809 * @return the default site 810 */ 811 public CmsSite getDefaultSite() { 812 813 return m_defaultSite; 814 } 815 816 /** 817 * Returns the defaultUri.<p> 818 * 819 * @return the defaultUri 820 */ 821 public String getDefaultUri() { 822 823 return m_defaultUri; 824 } 825 826 /** 827 * Returns the shared folder path.<p> 828 * 829 * @return the shared folder path 830 */ 831 public String getSharedFolder() { 832 833 return m_sharedFolder; 834 } 835 836 /** 837 * Returns the site for the given resource path, using the fall back site root 838 * in case the resource path is no root path.<p> 839 * 840 * In case neither the given resource path, nor the given fall back site root 841 * matches any configured site, the default site is returned.<p> 842 * 843 * Usually the fall back site root should be taken from {@link org.opencms.file.CmsRequestContext#getSiteRoot()}, 844 * in which case a site for the site root should always exist.<p> 845 * 846 * This is the same as first calling {@link #getSiteForRootPath(String)} with the 847 * <code>resourcePath</code> parameter, and if this fails calling 848 * {@link #getSiteForSiteRoot(String)} with the <code>fallbackSiteRoot</code> parameter, 849 * and if this fails calling {@link #getDefaultSite()}.<p> 850 * 851 * @param rootPath the resource root path to get the site for 852 * @param fallbackSiteRoot site root to use in case the resource path is no root path 853 * 854 * @return the site for the given resource path, using the fall back site root 855 * in case the resource path is no root path 856 * 857 * @see #getSiteForRootPath(String) 858 */ 859 public CmsSite getSite(String rootPath, String fallbackSiteRoot) { 860 861 CmsSite result = getSiteForRootPath(rootPath); 862 if (result == null) { 863 result = getSiteForSiteRoot(fallbackSiteRoot); 864 if (result == null) { 865 result = getDefaultSite(); 866 } 867 } 868 return result; 869 } 870 871 /** 872 * Gets the site which is mapped to the default uri, or the 'absent' value of no such site exists.<p> 873 * 874 * @return the optional site mapped to the default uri 875 */ 876 public Optional<CmsSite> getSiteForDefaultUri() { 877 878 String defaultUri = getDefaultUri(); 879 CmsSite candidate = m_siteRootSites.get(CmsFileUtil.removeTrailingSeparator(defaultUri)); 880 return Optional.fromNullable(candidate); 881 } 882 883 /** 884 * Returns the site for the given resources root path, 885 * or <code>null</code> if the resources root path does not match any site.<p> 886 * 887 * @param rootPath the root path of a resource 888 * 889 * @return the site for the given resources root path, 890 * or <code>null</code> if the resources root path does not match any site 891 * 892 * @see #getSiteForSiteRoot(String) 893 * @see #getSiteRoot(String) 894 */ 895 public CmsSite getSiteForRootPath(String rootPath) { 896 897 if ((rootPath.length() > 0) && !rootPath.endsWith("/")) { 898 rootPath = rootPath + "/"; 899 } 900 // most sites will be below the "/sites/" folder, 901 CmsSite result = lookupSitesFolder(rootPath); 902 if (result != null) { 903 return result; 904 } 905 // look through all folders that are not below "/sites/" 906 String siteRoot = lookupAdditionalSite(rootPath); 907 return (siteRoot != null) ? getSiteForSiteRoot(siteRoot) : null; 908 } 909 910 /** 911 * Returns the site with has the provided site root, 912 * or <code>null</code> if no configured site has that site root.<p> 913 * 914 * The site root must have the form: 915 * <code>/sites/default</code>.<br> 916 * That means there must be a leading, but no trailing slash.<p> 917 * 918 * @param siteRoot the site root to look up the site for 919 * 920 * @return the site with has the provided site root, 921 * or <code>null</code> if no configured site has that site root 922 * 923 * @see #getSiteForRootPath(String) 924 */ 925 public CmsSite getSiteForSiteRoot(String siteRoot) { 926 927 return m_siteRootSites.get(siteRoot); 928 } 929 930 /** 931 * Returns the site root part for the given resources root path, 932 * or <code>null</code> if the given resources root path does not match any site root.<p> 933 * 934 * The site root returned will have the form: 935 * <code>/sites/default</code>.<br> 936 * That means there will a leading, but no trailing slash.<p> 937 * 938 * @param rootPath the root path of a resource 939 * 940 * @return the site root part of the resources root path, 941 * or <code>null</code> if the path does not match any site root 942 * 943 * @see #getSiteForRootPath(String) 944 */ 945 public String getSiteRoot(String rootPath) { 946 947 // add a trailing slash, because the path may be the path of a site root itself 948 if (!rootPath.endsWith("/")) { 949 rootPath = rootPath + "/"; 950 } 951 // most sites will be below the "/sites/" folder, 952 CmsSite site = lookupSitesFolder(rootPath); 953 if (site != null) { 954 return site.getSiteRoot(); 955 } 956 // look through all folders that are not below "/sites/" 957 return lookupAdditionalSite(rootPath); 958 } 959 960 /** 961 * Returns an unmodifiable set of all configured site roots (Strings).<p> 962 * 963 * @return an unmodifiable set of all configured site roots (Strings) 964 */ 965 public Set<String> getSiteRoots() { 966 967 return m_siteRootSites.keySet(); 968 } 969 970 /** 971 * Returns the map of configured sites, using 972 * {@link CmsSiteMatcher} objects as keys and {@link CmsSite} objects as values.<p> 973 * 974 * @return the map of configured sites, using {@link CmsSiteMatcher} 975 * objects as keys and {@link CmsSite} objects as values 976 */ 977 public Map<CmsSiteMatcher, CmsSite> getSites() { 978 979 return m_siteMatcherSites; 980 } 981 982 /** 983 * Returns the site title.<p> 984 * 985 * @param cms the cms context 986 * @param resource the site root resource 987 * 988 * @return the title 989 * 990 * @throws CmsException in case reading the title property fails 991 */ 992 public String getSiteTitle(CmsObject cms, CmsResource resource) throws CmsException { 993 994 String title = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue(); 995 if (title == null) { 996 title = resource.getRootPath(); 997 } 998 if (resource.getRootPath().equals(getSharedFolder())) { 999 title = SHARED_FOLDER_TITLE; 1000 } 1001 return title; 1002 } 1003 1004 /** 1005 * Gets the SSLMode for given workplace server.<p> 1006 * 1007 * @param server to obtain ssl mode for 1008 * @return CmsSSLMode 1009 */ 1010 public CmsSSLMode getSSLModeForWorkplaceServer(String server) { 1011 1012 if (server == null) { 1013 return CmsSSLMode.NO; 1014 } 1015 if (!m_workplaceServers.containsKey(server)) { 1016 return CmsSSLMode.NO; 1017 } 1018 1019 return m_workplaceServers.get(server); 1020 } 1021 1022 /** 1023 * Get web server scripting configurations.<p> 1024 * 1025 * @return Map with configuration data 1026 */ 1027 public Map<String, String> getWebServerConfig() { 1028 1029 return m_apacheConfig; 1030 } 1031 1032 /** 1033 * Returns the workplace server.<p> 1034 * 1035 * @return the workplace server 1036 */ 1037 public String getWorkplaceServer() { 1038 1039 return m_workplaceServers.keySet().isEmpty() ? null : m_workplaceServers.keySet().iterator().next(); 1040 } 1041 1042 /** 1043 * Returns the configured worklace servers.<p> 1044 * 1045 * @return the workplace servers 1046 */ 1047 public List<String> getWorkplaceServers() { 1048 1049 return Collections.unmodifiableList(new ArrayList<String>(m_workplaceServers.keySet())); 1050 } 1051 1052 /** 1053 * Returns the configured worklace servers.<p> 1054 * 1055 * @param filterMode CmsSSLMode to filter results for. 1056 * @return the workplace servers 1057 */ 1058 public List<String> getWorkplaceServers(CmsSSLMode filterMode) { 1059 1060 if (filterMode == null) { 1061 return getWorkplaceServers(); 1062 } 1063 List<String> ret = new ArrayList<String>(); 1064 for (String server : m_workplaceServers.keySet()) { 1065 if (m_workplaceServers.get(server).equals(filterMode)) { 1066 ret.add(server); 1067 } 1068 } 1069 return ret; 1070 } 1071 1072 /** 1073 * Returns the configured worklace servers.<p> 1074 * 1075 * @return the workplace servers 1076 */ 1077 public Map<String, CmsSSLMode> getWorkplaceServersMap() { 1078 1079 return Collections.unmodifiableMap(m_workplaceServers); 1080 } 1081 1082 /** 1083 * Returns the site matcher that matches the workplace site.<p> 1084 * 1085 * @return the site matcher that matches the workplace site 1086 */ 1087 public CmsSiteMatcher getWorkplaceSiteMatcher() { 1088 1089 return m_workplaceMatchers.isEmpty() ? null : m_workplaceMatchers.get(0); 1090 } 1091 1092 /** 1093 * Initializes the site manager with the OpenCms system configuration.<p> 1094 * 1095 * @param cms an OpenCms context object that must have been initialized with "Admin" permissions 1096 */ 1097 public void initialize(CmsObject cms) { 1098 1099 if (CmsLog.INIT.isInfoEnabled()) { 1100 CmsLog.INIT.info( 1101 Messages.get().getBundle().key( 1102 Messages.INIT_NUM_SITE_ROOTS_CONFIGURED_1, 1103 new Integer((m_siteMatcherSites.size() + ((m_defaultUri != null) ? 1 : 0))))); 1104 } 1105 1106 try { 1107 1108 m_clone = OpenCms.initCmsObject(cms); 1109 m_clone.getRequestContext().setSiteRoot(""); 1110 m_clone.getRequestContext().setCurrentProject(m_clone.readProject(CmsProject.ONLINE_PROJECT_NAME)); 1111 1112 CmsObject cms_offline = OpenCms.initCmsObject(m_clone); 1113 CmsProject tempProject = cms_offline.createProject( 1114 "tempProjectSites", 1115 "", 1116 "/Users", 1117 "/Users", 1118 CmsProject.PROJECT_TYPE_TEMPORARY); 1119 cms_offline.getRequestContext().setCurrentProject(tempProject); 1120 1121 m_siteUUIDs = new HashMap<CmsUUID, CmsSite>(); 1122 // check the presence of sites in VFS 1123 1124 m_onlyOfflineSites = new ArrayList<CmsSite>(); 1125 1126 for (CmsSite site : m_siteMatcherSites.values()) { 1127 checkUUIDOfSiteRoot(site, m_clone, cms_offline); 1128 try { 1129 CmsResource siteRes = m_clone.readResource(site.getSiteRoot()); 1130 site.setSiteRootUUID(siteRes.getStructureId()); 1131 1132 m_siteUUIDs.put(siteRes.getStructureId(), site); 1133 // during server startup the digester can not access properties, so set the title afterwards 1134 if (CmsStringUtil.isEmptyOrWhitespaceOnly(site.getTitle())) { 1135 String title = m_clone.readPropertyObject( 1136 siteRes, 1137 CmsPropertyDefinition.PROPERTY_TITLE, 1138 false).getValue(); 1139 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(title)) { 1140 site.setTitle(title); 1141 } 1142 } 1143 } catch (Throwable t) { 1144 if (CmsLog.INIT.isWarnEnabled()) { 1145 CmsLog.INIT.warn(Messages.get().getBundle().key(Messages.INIT_NO_ROOT_FOLDER_1, site)); 1146 } 1147 } 1148 } 1149 cms_offline.deleteProject(tempProject.getUuid()); 1150 1151 // check the presence of the default site in VFS 1152 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_defaultUri)) { 1153 m_defaultSite = null; 1154 } else { 1155 m_defaultSite = new CmsSite(m_defaultUri, CmsSiteMatcher.DEFAULT_MATCHER); 1156 try { 1157 m_clone.readResource(m_defaultSite.getSiteRoot()); 1158 } catch (Throwable t) { 1159 if (CmsLog.INIT.isWarnEnabled()) { 1160 CmsLog.INIT.warn( 1161 Messages.get().getBundle().key(Messages.INIT_NO_ROOT_FOLDER_DEFAULT_SITE_1, m_defaultSite)); 1162 } 1163 } 1164 } 1165 if (m_defaultSite == null) { 1166 m_defaultSite = new CmsSite("/", CmsSiteMatcher.DEFAULT_MATCHER); 1167 } 1168 if (CmsLog.INIT.isInfoEnabled()) { 1169 if (m_defaultSite != null) { 1170 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DEFAULT_SITE_ROOT_1, m_defaultSite)); 1171 } else { 1172 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DEFAULT_SITE_ROOT_0)); 1173 } 1174 } 1175 initWorkplaceMatchers(); 1176 1177 // set site lists to unmodifiable 1178 setSiteMatcherSites(m_siteMatcherSites); 1179 1180 // store additional site roots to optimize lookups later 1181 for (String root : m_siteRootSites.keySet()) { 1182 if (!root.startsWith(SITES_FOLDER) || (root.split("/").length >= 4)) { 1183 m_additionalSiteRoots.add(root); 1184 } 1185 } 1186 1187 if (m_sharedFolder == null) { 1188 m_sharedFolder = DEFAULT_SHARED_FOLDER; 1189 } 1190 1191 // initialization is done, set the frozen flag to true 1192 m_frozen = true; 1193 } catch (CmsException e) { 1194 LOG.warn(e); 1195 } 1196 if (!m_isListenerSet) { 1197 OpenCms.addCmsEventListener(this, new int[] {I_CmsEventListener.EVENT_PUBLISH_PROJECT}); 1198 m_isListenerSet = true; 1199 } 1200 } 1201 1202 /** 1203 * Checks if web server scripting is enabled.<p> 1204 * 1205 * @return true if web server scripting is set to available 1206 */ 1207 public boolean isConfigurableWebServer() { 1208 1209 return m_apacheConfig != null; 1210 } 1211 1212 /** 1213 * Returns <code>true</code> if the given site matcher matches any configured site, 1214 * which includes the workplace site.<p> 1215 * 1216 * @param matcher the site matcher to match the site with 1217 * 1218 * @return <code>true</code> if the matcher matches a site 1219 */ 1220 public boolean isMatching(CmsSiteMatcher matcher) { 1221 1222 boolean result = m_siteMatcherSites.get(matcher) != null; 1223 if (!result) { 1224 // try to match the workplace site 1225 result = isWorkplaceRequest(matcher); 1226 } 1227 return result; 1228 } 1229 1230 /** 1231 * Returns <code>true</code> if the given site matcher matches the current site.<p> 1232 * 1233 * @param cms the current OpenCms user context 1234 * @param matcher the site matcher to match the site with 1235 * 1236 * @return <code>true</code> if the matcher matches the current site 1237 */ 1238 public boolean isMatchingCurrentSite(CmsObject cms, CmsSiteMatcher matcher) { 1239 1240 return m_siteMatcherSites.get(matcher) == getCurrentSite(cms); 1241 } 1242 1243 /** 1244 * Checks if old style secure server is allowed.<p> 1245 * 1246 * @return boolean 1247 */ 1248 public boolean isOldStyleSecureServerAllowed() { 1249 1250 return m_oldStyleSecureServer; 1251 } 1252 1253 /** 1254 * Indicates if given site is only available for offline repository.<p> 1255 * 1256 * @param site to be looked up 1257 * @return true if only offline exists, false otherwise 1258 */ 1259 public boolean isOnlyOfflineSite(CmsSite site) { 1260 1261 return m_onlyOfflineSites.contains(site); 1262 } 1263 1264 /** 1265 * Checks if the given path is that of a shared folder.<p> 1266 * 1267 * @param name a path prefix 1268 * 1269 * @return true if the given prefix represents a shared folder 1270 */ 1271 public boolean isSharedFolder(String name) { 1272 1273 return (m_sharedFolder != null) && m_sharedFolder.equals(CmsStringUtil.joinPaths("/", name, "/")); 1274 } 1275 1276 /** 1277 * Checks whether a given root path is a site root.<p> 1278 * 1279 * @param rootPath a root path 1280 * 1281 * @return true if the given path is the path of a site root 1282 */ 1283 public boolean isSiteRoot(String rootPath) { 1284 1285 String siteRoot = getSiteRoot(rootPath); 1286 rootPath = CmsStringUtil.joinPaths(rootPath, "/"); 1287 return rootPath.equals(siteRoot); 1288 1289 } 1290 1291 /** 1292 * Checks if a given site is under another site.<p> 1293 * 1294 * @param site CmsSite to check 1295 * @return true if given site is invalid 1296 */ 1297 public boolean isSiteUnderSite(CmsSite site) { 1298 1299 return isSiteUnderSite(site.getSiteRoot()); 1300 } 1301 1302 /** 1303 * Checks if a given site is under another site.<p> 1304 * 1305 * @param siteRootPath site root path to check 1306 * @return true if given site is invalid 1307 */ 1308 public boolean isSiteUnderSite(String siteRootPath) { 1309 1310 for (String siteRoot : getSiteRoots()) { 1311 if ((siteRootPath.length() > siteRoot.length()) 1312 & siteRootPath.startsWith(CmsFileUtil.addTrailingSeparator(siteRoot))) { 1313 return true; 1314 } 1315 } 1316 return false; 1317 } 1318 1319 /** 1320 * Returns <code>true</code> if the given site matcher matches the configured OpenCms workplace.<p> 1321 * 1322 * @param matcher the site matcher to match the site with 1323 * 1324 * @return <code>true</code> if the given site matcher matches the configured OpenCms workplace 1325 */ 1326 public boolean isWorkplaceRequest(CmsSiteMatcher matcher) { 1327 1328 return m_workplaceMatchers.contains(matcher); 1329 } 1330 1331 /** 1332 * Returns <code>true</code> if the given request is against the configured OpenCms workplace.<p> 1333 * 1334 * @param req the request to match 1335 * 1336 * @return <code>true</code> if the given request is against the configured OpenCms workplace 1337 */ 1338 public boolean isWorkplaceRequest(HttpServletRequest req) { 1339 1340 if (req == null) { 1341 // this may be true inside a static export test case scenario 1342 return false; 1343 } 1344 return isWorkplaceRequest(getRequestMatcher(req)); 1345 } 1346 1347 /** 1348 * Matches the given request against all configures sites and returns 1349 * the matching site, or the default site if no sites matches.<p> 1350 * 1351 * @param req the request to match 1352 * 1353 * @return the matching site, or the default site if no sites matches 1354 */ 1355 public CmsSite matchRequest(HttpServletRequest req) { 1356 1357 CmsSiteMatcher matcher = getRequestMatcher(req); 1358 if (matcher.getTimeOffset() != 0) { 1359 HttpSession session = req.getSession(); 1360 if (session != null) { 1361 session.setAttribute( 1362 CmsContextInfo.ATTRIBUTE_REQUEST_TIME, 1363 new Long(System.currentTimeMillis() + matcher.getTimeOffset())); 1364 } 1365 } 1366 CmsSite site = matchSite(matcher); 1367 1368 if (LOG.isDebugEnabled()) { 1369 String requestServer = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort(); 1370 LOG.debug( 1371 Messages.get().getBundle().key( 1372 Messages.LOG_MATCHING_REQUEST_TO_SITE_2, 1373 requestServer, 1374 site.toString())); 1375 } 1376 return site; 1377 } 1378 1379 /** 1380 * Return the configured site that matches the given site matcher, 1381 * or the default site if no sites matches.<p> 1382 * 1383 * @param matcher the site matcher to match the site with 1384 * @return the matching site, or the default site if no sites matches 1385 */ 1386 public CmsSite matchSite(CmsSiteMatcher matcher) { 1387 1388 CmsSite site = m_siteMatcherSites.get(matcher); 1389 if (site == null) { 1390 // return the default site (might be null as well) 1391 site = m_defaultSite; 1392 } 1393 return site; 1394 } 1395 1396 /** 1397 * Removes a site from the list of configured sites.<p> 1398 * 1399 * @param cms the cms object 1400 * @param site the site to remove 1401 * 1402 * @throws CmsException if something goes wrong 1403 */ 1404 public void removeSite(CmsObject cms, CmsSite site) throws CmsException { 1405 1406 // check permissions 1407 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { 1408 // simple unit tests will have runlevel 1 and no CmsObject 1409 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 1410 } 1411 1412 // un-freeze 1413 m_frozen = false; 1414 1415 // create a new map containing all existing sites without the one to remove 1416 Map<CmsSiteMatcher, CmsSite> siteMatcherSites = new HashMap<CmsSiteMatcher, CmsSite>(); 1417 List<CmsSiteMatcher> matchersForSite = site.getAllMatchers(); 1418 for (Map.Entry<CmsSiteMatcher, CmsSite> entry : m_siteMatcherSites.entrySet()) { 1419 if (!(matchersForSite.contains(entry.getKey()))) { 1420 // entry not the site itself nor an alias of the site nor the secure URL of the site, so add it 1421 siteMatcherSites.put(entry.getKey(), entry.getValue()); 1422 } 1423 } 1424 setSiteMatcherSites(siteMatcherSites); 1425 1426 // remove the site from the map holding the site roots as keys and the sites as values 1427 Map<String, CmsSite> siteRootSites = new HashMap<String, CmsSite>(m_siteRootSites); 1428 siteRootSites.remove(site.getSiteRoot()); 1429 m_siteRootSites = Collections.unmodifiableMap(siteRootSites); 1430 1431 // re-initialize, will freeze the state when finished 1432 initialize(cms); 1433 OpenCms.writeConfiguration(CmsSitesConfiguration.class); 1434 } 1435 1436 /** 1437 * Sets the default URI, this is only allowed during configuration.<p> 1438 * 1439 * If this method is called after the configuration is finished, 1440 * a <code>RuntimeException</code> is thrown.<p> 1441 * 1442 * @param defaultUri the defaultUri to set 1443 */ 1444 public void setDefaultUri(String defaultUri) { 1445 1446 if (m_frozen) { 1447 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 1448 } 1449 m_defaultUri = defaultUri; 1450 } 1451 1452 /** 1453 * Sets the old style secure server boolean.<p> 1454 * 1455 * @param value value 1456 */ 1457 public void setOldStyleSecureServerAllowed(String value) { 1458 1459 m_oldStyleSecureServer = Boolean.parseBoolean(StringUtil.toLowerCase(value)); 1460 } 1461 1462 /** 1463 * Sets the shared folder path.<p> 1464 * 1465 * @param sharedFolder the shared folder path 1466 */ 1467 public void setSharedFolder(String sharedFolder) { 1468 1469 if (m_frozen) { 1470 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_CONFIG_FROZEN_0)); 1471 } 1472 m_sharedFolder = CmsStringUtil.joinPaths("/", sharedFolder, "/"); 1473 } 1474 1475 /** 1476 * Set webserver script configuration.<p> 1477 * 1478 * 1479 * @param webserverscript path 1480 * @param targetpath path 1481 * @param configtemplate path 1482 * @param securetemplate path 1483 * @param filenameprefix to add to files 1484 * @param loggingdir path 1485 */ 1486 public void setWebServerScripting( 1487 String webserverscript, 1488 String targetpath, 1489 String configtemplate, 1490 String securetemplate, 1491 String filenameprefix, 1492 String loggingdir) { 1493 1494 m_apacheConfig = new HashMap<String, String>(); 1495 m_apacheConfig.put(WEB_SERVER_CONFIG_WEBSERVERSCRIPT, webserverscript); 1496 m_apacheConfig.put(WEB_SERVER_CONFIG_TARGETPATH, targetpath); 1497 m_apacheConfig.put(WEB_SERVER_CONFIG_CONFIGTEMPLATE, configtemplate); 1498 m_apacheConfig.put(WEB_SERVER_CONFIG_SECURETEMPLATE, securetemplate); 1499 m_apacheConfig.put(WEB_SERVER_CONFIG_FILENAMEPREFIX, filenameprefix); 1500 m_apacheConfig.put(WEB_SERVER_CONFIG_LOGGINGDIR, loggingdir); 1501 } 1502 1503 /** 1504 * Returns true if the path starts with the shared folder path.<p> 1505 * 1506 * @param path the path to check 1507 * 1508 * @return true if the path starts with the shared folder path 1509 */ 1510 public boolean startsWithShared(String path) { 1511 1512 return (m_sharedFolder != null) && CmsFileUtil.addTrailingSeparator(path).startsWith(m_sharedFolder); 1513 } 1514 1515 /** 1516 * Method for backward compability reasons. Not sure if really needed //TODO check! 1517 * CmsSSLMode are set to No as default.<p> 1518 * 1519 * @param cms the cms to use 1520 * @param defaultUri the default URI 1521 * @param workplaceServersList the workplace server URLs 1522 * @param sharedFolder the shared folder URI 1523 * 1524 * @throws CmsException if something goes wrong 1525 */ 1526 public void updateGeneralSettings( 1527 CmsObject cms, 1528 String defaultUri, 1529 List<String> workplaceServersList, 1530 String sharedFolder) 1531 throws CmsException { 1532 1533 Map<String, CmsSSLMode> workplaceServers = new LinkedHashMap<String, CmsSSLMode>(); 1534 for (String server : workplaceServersList) { 1535 if (m_workplaceServers.containsKey(server)) { 1536 workplaceServers.put(server, m_workplaceServers.get(server)); 1537 } else { 1538 workplaceServers.put(server, CmsSSLMode.NO); 1539 } 1540 } 1541 updateGeneralSettings(cms, defaultUri, workplaceServers, sharedFolder); 1542 } 1543 1544 /** 1545 * Updates the general settings.<p> 1546 * 1547 * @param cms the cms to use 1548 * @param defaulrUri the default URI 1549 * @param workplaceServers the workplace server URLs 1550 * @param sharedFolder the shared folder URI 1551 * 1552 * @throws CmsException if something goes wrong 1553 */ 1554 public void updateGeneralSettings( 1555 CmsObject cms, 1556 String defaulrUri, 1557 Map<String, CmsSSLMode> workplaceServers, 1558 String sharedFolder) 1559 throws CmsException { 1560 1561 CmsObject clone = OpenCms.initCmsObject(cms); 1562 clone.getRequestContext().setSiteRoot(""); 1563 1564 // set the shared folder 1565 if ((sharedFolder == null) 1566 || sharedFolder.equals("") 1567 || sharedFolder.equals("/") 1568 || !sharedFolder.startsWith("/") 1569 || !sharedFolder.endsWith("/") 1570 || sharedFolder.startsWith("/sites/")) { 1571 throw new CmsException( 1572 Messages.get().container(Messages.ERR_INVALID_PATH_FOR_SHARED_FOLDER_1, sharedFolder)); 1573 } 1574 1575 m_frozen = false; 1576 setDefaultUri(clone.readResource(defaulrUri).getRootPath()); 1577 setSharedFolder(clone.readResource(sharedFolder).getRootPath()); 1578 m_workplaceServers = workplaceServers; 1579 initialize(cms); 1580 m_frozen = true; 1581 } 1582 1583 /** 1584 * Updates or creates a site.<p> 1585 * 1586 * @param cms the CMS object 1587 * @param oldSite the site to remove if not <code>null</code> 1588 * @param newSite the site to add if not <code>null</code> 1589 * 1590 * @throws CmsException if something goes wrong 1591 */ 1592 public void updateSite(CmsObject cms, CmsSite oldSite, CmsSite newSite) throws CmsException { 1593 1594 if (oldSite != null) { 1595 // remove the old site 1596 removeSite(cms, oldSite); 1597 } 1598 1599 if (newSite != null) { 1600 // add the new site 1601 addSite(cms, newSite); 1602 } 1603 } 1604 1605 /** 1606 * Returns true if this request goes to a secure site.<p> 1607 * 1608 * @param req the request to check 1609 * 1610 * @return true if the request goes to a secure site 1611 */ 1612 public boolean usesSecureSite(HttpServletRequest req) { 1613 1614 CmsSite site = matchRequest(req); 1615 if (site == null) { 1616 return false; 1617 } 1618 CmsSiteMatcher secureMatcher = site.getSecureServerMatcher(); 1619 boolean result = false; 1620 if (secureMatcher != null) { 1621 result = secureMatcher.equals(getRequestMatcher(req)); 1622 } 1623 return result; 1624 } 1625 1626 /** 1627 * Adds a new Site matcher object to the map of server names. 1628 * 1629 * @param matcher the SiteMatcher of the server 1630 * @param site the site to add 1631 */ 1632 private void addServer(CmsSiteMatcher matcher, CmsSite site) { 1633 1634 Map<CmsSiteMatcher, CmsSite> siteMatcherSites = new HashMap<CmsSiteMatcher, CmsSite>(m_siteMatcherSites); 1635 siteMatcherSites.put(matcher, site); 1636 setSiteMatcherSites(siteMatcherSites); 1637 } 1638 1639 /** 1640 * Fetches UUID for given site root from online and offline repository.<p> 1641 * 1642 * @param site to read and set UUID for 1643 * @param clone online CmsObject 1644 * @param cms_offline offline CmsObject 1645 */ 1646 private void checkUUIDOfSiteRoot(CmsSite site, CmsObject clone, CmsObject cms_offline) { 1647 1648 CmsUUID id = null; 1649 try { 1650 id = clone.readResource(site.getSiteRoot()).getStructureId(); 1651 } catch (CmsException e) { 1652 //Ok, site root not available for online repository. 1653 } 1654 1655 if (id == null) { 1656 try { 1657 id = cms_offline.readResource(site.getSiteRoot()).getStructureId(); 1658 m_onlyOfflineSites.add(site); 1659 } catch (CmsException e) { 1660 //Siteroot not valid for on- and offline repository. 1661 } 1662 } 1663 if (id != null) { 1664 site.setSiteRootUUID(id); 1665 m_siteUUIDs.put(id, site); 1666 } 1667 } 1668 1669 /** 1670 * Gets an offline project to read offline resources from.<p> 1671 * 1672 * @return CmsProject 1673 */ 1674 private CmsProject getOfflineProject() { 1675 1676 try { 1677 return m_clone.readProject("Offline"); 1678 } catch (CmsException e) { 1679 try { 1680 for (CmsProject p : OpenCms.getOrgUnitManager().getAllAccessibleProjects(m_clone, "/", true)) { 1681 if (!p.isOnlineProject()) { 1682 return p; 1683 } 1684 } 1685 } catch (CmsException e1) { 1686 LOG.error("Unable to get ptoject", e); 1687 } 1688 } 1689 return null; 1690 } 1691 1692 /** 1693 * Returns the site matcher for the given request.<p> 1694 * 1695 * @param req the request to get the site matcher for 1696 * 1697 * @return the site matcher for the given request 1698 */ 1699 private CmsSiteMatcher getRequestMatcher(HttpServletRequest req) { 1700 1701 CmsSiteMatcher matcher = new CmsSiteMatcher(req.getScheme(), req.getServerName(), req.getServerPort()); 1702 // this is required to get the right configured time offset 1703 int index = m_siteMatchers.indexOf(matcher); 1704 if (index < 0) { 1705 return matcher; 1706 } 1707 return m_siteMatchers.get(index); 1708 } 1709 1710 /** 1711 * Initializes the workplace matchers.<p> 1712 */ 1713 private void initWorkplaceMatchers() { 1714 1715 List<CmsSiteMatcher> matchers = new ArrayList<CmsSiteMatcher>(); 1716 if (!m_workplaceServers.isEmpty()) { 1717 Map<String, CmsSiteMatcher> matchersByUrl = Maps.newHashMap(); 1718 for (String server : m_workplaceServers.keySet()) { 1719 CmsSSLMode mode = m_workplaceServers.get(server); 1720 CmsSiteMatcher matcher = new CmsSiteMatcher(server); 1721 if ((mode == CmsSSLMode.LETS_ENCRYPT) || (mode == CmsSSLMode.MANUAL_EP_TERMINATION)) { 1722 CmsSiteMatcher httpMatcher = matcher.forDifferentScheme("http"); 1723 CmsSiteMatcher httpsMatcher = matcher.forDifferentScheme("https"); 1724 for (CmsSiteMatcher current : new CmsSiteMatcher[] {httpMatcher, httpsMatcher}) { 1725 matchersByUrl.put(current.getUrl(), current); 1726 if (CmsLog.INIT.isInfoEnabled()) { 1727 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WORKPLACE_SITE_1, matcher)); 1728 } 1729 } 1730 } else { 1731 matchersByUrl.put(matcher.getUrl(), matcher); 1732 if (CmsLog.INIT.isInfoEnabled()) { 1733 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WORKPLACE_SITE_1, matcher)); 1734 } 1735 1736 } 1737 } 1738 matchers = Lists.newArrayList(matchersByUrl.values()); 1739 } else if (CmsLog.INIT.isInfoEnabled()) { 1740 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WORKPLACE_SITE_0)); 1741 } 1742 m_workplaceMatchers = matchers; 1743 } 1744 1745 /** 1746 * Checks whether the given matcher is included in the currently configured and valid matchers.<p> 1747 * 1748 * @param matcher the matcher to check 1749 * 1750 * @return <code>true</code> in case the given matcher is included in the currently configured and valid matchers 1751 */ 1752 private boolean isServerValid(CmsSiteMatcher matcher) { 1753 1754 return !m_siteMatcherSites.containsKey(matcher); 1755 1756 } 1757 1758 /** 1759 * Returns <code>true</code> if the given root path matches any of the stored additional sites.<p> 1760 * 1761 * @param rootPath the root path to check 1762 * 1763 * @return <code>true</code> if the given root path matches any of the stored additional sites 1764 */ 1765 private String lookupAdditionalSite(String rootPath) { 1766 1767 for (int i = 0, size = m_additionalSiteRoots.size(); i < size; i++) { 1768 String siteRoot = m_additionalSiteRoots.get(i); 1769 if (rootPath.startsWith(siteRoot + "/")) { 1770 return siteRoot; 1771 } 1772 } 1773 return null; 1774 } 1775 1776 /** 1777 * Returns the configured site if the given root path matches site in the "/sites/" folder, 1778 * or <code>null</code> otherwise.<p> 1779 * 1780 * @param rootPath the root path to check 1781 * 1782 * @return the configured site if the given root path matches site in the "/sites/" folder, 1783 * or <code>null</code> otherwise 1784 */ 1785 private CmsSite lookupSitesFolder(String rootPath) { 1786 1787 int pos = rootPath.indexOf('/', SITES_FOLDER_POS); 1788 if (pos > 0) { 1789 // this assumes that the root path may likely start with something like "/sites/default/" 1790 // just cut the first 2 directories from the root path and do a direct lookup in the internal map 1791 return m_siteRootSites.get(rootPath.substring(0, pos)); 1792 } 1793 return null; 1794 } 1795 1796 /** 1797 * Sets the class member variables {@link #m_siteMatcherSites} and {@link #m_siteMatchers} 1798 * from the provided map of configured site matchers.<p> 1799 * 1800 * @param siteMatcherSites the site matches to set 1801 */ 1802 private void setSiteMatcherSites(Map<CmsSiteMatcher, CmsSite> siteMatcherSites) { 1803 1804 m_siteMatcherSites = Collections.unmodifiableMap(siteMatcherSites); 1805 m_siteMatchers = Collections.unmodifiableList(new ArrayList<CmsSiteMatcher>(m_siteMatcherSites.keySet())); 1806 } 1807}