001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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 originalFriendlyURL = request.getRequestURI();
194    
195                    String friendlyURL = originalFriendlyURL;
196    
197                    if ((Validator.isNotNull(contextPath)) &&
198                            (friendlyURL.indexOf(contextPath) != -1)) {
199    
200                            friendlyURL = friendlyURL.substring(contextPath.length());
201                    }
202    
203                    friendlyURL = StringUtil.replace(
204                            friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
205    
206                    String i18nLanguageId = null;
207    
208                    Set<String> languageIds = I18nServlet.getLanguageIds();
209    
210                    for (String languageId : languageIds) {
211                            if (friendlyURL.startsWith(languageId)) {
212                                    int pos = friendlyURL.indexOf(CharPool.SLASH, 1);
213    
214                                    if (((pos != -1) && (pos != languageId.length())) ||
215                                            ((pos == -1) && !friendlyURL.equals(languageId))) {
216    
217                                            continue;
218                                    }
219    
220                                    if (pos == -1) {
221                                            i18nLanguageId = friendlyURL;
222                                            friendlyURL = StringPool.SLASH;
223                                    }
224                                    else {
225                                            i18nLanguageId = friendlyURL.substring(0, pos);
226                                            friendlyURL = friendlyURL.substring(pos);
227                                    }
228    
229                                    break;
230                            }
231                    }
232    
233                    friendlyURL = StringUtil.replace(
234                            friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING, StringPool.BLANK);
235    
236                    if (_log.isDebugEnabled()) {
237                            _log.debug("Friendly URL " + friendlyURL);
238                    }
239    
240                    if (!friendlyURL.equals(StringPool.SLASH) &&
241                            !isValidFriendlyURL(friendlyURL)) {
242    
243                            _log.debug("Friendly URL is not valid");
244    
245                            processFilter(
246                                    VirtualHostFilter.class, request, response, filterChain);
247    
248                            return;
249                    }
250    
251                    LayoutSet layoutSet = (LayoutSet)request.getAttribute(
252                            WebKeys.VIRTUAL_HOST_LAYOUT_SET);
253    
254                    if (_log.isDebugEnabled()) {
255                            _log.debug("Layout set " + layoutSet);
256                    }
257    
258                    if (layoutSet != null) {
259                            try {
260                                    LastPath lastPath = new LastPath(
261                                            contextPath, friendlyURL, request.getParameterMap());
262    
263                                    request.setAttribute(WebKeys.LAST_PATH, lastPath);
264    
265                                    StringBundler prefix = new StringBundler(2);
266    
267                                    Group group = GroupLocalServiceUtil.getGroup(
268                                            layoutSet.getGroupId());
269    
270                                    if (layoutSet.isPrivateLayout()) {
271                                            if (group.isUser()) {
272                                                    prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
273                                            }
274                                            else {
275                                                    prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
276                                            }
277                                    }
278                                    else {
279                                            prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
280                                    }
281    
282                                    prefix.append(group.getFriendlyURL());
283    
284                                    StringBundler forwardURL = new StringBundler(6);
285    
286                                    if (i18nLanguageId != null) {
287                                            forwardURL.append(i18nLanguageId);
288                                    }
289    
290                                    if (originalFriendlyURL.startsWith(
291                                                    PropsValues.WIDGET_SERVLET_MAPPING)) {
292    
293                                            forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
294    
295                                            friendlyURL = StringUtil.replaceFirst(
296                                                    friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
297                                                    StringPool.BLANK);
298                                    }
299    
300                                    long plid = PortalUtil.getPlidFromFriendlyURL(
301                                            companyId, friendlyURL);
302    
303                                    if (plid <= 0) {
304                                            forwardURL.append(prefix);
305                                    }
306    
307                                    forwardURL.append(friendlyURL);
308    
309                                    if (_log.isDebugEnabled()) {
310                                            _log.debug("Forward to " + forwardURL);
311                                    }
312    
313                                    RequestDispatcher requestDispatcher =
314                                            _servletContext.getRequestDispatcher(forwardURL.toString());
315    
316                                    requestDispatcher.forward(request, response);
317    
318                                    return;
319                            }
320                            catch (Exception e) {
321                                    _log.error(e, e);
322                            }
323                    }
324    
325                    processFilter(VirtualHostFilter.class, request, response, filterChain);
326            }
327    
328            private static final String _PATH_C = "/c/";
329    
330            private static final String _PATH_COMBO = "/combo/";
331    
332            private static final String _PATH_DELEGATE = "/delegate/";
333    
334            private static final String _PATH_DISPLAY_CHART = "/display_chart";
335    
336            private static final String _PATH_DOCUMENTS = "/documents/";
337    
338            private static final String _PATH_DTD = "/dtd/";
339    
340            private static final String _PATH_FACEBOOK = "/facebook/";
341    
342            private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
343    
344            private static final String _PATH_HTML = "/html/";
345    
346            private static final String _PATH_IMAGE = "/image/";
347    
348            private static final String _PATH_LANGUAGE = "/language/";
349    
350            private static final String _PATH_NETVIBES = "/netvibes/";
351    
352            private static final String _PATH_PBHS = "/pbhs/";
353    
354            private static final String _PATH_POLLER = "/poller/";
355    
356            private static final String _PATH_SHAREPOINT = "/sharepoint/";
357    
358            private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
359    
360            private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
361    
362            private static final String _PATH_VTI = "/_vti_";
363    
364            private static final String _PATH_WAP = "/wap/";
365    
366            private static final String _PATH_WIDGET = "/widget/";
367    
368            private static final String _PATH_XMLRPC = "/xmlrpc/";
369    
370            private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
371                    PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
372    
373            private static final String _PRIVATE_USER_SERVLET_MAPPING =
374                    PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
375    
376            private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
377                    PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
378    
379            private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
380    
381            private ServletContext _servletContext;
382    
383    }