Index: wp-includes/class-http.php
===================================================================
--- wp-includes/class-http.php	(revision 17444)
+++ wp-includes/class-http.php	(working copy)
@@ -227,7 +227,9 @@
 			'body' => null,
 			'compress' => false,
 			'decompress' => true,
-			'sslverify' => true
+			'sslverify' => true,
+			'stream' => false,
+			'filename' => null
 		);
 
 		$r = wp_parse_args( $args, $defaults );
@@ -255,6 +257,18 @@
 		$r['local'] = $homeURL['host'] == $arrURL['host'] || 'localhost' == $arrURL['host'];
 		unset( $homeURL );
 
+		// If we are streaming to a file but no filename was given drop it in the WP temp dir
+		// and pick it's name using the basename of the $url
+		if ( $r['stream']  && ! $r['filename'] )
+			$r['filename'] = get_temp_dir() . basename( $url );
+
+		// Force some settings if we are streaming to a file and check for existence and perms of destination directory
+		if ( $r['stream'] ) {
+			$r['blocking'] = true;
+			if ( ! is_writable( dirname( $r['filename'] ) ) )
+				return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
+		}
+
 		if ( is_null( $r['headers'] ) )
 			$r['headers'] = array();
 
@@ -748,16 +762,51 @@
 		}
 
 		$strResponse = '';
-		while ( ! feof($handle) )
-			$strResponse .= fread($handle, 4096);
+		$bodyStarted = false;
 
-		fclose($handle);
+		// If streaming to a file setup the file handle
+		if ( $r['stream'] ) {
+			if ( ! WP_DEBUG )
+				$stream_handle = @fopen( $r['filename'], 'w+' );
+			else
+				$stream_handle = fopen( $r['filename'], 'w+' );
+			if ( ! $stream_handle )
+				return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
+		}
 
+		while ( ! feof($handle) ) {
+			if ( $r['stream'] ) {
+				$block = fread( $handle, 4096 );
+				if ( $bodyStarted ) {
+					fwrite( $stream_handle, $block );
+				} else {
+					$strResponse .= $block;
+					if ( strpos( $strResponse, "\r\n\r\n" ) ) {
+						$process = WP_Http::processResponse( $strResponse );
+						$bodyStarted = true;
+						fwrite( $stream_handle, $process['body'] );
+						unset( $strResponse );
+						$process['body'] = '';
+					}
+				}
+			} else {
+				$strResponse .= fread( $handle, 4096 );
+			}
+		}
+
+		fclose( $handle );
+
 		if ( true === $secure_transport )
 			error_reporting($error_reporting);
 
-		$process = WP_Http::processResponse($strResponse);
-		$arrHeaders = WP_Http::processHeaders($process['headers']);
+		if ( $r['stream'] ) {
+			fclose( $stream_handle );
+			$arrHeaders = WP_Http::processHeaders( $process['headers'] );
+		} else {
+			$process = WP_Http::processResponse( $strResponse );
+			$arrHeaders = WP_Http::processHeaders( $process['headers'] );
+			unset( $strResponse );
+		}
 
 		// Is the response code within the 400 range?
 		if ( (int) $arrHeaders['response']['code'] >= 400 && (int) $arrHeaders['response']['code'] < 500 )
@@ -779,7 +828,7 @@
 		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) )
 			$process['body'] = WP_Http_Encoding::decompress( $process['body'] );
 
-		return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
+		return array( 'headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies'], 'filename' => $r['filename'] );
 	}
 
 	/**
@@ -1075,11 +1124,24 @@
 			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
 		}
 
-		$strResponse = stream_get_contents($handle);
-		$meta = stream_get_meta_data($handle);
+		if ( $r['stream'] ) {
+			if ( ! WP_DEBUG )
+				$stream_handle = @fopen( $r['filename'], 'w+' );
+			else
+				$stream_handle = fopen( $r['filename'], 'w+' );
+			if ( ! $stream_handle )
+				return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
+			stream_copy_to_stream( $handle, $stream_handle );
+			fclose( $stream_handle );
+			$strResponse = '';
+		} else {
+			$strResponse = stream_get_contents( $handle );
+		}
 
-		fclose($handle);
+		$meta = stream_get_meta_data( $handle );
 
+		fclose( $handle );
+
 		$processedHeaders = array();
 		if ( isset( $meta['wrapper_data']['headers'] ) )
 			$processedHeaders = WP_Http::processHeaders($meta['wrapper_data']['headers']);
@@ -1092,7 +1154,7 @@
 		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) )
 			$strResponse = WP_Http_Encoding::decompress( $strResponse );
 
-		return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
+		return array( 'headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies'], 'filename' => $r['filename'] );
 	}
 
 	/**
@@ -1275,8 +1337,18 @@
 	 *
 	 * @return boolean False means this class can not be used, true means it can.
 	 */
