1   /**
2    * Copyright (c) 2000-2008 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.model.Group;
31  import com.liferay.portal.model.LayoutSet;
32  import com.liferay.portal.model.impl.LayoutImpl;
33  import com.liferay.portal.service.GroupLocalServiceUtil;
34  import com.liferay.portal.servlet.AbsoluteRedirectsResponse;
35  import com.liferay.portal.servlet.filters.BasePortalFilter;
36  import com.liferay.portal.struts.LastPath;
37  import com.liferay.portal.util.PortalInstances;
38  import com.liferay.portal.util.PortalUtil;
39  import com.liferay.portal.util.WebKeys;
40  
41  import java.io.IOException;
42  
43  import javax.servlet.FilterChain;
44  import javax.servlet.FilterConfig;
45  import javax.servlet.RequestDispatcher;
46  import javax.servlet.ServletContext;
47  import javax.servlet.ServletException;
48  import javax.servlet.ServletRequest;
49  import javax.servlet.ServletResponse;
50  import javax.servlet.http.HttpServletRequest;
51  import javax.servlet.http.HttpServletResponse;
52  import javax.servlet.http.HttpSession;
53  
54  /**
55   * <a href="VirtualHostFilter.java.html"><b><i>View Source</i></b></a>
56   *
57   * <p>
58   * This filter is used to provide virtual host functionality. However, this
59   * filter is still required even if you do not use virtual hosting because it
60   * sets the company id in the request so that subsequent calls in the thread
61   * have the company id properly set. This filter must also always be the first
62   * filter in the list of filters.
63   * </p>
64   *
65   * @author Joel Kozikowski
66   * @author Brian Wing Shun Chan
67   * @author Raymond Augé
68   *
69   */
70  public class VirtualHostFilter extends BasePortalFilter {
71  
72      public void init(FilterConfig filterConfig) {
73          super.init(filterConfig);
74  
75          _servletContext = filterConfig.getServletContext();
76      }
77  
78      public void doFilter(
79              ServletRequest servletRequest, ServletResponse servletResponse,
80              FilterChain filterChain)
81          throws IOException, ServletException {
82  
83          if (_log.isDebugEnabled()) {
84              if (isFilterEnabled()) {
85                  _log.debug(VirtualHostFilter.class + " is enabled");
86              }
87              else {
88                  _log.debug(VirtualHostFilter.class + " is disabled");
89              }
90          }
91  
92          HttpServletRequest request = (HttpServletRequest)servletRequest;
93          HttpServletResponse response = (HttpServletResponse)servletResponse;
94  
95          request.setCharacterEncoding(StringPool.UTF8);
96          //response.setContentType(ContentTypes.TEXT_HTML_UTF8);
97  
98          // Make sure all redirects issued by the portal are absolute
99  
100         response = new AbsoluteRedirectsResponse(request, response);
101 
102         // Company id needs to always be called here so that it's properly set
103         // in subsequent calls
104 
105         long companyId = PortalInstances.getCompanyId(request);
106 
107         if (_log.isDebugEnabled()) {
108             _log.debug("Company id " + companyId);
109         }
110 
111         PortalUtil.getCurrentURL(request);
112 
113         HttpSession session = request.getSession();
114 
115         Boolean httpsInitial = (Boolean)session.getAttribute(
116             WebKeys.HTTPS_INITIAL);
117 
118         if (httpsInitial == null) {
119             httpsInitial = Boolean.valueOf(request.isSecure());
120 
121             session.setAttribute(WebKeys.HTTPS_INITIAL, httpsInitial);
122 
123             if (_log.isDebugEnabled()) {
124                 _log.debug("Setting httpsInitial to " + httpsInitial);
125             }
126         }
127 
128         if (!isFilterEnabled()) {
129             processFilter(
130                 VirtualHostFilter.class, request, response, filterChain);
131 
132             return;
133         }
134 
135         StringBuffer requestURL = request.getRequestURL();
136 
137         if (_log.isDebugEnabled()) {
138             _log.debug("Received " + requestURL);
139         }
140 
141         if (!isValidRequestURL(requestURL)) {
142             processFilter(
143                 VirtualHostFilter.class, request, response, filterChain);
144 
145             return;
146         }
147 
148         String contextPath = PortalUtil.getPathContext();
149 
150         String friendlyURL = request.getRequestURI();
151 
152         if ((!contextPath.equals(StringPool.SLASH)) &&
153             (friendlyURL.indexOf(contextPath) != -1)) {
154 
155             friendlyURL = friendlyURL.substring(
156                 contextPath.length(), friendlyURL.length());
157         }
158 
159         friendlyURL = StringUtil.replace(
160             friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
161 
162         if (_log.isDebugEnabled()) {
163             _log.debug("Friendly URL " + friendlyURL);
164         }
165 
166         if (!isValidFriendlyURL(friendlyURL)) {
167             processFilter(
168                 VirtualHostFilter.class, request, response, filterChain);
169 
170             return;
171         }
172 
173         LayoutSet layoutSet = (LayoutSet)servletRequest.getAttribute(
174             WebKeys.VIRTUAL_HOST_LAYOUT_SET);
175 
176         if (_log.isDebugEnabled()) {
177             _log.debug("Layout set " + layoutSet);
178         }
179 
180         if (layoutSet != null) {
181             try {
182                 LastPath lastPath = new LastPath(
183                     StringPool.BLANK, friendlyURL,
184                     servletRequest.getParameterMap());
185 
186                 servletRequest.setAttribute(WebKeys.LAST_PATH, lastPath);
187 
188                 StringBuilder prefix = new StringBuilder();
189 
190                 if (layoutSet.isPrivateLayout()) {
191                     prefix.append(PortalUtil.getPathFriendlyURLPrivateGroup());
192                 }
193                 else {
194                     prefix.append(PortalUtil.getPathFriendlyURLPublic());
195                 }
196 
197                 Group group = GroupLocalServiceUtil.getGroup(
198                     layoutSet.getGroupId());
199 
200                 prefix.append(group.getFriendlyURL());
201 
202                 StringBuilder redirect = new StringBuilder();
203 
204                 redirect.append(prefix);
205                 redirect.append(friendlyURL);
206 
207                 String query = request.getQueryString();
208 
209                 if (query != null) {
210                     redirect.append(StringPool.QUESTION);
211                     redirect.append(query);
212                 }
213 
214                 if (_log.isDebugEnabled()) {
215                     _log.debug("Redirect to " + redirect);
216                 }
217 
218                 RequestDispatcher requestDispatcher =
219                     _servletContext.getRequestDispatcher(redirect.toString());
220 
221                 requestDispatcher.forward(servletRequest, response);
222 
223                 return;
224             }
225             catch (Exception e) {
226                 _log.error(e, e);
227             }
228         }
229 
230         processFilter(VirtualHostFilter.class, request, response, filterChain);
231     }
232 
233     protected boolean isValidFriendlyURL(String friendlyURL) {
234         friendlyURL = friendlyURL.toLowerCase();
235 
236         if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
237             friendlyURL.startsWith(
238                 PortalUtil.getPathFriendlyURLPrivateGroup()) ||
239             friendlyURL.startsWith(PortalUtil.getPathFriendlyURLPublic()) ||
240             friendlyURL.startsWith(
241                 PortalUtil.getPathFriendlyURLPrivateUser()) ||
242             friendlyURL.startsWith(_PATH_C) ||
243             friendlyURL.startsWith(_PATH_DELEGATE) ||
244             friendlyURL.startsWith(_PATH_HTML) ||
245             friendlyURL.startsWith(_PATH_IMAGE) ||
246             friendlyURL.startsWith(_PATH_LANGUAGE) ||
247             friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
248             friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
249             friendlyURL.startsWith(_PATH_WAP) ||
250             friendlyURL.startsWith(_PATH_WSRP)) {
251 
252             return false;
253         }
254 
255         int code = LayoutImpl.validateFriendlyURL(friendlyURL);
256 
257         if ((code > -1) &&
258             (code != LayoutFriendlyURLException.ENDS_WITH_SLASH)) {
259 
260             return false;
261         }
262 
263         return true;
264     }
265 
266     protected boolean isValidRequestURL(StringBuffer requestURL) {
267         if (requestURL == null) {
268             return false;
269         }
270 
271         String url = requestURL.toString();
272 
273         if (url.endsWith(_EXT_C) || url.endsWith(_EXT_CSS) ||
274             url.endsWith(_EXT_GIF) || url.endsWith(_EXT_IMAGE_COMPANY_LOGO) ||
275             url.endsWith(_EXT_ICO) || url.endsWith(_EXT_JS) ||
276             url.endsWith(_EXT_JPEG) || url.endsWith(_EXT_PORTAL_CSS_CACHED) ||
277             url.endsWith(_EXT_PORTAL_JAVASCRIPT_CACHED) ||
278             url.endsWith(_EXT_PORTAL_LAYOUT) ||
279             url.endsWith(_EXT_PORTAL_LOGIN) ||
280             url.endsWith(_EXT_PORTAL_LOGOUT) || url.endsWith(_EXT_PNG)) {
281 
282             return false;
283         }
284         else {
285             return true;
286         }
287     }
288 
289     protected void processFilter(
290         HttpServletRequest request, HttpServletResponse response,
291         FilterChain filterChain) {
292     }
293 
294     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
295 
296     private static String _EXT_C = "/c";
297 
298     private static String _EXT_CSS = ".css";
299 
300     private static String _EXT_GIF = ".gif";
301 
302     private static String _EXT_IMAGE_COMPANY_LOGO = "/image/company_logo";
303 
304     private static String _EXT_ICO = ".ico";
305 
306     private static String _EXT_JS = ".js";
307 
308     private static String _EXT_JPEG = ".jpeg";
309 
310     private static String _EXT_PORTAL_CSS_CACHED = "/portal/css_cached";
311 
312     private static String _EXT_PORTAL_JAVASCRIPT_CACHED =
313         "/portal/javascript_cached";
314 
315     private static String _EXT_PORTAL_LAYOUT = "/portal/layout";
316 
317     private static String _EXT_PORTAL_LOGIN = "/portal/login";
318 
319     private static String _EXT_PORTAL_LOGOUT = "/portal/logout";
320 
321     private static String _EXT_PNG = ".png";
322 
323     private static String _PATH_C = "/c/";
324 
325     private static String _PATH_DELEGATE = "/delegate/";
326 
327     private static String _PATH_HTML = "/html/";
328 
329     private static String _PATH_IMAGE = "/image/";
330 
331     private static String _PATH_LANGUAGE = "/language/";
332 
333     private static String _PATH_SITEMAP_XML = "/sitemap.xml";
334 
335     private static String _PATH_SOFTWARE_CATALOG = "/software_catalog/";
336 
337     private static String _PATH_WAP = "/wap/";
338 
339     private static String _PATH_WSRP = "/wsrp/";
340 
341     private ServletContext _servletContext;
342 
343 }