1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.servlet.filters.virtualhost;
16  
17  import com.liferay.portal.LayoutFriendlyURLException;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.util.StringBundler;
21  import com.liferay.portal.kernel.util.StringPool;
22  import com.liferay.portal.kernel.util.StringUtil;
23  import com.liferay.portal.kernel.util.Validator;
24  import com.liferay.portal.model.Group;
25  import com.liferay.portal.model.LayoutSet;
26  import com.liferay.portal.model.impl.LayoutImpl;
27  import com.liferay.portal.service.GroupLocalServiceUtil;
28  import com.liferay.portal.servlet.AbsoluteRedirectsResponse;
29  import com.liferay.portal.servlet.I18nServlet;
30  import com.liferay.portal.servlet.filters.BasePortalFilter;
31  import com.liferay.portal.struts.LastPath;
32  import com.liferay.portal.util.PortalInstances;
33  import com.liferay.portal.util.PortalUtil;
34  import com.liferay.portal.util.PropsValues;
35  import com.liferay.portal.util.WebKeys;
36  
37  import java.util.Set;
38  
39  import javax.servlet.FilterChain;
40  import javax.servlet.FilterConfig;
41  import javax.servlet.RequestDispatcher;
42  import javax.servlet.ServletContext;
43  import javax.servlet.http.HttpServletRequest;
44  import javax.servlet.http.HttpServletResponse;
45  import javax.servlet.http.HttpSession;
46  
47  /**
48   * <a href="VirtualHostFilter.java.html"><b><i>View Source</i></b></a>
49   *
50   * <p>
51   * This filter is used to provide virtual host functionality. However, this
52   * filter is still required even if you do not use virtual hosting because it
53   * sets the company id in the request so that subsequent calls in the thread
54   * have the company id properly set. This filter must also always be the first
55   * filter in the list of filters.
56   * </p>
57   *
58   * @author Joel Kozikowski
59   * @author Brian Wing Shun Chan
60   * @author Raymond Augé
61   */
62  public class VirtualHostFilter extends BasePortalFilter {
63  
64      public void init(FilterConfig filterConfig) {
65          super.init(filterConfig);
66  
67          _servletContext = filterConfig.getServletContext();
68      }
69  
70      protected boolean isValidFriendlyURL(String friendlyURL) {
71          friendlyURL = friendlyURL.toLowerCase();
72  
73          if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
74              friendlyURL.startsWith(
75                  PortalUtil.getPathFriendlyURLPrivateGroup() +
76                      StringPool.SLASH) ||
77              friendlyURL.startsWith(
78                  PortalUtil.getPathFriendlyURLPublic() + StringPool.SLASH) ||
79              friendlyURL.startsWith(
80                  PortalUtil.getPathFriendlyURLPrivateUser() +
81                      StringPool.SLASH) ||
82              friendlyURL.startsWith(_PATH_C) ||
83              friendlyURL.startsWith(_PATH_DELEGATE) ||
84              friendlyURL.startsWith(_PATH_DISPLAY_CHART) ||
85              friendlyURL.startsWith(_PATH_DOCUMENT) ||
86              friendlyURL.startsWith(_PATH_DTD) ||
87              friendlyURL.startsWith(_PATH_FACEBOOK) ||
88              friendlyURL.startsWith(_PATH_GOOGLE_GADGET) ||
89              friendlyURL.startsWith(_PATH_HTML) ||
90              friendlyURL.startsWith(_PATH_IMAGE) ||
91              friendlyURL.startsWith(_PATH_LANGUAGE) ||
92              friendlyURL.startsWith(_PATH_NETVIBES) ||
93              friendlyURL.startsWith(_PATH_PBHS) ||
94              friendlyURL.startsWith(_PATH_POLLER) ||
95              friendlyURL.startsWith(_PATH_SHAREPOINT) ||
96              friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
97              friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
98              friendlyURL.startsWith(_PATH_VTI) ||
99              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 != languageId.length()) {
214                     continue;
215                 }
216 
217                 i18nLanguageId = friendlyURL.substring(0, pos);
218                 friendlyURL = friendlyURL.substring(pos);
219 
220                 break;
221             }
222         }
223 
224         if (_log.isDebugEnabled()) {
225             _log.debug("Friendly URL " + friendlyURL);
226         }
227 
228         if (!friendlyURL.equals(StringPool.SLASH) &&
229             !isValidFriendlyURL(friendlyURL)) {
230 
231             _log.debug("Friendly URL is not valid");
232 
233             processFilter(
234                 VirtualHostFilter.class, request, response, filterChain);
235 
236             return;
237         }
238 
239         LayoutSet layoutSet = (LayoutSet)request.getAttribute(
240             WebKeys.VIRTUAL_HOST_LAYOUT_SET);
241 
242         if (_log.isDebugEnabled()) {
243             _log.debug("Layout set " + layoutSet);
244         }
245 
246         if (layoutSet != null) {
247             try {
248                 LastPath lastPath = new LastPath(
249                     contextPath, friendlyURL, request.getParameterMap());
250 
251                 request.setAttribute(WebKeys.LAST_PATH, lastPath);
252 
253                 StringBundler prefix = new StringBundler(2);
254 
255                 Group group = GroupLocalServiceUtil.getGroup(
256                     layoutSet.getGroupId());
257 
258                 if (layoutSet.isPrivateLayout()) {
259                     if (group.isUser()) {
260                         prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
261                     }
262                     else {
263                         prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
264                     }
265                 }
266                 else {
267                     prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
268                 }
269 
270                 prefix.append(group.getFriendlyURL());
271 
272                 StringBundler forwardURL = new StringBundler(6);
273 
274                 if (i18nLanguageId != null) {
275                     forwardURL.append(i18nLanguageId);
276                 }
277 
278                 if (friendlyURL.startsWith(
279                         PropsValues.WIDGET_SERVLET_MAPPING)) {
280 
281                     forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
282 
283                     friendlyURL = StringUtil.replaceFirst(
284                         friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
285                         StringPool.BLANK);
286                 }
287 
288                 long plid = PortalUtil.getPlidFromFriendlyURL(
289                     companyId, friendlyURL);
290 
291                 if (plid <= 0) {
292                     forwardURL.append(prefix);
293                 }
294 
295                 forwardURL.append(friendlyURL);
296 
297                 if (_log.isDebugEnabled()) {
298                     _log.debug("Forward to " + forwardURL);
299                 }
300 
301                 RequestDispatcher requestDispatcher =
302                     _servletContext.getRequestDispatcher(forwardURL.toString());
303 
304                 requestDispatcher.forward(request, response);
305 
306                 return;
307             }
308             catch (Exception e) {
309                 _log.error(e, e);
310             }
311         }
312 
313         processFilter(VirtualHostFilter.class, request, response, filterChain);
314     }
315 
316     private static final String _PATH_C = "/c/";
317 
318     private static final String _PATH_DELEGATE = "/delegate/";
319 
320     private static final String _PATH_DISPLAY_CHART = "/display_chart";
321 
322     private static final String _PATH_DOCUMENT = "/document/";
323 
324     private static final String _PATH_DTD = "/dtd/";
325 
326     private static final String _PATH_FACEBOOK = "/facebook/";
327 
328     private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
329 
330     private static final String _PATH_HTML = "/html/";
331 
332     private static final String _PATH_IMAGE = "/image/";
333 
334     private static final String _PATH_LANGUAGE = "/language/";
335 
336     private static final String _PATH_NETVIBES = "/netvibes/";
337 
338     private static final String _PATH_PBHS = "/pbhs/";
339 
340     private static final String _PATH_POLLER = "/poller/";
341 
342     private static final String _PATH_SHAREPOINT = "/sharepoint/";
343 
344     private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
345 
346     private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
347 
348     private static final String _PATH_VTI = "/_vti_";
349 
350     private static final String _PATH_WAP = "/wap/";
351 
352     private static final String _PATH_WIDGET = "/widget/";
353 
354     private static final String _PATH_XMLRPC = "/xmlrpc/";
355 
356     private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
357         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
358 
359     private static final String _PRIVATE_USER_SERVLET_MAPPING =
360         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
361 
362     private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
363         PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
364 
365     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
366 
367     private ServletContext _servletContext;
368 
369 }