-	function test($args = array()) {
-		return apply_filters('use_http_extension_transport', function_exists('http_request'), $args );
+	function test( $args = array() ) {
+		$use = true;
+
+		if ( ! function_exists( 'http_request' ) )
+			$use = false;
+
+		$stream = ! empty( $args['stream'] );
+
+		if ( $stream )
+			$use = false;
+
+		return apply_filters( 'use_http_extension_transport', $use, $args );
 	}
 }
 
@@ -1292,6 +1364,15 @@
 class WP_Http_Curl {
 
 	/**
+	 * Temporary header storage for use with streaming to a file.
+	 *
+	 * @since 3.2.0
+	 * @access private
+	 * @var string
+	 */
+	private $headers;
+
+	/**
 	 * Send a HTTP request to a URI using cURL extension.
 	 *
 	 * @access public
@@ -1382,11 +1463,23 @@
 				break;
 		}
 
-		if ( true === $r['blocking'] )
+		if ( true === $r['blocking'] && true !== $r['stream'] )
 			curl_setopt( $handle, CURLOPT_HEADER, true );
 		else
 			curl_setopt( $handle, CURLOPT_HEADER, false );
 
+		// If streaming to a file open a file handle, and setup our callback for grabbing the headers
+		if ( $r['stream'] ) {
+			if ( ! WP_DEBUG )
+				$stream_handle = @fopen( $r['filename'], 'w+' );
+			else
+				$stream_handle = fopen( $r['filename'], 'w+' );
+			if ( ! $stream_handle )
+				return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
+			curl_setopt( $handle, CURLOPT_FILE, $stream_handle );
+			curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( &$this, 'stream_headers' ) );
+		}
+
 		// The option doesn't work with safe mode or when open_basedir is set.
 		// Disable HEAD when making HEAD requests.
 		if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 'HEAD' != $r['method'] )
@@ -1419,23 +1512,30 @@
 
 		$theResponse = curl_exec( $handle );
 
-		if ( !empty($theResponse) ) {
-			$headerLength = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
-			$theHeaders = trim( substr($theResponse, 0, $headerLength) );
-			if ( strlen($theResponse) > $headerLength )
-				$theBody = substr( $theResponse, $headerLength );
-			else
+		if ( ! empty( $theResponse ) ) {
+			// If we are streaming to a file the $headers property will hold our headers
+			if ( ! empty( $this->headers ) ) {
+				$theHeaders = $this->headers;
+				unset( $this->headers );
 				$theBody = '';
-			if ( false !== strpos($theHeaders, "\r\n\r\n") ) {
-				$headerParts = explode("\r\n\r\n", $theHeaders);
-				$theHeaders = $headerParts[ count($headerParts) -1 ];
+			} else {
+				$headerLength = curl_getinfo( $handle, CURLINFO_HEADER_SIZE );
+				$theHeaders = trim( substr( $theResponse, 0, $headerLength ) );
+				if ( strlen( $theResponse ) > $headerLength )
+					$theBody = substr( $theResponse, $headerLength );
+				else
+					$theBody = '';
+				if ( false !== strpos( $theHeaders, "\r\n\r\n" ) ) {
+					$headerParts = explode( "\r\n\r\n", $theHeaders );
+					$theHeaders = $headerParts[ count( $headerParts ) - 1 ];
+				}
 			}
-			$theHeaders = WP_Http::processHeaders($theHeaders);
+			$theHeaders = WP_Http::processHeaders( $theHeaders );
 		} else {
-			if ( $curl_error = curl_error($handle) )
-				return new WP_Error('http_request_failed', $curl_error);
-			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
-				return new WP_Error('http_request_failed', __('Too many redirects.'));
+			if ( $curl_error = curl_error( $handle ) )
+				return new WP_Error( 'http_request_failed', $curl_error );
+			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) )
+				return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
 
 			$theHeaders = array( 'headers' => array(), 'cookies' => array() );
 			$theBody = '';
@@ -1447,6 +1547,9 @@
 
 		curl_close( $handle );
 
+		if ( $r['stream'] )
+			fclose( $stream_handle );
+
 		// See #11305 - When running under safe mode, redirection is disabled above. Handle it manually.
 		if ( !empty($theHeaders['headers']['location']) && (ini_get('safe_mode') || ini_get('open_basedir')) ) {
 			if ( $r['redirection']-- > 0 ) {
@@ -1459,10 +1562,24 @@
 		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
 			$theBody = WP_Http_Encoding::decompress( $theBody );
 
-		return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
+		return array( 'headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies'], 'filename' => $r['filename'] );
 	}
 
 	/**
+	 * Grab the headers of the cURL request when steraming to a file
+	 *
+	 * Each header is sent individually to this callback, so we append to the $header property for temporary storage
+	 *
+	 * @since 3.2.0
+	 * @access private
+	 * @return int
+	 */
+	private function stream_headers( $handle, $headers ) {
+		$this->headers .= $headers;
+		return strlen( $headers );
+	}
+
+	/**
 	 * Whether this class can be used for retrieving an URL.
 	 *
 	 * @static
@@ -1470,7 +1587,7 @@
 	 *
 	 * @return boolean False means this class can not be used, true means it can.
 	 */
-	function test($args = array()) {
+	function test( $args = array() ) {
 		if ( function_exists('curl_init') && function_exists('curl_exec') )
 			return apply_filters('use_curl_transport', true, $args);
 
Index: wp-includes/functions.php
===================================================================
--- wp-includes/functions.php	(revision 17444)
+++ wp-includes/functions.php	(working copy)
@@ -2111,6 +2111,42 @@
 }
 
 /**
+ * Determines a writable directory for temporary files.
+ * Function's preference is to WP_CONTENT_DIR followed by the return value of <code>sys_get_temp_dir()</code>, before finally defaulting to /tmp/
+ *
+ * In the event that this function does not find a writable location, It may be overridden by the <code>WP_TEMP_DIR</code> constant in your <code>wp-config.php</code> file.
+ *
+ * @since 2.5.0
+ *
+ * @return string Writable temporary directory
+ */
+function get_temp_dir() {
+	static $temp;
+	if ( defined('WP_TEMP_DIR') )
+		return trailingslashit(WP_TEMP_DIR);
+
+	if ( $temp )
+		return trailingslashit($temp);
+
+	$temp = WP_CONTENT_DIR . '/';
+	if ( is_dir($temp) && @is_writable($temp) )
+		return $temp;
+
+	if  ( function_exists('sys_get_temp_dir') ) {
+		$temp = sys_get_temp_dir();
+		if ( @is_writable($temp) )
+			return trailingslashit($temp);
+	}
+
+	$temp = ini_get('upload_tmp_dir');
+	if ( is_dir($temp) && @is_writable($temp) )
+		return trailingslashit($temp);
+
+	$temp = '/tmp/';
+	return $temp;
+}
+
+/**
  * Get an array containing the current upload directory's path and url.
  *
  * Checks the 'upload_path' option, which should be from the web root folder,
Index: wp-admin/includes/file.php
===================================================================
--- wp-admin/includes/file.php	(revision 17444)
+++ wp-admin/includes/file.php	(working copy)
@@ -153,42 +153,6 @@
 }
 
 /**
- * Determines a writable directory for temporary files.
- * Function's preference is to WP_CONTENT_DIR followed by the return value of <code>sys_get_temp_dir()</code>, before finally defaulting to /tmp/
- *
- * In the event that this function does not find a writable location, It may be overridden by the <code>WP_TEMP_DIR</code> constant in your <code>wp-config.php</code> file.
- *
- * @since 2.5.0
- *
- * @return string Writable temporary directory
- */
-function get_temp_dir() {
-	static $temp;
-	if ( defined('WP_TEMP_DIR') )
-		return trailingslashit(WP_TEMP_DIR);
-
-	if ( $temp )
-		return trailingslashit($temp);
-
-	$temp = WP_CONTENT_DIR . '/';
-	if ( is_dir($temp) && @is_writable($temp) )
-		return $temp;
-
-	if  ( function_exists('sys_get_temp_dir') ) {
-		$temp = sys_get_temp_dir();
-		if ( @is_writable($temp) )
-			return trailingslashit($temp);
-	}
-
-	$temp = ini_get('upload_tmp_dir');
-	if ( is_dir($temp) && @is_writable($temp) )
-		return trailingslashit($temp);
-
-	$temp = '/tmp/';
-	return $temp;
-}
-
-/**
  * Returns a filename of a Temporary unique file.
  * Please note that the calling function must unlink() this itself.
  *
@@ -513,33 +477,24 @@
 function download_url( $url, $timeout = 300 ) {
 	//WARNING: The file is not automatically deleted, The script must unlink() the file.
 	if ( ! $url )
-		return new WP_Error('http_no_url', __('Invalid URL Provided.'));
+		return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
 
-	$tmpfname = wp_tempnam($url);
+	$tmpfname = wp_tempnam( $url );
 	if ( ! $tmpfname )
-		return new WP_Error('http_no_file', __('Could not create Temporary file.'));
+		return new WP_Error( 'http_no_file', __( 'Could not create Temporary file.' ) );
 
-	$handle = @fopen($tmpfname, 'wb');
-	if ( ! $handle )
-		return new WP_Error('http_no_file', __('Could not create Temporary file.'));
+	$response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) );
 
-	$response = wp_remote_get($url, array('timeout' => $timeout));
-
-	if ( is_wp_error($response) ) {
-		fclose($handle);
-		unlink($tmpfname);
+	if ( is_wp_error( $response ) ) {
+		unlink( $tmpfname );
 		return $response;
 	}
 
 	if ( $response['response']['code'] != '200' ){
-		fclose($handle);
-		unlink($tmpfname);
-		return new WP_Error('http_404', trim($response['response']['message']));
+		unlink( $tmpfname );
+		return new WP_Error( 'http_404', trim( $response['response']['message'] ) );
 	}
 
-	fwrite($handle, $response['body']);
-	fclose($handle);
-
 	return $tmpfname;
 }
 
