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