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                     continue;
214                 }
215 
216                 if (pos == -1) {
217                     i18nLanguageId = friendlyURL;
218                     friendlyURL = StringPool.SLASH;
219                 }
220                 else {
221                     i18nLanguageId = friendlyURL.substring(0, pos);
222                     friendlyURL = friendlyURL.substring(pos);
223                 }
224 
225                 break;
226             }
227         }
228 
229         if (_log.isDebugEnabled()) {
230             _log.debug("Friendly URL " + friendlyURL);
231         }
232 
233         if (!friendlyURL.equals(StringPool.SLASH) &&
234             !isValidFriendlyURL(friendlyURL)) {
235 
236             _log.debug("Friendly URL is not valid");
237 
238             processFilter(
239                 VirtualHostFilter.class, request, response, filterChain);
240 
241             return;
242         }
243 
244         LayoutSet layoutSet = (LayoutSet)request.getAttribute(
245             WebKeys.VIRTUAL_HOST_LAYOUT_SET);
246 
247         if (_log.isDebugEnabled()) {
248             _log.debug("Layout set " + layoutSet);
249         }
250 
251         if (layoutSet != null) {
252             try {
253                 LastPath lastPath = new LastPath(
254                     contextPath, friendlyURL, request.getParameterMap());
255 
256                 request.setAttribute(WebKeys.LAST_PATH, lastPath);
257 
258                 StringBundler prefix = new StringBundler(2);
259 
260                 Group group = GroupLocalServiceUtil.getGroup(
261                     layoutSet.getGroupId());
262 
263                 if (layoutSet.isPrivateLayout()) {
264                     if (group.isUser()) {
265                         prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
266                     }
267                     else {
268                         prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
269                     }
270                 }
271                 else {
272                     prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
273                 }
274 
275                 prefix.append(group.getFriendlyURL());
276 
277                 StringBundler forwardURL = new StringBundler(6);
278 
279                 if (i18nLanguageId != null) {
280                     forwardURL.append(i18nLanguageId);
281                 }
282 
283                 if (friendlyURL.startsWith(
284                         PropsValues.WIDGET_SERVLET_MAPPING)) {
285 
286                     forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
287 
288                     friendlyURL = StringUtil.replaceFirst(
289                         friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
290                         StringPool.BLANK);
291                 }
292 
293                 long plid = PortalUtil.getPlidFromFriendlyURL(
294                     companyId, friendlyURL);
295 
296                 if (plid <= 0) {
297                     forwardURL.append(prefix);
298                 }
299 
300                 forwardURL.append(friendlyURL);
301 
302                 if (_log.isDebugEnabled()) {
303                     _log.debug("Forward to " + forwardURL);
304                 }
305 
306                 RequestDispatcher requestDispatcher =
307                     _servletContext.getRequestDispatcher(forwardURL.toString());
308 
309                 requestDispatcher.forward(request, response);
310 
311                 return;
312             }
313             catch (Exception e) {
314                 _log.error(e, e);
315             }
316         }
317 
318         processFilter(VirtualHostFilter.class, request, response, filterChain);
319     }
320 
321     private static final String _PATH_C = "/c/";
322 
323     private static final String _PATH_DELEGATE = "/delegate/";
324 
325     private static final String _PATH_DISPLAY_CHART = "/display_chart";
326 
327     private static final String _PATH_DOCUMENT = "/document/";
328 
329     private static final String _PATH_DTD = "/dtd/";
330 
331     private static final String _PATH_FACEBOOK = "/facebook/";
332 
333     private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
334 
335     private static final String _PATH_HTML = "/html/";
336 
337     private static final String _PATH_IMAGE = "/image/";
338 
339     private static final String _PATH_LANGUAGE = "/language/";
340 
341     private static final String _PATH_NETVIBES = "/netvibes/";
342 
343     private static final String _PATH_PBHS = "/pbhs/";
344 
345     private static final String _PATH_POLLER = "/poller/";
346 
347     private static final String _PATH_SHAREPOINT = "/sharepoint/";
348 
349     private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
350 
351     private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
352 
353     private static final String _PATH_VTI = "/_vti_";
354 
355     private static final String _PATH_WAP = "/wap/";
356 
357     private static final String _PATH_WIDGET = "/widget/";
358 
359     private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
360         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
361 
362     private static final String _PRIVATE_USER_SERVLET_MAPPING =
363         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
364 
365     private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
366         PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
367 
368     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
369 
370     private ServletContext _servletContext;
371 
372 }