001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.servlet.filters.virtualhost;
016    
017    import com.liferay.portal.LayoutFriendlyURLException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.struts.LastPath;
021    import com.liferay.portal.kernel.util.CharPool;
022    import com.liferay.portal.kernel.util.StringBundler;
023    import com.liferay.portal.kernel.util.StringPool;
024    import com.liferay.portal.kernel.util.StringUtil;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.model.Group;
027    import com.liferay.portal.model.LayoutSet;
028    import com.liferay.portal.model.impl.LayoutImpl;
029    import com.liferay.portal.service.GroupLocalServiceUtil;
030    import com.liferay.portal.servlet.I18nServlet;
031    import com.liferay.portal.servlet.filters.BasePortalFilter;
032    import com.liferay.portal.util.PortalInstances;
033    import com.liferay.portal.util.PortalUtil;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.portal.util.WebKeys;
036    import com.liferay.portal.webserver.WebServerServlet;
037    
038    import java.util.Set;
039    
040    import javax.servlet.FilterChain;
041    import javax.servlet.FilterConfig;
042    import javax.servlet.RequestDispatcher;
043    import javax.servlet.ServletContext;
044    import javax.servlet.http.HttpServletRequest;
045    import javax.servlet.http.HttpServletResponse;
046    
047    /**
048     * <p>
049     * This filter is used to provide virtual host functionality.
050     * </p>
051     *
052     * @author Joel Kozikowski
053     * @author Brian Wing Shun Chan
054     * @author Raymond Augé
055     * @author Eduardo Lundgren
056     */
057    public class VirtualHostFilter extends BasePortalFilter {
058    
059            @Override
060            public void init(FilterConfig filterConfig) {
061                    super.init(filterConfig);
062    
063                    _servletContext = filterConfig.getServletContext();
064            }
065    
066            @Override
067            public boolean isFilterEnabled(
068                    HttpServletRequest request, HttpServletResponse response) {
069    
070                    StringBuffer requestURL = request.getRequestURL();
071    
072                    if (isValidRequestURL(requestURL)) {
073                            return true;
074                    }
075                    else {
076                            return false;
077                    }
078            }
079    
080            protected boolean isValidFriendlyURL(String friendlyURL) {
081                    friendlyURL = friendlyURL.toLowerCase();
082    
083                    if (PortalInstances.isVirtualHostsIgnorePath(friendlyURL) ||
084                            friendlyURL.startsWith(
085                                    _PRIVATE_GROUP_SERVLET_MAPPING + StringPool.SLASH) ||
086                            friendlyURL.startsWith(
087                                    _PUBLIC_GROUP_SERVLET_MAPPING + StringPool.SLASH) ||
088                            friendlyURL.startsWith(
089                                    _PRIVATE_USER_SERVLET_MAPPING + StringPool.SLASH) ||
090                            friendlyURL.startsWith(_PATH_C) ||
091                            friendlyURL.startsWith(_PATH_COMBO) ||
092                            friendlyURL.startsWith(_PATH_DELEGATE) ||
093                            friendlyURL.startsWith(_PATH_DISPLAY_CHART) ||
094                            friendlyURL.startsWith(_PATH_DTD) ||
095                            friendlyURL.startsWith(_PATH_ELOQUA) ||
096                            friendlyURL.startsWith(_PATH_FACEBOOK) ||
097                            friendlyURL.startsWith(_PATH_GOOGLE_GADGET) ||
098                            friendlyURL.startsWith(_PATH_HTML) ||
099                            friendlyURL.startsWith(_PATH_IMAGE) ||
100                            friendlyURL.startsWith(_PATH_LANGUAGE) ||
101                            friendlyURL.startsWith(_PATH_LUCENE) ||
102                            friendlyURL.startsWith(_PATH_NETVIBES) ||
103                            friendlyURL.startsWith(_PATH_OSGI) ||
104                            friendlyURL.startsWith(_PATH_PBHS) ||
105                            friendlyURL.startsWith(_PATH_POLLER) ||
106                            friendlyURL.startsWith(_PATH_REST) ||
107                            friendlyURL.startsWith(_PATH_ROBOTS_TXT) ||
108                            friendlyURL.startsWith(_PATH_SHAREPOINT) ||
109                            friendlyURL.startsWith(_PATH_SITEMAP_XML) ||
110                            friendlyURL.startsWith(_PATH_SOFTWARE_CATALOG) ||
111                            friendlyURL.startsWith(_PATH_VTI) ||
112                            friendlyURL.startsWith(_PATH_WAP) ||
113                            friendlyURL.startsWith(_PATH_WIDGET) ||
114                            friendlyURL.startsWith(_PATH_XMLRPC)) {
115    
116                            return false;
117                    }
118    
119                    int code = LayoutImpl.validateFriendlyURL(friendlyURL);
120    
121                    if ((code > -1) &&
122                            (code != LayoutFriendlyURLException.ENDS_WITH_SLASH)) {
123    
124                            return false;
125                    }
126    
127                    return true;
128            }
129    
130            protected boolean isValidRequestURL(StringBuffer requestURL) {
131                    if (requestURL == null) {
132                            return false;
133                    }
134    
135                    String url = requestURL.toString();
136    
137                    for (String extension : PropsValues.VIRTUAL_HOSTS_IGNORE_EXTENSIONS) {
138                            if (url.endsWith(extension)) {
139                                    return false;
140                            }
141                    }
142    
143                    return true;
144            }
145    
146            @Override
147            protected void processFilter(
148                            HttpServletRequest request, HttpServletResponse response,
149                            FilterChain filterChain)
150                    throws Exception {
151    
152                    long companyId = PortalInstances.getCompanyId(request);
153    
154                    String contextPath = PortalUtil.getPathContext();
155    
156                    String originalFriendlyURL = request.getRequestURI();
157    
158                    String friendlyURL = originalFriendlyURL;
159    
160                    if ((Validator.isNotNull(contextPath)) &&
161                            (friendlyURL.indexOf(contextPath) != -1)) {
162    
163                            friendlyURL = friendlyURL.substring(contextPath.length());
164                    }
165    
166                    friendlyURL = StringUtil.replace(
167                            friendlyURL, StringPool.DOUBLE_SLASH, StringPool.SLASH);
168    
169                    String i18nLanguageId = null;
170    
171                    Set<String> languageIds = I18nServlet.getLanguageIds();
172    
173                    for (String languageId : languageIds) {
174                            if (StringUtil.startsWith(friendlyURL, languageId)) {
175                                    int pos = friendlyURL.indexOf(CharPool.SLASH, 1);
176    
177                                    if (((pos != -1) && (pos != languageId.length())) ||
178                                            ((pos == -1) &&
179                                             !friendlyURL.equalsIgnoreCase(languageId))) {
180    
181                                            continue;
182                                    }
183    
184                                    if (pos == -1) {
185                                            i18nLanguageId = languageId;
186                                            friendlyURL = StringPool.SLASH;
187                                    }
188                                    else {
189                                            i18nLanguageId = languageId.substring(0, pos);
190                                            friendlyURL = friendlyURL.substring(pos);
191                                    }
192    
193                                    break;
194                            }
195                    }
196    
197                    friendlyURL = StringUtil.replace(
198                            friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING, StringPool.BLANK);
199    
200                    if (_log.isDebugEnabled()) {
201                            _log.debug("Friendly URL " + friendlyURL);
202                    }
203    
204                    if (!friendlyURL.equals(StringPool.SLASH) &&
205                            !isValidFriendlyURL(friendlyURL)) {
206    
207                            _log.debug("Friendly URL is not valid");
208    
209                            processFilter(
210                                    VirtualHostFilter.class, request, response, filterChain);
211    
212                            return;
213                    }
214                    else if (friendlyURL.startsWith(_PATH_DOCUMENTS)) {
215                            if (WebServerServlet.hasFiles(request)) {
216                                    processFilter(
217                                            VirtualHostFilter.class, request, response, filterChain);
218    
219                                    return;
220                            }
221                    }
222    
223                    LayoutSet layoutSet = (LayoutSet)request.getAttribute(
224                            WebKeys.VIRTUAL_HOST_LAYOUT_SET);
225    
226                    if (_log.isDebugEnabled()) {
227                            _log.debug("Layout set " + layoutSet);
228                    }
229    
230                    if (layoutSet == null) {
231                            processFilter(
232                                    VirtualHostFilter.class, request, response, filterChain);
233    
234                            return;
235                    }
236    
237                    try {
238                            LastPath lastPath = new LastPath(
239                                    contextPath, friendlyURL, request.getParameterMap());
240    
241                            request.setAttribute(WebKeys.LAST_PATH, lastPath);
242    
243                            StringBundler forwardURL = new StringBundler(5);
244    
245                            if (i18nLanguageId != null) {
246                                    forwardURL.append(i18nLanguageId);
247                            }
248    
249                            if (originalFriendlyURL.startsWith(
250                                            PropsValues.WIDGET_SERVLET_MAPPING)) {
251    
252                                    forwardURL.append(PropsValues.WIDGET_SERVLET_MAPPING);
253    
254                                    friendlyURL = StringUtil.replaceFirst(
255                                            friendlyURL, PropsValues.WIDGET_SERVLET_MAPPING,
256                                            StringPool.BLANK);
257                            }
258    
259                            long plid = PortalUtil.getPlidFromFriendlyURL(
260                                    companyId, friendlyURL);
261    
262                            if (plid <= 0) {
263                                    Group group = GroupLocalServiceUtil.getGroup(
264                                            layoutSet.getGroupId());
265    
266                                    if (group.isGuest() &&
267                                            friendlyURL.equals(StringPool.SLASH)) {
268    
269                                            String homeURL = PortalUtil.getRelativeHomeURL(request);
270    
271                                            if (Validator.isNotNull(homeURL)) {
272                                                    friendlyURL = homeURL;
273                                            }
274                                    }
275                                    else {
276                                            if (layoutSet.isPrivateLayout()) {
277                                                    if (group.isUser()) {
278                                                            forwardURL.append(_PRIVATE_USER_SERVLET_MAPPING);
279                                                    }
280                                                    else {
281                                                            forwardURL.append(_PRIVATE_GROUP_SERVLET_MAPPING);
282                                                    }
283                                            }
284                                            else {
285                                                    forwardURL.append(_PUBLIC_GROUP_SERVLET_MAPPING);
286                                            }
287    
288                                            forwardURL.append(group.getFriendlyURL());
289                                    }
290                            }
291    
292                            forwardURL.append(friendlyURL);
293    
294                            if (_log.isDebugEnabled()) {
295                                    _log.debug("Forward to " + forwardURL);
296                            }
297    
298                            RequestDispatcher requestDispatcher =
299                                    _servletContext.getRequestDispatcher(forwardURL.toString());
300    
301                            requestDispatcher.forward(request, response);
302                    }
303                    catch (Exception e) {
304                            _log.error(e, e);
305    
306                            processFilter(
307                                    VirtualHostFilter.class, request, response, filterChain);
308                    }
309            }
310    
311            private static final String _PATH_C = "/c/";
312    
313            private static final String _PATH_COMBO = "/combo/";
314    
315            private static final String _PATH_DELEGATE = "/delegate/";
316    
317            private static final String _PATH_DISPLAY_CHART = "/display_chart";
318    
319            private static final String _PATH_DOCUMENTS = "/documents/";
320    
321            private static final String _PATH_DTD = "/dtd/";
322    
323            private static final String _PATH_ELOQUA = "/elqNow/";
324    
325            private static final String _PATH_FACEBOOK = "/facebook/";
326    
327            private static final String _PATH_GOOGLE_GADGET = "/google_gadget/";
328    
329            private static final String _PATH_HTML = "/html/";
330    
331            private static final String _PATH_IMAGE = "/image/";
332    
333            private static final String _PATH_LANGUAGE = "/language/";
334    
335            private static final String _PATH_LUCENE = "/lucene/";
336    
337            private static final String _PATH_NETVIBES = "/netvibes/";
338    
339            private static final String _PATH_OSGI = "/osgi/";
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_REST = "/rest/";
346    
347            private static final String _PATH_ROBOTS_TXT = "/robots.txt";
348    
349            private static final String _PATH_SHAREPOINT = "/sharepoint/";
350    
351            private static final String _PATH_SITEMAP_XML = "/sitemap.xml";
352    
353            private static final String _PATH_SOFTWARE_CATALOG = "/software_catalog";
354    
355            private static final String _PATH_VTI = "/_vti_";
356    
357            private static final String _PATH_WAP = "/wap/";
358    
359            private static final String _PATH_WIDGET = "/widget/";
360    
361            private static final String _PATH_XMLRPC = "/xmlrpc/";
362    
363            private static final String _PRIVATE_GROUP_SERVLET_MAPPING =
364                    PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
365    
366            private static final String _PRIVATE_USER_SERVLET_MAPPING =
367                    PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
368    
369            private static final String _PUBLIC_GROUP_SERVLET_MAPPING =
370                    PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
371    
372            private static Log _log = LogFactoryUtil.getLog(VirtualHostFilter.class);
373    
374            private ServletContext _servletContext;
375    
376    }