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