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