| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. The ASF licenses this file to You |
| * under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. For additional information regarding |
| * copyright in this work, please see the NOTICE file in the top level |
| * directory of this distribution. |
| */ |
| |
| package org.apache.roller.weblogger.ui.rendering; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Set; |
| import javax.servlet.RequestDispatcher; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.roller.weblogger.config.WebloggerConfig; |
| import org.apache.roller.weblogger.business.WebloggerFactory; |
| import org.apache.roller.weblogger.pojos.Weblog; |
| |
| |
| /** |
| * Handles rendering requests for Roller pages/feeds by routing to the appropriate Servlet. |
| * |
| * This request mapper is used to map all weblog specific urls of the form |
| * /<weblog handle>/* to the appropriate servlet for handling the actual |
| * request. |
| * |
| * TODO: we should try and make this class easier to extend and build upon |
| */ |
| public class WeblogRequestMapper implements RequestMapper { |
| |
| private static Log log = LogFactory.getLog(WeblogRequestMapper.class); |
| |
| private static final String PAGE_SERVLET = "/roller-ui/rendering/page"; |
| private static final String FEED_SERVLET = "/roller-ui/rendering/feed"; |
| private static final String RESOURCE_SERVLET = "/roller-ui/rendering/resources"; |
| private static final String MEDIA_SERVLET = "/roller-ui/rendering/media-resources"; |
| private static final String SEARCH_SERVLET = "/roller-ui/rendering/search"; |
| private static final String RSD_SERVLET = "/roller-ui/rendering/rsd"; |
| |
| private static final String COMMENT_SERVLET = "/roller-ui/rendering/comment"; |
| private static final String TRACKBACK_SERVLET = "/roller-ui/rendering/trackback"; |
| |
| |
| // url patterns that are not allowed to be considered weblog handles |
| Set<String> restricted = null; |
| |
| |
| public WeblogRequestMapper() { |
| |
| this.restricted = new HashSet<String>(); |
| |
| // build roller restricted list |
| String restrictList = |
| WebloggerConfig.getProperty("rendering.weblogMapper.rollerProtectedUrls"); |
| if(restrictList != null && restrictList.trim().length() > 0) { |
| String[] restrict = restrictList.split(","); |
| this.restricted.addAll(Arrays.asList(restrict)); |
| } |
| |
| // add user restricted list |
| restrictList = |
| WebloggerConfig.getProperty("rendering.weblogMapper.userProtectedUrls"); |
| if(restrictList != null && restrictList.trim().length() > 0) { |
| String[] restrict = restrictList.split(","); |
| this.restricted.addAll(Arrays.asList(restrict)); |
| } |
| } |
| |
| |
| public boolean handleRequest(HttpServletRequest request, HttpServletResponse response) |
| throws ServletException, IOException { |
| |
| // kinda silly, but we need to keep track of whether or not the url had |
| // a trailing slash so that we can act accordingly |
| boolean trailingSlash = false; |
| |
| String weblogHandle = null; |
| String weblogLocale = null; |
| String weblogRequestContext = null; |
| String weblogRequestData = null; |
| |
| log.debug("evaluating ["+request.getRequestURI()+"]"); |
| |
| // figure out potential weblog handle |
| String servlet = request.getRequestURI(); |
| String pathInfo = null; |
| |
| if(servlet != null && servlet.trim().length() > 1) { |
| |
| if(request.getContextPath() != null) { |
| servlet = servlet.substring(request.getContextPath().length()); |
| } |
| |
| // strip off the leading slash |
| servlet = servlet.substring(1); |
| |
| // strip off trailing slash if needed |
| if(servlet.endsWith("/")) { |
| servlet = servlet.substring(0, servlet.length() - 1); |
| trailingSlash = true; |
| } |
| |
| if(servlet.indexOf('/') != -1) { |
| weblogHandle = servlet.substring(0, servlet.indexOf('/')); |
| pathInfo = servlet.substring(servlet.indexOf('/')+1); |
| } else { |
| weblogHandle = servlet; |
| } |
| } |
| |
| log.debug("potential weblog handle = "+weblogHandle); |
| |
| // check if it's a valid weblog handle |
| if(restricted.contains(weblogHandle) || !this.isWeblog(weblogHandle)) { |
| log.debug("SKIPPED "+weblogHandle); |
| return false; |
| } |
| |
| String weblogAbsoluteURL = |
| WebloggerConfig.getProperty("weblog.absoluteurl." + weblogHandle); |
| // If an absolute URL is specified for this weblog, make sure request URL matches |
| if (weblogAbsoluteURL != null && !request.getRequestURL().toString().startsWith(weblogAbsoluteURL)) { |
| log.debug("SKIPPED " + weblogHandle); |
| return false; |
| } |
| |
| log.debug("WEBLOG_URL "+request.getServletPath()); |
| |
| // parse the rest of the url and build forward url |
| if(pathInfo != null) { |
| |
| // parse the next portion of the url |
| // we expect [locale/]<context>/<extra>/<info> |
| String[] urlPath = pathInfo.split("/", 3); |
| |
| // if we have a locale, deal with it |
| if(this.isLocale(urlPath[0])) { |
| weblogLocale = urlPath[0]; |
| |
| // no extra path info specified |
| if(urlPath.length == 2) { |
| weblogRequestContext = urlPath[1]; |
| weblogRequestData = null; |
| |
| // request contains extra path info |
| } else if(urlPath.length == 3) { |
| weblogRequestContext = urlPath[1]; |
| weblogRequestData = urlPath[2]; |
| } |
| |
| // otherwise locale is empty |
| } else { |
| weblogLocale = null; |
| weblogRequestContext = urlPath[0]; |
| |
| // last part of request is extra path info |
| if(urlPath.length == 2) { |
| weblogRequestData = urlPath[1]; |
| |
| // if we didn't have a locale then we have split too much |
| // so we reassemble the last 2 path elements together |
| } else if(urlPath.length == 3) { |
| weblogRequestData = urlPath[1] + "/" + urlPath[2]; |
| } |
| } |
| |
| } |
| |
| // special handling for trailing slash issue |
| // we need this because by http standards the urls /foo and /foo/ are |
| // supposed to be considered different, so we must enforce that |
| if(weblogRequestContext == null && !trailingSlash) { |
| // this means someone referred to a weblog index page with the |
| // shortest form of url /<weblog> or /<weblog>/<locale> and we need |
| // to do a redirect to /<weblog>/ or /<weblog>/<locale>/ |
| String redirectUrl = request.getRequestURI() + "/"; |
| if(request.getQueryString() != null) { |
| redirectUrl += "?"+request.getQueryString(); |
| } |
| |
| response.sendRedirect(redirectUrl); |
| return true; |
| |
| } else if(weblogRequestContext != null && |
| "tags".equals(weblogRequestContext)) { |
| // tags section can have an index page at /<weblog>/tags/ and |
| // a tags query at /<weblog>/tags/tag1+tag2, buth that's it |
| if((weblogRequestData == null && !trailingSlash) || |
| (weblogRequestData != null && trailingSlash)) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| return true; |
| } |
| } else if(weblogRequestContext != null && trailingSlash) { |
| // this means that someone has accessed a weblog url and included |
| // a trailing slash, like /<weblog>/entry/<anchor>/ which is not |
| // supported, so we need to offer up a 404 Not Found |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| return true; |
| } |
| |
| // calculate forward url |
| String forwardUrl = calculateForwardUrl(request, weblogHandle, weblogLocale, |
| weblogRequestContext, weblogRequestData); |
| |
| // if we don't have a forward url then the request was invalid somehow |
| if(forwardUrl == null) { |
| return false; |
| } |
| |
| // dispatch to forward url |
| log.debug("forwarding to "+forwardUrl); |
| RequestDispatcher dispatch = request.getRequestDispatcher(forwardUrl); |
| dispatch.forward(request, response); |
| |
| // we dealt with this request ourselves, so return "true" |
| return true; |
| } |
| |
| |
| /** |
| * Convenience method for caculating the servlet forward url given a set |
| * of information to make the decision with. |
| * |
| * handle is always assumed valid, all other params may be null. |
| */ |
| private String calculateForwardUrl(HttpServletRequest request, |
| String handle, String locale, |
| String context, String data) { |
| |
| log.debug(handle+","+locale+","+context+","+data); |
| |
| StringBuilder forwardUrl = new StringBuilder(); |
| |
| // POST urls, like comment and trackback servlets |
| if("POST".equals(request.getMethod())) { |
| // posting to permalink, this means comment or trackback |
| if(context.equals("entry")) { |
| // trackback requests are required to have an "excerpt" param |
| if(request.getParameter("excerpt") != null) { |
| |
| forwardUrl.append(TRACKBACK_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(locale != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(locale); |
| } |
| forwardUrl.append("/"); |
| forwardUrl.append(context); |
| if(data != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(data); |
| } |
| |
| // comment requests are required to have a "content" param |
| } else if(request.getParameter("content") != null) { |
| |
| forwardUrl.append(COMMENT_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(locale != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(locale); |
| } |
| forwardUrl.append("/"); |
| forwardUrl.append(context); |
| if(data != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(data); |
| } |
| } |
| |
| } else { |
| // someone posting data where they aren't supposed to |
| return null; |
| } |
| |
| } else { |
| // no context means weblog homepage |
| if(context == null) { |
| |
| forwardUrl.append(PAGE_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(locale != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(locale); |
| } |
| |
| // requests handled by PageServlet |
| } else if(context.equals("page") || context.equals("entry") || |
| context.equals("date") || context.equals("category") |
| || context.equals("tags")) { |
| |
| forwardUrl.append(PAGE_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(locale != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(locale); |
| } |
| forwardUrl.append("/"); |
| forwardUrl.append(context); |
| if(data != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(data); |
| } |
| |
| // requests handled by FeedServlet |
| } else if(context.equals("feed")) { |
| |
| forwardUrl.append(FEED_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(locale != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(locale); |
| } |
| if(data != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(data); |
| } |
| |
| // requests handled by ResourceServlet |
| } else if(context.equals("resource")) { |
| |
| forwardUrl.append(RESOURCE_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(data != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(data); |
| } |
| |
| // requests handled by MediaResourceServlet |
| } else if(context.equals("mediaresource")) { |
| |
| forwardUrl.append(MEDIA_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(data != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(data); |
| } |
| |
| // requests handled by SearchServlet |
| } else if(context.equals("search")) { |
| |
| forwardUrl.append(SEARCH_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| if(locale != null) { |
| forwardUrl.append("/"); |
| forwardUrl.append(locale); |
| } |
| // requests handled by RSDServlet |
| } else if(context.equals("rsd")) { |
| |
| forwardUrl.append(RSD_SERVLET); |
| forwardUrl.append("/"); |
| forwardUrl.append(handle); |
| |
| // unsupported url |
| } else { |
| return null; |
| } |
| } |
| |
| log.debug("FORWARD_URL "+forwardUrl.toString()); |
| |
| return forwardUrl.toString(); |
| } |
| |
| |
| /** |
| * convenience method which determines if the given string is a valid |
| * weblog handle. |
| * |
| * TODO 3.0: some kind of caching |
| */ |
| private boolean isWeblog(String potentialHandle) { |
| |
| log.debug("checking weblog handle "+potentialHandle); |
| |
| boolean isWeblog = false; |
| |
| try { |
| Weblog weblog = WebloggerFactory.getWeblogger().getWeblogManager() |
| .getWeblogByHandle(potentialHandle); |
| |
| if(weblog != null) { |
| isWeblog = true; |
| } |
| } catch(Exception ex) { |
| // doesn't really matter to us why it's not a valid website |
| } |
| |
| return isWeblog; |
| } |
| |
| |
| /** |
| * Convenience method which determines if the given string is a valid |
| * locale string. |
| */ |
| private boolean isLocale(String potentialLocale) { |
| |
| boolean isLocale = false; |
| |
| // we only support 2 or 5 character locale strings, so check that first |
| if(potentialLocale != null && |
| (potentialLocale.length() == 2 || potentialLocale.length() == 5)) { |
| |
| // now make sure that the format is proper ... e.g. "en_US" |
| // we are not going to be picky about capitalization |
| String[] langCountry = potentialLocale.split("_"); |
| if(langCountry.length == 1 && |
| langCountry[0] != null && langCountry[0].length() == 2) { |
| isLocale = true; |
| |
| } else if(langCountry.length == 2 && |
| langCountry[0] != null && langCountry[0].length() == 2 && |
| langCountry[1] != null && langCountry[1].length() == 2) { |
| |
| isLocale = true; |
| } |
| } |
| |
| return isLocale; |
| } |
| |
| } |