File Source: WeblogRequestMapper.java

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   *  contributor license agreements.  The ASF licenses this file to You
     4   * under the Apache License, Version 2.0 (the "License"); you may not
     5   * use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.  For additional information regarding
    15   * copyright in this work, please see the NOTICE file in the top level
    16   * directory of this distribution.
    17   */
    18  
    19  package org.apache.roller.weblogger.ui.rendering;
    20  
    21  import java.io.IOException;
    22  import java.util.HashSet;
    23  import java.util.Set;
    24  import javax.servlet.RequestDispatcher;
    25  import javax.servlet.ServletException;
    26  import javax.servlet.http.HttpServletRequest;
    27  import javax.servlet.http.HttpServletResponse;
    28  
    29  import org.apache.commons.lang.StringUtils;
    30  import org.apache.commons.logging.Log;
    31  import org.apache.commons.logging.LogFactory;
    32  import org.apache.roller.weblogger.config.WebloggerConfig;
    33  import org.apache.roller.weblogger.business.WebloggerFactory;
    34  import org.apache.roller.weblogger.business.UserManager;
    35  import org.apache.roller.weblogger.pojos.Weblog;
    36  
    37  
    38  /**
    39   * Handles rendering requests for Roller pages/feeds by routing to the appropriate Servlet.
    40   *
    41   * This request mapper is used to map all weblog specific urls of the form
    42   * /<weblog handle>/* to the appropriate servlet for handling the actual
    43   * request.
    44   *
    45   * TODO: we should try and make this class easier to extend and build upon
    46   */
    47  public class WeblogRequestMapper implements RequestMapper {
    48      
             /* 
    P/P       *  Method: org.apache.roller.weblogger.ui.rendering.WeblogRequestMapper__static_init
              * 
              *  Postconditions:
              *    init'ed(log)
              */
    49      private static Log log = LogFactory.getLog(WeblogRequestMapper.class);
    50      
    51      private static final String PAGE_SERVLET = "/roller-ui/rendering/page";
    52      private static final String FEED_SERVLET = "/roller-ui/rendering/feed";
    53      private static final String RESOURCE_SERVLET = "/roller-ui/rendering/resources";
    54      private static final String SEARCH_SERVLET = "/roller-ui/rendering/search";
    55      private static final String RSD_SERVLET = "/roller-ui/rendering/rsd";
    56      
    57      private static final String COMMENT_SERVLET = "/roller-ui/rendering/comment";
    58      private static final String TRACKBACK_SERVLET = "/roller-ui/rendering/trackback";
    59      
    60      
    61      // url patterns that are not allowed to be considered weblog handles
    62      Set restricted = null;
    63      
    64      
             /* 
    P/P       *  Method: void org.apache.roller.weblogger.ui.rendering.WeblogRequestMapper()
              * 
              *  Postconditions:
              *    this.restricted == &new HashSet(WeblogRequestMapper#1)
              *    new HashSet(WeblogRequestMapper#1) num objects == 1
              * 
              *  Test Vectors:
              *    java.lang.String:length(...)@72: {0}, {1..232-1}
              *    java.lang.String:length(...)@82: {0}, {1..232-1}
              *    org.apache.roller.weblogger.config.WebloggerConfig:getProperty(...)@70: Addr_Set{null}, Inverse{null}
              *    org.apache.roller.weblogger.config.WebloggerConfig:getProperty(...)@80: Addr_Set{null}, Inverse{null}
              */
    65      public WeblogRequestMapper() {
    66          
    67          this.restricted = new HashSet();
    68          
    69          // build roller restricted list
    70          String restrictList = 
    71                  WebloggerConfig.getProperty("rendering.weblogMapper.rollerProtectedUrls");
    72          if(restrictList != null && restrictList.trim().length() > 0) {
    73              String[] restrict = restrictList.split(",");
+   74              for(int i=0; i < restrict.length; i++) {
+   75                  this.restricted.add(restrict[i]);
    76              }
    77          }
    78          
    79          // add user restricted list
    80          restrictList = 
    81                  WebloggerConfig.getProperty("rendering.weblogMapper.userProtectedUrls");
    82          if(restrictList != null && restrictList.trim().length() > 0) {
    83              String[] restrict = restrictList.split(",");
+   84              for(int i=0; i < restrict.length; i++) {
+   85                  this.restricted.add(restrict[i]);
    86              }
    87          }
    88      }
    89      
    90      
    91      public boolean handleRequest(HttpServletRequest request, HttpServletResponse response)
    92              throws ServletException, IOException {
    93          
    94          // kinda silly, but we need to keep track of whether or not the url had
    95          // a trailing slash so that we can act accordingly
                 /* 
    P/P           *  Method: bool handleRequest(HttpServletRequest, HttpServletResponse)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    request != null
                  *    this.restricted != null
                  *    (soft) response != null
                  * 
                  *  Presumptions:
                  *    java.lang.String:indexOf(...)@125 <= 232-2
                  *    javax.servlet.http.HttpServletRequest:getContextPath(...)@112 != null
                  *    javax.servlet.http.HttpServletRequest:getRequestDispatcher(...)@224 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.lang.String:endsWith(...)@118: {0}, {1}
                  *    java.lang.String:indexOf(...)@123: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:length(...)@109: {0,1}, {2..232-1}
                  *    java.util.Set:contains(...)@134: {1}, {0}
                  *    javax.servlet.http.HttpServletRequest:getContextPath(...)@111: Addr_Set{null}, Inverse{null}
                  *    javax.servlet.http.HttpServletRequest:getQueryString(...)@189: Addr_Set{null}, Inverse{null}
                  *    javax.servlet.http.HttpServletRequest:getRequestURI(...)@106: Addr_Set{null}, Inverse{null}
                  */
    96          boolean trailingSlash = false;
    97          
    98          String weblogHandle = null;
    99          String weblogLocale = null;
   100          String weblogRequestContext = null;
   101          String weblogRequestData = null;
   102          
   103          log.debug("evaluating ["+request.getRequestURI()+"]");
   104          
   105          // figure out potential weblog handle
   106          String servlet = request.getRequestURI();
   107          String pathInfo = null;
   108                  
   109          if(servlet != null && servlet.trim().length() > 1) {
   110              
   111              if(request.getContextPath() != null)
   112                  servlet = servlet.substring(request.getContextPath().length());
   113              
   114              // strip off the leading slash
   115              servlet = servlet.substring(1);
   116              
   117              // strip off trailing slash if needed
   118              if(servlet.endsWith("/")) {
   119                  servlet = servlet.substring(0, servlet.length() - 1);
   120                  trailingSlash = true;
   121              }
   122              
   123              if(servlet.indexOf("/") != -1) {
   124                  weblogHandle = servlet.substring(0, servlet.indexOf("/"));
   125                  pathInfo = servlet.substring(servlet.indexOf("/")+1);
   126              } else {
   127                  weblogHandle = servlet;
   128              }
   129          }
   130          
   131          log.debug("potential weblog handle = "+weblogHandle);
   132          
   133          // check if it's a valid weblog handle
   134          if(restricted.contains(weblogHandle) || !this.isWeblog(weblogHandle)) {
   135              log.debug("SKIPPED "+weblogHandle);
   136              return false;
   137          }
   138          
   139          log.debug("WEBLOG_URL "+request.getServletPath());
   140          
   141          // parse the rest of the url and build forward url
   142          if(pathInfo != null) {
   143              
   144              // parse the next portion of the url
   145              // we expect [locale/]<context>/<extra>/<info>
   146              String[] urlPath = pathInfo.split("/", 3);
   147              
   148              // if we have a locale, deal with it
+  149              if(this.isLocale(urlPath[0])) {
+  150                  weblogLocale = urlPath[0];
   151                  
   152                  // no extra path info specified
   153                  if(urlPath.length == 2) {
   154                      weblogRequestContext = urlPath[1];
   155                      weblogRequestData = null;
   156                      
   157                  // request contains extra path info
   158                  } else if(urlPath.length == 3) {
   159                      weblogRequestContext = urlPath[1];
   160                      weblogRequestData = urlPath[2];
   161                  }
   162              
   163              // otherwise locale is empty
   164              } else {
   165                  weblogLocale = null;
+  166                  weblogRequestContext = urlPath[0];
   167                  
   168                  // last part of request is extra path info
+  169                  if(urlPath.length == 2) {
+  170                      weblogRequestData = urlPath[1];
   171                      
   172                  // if we didn't have a locale then we have split too much
   173                  // so we reassemble the last 2 path elements together
+  174                  } else if(urlPath.length == 3) {
+  175                      weblogRequestData = urlPath[1] + "/" + urlPath[2];
   176                  }
   177              }
   178              
   179          }
   180          
   181          // special handling for trailing slash issue
   182          // we need this because by http standards the urls /foo and /foo/ are
   183          // supposed to be considered different, so we must enforce that
+  184          if(weblogRequestContext == null && !trailingSlash) {
   185              // this means someone referred to a weblog index page with the 
   186              // shortest form of url /<weblog> or /<weblog>/<locale> and we need
   187              // to do a redirect to /<weblog>/ or /<weblog>/<locale>/
   188              String redirectUrl = request.getRequestURI() + "/";
   189              if(request.getQueryString() != null) {
   190                  redirectUrl += "?"+request.getQueryString();
   191              }
   192              
   193              response.sendRedirect(redirectUrl);
   194              return true;
   195              
+  196          } else if(weblogRequestContext != null &&
   197                  "tags".equals(weblogRequestContext)) {
   198              // tags section can have an index page at /<weblog>/tags/ and
   199              // a tags query at /<weblog>/tags/tag1+tag2, buth that's it
   200              if((weblogRequestData == null && !trailingSlash) ||
   201                      (weblogRequestData != null && trailingSlash)) {
   202                  response.sendError(HttpServletResponse.SC_NOT_FOUND);
   203                  return true;
   204              }
+  205          } else if(weblogRequestContext != null && trailingSlash) {
   206              // this means that someone has accessed a weblog url and included
   207              // a trailing slash, like /<weblog>/entry/<anchor>/ which is not
   208              // supported, so we need to offer up a 404 Not Found
   209              response.sendError(HttpServletResponse.SC_NOT_FOUND);
   210              return true;
   211          }
   212          
   213          // calculate forward url
   214          String forwardUrl = calculateForwardUrl(request, weblogHandle, weblogLocale,
   215                  weblogRequestContext, weblogRequestData);
   216          
   217          // if we don't have a forward url then the request was invalid somehow
   218          if(forwardUrl == null) {
   219              return false;
   220          }
   221          
   222          // dispatch to forward url
   223          log.debug("forwarding to "+forwardUrl);
   224          RequestDispatcher dispatch = request.getRequestDispatcher(forwardUrl);
   225          dispatch.forward(request, response);
   226          
   227          // we dealt with this request ourselves, so return "true"
   228          return true;
   229      }
   230  
   231      
   232      /**
   233       * Convenience method for caculating the servlet forward url given a set
   234       * of information to make the decision with.
   235       *
   236       * handle is always assumed valid, all other params may be null.
   237       */
   238      private String calculateForwardUrl(HttpServletRequest request,
   239                                         String handle, String locale,
   240                                         String context, String data) {
   241          
                 /* 
    P/P           *  Method: String calculateForwardUrl(HttpServletRequest, String, String, String, String)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    request != null
                  * 
                  *  Postconditions:
                  *    init'ed(java.lang.StringBuffer:toString(...)._tainted)
                  *    return_value in Addr_Set{null,&java.lang.StringBuffer:toString(...)}
                  * 
                  *  Test Vectors:
                  *    context: Inverse{null}, Addr_Set{null}
                  *    data: Addr_Set{null}, Inverse{null}
                  *    locale: Addr_Set{null}, Inverse{null}
                  *    java.lang.String:equals(...)@247: {0}, {1}
                  *    java.lang.String:equals(...)@249: {0}, {1}
                  *    java.lang.String:equals(...)@303: {1}, {0}
                  *    java.lang.String:equals(...)@303: {0}, {1}
                  *    java.lang.String:equals(...)@322: {0}, {1}
                  *    java.lang.String:equals(...)@337: {0}, {1}
                  *    java.lang.String:equals(...)@348: {0}, {1}
                  *    ...
                  */
   242          log.debug(handle+","+locale+","+context+","+data);
   243          
   244          StringBuffer forwardUrl = new StringBuffer();
   245          
   246          // POST urls, like comment and trackback servlets
   247          if("POST".equals(request.getMethod())) {
   248              // posting to permalink, this means comment or trackback
+  249              if(context.equals("entry")) {
   250                  // trackback requests are required to have an "excerpt" param
   251                  if(request.getParameter("excerpt") != null) {
   252                      
   253                      forwardUrl.append(TRACKBACK_SERVLET);
   254                      forwardUrl.append("/");
   255                      forwardUrl.append(handle);
   256                      if(locale != null) {
   257                          forwardUrl.append("/");
   258                          forwardUrl.append(locale);
   259                      }
   260                      forwardUrl.append("/");
   261                      forwardUrl.append(context);
   262                      if(data != null) {
   263                          forwardUrl.append("/");
   264                          forwardUrl.append(data);
   265                      }
   266                      
   267                  // comment requests are required to have a "content" param
   268                  } else if(request.getParameter("content") != null) {
   269                      
   270                      forwardUrl.append(COMMENT_SERVLET);
   271                      forwardUrl.append("/");
   272                      forwardUrl.append(handle);
   273                      if(locale != null) {
   274                          forwardUrl.append("/");
   275                          forwardUrl.append(locale);
   276                      }
   277                      forwardUrl.append("/");
   278                      forwardUrl.append(context);
   279                      if(data != null) {
   280                          forwardUrl.append("/");
   281                          forwardUrl.append(data);
   282                      }
   283                  }
   284                  
   285              } else {
   286                  // someone posting data where they aren't supposed to
   287                  return null;
   288              }
   289              
   290          } else {
   291              // no context means weblog homepage
   292              if(context == null) {
   293                  
   294                  forwardUrl.append(PAGE_SERVLET);
   295                  forwardUrl.append("/");
   296                  forwardUrl.append(handle);
   297                  if(locale != null) {
   298                      forwardUrl.append("/");
   299                      forwardUrl.append(locale);
   300                  }
   301                  
   302                  // requests handled by PageServlet
   303              } else if(context.equals("page") || context.equals("entry") ||
   304                      context.equals("date") || context.equals("category")
   305                      || context.equals("tags")) {
   306                  
   307                  forwardUrl.append(PAGE_SERVLET);
   308                  forwardUrl.append("/");
   309                  forwardUrl.append(handle);
   310                  if(locale != null) {
   311                      forwardUrl.append("/");
   312                      forwardUrl.append(locale);
   313                  }
   314                  forwardUrl.append("/");
   315                  forwardUrl.append(context);
   316                  if(data != null) {
   317                      forwardUrl.append("/");
   318                      forwardUrl.append(data);
   319                  }
   320                  
   321                  // requests handled by FeedServlet
   322              } else if(context.equals("feed")) {
   323                  
   324                  forwardUrl.append(FEED_SERVLET);
   325                  forwardUrl.append("/");
   326                  forwardUrl.append(handle);
   327                  if(locale != null) {
   328                      forwardUrl.append("/");
   329                      forwardUrl.append(locale);
   330                  }
   331                  if(data != null) {
   332                      forwardUrl.append("/");
   333                      forwardUrl.append(data);
   334                  }
   335                  
   336                  // requests handled by ResourceServlet
   337              } else if(context.equals("resource")) {
   338                  
   339                  forwardUrl.append(RESOURCE_SERVLET);
   340                  forwardUrl.append("/");
   341                  forwardUrl.append(handle);
   342                  if(data != null) {
   343                      forwardUrl.append("/");
   344                      forwardUrl.append(data);
   345                  }
   346                  
   347                  // requests handled by SearchServlet
   348              } else if(context.equals("search")) {
   349                  
   350                  forwardUrl.append(SEARCH_SERVLET);
   351                  forwardUrl.append("/");
   352                  forwardUrl.append(handle);
   353                  
   354                  // requests handled by RSDServlet
   355              } else if(context.equals("rsd")) {
   356                  
   357                  forwardUrl.append(RSD_SERVLET);
   358                  forwardUrl.append("/");
   359                  forwardUrl.append(handle);
   360                  
   361                  // unsupported url
   362              } else {
   363                  return null;
   364              }
   365          }
   366          
   367          log.debug("FORWARD_URL "+forwardUrl.toString());
   368          
   369          return forwardUrl.toString();
   370      }
   371      
   372      
   373      /**
   374       * convenience method which determines if the given string is a valid
   375       * weblog handle.
   376       *
   377       * TODO 3.0: some kind of caching
   378       */
   379      private boolean isWeblog(String potentialHandle) {
   380          
                 /* 
    P/P           *  Method: bool isWeblog(String)
                  * 
                  *  Preconditions:
                  *    log != null
                  * 
                  *  Presumptions:
                  *    org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@386 != null
                  *    org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@386 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@387: Addr_Set{null}, Inverse{null}
                  */
   381          log.debug("checking weblog handle "+potentialHandle);
   382          
   383          boolean isWeblog = false;
   384          
   385          try {
   386              UserManager mgr = WebloggerFactory.getWeblogger().getUserManager();
   387              Weblog weblog = mgr.getWebsiteByHandle(potentialHandle);
   388              
   389              if(weblog != null) {
   390                  isWeblog = true;
   391              }
   392          } catch(Exception ex) {
   393              // doesn't really matter to us why it's not a valid website
   394          }
   395          
   396          return isWeblog;
   397      }
   398      
   399      
   400      /**
   401       * Convenience method which determines if the given string is a valid
   402       * locale string.
   403       */
   404      private boolean isLocale(String potentialLocale) {
   405          
                 /* 
    P/P           *  Method: bool isLocale(String)
                  * 
                  *  Postconditions:
                  *    return_value == 0
                  * 
                  *  Test Vectors:
                  *    potentialLocale: Addr_Set{null}, Inverse{null}
                  *    java.lang.String:length(...)@409: {2}, {0,1, 3..232-1}
                  *    java.lang.String:length(...)@409: {0..4, 6..232-1}, {5}
                  */
   406          boolean isLocale = false;
   407          
   408          // we only support 2 or 5 character locale strings, so check that first
   409          if(potentialLocale != null && 
   410                  (potentialLocale.length() == 2 || potentialLocale.length() == 5)) {
   411              
   412              // now make sure that the format is proper ... e.g. "en_US"
   413              // we are not going to be picky about capitalization
   414              String[] langCountry = potentialLocale.split("_");
+  415              if(langCountry.length == 1 && 
   416                      langCountry[0] != null && langCountry[0].length() == 2) {
   417                  isLocale = true;
   418                  
+  419              } else if(langCountry.length == 2 && 
   420                      langCountry[0] != null && langCountry[0].length() == 2 && 
   421                      langCountry[1] != null && langCountry[1].length() == 2) {
   422                  
   423                  isLocale = true;
   424              }
   425          }
   426          
   427          return isLocale;
   428      }
   429      
   430  }








SofCheck Inspector Build Version : 2.18479
WeblogRequestMapper.java 2009-Jan-02 14:25:26
WeblogRequestMapper.class 2009-Sep-04 03:12:44