? cats-big.jpg
? cats.jpg
? docs
? gravatar_check.php
? log.txt
? sites/mysql.drupal6.local
? sites/pgsql.drupal6.local
? sites/all/cats-big.jpg
? sites/all/cats.jpg
? sites/all/themes
Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.121.2.5
diff -u -p -r1.121.2.5 file.inc
--- includes/file.inc	20 Oct 2008 09:42:31 -0000	1.121.2.5
+++ includes/file.inc	20 Jan 2009 14:40:16 -0000
@@ -451,11 +451,10 @@ function file_space_used($uid = NULL) {
 }
 
 /**
- * Saves a file upload to a new location. The source file is validated as a
- * proper upload and handled as such.
+ * Saves a file upload to a new location.
  *
- * The file will be added to the files table as a temporary file. Temporary files
- * are periodically cleaned. To make the file permanent file call
+ * The file will be added to the files table as a temporary file. Temporary
+ * files are periodically cleaned. To make the file permanent file call
  * file_set_status() to change its status.
  *
  * @param $source
@@ -467,7 +466,7 @@ function file_space_used($uid = NULL) {
  *   functions should return an array of error messages, an empty array
  *   indicates that the file passed validation. The functions will be called in
  *   the order specified.
- * @param $dest
+ * @param $destination
  *   A string containing the directory $source should be copied to. If this is
  *   not provided or is not writable, the temporary directory will be used.
  * @param $replace
@@ -475,117 +474,139 @@ function file_space_used($uid = NULL) {
  *   destination directory should overwritten. A false value will generate a
  *   new, unique filename in the destination directory.
  * @return
- *   An object containing the file information, or 0 in the event of an error.
+ *   An object containing the file information if the upload succeeded, FALSE
+ *   in the event of an error, or NULL if no file was uploaded.
  */
-function file_save_upload($source, $validators = array(), $dest = FALSE, $replace = FILE_EXISTS_RENAME) {
+function file_save_upload($source, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
   global $user;
   static $upload_cache;
 
-  // Add in our check of the the file name length.
-  $validators['file_validate_name_length'] = array();
-
   // Return cached objects without processing since the file will have
   // already been processed and the paths in _FILES will be invalid.
   if (isset($upload_cache[$source])) {
     return $upload_cache[$source];
   }
 
-  // If a file was uploaded, process it.
-  if (isset($_FILES['files']) && $_FILES['files']['name'][$source] && is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
-    // Check for file upload errors and return FALSE if a
-    // lower level system error occurred.
-    switch ($_FILES['files']['error'][$source]) {
-      // @see http://php.net/manual/en/features.file-upload.errors.php
-      case UPLOAD_ERR_OK:
-        break;
-
-      case UPLOAD_ERR_INI_SIZE:
-      case UPLOAD_ERR_FORM_SIZE:
-        drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size()))), 'error');
-        return 0;
-
-      case UPLOAD_ERR_PARTIAL:
-      case UPLOAD_ERR_NO_FILE:
-        drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error');
-        return 0;
-
-        // Unknown error
-      default:
-        drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error');
-        return 0;
-    }
-
-    // Build the list of non-munged extensions.
-    // @todo: this should not be here. we need to figure out the right place.
-    $extensions = '';
-    foreach ($user->roles as $rid => $name) {
-      $extensions .= ' '. variable_get("upload_extensions_$rid",
-      variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
-    }
-
-    // Begin building file object.
-    $file = new stdClass();
-    $file->filename = file_munge_filename(trim(basename($_FILES['files']['name'][$source]), '.'), $extensions);
-    $file->filepath = $_FILES['files']['tmp_name'][$source];
-    $file->filemime = file_get_mimetype($file->filename);
-
-    // Rename potentially executable files, to help prevent exploits.
-    if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
-      $file->filemime = 'text/plain';
-      $file->filepath .= '.txt';
-      $file->filename .= '.txt';
-    }
-
-    // If the destination is not provided, or is not writable, then use the
-    // temporary directory.
-    if (empty($dest) || file_check_path($dest) === FALSE) {
-      $dest = file_directory_temp();
-    }
-
-    $file->source = $source;
-    $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace);
-    $file->filesize = $_FILES['files']['size'][$source];
-
-    // Call the validation functions.
-    $errors = array();
-    foreach ($validators as $function => $args) {
-      array_unshift($args, $file);
-      $errors = array_merge($errors, call_user_func_array($function, $args));
-    }
-
-    // Check for validation errors.
-    if (!empty($errors)) {
-      $message = t('The selected file %name could not be uploaded.', array('%name' => $file->filename));
-      if (count($errors) > 1) {
-        $message .= '<ul><li>'. implode('</li><li>', $errors) .'</li></ul>';
+  // Make sure there's an upload to process.
+  if (empty($_FILES['files']['name'][$source])) {
+    return NULL;
+  }
+
+  // Check for file upload errors and return FALSE if a lower level system
+  // error occurred. For a complete list of errors:
+  // @see http://php.net/manual/en/features.file-upload.errors.php
+  switch ($_FILES['files']['error'][$source]) {
+    case UPLOAD_ERR_INI_SIZE:
+    case UPLOAD_ERR_FORM_SIZE:
+      drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size()))), 'error');
+      return FALSE;
+
+    case UPLOAD_ERR_PARTIAL:
+    case UPLOAD_ERR_NO_FILE:
+      drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error');
+      return FALSE;
+
+    case UPLOAD_ERR_OK:
+      // Final check that this is a valid upload, if it isn't, use the
+      // default error handler.
+      if (is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
+         break;
       }
