b2evolution PHP Cross Reference Blogging Systems

Source: /inc/files/model/_file.funcs.php - 2197 lines - 64478 bytes - Summary - Text - Print

Description: This file implements various File handling functions. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements various File handling functions.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * Daniel HAHLER grants Francois PLANQUE the right to license
  22   * Daniel HAHLER's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   * }}
  25   *
  26   * @package evocore
  27   *
  28   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  29   * @author blueyed: Daniel HAHLER.
  30   * @author fplanque: Francois PLANQUE.
  31   *
  32   * @version $Id: _file.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  33   */
  34  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  35  
  36  
  37  if( ! function_exists('fnmatch') )
  38  {
  39      /**
  40       * A replacement for fnmatch() which needs PHP 4.3 and a POSIX compliant system (Windows is not).
  41       *
  42       * @author jk at ricochetsolutions dot com {@link http://php.net/manual/function.fnmatch.php#71725}
  43       */
  44  	function fnmatch($pattern, $string)
  45      {
  46         return preg_match( '#^'.strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.')).'$#i', $string);
  47      }
  48  }
  49  
  50  
  51  /**
  52   * Converts bytes to readable bytes/kb/mb/gb, like "12.45mb"
  53   *
  54   * @param integer bytes
  55   * @param boolean use HTML <abbr> tags
  56   * @param boolean Display full text of size type when $htmlabbr == false
  57   * @return string bytes made readable
  58   */
  59  function bytesreadable( $bytes, $htmlabbr = true, $display_size_type = true )
  60  {
  61      static $types = NULL;
  62  
  63      if( empty($bytes) )
  64      {
  65          return T_('Empty');
  66      }
  67  
  68      if( !isset($types) )
  69      { // generate once:
  70          $types = array(
  71              0 => array( 'abbr' => /* TRANS: Abbr. for "Bytes" */ T_('B.'), 'text' => T_('Bytes') ),
  72              1 => array( 'abbr' => /* TRANS: Abbr. for "Kilobytes" */ T_('KB'), 'text' => T_('Kilobytes') ),
  73              2 => array( 'abbr' => /* TRANS: Abbr. for Megabytes */ T_('MB'), 'text' => T_('Megabytes') ),
  74              3 => array( 'abbr' => /* TRANS: Abbr. for Gigabytes */ T_('GB'), 'text' => T_('Gigabytes') ),
  75              4 => array( 'abbr' => /* TRANS: Abbr. for Terabytes */ T_('TB'), 'text' => T_('Terabytes') )
  76          );
  77      }
  78  
  79      for( $i = 0; $bytes > 1024; $i++ )
  80      {
  81          $bytes /= 1024;
  82      }
  83  
  84      // Format to maximum of 1 digit after .
  85      $precision = max( 0, ( 1 -floor(log($bytes)/log(10))) );
  86      $r = sprintf( '%.'.$precision.'f', $bytes );
  87  
  88      $r .= $htmlabbr ? ( '&nbsp;<abbr title="'.$types[$i]['text'].'">' ) : ' ';
  89      $r .= $types[$i]['abbr'];
  90      $r .= $htmlabbr ? '</abbr>' : ( $display_size_type ? ' ('.$types[$i]['text'].')' : '' );
  91  
  92      // $r .= ' '.$precision;
  93  
  94      return $r;
  95  }
  96  
  97  
  98  /**
  99   * Get an array of all directories (and optionally files) of a given
 100   * directory, either flat (one-dimensional array) or multi-dimensional (then
 101   * dirs are the keys and hold subdirs/files).
 102   *
 103   * Note: there is no ending slash on dir names returned.
 104   *
 105   * @param string the path to start
 106   * @param array of params
 107   * @return false|array false if the first directory could not be accessed,
 108   *                     array of entries otherwise
 109   */
 110  function get_filenames( $path, $params = array() )
 111  {
 112      global $Settings;
 113  
 114      $params = array_merge( array(
 115              'inc_files'        => true,    // include files (not only directories)
 116              'inc_dirs'        => true,    // include directories (not the directory itself!)
 117              'flat'            => true,    // return a one-dimension-array
 118              'recurse'        => true,    // recurse into subdirectories
 119              'basename'        => false,    // get the basename only
 120              'trailing_slash'=> false,    // add trailing slash
 121              'inc_hidden'    => true,    // inlcude hidden files, directories and content
 122              'inc_evocache'    => false,    // exclude evocache directories and content
 123          ), $params );
 124  
 125      $r = array();
 126  
 127      $path = trailing_slash( $path );
 128  
 129      if( $dir = @opendir($path) )
 130      {
 131          while( ( $file = readdir($dir) ) !== false )
 132          {
 133              if( $file == '.' || $file == '..' )
 134                  continue;
 135  
 136              // asimo> Also check if $Settings is not empty because this function is called from the install srcipt too, where $Settings is not initialized yet
 137              if( ! $params['inc_evocache'] && ! empty( $Settings ) && $file == $Settings->get('evocache_foldername') )
 138                  continue;
 139  
 140              // Check for hidden status...
 141              if( ( ! $params['inc_hidden'] ) && ( substr( $file, 0, 1 ) == '.' ) )
 142              { // Do not load & show hidden files and folders (prefixed with .)
 143                  continue;
 144              }
 145  
 146              if( is_dir($path.$file) )
 147              {
 148                  if( $params['flat'] )
 149                  {
 150                      if( $params['inc_dirs'] )
 151                      {
 152                          $directory_name = $params['basename'] ? $file : $path.$file;
 153                          if( $params['trailing_slash'] )
 154                          {
 155                              $directory_name = trailing_slash( $directory_name );
 156                          }
 157  
 158                          $r[] = $directory_name;
 159                      }
 160                      if( $params['recurse'] )
 161                      {
 162                          $rSub = get_filenames( $path.$file, $params );
 163                          if( $rSub )
 164                          {
 165                              $r = array_merge( $r, $rSub );
 166                          }
 167                      }
 168                  }
 169                  else
 170                  {
 171                      $r[$file] = get_filenames( $path.$file, $params );
 172                  }
 173              }
 174              elseif( $params['inc_files'] )
 175              {
 176                  $r[] = $params['basename'] ? $file : $path.$file;
 177              }
 178          }
 179          closedir($dir);
 180      }
 181      else
 182      {
 183          return false;
 184      }
 185  
 186      return $r;
 187  }
 188  
 189  
 190  /**
 191   * Get a list of available admin skins.
 192   *
 193   * This checks if there's a _adminUI.class.php in there.
 194   *
 195   * @return array  List of directory names that hold admin skins or false, if the admin skins driectory does not exist.
 196   */
 197  function get_admin_skins()
 198  {
 199      global $adminskins_path, $admin_subdir, $adminskins_subdir;
 200  
 201      $filename_params = array(
 202              'inc_files'        => false,
 203              'recurse'        => false,
 204              'basename'      => true,
 205          );
 206      $dirs_in_adminskins_dir = get_filenames( $adminskins_path, $filename_params );
 207  
 208      if( $dirs_in_adminskins_dir === false )
 209      {
 210          return false;
 211      }
 212  
 213      $r = array();
 214      if( $dirs_in_adminskins_dir )
 215      {
 216          foreach( $dirs_in_adminskins_dir as $l_dir )
 217          {
 218              if( !file_exists($adminskins_path.$l_dir.'/_adminUI.class.php') )
 219              {
 220                  continue;
 221              }
 222              $r[] = $l_dir;
 223          }
 224      }
 225      return $r;
 226  }
 227  
 228  
 229  /**
 230   * Get size of a directory, including anything (especially subdirs) in there.
 231   *
 232   * @param string the dir's full path
 233   */
 234  function get_dirsize_recursive( $path )
 235  {
 236      $files = get_filenames( $path );
 237      $total = 0;
 238  
 239      if( !empty( $files ) )
 240      {
 241          foreach( $files as $lFile )
 242          {
 243              $total += filesize($lFile);
 244          }
 245      }
 246  
 247      return $total;
 248  }
 249  
 250  
 251  /**
 252   * Deletes a dir recursively, wiping out all subdirectories!!
 253   *
 254   * @param string The dir
 255   * @return boolean False on failure
 256   */
 257  function rmdir_r( $path )
 258  {
 259      $path = trailing_slash( $path );
 260  
 261      $r = true;
 262  
 263      if( ! cleardir_r( $path ) )
 264      {
 265          $r = false;
 266      }
 267  
 268      if( ! @rmdir( $path ) )
 269      {
 270          $r = false;
 271      }
 272  
 273      return $r;
 274  }
 275  
 276  
 277  /**
 278   * Clear contents of dorectory, but do not delete directory itself
 279   * @return boolean False on failure (may be only partial), true on success.
 280   */
 281  function cleardir_r( $path )
 282  {
 283      $path = trailing_slash( $path );
 284      // echo "<br>rmdir_r($path)";
 285  
 286      $r = true; // assume success
 287  
 288      if( $dir = @opendir($path) )
 289      {
 290          while( ( $file = readdir($dir) ) !== false )
 291          {
 292              if( $file == '.' || $file == '..' )
 293                  continue;
 294  
 295              $adfp_filepath = $path.$file;
 296  
 297              // echo "<br> - $os_filepath ";
 298  
 299              if( is_dir( $adfp_filepath ) && ! is_link($adfp_filepath) )
 300              { // Note: we do NOT follow symlinks
 301                  // echo 'D';
 302                  if( ! rmdir_r( $adfp_filepath ) )
 303                  {
 304                      $r = false;
 305                  }
 306              }
 307              else
 308              { // File or symbolic link
 309                  //echo 'F/S';
 310                  if( ! @unlink( $adfp_filepath ) )
 311                  {
 312                      $r = false;
 313                  }
 314              }
 315          }
 316          closedir($dir);
 317      }
 318      else
 319      {
 320          $r = false;
 321      }
 322  
 323      return $r;
 324  }
 325  
 326  /**
 327   * Get the size of an image file
 328   *
 329   * @param string absolute file path
 330   * @param string what property/format to get: 'width', 'height', 'widthxheight',
 331   *               'type', 'string' (as for img tags), 'widthheight_assoc' (array
 332   *               with keys "width" and "height", else 'widthheight' (numeric array)
 333   * @return mixed false if no image, otherwise what was requested through $param
 334   */
 335  function imgsize( $path, $param = 'widthheight' )
 336  {
 337      /**
 338       * Cache image sizes
 339       */
 340      global $cache_imgsize;
 341  
 342      if( isset($cache_imgsize[$path]) )
 343      {
 344          $size = $cache_imgsize[$path];
 345      }
 346      elseif( !($size = @getimagesize( $path )) )
 347      {
 348          return false;
 349      }
 350      else
 351      {
 352          $cache_imgsize[$path] = $size;
 353      }
 354  
 355      if( $param == 'width' )
 356      {
 357          return $size[0];
 358      }
 359      elseif( $param == 'height' )
 360      {
 361          return $size[1];
 362      }
 363      elseif( $param == 'widthxheight' )
 364      {
 365          return $size[0].'x'.$size[1];
 366      }
 367      elseif( $param == 'type' )
 368      {
 369          switch( $size[1] )
 370          {
 371              case 1: return 'gif';
 372              case 2: return 'jpg';
 373              case 3: return 'png';
 374              case 4: return 'swf';
 375              default: return 'unknown';
 376          }
 377      }
 378      elseif( $param == 'string' )
 379      {
 380          return $size[3];
 381      }
 382      elseif( $param == 'widthheight_assoc' )
 383      {
 384          return array( 'width' => $size[0], 'height' => $size[1] );
 385      }
 386      else
 387      { // default: 'widthheight'
 388          return array( $size[0], $size[1] );
 389      }
 390  }
 391  
 392  
 393  /**
 394   * Remove leading slash, if any.
 395   *
 396   * @param string
 397   * @return string
 398   */
 399  function no_leading_slash( $path )
 400  {
 401      if( isset($path[0]) && $path[0] == '/' )
 402      {
 403          return substr( $path, 1 );
 404      }
 405      else
 406      {
 407          return $path;
 408      }
 409  }
 410  
 411  
 412  /**
 413   * Returns canonicalized pathname of a directory + ending slash
 414   *
 415   * @param string absolute path to be reduced ending with slash
 416   * @return string absolute reduced path, slash terminated or NULL if the path could not get canonicalized.
 417   */
 418  function get_canonical_path( $ads_path )
 419  {
 420      // Remove windows backslashes:
 421      $ads_path = str_replace( '\\', '/', $ads_path );
 422  
 423      $is_absolute = is_absolute_pathname($ads_path);
 424  
 425      // Make sure there's a trailing slash
 426      $ads_path = trailing_slash($ads_path);
 427  
 428      while( strpos($ads_path, '//') !== false )
 429      {
 430          $ads_path = str_replace( '//', '/', $ads_path );
 431      }
 432      while( strpos($ads_path, '/./') !== false )
 433      {
 434          $ads_path = str_replace( '/./', '/', $ads_path );
 435      }
 436      $parts = explode('/', $ads_path);
 437      for( $i = 0; $i < count($parts); $i++ )
 438      {
 439          if( $parts[$i] != '..' )
 440          {
 441              continue;
 442          }
 443          if( $i <= 0 || $parts[$i-1] == '' || substr($parts[$i-1], -1) == ':' /* windows drive letter */ )
 444          {
 445              return NULL;
 446          }
 447          // Remove ".." and the part before it
 448          unset($parts[$i-1], $parts[$i]);
 449          // Respin array
 450          $parts = array_values($parts);
 451          $i = $i-2;
 452      }
 453      $ads_realpath = implode('/', $parts);
 454  
 455      // pre_dump( 'get_canonical_path()', $ads_path, $ads_realpath );
 456  
 457      if( strpos( $ads_realpath, '..' ) !== false )
 458      {    // Path malformed:
 459          return NULL;
 460      }
 461  
 462      if( $is_absolute && ! strlen($ads_realpath) )
 463      {
 464          return NULL;
 465      }
 466  
 467      return $ads_realpath;
 468  }
 469  
 470  
 471  /**
 472   * Fix the length of a given file name based on the global $filename_max_length setting.
 473   *
 474   * @param string the file name
 475   * @param string the index before we should remove the over characters
 476   * @return string the modified filename if the length was above the max length and the $remove_before_index param was correct. The original filename otherwie.
 477   */
 478  function fix_filename_length( $filename, $remove_before_index )
 479  {
 480      global $filename_max_length;
 481  
 482      $filename_length = strlen( $filename );
 483      if( $filename_length > $filename_max_length )
 484      {
 485          $difference = $filename_length - $filename_max_length;
 486          if( $remove_before_index > $difference )
 487          { // Fix file name length only if the filename part before the 'remove index' contains more characters then what we have to remove
 488              $filename = substr_replace( $filename, '', $remove_before_index - $difference, $difference );
 489          }
 490      }
 491      return $filename;
 492  }
 493  
 494  
 495  /**
 496   * Process filename:
 497   *  - convert to lower case
 498   *  - replace consecutive dots with one dot
 499   *  - if force_validation is true, then replace every not valid character to '_'
 500   *  - check if file name is valid
 501   *
 502   * @param string file name (by reference) - this file name will be processed
 503   * @param boolean force validation ( replace not valid characters to '_' without warning )
 504   * @return error message if the file name is not valid, false otherwise
 505   */
 506  function process_filename( & $filename, $force_validation = false )
 507  {
 508      global $filename_max_length;
 509  
 510      if( empty( $filename ) )
 511      {
 512          return T_( 'Empty file name is not valid.' );
 513      }
 514  
 515      if( $force_validation )
 516      { // replace every not valid characters
 517          $filename = preg_replace( '/[^a-z0-9\-_.]+/i', '_', $filename );
 518          // Make sure the filename length doesn't exceed the maximum allowed. Remove characters from the end of the filename ( before the extension ) if required.
 519          $extension_pos = strrpos( $filename, '.' );
 520          $filename = fix_filename_length( $filename, strrpos( $filename, '.', ( $extension_pos ? $extension_pos : strlen( $filename ) ) ) );
 521      }
 522  
 523      // check if the file name contains consecutive dots, and replace them with one dot without warning ( keep only one dot '.' instead of '...' )
 524      $filename = preg_replace( '/\.(\.)+/', '.', evo_strtolower( $filename ) );
 525  
 526      if( $error_filename = validate_filename( $filename ) )
 527      { // invalid file name
 528          return $error_filename;
 529      }
 530  
 531      // on success
 532      return false;
 533  }
 534  
 535  
 536  /**
 537   * Check for valid filename and extension of the filename (no path allowed). (MB)
 538   *
 539   * @uses $FiletypeCache, $settings or $force_regexp_filename form _advanced.php
 540   *
 541   * @param string filename to test
 542   * @param mixed true/false to allow locked filetypes. NULL means that FileType will decide
 543   * @return nothing if the filename is valid according to the regular expression and the extension too, error message if not
 544   */
 545  function validate_filename( $filename, $allow_locked_filetypes = NULL )
 546  {
 547      global $Settings, $force_regexp_filename, $filename_max_length;
 548  
 549      if( strpos( $filename, '..' ) !== false )
 550      { // consecutive dots are not allowed in file name
 551          return sprintf( T_('&laquo;%s&raquo; is not a valid filename.').' '.T_( 'Consecutive dots are not allowed.' ), $filename );
 552      }
 553  
 554      if( strlen( $filename ) > $filename_max_length )
 555      { // filename is longer then the maximum allowed
 556          return sprintf( T_('&laquo;%s&raquo; is not a valid filename.').' '.sprintf( T_( 'Max %d characters are allowed on filenames.' ), $filename_max_length ), $filename );
 557      }
 558  
 559      // Check filename
 560      if( $force_regexp_filename )
 561      { // Use the regexp from _advanced.php
 562          if( !preg_match( ':'.str_replace( ':', '\:', $force_regexp_filename ).':', $filename ) )
 563          { // Invalid filename
 564              return sprintf( T_('&laquo;%s&raquo; is not a valid filename.'), $filename );
 565          }
 566      }
 567      else
 568      {    // Use the regexp from SETTINGS
 569          if( !preg_match( ':'.str_replace( ':', '\:', $Settings->get( 'regexp_filename' ) ).':', $filename ) )
 570          { // Invalid filename
 571              return sprintf( T_('&laquo;%s&raquo; is not a valid filename.'), $filename );
 572          }
 573      }
 574  
 575      // Check extension filename
 576      if( preg_match( '#\.([a-zA-Z0-9\-_]+)$#', $filename, $match ) )
 577      { // Filename has a valid extension
 578          $FiletypeCache = & get_FiletypeCache();
 579          if( $Filetype = & $FiletypeCache->get_by_extension( strtolower( $match[1] ) , false ) )
 580          {
 581              if( $Filetype->is_allowed( $allow_locked_filetypes ) )
 582              { // Filename has an unlocked extension or we allow locked extensions
 583                  return;
 584              }
 585              else
 586              {    // Filename hasn't an allowed extension
 587                  return sprintf( T_('&laquo;%s&raquo; is a locked extension.'), htmlentities($match[1]) );
 588              }
 589          }
 590          else
 591          { // Filename hasn't an allowed extension
 592              return sprintf( T_('&laquo;%s&raquo; has an unrecognized extension.'), $filename );
 593          }
 594      }
 595      else
 596      { // Filename hasn't a valid extension
 597          return sprintf( T_('&laquo;%s&raquo; has not a valid extension.'), $filename );
 598      }
 599  }
 600  
 601  
 602  /**
 603   * Check for valid dirname (no path allowed). ( MB )
 604   *
 605   * @uses $Settings or $force_regexp_dirname form _advanced.php
 606   * @param string dirname to test
 607   * @return nothing if the dirname is valid according to the regular expression, error message if not
 608   */
 609  function validate_dirname( $dirname )
 610  {
 611      global $Settings, $force_regexp_dirname, $filename_max_length;
 612  
 613      if( $dirname != '..' )
 614      {
 615          if( strlen( $dirname ) > $filename_max_length )
 616          { // Don't allow longer directory names then the max file name length
 617              return sprintf( T_('&laquo;%s&raquo; is not a valid directory name.'), $dirname ).' '.sprintf( T_( 'Max %d characters are allowed.' ), $filename_max_length );
 618          }
 619  
 620          if( !empty( $force_regexp_dirname ) )
 621          { // Use the regexp from _advanced.php
 622              if( preg_match( ':'.str_replace( ':', '\:', $force_regexp_dirname ).':', $dirname ) )
 623              { // Valid dirname
 624                  return;
 625              }
 626          }
 627          else
 628          { // Use the regexp from SETTINGS
 629              if( preg_match( ':'.str_replace( ':', '\:', $Settings->get( 'regexp_dirname' ) ).':', $dirname ) )
 630              { // Valid dirname
 631                  return;
 632              }
 633          }
 634      }
 635  
 636      return sprintf( T_('&laquo;%s&raquo; is not a valid directory name.'), $dirname );
 637  }
 638  
 639  
 640  /**
 641   * Check if file rename is acceptable
 642   *
 643   * used when renaming a file, File settings
 644   *
 645   * @param string the new name
 646   * @param boolean true if it is a directory, false if not
 647   * @param string the absolute path of the parent directory
 648   * @param boolean true if user has permission to all kind of fill types, false otherwise
 649   * @return mixed NULL if the rename is acceptable, error message if not
 650   */
 651  function check_rename( & $newname, $is_dir, $dir_path, $allow_locked_filetypes )
 652  {
 653      global $dirpath_max_length;
 654  
 655      // Check if provided name is okay:
 656      $newname = trim( strip_tags($newname) );
 657  
 658      if( $is_dir )
 659      {
 660          if( $error_dirname = validate_dirname( $newname ) )
 661          { // invalid directory name
 662              return $error_dirname;
 663          }
 664          if( $dirpath_max_length < ( strlen( $dir_path ) + strlen( $newname ) ) )
 665          { // The new path length would be too long
 666              return T_('The new name is too long for this folder.');
 667          }
 668      }
 669      elseif( $error_filename = validate_filename( $newname, $allow_locked_filetypes ) )
 670      { // Not a file name or not an allowed extension
 671          return $error_filename;
 672      }
 673  
 674      return NULL;
 675  }
 676  
 677  
 678  /**
 679   * Return a string with upload restrictions ( allowed extensions, max file size )
 680   */
 681  function get_upload_restriction()
 682  {
 683      global $DB, $Settings, $current_User;
 684      $restrictNotes = array();
 685  
 686      if( is_logged_in( false ) )
 687      {
 688          $condition = ( $current_User->check_perm( 'files', 'all' ) ) ? '' : 'ftyp_allowed <> "admin"';
 689      }
 690      else
 691      {
 692          $condition = 'ftyp_allowed = "any"';
 693      }
 694  
 695      if( !empty( $condition ) )
 696      {
 697          $condition = ' WHERE '.$condition;
 698      }
 699  
 700      // Get list of recognized file types (others are not allowed to get uploaded)
 701      // dh> because FiletypeCache/DataObjectCache has no interface for getting a list, this dirty query seems less dirty to me.
 702      $allowed_extensions = $DB->get_col( 'SELECT ftyp_extensions FROM T_filetypes'.$condition );
 703      $allowed_extensions = implode( ' ', $allowed_extensions ); // implode with space, ftyp_extensions can hold many, separated by space
 704      // into array:
 705      $allowed_extensions = preg_split( '~\s+~', $allowed_extensions, -1, PREG_SPLIT_NO_EMPTY );
 706      // readable:
 707      $allowed_extensions = implode_with_and($allowed_extensions);
 708  
 709      $restrictNotes[] = '<strong>'.T_('Allowed file extensions').'</strong>: '.$allowed_extensions;
 710  
 711      if( $Settings->get( 'upload_maxkb' ) )
 712      { // We want to restrict on file size:
 713          $restrictNotes[] = '<strong>'.T_('Maximum allowed file size').'</strong>: '.bytesreadable( $Settings->get( 'upload_maxkb' )*1024 );
 714      }
 715  
 716      return implode( '<br />', $restrictNotes ).'<br />';
 717  }
 718  
 719  
 720  /**
 721   * Return the path without the leading {@link $basepath}, or if not
 722   * below {@link $basepath}, just the basename of it.
 723   *
 724   * Do not use this for file handling.  JUST for displaying! (DEBUG MESSAGE added)
 725   *
 726   * @param string Path
 727   * @return string Relative path or even base name.
 728   *   NOTE: when $debug, the real path gets appended.
 729   */
 730  function rel_path_to_base( $path )
 731  {
 732      global $basepath, $debug;
 733  
 734      // Remove basepath prefix:
 735      if( preg_match( '~^('.preg_quote($basepath, '~').')(.+)$~', $path, $match ) )
 736      {
 737          $r = $match[2];
 738      }
 739      else
 740      {
 741          $r = basename($path).( is_dir($path) ? '/' : '' );
 742      }
 743  
 744      if( $debug )
 745      {
 746          $r .= ' [DEBUG: '.$path.']';
 747      }
 748  
 749      return $r;
 750  }
 751  
 752  
 753  /**
 754   * Get the directories of the supplied path as a radio button tree.
 755   *
 756   * @todo fp> Make a DirTree class (those static hacks suck)
 757   *
 758   * @param FileRoot A single root or NULL for all available.
 759   * @param string the root path to use
 760   * @param boolean add radio buttons ?
 761   * @param string used by recursion
 762   * @param string what kind of action do the user ( we need this to check permission )
 763   *             fp>asimo : in what case can this be something else than "view" ?
 764   *             asimo>fp : on the _file_upload.view, we must show only those roots where current user has permission to add files
 765   * @return string
 766   */
 767  function get_directory_tree( $Root = NULL, $ads_full_path = NULL, $ads_selected_full_path = NULL, $radios = false, $rds_rel_path = NULL, $is_recursing = false, $action = 'view' )
 768  {
 769      static $js_closeClickIDs; // clickopen IDs that should get closed
 770      static $instance_ID = 0;
 771      static $fm_highlight;
 772      global $current_User;
 773  
 774      // A folder might be highlighted (via "Locate this directory!")
 775      if( ! isset($fm_highlight) )
 776      {
 777          $fm_highlight = param('fm_highlight', 'string', '');
 778      }
 779  
 780  
 781      if( ! $is_recursing )
 782      {    // This is not a recursive call (yet):
 783          // Init:
 784          $instance_ID++;
 785          $js_closeClickIDs = array();
 786          $ret = '<ul class="clicktree">';
 787      }
 788      else
 789      {
 790          $ret = '';
 791      }
 792  
 793      // ________________________ Handle Roots ______________________
 794      if( $Root === NULL )
 795      { // We want to list all roots:
 796          $FileRootCache = & get_FileRootCache();
 797          $_roots = $FileRootCache->get_available_FileRoots();
 798  
 799          foreach( $_roots as $l_Root )
 800          {
 801              if( ! $current_User->check_perm( 'files', $action, false, $l_Root ) )
 802              {    // current user does not have permission to "view" (or other $action) this root
 803                  continue;
 804              }
 805              $subR = get_directory_tree( $l_Root, $l_Root->ads_path, $ads_selected_full_path, $radios, '', true );
 806              if( !empty( $subR['string'] ) )
 807              {
 808                  $ret .= '<li>'.$subR['string'].'</li>';
 809              }
 810          }
 811      }
 812      else
 813      {
 814          // We'll go through files in current dir:
 815          $Nodelist = new Filelist( $Root, trailing_slash($ads_full_path) );
 816          check_showparams( $Nodelist );
 817          $Nodelist->load();
 818          $Nodelist->sort( 'name' );
 819          $has_sub_dirs = $Nodelist->count_dirs();
 820  
 821          $id_path = 'id_path_'.$instance_ID.md5( $ads_full_path );
 822  
 823          $r['string'] = '<span class="folder_in_tree"';
 824  
 825          if( $ads_full_path == $ads_selected_full_path )
 826          {    // This is the current open path
 827               $r['opened'] = true;
 828  
 829               if( $fm_highlight && $fm_highlight == substr($rds_rel_path, 0, -1) )
 830               {
 831                   $r['string'] .= ' id="fm_highlighted"';
 832                   unset($fm_highlight);
 833               }
 834          }
 835          else
 836          {
 837               $r['opened'] = NULL;
 838          }
 839  
 840          $r['string'] .= '>';
 841  
 842          if( $radios )
 843          { // Optional radio input to select this path:
 844              $root_and_path = format_to_output( implode( '::', array($Root->ID, $rds_rel_path) ), 'formvalue' );
 845  
 846              $r['string'] .= '<input type="radio" name="root_and_path" value="'.$root_and_path.'" id="radio_'.$id_path.'"';
 847  
 848              if( $r['opened'] )
 849              {    // This is the current open path
 850                  $r['string'] .= ' checked="checked"';
 851              }
 852  
 853              //.( ! $has_sub_dirs ? ' style="margin-right:'.get_icon( 'collapse', 'size', array( 'size' => 'width' ) ).'px"' : '' )
 854              $r['string'] .= ' /> &nbsp; &nbsp;';
 855          }
 856  
 857          // Folder Icon + Name:
 858          $url = regenerate_url( 'root,path', 'root='.$Root->ID.'&amp;path='.$rds_rel_path );
 859          $label = action_icon( T_('Open this directory in the file manager'), 'folder', $url )
 860              .'<a href="'.$url.'"
 861              title="'.T_('Open this directory in the file manager').'">'
 862              .( empty($rds_rel_path) ? $Root->name : basename( $ads_full_path ) )
 863              .'</a>';
 864  
 865          // Handle potential subdir:
 866          if( ! $has_sub_dirs )
 867          {    // No subdirs
 868              $r['string'] .= get_icon( 'expand', 'noimg', array( 'class'=>'' ) ).'&nbsp;'.$label.'</span>';
 869          }
 870          else
 871          { // Process subdirs
 872              $r['string'] .= get_icon( 'collapse', 'imgtag', array( 'onclick' => 'toggle_clickopen(\''.$id_path.'\');',
 873                          'id' => 'clickimg_'.$id_path,
 874                          'style'=>'margin:0 2px'
 875                      ) )
 876                  .'&nbsp;'.$label.'</span>'
 877                  .'<ul class="clicktree" id="clickdiv_'.$id_path.'">'."\n";
 878  
 879              while( $l_File = & $Nodelist->get_next( 'dir' ) )
 880              {
 881                  $rSub = get_directory_tree( $Root, $l_File->get_full_path(), $ads_selected_full_path, $radios, $l_File->get_rdfs_rel_path(), true );
 882  
 883                  if( $rSub['opened'] )
 884                  { // pass opened status on, if given
 885                      $r['opened'] = $rSub['opened'];
 886                  }
 887  
 888                  $r['string'] .= '<li>'.$rSub['string'].'</li>';
 889              }
 890  
 891              if( !$r['opened'] )
 892              {
 893                  $js_closeClickIDs[] = $id_path;
 894              }
 895              $r['string'] .= '</ul>';
 896          }
 897  
 898         if( $is_recursing )
 899          {
 900              return $r;
 901          }
 902          else
 903          {
 904              $ret .= '<li>'.$r['string'].'</li>';
 905          }
 906      }
 907  
 908      if( ! $is_recursing )
 909      {
 910           $ret .= '</ul>';
 911  
 912          if( ! empty($js_closeClickIDs) )
 913          { // there are IDs of checkboxes that we want to close
 914              $ret .= "\n".'<script type="text/javascript">toggle_clickopen( \''
 915                          .implode( "' );\ntoggle_clickopen( '", $js_closeClickIDs )
 916                          ."' );\n</script>";
 917          }
 918      }
 919  
 920      return $ret;
 921  }
 922  
 923  
 924  /**
 925   * Create a directory recursively.
 926   *
 927   * @todo dh> simpletests for this (especially for open_basedir)
 928   *
 929   * @param string directory name
 930   * @param integer permissions
 931   * @return boolean
 932   */
 933  function mkdir_r( $dirName, $chmod = NULL )
 934  {
 935      return evo_mkdir( $dirName, $chmod, true );
 936  }
 937  
 938  
 939  /**
 940   * Create a directory
 941   *
 942   * @param string Directory path
 943   * @param integer Permissions
 944   * @param boolean Create a dir recursively
 945   * @return boolean TRUE on success
 946   */
 947  function evo_mkdir( $dir_path, $chmod = NULL, $recursive = false )
 948  {
 949      if( is_dir( $dir_path ) )
 950      { // already exists:
 951          return true;
 952      }
 953  
 954      if( mkdir( $dir_path, 0777, $recursive ) )
 955      { // Directory is created succesfully
 956          if( $chmod === NULL )
 957          { // Get default permissions
 958              global $Settings;
 959              $chmod = $Settings->get( 'fm_default_chmod_dir' );
 960          }
 961  
 962          if( ! empty( $chmod ) )
 963          { // Set the dir rights by chmod() function because mkdir() doesn't provide this operation correctly
 964              chmod( $dir_path, is_string( $chmod ) ? octdec( $chmod ) : $chmod );
 965          }
 966  
 967          return true;
 968      }
 969  
 970      return false;
 971  }
 972  
 973  
 974  /**
 975   * Copy directory recursively or one file
 976   *
 977   * @param mixed Source path
 978   * @param mixed Destination path
 979   */
 980  function copy_r( $source, $dest )
 981  {
 982      $result = true;
 983  
 984      if( is_dir( $source ) )
 985      { // Copy directory recusively
 986          if( ! ( $dir_handle = @opendir( $source ) ) )
 987          { // Unable to open dir
 988              return false;
 989          }
 990          $source_folder = basename( $source );
 991          if( ! mkdir_r( $dest.'/'.$source_folder ) )
 992          { // No rights to create a dir
 993              return false;
 994          }
 995          while( $file = readdir( $dir_handle ) )
 996          {
 997              if( $file != '.' && $file != '..' )
 998              {
 999                  if( is_dir( $source.'/'.$file ) )
1000                  { // Copy the files of subdirectory
1001                      $result = copy_r( $source.'/'.$file, $dest.'/'.$source_folder ) && $result;
1002                  }
1003                  else
1004                  { // Copy one file of the directory
1005                      $result = @copy( $source.'/'.$file, $dest.'/'.$source_folder.'/'.$file ) && $result;
1006                  }
1007              }
1008          }
1009          closedir( $dir_handle );
1010      }
1011      else
1012      { // Copy a file and check destination folder for existing
1013          $dest_folder = preg_replace( '#(.+)/[^/]+$#', '$1', $dest );
1014          if( ! file_exists( $dest_folder ) )
1015          { // Create destination folder recursively if it doesn't exist
1016              if( ! mkdir_r( $dest_folder ) )
1017              { // Unable to create a destination folder
1018                  return false;
1019              }
1020          }
1021          // Copy a file
1022          $result = @copy( $source, $dest );
1023      }
1024  
1025      return $result;
1026  }
1027  
1028  
1029  /**
1030   * Is the given path absolute (non-relative)?
1031   *
1032   * @return boolean
1033   */
1034  function is_absolute_pathname($path)
1035  {
1036      $pathlen = strlen($path);
1037      if( ! $pathlen )
1038      {
1039          return false;
1040      }
1041  
1042      if( is_windows() )
1043      { // windows e-g: (note: "XY:" can actually happen as a drive ID in windows; I have seen it once in 2009 on MY XP sp3 after plugin in & plugin out an USB stick like 26 times over 26 days! (with sleep/hibernate in between)
1044          return ( $pathlen > 1 && $path[1] == ':' );
1045      }
1046      else
1047      { // unix
1048          return ( $path[0] == '/' );
1049      }
1050  }
1051  
1052  
1053  /**
1054   * Define sys_get_temp_dir, if not available (PHP 5 >= 5.2.1)
1055   * @link http://us2.php.net/manual/en/function.sys-get-temp-dir.php#93390
1056   * @return string NULL on failure
1057   */
1058  if ( !function_exists('sys_get_temp_dir'))
1059  {
1060    function sys_get_temp_dir()
1061      {
1062      if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
1063      if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
1064      if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
1065      $tempfile=tempnam(__FILE__,'');
1066      if (file_exists($tempfile))
1067          {
1068        unlink($tempfile);
1069        return realpath(dirname($tempfile));
1070      }
1071      return null;
1072    }
1073  }
1074  
1075  
1076  /**
1077   * Controller helper
1078   */
1079  function file_controller_build_tabs()
1080  {
1081      global $AdminUI, $current_User, $blog;
1082  
1083      $AdminUI->add_menu_entries(
1084              'files',
1085              array(
1086                      'browse' => array(
1087                          'text' => T_('Browse'),
1088                          'href' => regenerate_url( 'ctrl', 'ctrl=files' ) ),
1089                      )
1090                  );
1091  
1092      if( $current_User->check_perm( 'files', 'add', false, $blog ? $blog : NULL ) )
1093      { // Permission to upload: (no subtabs needed otherwise)
1094          $AdminUI->add_menu_entries(
1095                  'files',
1096                  array(
1097                          'upload' => array(
1098                              'text' => /* TRANS: verb */ T_('Upload '),
1099                              'href' => regenerate_url( 'ctrl', 'ctrl=upload' ) ),
1100                      )
1101              );
1102  
1103          $AdminUI->add_menu_entries(
1104              array('files', 'upload'),
1105              array(
1106                      'quick' => array(
1107                          'text' => /* TRANS: Quick upload method */ T_('Quick '),
1108                          'href' => '?ctrl=upload&amp;tab3=quick',
1109                          'onclick' => 'return b2edit_reload( document.getElementById( \'fm_upload_checkchanges\' ), \'?ctrl=upload&amp;tab3=quick\' );' ),
1110                      'standard' => array(
1111                          'text' => /* TRANS: Standard upload method */ T_('Standard '),
1112                          'href' => '?ctrl=upload&amp;tab3=standard',
1113                          'onclick' => 'return b2edit_reload( document.getElementById( \'fm_upload_checkchanges\' ), \'?ctrl=upload&amp;tab3=standard\' );' ),
1114                      'advanced' => array(
1115                          'text' => /* TRANS: Advanced upload method */ T_('Advanced '),
1116                          'href' => '?ctrl=upload&amp;tab3=advanced',
1117                          'onclick' => 'return b2edit_reload( document.getElementById( \'fm_upload_checkchanges\' ), \'?ctrl=upload&amp;tab3=advanced\' );' ),
1118                  )
1119              );
1120      }
1121  
1122      if( $current_User->check_perm( 'options', 'view' ) )
1123      {    // Permission to view settings:
1124          $AdminUI->add_menu_entries(
1125              'files',
1126              array(
1127                  'settings' => array(
1128                      'text' => T_('Settings'),
1129                      'href' => '?ctrl=fileset',
1130                      )
1131                  )
1132              );
1133  
1134          $AdminUI->add_menu_entries(
1135              array('files', 'settings'),
1136              array(
1137                      'settings' => array(
1138                          'text' => T_('Settings'),
1139                          'href' => '?ctrl=fileset' ),
1140                      'filetypes' => array(
1141                          'text' => T_('File types'),
1142                          'href' => '?ctrl=filetypes' ),
1143                  )
1144              );
1145      }
1146  
1147      if( $current_User->check_perm( 'options', 'edit' ) )
1148      {    // Permission to edit settings:
1149          $AdminUI->add_menu_entries(
1150              'files',
1151              array(
1152                  'moderation' => array(
1153                      'text' => T_('Moderation'),
1154                      'href' => '?ctrl=filemod',
1155                      'entries' => array(
1156                          'suspicious' => array(
1157                              'text' => T_('Suspicious'),
1158                              'href' => '?ctrl=filemod&amp;tab=suspicious' ),
1159                          'duplicates' => array(
1160                              'text' => T_('Duplicates'),
1161                              'href' => '?ctrl=filemod&amp;tab=duplicates' ),
1162                          )
1163                      )
1164                  )
1165              );
1166      }
1167  
1168  }
1169  
1170  
1171  /**
1172   * Rename evocache folders after File settings update, whe evocahe folder name was chaned
1173   *
1174   * @param string old evocache folder name
1175   * @param string new evocache folder name
1176   * @return bool true on success
1177   */
1178  function rename_cachefolders( $oldname, $newname )
1179  {
1180      $FileRootCache = & get_FileRootCache();
1181  
1182      $available_Roots = $FileRootCache->get_available_FileRoots();
1183  
1184      $slash_oldname = '/'.$oldname;
1185  
1186      $result = true;
1187      foreach( $available_Roots as $fileRoot )
1188      {
1189          $filename_params = array(
1190                  'inc_files'        => false,
1191                  'inc_evocache'    => true,
1192              );
1193          $dirpaths = get_filenames( $fileRoot->ads_path, $filename_params );
1194  
1195          foreach( $dirpaths as $dirpath )
1196          { // search ?evocache folders
1197              $dirpath_length = strlen( $dirpath );
1198              if( $dirpath_length < 10 )
1199              { // The path is to short, can not contains ?evocache folder name
1200                  continue;
1201              }
1202              // searching at the end of the path -> '/' character + ?evocache, length = 1 + 9
1203              $path_end = substr( $dirpath, $dirpath_length - 10 );
1204              if( $path_end == $slash_oldname )
1205              { // this is a ?evocache folder
1206                  $new_dirpath = substr_replace( $dirpath, $newname, $dirpath_length - 9 );
1207                  // result is true only if all rename call return true (success)
1208                  $result = $result && @rename( $dirpath, $new_dirpath );
1209              }
1210          }
1211      }
1212      return $result;
1213  }
1214  
1215  
1216  /**
1217   * Delete any ?evocache folders.
1218   *
1219   * @param Log Pass a Log object here to have error messages added to it.
1220   * @return integer Number of deleted dirs.
1221   */
1222  function delete_cachefolders( $Log = NULL )
1223  {
1224      global $media_path, $Settings;
1225  
1226      // Get this, just in case someone comes up with a different naming:
1227      $evocache_foldername = $Settings->get( 'evocache_foldername' );
1228  
1229      $filename_params = array(
1230              'inc_files'        => false,
1231              'inc_evocache'    => true,
1232          );
1233      $dirs = get_filenames( $media_path, $filename_params );
1234  
1235      $deleted_dirs = 0;
1236      foreach( $dirs as $dir )
1237      {
1238          $basename = basename($dir);
1239          if( $basename == '.evocache' || $basename == '_evocache' || $basename == $evocache_foldername )
1240          {    // Delete .evocache directory recursively
1241              if( rmdir_r( $dir ) )
1242              {
1243                  $deleted_dirs++;
1244              }
1245              elseif( $Log )
1246              {
1247                  $Log->add( sprintf( T_('Could not delete directory: %s'), $dir ), 'error' );
1248              }
1249          }
1250      }
1251      return $deleted_dirs;
1252  }
1253  
1254  
1255  /**
1256   * Check and set the given FileList object fm_showhidden and fm_showevocache params
1257   */
1258  function check_showparams( & $Filelist )
1259  {
1260      global $UserSettings;
1261  
1262      if( $UserSettings->param_Request( 'fm_showhidden', 'fm_showhidden', 'integer', 0 ) )
1263      {
1264          $Filelist->_show_hidden_files = true;
1265      }
1266  
1267      if( $UserSettings->param_Request( 'fm_showevocache', 'fm_showevocache', 'integer', 0 ) )
1268      {
1269          $Filelist->_show_evocache = true;
1270      }
1271  }
1272  
1273  
1274  /**
1275   * Process file uploads (this can process multiple file uploads at once)
1276   *
1277   * @param string FileRoot id string
1278   * @param string the upload dir relative path in the FileRoot
1279   * @param boolean Shall we create path dirs if they do not exist?
1280   * @param boolean Shall we check files add permission for current_User?
1281   * @param boolean upload quick mode
1282   * @param boolean show warnings if filename is not valid
1283   * @param integer minimum size for pictures in pixels (width and height)
1284   * @return mixed NULL if upload was impossible to complete for some reason (wrong fileroot ID, insufficient user permission, etc.)
1285   *                        array, which contains uploadedFiles, failedFiles, renamedFiles and renamedMessages
1286   */
1287  function process_upload( $root_ID, $path, $create_path_dirs = false, $check_perms = true, $upload_quickmode = true, $warn_invalid_filenames = true, $min_size = 0 )
1288  {
1289      global $Settings, $Plugins, $Messages, $current_User, $force_upload_forbiddenext;
1290  
1291      if( empty($_FILES) )
1292      {    // We have NO uploaded files to process...
1293          return NULL;
1294      }
1295  
1296      /**
1297       * Remember failed files (and the error messages)
1298       * @var array
1299       */
1300      $failedFiles = array();
1301      /**
1302       * Remember uploaded files
1303       * @var array
1304       */
1305      $uploadedFiles = array();
1306      /**
1307       * Remember renamed files
1308       * @var array
1309       */
1310      $renamedFiles = array();
1311      /**
1312       * Remember renamed Messages
1313       * @var array
1314       */
1315      $renamedMessages = array();
1316  
1317      $FileRootCache = & get_FileRootCache();
1318      $fm_FileRoot = & $FileRootCache->get_by_ID($root_ID, true);
1319      if( !$fm_FileRoot )
1320      { // fileRoot not found:
1321          return NULL;
1322      }
1323  
1324      if( $check_perms && ( !isset( $current_User ) || $current_User->check_perm( 'files', 'add', false, $fm_FileRoot ) ) )
1325      { // Permission check required but current User has no permission to upload:
1326          return NULL;
1327      }
1328  
1329      // Let's get into requested list dir...
1330      $non_canonical_list_path = $fm_FileRoot->ads_path.$path;
1331      // Dereference any /../ just to make sure, and CHECK if directory exists:
1332      $ads_list_path = get_canonical_path( $non_canonical_list_path );
1333  
1334      // check if the upload dir exists
1335      if( !is_dir( $ads_list_path ) )
1336      {
1337          if( $create_path_dirs )
1338          { // Create path
1339              mkdir_r( $ads_list_path );
1340          }
1341          else
1342          { // This case should not happen! If it happens then there is a bug in the code where this function was called!
1343              return NULL;
1344          }
1345      }
1346  
1347      // Get param arrays for all uploaded files:
1348      $uploadfile_title = param( 'uploadfile_title', 'array/string', array() );
1349      $uploadfile_alt = param( 'uploadfile_alt', 'array/string', array() );
1350      $uploadfile_desc = param( 'uploadfile_desc', 'array/string', array() );
1351      $uploadfile_name = param( 'uploadfile_name', 'array/string', array() );
1352  
1353      // LOOP THROUGH ALL UPLOADED FILES AND PROCCESS EACH ONE:
1354      foreach( $_FILES['uploadfile']['name'] as $lKey => $lName )
1355      {
1356          if( empty( $lName ) )
1357          { // No file name:
1358              if( $upload_quickmode
1359                   || !empty( $uploadfile_title[$lKey] )
1360                   || !empty( $uploadfile_alt[$lKey] )
1361                   || !empty( $uploadfile_desc[$lKey] )
1362                   || !empty( $uploadfile_name[$lKey] ) )
1363              { // User specified params but NO file! Warn the user:
1364                  $failedFiles[$lKey] = T_( 'Please select a local file to upload.' );
1365              }
1366              // Abort upload for this file:
1367              continue;
1368          }
1369  
1370          if( $Settings->get( 'upload_maxkb' )
1371                  && $_FILES['uploadfile']['size'][$lKey] > $Settings->get( 'upload_maxkb' )*1024 )
1372          { // File is larger than allowed in settings:
1373              $failedFiles[$lKey] = sprintf(
1374                      T_('The file is too large: %s but the maximum allowed is %s.'),
1375                      bytesreadable( $_FILES['uploadfile']['size'][$lKey] ),
1376                      bytesreadable($Settings->get( 'upload_maxkb' )*1024) );
1377              // Abort upload for this file:
1378              continue;
1379          }
1380  
1381          if( ( !( $_FILES['uploadfile']['error'][$lKey] ) ) && ( !empty( $min_size ) ) )
1382          { // If there is no error and a minimum size is required, check if the uploaded picture satisfies the "minimum size" criteria
1383              $image_sizes = imgsize( $_FILES['uploadfile']['tmp_name'][$lKey], 'widthheight' );
1384              if( $image_sizes[0] < $min_size || $image_sizes[1] < $min_size )
1385              {    // Abort upload for this file:
1386                  $failedFiles[$lKey] = sprintf(
1387                      T_( 'Your profile picture must have a minimum size of %dx%d pixels.' ),
1388                      $min_size,
1389                      $min_size );
1390                  continue;
1391              }
1392          }
1393  
1394          if( $_FILES['uploadfile']['error'][$lKey] )
1395          { // PHP itself has detected an error!:
1396              switch( $_FILES['uploadfile']['error'][$lKey] )
1397              {
1398                  case UPLOAD_ERR_FORM_SIZE:
1399                      // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form.
1400                      // This can easily be edited by the user/hacker, so we do not use it.. file size gets checked for real just above.
1401                      break;
1402  
1403                  case UPLOAD_ERR_INI_SIZE:
1404                      // File is larger than allowed in php.ini:
1405                      $failedFiles[$lKey] = 'The file exceeds the upload_max_filesize directive in php.ini.'; // Configuration error, no translation
1406                      // Abort upload for this file:
1407                      continue;
1408  
1409                  case UPLOAD_ERR_PARTIAL:
1410                      $failedFiles[$lKey] = T_('The file was only partially uploaded.');
1411                      // Abort upload for this file:
1412                      continue;
1413  
1414                  case UPLOAD_ERR_NO_FILE:
1415                      // Is probably the same as empty($lName) before.
1416                      $failedFiles[$lKey] = T_('No file was uploaded.');
1417                      // Abort upload for this file:
1418                      continue;
1419  
1420                  case 6: // numerical value of UPLOAD_ERR_NO_TMP_DIR
1421                  # (min_php: 4.3.10, 5.0.3) case UPLOAD_ERR_NO_TMP_DIR:
1422                      // Missing a temporary folder.
1423                      $failedFiles[$lKey] = 'Temporary upload dir is missing! (upload_tmp_dir in php.ini)'; // Configuration error, no translation
1424                      // Abort upload for this file:
1425                      continue;
1426  
1427                  default:
1428                      $failedFiles[$lKey] = T_('An unknown error has occurred!').' Error code #'.$_FILES['uploadfile']['error'][$lKey];
1429                      // Abort upload for this file:
1430                      continue;
1431              }
1432          }
1433  
1434          if( ! isset($_FILES['uploadfile']['_evo_fetched_url'][$lKey]) // skip check for fetched URLs
1435              && ! is_uploaded_file( $_FILES['uploadfile']['tmp_name'][$lKey] ) )
1436          { // Ensure that a malicious user hasn't tried to trick the script into working on files upon which it should not be working.
1437              $failedFiles[$lKey] = T_('The file does not seem to be a valid upload! It may exceed the upload_max_filesize directive in php.ini.');
1438              // Abort upload for this file:
1439              continue;
1440          }
1441  
1442          // Use new name on server if specified:
1443          $newName = !empty( $uploadfile_name[ $lKey ] ) ? $uploadfile_name[ $lKey ] : $lName;
1444          // validate file name
1445          if( $error_filename = process_filename( $newName, !$warn_invalid_filenames ) )
1446          { // Not a valid file name or not an allowed extension:
1447              $failedFiles[$lKey] = $error_filename;
1448              // Abort upload for this file:
1449              continue;
1450          }
1451  
1452          // Check if the uploaded file type is an image, and if is an image then try to fix the file extension based on mime type
1453          // If the mime type is a known mime type and user has right to upload files with this kind of file type,
1454          // this part of code will check if the file extension is the same as admin defined for this file type, and will fix it if it isn't the same
1455          // Note: it will also change the jpeg extensions to jpg.
1456          $uploadfile_path = $_FILES['uploadfile']['tmp_name'][$lKey];
1457          // this image_info variable will be used again to get file thumb
1458          $image_info = getimagesize($uploadfile_path);
1459          if( $image_info )
1460          { // This is an image, validate mimetype vs. extension:
1461              $image_mimetype = $image_info['mime'];
1462              $FiletypeCache = & get_FiletypeCache();
1463              // Get correct file type based on mime type
1464              $correct_Filetype = $FiletypeCache->get_by_mimetype( $image_mimetype, false, false );
1465  
1466              // Check if file type is known by us, and if it is allowed for upload.
1467              // If we don't know this file type or if it isn't allowed we don't change the extension! The current extension is allowed for sure.
1468              if( $correct_Filetype && $correct_Filetype->is_allowed() )
1469              { // A FileType with the given mime type exists in database and it is an allowed file type for current User
1470                  // The "correct" extension is a plausible one, proceed...
1471                  $correct_extension = array_shift($correct_Filetype->get_extensions());
1472                  $path_info = pathinfo($newName);
1473                  $current_extension = $path_info['extension'];
1474  
1475                  // change file extension to the correct extension, but only if the correct extension is not restricted, this is an extra security check!
1476                  if( strtolower($current_extension) != strtolower($correct_extension) && ( !in_array( $correct_extension, $force_upload_forbiddenext ) ) )
1477                  { // change the file extension to the correct extension
1478                      $old_name = $newName;
1479                      $newName = $path_info['filename'].'.'.$correct_extension;
1480                      $Messages->add( sprintf(T_('The extension of the file &laquo;%s&raquo; has been corrected. The new filename is &laquo;%s&raquo;.'), $old_name, $newName), 'warning' );
1481                  }
1482              }
1483          }
1484  
1485          // Get File object for requested target location:
1486          $oldName = strtolower( $newName );
1487          list( $newFile, $oldFile_thumb ) = check_file_exists( $fm_FileRoot, $path, $newName, $image_info );
1488          $newName = $newFile->get( 'name' );
1489  
1490          // Trigger plugin event
1491          if( $Plugins->trigger_event_first_false( 'AfterFileUpload', array(
1492                      'File' => & $newFile,
1493                      'name' => & $_FILES['uploadfile']['name'][$lKey],
1494                      'type' => & $_FILES['uploadfile']['type'][$lKey],
1495                      'tmp_name' => & $_FILES['uploadfile']['tmp_name'][$lKey],
1496                      'size' => & $_FILES['uploadfile']['size'][$lKey],
1497                  ) ) )
1498          {
1499              // Plugin returned 'false'.
1500              // Abort upload for this file:
1501              continue;
1502          }
1503  
1504          // Attempt to move the uploaded file to the requested target location:
1505          if( isset($_FILES['uploadfile']['_evo_fetched_url'][$lKey]) )
1506          { // fetched remotely
1507              if( ! rename( $_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path() ) )
1508              {
1509                  $failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.');
1510                  // Abort upload for this file:
1511                  continue;
1512              }
1513          }
1514          elseif( ! move_uploaded_file( $_FILES['uploadfile']['tmp_name'][$lKey], $newFile->get_full_path() ) )
1515          {
1516              $failedFiles[$lKey] = T_('An unknown error occurred when moving the uploaded file on the server.');
1517              // Abort upload for this file:
1518              continue;
1519          }
1520  
1521          // change to default chmod settings
1522          if( $newFile->chmod( NULL ) === false )
1523          { // add a note, this is no error!
1524              $Messages->add( sprintf( T_('Could not change permissions of &laquo;%s&raquo; to default chmod setting.'), $newFile->dget('name') ), 'note' );
1525          }
1526  
1527          // Refreshes file properties (type, size, perms...)
1528          $newFile->load_properties();
1529  
1530          if( ! empty( $oldFile_thumb ) )
1531          { // The file name was changed!
1532              if( $image_info )
1533              {
1534                  $newFile_thumb = $newFile->get_preview_thumb( 'fulltype' );
1535              }
1536              else
1537              {
1538                  $newFile_thumb = $newFile->get_size_formatted();
1539              }
1540              //$newFile_size = bytesreadable ($_FILES['uploadfile']['size'][$lKey]);
1541              $renamedMessages[$lKey]['message'] = sprintf( T_('"%s was renamed to %s. Would you like to replace %s with the new version instead?'),
1542                                                           '&laquo;'.$oldName.'&raquo;', '&laquo;'.$newName.'&raquo;', '&laquo;'.$oldName.'&raquo;' );
1543              $renamedMessages[$lKey]['oldThumb'] = $oldFile_thumb;
1544              $renamedMessages[$lKey]['newThumb'] = $newFile_thumb;
1545              $renamedFiles[$lKey]['oldName'] = $oldName;
1546              $renamedFiles[$lKey]['newName'] = $newName;
1547          }
1548  
1549          // Store extra info about the file into File Object:
1550          if( isset( $uploadfile_title[$lKey] ) )
1551          { // If a title text has been passed... (does not happen in quick upload mode)
1552              $newFile->set( 'title', trim( strip_tags($uploadfile_title[$lKey])) );
1553          }
1554          if( isset( $uploadfile_alt[$lKey] ) )
1555          { // If an alt text has been passed... (does not happen in quick upload mode)
1556              $newFile->set( 'alt', trim( strip_tags($uploadfile_alt[$lKey])) );
1557          }
1558          if( isset( $uploadfile_desc[$lKey] ) )
1559          { // If a desc text has been passed... (does not happen in quick upload mode)
1560              $newFile->set( 'desc', trim( strip_tags($uploadfile_desc[$lKey])) );
1561          }
1562  
1563          // Store File object into DB:
1564          $newFile->dbsave();
1565          $uploadedFiles[] = $newFile;
1566      }
1567  
1568      prepare_uploaded_files( $uploadedFiles );
1569  
1570      return array( 'uploadedFiles' => $uploadedFiles, 'failedFiles' => $failedFiles, 'renamedFiles' => $renamedFiles, 'renamedMessages' => $renamedMessages );
1571  }
1572  
1573  
1574  /**
1575   * Prepare the uploaded files
1576   *
1577   * @param array Uploaded files
1578   */
1579  function prepare_uploaded_files( $uploadedFiles )
1580  {
1581      if( count( $uploadedFiles ) == 0 )
1582      {    // No uploaded files
1583          return;
1584      }
1585  
1586      foreach( $uploadedFiles as $File )
1587      {
1588          $Filetype = & $File->get_Filetype();
1589          if( $Filetype )
1590          {
1591              if( in_array( $Filetype->mimetype, array( 'image/jpeg', 'image/gif', 'image/png' ) ) )
1592              {    // Image file
1593                  prepare_uploaded_image( $File, $Filetype->mimetype );
1594              }
1595          }
1596      }
1597  }
1598  
1599  
1600  /**
1601   * Prepare image file (Resize, Rotate and etc.)
1602   *
1603   * @param object File
1604   * @param string mimetype
1605   */
1606  function prepare_uploaded_image( $File, $mimetype )
1607  {
1608      global $Settings, $Messages;
1609  
1610      $thumb_width = $Settings->get( 'fm_resize_width' );
1611      $thumb_height = $Settings->get( 'fm_resize_height' );
1612      $thumb_quality = $Settings->get( 'fm_resize_quality' );
1613  
1614      $do_resize = false;
1615      if( $Settings->get( 'fm_resize_enable' ) &&
1616          $thumb_width > 0 && $thumb_height > 0 )
1617      {    // Image resizing is enabled
1618          list( $image_width, $image_height ) = explode( 'x', $File->get_image_size() );
1619          if( $image_width > $thumb_width || $image_height > $thumb_height )
1620          {    // This image should be resized
1621              $do_resize = true;
1622          }
1623      }
1624  
1625      load_funcs( 'files/model/_image.funcs.php' );
1626  
1627      $resized_imh = null;
1628      if( $do_resize )
1629      { // Resize image
1630          list( $err, $src_imh ) = load_image( $File->get_full_path(), $mimetype );
1631          if( empty( $err ) )
1632          {
1633              list( $err, $resized_imh ) = generate_thumb( $src_imh, 'fit', $thumb_width, $thumb_height );
1634          }
1635  
1636          if( empty( $err ) )
1637          { // Image was rezised successfully
1638              $Messages->add( sprintf( T_( '%s was resized to %dx%d pixels.' ), '<b>'.$File->get('name').'</b>', imagesx( $resized_imh ), imagesy( $resized_imh ) ), 'success' );
1639          }
1640          else
1641          { // Image was not rezised
1642              $Messages->add( sprintf( T_( '%s could not be resized to target resolution of %dx%d pixels.' ), '<b>'.$File->get('name').'</b>', $thumb_width, $thumb_height ), 'error' );
1643              // Error exists, exit here
1644              return;
1645          }
1646      }
1647  
1648      if( $mimetype == 'image/jpeg' )
1649      {    // JPEG, do autorotate if EXIF Orientation tag is defined
1650          $save_image = !$do_resize; // If image was be resized, we should save file only in the end of this function
1651          exif_orientation( $File->get_full_path(), $resized_imh, $save_image );
1652      }
1653  
1654      if( !$resized_imh )
1655      {    // Image resource is incorrect
1656          return;
1657      }
1658  
1659      if( $do_resize && empty( $err ) )
1660      {    // Save resized image ( and also rotated image if this operation was done )
1661          save_image( $resized_imh, $File->get_full_path(), $mimetype, $thumb_quality );
1662      }
1663  }
1664  
1665  
1666  /**
1667   * Rotate the JPEG image if EXIF Orientation tag is defined
1668   *
1669   * @param string File name (with full path)
1670   * @param resource Image resource ( result of the function imagecreatefromjpeg() ) (by reference)
1671   * @param boolean TRUE - to save the rotated image in the end of this function
1672   */
1673  function exif_orientation( $file_name, & $imh/* = null*/, $save_image = false )
1674  {
1675      global $Settings;
1676  
1677      if( !$Settings->get( 'exif_orientation' ) )
1678      { // Autorotate is disabled
1679          return;
1680      }
1681  
1682      if( ! function_exists('exif_read_data') )
1683      { // Exif extension is not loaded
1684          return;
1685      }
1686  
1687      $image_info = array();
1688      getimagesize( $file_name, $image_info );
1689      if( ! isset( $image_info['APP1'] ) || ( strpos( $image_info['APP1'], 'Exif' ) !== 0 ) )
1690      { // This file format is not an 'Exchangeable image file format' so there are no Exif data to read
1691          return;
1692      }
1693  
1694      if( ( $exif_data = exif_read_data( $file_name ) ) === false )
1695      { // Could not read Exif data
1696          return;
1697      }
1698  
1699      if( !( isset( $exif_data['Orientation'] ) && in_array( $exif_data['Orientation'], array( 3, 6, 8 ) ) ) )
1700      { // Exif Orientation tag is not defined OR we don't interested in current value
1701          return;
1702      }
1703  
1704      load_funcs( 'files/model/_image.funcs.php' );
1705  
1706      if( is_null( $imh ) )
1707      { // Create image resource from file name
1708          $imh = imagecreatefromjpeg( $file_name );
1709      }
1710  
1711      if( !$imh )
1712      { // Image resource is incorrect
1713          return;
1714      }
1715  
1716      switch( $exif_data['Orientation'] )
1717      {
1718          case 3:    // Rotate for 180 degrees
1719              $imh = @imagerotate( $imh, 180, 0 );
1720              break;
1721  
1722          case 6:    // Rotate for 90 degrees to the right
1723              $imh = @imagerotate( $imh, 270, 0 );
1724              break;
1725  
1726          case 8:    // Rotate for 90 degrees to the left
1727              $imh = @imagerotate( $imh, 90, 0 );
1728              break;
1729      }
1730  
1731      if( !$imh )
1732      {    // Image resource is incorrect
1733          return;
1734      }
1735  
1736      if( $save_image )
1737      {    // Save rotated image
1738          save_image( $imh, $file_name, 'image/jpeg' );
1739      }
1740  }
1741  
1742  
1743  /**
1744   * Check if file exists in the target location with the given name. Used during file upload.
1745   *
1746   * @param FileRoot target file Root
1747   * @param string target path
1748   * @param string file name
1749   * @param array the new file image_info
1750   * @return array contains two elements
1751   *             first elements is a new File object
1752   *             second element is the existing file thumb, or empty string, if the file doesn't exists
1753   */
1754  function check_file_exists( $fm_FileRoot, $path, $newName, $image_info = NULL )
1755  {
1756      global $filename_max_length;
1757  
1758      // Get File object for requested target location:
1759      $FileCache = & get_FileCache();
1760      $newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$newName, true );
1761  
1762      $num_ext = 0;
1763      $oldName = $newName;
1764  
1765      $oldFile_thumb = "";
1766      while( $newFile->exists() )
1767      { // The file already exists in the target location!
1768          $num_ext++;
1769          $ext_pos = strrpos( $newName, '.');
1770          if( $num_ext == 1 )
1771          {
1772              if( $image_info == NULL )
1773              {
1774                  $image_info = getimagesize( $newFile->get_full_path() );
1775              }
1776              $newName = substr_replace( $newName, '-'.$num_ext.'.', $ext_pos, 1 );
1777              if( $image_info )
1778              {
1779                  $oldFile_thumb = $newFile->get_preview_thumb( 'fulltype' );
1780              }
1781              else
1782              {
1783                  $oldFile_thumb = $newFile->get_size_formatted();
1784              }
1785          }
1786          else
1787          {
1788              $replace_length = strlen( '-'.($num_ext-1) );
1789              $newName = substr_replace( $newName, '-'.$num_ext, $ext_pos-$replace_length, $replace_length );
1790          }
1791          if( strlen( $newName ) > $filename_max_length )
1792          {
1793              $newName = fix_filename_length( $newName, strrpos( $newName, '-' ) );
1794              if( $error_filename = process_filename( $newName, true ) )
1795              { // The file name is still not valid
1796                  debug_die( 'Invalid file name has found during file exists check: '.$error_filename );
1797              }
1798          }
1799          $newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($path).$newName, true );
1800      }
1801  
1802      return array( $newFile, $oldFile_thumb );
1803  }
1804  
1805  
1806  /**
1807   * Remove files with the given ids
1808   *
1809   * @param array file ids to remove, default to remove all orphan file IDs
1810   * @param integer remove files older than the given hour, default NULL will remove all
1811   * @return integer the number of removed files
1812   */
1813  function remove_orphan_files( $file_ids = NULL, $older_then = NULL )
1814  {
1815      global $DB, $localtimenow;
1816      // asimo> This SQL query should use file class delete_restrictions array (currently T_links and T_users is explicitly used)
1817      // select orphan comment attachment file ids
1818      $sql = 'SELECT file_ID FROM T_files
1819                  WHERE ( file_path LIKE "comments/p%" OR file_path LIKE "anonymous_comments/p%" ) AND file_ID NOT IN (
1820                      SELECT * FROM (
1821                          ( SELECT DISTINCT link_file_ID FROM T_links
1822                              WHERE link_file_ID IS NOT NULL ) UNION
1823                          ( SELECT DISTINCT user_avatar_file_ID FROM T_users
1824                              WHERE user_avatar_file_ID IS NOT NULL ) ) AS linked_files )';
1825  
1826      if( $file_ids != NULL )
1827      { // remove only from the given files
1828          $sql .= ' AND file_ID IN ( '.implode( ',', $file_ids ).' )';
1829      }
1830  
1831      $result = $DB->get_col( $sql );
1832      $FileCache = & get_FileCache();
1833      $FileCache->load_list( $result );
1834      $count = 0;
1835      foreach( $result as $file_ID )
1836      {
1837          $File = $FileCache->get_by_ID( $file_ID, false, false );
1838          if( $older_then != NULL )
1839          { // we have to check if the File is older then the given value
1840              $datediff = $localtimenow - filemtime( $File->_adfp_full_path );
1841              if( $datediff > $older_then * 3600 ) // convert hours to seconds
1842              { // not older
1843                  continue;
1844              }
1845          }
1846          // delete the file
1847          if( $File->unlink() )
1848          {
1849              $count++;
1850          }
1851      }
1852      // Clear FileCache to save memory
1853      $FileCache->clear();
1854  
1855      return $count;
1856  }
1857  
1858  
1859  /**
1860   * Get available icons for file types
1861   *
1862   * @return array 'key'=>'name'
1863   */
1864  function get_available_filetype_icons()
1865  {
1866      $icons = array(
1867          ''               => T_('Unknown'),
1868          'file_empty'     => T_('Empty'),
1869          'file_image'     => T_('Image'),
1870          'file_document'  => T_('Document'),
1871          'file_www'       => T_('Web file'),
1872          'file_log'       => T_('Log file'),
1873          'file_sound'     => T_('Audio file'),
1874          'file_video'     => T_('Video file'),
1875          'file_message'   => T_('Message'),
1876          'file_pdf'       => T_('PDF'),
1877          'file_php'       => T_('PHP script'),
1878          'file_encrypted' => T_('Encrypted file'),
1879          'file_zip'       => T_('Zip archive'),
1880          'file_tar'       => T_('Tar archive'),
1881          'file_tgz'       => T_('Tgz archive'),
1882          'file_pk'        => T_('Archive'),
1883          'file_doc'       => T_('Microsoft Word'),
1884          'file_xls'       => T_('Microsoft Excel'),
1885          'file_ppt'       => T_('Microsoft PowerPoint'),
1886          'file_pps'       => T_('Microsoft PowerPoint Slideshow'),
1887      );
1888  
1889      return $icons;
1890  }
1891  
1892  
1893  /**
1894   * Save a vote for the file by user
1895   *
1896   * @param string File ID
1897   * @param integer User ID
1898   * @param string Action of the voting ( 'like', 'noopinion', 'dontlike', 'inappropriate', 'spam' )
1899   * @param integer 1 = checked, 0 = unchecked (for checkboxes: 'Inappropriate' & 'Spam' )
1900   */
1901  function file_vote( $file_ID, $user_ID, $vote_action, $checked = 1 )
1902  {
1903      global $DB;
1904  
1905      // Set modified field name and value
1906      switch( $vote_action )
1907      {
1908          case 'like':
1909              $field_name = 'fvot_like';
1910              $field_value = '1';
1911              break;
1912  
1913          case 'noopinion':
1914              $field_name = 'fvot_like';
1915              $field_value = '0';
1916              break;
1917  
1918          case 'dontlike':
1919              $field_name = 'fvot_like';
1920              $field_value = '-1';
1921              break;
1922  
1923          case 'inappropriate':
1924              $field_name = 'fvot_inappropriate';
1925              $field_value = $checked;
1926              break;
1927  
1928          case 'spam':
1929              $field_name = 'fvot_spam';
1930              $field_value = $checked;
1931              break;
1932  
1933          default:
1934              // invalid vote action
1935              return;
1936      }
1937  
1938      $DB->begin();
1939  
1940      $SQL = new SQL();
1941      $SQL->SELECT( 'fvot_file_ID' );
1942      $SQL->FROM( 'T_files__vote' );
1943      $SQL->WHERE( 'fvot_file_ID = '.$DB->quote( $file_ID ) );
1944      $SQL->WHERE_and( 'fvot_user_ID = '.$DB->quote( $user_ID ) );
1945      $vote = $DB->get_row( $SQL->get() );
1946  
1947      // Save a voting results in DB
1948      if( empty( $vote ) )
1949      {    // User replace into to avoid duplicate key conflict in case when user clicks two times fast one after the other
1950          $result = $DB->query( 'REPLACE INTO T_files__vote ( fvot_file_ID, fvot_user_ID, '.$field_name.' )
1951                          VALUES ( '.$DB->quote( $file_ID ).', '.$DB->quote( $user_ID ).', '.$DB->quote( $field_value ).' )' );
1952      }
1953      else
1954      {    // Update existing record, because user already has a vote for this file
1955          $result = $DB->query( 'UPDATE T_files__vote
1956                      SET '.$field_name.' = '.$DB->quote( $field_value ).'
1957                      WHERE fvot_file_ID = '.$DB->quote( $file_ID ).'
1958                          AND fvot_user_ID = '.$DB->quote( $user_ID ) );
1959      }
1960  
1961      if( $result )
1962      {
1963          $DB->commit();
1964      }
1965      else
1966      {
1967          $DB->rollback();
1968      }
1969  }
1970  
1971  
1972  /**
1973   * Copy file from source path to destination path (Used on import)
1974   *
1975   * @param string Path of source file
1976   * @param string FileRoot id string
1977   * @param string the upload dir relative path in the FileRoot
1978   * @param boolean Shall we check files add permission for current_User?
1979   * @return mixed NULL if import was impossible to complete for some reason (wrong fileroot ID, insufficient user permission, etc.)
1980   *               file ID of new inserted file in DB
1981   */
1982  function copy_file( $file_path, $root_ID, $path, $check_perms = true )
1983  {
1984      global $current_User;
1985  
1986      $FileRootCache = & get_FileRootCache();
1987      $fm_FileRoot = & $FileRootCache->get_by_ID($root_ID, true);
1988      if( !$fm_FileRoot )
1989      {    // fileRoot not found:
1990          return NULL;
1991      }
1992  
1993      if( $check_perms && ( !isset( $current_User ) || $current_User->check_perm( 'files', 'add', false, $fm_FileRoot ) ) )
1994      {    // Permission check required but current User has no permission to upload:
1995          return NULL;
1996      }
1997  
1998      // Let's get into requested list dir...
1999      $non_canonical_list_path = $fm_FileRoot->ads_path.$path;
2000      // Dereference any /../ just to make sure, and CHECK if directory exists:
2001      $ads_list_path = get_canonical_path( $non_canonical_list_path );
2002  
2003      // check if the upload dir exists
2004      if( !is_dir( $ads_list_path ) )
2005      {    // Create path
2006          mkdir_r( $ads_list_path );
2007      }
2008  
2009      // Get file name from full path:
2010      $newName = basename( $file_path );
2011      // validate file name
2012      if( $error_filename = process_filename( $newName, true ) )
2013      {    // Not a valid file name or not an allowed extension:
2014          // Abort import for this file:
2015          return NULL;
2016      }
2017  
2018      // Check if the imported file type is an image, and if is an image then try to fix the file extension based on mime type
2019      // If the mime type is a known mime type and user has right to import files with this kind of file type,
2020      // this part of code will check if the file extension is the same as admin defined for this file type, and will fix it if it isn't the same
2021      // Note: it will also change the jpeg extensions to jpg.
2022      // this image_info variable will be used again to get file thumb
2023      $image_info = getimagesize( $file_path );
2024      if( $image_info )
2025      {    // This is an image, validate mimetype vs. extension:
2026          $image_mimetype = $image_info['mime'];
2027          $FiletypeCache = & get_FiletypeCache();
2028          // Get correct file type based on mime type
2029          $correct_Filetype = $FiletypeCache->get_by_mimetype( $image_mimetype, false, false );
2030  
2031          // Check if file type is known by us, and if it is allowed for upload.
2032          // If we don't know this file type or if it isn't allowed we don't change the extension! The current extension is allowed for sure.
2033          if( $correct_Filetype && $correct_Filetype->is_allowed() )
2034          {    // A FileType with the given mime type exists in database and it is an allowed file type for current User
2035              // The "correct" extension is a plausible one, proceed...
2036              $correct_extension = array_shift($correct_Filetype->get_extensions());
2037              $path_info = pathinfo($newName);
2038              $current_extension = $path_info['extension'];
2039  
2040              // change file extension to the correct extension, but only if the correct extension is not restricted, this is an extra security check!
2041              if( strtolower($current_extension) != strtolower($correct_extension) && ( !in_array( $correct_extension, $force_upload_forbiddenext ) ) )
2042              {    // change the file extension to the correct extension
2043                  $old_name = $newName;
2044                  $newName = $path_info['filename'].'.'.$correct_extension;
2045              }
2046          }
2047      }
2048  
2049      // Get File object for requested target location:
2050      $oldName = strtolower( $newName );
2051      list( $newFile, $oldFile_thumb ) = check_file_exists( $fm_FileRoot, $path, $newName, $image_info );
2052      $newName = $newFile->get( 'name' );
2053  
2054      if( ! copy( $file_path, $newFile->get_full_path() ) )
2055      {    // Abort import for this file:
2056          return NULL;
2057      }
2058  
2059      // change to default chmod settings
2060      $newFile->chmod( NULL );
2061  
2062      // Refreshes file properties (type, size, perms...)
2063      $newFile->load_properties();
2064  
2065      // Store File object into DB:
2066      if( $newFile->dbsave() )
2067      {    // Success
2068          return $newFile->ID;
2069      }
2070      else
2071      {    // Failure
2072          return NULL;
2073      }
2074  }
2075  
2076  
2077  /**
2078   * Create links between users and image files from the users profile_pictures folder
2079   */
2080  function create_profile_picture_links()
2081  {
2082      global $DB;
2083  
2084      load_class( 'files/model/_filelist.class.php', 'Filelist' );
2085      load_class( 'files/model/_fileroot.class.php', 'FileRoot' );
2086      $path = 'profile_pictures';
2087  
2088      $FileRootCache = & get_FileRootCache();
2089      $UserCache = & get_UserCache();
2090  
2091      // SQL query to get all users and limit by page below
2092      $users_SQL = new SQL();
2093      $users_SQL->SELECT( '*' );
2094      $users_SQL->FROM( 'T_users' );
2095      $users_SQL->ORDER_BY( 'user_ID' );
2096  
2097      $page = 0;
2098      $page_size = 100;
2099      while( count( $UserCache->cache ) > 0 || $page == 0 )
2100      { // Load users by 100 at one time to avoid errors about memory exhausting
2101          $users_SQL->LIMIT( ( $page * $page_size ).', '.$page_size );
2102          $UserCache->clear();
2103          $UserCache->load_by_sql( $users_SQL );
2104  
2105          while( ( $iterator_User = & $UserCache->get_next(/* $user_ID, false, false */) ) != NULL )
2106          { // Iterate through UserCache)
2107              $FileRootCache->clear();
2108              $user_FileRoot = & $FileRootCache->get_by_type_and_ID( 'user', $iterator_User->ID );
2109              if( !$user_FileRoot )
2110              { // User FileRoot doesn't exist
2111                  continue;
2112              }
2113  
2114              $ads_list_path = get_canonical_path( $user_FileRoot->ads_path.$path );
2115              // Previously uploaded avatars
2116              if( !is_dir( $ads_list_path ) )
2117              { // profile_picture folder doesn't exists in the user root dir
2118                  continue;
2119              }
2120  
2121              $user_avatar_Filelist = new Filelist( $user_FileRoot, $ads_list_path );
2122              $user_avatar_Filelist->load();
2123  
2124              if( $user_avatar_Filelist->count() > 0 )
2125              {    // profile_pictures folder is not empty
2126                  $info_content = '';
2127                  $LinkOwner = new LinkUser( $iterator_User );
2128                  while( $lFile = & $user_avatar_Filelist->get_next() )
2129                  { // Loop through all Files:
2130                      $fileName = $lFile->get_name();
2131                      if( process_filename( $fileName ) )
2132                      { // The file has invalid file name, don't create in the database
2133                          // TODO: asimo> we should collect each invalid file name here, and send an email to the admin
2134                          continue;
2135                      }
2136                      $lFile->load_meta( true );
2137                      if( $lFile->is_image() )
2138                      {
2139                          $lFile->link_to_Object( $LinkOwner );
2140                      }
2141                  }
2142              }
2143          }
2144  
2145          // Increase page number to get next portion of users
2146          $page++;
2147      }
2148  
2149      // Clear cache data
2150      $UserCache->clear();
2151      $FileRootCache->clear();
2152  }
2153  
2154  
2155  /**
2156   * Create .htaccess and sample.htaccess files with deny rules in the folder
2157   *
2158   * @param string Directory path
2159   * @return boolean TRUE if files have been created successfully
2160   */
2161  function create_htaccess_deny( $dir )
2162  {
2163      if( ! mkdir_r( $dir, NULL ) )
2164      {
2165          return false;
2166      }
2167  
2168      $htaccess_files = array(
2169              $dir.'.htaccess',
2170              $dir.'sample.htaccess'
2171          );
2172  
2173      $htaccess_content = '# We don\'t want web users to access any file in this directory'."\r\n".
2174          'Order Deny,Allow'."\r\n".
2175          'Deny from All';
2176  
2177      foreach( $htaccess_files as $htaccess_file )
2178      {
2179          if( file_exists( $htaccess_file ) )
2180          { // File already exists
2181              continue;
2182          }
2183  
2184          $handle = @fopen( $htaccess_file, 'w' );
2185  
2186          if( !$handle )
2187          { // File cannot be created
2188              return false;
2189          }
2190  
2191          fwrite( $handle, $htaccess_content );
2192          fclose( $handle );
2193      }
2194  
2195      return true;
2196  }
2197  ?>

title

Description

title

Description

title

Description

title

title

Body