Alternate scaling using GD is implemented now; images will be scaled with ImageMagick if enabled and working; else, GD will be used. No UI changes to speak of, but a check in the installer will be added in a later commit
authorDan
Sat, 03 Nov 2007 14:15:14 -0400
changeset 230 3daa715e0f69
parent 224 6a4573507ff8
child 231 b11a2f1353c0
child 232 4cc0736ca168
Alternate scaling using GD is implemented now; images will be scaled with ImageMagick if enabled and working; else, GD will be used. No UI changes to speak of, but a check in the installer will be added in a later commit
includes/functions.php
includes/template.php
install.php
plugins/SpecialUpdownload.php
--- a/includes/functions.php	Fri Nov 02 15:38:20 2007 -0400
+++ b/includes/functions.php	Sat Nov 03 14:15:14 2007 -0400
@@ -3182,6 +3182,169 @@
   $cron_tasks[$hour_interval][] = $func;
 }
 
+/**
+ * Scales an image to the specified width and height, and writes the output to the specified
+ * file. Will use ImageMagick if present, but if not will attempt to scale with GD. This will
+ * always scale images proportionally.
+ * @param string Path to image file
+ * @param string Path to output file
+ * @param int Image width, in pixels
+ * @param int Image height, in pixels
+ * @param bool If true, the output file will be deleted if it exists before it is written
+ * @return bool True on success, false on failure
+ */
+
+function scale_image($in_file, $out_file, $width = 225, $height = 225, $unlink = false)
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  
+  if ( !is_int($width) || !is_int($height) )
+    return false;
+  
+  if ( !file_exists($in_file) )
+    return false;
+  
+  if ( preg_match('/["\'\/\\]/', $in_file) || preg_match('/["\'\/\\]/', $out_file) )
+    die('SECURITY: scale_image(): infile or outfile path is screwy');
+  
+  if ( file_exists($out_file) && !$unlink )
+    return false;
+  else if ( file_exists($out_file) && $unlink )
+    @unlink($out_file);
+  if ( file_exists($out_file) )
+    // couldn't unlink (delete) the output file
+    return false;
+    
+  $file_ext = substr($in_file, ( strrpos($in_file, '.') + 1));
+  switch($file_ext)
+  {
+    case 'png':
+      $func = 'imagecreatefrompng';
+      break;
+    case 'jpg':
+    case 'jpeg':
+      $func = 'imagecreatefromjpeg';
+      break;
+    case 'gif':
+      $func = 'imagecreatefromgif';
+      break;
+    case 'xpm':
+      $func = 'imagecreatefromxpm';
+      break;
+    default:
+      return false;
+  }
+    
+  $magick_path = getConfig('imagemagick_path');
+  $can_use_magick = (
+      getConfig('enable_imagemagick') == '1' &&
+      file_exists($magick_path)              &&
+      is_executable($magick_path)
+    );
+  $can_use_gd = (
+      function_exists('getimagesize')         &&
+      function_exists('imagecreatetruecolor') &&
+      function_exists('imagecopyresampled')   &&
+      function_exists($func)
+    );
+  if ( $can_use_magick )
+  {
+    if ( !preg_match('/^([\/A-z0-9_-]+)$/', $magick_path) )
+    {
+      die('SECURITY: ImageMagick path is screwy');
+    }
+    $cmdline = "$magick_path \"$in_file\" -resize \"{$width}x{$height}>\" \"$out_file\"";
+    system($cmdline, $return);
+    if ( !file_exists($out_file) )
+      return false;
+    return true;
+  }
+  else if ( $can_use_gd )
+  {
+    @list($width_orig, $height_orig) = @getimagesize($in_file);
+    if ( !$width_orig || !$height_orig )
+      return false;
+    // calculate new width and height
+    
+    $ratio = $width_orig / $height_orig;
+    if ( $ratio > 1 )
+    {
+      // orig. width is greater that height
+      $new_width = $width;
+      $new_height = round( $width / $ratio );
+    }
+    else if ( $ratio < 1 )
+    {
+      // orig. height is greater than width
+      $new_width = round( $height / $ratio );
+      $new_height = $height;
+    }
+    else if ( $ratio == 1 )
+    {
+      $new_width = $width;
+      $new_height = $width;
+    }
+    if ( $new_width > $width_orig || $new_height > $height_orig )
+    {
+      // Too big for our britches here; set it to only convert the file
+      $new_width = $width_orig;
+      $new_height = $height_orig;
+    }
+    
+    $newimage = @imagecreatetruecolor($new_width, $new_height);
+    if ( !$newimage )
+      return false;
+    $oldimage = @$func($in_file);
+    if ( !$oldimage )
+      return false;
+    
+    // Perform scaling
+    imagecopyresampled($newimage, $oldimage, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
+    
+    // Get output format
+    $out_ext = substr($out_file, ( strrpos($out_file, '.') + 1));
+    switch($out_ext)
+    {
+      case 'png':
+        $outfunc = 'imagepng';
+        break;
+      case 'jpg':
+      case 'jpeg':
+        $outfunc = 'imagejpeg';
+        break;
+      case 'gif':
+        $outfunc = 'imagegif';
+        break;
+      case 'xpm':
+        $outfunc = 'imagexpm';
+        break;
+      default:
+        imagedestroy($newimage);
+        imagedestroy($oldimage);
+        return false;
+    }
+    
+    // Write output
+    $outfunc($newimage, $out_file);
+    
+    // clean up
+    imagedestroy($newimage);
+    imagedestroy($oldimage);
+    
+    // done!
+    return true;
+  }
+  // Neither scaling method worked; we'll let plugins try to scale it, and then if the file still doesn't exist, die
+  $code = $plugins->setHook('scale_image_failure');
+  foreach ( $code as $cmd )
+  {
+    eval($cmd);
+  }
+  if ( file_exists($out_file) )
+    return true;
+  return false;
+}
+
 //die('<pre>Original:  01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'</pre>');
 
 ?>
