1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.servlet.filters.virtualhost;
16  
17  import com.liferay.portal.LayoutFriendlyURLException;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.struts.LastPath;
21  import com.liferay.portal.kernel.util.CharPool;
22  import com.liferay.portal.kernel.util.StringBundler;
23  import com.liferay.portal.kernel.util.StringPool;
24  import com.liferay.portal.kernel.util.StringUtil;
25  import com.liferay.portal.kernel.util.Validator;
26  import com.liferay.portal.model.Group;
27  import com.liferay.portal.model.LayoutSet;
28  import com.liferay.portal.model.impl.LayoutImpl;
29  import com.liferay.portal.service.GroupLocalServiceUtil;
30  import com.liferay.portal.servlet.AbsoluteRedirectsResponse;
31  import com.liferay.portal.servlet.I18nServlet;
32  import com.liferay.portal.servlet.filters.BasePortalFilter;
33  import com.liferay.portal.util.PortalInstances;
34  import com.liferay.portal.util.PortalUtil;
35  import com.liferay.portal.util.PropsValues;
36  import com.liferay.portal.util.WebKeys;
37  
38  import java.util.Set;
39  
40  import javax.servlet.FilterChain;
41  import javax.servlet.FilterConfig;
42  import javax.servlet.RequestDispatcher;
43  import javax.servlet.ServletContext;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  import javax.servlet.http.HttpSession;
47  
48  /**
49   * <a href="VirtualHostFilter.java.html"><b><i>View Source</i></b></a>
50   *
51   * <p>
52   * This filter is used to provide virtual host functionality. However, this
53   * filter is still required even if you do not use virtual hosting because it
54   * sets the company id in the request so that subsequent calls in the thread
55   * have the company id properly set. This filter must also always be the first
56   * filter in the list of filters.
57   * </p>
58   *
59   * @author Joel Kozikowski
60   * @author Brian Wing Shun Chan
61   * @author Raymond Augé
62   */
63  public class VirtualHostFilter extends BasePortalFilter {
64  
65      public void init(FilterConfig filterConfig) {
66          super.init(filterConfig);
67  
68          _servletContext = filterConfig.getServletContext();
69      }
70  
71      protected boolean isValidFriendlyURL(String friendlyURL) {
72          friendlyURL = friendlyURL.toLowerCase();
73  
74          if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
75              friendlyURL.startsWith(
76                  _PRIVATE_GROUP_SERVLET_MAPPING + StringPool.SLASH) ||
77              friendlyURL.startsWith(
78                  _PUBLIC_GROUP_SERVLET_MAPPING + StringPool.SLASH) ||
79              friendlyURL.startsWith(
80                  _PRIVATE_USER_SERVLET_MAPPING + StringPool.SLASH) ||
81              friendlyURL.startsWith(_PATH_C) ||
82              friendlyURL.startsWith(_PATH_DELEGATE) ||
83              friendlyURL.startsWith(_PATH_DISPLAY_CHART) ||
84              friendlyURL.startsWith(_PATH_DOCUMENT) ||
85              friendlyURL.startsWith(_PATH_DTD) ||
86              friendlyURL.startsWith(_PATH_FACEBOOK) ||
87              friendlyURL.startsWith(_PATH_GOOGLE_GADGET) ||
88              friendlyURL.startsWith(_PATH_HTML) ||
89              friendlyURL.startsWith(_PATH_IMAGE) ||
90              friendlyURL.startsWith(_PATH_LANGUAGE) ||
91              friendlyURL.startsWith(_PATH_NETVIBES) ||
92              friendlyURL.startsWith(_PATH_PBHS) ||
93              friendlyURL.startsWith(_PATH_POLLER) ||
94              friendlyURL.startsWith(_PATH_SHAREPOINT) ||
95              friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
96              friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
97              friendlyURL.startsWith(_PATH_VTI) ||
98              friendlyURL.startsWith(_PATH_WAP) ||
99              friendlyURL.startsWith(_PATH_WIDGET)) {
100 
101             return false;
102         }
103 
104         int code = LayoutImpl.validateFriendlyURL(friendlyURL);
105 
106         if ((code > -1) &&
107             (code != LayoutFriendlyURLException.ENDS_WITH_SLASH)) {
108 
109             return false;
110         }
111 
112         return true;
113     }
114 
115     protected boolean isValidRequestURL(StringBuffer requestURL) {
116         if (requestURL == null) {
117             return false;
118         }
119 
120         String url = requestURL.toString();
121 
122         for (String extension : PropsValues.VIRTUAL_HOSTS_IGNORE_EXTENSIONS) {
123             if (url.endsWith(extension)) {
124                 return false;
125             }
126         }
127 
128         return true;
129     }
130 
131     protected void processFilter(
132             HttpServletRequest request, HttpServletResponse response,
133             FilterChain filterChain)
134         throws Exception {
135 
136         request.setCharacterEncoding(StringPool.UTF8);
137         //response.setContentType(ContentTypes.TEXT_HTML_UTF8);
138 
139         // Make sure all redirects issued by the portal are absolute
140 
141         response = new AbsoluteRedirectsResponse(request, response);
142 
143         // Company id needs to always be called here so that it's properly set
144         // in subsequent calls
145 
146         long companyId = PortalInstances.getCompanyId(request);
147 
148         if (_log.isDebugEnabled()) {
149             _log.debug("Company id " + companyId);
150         }
151 
152         PortalUtil.getCurrentCompleteURL(request);
153         PortalUtil.getCurrentURL(request);
154 
155         HttpSession session = request.getSession();
156 
157         Boolean httpsInitial = (Boolean)session.getAttribute(
158             WebKeys.HTTPS_INITIAL);
159 
160         if (httpsInitial == null) {
161             httpsInitial = Boolean.valueOf(request.isSecure());
162 
163             session.setAttribute(WebKeys.HTTPS_INITIAL, httpsInitial);
164 
165             if (_log.isDebugEnabled()) {
166                 _log.debug("Setting httpsInitial to " + httpsInitial);
167             }
168         }
169 
170         if (!isFilterEnabled()) {
171             processFilter(
172                 VirtualHostFilter.class, request, response, filterChain);
173 
174             return;
175         }
176 
177         StringBuffer requestURL = request.getRequestURL();
178 
179         if (_log.isDebugEnabled()) {
180             _log.debug("Received " + requestURL);
181         }
182 
183         if (!isValidRequestURL(requestURL)) {
184             processFilter(
185                 VirtualHostFilter.class, request, response, filterChain);
186 
187             return;
188         }
189 
190         String contextPath = PortalUtil.getPathContext();
191 
192         String originalFriendlyURL = request.getRequestURI();
193 
194         String friendlyURL = originalFriendlyURL;
195 
196         if ((Validator.isNotNull(contextPath)) &&
197             (friendlyURL.indexOf(contextPath) != -1)) {
198 
199             friendlyURL = friendlyURL.substring(contextPath.length());
200         }
201 
202         friendlyURL = StringUtil.replace(
203             friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
204 
205         String i18nLanguageId = null;
206 
207         Set<String> languageIds = I18nServlet.getLanguageIds();
208 
209         for (String languageId : languageIds) {
210             if (friendlyURL.startsWith(languageId)) {
211                 int pos = friendlyURL.indexOf(CharPool.SLASH, 1);
212 
213                 if (((pos != -1) && (pos != languageId.length())) ||
214                     ((pos == -1) && !friendlyURL.equals(languageId))) {
215 
216                     continue;
217                 }
218 
219                 if (pos == -1) {
220                     i18nLanguageId = friendlyURL;
221                     friendlyURL = StringPool.SLASH;
222                 }
223                 else {
224                     i18nLanguageId = friendlyURL.substring(0, pos);
225                     friendlyURL = friendlyURL.substring(pos);
226                 }
227 
228                 break;
229             }
230         }
231 
232         friendlyURL = StringUtil.replace(
233             friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING, StringPool.BLANK);
234 
235         if (_log.isDebugEnabled()) {
236             _log.debug("Friendly URL " + friendlyURL);
237         }
238 
239         if (!friendlyURL.equals(StringPool.SLASH) &&
240             !isValidFriendlyURL(friendlyURL)) {
241 
242             _log.debug("Friendly URL is not valid");
243 
244             processFilter(
245                 VirtualHostFilter.class, request, response, filterChain);
246 
247             return;
248         }
249 
250         LayoutSet layoutSet = (LayoutSet)request.getAttribute(
251             WebKeys.VIRTUAL_HOST_LAYOUT_SET);
252 
253         if (_log.isDebugEnabled()) {
254             _log.debug("Layout set " + layoutSet);
255         }
256 
257         if (layoutSet != null) {
258             try {
259                 LastPath lastPath = new LastPath(
260                     contextPath, friendlyURL, request.getParameterMap());
261 
262                 request.setAttribute(WebKeys.LAST_PATH, lastPath);
263 
264                 StringBundler prefix = new StringBundler(2);
265 
266                 Group group = GroupLocalServiceUtil.getGroup(
267                     layoutSet.getGroupId());
268 
269                 if (layoutSet.isPrivateLayout()) {
270                     if (group.isUser()) {
271                         prefix.append(_PRIVATE_USER_SERVLET_MAPPING);
272                     }
273                     else {
274                         prefix.append(_PRIVATE_GROUP_SERVLET_MAPPING);
275                     }
276                 }
277                 else {
278                     prefix.append(_PUBLIC_GROUP_SERVLET_MAPPING);
279                 }
280 
281                 prefix.append(group.getFriendlyURL());
282 
283                 StringBundler forwardURL = new StringBundler(6);
284 
285                 if (i18nLanguageId != null) {
286                     forwardURL.append(i18nLanguageId);
287                 }
288 
289                 if (originalFriendlyURL.startsWith(
290                         PropsValues.WIDGET_SERVLET_MAPPING)) {
291 
292                     forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
293 
294                     friendlyURL = StringUtil.replaceFirst(
295                         friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
296                         StringPool.BLANK);
297                 }
298 
299                 long plid = PortalUtil.getPlidFromFriendlyURL(
300                     companyId, friendlyURL);
301 
302                 if (plid <= 0) {
303                     forwardURL.append(prefix);
304                 }
305 
306                 forwardURL.append(friendlyURL);
307 
308                 if (_log.isDebugEnabled()) {
309                     _log.debug("Forward to " + forwardURL);
310                 }
311 
312                 RequestDispatcher requestDispatcher =
313                     _servletContext.getRequestDispatcher(forwardURL.toString());
314 
315                 requestDispatcher.forward(request, response);
316 
317                 return;
318             }
319             catch (Exception e) {
320                 _log.error(e, e);
321             }
322         }
323 
324         processFilter(VirtualHostFilter.class, request, response, filterChain);
325     }
326 
327     private static final String _PATH_C = "/c/";
328 
329     private static final String _PATH_DELEGATE = "/delegate/";
330 
331     private static final String _PATH_DISPLAY_CHART = "/display_chart";
332 
333     private static final String _PATH_DOCUMENT = "/document/";
334 
335     private static final String _PATH_DTD = "/dtd/";
336 
337     private static final String _PATH_FACEBOOK = "/facebook/";
338 
339     private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
340 
341     private static final String _PATH_HTML = "/html/";
342 
343     private static final String _PATH_IMAGE = "/image/";
344 
345     private static final String _PATH_LANGUAGE = "/language/";
346 
347     private static final String _PATH_NETVIBES = "/netvibes/";
348 
349     private static final String _PATH_PBHS = "/pbhs/";
350 
351     private static final String _PATH_POLLER = "/poller/";
352 
353     private static final String _PATH_SHAREPOINT = "/sharepoint/";
354 
355     private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
356 
357     private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
358 
359     private static final String _PATH_VTI = "/_vti_";
360 
361     private static final String _PATH_WAP = "/wap/";
362 
363     private static final String _PATH_WIDGET = "/widget/";
364 
365     private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
366         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
367 
368     private static final String _PRIVATE_USER_SERVLET_MAPPING =
369         PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
370 
371     private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
372         PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
373 
374     private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
375 
376     private ServletContext _servletContext;
377 
378 }