From 0549a3b1aef5e882da1b458a4ff39ecf6ab5d771 Mon Sep 17 00:00:00 2001
From: boombatower <jimmy@boombatower.com>
Date: Fri, 18 Oct 2013 06:56:51 +0000
Subject: Issue #2114885 by boombatower: Modify drupal_move_uploaded_file() to
 check is_uploaded_file(), copy(), and unlink() the file to properly support
 stream wrappers.

---
 core/includes/file.inc | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/core/includes/file.inc b/core/includes/file.inc
index 6065dd4..d80f951 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -1046,20 +1046,25 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) {
  * @ingroup php_wrappers
  */
 function drupal_move_uploaded_file($filename, $uri) {
-  $result = @move_uploaded_file($filename, $uri);
-  // PHP's move_uploaded_file() does not properly support streams if safe_mode
-  // or open_basedir are enabled so if the move failed, try finding a real path
-  // and retry the move operation.
-  if (!$result) {
-    if ($realpath = drupal_realpath($uri)) {
-      $result = move_uploaded_file($filename, $realpath);
-    }
-    else {
-      $result = move_uploaded_file($filename, $uri);
-    }
+  // move_uploaded_file() does not support moving a file between two different
+  // stream wrappers. Other file handling functions like rename() have the same
+  // problem, but copy() works since it explicitly sends the contents to the
+  // new source. As such move_uploaded_file() is replaced by is_uploaded_file(),
+  // copy(), and unlink() the old file. Doing so also works around the
+  // open_basedir limitations of move_uploaded_file() while maintaining the
+  // functionality since copy is used internally.
+  //
+  // This also supports upload proxying during which the file may be remotely
+  // located and referenced by a stream wrapper. In that case $filename may be
+  // something like remote://... and $uri public://... which may end up on the
+  // same remote filesystem, but PHP does not make the distinction. Of course,
+  // this also means the file can be moved between two different file systems.
+  if (is_uploaded_file($filename)) {
+    // Attempt rename() which is less expensive if the origin and destination
+    // use the same stream wrapper, otherwise perform copy() and unlink().
+    return @rename($filename, $uri) || (copy($filename, $uri) && unlink($filename));
   }
-
-  return $result;
+  return FALSE;
 }
 
 /**
-- 
1.8.1.2