--- a/includes/template.php	Fri Nov 02 15:38:20 2007 -0400
+++ b/includes/template.php	Sat Nov 03 14:15:14 2007 -0400
@@ -1413,7 +1413,9 @@
     // $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?) ([^\]]+)\\]#', '<a href="\\1://\\2">\\3</a><br style="display: none;" />', $message);
     // $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?)\\]#', '<a href="\\1://\\2">\\1://\\2</a><br style="display: none;" />', $message);
     
-    preg_match_all('/\[((https?|ftp|irc):\/\/([^@\]"\':]+)?((([a-z0-9-]+\.)*)[a-z0-9-]+)(\/[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]*(\?(([a-z0-9_-]+)(=[A-z0-9_%\|~`\!@#\$\^&\*\(\):;\.,\/-\[\]]+)?((&([a-z0-9_-]+)(=[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]+)?)*))?)?)?) ([^\]]+)\]/is', $message, $ext_link);
+    preg_match_all('/\[((https?|ftp|irc):\/\/([^@\s\]"\':]+)?((([a-z0-9-]+\.)*)[a-z0-9-]+)(\/[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]*(\?(([a-z0-9_-]+)(=[A-z0-9_%\|~`\!@#\$\^&\*\(\):;\.,\/-\[\]]+)?((&([a-z0-9_-]+)(=[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]+)?)*))?)?)?) ([^\]]+)\]/is', $message, $ext_link);
+    
+    // die('<pre>' . htmlspecialchars( print_r($ext_link, true) ) . '</pre>');
     
     for ( $i = 0; $i < count($ext_link[0]); $i++ )
     {
@@ -1425,7 +1427,7 @@
       $message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
     }
     
