001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.servlet.filters.virtualhost;
016    
017    import com.liferay.portal.LayoutFriendlyURLException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.struts.LastPath;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Validator;
025    import com.liferay.portal.model.Group;
026    import com.liferay.portal.model.LayoutSet;
027    import com.liferay.portal.model.impl.LayoutImpl;
028    import com.liferay.portal.service.GroupLocalServiceUtil;
029    import com.liferay.portal.servlet.AbsoluteRedirectsResponse;
030    import com.liferay.portal.servlet.I18nServlet;
031    import com.liferay.portal.servlet.filters.BasePortalFilter;
032    import com.liferay.portal.util.PortalInstances;
033    import com.liferay.portal.util.PortalUtil;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.portal.util.WebKeys;
036    
037    import java.util.Set;
038    
039    import javax.servlet.FilterChain;
040    import javax.servlet.FilterConfig;
041    import javax.servlet.RequestDispatcher;
042    import javax.servlet.ServletContext;
043    import javax.servlet.http.HttpServletRequest;
044    import javax.servlet.http.HttpServletResponse;
045    import javax.servlet.http.HttpSession;
046    
047    /**
048     * <p>
049     * This filter is used to provide virtual host functionality. However, this
050     * filter is still required even if you do not use virtual hosting because it
051     * sets the company id in the request so that subsequent calls in the thread
052     * have the company id properly set. This filter must also always be the first
053     * filter in the list of filters.
054     * </p>
055     *
056     * @author Joel Kozikowski
057     * @author Brian Wing Shun Chan
058     * @author Raymond Augé
059     * @author Eduardo Lundgren
060     */
061    public class VirtualHostFilter extends BasePortalFilter {
062    
063            public void init(FilterConfig filterConfig) {
064                    super.init(filterConfig);
065    
066                    _servletContext = filterConfig.getServletContext();
067            }
068    
069            protected boolean isValidFriendlyURL(String friendlyURL) {
070                    friendlyURL = friendlyURL.toLowerCase();
071    
072                    if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
073                            friendlyURL.startsWith(
074                                    PortalUtil.getPathFriendlyURLPrivateGroup() +
075                                            StringPool.SLASH) ||
076                            friendlyURL.startsWith(
077                                    PortalUtil.getPathFriendlyURLPublic() + StringPool.SLASH) ||
078                            friendlyURL.startsWith(
079                                    PortalUtil.getPathFriendlyURLPrivateUser() +
080                                            StringPool.SLASH) ||
081                            friendlyURL.startsWith(_PATH_C) ||
082                            friendlyURL.startsWith(_PATH_COMBO) ||
083                            friendlyURL.startsWith(_PATH_DELEGATE) ||
084                            friendlyURL.startsWith(_PATH_DISPLAY_CHART) ||
085                            friendlyURL.startsWith(_PATH_DOCUMENTS) ||
086                            friendlyURL.startsWith(_PATH_DTD) ||
087                            friendlyURL.startsWith(_PATH_FACEBOOK) ||
088                            friendlyURL.startsWith(_PATH_GOOGLE_GADGET) ||
089                            friendlyURL.startsWith(_PATH_HTML) ||
090                            friendlyURL.startsWith(_PATH_IMAGE) ||
091                            friendlyURL.startsWith(_PATH_LANGUAGE) ||
092                            friendlyURL.startsWith(_PATH_NETVIBES) ||
093                            friendlyURL.startsWith(_PATH_PBHS) ||
094                            friendlyURL.startsWith(_PATH_POLLER) ||
095                            friendlyURL.startsWith(_PATH_SHAREPOINT) ||
096                            friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
097                            friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
098                            friendlyURL.startsWith(_PATH_VTI) ||
099                            friendlyURL.startsWith(_PATH_WAP) ||
100                            friendlyURL.startsWith(_PATH_WIDGET) ||
101                            friendlyURL.startsWith(_PATH_XMLRPC)) {
102    
103                            return false;
104                    }
105    
106                    int code = LayoutImpl.validateFriendlyURL(friendlyURL);
107    
108                    if ((code > -1) &&
109                            (code != LayoutFriendlyURLException.ENDS_WITH_SLASH)) {
110    
111                            return false;
112                    }
113    
114                    return true;
115            }
116    
117            protected boolean isValidRequestURL(StringBuffer requestURL) {
118                    if (requestURL == null) {
119                            return false;
120                    }
121    
122                    String url = requestURL.toString();
123    
124                    for (String extension : PropsValues.VIRTUAL_HOSTS_IGNORE_EXTENSIONS) {
125                            if (url.endsWith(extension)) {
126                                    return false;
127                            }
128                    }
129    
130                    return true;
131            }
132    
133            protected void processFilter(
134                            HttpServletRequest request, HttpServletResponse response,
135                            FilterChain filterChain)
136                    throws Exception {
137    
138                    request.setCharacterEncoding(StringPool.UTF8);
139                    //response.setContentType(ContentTypes.TEXT_HTML_UTF8);
140    
141                    // Make sure all redirects issued by the portal are absolute
142    
143                    response = new AbsoluteRedirectsResponse(request, response);
144    
145                    // Company id needs to always be called here so that it's properly set
146                    // in subsequent calls
147    
148                    long companyId = PortalInstances.getCompanyId(request);
149    
150                    if (_log.isDebugEnabled()) {
151                            _log.debug("Company id " + companyId);
152                    }
153    
154                    PortalUtil.getCurrentCompleteURL(request);
155                    PortalUtil.getCurrentURL(request);
156    
157                    HttpSession session = request.getSession();
158    
159                    Boolean httpsInitial = (Boolean)session.getAttribute(
160                            WebKeys.HTTPS_INITIAL);
161    
162                    if (httpsInitial == null) {
163                            httpsInitial = Boolean.valueOf(request.isSecure());
164    
165                            session.setAttribute(WebKeys.HTTPS_INITIAL, httpsInitial);
166    
167                            if (_log.isDebugEnabled()) {
168                                    _log.debug("Setting httpsInitial to " + httpsInitial);
169                            }
170                    }
171    
172                    if (!isFilterEnabled()) {
173                            processFilter(
174                                    VirtualHostFilter.class, request, response, filterChain);
175    
176                            return;
177                    }
178    
179                    StringBuffer requestURL = request.getRequestURL();
180    
181                    if (_log.isDebugEnabled()) {
182                            _log.debug("Received " + requestURL);
183                    }
184    
185                    if (!isValidRequestURL(requestURL)) {
186                            processFilter(
187                                    VirtualHostFilter.class, request, response, filterChain);
188    
189                            return;
190                    }
191    
192                    String contextPath = PortalUtil.getPathContext();
193    
194                    String friendlyURL = request.getRequestURI();
195    
196                    if ((Validator.isNotNull(contextPath)) &&
197                            (friendlyURL.indexOf(contextPath) != -1)) {
198    
199                            friendlyURL = friendlyURL.substring(contextPath.length());
200                    }
201    
202                    friendlyURL = StringUtil.replace(
203                            friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
204    
205                    String i18nLanguageId = null;
206    
207                    Set<String> languageIds = I18nServlet.getLanguageIds();
208    
209                    for (String languageId : languageIds) {
210                            if (friendlyURL.startsWith(languageId)) {
211                                    int pos = friendlyURL.indexOf(StringPool.SLASH, 1);
212    
213                                    if (((pos != -1) && (pos != languageId.length())) ||
214                                            ((pos == -1) && !friendlyURL.equals(languageId))) {
215    
216                                            continue;
217                                    }
218    
219                                    if (pos == -1) {
220                                            i18nLanguageId = friendlyURL;
221                                            friendlyURL = StringPool.SLASH;
222                                    }
223                                    else {
224                                            i18nLanguageId = friendlyURL.substring(0, pos);
225                                            friendlyURL = friendlyURL.substring(pos);
226                                    }
227    
228                                    break;
229                            }
230                    }
231    
232                    if (_log.isDebugEnabled()) {
233                            _log.debug("Friendly URL " + friendlyURL);
234                    }
235    
236                    if (!friendlyURL.equals(StringPool.SLASH) &&
237                            !isValidFriendlyURL(friendlyURL)) {
238    
239                            _log.debug("Friendly URL is not valid");
240    
241                            processFilter(
242                                    VirtualHostFilter.class, request, response, filterChain);
243    
244                            return;
245                    }
246    
247                    LayoutSet layoutSet = (LayoutSet)request.getAttribute(
248                            WebKeys.VIRTUAL_HOST_LAYOUT_SET);
249    
250                    if (_log.isDebugEnabled()) {
251                            _log.debug("Layout set " + layoutSet);
252                    }
253    
254                    if (layoutSet != null) {
255                            try {
256                                    LastPath lastPath = new LastPath(
257                                            contextPath, friendlyURL, request.getParameterMap());
258    
259                                    request.setAttribute(WebKeys.LAST_PATH, lastPath);
260    
261                                    StringBundler prefix = new StringBundler(2);
262    
263                                    Group group = GroupLocalServiceUtil.getGroup(
264                                            layoutSet.getGroupId());
265    
266                                    if (layoutSet.isPrivateLayout()) {
267                                            if (group.isUser()) {
268                                                    prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
269                                            }
270                                            else {
271                                                    prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
272                                            }
273                                    }
274                                    else {
275                                            prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
276                                    }
277    
278                                    prefix.append(group.getFriendlyURL());
279    
280                                    StringBundler forwardURL = new StringBundler(6);
281    
282                                    if (i18nLanguageId != null) {
283                                            forwardURL.append(i18nLanguageId);
284                                    }
285    
286                                    if (friendlyURL.startsWith(
287                                                    PropsValues.WIDGET_SERVLET_MAPPING)) {
288    
289                                            forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
290    
291                                            friendlyURL = StringUtil.replaceFirst(
292                                                    friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
293                                                    StringPool.BLANK);
294                                    }
295    
296                                    long plid = PortalUtil.getPlidFromFriendlyURL(
297                                            companyId, friendlyURL);
298    
299                                    if (plid <= 0) {
300                                            forwardURL.append(prefix);
301                                    }
302    
303                                    forwardURL.append(friendlyURL);
304    
305                                    if (_log.isDebugEnabled()) {
306                                            _log.debug("Forward to " + forwardURL);
307                                    }
308    
309                                    RequestDispatcher requestDispatcher =
310                                            _servletContext.getRequestDispatcher(forwardURL.toString());
311    
312                                    requestDispatcher.forward(request, response);
313    
314                                    return;
315                            }
316                            catch (Exception e) {
317                                    _log.error(e, e);
318                            }
319                    }
320    
321                    processFilter(VirtualHostFilter.class, request, response, filterChain);
322            }
323    
324            private static final String _PATH_C = "/c/";
325    
326            private static final String _PATH_COMBO = "/combo/";
327    
328            private static final String _PATH_DELEGATE = "/delegate/";
329    
330            private static final String _PATH_DISPLAY_CHART = "/display_chart";
331    
332            private static final String _PATH_DOCUMENTS = "/documents/";
333    
334            private static final String _PATH_DTD = "/dtd/";
335    
336            private static final String _PATH_FACEBOOK = "/facebook/";
337    
338            private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
339    
340            private static final String _PATH_HTML = "/html/";
341    
342            private static final String _PATH_IMAGE = "/image/";
343    
344            private static final String _PATH_LANGUAGE = "/language/";
345    
346            private static final String _PATH_NETVIBES = "/netvibes/";
347    
348            private static final String _PATH_PBHS = "/pbhs/";
349    
350            private static final String _PATH_POLLER = "/poller/";
351    
352            private static final String _PATH_SHAREPOINT = "/sharepoint/";
353    
354            private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
355    
356            private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
357    
358            private static final String _PATH_VTI = "/_vti_";
359    
360            private static final String _PATH_WAP = "/wap/";
361    
362            private static final String _PATH_WIDGET = "/widget/";
363    
364            private static final String _PATH_XMLRPC = "/xmlrpc/";
365    
366            private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
367                    PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
368    
369            private static final String _PRIVATE_USER_SERVLET_MAPPING =
370                    PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
371    
372            private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
373                    PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
374    
375            private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
376    
377            private ServletContext _servletContext;
378    
379    }