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