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 org.opencms.main.CmsLog; 031import org.opencms.util.CmsStringUtil; 032 033import java.io.Serializable; 034import java.net.URI; 035import java.util.Arrays; 036 037import org.apache.commons.logging.Log; 038 039/** 040 * A matcher object to compare request data against the configured sites.<p> 041 * 042 * @since 6.0.0 043 */ 044public final class CmsSiteMatcher implements Cloneable, Serializable { 045 046 /** 047 * Represents the different redirect modes for a site alias. 048 */ 049 public static enum RedirectMode { 050 051 /** Don't redirect. */ 052 none, 053 /** HTTP 302 */ 054 temporary, 055 /** HTTP 301 */ 056 permanent; 057 058 /** 059 * Converts a redirect mode string from the configuration to the corresponding enum value. 060 * 061 * @param strValue the string value 062 * @return the enum value 063 */ 064 public static RedirectMode parse(String strValue) { 065 066 if (strValue == null) { 067 return none; 068 } 069 strValue = strValue.toLowerCase(); 070 if ("true".equals(strValue)) { 071 return temporary; 072 } else if ("permanent".equals(strValue)) { 073 return permanent; 074 } 075 076 return none; 077 078 } 079 } 080 081 /** The serial version id. */ 082 private static final long serialVersionUID = -3988887650237005342L; 083 084 /** The logger instance for this class. */ 085 private static final Log LOG = CmsLog.getLog(CmsSiteMatcher.class); 086 087 /** Constant for the "http" port. */ 088 private static final int PORT_HTTP = 80; 089 090 /** Constant for the "https" port. */ 091 private static final int PORT_HTTPS = 443; 092 093 /** Constant for the "http" scheme. */ 094 private static final String SCHEME_HTTP = "http"; 095 096 /** Constant for the "https" scheme. */ 097 private static final String SCHEME_HTTPS = "https"; 098 099 /** Wildcard for string matching. */ 100 private static final String WILDCARD = "*"; 101 102 /** Default matcher that always matches all other Site matchers. */ 103 public static final CmsSiteMatcher DEFAULT_MATCHER = new CmsSiteMatcher(WILDCARD, WILDCARD, 0); 104 105 /** Hashcode buffer to save multiple calculations. */ 106 private transient Integer m_hashCode; 107 108 /** The hostname (e.g. localhost) which is required to access this site. */ 109 private String m_serverName; 110 111 /** The port (e.g. 80) which is required to access this site. */ 112 private int m_serverPort; 113 114 /** The protocol (e.g. "http", "https") which is required to access this site. */ 115 private String m_serverProtocol; 116 117 /** The time offset. */ 118 private long m_timeOffset; 119 120 /**Redirect (only for aliase). */ 121 private RedirectMode m_redirect = RedirectMode.none; 122 123 /** 124 * Construct a new site matcher from a String which should be in default URL notation.<p> 125 * 126 * If no port is provided, the default port 80 or 443 will be used for http or https respectively. 127 * If no protocol is provided, the default protocol "http" will be used. 128 * 129 * @param serverString the String, e.g. http://localhost:8080 130 */ 131 public CmsSiteMatcher(String serverString) { 132 133 this(serverString, 0); 134 } 135 136 /** 137 * Construct a new site matcher from a String which should be in default URL notation.<p> 138 * 139 * If no port is provided, the default port 80 or 443 will be used for http or https respectively. 140 * If no protocol is provided, the default protocol "http" will be used. 141 * 142 * @param serverString the String, e.g. http://localhost:8080 143 * @param timeOffset the time offset 144 */ 145 public CmsSiteMatcher(String serverString, long timeOffset) { 146 147 if (serverString == null) { 148 init(WILDCARD, WILDCARD, 0, timeOffset); 149 return; 150 } 151 // remove whitespace 152 serverString = serverString.trim(); 153 154 // remove fragment and query if present 155 int pos = serverString.indexOf("#"); 156 if (pos > 0) { 157 serverString = serverString.substring(0, pos); 158 } 159 pos = serverString.indexOf("?"); 160 if (pos > 0) { 161 serverString = serverString.substring(0, pos); 162 } 163 // cut trailing "/" 164 if (serverString.endsWith("/")) { 165 serverString = serverString.substring(0, serverString.length() - 1); 166 } 167 int serverPort; 168 String serverProtocol, serverName; 169 // check for protocol 170 pos = serverString.indexOf("://"); 171 if (pos >= 0) { 172 serverProtocol = serverString.substring(0, pos); 173 serverString = serverString.substring(pos + 3); 174 } else { 175 serverProtocol = SCHEME_HTTP; 176 } 177 // check for server name and port 178 pos = serverString.indexOf(":"); 179 if (pos >= 0) { 180 serverName = serverString.substring(0, pos); 181 try { 182 String port = serverString.substring(pos + 1); 183 pos = port.indexOf("/"); 184 if (pos >= 0) { 185 port = port.substring(0, pos); 186 } 187 serverPort = Integer.valueOf(port).intValue(); 188 } catch (NumberFormatException e) { 189 if (SCHEME_HTTPS.equals(serverProtocol)) { 190 serverPort = PORT_HTTPS; 191 } else { 192 serverPort = PORT_HTTP; 193 } 194 } 195 } else { 196 serverName = serverString; 197 if (SCHEME_HTTPS.equals(serverProtocol)) { 198 serverPort = PORT_HTTPS; 199 } else { 200 serverPort = PORT_HTTP; 201 } 202 } 203 204 // cut trailing path in server name 205 pos = serverName.indexOf("/"); 206 if (pos >= 0) { 207 serverName = serverName.substring(0, pos); 208 } 209 210 // initialize members 211 init(serverProtocol, serverName, serverPort, timeOffset); 212 } 213 214 /** 215 * Constructs a new site matcher object.<p> 216 * 217 * @param serverProtocol to protocol required to access this site 218 * @param serverName the server URL prefix to which this site is mapped 219 * @param serverPort the port required to access this site 220 */ 221 public CmsSiteMatcher(String serverProtocol, String serverName, int serverPort) { 222 223 init(serverProtocol, serverName, serverPort, 0); 224 } 225 226 /** 227 * Constructs a new site matcher object.<p> 228 * 229 * @param serverProtocol to protocol required to access this site 230 * @param serverName the server URL prefix to which this site is mapped 231 * @param serverPort the port required to access this site 232 * @param timeOffset the time offset 233 */ 234 public CmsSiteMatcher(String serverProtocol, String serverName, int serverPort, long timeOffset) { 235 236 init(serverProtocol, serverName, serverPort, timeOffset); 237 } 238 239 /** 240 * Returns a clone of this Objects instance.<p> 241 * 242 * @return a clone of this instance 243 */ 244 @Override 245 public Object clone() { 246 247 try { 248 return super.clone(); 249 } catch (CloneNotSupportedException e) { 250 // should not happen 251 throw new RuntimeException(e); 252 } 253 } 254 255 /** 256 * @see java.lang.Object#equals(java.lang.Object) 257 */ 258 @Override 259 public boolean equals(Object obj) { 260 261 if (obj == this) { 262 return true; 263 } 264 if (!(obj instanceof CmsSiteMatcher)) { 265 return false; 266 } 267 // if one of the object is the default matcher the result is always true 268 if ((this == DEFAULT_MATCHER) || (obj == DEFAULT_MATCHER)) { 269 return true; 270 } 271 CmsSiteMatcher other = (CmsSiteMatcher)obj; 272 return (m_serverPort == other.m_serverPort) 273 && m_serverName.equalsIgnoreCase(other.m_serverName) 274 && m_serverProtocol.equals(other.m_serverProtocol); 275 } 276 277 /** 278 * Checks if this site matcher equals another site matcher, ignoring the scheme. 279 * 280 * @param matcher the matcher to compare 281 * @return true if the site matchers are equal 282 */ 283 public boolean equalsIgnoreScheme(CmsSiteMatcher matcher) { 284 for (String scheme: Arrays.asList("http", "https")) { 285 if (this.forDifferentScheme(scheme).equals(matcher)) { 286 return true; 287 } 288 } 289 return false; 290 } 291 292 /** 293 * Generates a site matcher equivalent to this one but with a different scheme.<p> 294 * 295 * @param scheme the new scheme 296 * @return the new site matcher 297 */ 298 public CmsSiteMatcher forDifferentScheme(String scheme) { 299 300 try { 301 URI uri = new URI(getUrl()); 302 URI changedUri = new URI(scheme, uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment()); 303 CmsSiteMatcher res = new CmsSiteMatcher(changedUri.toString(), m_timeOffset); 304 res.m_redirect = m_redirect; 305 return res; 306 } catch (Exception e) { 307 LOG.error(e.getLocalizedMessage(), e); 308 return null; 309 } 310 } 311 312 /** 313 * Gets the redirect mode. 314 * 315 * @return the redirect mode 316 */ 317 public RedirectMode getRedirectMode() { 318 319 return m_redirect; 320 } 321 322 /** 323 * Returns the hostname (e.g. localhost) which is required to access this site.<p> 324 * 325 * @return the hostname (e.g. localhost) which is required to access this site 326 */ 327 public String getServerName() { 328 329 return m_serverName; 330 } 331 332 /** 333 * Returns the port (e.g. 80) which is required to access this site.<p> 334 * 335 * @return the port (e.g. 80) which is required to access this site 336 */ 337 public int getServerPort() { 338 339 return m_serverPort; 340 } 341 342 /** 343 * Returns the protocol (e.g. "http", "https") which is required to access this site.<p> 344 * 345 * @return the protocol (e.g. "http", "https") which is required to access this site 346 */ 347 public String getServerProtocol() { 348 349 return m_serverProtocol; 350 } 351 352 /** 353 * Returns the time Offset.<p> 354 * 355 * @return the time Offset 356 */ 357 public long getTimeOffset() { 358 359 return m_timeOffset; 360 } 361 362 /** 363 * Returns the url of this site matcher.<p> 364 * 365 * @return the url, i.e. {protocol}://{servername}[:{port}], port appened only if != 80 366 */ 367 public String getUrl() { 368 369 return m_serverProtocol 370 + "://" 371 + m_serverName 372 + (((m_serverPort != PORT_HTTP) && (m_serverPort != PORT_HTTPS)) ? ":" + m_serverPort : ""); 373 } 374 375 /** 376 * @see java.lang.Object#hashCode() 377 */ 378 @Override 379 public int hashCode() { 380 381 if (m_hashCode == null) { 382 m_hashCode = Integer.valueOf(toString().hashCode()); 383 } 384 return m_hashCode.intValue(); 385 } 386 387 /** 388 * Is alias to be redirected? 389 * 390 * @return boolean 391 */ 392 public boolean isRedirect() { 393 394 return m_redirect != RedirectMode.none; 395 } 396 397 /** 398 * Set redirect.<p> 399 * 400 * @param redirect boolean 401 */ 402 public void setRedirectMode(RedirectMode redirect) { 403 404 m_redirect = redirect; 405 } 406 407 /** 408 * @see java.lang.Object#toString() 409 */ 410 @Override 411 public String toString() { 412 413 StringBuffer result = new StringBuffer(32); 414 if ((m_serverProtocol != null) && !(WILDCARD.equals(m_serverProtocol))) { 415 result.append(m_serverProtocol); 416 result.append("://"); 417 } 418 result.append(m_serverName); 419 if ((m_serverPort > 0) 420 && (!(SCHEME_HTTP.equals(m_serverProtocol) && (m_serverPort == PORT_HTTP))) 421 && (!(SCHEME_HTTPS.equals(m_serverProtocol) && (m_serverPort == PORT_HTTPS)))) { 422 result.append(":"); 423 result.append(m_serverPort); 424 } 425 return result.toString(); 426 } 427 428 /** 429 * Sets the hostname (e.g. localhost) which is required to access this site.<p> 430 * 431 * Setting the hostname to "*" is a wildcard that matches all hostnames 432 * 433 * @param serverName the hostname (e.g. localhost) which is required to access this site 434 */ 435 protected void setServerName(String serverName) { 436 437 if (CmsStringUtil.isEmpty(serverName) || (WILDCARD.equals(serverName))) { 438 m_serverName = WILDCARD; 439 } else { 440 m_serverName = serverName.trim(); 441 } 442 } 443 444 /** 445 * Sets the port (e.g. 80) which is required to access this site.<p> 446 * 447 * Setting the port to 0 (zero) is a wildcard that matches all ports 448 * 449 * @param serverPort the port (e.g. 80) which is required to access this site 450 */ 451 protected void setServerPort(int serverPort) { 452 453 m_serverPort = serverPort; 454 if (m_serverPort < 0) { 455 m_serverPort = 0; 456 } 457 } 458 459 /** 460 * Sets the protocol (e.g. "http", "https") which is required to access this site.<p> 461 * 462 * Setting the protocol to "*" is a wildcard that matches all protocols.<p> 463 * 464 * @param serverProtocol the protocol (e.g. "http", "https") which is required to access this site 465 */ 466 protected void setServerProtocol(String serverProtocol) { 467 468 if (CmsStringUtil.isEmpty(serverProtocol) || (WILDCARD.equals(serverProtocol))) { 469 m_serverProtocol = WILDCARD; 470 } else { 471 int pos = serverProtocol.indexOf("/"); 472 if (pos > 0) { 473 m_serverProtocol = serverProtocol.substring(0, pos).toLowerCase(); 474 } else { 475 m_serverProtocol = serverProtocol.toLowerCase().trim(); 476 } 477 } 478 } 479 480 /** 481 * Sets the time Offset in seconds.<p> 482 * 483 * @param timeOffset the time Offset to set 484 */ 485 protected void setTimeOffset(long timeOffset) { 486 487 m_timeOffset = timeOffset * 1000L; 488 } 489 490 /** 491 * Initializes the member variables.<p> 492 * 493 * @param serverProtocol to protocol required to access this site 494 * @param serverName the server URL prefix to which this site is mapped 495 * @param serverPort the port required to access this site 496 * @param timeOffset the time offset 497 */ 498 private void init(String serverProtocol, String serverName, int serverPort, long timeOffset) { 499 500 setServerProtocol(serverProtocol); 501 setServerName(serverName); 502 setServerPort(serverPort); 503 setTimeOffset(timeOffset); 504 } 505}