class.t3lib_basicfilefunc.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 1999-2008 Kasper Skaarhoj (kasperYYYY@typo3.com)
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 /**
00028  * Contains class with basic file management functions
00029  *
00030  * $Id: class.t3lib_basicfilefunc.php 4432 2008-11-07 03:52:22Z flyguide $
00031  * Revised for TYPO3 3.6 July/2003 by Kasper Skaarhoj
00032  *
00033  * @author  Kasper Skaarhoj <kasperYYYY@typo3.com>
00034  */
00035 /**
00036  * [CLASS/FUNCTION INDEX of SCRIPT]
00037  *
00038  *
00039  *
00040  *   81: class t3lib_basicFileFunctions
00041  *
00042  *              SECTION: Checking functions
00043  *  133:     function init($mounts, $f_ext)
00044  *  152:     function getTotalFileInfo($wholePath)
00045  *  172:     function is_allowed($iconkey,$type)
00046  *  197:     function checkIfFullAccess($theDest)
00047  *  211:     function is_webpath($path)
00048  *  231:     function checkIfAllowed($ext, $theDest, $filename='')
00049  *  241:     function checkFileNameLen($fileName)
00050  *  251:     function is_directory($theDir)
00051  *  268:     function isPathValid($theFile)
00052  *  283:     function getUniqueName($theFile, $theDest, $dontCheckForUnique=0)
00053  *  326:     function checkPathAgainstMounts($thePath)
00054  *  342:     function findFirstWebFolder()
00055  *  362:     function blindPath($thePath)
00056  *  378:     function findTempFolder()
00057  *
00058  *              SECTION: Cleaning functions
00059  *  412:     function cleanDirectoryName($theDir)
00060  *  422:     function rmDoubleSlash($string)
00061  *  432:     function slashPath($path)
00062  *  446:     function cleanFileName($fileName,$charset='')
00063  *  480:     function formatSize($sizeInBytes)
00064  *
00065  * TOTAL FUNCTIONS: 19
00066  * (This index is automatically created/updated by the extension "extdeveval")
00067  *
00068  */
00069 
00070 require_once(PATH_t3lib.'class.t3lib_cs.php');
00071 
00072 
00073 /**
00074  * Contains functions for management, validation etc of files in TYPO3, using the concepts of web- and ftp-space. Please see the comment for the init() function
00075  *
00076  * @author  Kasper Skaarhoj <kasperYYYY@typo3.com>
00077  * @package TYPO3
00078  * @subpackage t3lib
00079  * @see t3lib_basicFileFunctions::init()
00080  */
00081 class t3lib_basicFileFunctions  {
00082     var $getUniqueNamePrefix = '';  // Prefix which will be prepended the file when using the getUniqueName-function
00083     var $maxNumber = 99;            // This number decides the highest allowed appended number used on a filename before we use naming with unique strings
00084     var $uniquePrecision = 6;       // This number decides how many characters out of a unique MD5-hash that is appended to a filename if getUniqueName is asked to find an available filename.
00085     var $maxInputNameLen = 60;      // This is the maximum length of names treated by cleanFileName()
00086     var $tempFN = '_temp_';         // Temp-foldername. A folder in the root of one of the mounts with this name is regarded a TEMP-folder (used for upload from clipboard)
00087 
00088         // internal
00089     var $f_ext = Array();           // See comment in header
00090     var $mounts = Array();          // See comment in header
00091     var $webPath ='';               // Set to DOCUMENT_ROOT.
00092     var $isInit = 0;                // Set to true after init()/start();
00093 
00094 
00095 
00096     /**********************************
00097      *
00098      * Checking functions
00099      *
00100      **********************************/
00101 
00102     /**
00103      * Constructor
00104      * This function should be called to initialise the internal arrays $this->mounts and $this->f_ext
00105      *
00106      *  A typical example of the array $mounts is this:
00107      *      $mounts[xx][path] = (..a mounted path..)
00108      *  the 'xx'-keys is just numerical from zero. There are also a [name] and [type] value that just denotes the mountname and type. Not used for athentication here.
00109      *  $this->mounts is traversed in the function checkPathAgainstMounts($thePath), and it is checked that $thePath is actually below one of the mount-paths
00110      *  The mountpaths are with a trailing '/'. $thePath must be with a trailing '/' also!
00111      *  As you can see, $this->mounts is very critical! This is the array that decides where the user will be allowed to copy files!!
00112      *  Typically the global var $WEBMOUNTS would be passed along as $mounts
00113      *
00114      *  A typical example of the array $f_ext is this:
00115      *      $f_ext['webspace']['allow']='';
00116      *      $f_ext['webspace']['deny']= PHP_EXTENSIONS_DEFAULT;
00117      *      $f_ext['ftpspace']['allow']='*';
00118      *      $f_ext['ftpspace']['deny']='';
00119      *  The control of fileextensions goes in two catagories. Webspace and Ftpspace. Webspace is folders accessible from a webbrowser (below TYPO3_DOCUMENT_ROOT) and ftpspace is everything else.
00120      *  The control is done like this: If an extension matches 'allow' then the check returns true. If not and an extension matches 'deny' then the check return false. If no match at all, returns true.
00121      *  You list extensions comma-separated. If the value is a '*' every extension is allowed
00122      *  The list is case-insensitive when used in this class (see init())
00123      *  Typically TYPO3_CONF_VARS['BE']['fileExtensions'] would be passed along as $f_ext.
00124      *
00125      *  Example:
00126      *  $basicff->init($GLOBALS['FILEMOUNTS'],$TYPO3_CONF_VARS['BE']['fileExtensions']);
00127      *
00128      * @param   array       Contains the paths of the file mounts for the current BE user. Normally $GLOBALS['FILEMOUNTS'] is passed. This variable is set during backend user initialization; $FILEMOUNTS = $BE_USER->returnFilemounts(); (see typo3/init.php)
00129      * @param   array       Array with information about allowed and denied file extensions. Typically passed: $TYPO3_CONF_VARS['BE']['fileExtensions']
00130      * @return  void
00131      * @see typo3/init.php, t3lib_userAuthGroup::returnFilemounts()
00132      */
00133     function init($mounts, $f_ext)  {
00134         $this->f_ext['webspace']['allow'] = t3lib_div::uniqueList(strtolower($f_ext['webspace']['allow']));
00135         $this->f_ext['webspace']['deny'] = t3lib_div::uniqueList(strtolower($f_ext['webspace']['deny']));
00136         $this->f_ext['ftpspace']['allow'] = t3lib_div::uniqueList(strtolower($f_ext['ftpspace']['allow']));
00137         $this->f_ext['ftpspace']['deny'] = t3lib_div::uniqueList(strtolower($f_ext['ftpspace']['deny']));
00138 
00139         $this->mounts = $mounts;
00140         $this->webPath = t3lib_div::getIndpEnv('TYPO3_DOCUMENT_ROOT');
00141         $this->isInit = 1;
00142 
00143         $this->maxInputNameLen = $GLOBALS['TYPO3_CONF_VARS']['SYS']['maxFileNameLength'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['maxFileNameLength'] : $this->maxInputNameLen;
00144     }
00145 
00146     /**
00147      * Returns an array with a whole lot of fileinformation.
00148      *
00149      * @param   string      Filepath to existing file. Should probably be absolute. Filefunctions are performed on this value.
00150      * @return  array       Information about the file in the filepath
00151      */
00152     function getTotalFileInfo($wholePath)   {
00153         $theuser = getmyuid();
00154         $info = t3lib_div::split_fileref($wholePath);
00155         $info['tstamp'] = @filemtime($wholePath);
00156         $info['size'] = @filesize($wholePath);
00157         $info['type'] = @filetype($wholePath);
00158         $info['owner'] = @fileowner($wholePath);
00159         $info['perms'] = @fileperms($wholePath);
00160         $info['writable'] = !@is_writable($wholePath);
00161         $info['readable'] = !@is_readable($wholePath);
00162         return $info;
00163     }
00164 
00165     /**
00166      * Checks if a $iconkey (fileextension) is allowed according to $this->f_ext.
00167      *
00168      * @param   string      The extension to check, eg. "php" or "html" etc.
00169      * @param   string      Either "webspage" or "ftpspace" - points to a key in $this->f_ext
00170      * @return  boolean     True if file extension is allowed.
00171      */
00172     function is_allowed($iconkey,$type) {
00173         if (isset($this->f_ext[$type])) {
00174             $ik = strtolower($iconkey);
00175             if ($ik)    {
00176                     // If the extension is found amongst the allowed types, we return true immediately
00177                 if ($this->f_ext[$type]['allow']=='*' || t3lib_div::inList($this->f_ext[$type]['allow'],$ik))   return true;
00178                     // If the extension is found amongst the denied types, we return false immediately
00179                 if ($this->f_ext[$type]['deny']=='*' || t3lib_div::inList($this->f_ext[$type]['deny'],$ik)) return false;
00180                     // If no match we return true
00181                 return true;
00182             } else {    // If no extension:
00183                 if ($this->f_ext[$type]['allow']=='*')  return true;
00184                 if ($this->f_ext[$type]['deny']=='*')   return false;
00185                 return true;
00186             }
00187         }
00188         return false;
00189     }
00190 
00191     /**
00192      * Returns true if you can operate of ANY file ('*') in the space $theDest is in ('webspace' / 'ftpspace')
00193      *
00194      * @param   string      Absolute path
00195      * @return  boolean
00196      */
00197     function checkIfFullAccess($theDest)    {
00198         $type = $this->is_webpath($theDest)?'webspace':'ftpspace';
00199         if (isset($this->f_ext[$type])) {
00200             if ((string)$this->f_ext[$type]['deny']=='' || $this->f_ext[$type]['allow']=='*')   return true;
00201         }
00202     }
00203 
00204     /**
00205      * Checks if $this->webPath (should be TYPO3_DOCUMENT_ROOT) is in the first part of $path
00206      * Returns true also if $this->init is not set or if $path is empty...
00207      *
00208      * @param   string      Absolute path to check
00209      * @return  boolean
00210      */
00211     function is_webpath($path)  {
00212         if ($this->isInit)  {
00213             $testPath = $this->slashPath($path);
00214             $testPathWeb = $this->slashPath($this->webPath);
00215             if ($testPathWeb && $testPath)  {
00216                 return t3lib_div::isFirstPartOfStr($testPath,$testPathWeb);
00217             }
00218         }
00219         return true;    // Its more safe to return true (as the webpath is more restricted) if something went wrong...
00220     }
00221 
00222     /**
00223      * If the filename is given, check it against the TYPO3_CONF_VARS[BE][fileDenyPattern] +
00224      * Checks if the $ext fileextension is allowed in the path $theDest (this is based on whether $theDest is below the $this->webPath)
00225      *
00226      * @param   string      File extension, eg. "php" or "html"
00227      * @param   string      Absolute path for which to test
00228      * @param   string      Filename to check against TYPO3_CONF_VARS[BE][fileDenyPattern]
00229      * @return  boolean     True if extension/filename is allowed
00230      */
00231     function checkIfAllowed($ext, $theDest, $filename='')   {
00232         return t3lib_div::verifyFilenameAgainstDenyPattern($filename) && $this->is_allowed($ext,($this->is_webpath($theDest)?'webspace':'ftpspace'));
00233     }
00234 
00235     /**
00236      * Returns true if the input filename string is shorter than $this->maxInputNameLen.
00237      *
00238      * @param   string      Filename, eg "somefile.html"
00239      * @return  boolean
00240      */
00241     function checkFileNameLen($fileName)    {
00242         return strlen($fileName) <= $this->maxInputNameLen;
00243     }
00244 
00245     /**
00246      * Cleans $theDir for slashes in the end of the string and returns the new path, if it exists on the server.
00247      *
00248      * @param   string      Directory path to check
00249      * @return  string      Returns the cleaned up directory name if OK, otherwise false.
00250      */
00251     function is_directory($theDir)  {
00252         if ($this->isPathValid($theDir))    {
00253             $theDir=$this->cleanDirectoryName($theDir);
00254             if (@is_dir($theDir))   {
00255                 return $theDir;
00256             }
00257         }
00258         return false;
00259     }
00260 
00261     /**
00262      * Wrapper for t3lib_div::validPathStr()
00263      *
00264      * @param   string      Filepath to evaluate
00265      * @return  boolean     True, if no '//', '..' or '\' is in the $theFile
00266      * @see t3lib_div::validPathStr()
00267      */
00268     function isPathValid($theFile)  {
00269         return t3lib_div::validPathStr($theFile);
00270     }
00271 
00272     /**
00273      * Returns the destination path/filename of a unique filename/foldername in that path.
00274      * If $theFile exists in $theDest (directory) the file have numbers appended up to $this->maxNumber. Hereafter a unique string will be appended.
00275      * This function is used by fx. TCEmain when files are attached to records and needs to be uniquely named in the uploads/* folders
00276      *
00277      * @param   string      The input filename to check
00278      * @param   string      The directory for which to return a unique filename for $theFile. $theDest MUST be a valid directory. Should be absolute.
00279      * @param   boolean     If set the filename is returned with the path prepended without checking whether it already existed!
00280      * @return  string      The destination absolute filepath (not just the name!) of a unique filename/foldername in that path.
00281      * @see t3lib_TCEmain::checkValue()
00282      */
00283     function getUniqueName($theFile, $theDest, $dontCheckForUnique=0)   {
00284         $theDest = $this->is_directory($theDest);   // $theDest is cleaned up
00285         $origFileInfo = t3lib_div::split_fileref($theFile);     // Fetches info about path, name, extention of $theFile
00286         if ($theDest)   {
00287             if ($this->getUniqueNamePrefix) {       // Adds prefix
00288                 $origFileInfo['file']     = $this->getUniqueNamePrefix.$origFileInfo['file'];
00289                 $origFileInfo['filebody'] = $this->getUniqueNamePrefix.$origFileInfo['filebody'];
00290             }
00291 
00292                 // Check if the file exists and if not - return the filename...
00293             $fileInfo = $origFileInfo;
00294             $theDestFile = $theDest.'/'.$fileInfo['file'];      // The destinations file
00295             if (!file_exists($theDestFile) || $dontCheckForUnique)      {   // If the file does NOT exist we return this filename
00296                 return $theDestFile;
00297             }
00298 
00299                 // Well the filename in its pure form existed. Now we try to append numbers / unique-strings and see if we can find an available filename...
00300             $theTempFileBody = preg_replace('/_[0-9][0-9]$/','',$origFileInfo['filebody']);     // This removes _xx if appended to the file
00301             $theOrigExt = $origFileInfo['realFileext'] ? '.'.$origFileInfo['realFileext'] : '';
00302 
00303             for ($a=1; $a<=($this->maxNumber+1); $a++)  {
00304                 if ($a<=$this->maxNumber)   {   // First we try to append numbers
00305                     $insert = '_'.sprintf('%02d', $a);
00306                 } else {        // .. then we try unique-strings...
00307                     $insert = '_'.substr(md5(uniqId('')),0,$this->uniquePrecision);
00308                 }
00309                 $theTestFile = $theTempFileBody.$insert.$theOrigExt;
00310                 $theDestFile = $theDest.'/'.$theTestFile;       // The destinations file
00311                 if (!file_exists($theDestFile))     {   // If the file does NOT exist we return this filename
00312                     return $theDestFile;
00313                 }
00314             }
00315         }
00316     }
00317 
00318     /**
00319      * Checks if $thePath is a path under one of the paths in $this->mounts
00320      * See comment in the header of this class.
00321      *
00322      * @param   string      $thePath MUST HAVE a trailing '/' in order to match correctly with the mounts
00323      * @return  string      The key to the first mount found, otherwise nothing is returned.
00324      * @see init()
00325      */
00326     function checkPathAgainstMounts($thePath)   {
00327         if ($thePath && $this->isPathValid($thePath) && is_array($this->mounts))    {
00328             reset ($this->mounts);
00329             while(list($k,$val)=each($this->mounts))    {
00330                 if (t3lib_div::isFirstPartOfStr($thePath,$val['path'])) {
00331                     return $k;
00332                 }
00333             }
00334         }
00335     }
00336 
00337     /**
00338      * Find first web folder (relative to PATH_site.'fileadmin') in filemounts array
00339      *
00340      * @return  string      The key to the first mount inside PATH_site."fileadmin" found, otherwise nothing is returned.
00341      */
00342     function findFirstWebFolder()   {
00343         global $TYPO3_CONF_VARS;
00344 
00345         if (is_array($this->mounts))    {
00346             reset ($this->mounts);
00347             while(list($k,$val)=each($this->mounts))    {
00348                 if (t3lib_div::isFirstPartOfStr($val['path'], PATH_site.$TYPO3_CONF_VARS['BE']['fileadminDir']))    {
00349                     return $k;
00350                 }
00351             }
00352         }
00353     }
00354 
00355     /**
00356      * Removes filemount part of a path, thus blinding the position.
00357      * Takes a path, $thePath, and removes the part of the path which equals the filemount.
00358      *
00359      * @param   string      $thePath is a path which MUST be found within one of the internally set filemounts, $this->mounts
00360      * @return  string      The processed input path
00361      */
00362     function blindPath($thePath)    {
00363         $k=$this->checkPathAgainstMounts($thePath);
00364         if ($k) {
00365             $name='';
00366             $name.='['.$this->mounts[$k]['name'].']: ';
00367             $name.=substr($thePath,strlen($this->mounts[$k]['path']));
00368             return $name;
00369         }
00370     }
00371 
00372     /**
00373      * Find temporary folder
00374      * Finds the first $this->tempFN ('_temp_' usually) -folder in the internal array of filemounts, $this->mounts
00375      *
00376      * @return  string      Returns the path if found, otherwise nothing if error.
00377      */
00378     function findTempFolder()   {
00379         if ($this->tempFN && is_array($this->mounts))   {
00380             reset ($this->mounts);
00381             while(list($k,$val)=each($this->mounts))    {
00382                 $tDir = $val['path'].$this->tempFN;
00383                 if (@is_dir($tDir)) {
00384                     return $tDir;
00385                 }
00386             }
00387         }
00388     }
00389 
00390 
00391 
00392 
00393 
00394 
00395 
00396 
00397 
00398 
00399 
00400     /*********************
00401      *
00402      * Cleaning functions
00403      *
00404      *********************/
00405 
00406     /**
00407      * Removes all dots, slashes and spaces after a path...
00408      *
00409      * @param   string      Input string
00410      * @return  string      Output string
00411      */
00412     function cleanDirectoryName($theDir)    {
00413         return preg_replace('/[\/\. ]*$/','',$this->rmDoubleSlash($theDir));
00414     }
00415 
00416     /**
00417      * Converts any double slashes (//) to a single slash (/)
00418      *
00419      * @param   string      Input value
00420      * @return  string      Returns the converted string
00421      */
00422     function rmDoubleSlash($string) {
00423         return str_replace('//','/',$string);
00424     }
00425 
00426     /**
00427      * Returns a string which has a slash '/' appended if it doesn't already have that slash
00428      *
00429      * @param   string      Input string
00430      * @return  string      Output string with a slash in the end (if not already there)
00431      */
00432     function slashPath($path)   {
00433         if (substr($path,-1)!='/')  {
00434             return $path.'/';
00435         }
00436         return $path;
00437     }
00438 
00439     /**
00440      * Returns a string where any character not matching [.a-zA-Z0-9_-] is substituted by '_'
00441      *
00442      * @param   string      Input string, typically the body of a filename
00443      * @param   string      Charset of the a filename (defaults to current charset; depending on context)
00444      * @return  string      Output string with any characters not matching [.a-zA-Z0-9_-] is substituted by '_'
00445      */
00446     function cleanFileName($fileName,$charset='')   {
00447             // handle UTF-8 characters
00448         if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] == 'utf-8' && $GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem'])   {
00449                 // allow ".", "-", 0-9, a-z, A-Z and everything beyond U+C0 (latin capital letter a with grave)
00450             return preg_replace('/[\x00-\x2C\/\x3A-\x3F\x5B-\x60\x7B-\xBF]/u','_',trim($fileName));
00451         }
00452 
00453         if (!is_object($this->csConvObj))   {
00454             if (TYPO3_MODE=='FE')   {
00455                 $this->csConvObj = &$GLOBALS['TSFE']->csConvObj;
00456             } elseif (is_object($GLOBALS['LANG']))  {   // BE assumed:
00457                 $this->csConvObj = &$GLOBALS['LANG']->csConvObj;
00458             } else {    // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
00459                 $this->csConvObj = &t3lib_div::makeInstance('t3lib_cs');
00460             }
00461         }
00462 
00463         if (!$charset)  {
00464             if (TYPO3_MODE=='FE')   {
00465                 $charset = $GLOBALS['TSFE']->renderCharset;
00466             } elseif (is_object($GLOBALS['LANG']))  {   // BE assumed:
00467                 $charset = $GLOBALS['LANG']->charSet;
00468             } else {    // best guess
00469                 $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
00470             }
00471         }
00472 
00473         if ($charset)   {
00474             $fileName = $this->csConvObj->specCharsToASCII($charset,$fileName);
00475         }
00476 
00477         $fileName = preg_replace('/[^.[:alnum:]_-]/','_',trim($fileName));
00478         return preg_replace('/\.*$/','',$fileName);
00479     }
00480 
00481     /**
00482      * Formats an integer, $sizeInBytes, to Mb or Kb or just bytes
00483      *
00484      * @param   integer     Bytes to be formated
00485      * @return  string      Formatted with M,K or &nbsp;&nbsp; appended.
00486      */
00487     function formatSize($sizeInBytes)   {
00488         if ($sizeInBytes>900)   {
00489             if ($sizeInBytes>900000)    {   // MB
00490                 $val = $sizeInBytes/(1024*1024);
00491                 return number_format($val, (($val<20)?1:0), '.', '').' M';
00492             } else {    // KB
00493                 $val = $sizeInBytes/(1024);
00494                 return number_format($val, (($val<20)?1:0), '.', '').' K';
00495             }
00496         } else {    // Bytes
00497             return $sizeInBytes.'&nbsp;&nbsp;';
00498         }
00499     }
00500 }
00501 
00502 
00503 
00504 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_basicfilefunc.php']) {
00505     include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_basicfilefunc.php']);
00506 }
00507 
00508 ?>

Generated on Sat Jan 3 04:23:26 2009 for TYPO3 API by  doxygen 1.4.7