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