-    preg_match_all('/\[((https?|ftp|irc):\/\/([^@\]"\':]+)?((([a-z0-9-]+\.)*)[a-z0-9-]+)(\/[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]*(\?(([a-z0-9_-]+)(=[A-z0-9_%\|~`\!@#\$\^&\*\(\):;\.,\/-\[\]]+)?((&([a-z0-9_-]+)(=[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]+)?)*))?)?)?)\]/is', $message, $ext_link);
+    preg_match_all('/\[((https?|ftp|irc):\/\/([^@\s\]"\':]+)?((([a-z0-9-]+\.)*)[a-z0-9-]+)(\/[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]*(\?(([a-z0-9_-]+)(=[A-z0-9_%\|~`\!@#\$\^&\*\(\):;\.,\/-\[\]]+)?((&([a-z0-9_-]+)(=[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]+)?)*))?)?)?)\]/is', $message, $ext_link);
     
     for ( $i = 0; $i < count($ext_link[0]); $i++ )
     {
--- a/install.php	Fri Nov 02 15:38:20 2007 -0400
+++ b/install.php	Sat Nov 03 14:15:14 2007 -0400
@@ -665,7 +665,7 @@
     break;
   case "website":
     if(!isset($_POST['_cont'])) {
-      echo 'No POST data signature found. Please <a href="install.php?mode=license">restart the installation</a>.';
+      echo 'No POST data signature found. Please <a href="install.php?mode=sysreqs">restart the installation</a>.';
       $template->footer();
       exit;
     }
@@ -736,7 +736,7 @@
     break;
   case "login":
     if(!isset($_POST['_cont'])) {
-      echo 'No POST data signature found. Please <a href="install.php?mode=license">restart the installation</a>.';
+      echo 'No POST data signature found. Please <a href="install.php?mode=sysreqs">restart the installation</a>.';
       $template->footer();
       exit;
     }
@@ -766,7 +766,9 @@
       {
         var frm = document.forms.login;
         ret = true;
-        if ( frm.admin_user.value.match(/^([A-z0-9 \-\.]+)$/g) && !frm.admin_user.value.match(/^(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/) && frm.admin_user.value.toLowerCase() != 'anonymous' )
+        var ip_regexp = new RegExp('^(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$', '');
+        var valid_username = new RegExp('^([A-z0-9 \-\.]+)$', '');
+        if ( frm.admin_user.value.match(valid_username) && !frm.admin_user.value.match(ip_regexp) && frm.admin_user.value.toLowerCase() != 'anonymous' )
         {
           document.getElementById('s_user').src = 'images/good.gif';
         }
@@ -784,7 +786,8 @@
           document.getElementById('s_password').src = 'images/bad.gif';
           ret = false;
         }
-        if(frm.admin_email.value.match(/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/))
+        var valid_email = new RegExp('^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$', '');
+        if(frm.admin_email.value.match(valid_email))
         {
           document.getElementById('s_email').src = 'images/good.gif';
         }
@@ -920,7 +923,7 @@
     break;
   case "confirm":
     if(!isset($_POST['_cont'])) {
-      echo 'No POST data signature found. Please <a href="install.php?mode=license">restart the installation</a>.';
+      echo 'No POST data signature found. Please <a href="install.php?mode=sysreqs">restart the installation</a>.';
       $template->footer();
       exit;
     }
@@ -970,7 +973,7 @@
        !isset($_POST['urlscheme'])
        )
     {
-      echo 'The installer has detected that one or more required form values is not set. Please <a href="install.php?mode=license">restart the installation</a>.';
+      echo 'The installer has detected that one or more required form values is not set. Please <a href="install.php?mode=sysreqs">restart the installation</a>.';
       $template->footer();
       exit;
     }
@@ -989,6 +992,9 @@
     }
     function err($t) { global $template; echo $t; $template->footer(); exit; }
     
