001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (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, 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.main;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.i18n.CmsMessageContainer;
033import org.opencms.security.CmsPermissionViolationException;
034import org.opencms.util.CmsStringUtil;
035import org.opencms.util.CmsUUID;
036
037import java.util.regex.Matcher;
038import java.util.regex.Pattern;
039
040import javax.servlet.http.HttpServletRequest;
041import javax.servlet.http.HttpServletResponse;
042
043import org.apache.commons.logging.Log;
044
045/**
046 * Resource init handler that loads a resource given its permalink.<p>
047 *
048 * The permalink must have following format:<br>
049 * <code>/${CONTEXT}/${SERVLET}/permalink/${UUID}.${EXT}</code><p>
050 * 
051 * for example:<br>
052 * <code>/opencms/opencms/permalink/a7b5d298-b3ab-11d8-b3e3-514d35713fed.html</code><p>
053 * 
054 * @since 6.3 
055 */
056public class CmsPermalinkResourceHandler implements I_CmsResourceInit {
057
058    /** Regex for capturing a UUID. */
059    public static final String CAPTURE_UUID_REGEX = "(" + CmsUUID.UUID_REGEX + ")";
060
061    /** The permalink handler path. */
062    public static final String PERMALINK_HANDLER = "/permalink/";
063
064    /** Regex for the optional file extension. */
065    public static final String SUFFIX_REGEX = "(?:\\.[a-zA-Z0-9]*)?$";
066
067    /** The log object for this class. */
068    private static final Log LOG = CmsLog.getLog(CmsPermalinkResourceHandler.class);
069
070    /** The compiled pattern for detail page permalinks. */
071    private Pattern m_detailPattern;
072
073    /** The pattern used to match permalink uris and extract the structure id. */
074    private Pattern m_simplePermalinkPattern;
075
076    /**
077     * Default constructor.<p>
078     */
079    public CmsPermalinkResourceHandler() {
080
081        String uriRegex = PERMALINK_HANDLER + CAPTURE_UUID_REGEX + SUFFIX_REGEX;
082        String detailUriRegex = PERMALINK_HANDLER + CAPTURE_UUID_REGEX + ":" + CAPTURE_UUID_REGEX + SUFFIX_REGEX;
083        m_simplePermalinkPattern = Pattern.compile(uriRegex);
084        m_detailPattern = Pattern.compile(detailUriRegex);
085    }
086
087    /**
088     * @see org.opencms.main.I_CmsResourceInit#initResource(org.opencms.file.CmsResource, org.opencms.file.CmsObject, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
089     */
090    public CmsResource initResource(CmsResource resource, CmsObject cms, HttpServletRequest req, HttpServletResponse res)
091    throws CmsResourceInitException, CmsPermissionViolationException {
092
093        // only do something if the resource was not found 
094        if (resource == null) {
095            String uri = cms.getRequestContext().getUri();
096            // check if the resource starts with the PERMALINK_HANDLER
097            Matcher matcher = m_simplePermalinkPattern.matcher(uri);
098            if (matcher.find()) {
099                CmsResource resource1 = resource;
100                // get the id of the real resource
101                String id = matcher.group(1);
102                String storedSiteRoot = cms.getRequestContext().getSiteRoot();
103                try {
104                    // we now must switch to the root site to read the resource
105                    cms.getRequestContext().setSiteRoot("/");
106                    // read the resource
107                    resource1 = cms.readDefaultFile(id);
108                } catch (CmsPermissionViolationException e) {
109                    throw e;
110                } catch (Throwable e) {
111                    CmsMessageContainer msg = Messages.get().container(Messages.ERR_PERMALINK_1, id);
112                    if (LOG.isErrorEnabled()) {
113                        LOG.error(msg.key(), e);
114                    }
115                    throw new CmsResourceInitException(msg, e);
116                } finally {
117                    // restore the siteroot
118                    cms.getRequestContext().setSiteRoot(storedSiteRoot);
119                    // resource may be null in case of an error
120                    if (resource1 != null) {
121                        // modify the uri to the one of the real resource
122                        cms.getRequestContext().setUri(cms.getSitePath(resource1));
123                    }
124                }
125                resource = resource1;
126            } else {
127                matcher = m_detailPattern.matcher(uri);
128                // detail page permalink. Handle the cases 'getI18NInfo' and 'showResource' differently:
129                // In the 'showResource' case, we do a redirect to the real detail page URL
130                // In the 'getI18NInfo' case, we return the container page so the locale in the CmsRequestContext is set correctly for the 'showResource' case 
131                if (matcher.find()) {
132                    try {
133                        CmsUUID pageId = new CmsUUID(matcher.group(1));
134                        CmsUUID detailId = new CmsUUID(matcher.group(2));
135                        CmsResource pageResource = cms.readResource(pageId);
136                        if (res != null) {
137                            CmsResource detailResource = cms.readResource(detailId);
138                            String detailName = cms.getDetailName(detailResource, cms.getRequestContext().getLocale(), // the locale in the request context should be the locale of the container page 
139                                OpenCms.getLocaleManager().getDefaultLocales());
140                            CmsResource parentFolder = cms.readParentFolder(pageResource.getStructureId());
141                            String baseLink = OpenCms.getLinkManager().substituteLink(cms, parentFolder);
142                            String redirectLink = CmsStringUtil.joinPaths(baseLink, detailName);
143                            CmsResourceInitException resInitException = new CmsResourceInitException(getClass());
144
145                            resInitException.setClearErrors(true);
146                            res.sendRedirect(redirectLink);
147                            throw resInitException;
148                        } else {
149                            // we're being called from getI18NInfo; assume that the locale for the container page is the locale we want
150                            return pageResource;
151                        }
152                    } catch (CmsResourceInitException e) {
153                        throw e;
154                    } catch (Exception e) {
155                        LOG.error(e.getLocalizedMessage(), e);
156                        throw new CmsResourceInitException(getClass());
157                    }
158                }
159                return null;
160            }
161        }
162        return resource;
163    }
164
165}