-      else {
-        $message .= ' '. array_pop($errors);
-      }
-      form_set_error($source, $message);
-      return 0;
+
+    // Unknown error
+    default:
+      drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error');
+      return FALSE;
+  }
+
+  // Build the list of non-munged extensions.
+  // @todo: this should not be here. we need to figure out the right place.
+  $extensions = '';
+  foreach ($user->roles as $rid => $name) {
+    $extensions .= ' ' . variable_get("upload_extensions_$rid",
+    variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'));
+  }
+
+  // Begin building file object.
+  $file = new stdClass();
+  $file->uid = $user->uid;
+  $file->status = FILE_STATUS_TEMPORARY;
+  $file->timestamp = time();
+  $file->filename = file_munge_filename(trim(basename($_FILES['files']['name'][$source]), '.'), $extensions);
+  $file->filepath = $_FILES['files']['tmp_name'][$source];
+  $file->filemime = file_get_mimetype($file->filename);
+  $file->filesize = $_FILES['files']['size'][$source];
+
+  // Rename potentially executable files, to help prevent exploits.
+  if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
+    $file->filemime = 'text/plain';
+    $file->filepath .= '.txt';
+    $file->filename .= '.txt';
+  }
+
+  // If the destination is not provided, or is not writable, then use the
+  // temporary directory.
+  if (empty($destination) || file_check_path($destination) === FALSE) {
+    $destination = file_directory_temp();
+  }
+
+  $file->source = $source;
+  $file->destination = file_destination(file_create_path($destination .'/'. $file->filename), $replace);
+  // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
+  // there's an existing file so we need to bail.
+  if ($file->destination === FALSE) {
+    return FALSE;
+  }
+
+  // Add in our check of the the file name length.
+  $validators['file_validate_name_length'] = array();
+
+  // Call the validation functions.
+  $errors = array();
+  foreach ($validators as $function => $args) {
+    array_unshift($args, $file);
+    $errors = array_merge($errors, call_user_func_array($function, $args));
+  }
+
+  // Check for errors.
+  if (!empty($errors)) {
+    $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename));
+    if (count($errors) > 1) {
+      $message .= theme('item_list', $errors);
     }
+    else {
+      $message .= ' '. array_pop($errors);
+    }
+    form_set_error($source, $message);
+    return FALSE;
+  }
 
-    // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary directory.
-    // This overcomes open_basedir restrictions for future file operations.
-    $file->filepath = $file->destination;
-    if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) {
-      form_set_error($source, t('File upload error. Could not move uploaded file.'));
-      watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->filepath));
-      return 0;
-    }
-
-    // If we made it this far it's safe to record this file in the database.
-    $file->uid = $user->uid;
-    $file->status = FILE_STATUS_TEMPORARY;
-    $file->timestamp = time();
-    drupal_write_record('files', $file);
+  // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
+  // directory. This overcomes open_basedir restrictions for future file
+  // operations.
+  $file->filepath = $file->destination;
+  if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) {
+    form_set_error($source, t('File upload error. Could not move uploaded file.'));
+    watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->filepath));
+    return FALSE;
+  }
 
+  // If we are replacing an existing file re-use its database record.
+  if ($replace == FILE_EXISTS_REPLACE) {
+    $existing_files = file_load_multiple(array(), array('filepath' => $file->filepath));
+    if (count($existing_files)) {
+      $existing = reset($existing_files);
+      $file->fid = $existing->fid;
+    }
+  }
+
+  // If we made it this far it's safe to record this file in the database.
+  if (drupal_write_record('files', $file)) {
     // Add file to the cache.
     $upload_cache[$source] = $file;
     return $file;
   }
-  return 0;
+  return FALSE;
 }
 
 /**
