1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   * 
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 
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(StringPool.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_DELEGATE = "/delegate/";
326 
327     private static final String _PATH_DISPLAY_CHART = "/display_chart";
328 
329     private static final String _PATH_DOCUMENT = "/document/";
330 
331     private static final String _PATH_DTD = "/dtd/";
332 
333     private static final String _PATH_FACEBOOK = "/facebook/";
334 
335     private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
336 
337     private static final String _PATH_HTML = "/html/";
338 
339     private static final String _PATH_IMAGE = "/image/";
340 
341     private static final String _PATH_LANGUAGE = "/language/";
342 
343     private static final String _PATH_NETVIBES = "/netvibes/";
344 
345     private static final String _PATH_PBHS = "/pbhs/";
346 
347     private static final String _PATH_POLLER = "/poller/";
348 
349     private static final String _PATH_SHAREPOINT = "/sharepoint/";
350 
351     private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
352 
353     private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
354 
355     private static final String _PATH_VTI = "/_vti_";
356 
357     private static final String _PATH_WAP = "/wap/";
358 
359     private static final String _PATH_WIDGET = "/widget/";
360 
361     private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
362         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
363 
364     private static final String _PRIVATE_USER_SERVLET_MAPPING =
365         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
366 
367     private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
368         PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
369 
370     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
371 
372     private ServletContext _servletContext;
373 
374 }