+    if ( !preg_match('/^[a-z0-9_]*$/', $_POST['table_prefix']) )
+      err('Hacking attempt was detected in table_prefix.');
+    
       echo 'Connecting to MySQL...';
       if($_POST['db_root_user'] != '')
       {
@@ -1138,12 +1144,13 @@
 $dbname   = \''.addslashes($_POST['db_name']).'\';
 $dbuser   = \''.addslashes($_POST['db_user']).'\';
 $dbpasswd = \''.addslashes($_POST['db_pass']).'\';
-if(!defined(\'ENANO_CONSTANTS\')) {
-define(\'ENANO_CONSTANTS\', \'\');
-define(\'table_prefix\', \''.$_POST['table_prefix'].'\');
-define(\'scriptPath\', \''.scriptPath.'\');
-define(\'contentPath\', \''.$cp.'\');
-define(\'ENANO_INSTALLED\', \'true\');
+if ( !defined(\'ENANO_CONSTANTS\') )
+{
+  define(\'ENANO_CONSTANTS\', \'\');
+  define(\'table_prefix\', \''.addslashes($_POST['table_prefix']).'\');
+  define(\'scriptPath\', \''.scriptPath.'\');
+  define(\'contentPath\', \''.$cp.'\');
+  define(\'ENANO_INSTALLED\', \'true\');
 }
 $crypto_key = \''.$privkey.'\';
 ?>';
--- a/plugins/SpecialUpdownload.php	Fri Nov 02 15:38:20 2007 -0400
+++ b/plugins/SpecialUpdownload.php	Sat Nov 03 14:15:14 2007 -0400
@@ -235,45 +235,73 @@
   }
   
   $fname = ENANO_ROOT . '/files/' . $row['file_key'] . '_' . $row['time_id'] . $row['file_extension'];
-  $data = file_get_contents($fname);
-  if(isset($_GET['preview']) && getConfig('enable_imagemagick')=='1' && file_exists(getConfig('imagemagick_path')) && substr($row['mimetype'], 0, 6) == 'image/')
+  
+  if ( isset($_GET['preview']) && substr($row['mimetype'], 0, 6) == 'image/' )
   {
-    $nam = tempnam('/tmp', $filename);
-    $h = @fopen($nam, 'w');
-    if(!$h) die('Error opening '.$nam.' for writing');
-    fwrite($h, $data);
-    fclose($h);
-    /* Make sure the request doesn't contain commandline injection - yow! */
-    if(!isset($_GET['width' ]) || (isset($_GET['width'] ) && !preg_match('#^([0-9]+)$#', $_GET['width']  ))) $width  = '320'; else $width  = $_GET['width' ];
-    if(!isset($_GET['height']) || (isset($_GET['height']) && !preg_match('#^([0-9]+)$#', $_GET['height'] ))) $height = '240'; else $height = $_GET['height'];
-    $cache_filename=ENANO_ROOT.'/cache/'.$filename.'-'.$row['time_id'].'-'.$width.'x'.$height.$row['file_extension'];
-    if(getConfig('cache_thumbs')=='1' && file_exists($cache_filename) && is_writable(ENANO_ROOT.'/cache')) {
-      $data = file_get_contents($cache_filename);
-    } elseif(getConfig('enable_imagemagick')=='1' && file_exists(getConfig('imagemagick_path'))) {
-      // Use ImageMagick to convert the image
-      //unlink($nam);
-      error_reporting(E_ALL);
-      $cmd = ''.getConfig('imagemagick_path').' "'.$nam.'" -resize "'.$width.'x'.$height.'>" "'.$nam.'.scaled'.$row['file_extension'].'"';
-      system($cmd, $stat);
-      if(!file_exists($nam.'.scaled'.$row['file_extension'])) die('Failed to call ImageMagick (return value '.$stat.'), command line was:<br />'.$cmd);
-      $data = file_get_contents($nam.'.scaled'.$row['file_extension']);
-      // Be stingy about it - better to re-generate the image hundreds of times than to fail completely
-      if(getConfig('cache_thumbs')=='1' && !file_exists($cache_filename)) {
-        // Write the generated thumbnail to the cache directory
-        $h = @fopen($cache_filename, 'w');
-        if(!$h) die('Error opening cache file "'.$cache_filename.'" for writing.');
-        fwrite($h, $data);
-        fclose($h);
+    // Determine appropriate width and height
+    $width  = ( isset($_GET['width'])  ) ? intval($_GET['width'] ) : 320;
+    $height = ( isset($_GET['height']) ) ? intval($_GET['height']) : 320;
+    $cache_filename = ENANO_ROOT . "/cache/{$filename}-{$row['time_id']}-{$width}x{$height}{$row['file_extension']}";
+    if ( file_exists($cache_filename) )
+    {
+      $fname = $cache_filename;
+    }
+    else
+    {
+      $allow_scale = false;
+      $orig_fname = $fname;
+      // is caching enabled?
+      if ( getConfig('cache_thumbs') == '1' )
+      {
+        $fname = $cache_filename;
+        if ( is_writeable(dirname($fname)) )
+        {
+          $allow_scale = true;
+        }
+      }
+      else
+      {
+        // Get a temporary file
+        // In this case, the file will not be cached and will be scaled each time it's requested
+        $temp_dir = ( is_dir('/tmp') ) ? '/tmp' : ( isset($_ENV['TEMP']) ) ? $_ENV['TEMP'] : 'SOME RANDOM NAME';
+        // if tempnam() cannot use the specified directory name, it will fall back on the system default
+        $tempname = tempnam($temp_dir, $filename);
+        if ( $tempname && is_writeable($tempname) )
+        {
+          $allow_scale = true;
+        }
+      }
+      if ( $allow_scale )
+      {
+        $result = scale_image($orig_fname, $fname, $width, $height);
+        if ( !$result )
+          $fname = $orig_fname;
+      }
+      else
+      {
+        $fname = $orig_fname;
       }
     }
-    unlink($nam);
   }
-  $len = strlen($data);
+  $handle = @fopen($fname, 'r');
+  if ( !$handle )
+    die('Can\'t open output file for reading');
+  
+  $len = filesize($fname);
   header('Content-type: '.$row['mimetype']);
-  if(isset($_GET['download'])) header('Content-disposition: attachment, filename="'.$filename.'";');
+  if ( isset($_GET['download']) )
+  {
+    header('Content-disposition: attachment, filename="' . $filename . '";');
+  }
   header('Content-length: '.$len);
   header('Last-Modified: '.date('r', $row['time_id']));
-  echo($data);
+  
+  // using this method limits RAM consumption
+  while ( !feof($handle) )
+  {
+    echo fread($handle, 512000);
+  }
+  fclose($handle);
   
   gzip_output();