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.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.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      protected boolean isValidFriendlyURL(String friendlyURL) {
79          friendlyURL = friendlyURL.toLowerCase();
80  
81          if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
82              friendlyURL.startsWith(
83                  PortalUtil.getPathFriendlyURLPrivateGroup() +
84                      StringPool.SLASH) ||
85              friendlyURL.startsWith(
86                  PortalUtil.getPathFriendlyURLPublic() + StringPool.SLASH) ||
87              friendlyURL.startsWith(
88                  PortalUtil.getPathFriendlyURLPrivateUser() +
89                      StringPool.SLASH) ||
90              friendlyURL.startsWith(_PATH_C) ||
91              friendlyURL.startsWith(_PATH_DELEGATE) ||
92              friendlyURL.startsWith(_PATH_HTML) ||
93              friendlyURL.startsWith(_PATH_IMAGE) ||
94              friendlyURL.startsWith(_PATH_LANGUAGE) ||
95              friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
96              friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
97              friendlyURL.startsWith(_PATH_WAP) ||
98              friendlyURL.startsWith(_PATH_WSRP)) {
99  
100             return false;
101         }
102 
103         int code = LayoutImpl.validateFriendlyURL(friendlyURL);
104 
105         if ((code > -1) &&
106             (code != LayoutFriendlyURLException.ENDS_WITH_SLASH)) {
107 
108             return false;
109         }
110 
111         return true;
112     }
113 
114     protected boolean isValidRequestURL(StringBuffer requestURL) {
115         if (requestURL == null) {
116             return false;
117         }
118 
119         String url = requestURL.toString();
120 
121         if (url.endsWith(_EXT_C) || url.endsWith(_EXT_CSS) ||
122             url.endsWith(_EXT_GIF) || url.endsWith(_EXT_IMAGE_COMPANY_LOGO) ||
123             url.endsWith(_EXT_ICO) || url.endsWith(_EXT_JS) ||
124             url.endsWith(_EXT_JPEG) || url.endsWith(_EXT_JSP) ||
125             url.endsWith(_EXT_PORTAL_LAYOUT) ||
126             url.endsWith(_EXT_PORTAL_LOGIN) ||
127             url.endsWith(_EXT_PORTAL_LOGOUT) || url.endsWith(_EXT_PNG)) {
128 
129             return false;
130         }
131         else {
132             return true;
133         }
134     }
135 
136     protected void processFilter(
137             HttpServletRequest request, HttpServletResponse response,
138             FilterChain filterChain)
139         throws Exception {
140 
141         request.setCharacterEncoding(StringPool.UTF8);
142         //response.setContentType(ContentTypes.TEXT_HTML_UTF8);
143 
144         // Make sure all redirects issued by the portal are absolute
145 
146         response = new AbsoluteRedirectsResponse(request, response);
147 
148         // Company id needs to always be called here so that it's properly set
149         // in subsequent calls
150 
151         long companyId = PortalInstances.getCompanyId(request);
152 
153         if (_log.isDebugEnabled()) {
154             _log.debug("Company id " + companyId);
155         }
156 
157         PortalUtil.getCurrentCompleteURL(request);
158         PortalUtil.getCurrentURL(request);
159 
160         HttpSession session = request.getSession();
161 
162         Boolean httpsInitial = (Boolean)session.getAttribute(
163             WebKeys.HTTPS_INITIAL);
164 
165         if (httpsInitial == null) {
166             httpsInitial = Boolean.valueOf(request.isSecure());
167 
168             session.setAttribute(WebKeys.HTTPS_INITIAL, httpsInitial);
169 
170             if (_log.isDebugEnabled()) {
171                 _log.debug("Setting httpsInitial to " + httpsInitial);
172             }
173         }
174 
175         if (!isFilterEnabled()) {
176             processFilter(
177                 VirtualHostFilter.class, request, response, filterChain);
178 
179             return;
180         }
181 
182         StringBuffer requestURL = request.getRequestURL();
183 
184         if (_log.isDebugEnabled()) {
185             _log.debug("Received " + requestURL);
186         }
187 
188         if (!isValidRequestURL(requestURL)) {
189             processFilter(
190                 VirtualHostFilter.class, request, response, filterChain);
191 
192             return;
193         }
194 
195         String contextPath = PortalUtil.getPathContext();
196 
197         String friendlyURL = request.getRequestURI();
198 
199         if ((Validator.isNotNull(contextPath)) &&
200             (friendlyURL.indexOf(contextPath) != -1)) {
201 
202             friendlyURL = friendlyURL.substring(contextPath.length());
203         }
204 
205         friendlyURL = StringUtil.replace(
206             friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
207 
208         String i18nLanguageId = null;
209 
210         Set<String> languageIds = I18nServlet.getLanguageIds();
211 
212         for (String languageId : languageIds) {
213             if (friendlyURL.startsWith(languageId)) {
214                 int pos = friendlyURL.indexOf(StringPool.SLASH, 1);
215 
216                 if (pos == -1) {
217                     continue;
218                 }
219 
220                 i18nLanguageId = friendlyURL.substring(0, pos);
221                 friendlyURL = friendlyURL.substring(pos);
222 
223                 break;
224             }
225         }
226 
227         if (_log.isDebugEnabled()) {
228             _log.debug("Friendly URL " + friendlyURL);
229         }
230 
231         if (!friendlyURL.equals(StringPool.SLASH) &&
232             !isValidFriendlyURL(friendlyURL)) {
233 
234             _log.debug("Friendly URL is not valid");
235 
236             processFilter(
237                 VirtualHostFilter.class, request, response, filterChain);
238 
239             return;
240         }
241 
242         LayoutSet layoutSet = (LayoutSet)request.getAttribute(
243             WebKeys.VIRTUAL_HOST_LAYOUT_SET);
244 
245         if (_log.isDebugEnabled()) {
246             _log.debug("Layout set " + layoutSet);
247         }
248 
249         if (layoutSet != null) {
250             try {
251                 LastPath lastPath = new LastPath(
252                     contextPath, friendlyURL, request.getParameterMap());
253 
254                 request.setAttribute(WebKeys.LAST_PATH, lastPath);
255 
256                 StringBuilder prefix = new StringBuilder();
257 
258                 Group group = GroupLocalServiceUtil.getGroup(
259                     layoutSet.getGroupId());
260 
261                 if (layoutSet.isPrivateLayout()) {
262                     if (group.isUser()) {
263                         prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
264                     }
265                     else {
266                         prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
267                     }
268                 }
269                 else {
270                     prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
271                 }
272 
273                 prefix.append(group.getFriendlyURL());
274 
275                 StringBuilder forwardURL = new StringBuilder();
276 
277                 if (i18nLanguageId != null) {
278                     forwardURL.append(i18nLanguageId);
279                 }
280 
281                 if (friendlyURL.startsWith(
282                         PropsValues.WIDGET_SERVLET_MAPPING)) {
283 
284                     forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
285 
286                     friendlyURL = StringUtil.replaceFirst(
287                         friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
288                         StringPool.BLANK);
289                 }
290 
291                 long plid = PortalUtil.getPlidFromFriendlyURL(
292                     companyId, friendlyURL);
293 
294                 if (plid <= 0) {
295                     forwardURL.append(prefix);
296                 }
297 
298                 forwardURL.append(friendlyURL);
299 
300                 if (_log.isDebugEnabled()) {
301                     _log.debug("Forward to " + forwardURL);
302                 }
303 
304                 RequestDispatcher requestDispatcher =
305                     _servletContext.getRequestDispatcher(forwardURL.toString());
306 
307                 requestDispatcher.forward(request, response);
308 
309                 return;
310             }
311             catch (Exception e) {
312                 _log.error(e, e);
313             }
314         }
315 
316         processFilter(VirtualHostFilter.class, request, response, filterChain);
317     }
318 
319     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
320 
321     private static String _EXT_C = "/c";
322 
323     private static String _EXT_CSS = ".css";
324 
325     private static String _EXT_GIF = ".gif";
326 
327     private static String _EXT_IMAGE_COMPANY_LOGO = "/image/company_logo";
328 
329     private static String _EXT_ICO = ".ico";
330 
331     private static String _EXT_JS = ".js";
332 
333     private static String _EXT_JPEG = ".jpeg";
334 
335     private static String _EXT_JSP = ".jsp";
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 static String _PRIVATE_GROUP_SERVLET_MAPPING =
364         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
365 
366     private static String _PRIVATE_USER_SERVLET_MAPPING =
367         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
368 
369     private static String _PUBLIC_GROUP_SERVLET_MAPPING =
370         PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
371 
372     private ServletContext _servletContext;
373 
374 }