diff --git includes/file.inc includes/file.inc
index 6eac086..fd80845 100644
--- includes/file.inc
+++ includes/file.inc
@@ -7,6 +7,23 @@
  */
 
 /**
+ * Stream wrapper initialization is included here because there are cases where
+ * File API is needed before a bootstrap, or in an alternate order (e.g. maintenance theme).
+ * This method of initialization doesn't break anything and requires fewer changes
+ * to core.
+ */
+// Stream wrapper interface and base class implementation
+require_once DRUPAL_ROOT . '/includes/stream_wrappers.inc';
+
+// Stream wrapper registry
+require_once DRUPAL_ROOT . '/includes/stream_wrapper_registry.inc';
+
+// Register core wrappers
+DrupalStreamWrapperRegistry::register('public',  'DrupalPublicStreamWrapper');
+DrupalStreamWrapperRegistry::register('private', 'DrupalPrivateStreamWrapper');
+DrupalStreamWrapperRegistry::register('temp',    'DrupalTempStreamWrapper');
+
+/**
  * @defgroup file File interface
  * @{
  * Common file handling functions.
diff --git includes/stream_wrapper_registry.inc includes/stream_wrapper_registry.inc
new file mode 100644
index 0000000..912ba02
--- /dev/null
+++ includes/stream_wrapper_registry.inc
@@ -0,0 +1,253 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Drupal stream wrapper registry.
+ *
+ * Provide a class for managing and querying user defined stream wrappers
+ * in PHP. PHP's internal stream_get_wrappers doesn't return the class
+ * registered to handle a stream. We need to be able to find the handler
+ * for class instantiation.
+ *
+ * A stream is referenced as: scheme://target
+ */
+
+/**
+ * Drupal stream wrapper manager class
+ */
+class DrupalStreamWrapperRegistry {
+
+  /**
+   * Array mapping schemes to class names.
+   *
+   * @var Array
+   */
+  private static $wrappers = array();
+
+  /**
+   * Private constructor to enforce singleton.
+   */
+  private function __construct() { }
+
+  /**
+   * Registers a stream wrapper scheme.
+   *
+   * @see http://php.net/manual/en/function.stream-wrapper-register.php
+   *
+   * @param string $scheme
+   *   URI scheme.
+   * @param string $class
+   *   Class name for the stream wrapper.
+   * @return bool
+   *   result of stream_wrapper_register()
+   */
+  public static function register($scheme, $class_name) {
+    self::$wrappers[$scheme] = $class_name;
+    return stream_wrapper_register($scheme, $class_name);
+  }
+
+  /**
+   * Unregisters a stream wrapper.
+   *
+   * @see http://php.net/manual/en/function.stream-wrapper-unregister.php
+   *
+   * @param string $scheme
+   *   URI scheme.
+   * @return bool
+   *   result of stream_wrapper_unregister()
+   */
+  public static function unregister($scheme) {
+    unset(self::$wrappers[$scheme]);
+    return stream_wrapper_unregister($scheme);
+  }
+
+  /**
+   * Returns the entire Drupal stream wrapper registry.
+   *
+   * @return array
+   */
+  public function wrappers() {
+    return self::$wrappers;
+  }
+
+  /**
+   * Returns the stream wrapper class name for a given scheme.
+   *
+   * @param string $scheme
+   *   Stream scheme.
+   * @return mixed
+   *   Return string if a scheme has a registered handler, or FALSE.
+   */
+  public static function getClassName($scheme) {
+    if (empty(self::$wrappers[$scheme])) {
+      return FALSE;
+    }
+
+    return self::$wrappers[$scheme];
+  }
+
+  /**
+   * Gets the scheme of a URI (stream).
+   *
+   * A stream is referenced as scheme://target.
+   *
+   * @param $uri
+   *   A stream, referenced as scheme://target.
+   * @return mixed
+   *   A string containing the name of the scheme, or FALSE if none.
+   *   For example, the URI public://example.txt would return public.
+   */
+  public static function getStreamScheme($uri) {
+    $data = explode('://', $uri, 2);
+    // $data[0]://$data[1]
+    // scheme://target
+
+    return count($data) == 2 ? $data[0] : FALSE;
+  }
+
+  /**
+   * Asserts that the scheme is valid.
+   *
+   * Confirms that there is a registered stream handler for the
+   * provided scheme and that it is callable. This is usefule if
+   * you want to confirm a valid scheme without creating a new instance
+   * of the registered handler.
+   *
+   * A stream is referenced as scheme://target.
+   *
+   * @code
+   *   // Return 'public'
+   *   DrupalStreamWrapperRegistry::getValidStreamScheme('public://example.txt');
+   *
+   *   // Return FALSE unless there is a registered wrapper for 'foobar'
+   *   DrupalStreamWrapperRegistry::getValidStreamScheme('foobar://example.txt');
+   *
+   * @param $uri
+   *   A stream, referenced as scheme://target.
+   * @return mixed
+   *   Returns a string containing the name of a validated stream.
+   *   Returns false if the URI does not contain a scheme or the scheme
+   *   does not have a registered handler.
+   */
+  public static function getValidStreamScheme($uri) {
+
+    if (!$scheme = self::getStreamScheme($uri)) {
+      // URI doesn't even contain a scheme
+      return FALSE;
+    }
+
+    // Does the scheme have a registered handler that is callable?
+    $class = self::getClassName($scheme);
+    if (class_exists($class)) {
+      return $scheme;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Gets the targat of a URI (stream).
+   *
+   * A stream is referenced as scheme://target.
+   *
+   * @param $uri
+   *   A stream, referenced as scheme://target
+   * @return mixed
+   *   A string containing the target (path), or FALSE if none.
+   *   For example, the URI public://sample/test.txt would return 
+   *   sample/test.txt
+   */
+  public static function getStreamTarget($uri) {
+    $data = explode('://', $uri, 2);
+    // $data[0]://$data[1]
+    // scheme://target
+
+    if (count($data) != 2) {
+      return FALSE;
+    }
+
+    // Remove erroneous beginning forward slash
+    $data[1] = ltrim($data[1], '\/');
+
+    return $data[1];
+  }
+
+  /**
+   * Gets reference to stream wrapper class responsible for given URI (stream).
+   *
+   * The scheme determines the stream wrapper class that should be
+   * used by consulting the stream wrapper registry.
+   *
+   * @param $uri
+   *   A stream, referenced as scheme://target
+   * @return mixed
+   *   Returns a new stream wrapper object appropriate for the given URI.
+   *   For example, a URI of public://example.txt would return a new
+   *   private stream wrapper object (DrupalPrivateStreamWrapper).
+   *   FALSE is returned if no registered handler could be found.
+   */
+  public static function getInstanceByUri($uri) {
+
+    $class = self::getClassName(self::getStreamScheme($uri));
+    if (class_exists($class)) {
+      $instance = new $class;
+      $instance->__set('uri', $uri);
+      return $instance;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Gets reference to stream wrapper class responsible for given scheme.
+   *
+   * This helper method returns a stream instance using a scheme. That is, the
+   * passed string does not container a '://'. For example, 'public' is a scheme 
+   * but 'public://' is a URI (stream). This is because the later contains both 
+   * a scheme and target despite target being empty.
+   *
+   * Note: the instance URI will be initialized to 'scheme://' so that you can
+   * make the customary method calls as if you had retrieved an instance by URI.
+   *
+   * @param $scheme
+   *   If the stream was 'public://target', 'public' would be the scheme.
+   * @return mixed
+   *   Returns a new stream wrapper object appropriate for the given $scheme.
+   *   For example, for the public scheme a stream wrapper object
+   *   (DrupalPublicStreamWrapper).
+   *   FALSE is returned if no registered handler could be found.
+   */
+  public static function getInstanceByScheme($scheme) {
+
+    return self::getInstanceByUri($scheme . '://');
+  }
+
+  /**
+   * Normalizes URI by making it syntactically correct.
+   *
+   * A stream is referenced as scheme://target.
+   *
+   * The following actions are taken:
+   * - Replace ':///' with '://'
+   * - Remove trailing slashes on target.
+   *
+   * @param string &$uri
+   *   String reference containing the URI to normalize.
+   */
+  public static function normalizeUri(&$uri) {
+    if ($scheme = self::getValidStreamScheme($uri)) {
+      $target = self::getStreamTarget($uri);
+
+      // Remove all occurrences of the wrapper's directory path.
+      $directory_path = self::getInstanceByScheme($scheme)->getDirectoryPath();
+      $target         = str_replace($directory_path, '', $target);
+
+      // Trim erroneous leading slashes from target.
+      $uri = $scheme . '://' . ereg_replace('^[/\\]*', '', $target);
+    }
+  }
+
+}
diff --git includes/stream_wrappers.inc includes/stream_wrappers.inc
new file mode 100644
index 0000000..6ed3f80
--- /dev/null
+++ includes/stream_wrappers.inc
@@ -0,0 +1,655 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Drupal stream wrapper interface.
+ *
+ * Provides a Drupal interface and classes to implement PHP stream wrappers for
+ * public, private, and temporary files.
+ */
+
+/**
+ *  The scheme for the public stream wrapper.
+ */
+define('SCHEME_PUBLIC', 'public://');
+
+/**
+ *  The scheme for the private stream wrapper.
+ */
+define('SCHEME_PRIVATE', 'private://');
+
+/**
+ *  The scheme for the temporary stream wrapper.
+ */
+define('SCHEME_TEMP', 'temp://');
+
+/**
+ * Generic PHP stream wrapper interface.
+ *
+ * @see http://www.php.net/manual/en/class.streamwrapper.php
+ */
+interface StreamWrapperInterface {
+  public function stream_open($uri, $mode, $options, &$opened_url);
+  public function stream_close();
+  public function stream_lock($operation);
+  public function stream_read($count);
+  public function stream_write($data);
+  public function stream_eof();
+  public function stream_seek($offset, $whence);
+  public function stream_flush();
+  public function stream_tell();
+  public function stream_stat();
+  public function unlink($uri);
+  public function rename($from_uri, $to_uri);
+  public function mkdir($uri, $mode, $options);
+  public function rmdir($uri, $options);
+  public function url_stat($uri, $flags);
+  public function dir_opendir($uri, $options);
+  public function dir_readdir();
+  public function dir_rewinddir();
+  public function dir_closedir();
+}
+
+
+/**
+ * Drupal stream wrapper extension.
+ *
+ * Extend the StreamWrapperInterface with methods expected by
+ * Drupal stream wrapper classes.
+ */
+interface DrupalStreamWrapperInterface extends StreamWrapperInterface {
+
+  /**
+   * Return an absolute stream resource URL for internal purposes. In the case
+   * of the three local wrappers (public, private, temp) realpath should be
+   * used for security reasons.
+   *
+   * @return string
+   *   Returns cononcialized absolute pathname for internal purposes.
+   */
+  public function getInternalUri();
+
+  /**
+   * Return the HTML accessible URL for a resource.
+   *
+   * @return string
+   */
+  public function getExternalUrl();
+
+  /**
+   * Return the mime type of a resource.
+   *
+   * @param array $mapping
+   *   An optional map of extensions to their mimetypes, in the form:
+   *    - 'mimetypes': a list of mimetypes, keyed by an identifier,
+   *    - 'extensions': the mapping itself, an associative array in which
+   *      the key is the extension and the value is the mimetype identifier.
+   * @return string
+   */
+  public function getMimeType($mapping = NULL);
+
+  /**
+   * Change permissions of stream.
+   *
+   * PHP lacks this functionality and it is not part of the official
+   * stream wrapper interface. This is a custom implementation for
+   * Drupal.
+   *
+   * @param mixed $mode
+   * @return bool
+   *   Returns TRUE on success or FALSE on failure.
+   */
+  public function chmod($mode);
+
+  /**
+   * Returns canonicalized absolute pathname.
+   *
+   * Implementation placeholder. PHP's realpath does not support
+   * stream wrappers. We provide this as a default so that
+   * individual wrappers may implement their own solutions.
+   * The base class will always return FALSE.
+   *
+   * @return mixed
+   *   A string with absolute pathname on success (implemented
+   *   by core wrappers), or FALSE on failure or the registered
+   *   wrapper does not provide an implementation.
+   */
+  public function realpath();
+}
+
+
+/**
+ * Drupal stream wrapper base class for local files.
+ *
+ * This class provides a complete stream wrapper implementation. It passes
+ * incoming URI's through an interpolation method and then recursively calls
+ * the invoking PHP filesystem function.
+ *
+ * DrupalLocalStreamWrapper implementations need to override at least the
+ * getInternalUri() method to rewrite the URI before is it passed back to the
+ * calling function.
+ */
+abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface {
+
+  /**
+   * A generic resource handle.
+   *
+   * @var Resource
+   */
+  private $handle = NULL;
+
+  /**
+   * Instance URI (stream).
+   *
+   * A stream is referenced as scheme://target
+   *
+   * @var String
+   */
+  private $uri;
+
+  /**
+   * Stream context resource.
+   *
+   * @var Resource
+   */
+  private $context;
+
+  /**
+   * Sets private variables.
+   *
+   * This is a generic setter.
+   *
+   * @param string $key
+   *   The variable to change.
+   * @param mixed $value
+   *   $key is set to this value.
+   */
+  public function __set($key, $value) {
+    $this->$key = $value;
+  }
+
+  /**
+   * Gets private variables.
+   *
+   * This is a generic getter.
+   *
+   * @param string $key
+   *   The variable to retrieve.
+   * @return mixed
+   *   Returns the variable requested.
+   */
+  public function __get($key) {
+    return $this->$key;
+  }
+
+  /**
+   * Gets the path that the wrapper is responsible for.
+   *
+   * @return
+   *   String specifying the path.
+   */
+  abstract function getDirectoryPath();
+
+  /**
+   * Interpolate the URI path, adding the base path from $this->getDirectoryPath().
+   */
+  function getInternalUri() {
+    return $this->getDirectoryPath() . '/' . DrupalStreamWrapperRegistry::getStreamTarget($this->uri);
+  }
+
+  function getExternalUrl() {
+    return $this->uri;
+  }
+
+  function getMimeType($mapping = NULL) {
+    if (!isset($mapping)) {
+      $mapping = variable_get('mime_extension_mapping', NULL);
+      if (!isset($mapping) && drupal_function_exists('file_default_mimetype_mapping')) {
+        // The default file map, defined in file.mimetypes.inc is quite big.
+        // We only load it when necessary.
+        $mapping = file_default_mimetype_mapping();
+      }
+    }
+
+    $extension = '';
+    $file_parts = explode('.', basename($this->uri));
+
+    // Remove the first part: a full filename should not match an extension.
+    array_shift($file_parts);
+
+    // Iterate over the file parts, trying to find a match.
+    // For my.awesome.image.jpeg, we try:
+    //   - jpeg
+    //   - image.jpeg, and
+    //   - awesome.image.jpeg
+    while ($additional_part = array_pop($file_parts)) {
+      $extension = $additional_part . ($extension ? '.' . $extension : '');
+      if (isset($mapping['extensions'][$extension])) {
+        return $mapping['mimetypes'][$mapping['extensions'][$extension]];
+      }
+    }
+
+    return 'application/octet-stream';
+  }
+
+  function chmod($mode) {
+    return @chmod($this->realpath(), $mode);
+  }
+
+  function realpath() {
+    return @realpath($this->getDirectoryPath() . '/' . DrupalStreamWrapperRegistry::getStreamTarget($this->uri));
+  }
+
+  /**
+   * Support for fopen(), file_get_contents(), file_put_contents() etc.
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-open.php
+   *
+   * @param $path
+   *   A string containing the path to the file to open.
+   * @param $mode
+   *   The file mode ("r", "wb" etc.).
+   * @param $options
+   *   A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
+   * @param &$opened_path
+   *   A string containing the path actually opened.
+   * @return bool
+   *   TRUE if file was opened successfully.
+   */
+  public function stream_open($uri, $mode, $options, &$opened_url) {
+    $this->uri = $uri;
+    $uri = $this->getInternalUri();
+    $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($uri, $mode) : @fopen($uri, $mode);
+
+    if ((bool)$this->handle && $options & STREAM_USE_PATH) {
+      $opened_url = $uri;
+    }
+
+    return (bool)$this->handle;
+  }
+
+  /**
+   * Support for flock().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-lock.php
+   *
+   * @param int $operation
+   * @return bool
+   *   Always returns TRUE.
+   */
+  public function stream_lock($operation) {
+    if (in_array($operation, array(LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB))) {
+      return flock($this->handle, $operation);
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Support for fread(), file_get_contents() etc.
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-read.php
+   *
+   * @param $count
+   *   Maximum number of bytes to be read.
+   * @return
+   *   The string that was read, or FALSE in case of an error.
+   */
+  public function stream_read($count) {
+    return fread($this->handle, $count);
+  }
+
+  /**
+   * Support for fwrite(), file_put_contents() etc.
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-write.php
+   *
+   * @param $data
+   *   The string to be written.
+   * @return int
+   *   The number of bytes written.
+   */
+  public function stream_write($data) {
+    return fwrite($this->handle, $data);
+  }
+
+  /**
+   * Support for feof().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-eof.php
+   *
+   * @return bool
+   *   TRUE if end-of-file has been reached.
+   */
+  public function stream_eof() {
+    return feof($this->handle);
+  }
+
+  /**
+   * Support for fseek().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-seek.php
+   *
+   * @param $offset
+   *   The byte offset to got to.
+   * @param $whence
+   *   SEEK_SET, SEEK_CUR, or SEEK_END.
+   * @return
+   *   TRUE on success
+   */
+  public function stream_seek($offset, $whence) {
+    return fseek($this->handle, $offset, $whence);
+  }
+
+  /**
+   * Support for fflush().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-flush.php
+   *
+   * @return
+   *   TRUE if data was successfully stored (or there was no data to store).
+   */
+  public function stream_flush() {
+    return fflush($this->handle);
+  }
+
+  /**
+   * Support for ftell().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-tell.php
+   *
+   * @return
+   *   The current offset in bytes from the beginning of file.
+   */
+  public function stream_tell() {
+    return ftell($this->handle);
+  }
+
+  /**
+   * Support for fstat().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-stat.php
+   *
+   * @return
+   *   An array with file status, or FALSE in case of an error - see fstat()
+   *   for a description of this array.
+   */
+  public function stream_stat() {
+    return fstat($this->handle);
+  }
+
+  /**
+   * Support for fclose().
+   *
+   * @see http://php.net/manual/en/streamwrapper.stream-close.php
+   *
+   * @return
+   *   TRUE if stream was successfully closed.
+   */
+  public function stream_close() {
+    return fclose($this->handle);
+  }
+
+  /**
+   * Support for unlink().
+   *
+   * @see http://php.net/manual/en/streamwrapper.unlink.php
+   *
+   * @param $uri
+   *   A string containing the uri to the resource to delete.
+   * @return
+   *   TRUE if resource was successfully deleted.
+   */
+  public function unlink($uri) {
+    $this->uri = $uri;
+    return unlink($this->getInternalUri());
+  }
+
+  /**
+   * Support for rename().
+   *
+   * @see http://php.net/manual/en/streamwrapper.rename.php
+   *
+   * @param $from_uri,
+   *   The uri to the file to rename.
+   * @param $to_uri
+   *   The new uri for file.
+   * @return
+   *   TRUE if file was successfully renamed.
+   */
+  public function rename($from_uri, $to_uri) {
+    return rename($this->getInternalUri($from_uri), $this->getInternalUri($to_uri));
+  }
+
+  /**
+   * Support for mkdir().
+   *
+   * @see http://php.net/manual/en/streamwrapper.mkdir.php
+   *
+   * @param $uri
+   *   A string containing the url to the directory to create.
+   * @param $mode
+   *   Permission flags - see mkdir().
+   * @param $options
+   *   A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
+   * @return
+   *   TRUE if directory was successfully created.
+   */
+  public function mkdir($uri, $mode, $options) {
+    $this->uri = $uri;
+    $recursive = (bool)($options & STREAM_MKDIR_RECURSIVE);
+    if ($options & STREAM_REPORT_ERRORS) {
+      return mkdir($this->getInternalUri(), $mode, $recursive);
+    }
+    else {
+      return @mkdir($this->getInternalUri(), $mode, $recursive);
+    }
+  }
+
+  /**
+   * Support for rmdir().
+   *
+   * @see http://php.net/manual/en/streamwrapper.rmdir.php
+   *
+   * @param $uri
+   *   A string containing the url to the directory to delete.
+   * @param $options
+   *   A bit mask of STREAM_REPORT_ERRORS.
+   * @return
+   *   TRUE if directory was successfully removed.
+   */
+  public function rmdir($uri, $options) {
+    $this->uri = $uri;
+    if ($options & STREAM_REPORT_ERRORS) {
+      return rmdir($this->getInternalUri());
+    }
+    else {
+      return @rmdir($this->getInternalUri());
+    }
+  }
+
+  /**
+   * Support for stat().
+   *
+   * @see http://php.net/manual/en/streamwrapper.url-stat.php
+   *
+   * @param $uri
+   *   A string containing the url to get information about.
+   * @param $flags
+   *   A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
+   * @return
+   *   An array with file status, or FALSE in case of an error - see fstat()
+   *   for a description of this array.
+   */
+  public function url_stat($uri, $flags) {
+    $this->uri = $uri;
+    if ($flags & STREAM_URL_STAT_QUIET) {
+      return @stat($this->getInternalUri());
+    }
+    else {
+      return stat($this->getInternalUri());
+    }
+  }
+
+  /**
+   * Support for opendir().
+   *
+   * @see http://php.net/manual/en/streamwrapper.dir-opendir.php
+   *
+   * @param $uri
+   *   A string containing the url to the directory to open.
+   * @param $options
+   *   Unknown (parameter is not documented in PHP Manual).
+   * @return
+   *   TRUE on success.
+   */
+  public function dir_opendir($uri, $options) {
+    $this->uri = $uri;
+    $this->handle = opendir($this->getInternalUri());
+
+    return (bool)$this->handle;
+  }
+
+  /**
+   * Support for readdir().
+   *
+   * @see http://php.net/manual/en/streamwrapper.dir-readdir.php
+   *
+   * @return
+   *   The next filename, or FALSE if there are no more files in the directory.
+   */
+  public function dir_readdir() {
+    return readdir($this->handle);
+  }
+
+  /**
+   * Support for rewinddir().
+   *
+   * @see http://php.net/manual/en/streamwrapper.dir-rewinddir.php
+   *
+   * @return
+   *   TRUE on success.
+   */
+  public function dir_rewinddir() {
+    return rewinddir($this->handle);
+  }
+
+  /**
+   * Support for closedir().
+   *
+   * @see http://php.net/manual/en/streamwrapper.dir-closedir.php
+   *
+   * @return
+   *   TRUE on success.
+   */
+  public function dir_closedir() {
+    return closedir($this->handle);
+  }
+}
+
+
+/**
+ * Drupal public (public://) stream wrapper class.
+ *
+ * Provides support for storing publicly accessible
+ * files with the Drupal file interface.
+ */
+class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper {
+
+  /**
+   * Implements abstract public function getDirectoryPath()
+   */
+  public function getDirectoryPath() {
+    return variable_get('stream_public_path', 'sites/default/files');
+  }
+
+  /**
+   * Override getExternalUrl().
+   *
+   * Return the HTML URI of a public file.
+   */
+  function getExternalUrl() {
+    $path = str_replace('\\', '/', DrupalStreamWrapperRegistry::getStreamTarget($this->uri));
+    return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . $path;
+  }
+}
+
+
+/**
+ * Drupal private (private://) stream wrapper class.
+ *
+ * Provides support for storing privately accessible
+ * files with the Drupal file interface.
+ *
+ * Extends DrupalPublicStreamWrapper.
+ */
+class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper {
+
+  /**
+   * Implements abstract public function getDirectoryPath()
+   */
+  public function getDirectoryPath() {
+    return variable_get('stream_private_path', 'sites/default/files-private');
+  }
+
+  /**
+   * Override getExternalUrl().
+   *
+   * Return the HTML URI of a private file.
+   */
+  function getExternalUrl() {
+    $path = str_replace('\\', '/', DrupalStreamWrapperRegistry::getStreamTarget($this->uri));
+    return url('system/files/' . $path, array('absolute' => TRUE));
+  }
+}
+
+
+/**
+ * Drupal temp (temp://) stream wrapper class.
+ *
+ * Provides support for storing temporarily accessible
+ * files with the Drupal file interface.
+ *
+ * Extends DrupalPublicStreamWrapper.
+ */
+class DrupalTempStreamWrapper extends DrupalLocalStreamWrapper {
+
+  /**
+   * Implements abstract public function getDirectoryPath()
+   */
+  public function getDirectoryPath() {
+    $temporary_directory = variable_get('stream_temp_path');
+
+    if (is_null($temporary_directory)) {
+      $directories = array();
+
+      // Has PHP been set with an upload_tmp_dir?
+      if (ini_get('upload_tmp_dir')) {
+        $directories[] = ini_get('upload_tmp_dir');
+      }
+
+      // Operating system specific dirs.
+      if (substr(PHP_OS, 0, 3) == 'WIN') {
+        $directories[] = 'c:/windows/temp';
+        $directories[] = 'c:/winnt/temp';
+      }
+      else {
+        $directories[] = '/tmp';
+      }
+
+      foreach ($directories as $directory) {
+        if (!$temporary_directory && is_dir($directory)) {
+          $temporary_directory = $directory;
+        }
+      }
+
+      // if a directory has been found, use it, otherwise default to 'files/tmp'
+      $temporary_directory = $temporary_directory ? $temporary_directory : file_directory_path('public') . '/tmp';
+      variable_set('stream_temp_path', $temporary_directory);
+      return $temporary_directory;
+    }
+
+    return variable_get('stream_temp_path', '/tmp');
+  }
+}
diff --git modules/simpletest/tests/file.test modules/simpletest/tests/file.test
index 5564c53..29da33b 100644
--- modules/simpletest/tests/file.test
+++ modules/simpletest/tests/file.test
@@ -44,6 +44,37 @@ function file_test_file_scan_callback_reset() {
 }
 
 /**
+ * Helper class for testing the stream wrapper registry.
+ *
+ * Dummy stream wrapper implementation (dummy://).
+ */
+class DrupalDummyStreamWrapper extends DrupalLocalStreamWrapper {
+  // TODO figure out what this should really return.
+  function getDirectoryPath() {
+    return variable_get('stream_public_path', 'sites/default/files');
+  }
+
+  /**
+   * Override getInternalUri().
+   *
+   * Return a dummy path for testing.
+   */
+  function getInternalUri() {
+    return '/dummy/example.txt';
+  }
+
+  /**
+   * Override getExternalUrl().
+   *
+   * Return the HTML URI of a public file.
+   */
+  function getExternalUrl() {
+    return '/dummy/example.txt';
+  }
+
+}
+
+/**
  * Base class for file tests that adds some additional file specific
  * assertions and helper functions.
  */
@@ -2036,3 +2067,102 @@ class FileMimeTypeTest extends DrupalWebTestCase {
     }
   }
 }
+
+/**
+ * Tests stream wrapper registry.
+ */
+class StreamWrapperRegistryUnitTest extends DrupalWebTestCase {
+
+  protected $scheme = 'dummy';
+  protected $classname = 'DrupalDummyStreamWrapper';
+
+  public static function getInfo() {
+    return array(
+      'name' => t('Stream Wrapper Registry'),
+      'description' => t('Tests stream wrapper registry.'),
+      'group' => t('File'),
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    DrupalStreamWrapperRegistry::register($this->scheme, $this->classname);
+  }
+
+  function tearDown() {
+    parent::tearDown();
+    // Unregister the test handler.
+    $wrappers = DrupalStreamWrapperRegistry::wrappers();
+    if (isset($wrappers[$this->scheme])) {
+      DrupalStreamWrapperRegistry::unregister($this->scheme);
+    }
+  }
+
+  /**
+   * Test the register(), unregister() and wrappers() functions.
+   */
+  function testRegisterUnregisterWrappers() {
+    // Make sure it's not already registered.
+    $wrappers = DrupalStreamWrapperRegistry::wrappers();
+    if (isset($wrappers[$this->scheme])) {
+      DrupalStreamWrapperRegistry::unregister($this->scheme);
+    }
+
+    // Register the wrapper.
+    $this->assertTrue(DrupalStreamWrapperRegistry::register($this->scheme, $this->classname), t('The stream wrapper registered'));
+    $wrappers = DrupalStreamWrapperRegistry::wrappers();
+    $this->assertTrue(isset($wrappers[$this->scheme]), t('The stream wrapper has actually been registered.'));
+
+    // Unregister the wrapper.
+    $this->assertTrue(DrupalStreamWrapperRegistry::unregister($this->scheme), t('Stream wrapper unregistered.'));
+    $wrappers = DrupalStreamWrapperRegistry::wrappers();
+    $this->assertFalse(isset($wrappers[$this->scheme]), t('The stream wrapper has actually been unregistered.'));
+  }
+
+  /**
+   * Test the getClassName() function.
+   */
+  function testGetClassName() {
+    // Check the dummy scheme.
+    $this->assertEqual($this->classname, DrupalStreamWrapperRegistry::getClassName($this->scheme), t('Got correct class name for dummy scheme.'));
+    // Check core's scheme.
+    $this->assertEqual('DrupalPublicStreamWrapper', DrupalStreamWrapperRegistry::getClassName('public'), t('Got correct class name for public scheme.'));
+  }
+
+  /**
+   * Test the getInstanceByScheme() functions.
+   */
+  function testGetInstanceByScheme() {
+    $instance = DrupalStreamWrapperRegistry::getInstanceByScheme($this->scheme);
+    $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy scheme.'));
+
+    $instance = DrupalStreamWrapperRegistry::getInstanceByScheme('public');
+    $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public scheme.'));
+  }
+
+  /**
+   * Test the getInstanceByUri() function.
+   */
+  function testGetInstanceByUri() {
+    $instance = DrupalStreamWrapperRegistry::getInstanceByUri($this->scheme . '://foo');
+    $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy URI.'));
+
+    $instance = DrupalStreamWrapperRegistry::getInstanceByUri('public://foo');
+    $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.'));
+  }
+
+  /**
+   * Test the getStreamScheme() function.
+   */
+  function testGetStreamScheme() {
+    $this->assertEqual('foo', DrupalStreamWrapperRegistry::getStreamScheme('foo://pork//chops'), t('Got the correct scheme from foo://asdf'));
+  }
+
+  /**
+   * Test the getValidStreamScheme() function.
+   */
+  function testGetValidStreamScheme() {
+    $this->assertEqual('public', DrupalStreamWrapperRegistry::getValidStreamScheme('public://asdf'), t('Got a valid stream scheme from public://asdf'));
+    $this->assertFalse(DrupalStreamWrapperRegistry::getValidStreamScheme('foo://asdf'), t('Did not get a valid stream scheme from foo://asdf'));
+  }
+}
