b2evolution PHP Cross Reference Blogging Systems

Source: /htsrv/getfile.php - 260 lines - 9951 bytes - Summary - Text - Print

Description: This file implements the File view (including resizing of images) 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 the File view (including resizing of images)
   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)2005-2006 by PROGIDISTRI - {@link http://progidistri.com/}.
  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   * PROGIDISTRI S.A.S. grants Francois PLANQUE the right to license
  22   * PROGIDISTRI S.A.S.'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 htsrv
  27   *
  28   * @todo dh> Add support for ETag / If-Modified-Since. Maybe send "Expires", too? (to "force" caching)
  29   *       fp> for more efficient caching (like creating a thumbnail on view 1 then displaying the thumbnail again on view 2), this should probably redirect to the static file right after creating it (when $public_access_to_media=true OF COURSE)
  30   *       dh> this would add another redirect/HTTP request and no cache handling, assuming
  31   *           that the server is not configured for smart caching.
  32   *           Additionally, it does not help for non-public access, which is the meat of this file.
  33   *           I've added "Expires: in ten years" now, but not for thumbs (see comment there).
  34   *
  35   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  36   * @author blueyed: Daniel HAHLER
  37   * @author fplanque: Francois PLANQUE.
  38   * @author mbruneau: Marc BRUNEAU / PROGIDISTRI
  39   */
  40  
  41  
  42  /**
  43   * Load config, init and get the {@link $mode mode param}.
  44   */
  45  require_once dirname(__FILE__).'/../conf/_config.php';
  46  require_once $inc_path.'/_main.inc.php';
  47  
  48  if( ! isset($GLOBALS['files_Module']) )
  49  {
  50      debug_die( 'Files module is disabled or missing!' );
  51  }
  52  
  53  // We need this param early to check blog perms, if possible
  54  param( 'root', 'string', true ); // the root directory from the dropdown box (user_X or blog_X; X is ID - 'user' for current user (default))
  55  
  56  // Check permission:
  57  if( ! $public_access_to_media )
  58  {
  59      if( ! isset($current_User) )
  60      {
  61          debug_die( 'No permission to get file (not logged in)!', array('status'=>'403 Forbidden') );
  62      }
  63  
  64  
  65      // fp> I don't think we need the following if public_access_to_media
  66      if( preg_match( '/^collection_(\d+)$/', $root, $perm_blog ) )
  67      {    // OK, we got a blog ID:
  68          $perm_blog = $perm_blog[1];
  69      }
  70      else
  71      {    // No blog ID, we will check the global group perm
  72          $perm_blog = NULL;
  73      }
  74      //pre_dump( $perm_blog );
  75  
  76      // Check permission (#2):
  77      $current_User->check_perm( 'files', 'view', true, $perm_blog );
  78  }
  79  
  80  // Load the other params:
  81  param( 'path', 'string', true );
  82  param( 'size', 'string', NULL );    // Can be used for images.
  83  param( 'mtime', 'integer', 0 );     // used for unique URLs (that never expire).
  84  
  85  // TODO: dh> this failed with filenames containing multiple dots!
  86  if ( false !== strpos( urldecode( $path ), '..' ) )
  87  // TODO: dh> fix this better. by adding is_relative_path()?
  88  // fp> the following doesn't look secure. I can't take the risk. What if the path ends with or is just '..' ? I don't want to allow this to go through.
  89  // if( preg_match( '~\.\.[/\\\]~', urldecode( $path ) ) )
  90  {
  91      debug_die( 'Relative pathnames not allowed!' );
  92  }
  93  
  94  // Load fileroot info:
  95  $FileRootCache = & get_FileRootCache();
  96  $FileRoot = & $FileRootCache->get_by_ID( $root );
  97  
  98  // Load file object (not the file content):
  99  $File = new File( $FileRoot->type, $FileRoot->in_type_ID, $path );
 100  
 101  // Check if the request has an If-Modified-Since date
 102  if( array_key_exists( 'HTTP_IF_MODIFIED_SINCE', $_SERVER) )
 103  {
 104      $if_modified_since = strtotime( preg_replace('/;.*$/','',$_SERVER['HTTP_IF_MODIFIED_SINCE']) );
 105      $file_lastmode_ts = $File->get_lastmod_ts();
 106      if( $file_lastmode_ts <= $if_modified_since )
 107      { // file was not modified since if_modified_since ts
 108          header_http_response( '304 Not Modified' );
 109          exit(0);
 110      }
 111  }
 112  
 113  if( !empty($size) && $File->is_image() )
 114  {    // We want a thumbnail:
 115      // fp> TODO: for more efficient caching, this should probably redirect to the static file right after creating it (when $public_access_to_media=true OF COURSE)
 116  
 117      global $thumbnail_sizes;
 118  
 119      load_funcs( '/files/model/_image.funcs.php' );
 120  
 121      $size_name = $size;
 122      if( ! isset($thumbnail_sizes[$size] ) )
 123      { // this file size alias is not defined, use default:
 124          // TODO: dh> this causes links for e.g. "fit-50x50" to work also, but with the drawback of images not getting served from the
 125          //           .evocache directory directly. I think invalid $size params should bark out here.
 126          // fp> ok.
 127          $size_name = 'fit-80x80';
 128      }
 129  
 130      if( ! isset ( $thumbnail_sizes[$size_name][4] ) )
 131      {    // Set blur percent in 0 by default
 132          $thumbnail_sizes[$size_name][4] = 0;
 133      }
 134      // Set all params for requested size:
 135      list( $thumb_type, $thumb_width, $thumb_height, $thumb_quality, $thumb_percent_blur ) = $thumbnail_sizes[$size_name];
 136  
 137      $Filetype = & $File->get_Filetype();
 138      // pre_dump( $Filetype );
 139      // TODO: dh> Filetype may be NULL here! see also r1.18 (IIRC)
 140      $mimetype = $Filetype->mimetype;
 141      // pre_dump( $mimetype );
 142  
 143      // Try to output the cached thumbnail:
 144      $err = $File->output_cached_thumb( $size_name, $mimetype, $mtime );
 145      //pre_dump( $err );
 146  
 147      if( $err == '!Thumbnail not found in'.$Settings->get( 'evocache_foldername' ) )
 148      {    // The thumbnail wasn't already in the cache, try to generate and cache it now:
 149          $err = NULL;        // Short error code
 150  
 151          list( $src_width, $src_height ) = imgsize( $File->get_full_path() );
 152  
 153          if( ! $resample_all_images && $src_width <= $thumb_width && $src_height <= $thumb_height )
 154          {    // There is no need to resample, use original!
 155              $err = $File->get_af_thumb_path( $size_name, $mimetype, true );
 156  
 157              if( $err[0] != '!' && @copy( $File->get_full_path(), $err ) )
 158              {    // File was saved. Ouput that same file immediately:
 159                  // note: @copy returns FALSE on failure, if not muted it'll print the error on screen
 160                  $err = $File->output_cached_thumb( $size_name, $mimetype, $mtime );
 161              }
 162          }
 163          else
 164          {    // Resample
 165              list( $err, $src_imh ) = load_image( $File->get_full_path(), $mimetype );
 166  
 167              if( empty( $err ) )
 168              {
 169                  list( $err, $dest_imh ) = generate_thumb( $src_imh, $thumb_type, $thumb_width, $thumb_height, $thumb_percent_blur );
 170                  if( empty( $err ) )
 171                  {
 172                      $err = $File->save_thumb_to_cache( $dest_imh, $size_name, $mimetype, $thumb_quality );
 173                      if( empty( $err ) )
 174                      {    // File was saved. Ouput that same file immediately:
 175                          // This is probably better than recompressing the memory image..
 176                          $err = $File->output_cached_thumb( $size_name, $mimetype, $mtime );
 177                      }
 178                      else
 179                      {    // File could not be saved.
 180                          // fp> We might want to output dynamically...
 181                          // $err = output_image( $dest_imh, $mimetype );
 182                      }
 183                  }
 184              }
 185          }
 186      }
 187  
 188      // ERROR IMAGE
 189      if( !empty( $err ) )
 190      {    // Generate an error image and try to squeeze an error message inside:
 191          // Note: we write small and close to the upper left in order to have as much text as possible on small thumbs
 192          $line_height = 11;
 193          $err = substr( $err, 1 ); // crop 1st car
 194          $car_width = ceil( ($thumb_width-4)/6 );
 195          // $err = 'w='.$car_width.' '.$err;
 196  
 197          // Wrap error message and split it into lines:
 198          $err_lines = preg_split( '~\n~', wordwrap( $err, $car_width, "\n", true ) );
 199          $im_handle = imagecreatetruecolor( $thumb_width, $thumb_height ); // Create a black image
 200          if( count($err_lines)*$line_height > $thumb_height )
 201          { // Message does not fit into picture:
 202            // Rewrite error messages, so they fit better into the generated images.
 203              $rewritten = true;
 204              if( preg_match('~Unable to open \'.*?\' for writing: Permission denied~', $err) )
 205                  $err = 'Cannot write: permission denied';
 206              else
 207                  $rewritten = false;
 208              // Recreate error lines, if it has been rewritten/shortened.
 209              if( $rewritten )
 210              {
 211                  $err_lines = preg_split( '~\n~', wordwrap( $err, $car_width, "\n", true ) );
 212              }
 213          }
 214  
 215          $text_color = imagecolorallocate( $im_handle, 255, 0, 0 );
 216          $y = 0;
 217          foreach( $err_lines as $err_string )
 218          {
 219              imagestring( $im_handle, 2, 2, $y, $err_string, $text_color);
 220              $y += $line_height;
 221          }
 222  
 223          header('Content-type: image/png' );
 224          header_nocache();    // Do NOT cache errors! People won't see they have fixed them!!
 225  
 226          imagepng( $im_handle );
 227      }
 228  }
 229  else
 230  {    // We want the regular file:
 231      // Headers to display the file directly in the browser
 232      if( ! is_readable($File->get_full_path()) )
 233      {
 234          debug_die( sprintf('File "%s" is not readable!', rel_path_to_base($File->get_full_path())) );
 235      }
 236  
 237      $Filetype = & $File->get_Filetype();
 238      if( ! empty($Filetype) )
 239      {
 240          header('Content-type: '.$Filetype->mimetype );
 241          if( $Filetype->viewtype == 'download' )
 242          {
 243              header('Content-disposition: attachment; filename="'
 244                  .addcslashes($File->get_name(), '\\"').'"' ); // escape quotes and slashes, according to RFC
 245          }
 246      }
 247      $file_path = $File->get_full_path();
 248      header('Content-Length: '.filesize( $file_path ) );
 249  
 250      // The URL refers to this specific file, therefore we can tell the browser that
 251      // it does not expire anytime soon.
 252      // fp> I don't think mtime changes anything to the cacheability of the data
 253      // if( $mtime && $mtime == $File->get_lastmod_ts() ) // TODO: dh> use salt here?! fp>what for?
 254      header_noexpire();    // static file
 255  
 256      // Display the content of the file
 257      readfile( $file_path );
 258  }
 259  
 260  ?>

title

Description

title

Description

title

Description

title

title

Body