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