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