Home » freemarker-2.3.13 » freemarker.cache » [javadoc | source]

    1   /*
    2    * Copyright (c) 2003 The Visigoth Software Society. All rights
    3    * reserved.
    4    *
    5    * Redistribution and use in source and binary forms, with or without
    6    * modification, are permitted provided that the following conditions
    7    * are met:
    8    *
    9    * 1. Redistributions of source code must retain the above copyright
   10    *    notice, this list of conditions and the following disclaimer.
   11    *
   12    * 2. Redistributions in binary form must reproduce the above copyright
   13    *    notice, this list of conditions and the following disclaimer in
   14    *    the documentation and/or other materials provided with the
   15    *    distribution.
   16    *
   17    * 3. The end-user documentation included with the redistribution, if
   18    *    any, must include the following acknowledgement:
   19    *       "This product includes software developed by the
   20    *        Visigoth Software Society (http://www.visigoths.org/)."
   21    *    Alternately, this acknowledgement may appear in the software itself,
   22    *    if and wherever such third-party acknowledgements normally appear.
   23    *
   24    * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
   25    *    project contributors may be used to endorse or promote products derived
   26    *    from this software without prior written permission. For written
   27    *    permission, please contact visigoths@visigoths.org.
   28    *
   29    * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
   30    *    nor may "FreeMarker" or "Visigoth" appear in their names
   31    *    without prior written permission of the Visigoth Software Society.
   32    *
   33    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   34    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   35    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   36    * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
   37    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   38    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   39    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   40    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   41    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   42    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   43    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   44    * SUCH DAMAGE.
   45    * ====================================================================
   46    *
   47    * This software consists of voluntary contributions made by many
   48    * individuals on behalf of the Visigoth Software Society. For more
   49    * information on the Visigoth Software Society, please see
   50    * http://www.visigoths.org/
   51    */
   52   
   53   package freemarker.cache;
   54   
   55   import java.io.File;
   56   import java.io.FileInputStream;
   57   import java.io.FileNotFoundException;
   58   import java.io.IOException;
   59   import java.io.InputStreamReader;
   60   import java.io.Reader;
   61   import java.security.AccessController;
   62   import java.security.PrivilegedAction;
   63   import java.security.PrivilegedActionException;
   64   import java.security.PrivilegedExceptionAction;
   65   
   66   import freemarker.template.utility.SecurityUtilities;
   67   
   68   /**
   69    * A {@link TemplateLoader} that uses files in a specified directory as the
   70    * source of templates. If contains security checks that will prevent it
   71    * serving templates outside the template directory (like <code>&lt;include /etc/passwd></code>.
   72    * It compares canonical paths for this, so templates that are symbolically
   73    * linked into the template directory from outside of it won't work either.
   74    * @author Attila Szegedi, szegedia at freemail dot hu
   75    * @version $Id: FileTemplateLoader.java,v 1.26 2004/03/29 08:06:22 szegedia Exp $
   76    */
   77   public class FileTemplateLoader implements TemplateLoader
   78   {
   79       private static final boolean SEP_IS_SLASH = File.separatorChar == '/';
   80       public final File baseDir;
   81       private final String canonicalPath;
   82   
   83       /**
   84        * Creates a new file template cache that will use the current directory
   85        * (the value of the system property <code>user.dir</code> as the base
   86        * directory for loading templates. It will not allow access to template
   87        * files that are accessible through symlinks that point outside the
   88        * base directory.
   89        */
   90       public FileTemplateLoader()
   91       throws
   92       	IOException
   93       {
   94           this(new File(SecurityUtilities.getSystemProperty("user.dir")));
   95       }
   96   
   97       /**
   98        * Creates a new file template loader that will use the specified directory
   99        * as the base directory for loading templates. It will not allow access to
  100        * template files that are accessible through symlinks that point outside 
  101        * the base directory.
  102        * @param baseDir the base directory for loading templates
  103        */
  104       public FileTemplateLoader(final File baseDir)
  105       throws
  106           IOException
  107       {
  108           this(baseDir, false);
  109       }
  110   
  111       /**
  112        * Creates a new file template loader that will use the specified directory
  113        * as the base directory for loading templates.
  114        * @param baseDir the base directory for loading templates
  115        * @param allowLinking if true, it will allow 
  116        */
  117       public FileTemplateLoader(final File baseDir, final boolean allowLinking)
  118       throws
  119       	IOException
  120       {
  121           try {
  122               Object[] retval = (Object[]) AccessController.doPrivileged(new PrivilegedExceptionAction() {
  123                   public Object run() throws IOException {
  124                       if (!baseDir.exists()) {
  125                           throw new FileNotFoundException(baseDir + " does not exist.");
  126                       }
  127                       if (!baseDir.isDirectory()) {
  128                           throw new IOException(baseDir + " is not a directory.");
  129                       }
  130                       Object[] retval = new Object[2];
  131                       if(allowLinking) {
  132                           retval[0] = baseDir;
  133                           retval[1] = null;
  134                       }
  135                       else {
  136                           retval[0] = baseDir.getCanonicalFile();
  137                           retval[1] = ((File) retval[0]).getPath() + File.separatorChar;
  138                       }
  139                       return retval;
  140                   }
  141               });
  142               this.baseDir = (File) retval[0];
  143               this.canonicalPath = (String) retval[1]; 
  144           }
  145           catch(PrivilegedActionException e)
  146           {
  147               throw (IOException)e.getException();
  148           }
  149       }
  150       
  151       public Object findTemplateSource(final String name)
  152       throws
  153       	IOException
  154       {
  155           try {
  156               return AccessController.doPrivileged(new PrivilegedExceptionAction() {
  157                   public Object run() throws IOException {
  158                       File source = new File(baseDir, SEP_IS_SLASH ? name : 
  159                           name.replace('/', File.separatorChar));
  160                       if(!source.isFile()) {
  161                           return null;
  162                       }
  163                       // Security check for inadvertently returning something 
  164                       // outside the template directory when linking is not 
  165                       // allowed.
  166                       if(canonicalPath != null) {
  167                           String normalized = source.getCanonicalPath();
  168                           if (!normalized.startsWith(canonicalPath)) {
  169                               throw new SecurityException(source.getAbsolutePath() 
  170                                       + " resolves to " + normalized + " which " + 
  171                                       " doesn't start with " + canonicalPath);
  172                           }
  173                       }
  174                       return source;
  175                   }
  176               });
  177           }
  178           catch(PrivilegedActionException e)
  179           {
  180               throw (IOException)e.getException();
  181           }
  182       }
  183       
  184       public long getLastModified(final Object templateSource)
  185       {
  186           return ((Long)(AccessController.doPrivileged(new PrivilegedAction()
  187           {
  188               public Object run()
  189               {
  190                   return new Long(((File)templateSource).lastModified());
  191               }
  192           }))).longValue();
  193           
  194           
  195       }
  196       
  197       public Reader getReader(final Object templateSource, final String encoding)
  198       throws
  199           IOException
  200       {
  201           try
  202           {
  203               return (Reader)AccessController.doPrivileged(new PrivilegedExceptionAction()
  204               {
  205                   public Object run()
  206                   throws 
  207                       IOException
  208                   {
  209                       if (!(templateSource instanceof File)) {
  210                           throw new IllegalArgumentException(
  211                                   "templateSource is a: " + 
  212                                   templateSource.getClass().getName());
  213                       }
  214                       return new InputStreamReader(new FileInputStream(
  215                               (File)templateSource), encoding);
  216                   }
  217               });
  218           }
  219           catch(PrivilegedActionException e)
  220           {
  221               throw (IOException)e.getException();
  222           }
  223       }
  224       
  225       public void closeTemplateSource(Object templateSource)
  226       {
  227           // Do nothing.
  228       }
  229   }

Home » freemarker-2.3.13 » freemarker.cache » [javadoc | source]