001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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.xml;
029
030import org.opencms.file.CmsResource;
031import org.opencms.file.history.CmsHistoryResourceHandler;
032import org.opencms.main.CmsLog;
033import org.opencms.util.CmsStringUtil;
034
035import java.net.URI;
036import java.util.Collection;
037import java.util.Collections;
038import java.util.HashSet;
039import java.util.Set;
040import java.util.regex.Pattern;
041import java.util.regex.PatternSyntaxException;
042
043import org.apache.commons.logging.Log;
044import org.apache.http.client.utils.URIBuilder;
045
046/**
047 * Does final postprocessing on a link by cutting off specific path suffixes (e.g. index.html).
048 */
049public class CmsLinkFinisher {
050
051    /** Logger instance for this class. */
052    private static final Log LOG = CmsLog.getLog(CmsLinkFinisher.class);
053
054    /** True if the link  finisher is enabled. */
055    private boolean m_enabled;
056
057    /** Default file names to remove. */
058    private Set<String> m_defaultFileNames;
059
060    /** If a link matches this, it shouldn't be finished. */
061    private Pattern m_excludePattern;
062
063    /**
064     * Creates a new instance.
065     *
066     * @param enabled true if it should be enabled
067     * @param defaultFileNames the default file names to remove
068     * @param exclude regex to prevent certain links from being finished if they match
069     */
070    public CmsLinkFinisher(boolean enabled, Collection<String> defaultFileNames, String exclude) {
071
072        m_enabled = enabled;
073        m_defaultFileNames = Collections.unmodifiableSet(new HashSet<>(defaultFileNames));
074        if (exclude != null) {
075            try {
076                m_excludePattern = Pattern.compile(exclude);
077            } catch (PatternSyntaxException e) {
078                LOG.error(e.getLocalizedMessage(), e);
079            }
080        }
081
082    }
083
084    /**
085     * Transforms the link into a finished format.
086     *
087     * @param link the link
088     * @param full in full mode, remove all configured suffixes, otherwise just remove trailing slashes
089     * @return
090     */
091    public String transformLink(String link, boolean full) {
092
093        if (!m_enabled) {
094            return link;
095        }
096        if (CmsStringUtil.isEmptyOrWhitespaceOnly(link)) {
097            return link;
098        }
099        if (link.startsWith(CmsHistoryResourceHandler.HISTORY_HANDLER)) {
100            return link;
101        }
102        if (link.startsWith("javascript:")) {
103            return link;
104        }
105        if (m_excludePattern != null) {
106            if (m_excludePattern.matcher(link).matches()) {
107                return link;
108            }
109        }
110        try {
111            URI uri = new URI(link);
112            URIBuilder builder = new URIBuilder(uri);
113            String path = builder.getPath();
114            if (path != null) {
115                path = path.replaceFirst("/$", "");
116                if (full) {
117                    if (path.length() > 1) {
118                        String name = CmsResource.getName(path);
119                        if (m_defaultFileNames.contains(name)) {
120                            path = path.substring(0, path.lastIndexOf('/'));
121                        }
122                    }
123                }
124                // since we usually put the result of link substitution into hrefs, we need to avoid an empty path if there is no host, otherwise the href will link to the current page
125                if ((uri.getAuthority() == null) && "".equals(path)) {
126                    path = "/";
127                }
128                builder.setPath(path);
129                link = builder.toString();
130            }
131        } catch (Exception e) {
132            LOG.error(e.getLocalizedMessage(), e);
133        }
134
135        return link;
136    }
137
138}