/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core; import java.net.MalformedURLException; import java.net.URL; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.enonic.cms.framework.util.HttpServletUtil; import com.enonic.cms.framework.util.UrlPathEncoder; import com.enonic.cms.core.structure.SiteKey; import com.enonic.cms.core.structure.SitePath; import com.enonic.cms.core.structure.SitePropertiesService; import com.enonic.cms.core.structure.SitePropertyNames; import com.enonic.cms.core.vhost.VirtualHostHelper; /** * SiteURLResolver is somewhere spring bean, somewhere it is not . * * @see com.enonic.cms.core.portal.rendering.PageRenderer * @see com.enonic.cms.core.portal.rendering.WindowRenderer */ @Component public class SiteURLResolver { private final static int DEFAULT_HTTP_PORT = 80; private final static int DEFAULT_HTTPS_PORT = 443; public static final String DEFAULT_SITEPATH_PREFIX = "/site"; private String characterEncoding; private SitePropertiesService sitePropertiesService; /** * If not null the value overrides setting in site properties. */ private Boolean overridingSitePropertyCreateUrlAsPath; /** * Whether to encode & as & or not. */ private boolean htmlEscapeParameterAmps = false; public String createFullPathForRedirect( HttpServletRequest request, SiteKey siteKey, String path ) { if ( siteIsCreatingRelativeUrlsFromRoot( siteKey ) ) { String pathFromRoot; //if path is local or missing vhost - correct path if ( VirtualHostHelper.hasBasePath( request ) && !path.startsWith( VirtualHostHelper.getBasePath( request ) ) ) { if ( path.startsWith( "/" ) ) { pathFromRoot = VirtualHostHelper.getBasePath( request ) + path; } else { pathFromRoot = VirtualHostHelper.getBasePath( request ) + "/" + path; } } else { pathFromRoot = path; } return doCreateFullPathForRedirectFromRootPath( pathFromRoot ); } else { return doCreateFullPathForRedirectFromLocalPath( request, siteKey, path ); } } public String createPathWithinContextPath( HttpServletRequest request, SitePath sitePath, boolean externalPath ) { String localPathPrefix = resolveLocalSitePathPrefix( request, sitePath.getSiteKey(), externalPath ); return buildPath( localPathPrefix, sitePath ); } public String createUrl( HttpServletRequest request, SitePath sitePath, boolean includeParamsInPath ) { String basePathOverride = (String) request.getAttribute( Attribute.BASEPATH_OVERRIDE_ATTRIBUTE_NAME ); return doCreateUrl( request, sitePath, includeParamsInPath, basePathOverride ); } public String createUrlWithBasePathOverride( HttpServletRequest request, SitePath sitePath, boolean includeParamsInPath, String basePathOverride ) { return doCreateUrl( request, sitePath, includeParamsInPath, basePathOverride ); } private String doCreateUrl( HttpServletRequest request, SitePath sitePath, boolean includeParamsInPath, String basePathOverride ) { boolean createPathOnly; if ( overridingSitePropertyCreateUrlAsPath != null ) { createPathOnly = overridingSitePropertyCreateUrlAsPath; } else { //check property createPathOnly = sitePropertiesService.getSiteProperties( sitePath.getSiteKey() ).getPropertyAsBoolean( SitePropertyNames.CREATE_URL_AS_PATH_PROPERTY ); } SiteBasePath siteBasePath = SiteBasePathResolver.resolveSiteBasePath( request, sitePath.getSiteKey() ); SiteBasePathAndSitePath siteBasePathAndSitePath = new SiteBasePathAndSitePath( siteBasePath, sitePath ); String url; if ( basePathOverride != null ) { url = basePathOverride; String siteLocalUrl = sitePathAndSitePathToString( sitePath.getPathAndParams(), includeParamsInPath ); if ( siteLocalUrl.startsWith( "/" ) && url.endsWith( "/" ) ) { // preventing double slashes (example: //news/politics) siteLocalUrl = siteLocalUrl.substring( 1 ); } url += siteLocalUrl; } else if ( createPathOnly ) { url = siteBasePathAndSitePathToString( siteBasePathAndSitePath, includeParamsInPath ); } else { url = createAbsoluteUrl( request, siteBasePathAndSitePath, includeParamsInPath ); } return url; } public String createAbsoluteUrl( HttpServletRequest request, SiteBasePathAndSitePath siteBasePathAndSitePath, boolean includeParamsInPath ) { String pathFromRoot = siteBasePathAndSitePathToString( siteBasePathAndSitePath, includeParamsInPath ); return createAbsoluteUrl( request, pathFromRoot ); } private String createAbsoluteUrl( HttpServletRequest request, String pathFromRoot ) { URL url; try { String localScheme = request.getScheme(); String scheme = HttpServletUtil.getScheme( request ); final int port = request.getServerPort(); if ( !scheme.equalsIgnoreCase( localScheme ) ) { // When the scheme has been changed, the port in use is probably not correct, so we will trust that the default is correct: url = new URL( scheme, request.getServerName(), pathFromRoot ); } else if ( "http".equalsIgnoreCase( scheme ) && ( port == DEFAULT_HTTP_PORT ) ) { url = new URL( scheme, request.getServerName(), pathFromRoot ); } else if ( "https".equalsIgnoreCase( scheme ) && ( port == DEFAULT_HTTPS_PORT ) ) { url = new URL( scheme, request.getServerName(), pathFromRoot ); } else { url = new URL( scheme, request.getServerName(), port, pathFromRoot ); } } catch ( MalformedURLException e ) { throw new RuntimeException( "Failed to create absolute url to path: " + pathFromRoot, e ); } return url.toString(); } private String siteBasePathAndSitePathToString( SiteBasePathAndSitePath siteBasePathAndSitePath, boolean includeParamsInPath ) { SiteBasePathAndSitePathToStringBuilder siteBasePathAndSitePathToStringBuilder = new SiteBasePathAndSitePathToStringBuilder(); siteBasePathAndSitePathToStringBuilder.setEncoding( characterEncoding ); siteBasePathAndSitePathToStringBuilder.setHtmlEscapeParameterAmps( htmlEscapeParameterAmps ); siteBasePathAndSitePathToStringBuilder.setIncludeFragment( true ); siteBasePathAndSitePathToStringBuilder.setIncludeParamsInPath( includeParamsInPath ); siteBasePathAndSitePathToStringBuilder.setUrlEncodePath( true ); return siteBasePathAndSitePathToStringBuilder.toString( siteBasePathAndSitePath ); } private String sitePathAndSitePathToString( PathAndParams siteLocalPathAndParams, boolean includeParamsInPath ) { PathAndParamsToStringBuilder pathAndParamsToStringBuilder = new PathAndParamsToStringBuilder(); pathAndParamsToStringBuilder.setEncoding( characterEncoding ); pathAndParamsToStringBuilder.setHtmlEscapeParameterAmps( htmlEscapeParameterAmps ); pathAndParamsToStringBuilder.setIncludeFragment( true ); pathAndParamsToStringBuilder.setIncludeParamsInPath( includeParamsInPath ); pathAndParamsToStringBuilder.setUrlEncodePath( true ); return pathAndParamsToStringBuilder.toString( siteLocalPathAndParams ); } /** * This method must behave different under redirect and forward in cases where rewrite is active. */ private String resolveLocalSitePathPrefix( HttpServletRequest request, SiteKey siteKey, boolean externalPath ) { if ( externalPath && VirtualHostHelper.hasBasePath( request ) ) { return VirtualHostHelper.getBasePath( request ); } else { return DEFAULT_SITEPATH_PREFIX + "/" + siteKey; } } /** * Builds up a path from given localPathPrefix and localPath in sitePath. Ensures that the localPath gets url * encoded. */ private String buildPath( String localPathPrefix, SitePath sitePath ) { final String localPathEncoded = sitePath.getLocalPath().getAsUrlEncoded( true, characterEncoding ); return localPathPrefix + localPathEncoded; } private String doCreateFullPathForRedirectFromRootPath( String pathFromRoot ) { final StringBuilder stringBuilder = new StringBuilder(); if ( !pathFromRoot.startsWith( "/" ) && !pathFromRoot.equals( "" ) ) { pathFromRoot = "/" + pathFromRoot; } stringBuilder.append( encodePath( pathFromRoot ) ); return stringBuilder.toString(); } /** * Builds up a path consisting of contextPath and sitePath from given params. Ensures that the localPath gets url * encoded. */ private String doCreateFullPathForRedirectFromLocalPath( HttpServletRequest request, SiteKey siteKey, String localPath ) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append( request.getContextPath() ); stringBuilder.append( resolveLocalSitePathPrefix( request, siteKey, true ) ); if ( !localPath.startsWith( "/" ) && !localPath.equals( "" ) ) { localPath = "/" + localPath; } stringBuilder.append( encodePath( localPath ) ); return stringBuilder.toString(); } private String encodePath( String path ) { return UrlPathEncoder.encodeUrlPath( path, characterEncoding ); } private boolean siteIsCreatingRelativeUrlsFromRoot( SiteKey siteKey ) { return sitePropertiesService.getSiteProperties( siteKey ).getPropertyAsBoolean( SitePropertyNames.CREATE_URL_AS_PATH_PROPERTY ); } public void setOverridingSitePropertyCreateUrlAsPath( final Boolean value ) { if ( value != null ) { this.overridingSitePropertyCreateUrlAsPath = value; } } public void setHtmlEscapeParameterAmps( boolean htmlEscapeParameterAmps ) { this.htmlEscapeParameterAmps = htmlEscapeParameterAmps; } @Value("${cms.url.characterEncoding}") public void setCharacterEncoding( String characterEncoding ) { this.characterEncoding = characterEncoding; } public String getCharacterEncoding() { return characterEncoding; } @Autowired public void setSitePropertiesService( SitePropertiesService value ) { this.sitePropertiesService = value; } }