Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.124
diff -u -8 -p -r1.124 file.inc
--- includes/file.inc	23 Apr 2008 18:17:41 -0000	1.124
+++ includes/file.inc	28 Apr 2008 20:02:14 -0000
@@ -28,16 +28,57 @@ define('FILE_EXISTS_ERROR', 2);
  *
  * If you wish to add custom statuses for use by contrib modules please expand as
  * binary flags and consider the first 8 bits reserved. (0,1,2,4,8,16,32,64,128)
  */
 define('FILE_STATUS_TEMPORARY', 0);
 define('FILE_STATUS_PERMANENT', 1);
 
 /**
+ * Get canonicalized absolute path of a file or directory. The Windows path
+ * separator "\" is converted to "/", and consecutive "/" characters are stripped.
+ * If path is a directory, the trailing "/" is stripped.
+ * For regular files:
+ * - Symbolic links are expanded.
+ * - "/./" and "/../" segments are resolved.
+ * For paths with protocol/wrapper prefix (e.g. "mywrapper://foo/bar.txt"):
+ * - Paths containing "/../" are blocked (FALSE is returned).
+ * - Paths are assumed to be case-sensitive (no case normalization is done).
+ *
+ * @code
+ *   // Returns "/foo/bar/boo", or FALSE if the file does not exist:
+ *   file_realpath('/foo//bar/./baz/..\\boo');
+ *
+ *   // Returns FALSE due to "/../":
+ *   file_realpath('mywrapper://foo/bar/../baz');
+ * @endcode
+ *
+ * @param $path A string containing a path to a file or directory.
+ * @return A string containing the absolute path to the file/directory,
+ *         or FALSE if file/directory does not exist.
+ */
+function file_realpath($path) {
+  // Does $path include an explicit protocol/wrapper prefix "foo://" (not a
+  // Windows drive letter "C:/temp")?
+  if (preg_match('@^([a-z0-9.+-]{2,})://(.*)@i', $path, $reg)) {
+    // Replace "\" and "//" with "/"
+    $wrappedPath = preg_replace('@[/\\\\]+@', '/', $reg[2]);
+    // Look for ".." separated by "/" or string boundary
+    if (preg_match('@(?<=^|/)\.\.(?=/|$)@', $wrappedPath)) {
+      return FALSE;
+    }
+    $path = $reg[1] . '://' . $wrappedPath;
+    return file_exists($path) ? rtrim($path, '/') : FALSE;
+  }
+  else {
+    return realpath($path);
+  }
+}
+
+/**
  * Create the download path to a file.
  *
  * @param $path A string containing the path of the file to generate URL for.
  * @return A string containing a URL that can be used to download the file.
  */
 function file_create_url($path) {
   // Strip file_directory_path from $path. We only include relative paths in urls.
   if (strpos($path, file_directory_path() . '/') === 0) {
@@ -123,17 +164,17 @@ function file_check_directory(&$director
       return FALSE;
     }
   }
 
   if ((file_directory_path() == $directory || file_directory_temp() == $directory) && !is_file("$directory/.htaccess")) {
     $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks";
     if (($fp = fopen("$directory/.htaccess", 'w')) && fputs($fp, $htaccess_lines)) {
       fclose($fp);
-      chmod($directory . '/.htaccess', 0664);
+      @chmod($directory . '/.htaccess', 0664);
     }
     else {
       $variables = array('%directory' => $directory, '!htaccess' => '<br />' . nl2br(check_plain($htaccess_lines)));
       form_set_error($form_item, t("Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code>", $variables));
       watchdog('security', "Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code>", $variables, WATCHDOG_ERROR);
     }
   }
 
@@ -174,25 +215,25 @@ function file_check_path(&$path) {
  *   file_check_location('/www/example.com/files/../../../etc/passwd', '/www/example.com/files');
  * @endcode
  *
  * @param $source A string set to the file to check.
  * @param $directory A string where the file should be located.
  * @return 0 for invalid path or the real path of the source.
  */
 function file_check_location($source, $directory = '') {
-  $check = realpath($source);
+  $check = file_realpath($source);
   if ($check) {
     $source = $check;
   }
   else {
     // This file does not yet exist
-    $source = realpath(dirname($source)) . '/' . basename($source);
+    $source = file_realpath(dirname($source)) . '/' . basename($source);
   }
-  $directory = realpath($directory);
+  $directory = file_realpath($directory);
   if ($directory && strpos($source, $directory) !== 0) {
     return 0;
   }
   return $source;
 }
 
 /**
  * Copies a file to a new location. This is a powerful function that in many ways
@@ -231,30 +272,30 @@ function file_copy(&$source, $dest = 0, 
   if (is_object($source)) {
     $file = $source;
     $source = $file->filepath;
     if (!$basename) {
       $basename = $file->filename;
     }
   }
 
-  $source = realpath($source);
+  $source = file_realpath($source);
   if (!file_exists($source)) {
     drupal_set_message(t('The selected file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $source)), 'error');
     return 0;
   }
 
   // If the destination file is not specified then use the filename of the source file.
   $basename = $basename ? $basename : basename($source);
   $dest = $directory . '/' . $basename;
 
   // Make sure source and destination filenames are not the same, makes no sense
   // to copy it if they are. In fact copying the file will most likely result in
   // a 0 byte file. Which is bad. Real bad.
-  if ($source != realpath($dest)) {
+  if ($source != file_realpath($dest)) {
     if (!$dest = file_destination($dest, $replace)) {
       drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error');
       return FALSE;
     }
 
     if (!@copy($source, $dest)) {
       drupal_set_message(t('The selected file %file could not be copied.', array('%file' => $source)), 'error');
       return 0;
@@ -746,19 +787,18 @@ function file_validate_image_resolution(
  * @param $replace Replace behavior when the destination file already exists.
  *   - FILE_EXISTS_REPLACE - Replace the existing file
  *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
  *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
  *
  * @return A string containing the resulting filename or 0 on error
  */
 function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
-  $temp = file_directory_temp();
-  // On Windows, tempnam() requires an absolute path, so we use realpath().
-  $file = tempnam(realpath($temp), 'file');
+  // Create temporary file with unique name (tempnam() does not work with stream wrappers)
+  $file = file_directory_temp() . '/file_' . getmypid() . '_' . microtime(true);
   if (!$fp = fopen($file, 'wb')) {
     drupal_set_message(t('The file could not be created.'), 'error');
     return 0;
   }
   fwrite($fp, $data);
   fclose($fp);
 
   if (!file_move($file, $dest, $replace)) {
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.599
diff -u -8 -p -r1.599 system.module
--- modules/system/system.module	23 Apr 2008 20:01:54 -0000	1.599
+++ modules/system/system.module	28 Apr 2008 20:02:15 -0000
@@ -893,16 +893,20 @@ function system_theme_select_form($descr
  * file_directory_path and file_directory_temp directories. If validation
  * fails, the form element is flagged with an error from within the
  * file_check_directory function.
  *
  * @param $form_element
  *   The form element containing the name of the directory to check.
  */
 function system_check_directory($form_element) {
+  // "file://" prefix is redundant
+  if (strncasecmp($form_element['#value'], 'file://', 7) == 0) {
+    $form_element['#value'] = substr($form_element['#value'], 7);
+  }
   file_check_directory($form_element['#value'], FILE_CREATE_DIRECTORY, $form_element['#parents'][0]);
   return $form_element;
 }
 
 /**
  * Retrieves the current status of an array of files in the system table.
  *
  * @param $files
