1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.servlet.filters.virtualhost;
24  
25  import com.liferay.portal.LayoutFriendlyURLException;
26  import com.liferay.portal.kernel.log.Log;
27  import com.liferay.portal.kernel.log.LogFactoryUtil;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.kernel.util.StringUtil;
30  import com.liferay.portal.kernel.util.Validator;
31  import com.liferay.portal.model.Group;
32  import com.liferay.portal.model.LayoutSet;
33  import com.liferay.portal.model.impl.LayoutImpl;
34  import com.liferay.portal.service.GroupLocalServiceUtil;
35  import com.liferay.portal.servlet.AbsoluteRedirectsResponse;
36  import com.liferay.portal.servlet.I18nServlet;
37  import com.liferay.portal.servlet.filters.BasePortalFilter;
38  import com.liferay.portal.struts.LastPath;
39  import com.liferay.portal.util.PortalInstances;
40  import com.liferay.portal.util.PortalUtil;
41  import com.liferay.portal.util.PropsValues;
42  import com.liferay.portal.util.WebKeys;
43  
44  import java.io.IOException;
45  
46  import java.util.Set;
47  
48  import javax.servlet.FilterChain;
49  import javax.servlet.FilterConfig;
50  import javax.servlet.RequestDispatcher;
51  import javax.servlet.ServletContext;
52  import javax.servlet.ServletException;
53  import javax.servlet.ServletRequest;
54  import javax.servlet.ServletResponse;
55  import javax.servlet.http.HttpServletRequest;
56  import javax.servlet.http.HttpServletResponse;
57  import javax.servlet.http.HttpSession;
58  
59  /**
60   * <a href="VirtualHostFilter.java.html"><b><i>View Source</i></b></a>
61   *
62   * <p>
63   * This filter is used to provide virtual host functionality. However, this
64   * filter is still required even if you do not use virtual hosting because it
65   * sets the company id in the request so that subsequent calls in the thread
66   * have the company id properly set. This filter must also always be the first
67   * filter in the list of filters.
68   * </p>
69   *
70   * @author Joel Kozikowski
71   * @author Brian Wing Shun Chan
72   * @author Raymond Augé
73   *
74   */
75  public class VirtualHostFilter extends BasePortalFilter {
76  
77      public void init(FilterConfig filterConfig) {
78          super.init(filterConfig);
79  
80          _servletContext = filterConfig.getServletContext();
81      }
82  
83      public void doFilter(
84              ServletRequest servletRequest, ServletResponse servletResponse,
85              FilterChain filterChain)
86          throws IOException, ServletException {
87  
88          if (_log.isDebugEnabled()) {
89              if (isFilterEnabled()) {
90                  _log.debug(VirtualHostFilter.class + " is enabled");
91              }
92              else {
93                  _log.debug(VirtualHostFilter.class + " is disabled");
94              }
95          }
96  
97          HttpServletRequest request = (HttpServletRequest)servletRequest;
98          HttpServletResponse response = (HttpServletResponse)servletResponse;
99  
100         request.setCharacterEncoding(StringPool.UTF8);
101         //response.setContentType(ContentTypes.TEXT_HTML_UTF8);
102 
103         // Make sure all redirects issued by the portal are absolute
104 
105         response = new AbsoluteRedirectsResponse(request, response);
106 
107         // Company id needs to always be called here so that it's properly set
108         // in subsequent calls
109 
110         long companyId = PortalInstances.getCompanyId(request);
111 
112         if (_log.isDebugEnabled()) {
113             _log.debug("Company id " + companyId);
114         }
115 
116         PortalUtil.getCurrentURL(request);
117 
118         HttpSession session = request.getSession();
119 
120         Boolean httpsInitial = (Boolean)session.getAttribute(
121             WebKeys.HTTPS_INITIAL);
122 
123         if (httpsInitial == null) {
124             httpsInitial = Boolean.valueOf(request.isSecure());
125 
126             session.setAttribute(WebKeys.HTTPS_INITIAL, httpsInitial);
127 
128             if (_log.isDebugEnabled()) {
129                 _log.debug("Setting httpsInitial to " + httpsInitial);
130             }
131         }
132 
133         if (!isFilterEnabled()) {
134             processFilter(
135                 VirtualHostFilter.class, request, response, filterChain);
136 
137             return;
138         }
139 
140         StringBuffer requestURL = request.getRequestURL();
141 
142         if (_log.isDebugEnabled()) {
143             _log.debug("Received " + requestURL);
144         }
145 
146         if (!isValidRequestURL(requestURL)) {
147             processFilter(
148                 VirtualHostFilter.class, request, response, filterChain);
149 
150             return;
151         }
152 
153         String contextPath = PortalUtil.getPathContext();
154 
155         String friendlyURL = request.getRequestURI();
156 
157         if ((Validator.isNotNull(contextPath)) &&
158             (friendlyURL.indexOf(contextPath) != -1)) {
159 
160             friendlyURL = friendlyURL.substring(contextPath.length());
161         }
162 
163         friendlyURL = StringUtil.replace(
164             friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
165 
166         String i18nLanguageId = null;
167 
168         Set<String> languageIds = I18nServlet.getLanguageIds();
169 
170         for (String languageId : languageIds) {
171             if (friendlyURL.startsWith(languageId)) {
172                 int pos = friendlyURL.indexOf(StringPool.SLASH, 1);
173 
174                 i18nLanguageId = friendlyURL.substring(0, pos);
175                 friendlyURL = friendlyURL.substring(pos);
176 
177                 break;
178             }
179         }
180 
181         if (_log.isDebugEnabled()) {
182             _log.debug("Friendly URL " + friendlyURL);
183         }
184 
185         if (!isValidFriendlyURL(friendlyURL)) {
186             _log.debug("Friendly URL is not valid");
187 
188             processFilter(
189                 VirtualHostFilter.class, request, response, filterChain);
190 
191             return;
192         }
193 
194         LayoutSet layoutSet = (LayoutSet)servletRequest.getAttribute(
195             WebKeys.VIRTUAL_HOST_LAYOUT_SET);
196 
197         if (_log.isDebugEnabled()) {
198             _log.debug("Layout set " + layoutSet);
199         }
200 
201         if (layoutSet != null) {
202             try {
203                 LastPath lastPath = new LastPath(
204                     contextPath, friendlyURL, servletRequest.getParameterMap());
205 
206                 servletRequest.setAttribute(WebKeys.LAST_PATH, lastPath);
207 
208                 StringBuilder prefix = new StringBuilder();
209 
210                 Group group = GroupLocalServiceUtil.getGroup(
211                     layoutSet.getGroupId());
212 
213                 if (layoutSet.isPrivateLayout()) {
214                     if (group.isUser()) {
215                         prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
216                     }
217                     else {
218                         prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
219                     }
220                 }
221                 else {
222                     prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
223                 }
224 
225                 prefix.append(group.getFriendlyURL());
226 
227                 StringBuilder redirect = new StringBuilder();
228 
229                 if (i18nLanguageId != null) {
230                     redirect.append(i18nLanguageId);
231                 }
232 
233                 if (friendlyURL.startsWith(
234                         PropsValues.WIDGET_SERVLET_MAPPING)) {
235 
236                     redirect.append(PropsValues.WIDGET_SERVLET_MAPPING);
237 
238                     friendlyURL = StringUtil.replaceFirst(
239                         friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
240                         StringPool.BLANK);
241                 }
242 
243                 long plid = PortalUtil.getPlidFromFriendlyURL(
244                     companyId, friendlyURL);
245 
246                 if (plid <= 0) {
247                     redirect.append(prefix);
248                 }
249 
250                 redirect.append(friendlyURL);
251 
252                 String query = request.getQueryString();
253 
254                 if (query != null) {
255                     redirect.append(StringPool.QUESTION);
256                     redirect.append(query);
257                 }
258 
259                 if (_log.isDebugEnabled()) {
260                     _log.debug("Redirect to " + redirect);
261                 }
262 
263                 RequestDispatcher requestDispatcher =
264                     _servletContext.getRequestDispatcher(redirect.toString());
265 
266                 requestDispatcher.forward(servletRequest, response);
267 
268                 return;
269             }
270             catch (Exception e) {
271                 _log.error(e, e);
272             }
273         }
274 
275         processFilter(VirtualHostFilter.class, request, response, filterChain);
276     }
277 
278     protected boolean isValidFriendlyURL(String friendlyURL) {
279         friendlyURL = friendlyURL.toLowerCase();
280 
281         if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
282             friendlyURL.startsWith(
283                 PortalUtil.getPathFriendlyURLPrivateGroup() +
284                     StringPool.SLASH) ||
285             friendlyURL.startsWith(
286                 PortalUtil.getPathFriendlyURLPublic() + StringPool.SLASH) ||
287             friendlyURL.startsWith(
288                 PortalUtil.getPathFriendlyURLPrivateUser() +
289                     StringPool.SLASH) ||
290             friendlyURL.startsWith(_PATH_C) ||
291             friendlyURL.startsWith(_PATH_DELEGATE) ||
292             friendlyURL.startsWith(_PATH_HTML) ||
293             friendlyURL.startsWith(_PATH_IMAGE) ||
294             friendlyURL.startsWith(_PATH_LANGUAGE) ||
295             friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
296             friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
297             friendlyURL.startsWith(_PATH_WAP) ||
298             friendlyURL.startsWith(_PATH_WSRP)) {
299 
300             return false;
301         }
302 
303         int code = LayoutImpl.validateFriendlyURL(friendlyURL);
304 
305         if ((code > -1) &&
306             (code != LayoutFriendlyURLException.ENDS_WITH_SLASH)) {
307 
308             return false;
309         }
310 
311         return true;
312     }
313 
314     protected boolean isValidRequestURL(StringBuffer requestURL) {
315         if (requestURL == null) {
316             return false;
317         }
318 
319         String url = requestURL.toString();
320 
321         if (url.endsWith(_EXT_C) || url.endsWith(_EXT_CSS) ||
322             url.endsWith(_EXT_GIF) || url.endsWith(_EXT_IMAGE_COMPANY_LOGO) ||
323             url.endsWith(_EXT_ICO) || url.endsWith(_EXT_JS) ||
324             url.endsWith(_EXT_JPEG) || url.endsWith(_EXT_JSP) ||
325             url.endsWith(_EXT_PORTAL_LAYOUT) ||
326             url.endsWith(_EXT_PORTAL_LOGIN) ||
327             url.endsWith(_EXT_PORTAL_LOGOUT) || url.endsWith(_EXT_PNG)) {
328 
329             return false;
330         }
331         else {
332             return true;
333         }
334     }
335 
336     protected void processFilter(
337         HttpServletRequest request, HttpServletResponse response,
338         FilterChain filterChain) {
339     }
340 
341     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
342 
343     private static String _EXT_C = "/c";
344 
345     private static String _EXT_CSS = ".css";
346 
347     private static String _EXT_GIF = ".gif";
348 
349     private static String _EXT_IMAGE_COMPANY_LOGO = "/image/company_logo";
350 
351     private static String _EXT_ICO = ".ico";
352 
353     private static String _EXT_JS = ".js";
354 
355     private static String _EXT_JPEG = ".jpeg";
356 
357     private static String _EXT_JSP = ".jsp";
358 
359     private static String _EXT_PORTAL_LAYOUT = "/portal/layout";
360 
361     private static String _EXT_PORTAL_LOGIN = "/portal/login";
362 
363     private static String _EXT_PORTAL_LOGOUT = "/portal/logout";
364 
365     private static String _EXT_PNG = ".png";
366 
367     private static String _PATH_C = "/c/";
368 
369     private static String _PATH_DELEGATE = "/delegate/";
370 
371     private static String _PATH_HTML = "/html/";
372 
373     private static String _PATH_IMAGE = "/image/";
374 
375     private static String _PATH_LANGUAGE = "/language/";
376 
377     private static String _PATH_SITEMAP_XML = "/sitemap.xml";
378 
379     private static String _PATH_SOFTWARE_CATALOG = "/software_catalog/";
380 
381     private static String _PATH_WAP = "/wap/";
382 
383     private static String _PATH_WSRP = "/wsrp/";
384 
385     private static String _PRIVATE_GROUP_SERVLET_MAPPING =
386         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
387 
388     private static String _PRIVATE_USER_SERVLET_MAPPING =
389         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
390 
391     private static String _PUBLIC_GROUP_SERVLET_MAPPING =
392         PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
393 
394     private ServletContext _servletContext;
395 
396 }