home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (file.inc)

This example Drupal source code file (file.inc) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

array, destination, directory, false, file, function, if, not, php, return, scheme, source, the, uri

The file.inc Drupal example source code

<?php
// $Id: file.inc,v 1.243 2010/12/15 03:39:41 webchick Exp $

/**
 * @file
 * API for handling file uploads and server file management.
 */

/**
 * Manually include stream wrapper code.
 *
 * Stream wrapper code is included here because there are cases where
 * File API is needed before a bootstrap, or in an alternate order (e.g.
 * maintenance theme).
 */
require_once DRUPAL_ROOT . '/includes/stream_wrappers.inc';

/**
 * @defgroup file File interface
 * @{
 * Common file handling functions.
 *
 * Fields on the file object:
 * - fid: File ID
 * - uid: The {users}.uid of the user who is associated with the file.
 * - filename: Name of the file with no path components. This may differ from
 *   the basename of the filepath if the file is renamed to avoid overwriting
 *   an existing file.
 * - uri: URI of the file.
 * - filemime: The file's MIME type.
 * - filesize: The size of the file in bytes.
 * - status: A bitmapped field indicating the status of the file. The first 8
 *   bits are reserved for Drupal core. The least significant bit indicates
 *   temporary (0) or permanent (1). Temporary files older than
 *   DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during cron runs.
 * - timestamp: UNIX timestamp for the date the file was added to the database.
 */

/**
 * Flag used by file_prepare_directory() -- create directory if not present.
 */
define('FILE_CREATE_DIRECTORY', 1);

/**
 * Flag used by file_prepare_directory() -- file permissions may be changed.
 */
define('FILE_MODIFY_PERMISSIONS', 2);

/**
 * Flag for dealing with existing files: Appends number until name is unique.
 */
define('FILE_EXISTS_RENAME', 0);

/**
 * Flag for dealing with existing files: Replace the existing file.
 */
define('FILE_EXISTS_REPLACE', 1);

/**
 * Flag for dealing with existing files: Do nothing and return FALSE.
 */
define('FILE_EXISTS_ERROR', 2);

/**
 * Indicates that the file is permanent and should not be deleted.
 *
 * Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed
 * during cron runs, but permanent files will not be removed during the file
 * garbage collection process.
 */
define('FILE_STATUS_PERMANENT', 1);

/**
 * Methods to manage a registry of stream wrappers.
 */

/**
 * Drupal stream wrapper registry.
 *
 * A stream wrapper is an abstraction of a file system that allows Drupal to
 * use the same set of methods to access both local files and remote resources.
 *
 * Provide a facility 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, which we need to be able to find the handler
 * for class instantiation.
 *
 * If a module registers a scheme that is already registered with PHP, the
 * existing scheme will be unregistered and replaced with the specified class.
 *
 * A stream is referenced as "scheme://target".
 *
 * The optional $filter parameter can be used to retrieve only the stream
 * wrappers that are appropriate for particular usage. For example, this returns
 * only stream wrappers that use local file storage:
 * @code
 *   $local_stream_wrappers = file_get_stream_wrappers(STEAM_WRAPPERS_LOCAL);
 * @endcode
 *
 * The $filter parameter can only filter to types containing a particular flag.
 * In some cases, you may want to filter to types that do not contain a
 * particular flag. For example, you may want to retrieve all stream wrappers
 * that are not writable, or all stream wrappers that are not local. PHP's
 * array_diff_key() function can be used to help with this. For example, this
 * returns only stream wrappers that do not use local file storage:
 * @code
 *   $remote_stream_wrappers = array_diff_key(file_get_stream_wrappers(STREAM_WRAPPERS_ALL), file_get_stream_wrappers(STEAM_WRAPPERS_LOCAL));
 * @endcode
 *
 * @param $filter
 *   (Optional) Filters out all types except those with an on bit for each on
 *   bit in $filter. For example, if $filter is STREAM_WRAPPERS_WRITE_VISIBLE,
 *   which is equal to (STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE |
 *   STREAM_WRAPPERS_VISIBLE), then only stream wrappers with all three of these
 *   bits set are returned. Defaults to STREAM_WRAPPERS_ALL, which returns all
 *   registered stream wrappers.
 *
 * @return
 *   An array keyed by scheme, with values containing an array of information
 *   about the stream wrapper, as returned by hook_stream_wrappers(). If $filter
 *   is omitted or set to STREAM_WRAPPERS_ALL, the entire Drupal stream wrapper
 *   registry is returned. Otherwise only the stream wrappers whose 'type'
 *   bitmask has an on bit for each bit specified in $filter are returned.
 *
 * @see hook_stream_wrappers()
 * @see hook_stream_wrappers_alter()
 */
function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) {
  $wrappers_storage = &drupal_static(__FUNCTION__);

  if (!isset($wrappers_storage)) {
    $wrappers = module_invoke_all('stream_wrappers');
    foreach ($wrappers as $scheme => $info) {
      // Add defaults.
      $wrappers[$scheme] += array('type' => STREAM_WRAPPERS_NORMAL);
    }
    drupal_alter('stream_wrappers', $wrappers);
    $existing = stream_get_wrappers();
    foreach ($wrappers as $scheme => $info) {
      // We only register classes that implement our interface.
      if (in_array('DrupalStreamWrapperInterface', class_implements($info['class']), TRUE)) {
        // Record whether we are overriding an existing scheme.
        if (in_array($scheme, $existing, TRUE)) {
          $wrappers[$scheme]['override'] = TRUE;
          stream_wrapper_unregister($scheme);
        }
        else {
          $wrappers[$scheme]['override'] = FALSE;
        }
        if (($info['type'] & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) {
          stream_wrapper_register($scheme, $info['class']);
        }
        else {
          stream_wrapper_register($scheme, $info['class'], STREAM_IS_URL);
        }
      }
      // Pre-populate the static cache with the filters most typically used.
      $wrappers_storage[STREAM_WRAPPERS_ALL][$scheme] = $wrappers[$scheme];
      if (($info['type'] & STREAM_WRAPPERS_WRITE_VISIBLE) == STREAM_WRAPPERS_WRITE_VISIBLE) {
        $wrappers_storage[STREAM_WRAPPERS_WRITE_VISIBLE][$scheme] = $wrappers[$scheme];
      }
    }
  }

  if (!isset($wrappers_storage[$filter])) {
    $wrappers_storage[$filter] = array();
    foreach ($wrappers_storage[STREAM_WRAPPERS_ALL] as $scheme => $info) {
      // Bit-wise filter.
      if (($info['type'] & $filter) == $filter) {
        $wrappers_storage[$filter][$scheme] = $info;
      }
    }
  }

  return $wrappers_storage[$filter];
}

/**
 * Returns the stream wrapper class name for a given scheme.
 *
 * @param $scheme
 *   Stream scheme.
 *
 * @return
 *   Return string if a scheme has a registered handler, or FALSE.
 */
function file_stream_wrapper_get_class($scheme) {
  $wrappers = file_get_stream_wrappers();
  return empty($wrappers[$scheme]) ? FALSE : $wrappers[$scheme]['class'];
}

/**
 * Returns the scheme of a URI (e.g. a stream).
 *
 * @param $uri
 *   A stream, referenced as "scheme://target".
 *
 * @return
 *   A string containing the name of the scheme, or FALSE if none. For example,
 *   the URI "public://example.txt" would return "public".
 *
 * @see file_uri_target()
 */
function file_uri_scheme($uri) {
  $data = explode('://', $uri, 2);

  return count($data) == 2 ? $data[0] : FALSE;
}

/**
 * Check that the scheme of a stream URI is valid.
 *
 * Confirms that there is a registered stream handler for the provided scheme
 * and that it is callable. This is useful if you want to confirm a valid
 * scheme without creating a new instance of the registered handler.
 *
 * @param $scheme
 *   A URI scheme, a stream is referenced as "scheme://target".
 *
 * @return
 *   Returns TRUE if the string is the name of a validated stream,
 *   or FALSE if the scheme does not have a registered handler.
 */
function file_stream_wrapper_valid_scheme($scheme) {
  // Does the scheme have a registered handler that is callable?
  $class = file_stream_wrapper_get_class($scheme);
  if (class_exists($class)) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}


/**
 * Returns the part of an URI after the schema.
 *
 * @param $uri
 *   A stream, referenced as "scheme://target".
 *
 * @return
 *   A string containing the target (path), or FALSE if none.
 *   For example, the URI "public://sample/test.txt" would return
 *   "sample/test.txt".
 *
 * @see file_uri_scheme()
 */
function file_uri_target($uri) {
  $data = explode('://', $uri, 2);

  // Remove erroneous leading or trailing, forward-slashes and backslashes.
  return count($data) == 2 ? trim($data[1], '\/') : FALSE;
}

/**
 * Get the default file stream implementation.
 *
 * @return
 *   'public', 'private' or any other file scheme defined as the default.
 */
function file_default_scheme() {
  return variable_get('file_default_scheme', 'public');
}

/**
 * Normalizes a URI by making it syntactically correct.
 *
 * A stream is referenced as "scheme://target".
 *
 * The following actions are taken:
 * - Remove trailing slashes from target
 * - Trim erroneous leading slashes from target. e.g. ":///" becomes "://".
 *
 * @param $uri
 *   String reference containing the URI to normalize.
 *
 * @return
 *   The normalized URI.
 */
function file_stream_wrapper_uri_normalize($uri) {
  $scheme = file_uri_scheme($uri);

  if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
    $target = file_uri_target($uri);

    if ($target !== FALSE) {
      $uri = $scheme . '://' . $target;
    }
  }
  else {
    // The default scheme is file://
    $url = 'file://' . $uri;
  }
  return $uri;
}

/**
 * Returns a reference to the stream wrapper class responsible for a given URI.
 *
 * 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
 *   Returns a new stream wrapper object appropriate for the given URI or FALSE
 *   if no registered handler could be found. For example, a URI of
 *   "private://example.txt" would return a new private stream wrapper object
 *   (DrupalPrivateStreamWrapper).
 */
function file_stream_wrapper_get_instance_by_uri($uri) {
  $scheme = file_uri_scheme($uri);
  $class = file_stream_wrapper_get_class($scheme);
  if (class_exists($class)) {
    $instance = new $class();
    $instance->setUri($uri);
    return $instance;
  }
  else {
    return FALSE;
  }
}

/**
 * Returns a reference to the stream wrapper class responsible for a given scheme.
 *
 * This helper method returns a stream instance using a scheme. That is, the
 * passed string does not contain 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
 *   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.
 */
function file_stream_wrapper_get_instance_by_scheme($scheme) {
  $class = file_stream_wrapper_get_class($scheme);
  if (class_exists($class)) {
    $instance = new $class();
    $instance->setUri($scheme . '://');
    return $instance;
  }
  else {
    return FALSE;
  }
}

/**
 * Creates a web-accessible URL for a stream to an external or local file.
 *
 * Compatibility: normal paths and stream wrappers.
 * @see http://drupal.org/node/515192
 *
 * There are two kinds of local files:
 * - "managed files", i.e. those stored by a Drupal-compatible stream wrapper.
 *   These are files that have either been uploaded by users or were generated
 *   automatically (for example through CSS aggregation).
 * - "shipped files", i.e. those outside of the files directory, which ship as
 *   part of Drupal core or contributed modules or themes.
 *
 * @param $uri
 *   The URI to a file for which we need an external URL, or the path to a
 *   shipped file.
 *
 * @return
 *   A string containing a URL that may be used to access the file.
 *   If the provided string already contains a preceding 'http', 'https', or
 *   '/', nothing is done and the same string is returned. If a stream wrapper
 *   could not be found to generate an external URL, then FALSE is returned.
 */
function file_create_url($uri) {
  // Allow the URI to be altered, e.g. to serve a file from a CDN or static
  // file server.
  drupal_alter('file_url', $uri);

  $scheme = file_uri_scheme($uri);

  if (!$scheme) {
    // Allow for:
    // - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
    // - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
    //   http://example.com/bar.jpg by the browser when viewing a page over
    //   HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
    // Both types of relative URIs are characterized by a leading slash, hence
    // we can use a single check.
    if (drupal_substr($uri, 0, 1) == '/') {
      return $uri;
    }
    else {
      // If this is not a properly formatted stream, then it is a shipped file.
      // Therefore, return the urlencoded URI with the base URL prepended.
      return $GLOBALS['base_url'] . '/' . drupal_encode_path($uri);
    }
  }
  elseif ($scheme == 'http' || $scheme == 'https') {
    // Check for http so that we don't have to implement getExternalUrl() for
    // the http wrapper.
    return $uri;
  }
  else {
    // Attempt to return an external URL using the appropriate wrapper.
    if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
      return $wrapper->getExternalUrl();
    }
    else {
      return FALSE;
    }
  }
}

/**
 * Check that the directory exists and is writable.
 *
 * Directories need to have execute permissions to be considered a directory by
 * FTP servers, etc.
 *
 * @param &$directory
 *   A string reference containing the name of a directory path or URI. A
 *   trailing slash will be trimmed from a path.
 * @param $options
 *   A bitmask to indicate if the directory should be created if it does
 *   not exist (FILE_CREATE_DIRECTORY) or made writable if it is read-only
 *   (FILE_MODIFY_PERMISSIONS).
 *
 * @return
 *   TRUE if the directory exists (or was created) and is writable. FALSE
 *   otherwise.
 */
function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS) {
  if (!file_stream_wrapper_valid_scheme(file_uri_scheme($directory))) {
    // Only trim if we're not dealing with a stream.
    $directory = rtrim($directory, '/\\');
  }

  // Check if directory exists.
  if (!is_dir($directory)) {
    // Let mkdir() recursively create directories and use the default directory
    // permissions.
    if (($options & FILE_CREATE_DIRECTORY) && @drupal_mkdir($directory, NULL, TRUE)) {
      return drupal_chmod($directory);
    }
    return FALSE;
  }
  // The directory exists, so check to see if it is writable.
  $writable = is_writable($directory);
  if (!$writable && ($options & FILE_MODIFY_PERMISSIONS)) {
    return drupal_chmod($directory);
  }

  return $writable;
}

/**
 * If missing, create a .htaccess file in each Drupal files directory.
 */
function file_ensure_htaccess() {
  file_create_htaccess('public://', FALSE);
  if (variable_get('file_private_path', FALSE)) {
    file_create_htaccess('private://', TRUE);
  }
  file_create_htaccess('temporary://', TRUE);
}

/**
 * Creates an .htaccess file in the given directory.
 *
 * @param $directory
 *   The directory.
 * @param $private
 *   FALSE indicates that $directory should be an open and public directory.
 *   The default is TRUE which indicates a private and protected directory.
 */
function file_create_htaccess($directory, $private = TRUE) {
  if (file_uri_scheme($directory)) {
    $directory = file_stream_wrapper_uri_normalize($directory);
  }
  else {
    $directory = rtrim($directory, '/\\');
  }
  $htaccess_path =  $directory . '/.htaccess';

  if (file_exists($htaccess_path)) {
    // Short circuit if the .htaccess file already exists.
    return;
  }

  if ($private) {
    // Private .htaccess file.
    $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nDeny from all\nOptions None\nOptions +FollowSymLinks";
  }
  else {
    // Public .htaccess file.
    $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks";
  }

  // Write the .htaccess file.
  if (file_put_contents($htaccess_path, $htaccess_lines)) {
    drupal_chmod($htaccess_path, 0444);
  }
  else {
    $variables = array('%directory' => $directory, '!htaccess' => '<br />' . nl2br(check_plain($htaccess_lines)));
    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);
  }
}

/**
 * Loads file objects from the database.
 *
 * @param $fids
 *   An array of file IDs.
 * @param $conditions
 *   (deprecated) An associative array of conditions on the {file_managed}
 *   table, where the keys are the database fields and the values are the
 *   values those fields must have. Instead, it is preferable to use
 *   EntityFieldQuery to retrieve a list of entity IDs loadable by
 *   this function.
 *
 * @return
 *   An array of file objects, indexed by fid.
 *
 * @see hook_file_load()
 * @see file_load()
 * @see entity_load()
 * @see EntityFieldQuery
 *
 * @todo Remove $conditions in Drupal 8.
 */
function file_load_multiple($fids = array(), $conditions = array()) {
  return entity_load('file', $fids, $conditions);
}

/**
 * Load a file object from the database.
 *
 * @param $fid
 *   A file ID.
 *
 * @return
 *   A file object.
 *
 * @see hook_file_load()
 * @see file_load_multiple()
 */
function file_load($fid) {
  $files = file_load_multiple(array($fid), array());
  return reset($files);
}

/**
 * Save a file object to the database.
 *
 * If the $file->fid is not set a new record will be added.
 *
 * @param $file
 *   A file object returned by file_load().
 *
 * @return
 *   The updated file object.
 *
 * @see hook_file_insert()
 * @see hook_file_update()
 */
function file_save(stdClass $file) {
  $file->timestamp = REQUEST_TIME;
  $file->filesize = filesize($file->uri);

  // Load the stored entity, if any.
  if (!empty($file->fid) && !isset($file->original)) {
    $file->original = entity_load_unchanged('file', $file->fid);
  }

  module_invoke_all('file_presave', $file);
  module_invoke_all('entity_presave', $file, 'file');

  if (empty($file->fid)) {
    drupal_write_record('file_managed', $file);
    // Inform modules about the newly added file.
    module_invoke_all('file_insert', $file);
    module_invoke_all('entity_insert', $file, 'file');
  }
  else {
    drupal_write_record('file_managed', $file, 'fid');
    // Inform modules that the file has been updated.
    module_invoke_all('file_update', $file);
    module_invoke_all('entity_update', $file, 'file');
  }

  unset($file->original);
  return $file;
}

/**
 * Determines where a file is used.
 *
 * @param $file
 *   A file object.
 *
 * @return
 *   A nested array with usage data. The first level is keyed by module name,
 *   the second by object type, the third has 'id' and 'count' keys.
 *
 * @see file_usage_add()
 * @see file_usage_delete()
 */
function file_usage_list(stdClass $file) {
  $result = db_select('file_usage', 'f')
    ->fields('f', array('module', 'type', 'id', 'count'))
    ->condition('fid', $file->fid)
    ->condition('count', 0, '>')
    ->execute();
  $references = array();
  foreach ($result as $usage) {
    $references[$usage->module][$usage->type] = array('id' => $usage->id, 'count' => $usage->count);
  }
  return $references;
}

/**
 * Records that a module is using a file.
 *
 * This usage information will be queried during file_delete() to ensure that
 * a file is not in use before it is physically removed from disk.
 *
 * Examples:
 * - A module that associates files with nodes, so $type would be
 *   'node' and $id would be the node's nid. Files for all revisions are stored
 *   within a single nid.
 * - The User module associates an image with a user, so $type would be 'user'
 *   and the $id would be the user's uid.
 *
 * @param $file
 *   A file object.
 * @param $module
 *   The name of the module using the file.
 * @param $type
 *   The type of the object that contains the referenced file.
 * @param $id
 *   The unique, numeric ID of the object containing the referenced file.
 * @param $count
 *   (optional) The number of references to add to the object. Defaults to 1.
 *
 * @see file_usage_list()
 * @see file_usage_delete()
 */
function file_usage_add(stdClass $file, $module, $type, $id, $count = 1) {
  db_merge('file_usage')
    ->key(array(
      'fid' => $file->fid,
      'module' => $module,
      'type' => $type,
      'id' => $id,
    ))
    ->fields(array('count' => $count))
    ->expression('count', 'count + :count', array(':count' => $count))
    ->execute();
}

/**
 * Removes a record to indicate that a module is no longer using a file.
 *
 * The file_delete() function is typically called after removing a file usage
 * to remove the record from the file_managed table and delete the file itself.
 *
 * @param $file
 *   A file object.
 * @param $module
 *   The name of the module using the file.
 * @param $type
 *   (optional) The type of the object that contains the referenced file. May
 *   be omitted if all module references to a file are being deleted.
 * @param $id
 *   (optional) The unique, numeric ID of the object containing the referenced
 *   file. May be omitted if all module references to a file are being deleted.
 * @param $count
 *   (optional) The number of references to delete from the object. Defaults to
 *   1. 0 may be specified to delete all references to the file within a
 *   specific object.
 *
 * @see file_usage_add()
 * @see file_usage_list()
 * @see file_delete()
 */
function file_usage_delete(stdClass $file, $module, $type = NULL, $id = NULL, $count = 1) {
  // Delete rows that have a exact or less value to prevent empty rows.
  $query = db_delete('file_usage')
    ->condition('module', $module)
    ->condition('fid', $file->fid);
  if ($type && $id) {
    $query
      ->condition('type', $type)
      ->condition('id', $id);
  }
  if ($count) {
    $query->condition('count', $count, '<=');
  }
  $result = $query->execute();

  // If the row has more than the specified count decrement it by that number.
  if (!$result && $count > 0) {
    $query = db_update('file_usage')
      ->condition('module', $module)
      ->condition('fid', $file->fid);
    if ($type && $id) {
      $query
        ->condition('type', $type)
        ->condition('id', $id);
    }
    $query->expression('count', 'count - :count', array(':count' => $count));
    $query->execute();
  }
}

/**
 * Copies a file to a new location and adds a file record to the database.
 *
 * This function should be used when manipulating files that have records
 * stored in the database. This is a powerful function that in many ways
 * performs like an advanced version of copy().
 * - Checks if $source and $destination are valid and readable/writable.
 * - Checks that $source is not equal to $destination; if they are an error
 *   is reported.
 * - If file already exists in $destination either the call will error out,
 *   replace the file or rename the file based on the $replace parameter.
 * - Adds the new file to the files database. If the source file is a
 *   temporary file, the resulting file will also be a temporary file. See
 *   file_save_upload() for details on temporary files.
 *
 * @param $source
 *   A file object.
 * @param $destination
 *   A string containing the destination that $source should be copied to.
 *   This must be a stream wrapper URI. If this value is omitted, Drupal's
 *   default files scheme will be used, usually "public://".
 * @param $replace
 *   Replace behavior when the destination file already exists:
 *   - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with
 *       the destination name exists then its database entry will be updated. If
 *       no database entry is found then a new one will be created.
 *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
 *       unique.
 *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
 *
 * @return
 *   File object if the copy is successful, or FALSE in the event of an error.
 *
 * @see file_unmanaged_copy()
 * @see hook_file_copy()
 */
function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  if (!file_valid_uri($destination)) {
    watchdog('file', 'File %file (%realpath) could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => drupal_realpath($source->uri), '%destination' => $destination));
    drupal_set_message(t('The specified file %file could not be copied, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error');
    return FALSE;
  }

  if ($uri = file_unmanaged_copy($source->uri, $destination, $replace)) {
    $file = clone $source;
    $file->fid = NULL;
    $file->uri = $uri;
    $file->filename = basename($uri);
    // If we are replacing an existing file re-use its database record.
    if ($replace == FILE_EXISTS_REPLACE) {
      $existing_files = file_load_multiple(array(), array('uri' => $uri));
      if (count($existing_files)) {
        $existing = reset($existing_files);
        $file->fid = $existing->fid;
        $file->filename = $existing->filename;
      }
    }
    // If we are renaming around an existing file (rather than a directory),
    // use its basename for the filename.
    elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
      $file->filename = basename($destination);
    }

    $file = file_save($file);

    // Inform modules that the file has been copied.
    module_invoke_all('file_copy', $file, $source);

    return $file;
  }
  return FALSE;
}

/**
 * Determine whether the URI has a valid scheme for file API operations.
 *
 * There must be a scheme and it must be a Drupal-provided scheme like
 * 'public', 'private', 'temporary', or an extension provided with
 * hook_stream_wrappers().
 *
 * @param $uri
 *   The URI to be tested.
 *
 * @return
 *   TRUE if the URI is allowed.
 */
function file_valid_uri($uri) {
  // Assert that the URI has an allowed scheme. Barepaths are not allowed.
  $uri_scheme = file_uri_scheme($uri);
  if (empty($uri_scheme) || !file_stream_wrapper_valid_scheme($uri_scheme)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Copies a file to a new location without invoking the file API.
 *
 * This is a powerful function that in many ways performs like an advanced
 * version of copy().
 * - Checks if $source and $destination are valid and readable/writable.
 * - Checks that $source is not equal to $destination; if they are an error
 *   is reported.
 * - If file already exists in $destination either the call will error out,
 *   replace the file or rename the file based on the $replace parameter.
 *
 * @param $source
 *   A string specifying the filepath or URI of the source file.
 * @param $destination
 *   A URI containing the destination that $source should be copied to. The
 *   URI may be a bare filepath (without a scheme) and in that case the default
 *   scheme (file://) will be used. If this value is omitted, Drupal's default
 *   files scheme will be used, usually "public://".
 * @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
 *   The path to the new file, or FALSE in the event of an error.
 *
 * @see file_copy()
 */
function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  $original_source = $source;
  $original_destination = $destination;

  // Assert that the source file actually exists.
  $source = drupal_realpath($source);
  if (!file_exists($source)) {
    // @todo Replace drupal_set_message() calls with exceptions instead.
    drupal_set_message(t('The specified file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error');
    watchdog('file', 'File %file (%realpath) could not be copied because it does not exist.', array('%file' => $original_source, '%realpath' => drupal_realpath($original_source)));
    return FALSE;
  }

  // Build a destination URI if necessary.
  if (!isset($destination)) {
    $destination = file_build_uri(basename($source));
  }


  // Prepare the destination directory.
  if (file_prepare_directory($destination)) {
    // The destination is already a directory, so append the source basename.
    $destination = file_stream_wrapper_uri_normalize($destination . '/' . basename($source));
  }
  else {
    // Perhaps $destination is a dir/file?
    $dirname = drupal_dirname($destination);
    if (!file_prepare_directory($dirname)) {
      // The destination is not valid.
      watchdog('file', 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array('%file' => $original_source, '%destination' => drupal_realpath($dirname)));
      drupal_set_message(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $original_source)), 'error');
      return FALSE;
    }
  }

  // Determine whether we can perform this operation based on overwrite rules.
  $destination = file_destination($destination, $replace);
  if ($destination === FALSE) {
    drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory.', array('%file' => $original_source)), 'error');
    watchdog('file', 'File %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $original_source, '%destination' => drupal_realpath($destination)));
    return FALSE;
  }

  // Assert that the source and destination filenames are not the same.
  if (drupal_realpath($source) == drupal_realpath($destination)) {
    drupal_set_message(t('The specified file %file was not copied because it would overwrite itself.', array('%file' => $source)), 'error');
    watchdog('file', 'File %file could not be copied because it would overwrite itself.', array('%file' => $source));
    return FALSE;
  }
  // Make sure the .htaccess files are present.
  file_ensure_htaccess();
  // Perform the copy operation.
  if (!@copy($source, $destination)) {
    watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => drupal_realpath($destination)), WATCHDOG_ERROR);
    return FALSE;
  }

  // Set the permissions on the new file.
  drupal_chmod($destination);

  return $destination;
}

/**
 * Given a relative path, construct a URI into Drupal's default files location.
 */
function file_build_uri($path) {
  $uri = file_default_scheme() . '://' . $path;
  return file_stream_wrapper_uri_normalize($uri);
}

/**
 * Determines the destination path for a file depending on how replacement of
 * existing files should be handled.
 *
 * @param $destination
 *   A string specifying the desired final URI or filepath.
 * @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
 *   The destination filepath, or FALSE if the file already exists
 *   and FILE_EXISTS_ERROR is specified.
 */
function file_destination($destination, $replace) {
  if (file_exists($destination)) {
    switch ($replace) {
      case FILE_EXISTS_REPLACE:
        // Do nothing here, we want to overwrite the existing file.
        break;

      case FILE_EXISTS_RENAME:
        $basename = basename($destination);
        $directory = drupal_dirname($destination);
        $destination = file_create_filename($basename, $directory);
        break;

      case FILE_EXISTS_ERROR:
        // Error reporting handled by calling function.
        return FALSE;
    }
  }
  return $destination;
}

/**
 * Move a file to a new location and update the file's database entry.
 *
 * Moving a file is performed by copying the file to the new location and then
 * deleting the original.
 * - Checks if $source and $destination are valid and readable/writable.
 * - Performs a file move if $source is not equal to $destination.
 * - If file already exists in $destination either the call will error out,
 *   replace the file or rename the file based on the $replace parameter.
 * - Adds the new file to the files database.
 *
 * @param $source
 *   A file object.
 * @param $destination
 *   A string containing the destination that $source should be moved to.
 *   This must be a stream wrapper URI. If this value is omitted, Drupal's
 *   default files scheme will be used, usually "public://".
 * @param $replace
 *   Replace behavior when the destination file already exists:
 *   - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with
 *       the destination name exists then its database entry will be updated and
 *       file_delete() called on the source file after hook_file_move is called.
 *       If no database entry is found then the source files record will be
 *       updated.
 *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
 *       unique.
 *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
 *
 * @return
 *   Resulting file object for success, or FALSE in the event of an error.
 *
 * @see file_unmanaged_move()
 * @see hook_file_move()
 */
function file_move(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  if (!file_valid_uri($destination)) {
    watchdog('file', 'File %file (%realpath) could not be moved, because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => drupal_realpath($source->uri), '%destination' => $destination));
    drupal_set_message(t('The specified file %file could not be moved, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error');
    return FALSE;
  }

  if ($uri = file_unmanaged_move($source->uri, $destination, $replace)) {
    $delete_source = FALSE;

    $file = clone $source;
    $file->uri = $uri;
    // If we are replacing an existing file re-use its database record.
    if ($replace == FILE_EXISTS_REPLACE) {
      $existing_files = file_load_multiple(array(), array('uri' => $uri));
      if (count($existing_files)) {
        $existing = reset($existing_files);
        $delete_source = TRUE;
        $file->fid = $existing->fid;
      }
    }
    // If we are renaming around an existing file (rather than a directory),
    // use its basename for the filename.
    elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
      $file->filename = basename($destination);
    }

    $file = file_save($file);

    // Inform modules that the file has been moved.
    module_invoke_all('file_move', $file, $source);

    if ($delete_source) {
      // Try a soft delete to remove original if it's not in use elsewhere.
      file_delete($source);
    }

    return $file;
  }
  return FALSE;
}

/**
 * Move a file to a new location without calling any hooks or making any
 * changes to the database.
 *
 * @param $source
 *   A string specifying the filepath or URI of the original file.
 * @param $destination
 *   A string containing the destination that $source should be moved to.
 *   This must be a stream wrapper URI. If this value is omitted, Drupal's
 *   default files scheme will be used, usually "public://".
 * @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
 *   The URI of the moved file, or FALSE in the event of an error.
 *
 * @see file_move()
 */
function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  $filepath = file_unmanaged_copy($source, $destination, $replace);
  if ($filepath == FALSE || file_unmanaged_delete($source) == FALSE) {
    return FALSE;
  }
  return $filepath;
}

/**
 * Modify a filename as needed for security purposes.
 *
 * Munging a file name prevents unknown file extensions from masking exploit
 * files. When web servers such as Apache decide how to process a URL request,
 * they use the file extension. If the extension is not recognized, Apache
 * skips that extension and uses the previous file extension. For example, if
 * the file being requested is exploit.php.pps, and Apache does not recognize
 * the '.pps' extension, it treats the file as PHP and executes it. To make
 * this file name safe for Apache and prevent it from executing as PHP, the
 * .php extension is "munged" into .php_, making the safe file name
 * exploit.php_.pps.
 *
 * Specifically, this function adds an underscore to all extensions that are
 * between 2 and 5 characters in length, internal to the file name, and not
 * included in $extensions.
 *
 * Function behavior is also controlled by the Drupal variable
 * 'allow_insecure_uploads'. If 'allow_insecure_uploads' evaluates to TRUE, no
 * alterations will be made, if it evaluates to FALSE, the filename is 'munged'.
 *
 * @param $filename
 *   File name to modify.
 * @param $extensions
 *   A space-separated list of extensions that should not be altered.
 * @param $alerts
 *   If TRUE, drupal_set_message() will be called to display a message if the
 *   file name was changed.
 *
 * @return
 *   The potentially modified $filename.
 */
function file_munge_filename($filename, $extensions, $alerts = TRUE) {
  $original = $filename;

  // Allow potentially insecure uploads for very savvy users and admin
  if (!variable_get('allow_insecure_uploads', 0)) {
    $whitelist = array_unique(explode(' ', trim($extensions)));

    // Split the filename up by periods. The first part becomes the basename
    // the last part the final extension.
    $filename_parts = explode('.', $filename);
    $new_filename = array_shift($filename_parts); // Remove file basename.
    $final_extension = array_pop($filename_parts); // Remove final extension.

    // Loop through the middle parts of the name and add an underscore to the
    // end of each section that could be a file extension but isn't in the list
    // of allowed extensions.
    foreach ($filename_parts as $filename_part) {
      $new_filename .= '.' . $filename_part;
      if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
        $new_filename .= '_';
      }
    }
    $filename = $new_filename . '.' . $final_extension;

    if ($alerts && $original != $filename) {
      drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $filename)));
    }
  }

  return $filename;
}

/**
 * Undo the effect of upload_munge_filename().
 *
 * @param $filename
 *   String with the filename to be unmunged.
 *
 * @return
 *   An unmunged filename string.
 */
function file_unmunge_filename($filename) {
  return str_replace('_.', '.', $filename);
}

/**
 * Create a full file path from a directory and filename.
 *
 * If a file with the specified name already exists, an alternative will be
 * used.
 *
 * @param $basename
 *   String filename
 * @param $directory
 *   String containing the directory or parent URI.
 *
 * @return
 *   File path consisting of $directory and a unique filename based off
 *   of $basename.
 */
function file_create_filename($basename, $directory) {
  // Strip control characters (ASCII value < 32). Though these are allowed in
  // some filesystems, not many applications handle them well.
  $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename);
  if (substr(PHP_OS, 0, 3) == 'WIN') {
    // These characters are not allowed in Windows filenames
    $basename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $basename);
  }

  // A URI or path may already have a trailing slash or look like "public://".
  if (substr($directory, -1) == '/') {
    $separator = '';
  }
  else {
    $separator = '/';
  }

  $destination = $directory . $separator . $basename;

  if (file_exists($destination)) {
    // Destination file already exists, generate an alternative.
    $pos = strrpos($basename, '.');
    if ($pos !== FALSE) {
      $name = substr($basename, 0, $pos);
      $ext = substr($basename, $pos);
    }
    else {
      $name = $basename;
      $ext = '';
    }

    $counter = 0;
    do {
      $destination = $directory . $separator . $name . '_' . $counter++ . $ext;
    } while (file_exists($destination));
  }

  return $destination;
}

/**
 * Delete a file and its database record.
 *
 * If the $force parameter is not TRUE, file_usage_list() will be called to
 * determine if the file is being used by any modules. If the file is being
 * used the delete will be canceled.
 *
 * @param $file
 *   A file object.
 * @param $force
 *   Boolean indicating that the file should be deleted even if the file is
 *   reported as in use by the file_usage table.
 *
 * @return mixed
 *   TRUE for success, FALSE in the event of an error, or an array if the file
 *   is being used by any modules.
 *
 * @see file_unmanaged_delete()
 * @see file_usage_list()
 * @see file_usage_delete()
 * @see hook_file_delete()
 */
function file_delete(stdClass $file, $force = FALSE) {
  if (!file_valid_uri($file->uri)) {
    watchdog('file', 'File %file (%realpath) could not be deleted because it is not a valid URI. This may be caused by improper use of file_delete() or a missing stream wrapper.', array('%file' => $file->uri, '%realpath' => drupal_realpath($file->uri)));
    drupal_set_message(t('The specified file %file could not be deleted, because it is not a valid URI. More information is available in the system log.', array('%file' => $file->uri)), 'error');
    return FALSE;
  }

  // If any module still has a usage entry in the file_usage table, the file
  // will not be deleted, but file_delete() will return a populated array
  // that tests as TRUE.
  if (!$force && ($references = file_usage_list($file))) {
    return $references;
  }

  // Let other modules clean up any references to the deleted file.
  module_invoke_all('file_delete', $file);
  module_invoke_all('entity_delete', $file, 'file');

  // Make sure the file is deleted before removing its row from the
  // database, so UIs can still find the file in the database.
  if (file_unmanaged_delete($file->uri)) {
    db_delete('file_managed')->condition('fid', $file->fid)->execute();
    db_delete('file_usage')->condition('fid', $file->fid)->execute();
    return TRUE;
  }
  return FALSE;
}

/**
 * Delete a file without calling any hooks or making any changes to the
 * database.
 *
 * This function should be used when the file to be deleted does not have an
 * entry recorded in the files table.
 *
 * @param $path
 *   A string containing a file path or (streamwrapper) URI.
 *
 * @return
 *   TRUE for success or path does not exist, or FALSE in the event of an
 *   error.
 *
 * @see file_delete()
 * @see file_unmanaged_delete_recursive()
 */
function file_unmanaged_delete($path) {
  // Resolve streamwrapper URI to local path.
  $path = drupal_realpath($path);
  if (is_dir($path)) {
    watchdog('file', '%path is a directory and cannot be removed using file_unmanaged_delete().', array('%path' => $path), WATCHDOG_ERROR);
    return FALSE;
  }
  if (is_file($path)) {
    return drupal_unlink($path);
  }
  // Return TRUE for non-existent file, but log that nothing was actually
  // deleted, as the current state is the intended result.
  if (!file_exists($path)) {
    watchdog('file', 'The file %path was not deleted, because it does not exist.', array('%path' => $path), WATCHDOG_NOTICE);
    return TRUE;
  }
  // We cannot handle anything other than files and directories. Log an error
  // for everything else (sockets, symbolic links, etc).
  watchdog('file', 'The file %path is not of a recognized type so it was not deleted.', array('%path' => $path), WATCHDOG_ERROR);
  return FALSE;
}

/**
 * Recursively delete all files and directories in the specified filepath.
 *
 * If the specified path is a directory then the function will call itself
 * recursively to process the contents. Once the contents have been removed the
 * directory will also be removed.
 *
 * If the specified path is a file then it will be passed to
 * file_unmanaged_delete().
 *
 * Note that this only deletes visible files with write permission.
 *
 * @param $path
 *   A string containing either an URI or a file or directory path.
 *
 * @return
 *   TRUE for success or if path does not exist, FALSE in the event of an
 *   error.
 *
 * @see file_unmanaged_delete()
 */
function file_unmanaged_delete_recursive($path) {
  // Resolve streamwrapper URI to local path.
  $path = drupal_realpath($path);
  if (is_dir($path)) {
    $dir = dir($path);
    while (($entry = $dir->read()) !== FALSE) {
      if ($entry == '.' || $entry == '..') {
        continue;
      }
      $entry_path = $path . '/' . $entry;
      file_unmanaged_delete_recursive($entry_path);
    }
    $dir->close();

    return drupal_rmdir($path);
  }
  return file_unmanaged_delete($path);
}

/**
 * Determine total disk space used by a single user or the whole filesystem.
 *
 * @param $uid
 *   Optional. A user id, specifying NULL returns the total space used by all
 *   non-temporary files.
 * @param $status
 *   Optional. The file status to consider. The default is to only
 *   consider files in status FILE_STATUS_PERMANENT.
 *
 * @return
 *   An integer containing the number of bytes used.
 */
function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) {
  $query = db_select('file_managed', 'f');
  $query->condition('f.status', $status);
  $query->addExpression('SUM(f.filesize)', 'filesize');
  if (isset($uid)) {
    $query->condition('f.uid', $uid);
  }
  return $query->execute()->fetchField();
}

/**
 * Saves a file upload to a new location.
 *
 * The file will be added to the {file_managed} table as a temporary file.
 * Temporary files are periodically cleaned. To make the file a permanent file,
 * assign the status and use file_save() to save the changes.
 *
 * @param $source
 *   A string specifying the filepath or URI of the uploaded file to save.
 * @param $validators
 *   An optional, associative array of callback functions used to validate the
 *   file. See file_validate() for a full discussion of the array format.
 *   If no extension validator is provided it will default to a limited safe
 *   list of extensions which is as follows: "jpg jpeg gif png txt
 *   doc xls pdf ppt pps odt ods odp". To allow all extensions you must
 *   explicitly set the 'file_validate_extensions' validator to an empty array
 *   (Beware: this is not safe and should only be allowed for trusted users, if
 *   at all).
 * @param $destination
 *   A string containing the URI $source should be copied to.
 *   This must be a stream wrapper URI. If this value is omitted, Drupal's
 *   temporary files scheme will be used ("temporary://").
 * @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
 *   An object containing the file information if the upload succeeded, FALSE
 *   in the event of an error, or NULL if no file was uploaded. The
 *   documentation for the "File interface" group, which you can find under
 *   Related topics, or the header at the top of this file, documents the
 *   components of a file object. In addition to the standard components,
 *   this function adds:
 *   - source: Path to the file before it is moved.
 *   - destination: Path to the file after it is moved (same as 'uri').
 */
function file_save_upload($source, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
  global $user;
  static $upload_cache;

  // 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];
  }

  // 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' => $_FILES['files']['name'][$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' => $_FILES['files']['name'][$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;
      }

    // Unknown error
    default:
      drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$source])), 'error');
      return FALSE;
  }

  // Begin building file object.
  $file = new stdClass();
  $file->uid      = $user->uid;
  $file->status   = 0;
  $file->filename = trim(basename($_FILES['files']['name'][$source]), '.');
  $file->uri      = $_FILES['files']['tmp_name'][$source];
  $file->filemime = file_get_mimetype($file->filename);
  $file->filesize = $_FILES['files']['size'][$source];

  $extensions = '';
  if (isset($validators['file_validate_extensions'])) {
    if (isset($validators['file_validate_extensions'][0])) {
      // Build the list of non-munged extensions if the caller provided them.
      $extensions = $validators['file_validate_extensions'][0];
    }
    else {
      // If 'file_validate_extensions' is set and the list is empty then the
      // caller wants to allow any extension. In this case we have to remove the
      // validator or else it will reject all extensions.
      unset($validators['file_validate_extensions']);
    }
  }
  else {
    // No validator was provided, so add one using the default list.
    // Build a default non-munged safe list for file_munge_filename().
    $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
    $validators['file_validate_extensions'] = array();
    $validators['file_validate_extensions'][0] = $extensions;
  }

  if (!empty($extensions)) {
    // Munge the filename to protect against possible malicious extension hiding
    // within an unknown file type (ie: filename.html.foo).
    $file->filename = file_munge_filename($file->filename, $extensions);
  }

  // Rename potentially executable files, to help prevent exploits (i.e. will
  // rename filename.php.foo and filename.php to filename.php.foo.txt and
  // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
  // evaluates to TRUE.
  if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) {
    $file->filemime = 'text/plain';
    $file->uri .= '.txt';
    $file->filename .= '.txt';
    // The .txt extension may not be in the allowed list of extensions. We have
    // to add it here or else the file upload will fail.
    if (!empty($extensions)) {
      $validators['file_validate_extensions'][0] .= ' txt';
      drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename)));
    }
  }

  // If the destination is not provided, use the temporary directory.
  if (empty($destination)) {
    $destination = 'temporary://';
  }

  // Assert that the destination contains a valid stream.
  $destination_scheme = file_uri_scheme($destination);
  if (!$destination_scheme || !file_stream_wrapper_valid_scheme($destination_scheme)) {
    drupal_set_message(t('The file could not be uploaded, because the destination %destination is invalid.', array('%destination' => $destination)), 'error');
    return FALSE;
  }

  $file->source = $source;
  // A URI may already have a trailing slash or look like "public://".
  if (substr($destination, -1) != '/') {
    $destination .= '/';
  }
  $file->destination = file_destination($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) {
    drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error');
    return FALSE;
  }

  // Add in our check of the the file name length.
  $validators['file_validate_name_length'] = array();

  // Call the validation functions specified by this function's caller.
  $errors = file_validate($file, $validators);

  // 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', array('items' => $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->uri = $file->destination;
  if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->uri)) {
    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->uri));
    return FALSE;
  }

  // Set the permissions on the new file.
  drupal_chmod($file->uri);

  // If we are replacing an existing file re-use its database record.
  if ($replace == FILE_EXISTS_REPLACE) {
    $existing_files = file_load_multiple(array(), array('uri' => $file->uri));
    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 ($file = file_save($file)) {
    // Add file to the cache.
    $upload_cache[$source] = $file;
    return $file;
  }
  return FALSE;
}


/**
 * Check that a file meets the criteria specified by the validators.
 *
 * After executing the validator callbacks specified hook_file_validate() will
 * also be called to allow other modules to report errors about the file.
 *
 * @param $file
 *   A Drupal file object.
 * @param $validators
 *   An optional, associative array of callback functions used to validate the
 *   file. The keys are function names and the values arrays of callback
 *   parameters which will be passed in after the file object. The
 *   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.
 *
 * @return
 *   An array containing validation error messages.
 *
 * @see hook_file_validate()
 */
function file_validate(stdClass &$file, $validators = array()) {
  // Call the validation functions specified by this function's caller.
  $errors = array();
  foreach ($validators as $function => $args) {
    if (function_exists($function)) {
      array_unshift($args, $file);
      $errors = array_merge($errors, call_user_func_array($function, $args));
    }
  }

  // Let other modules perform validation on the new file.
  return array_merge($errors, module_invoke_all('file_validate', $file));
}

/**
 * Check for files with names longer than we can store in the database.
 *
 * @param $file
 *   A Drupal file object.
 * @return
 *   An array. If the file name is too long, it will contain an error message.
 */
function file_validate_name_length(stdClass $file) {
  $errors = array();

  if (empty($file->filename)) {
    $errors[] = t("The file's name is empty. Please give a name to the file.");
  }
  if (strlen($file->filename) > 240) {
    $errors[] = t("The file's name exceeds the 240 characters limit. Please rename the file and try again.");
  }
  return $errors;
}

/**
 * Check that the filename ends with an allowed extension.
 *
 * @param $file
 *   A Drupal file object.
 * @param $extensions
 *   A string with a space separated list of allowed extensions.
 *
 * @return
 *   An array. If the file extension is not allowed, it will contain an error
 *   message.
 *
 * @see hook_file_validate()
 */
function file_validate_extensions(stdClass $file, $extensions) {
  $errors = array();

  $regex = '/\.(' . preg_replace('/ +/', '|', preg_quote($extensions)) . ')$/i';
  if (!preg_match($regex, $file->filename)) {
    $errors[] = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => $extensions));
  }
  return $errors;
}

/**
 * Check that the file's size is below certain limits.
 *
 * This check is not enforced for the user #1.
 *
 * @param $file
 *   A Drupal file object.
 * @param $file_limit
 *   An integer specifying the maximum file size in bytes. Zero indicates that
 *   no limit should be enforced.
 * @param $user_limit
 *   An integer specifying the maximum number of bytes the user is allowed.
 *   Zero indicates that no limit should be enforced.
 *
 * @return
 *   An array. If the file size exceeds limits, it will contain an error
 *   message.
 *
 * @see hook_file_validate()
 */
function file_validate_size(stdClass $file, $file_limit = 0, $user_limit = 0) {
  global $user;

  $errors = array();

  // Bypass validation for uid  = 1.
  if ($user->uid != 1) {
    if ($file_limit && $file->filesize > $file_limit) {
      $errors[] = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($file->filesize), '%maxsize' => format_size($file_limit)));
    }

    // Save a query by only calling file_space_used() when a limit is provided.
    if ($user_limit && (file_space_used($user->uid) + $file->filesize) > $user_limit) {
      $errors[] = t('The file is %filesize which would exceed your disk quota of %quota.', array('%filesize' => format_size($file->filesize), '%quota' => format_size($user_limit)));
    }
  }
  return $errors;
}

/**
 * Check that the file is recognized by image_get_info() as an image.
 *
 * @param $file
 *   A Drupal file object.
 *
 * @return
 *   An array. If the file is not an image, it will contain an error message.
 *
 * @see hook_file_validate()
 */
function file_validate_is_image(stdClass $file) {
  $errors = array();

  $info = image_get_info($file->uri);
  if (!$info || empty($info['extension'])) {
    $errors[] = t('Only JPEG, PNG and GIF images are allowed.');
  }

  return $errors;
}

/**
 * Verify that image dimensions are within the specified maximum and minimum.
 *
 * Non-image files will be ignored. If a image toolkit is available the image
 * will be scaled to fit within the desired maximum dimensions.
 *
 * @param $file
 *   A Drupal file object. This function may resize the file affecting its
 *   size.
 * @param $maximum_dimensions
 *   An optional string in the form WIDTHxHEIGHT e.g. '640x480' or '85x85'. If
 *   an image toolkit is installed the image will be resized down to these
 *   dimensions. A value of 0 indicates no restriction on size, so resizing
 *   will be attempted.
 * @param $minimum_dimensions
 *   An optional string in the form WIDTHxHEIGHT. This will check that the
 *   image meets a minimum size. A value of 0 indicates no restriction.
 *
 * @return
 *   An array. If the file is an image and did not meet the requirements, it
 *   will contain an error message.
 *
 * @see hook_file_validate()
 */
function file_validate_image_resolution(stdClass $file, $maximum_dimensions = 0, $minimum_dimensions = 0) {
  $errors = array();

  // Check first that the file is an image.
  if ($info = image_get_info($file->uri)) {
    if ($maximum_dimensions) {
      // Check that it is smaller than the given dimensions.
      list($width, $height) = explode('x', $maximum_dimensions);
      if ($info['width'] > $width || $info['height'] > $height) {
        // Try to resize the image to fit the dimensions.
        if ($image = image_load($file->uri)) {
          image_scale($image, $width, $height);
          image_save($image);
          $file->filesize = $image->info['file_size'];
          drupal_set_message(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $maximum_dimensions)));
        }
        else {
          $errors[] = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $maximum_dimensions));
        }
      }
    }

    if ($minimum_dimensions) {
      // Check that it is larger than the given dimensions.
      list($width, $height) = explode('x', $minimum_dimensions);
      if ($info['width'] < $width || $info['height'] < $height) {
        $errors[] = t('The image is too small; the minimum dimensions are %dimensions pixels.', array('%dimensions' => $minimum_dimensions));
      }
    }
  }

  return $errors;
}

/**
 * Save a string to the specified destination and create a database file entry.
 *
 * @param $data
 *   A string containing the contents of the file.
 * @param $destination
 *   A string containing the destination URI.
 *   This must be a stream wrapper URI. If this value is omitted, Drupal's
 *   default files scheme will be used, usually "public://".
 * @param $replace
 *   Replace behavior when the destination file already exists:
 *   - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with
 *       the destination name exists then its database entry will be updated. If
 *       no database entry is found then a new one will be created.
 *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
 *       unique.
 *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
 *
 * @return
 *   A file object, or FALSE on error.
 *
 * @see file_unmanaged_save_data()
 */
function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  global $user;

  if (empty($destination)) {
    $destination = file_default_scheme() . '://';
  }
  if (!file_valid_uri($destination)) {
    watchdog('file', 'The data could not be saved because the destination %destination is invalid. This may be caused by improper use of file_save_data() or a missing stream wrapper.', array('%destination' => $destination));
    drupal_set_message(t('The data could not be saved, because the destination is invalid. More information is available in the system log.'), 'error');
    return FALSE;
  }

  if ($uri = file_unmanaged_save_data($data, $destination, $replace)) {
    // Create a file object.
    $file = new stdClass();
    $file->fid = NULL;
    $file->uri = $uri;
    $file->filename = basename($uri);
    $file->filemime = file_get_mimetype($file->uri);
    $file->uid      = $user->uid;
    $file->status   = FILE_STATUS_PERMANENT;
    // If we are replacing an existing file re-use its database record.
    if ($replace == FILE_EXISTS_REPLACE) {
      $existing_files = file_load_multiple(array(), array('uri' => $uri));
      if (count($existing_files)) {
        $existing = reset($existing_files);
        $file->fid = $existing->fid;
        $file->filename = $existing->filename;
      }
    }
    // If we are renaming around an existing file (rather than a directory),
    // use its basename for the filename.
    elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
      $file->filename = basename($destination);
    }

    return file_save($file);
  }
  return FALSE;
}

/**
 * Save a string to the specified destination without invoking file API.
 *
 * This function is identical to file_save_data() except the file will not be
 * saved to the {file_managed} table and none of the file_* hooks will be
 * called.
 *
 * @param $data
 *   A string containing the contents of the file.
 * @param $destination
 *   A string containing the destination location.
 *   This must be a stream wrapper URI.  If no value is provided, a
 *   randomized name will be generated and the file is saved using Drupal's
 *   default files scheme, usually "public://".
 * @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 with the path of the resulting file, or FALSE on error.
 *
 * @see file_save_data()
 */
function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  // Write the data to a temporary file.
  $temp_name = drupal_tempnam('temporary://', 'file');
  if (file_put_contents($temp_name, $data) === FALSE) {
    drupal_set_message(t('The file could not be created.'), 'error');
    return FALSE;
  }

  // Move the file to its final destination.
  return file_unmanaged_move($temp_name, $destination, $replace);
}

/**
 * Transfer file using HTTP to client.
 *
 * Pipes a file through Drupal to the client.
 *
 * @param $uri
 *   String specifying the file URI to transfer.
 * @param $headers
 *   An array of HTTP headers to send along with file.
 */
function file_transfer($uri, $headers) {
  if (ob_get_level()) {
    ob_end_clean();
  }

  foreach ($headers as $name => $value) {
    drupal_add_http_header($name, $value);
  }
  drupal_send_headers();
  $scheme = file_uri_scheme($uri);
  // Transfer file in 1024 byte chunks to save memory usage.
  if ($scheme && file_stream_wrapper_valid_scheme($scheme) && $fd = fopen($uri, 'rb')) {
    while (!feof($fd)) {
      print fread($fd, 1024);
    }
    fclose($fd);
  }
  else {
    drupal_not_found();
  }
  drupal_exit();
}

/**
 * Menu handler for private file transfers.
 *
 * Call modules that implement hook_file_download() to find out if a file is
 * accessible and what headers it should be transferred with. If a module
 * returns -1 drupal_access_denied() will be returned. If one or more modules
 * returned headers the download will start with the returned headers. If no
 * modules respond drupal_not_found() will be returned.
 *
 * @see hook_file_download()
 */
function file_download() {
  // Merge remainder of arguments from GET['q'], into relative file path.
  $args = func_get_args();
  $scheme = array_shift($args);
  $target = implode('/', $args);
  $uri = $scheme . '://' . $target;
  if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) {
    // Let other modules provide headers and controls access to the file.
    // module_invoke_all() uses array_merge_recursive() which merges header
    // values into a new array. To avoid that and allow modules to override
    // headers instead, use array_merge() to merge the returned arrays.
    $headers = array();
    foreach (module_implements('file_download') as $module) {
      $function = $module . '_file_download';
      $result = $function($uri);
      if ($result == -1) {
        return drupal_access_denied();
      }
      if (isset($result) && is_array($result)) {
        $headers = array_merge($headers, $result);
      }
    }
    if (count($headers)) {
      file_transfer($uri, $headers);
    }
  }
  return drupal_not_found();
}


/**
 * Finds all files that match a given mask in a given directory.
 *
 * Directories and files beginning with a period are excluded; this
 * prevents hidden files and directories (such as SVN working directories)
 * from being scanned.
 *
 * @param $dir
 *   The base directory or URI to scan, without trailing slash.
 * @param $mask
 *   The preg_match() regular expression of the files to find.
 * @param $options
 *   An associative array of additional options, with the following elements:
 *   - 'nomask': The preg_match() regular expression of the files to ignore.
 *     Defaults to '/(\.\.?|CVS)$/'.
 *   - 'callback': The callback function to call for each match. There is no
 *     default callback.
 *   - 'recurse': When TRUE, the directory scan will recurse the entire tree
 *     starting at the provided directory. Defaults to TRUE.
 *   - 'key': The key to be used for the returned associative array of files.
 *     Possible values are 'uri', for the file's URI; 'filename', for the
 *     basename of the file; and 'name' for the name of the file without the
 *     extension. Defaults to 'uri'.
 *   - 'min_depth': Minimum depth of directories to return files from. Defaults
 *     to 0.
 * @param $depth
 *   Current depth of recursion. This parameter is only used internally and
 *   should not be passed in.
 *
 * @return
 *   An associative array (keyed on the chosen key) of objects with 'uri',
 *   'filename', and 'name' members corresponding to the matching files.
 */
function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
  // Merge in defaults.
  $options += array(
    'nomask' => '/(\.\.?|CVS)$/',
    'callback' => 0,
    'recurse' => TRUE,
    'key' => 'uri',
    'min_depth' => 0,
  );

  $options['key'] = in_array($options['key'], array('uri', 'filename', 'name')) ? $options['key'] : 'uri';
  $files = array();
  if (is_dir($dir) && $handle = opendir($dir)) {
    while (FALSE !== ($filename = readdir($handle))) {
      if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') {
        $uri = "$dir/$filename";
        $uri = file_stream_wrapper_uri_normalize($uri);
        if (is_dir($uri) && $options['recurse']) {
          // Give priority to files in this folder by merging them in after any subdirectory files.
          $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files);
        }
        elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) {
          // Always use this match over anything already set in $files with the
          // same $$options['key'].
          $file = new stdClass();
          $file->uri = $uri;
          $file->filename = $filename;
          $file->name = pathinfo($filename, PATHINFO_FILENAME);
          $key = $options['key'];
          $files[$file->$key] = $file;
          if ($options['callback']) {
            $options['callback']($uri);
          }
        }
      }
    }

    closedir($handle);
  }

  return $files;
}

/**
 * Determine the maximum file upload size by querying the PHP settings.
 *
 * @return
 *   A file size limit in bytes based on the PHP upload_max_filesize and
 *   post_max_size
 */
function file_upload_max_size() {
  static $max_size = -1;

  if ($max_size < 0) {
    // Start with post_max_size.
    $max_size = parse_size(ini_get('post_max_size'));

    // If upload_max_size is less, then reduce. Except if upload_max_size is
    // zero, which indicates no limit.
    $upload_max = parse_size(ini_get('upload_max_filesize'));
    if ($upload_max > 0 && $upload_max < $max_size) {
      $max_size = $upload_max;
    }
  }
  return $max_size;
}

/**
 * Determine an Internet Media Type, or MIME type from a filename.
 *
 * @param $uri
 *   A string containing the URI, path, or filename.
 * @param $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 (lowercase) and the value is the mimetype
 *      identifier. If $mapping is NULL file_mimetype_mapping() is called.
 *
 * @return
 *   The internet media type registered for the extension or
 *   application/octet-stream for unknown extensions.
 *
 * @see file_default_mimetype_mapping()
 */
function file_get_mimetype($uri, $mapping = NULL) {
  if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
    return $wrapper->getMimeType($uri, $mapping);
  }
  else {
    // getMimeType() is not implementation specific, so we can directly
    // call it without an instance.
    return DrupalLocalStreamWrapper::getMimeType($uri, $mapping);
  }
}

/**
 * Set the permissions on a file or directory.
 *
 * This function will use the 'file_chmod_directory' and 'file_chmod_file'
 * variables for the default modes for directories and uploaded/generated
 * files. By default these will give everyone read access so that users
 * accessing the files with a user account without the webserver group (e.g.
 * via FTP) can read these files, and give group write permissions so webserver
 * group members (e.g. a vhost account) can alter files uploaded and owned by
 * the webserver.
 *
 * PHP's chmod does not support stream wrappers so we use our wrapper
 * implementation which interfaces with chmod() by default. Contrib wrappers
 * may override this behavior in their implementations as needed.
 *
 * @param $uri
 *   A string containing a URI file, or directory path.
 * @param $mode
 *   Integer value for the permissions. Consult PHP chmod() documentation for
 *   more information.
 *
 * @return
 *   TRUE for success, FALSE in the event of an error.
 *
 * @ingroup php_wrappers
 */
function drupal_chmod($uri, $mode = NULL) {
  if (!isset($mode)) {
    if (is_dir($uri)) {
      $mode = variable_get('file_chmod_directory', 0775);
    }
    else {
      $mode = variable_get('file_chmod_file', 0664);
    }
  }

  // If this URI is a stream, pass it off to the appropriate stream wrapper.
  // Otherwise, attempt PHP's chmod. This allows use of drupal_chmod even
  // for unmanaged files outside of the stream wrapper interface.
  if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
    if ($wrapper->chmod($mode)) {
      return TRUE;
    }
  }
  else {
    if (@chmod($uri, $mode)) {
      return TRUE;
    }
  }

  watchdog('file', 'The file permissions could not be set on %uri.', array('%uri' => $uri), WATCHDOG_ERROR);
  return FALSE;
}

/**
 * Deletes a file.
 *
 * PHP's unlink() is broken on Windows, as it can fail to remove a file
 * when it has a read-only flag set.
 *
 * @param $uri
 *   A URI or pathname.
 * @param $context
 *   Refer to http://php.net/manual/en/ref.stream.php
 *
 * @return
 *   Boolean TRUE on success, or FALSE on failure.
 *
 * @see unlink()
 * @ingroup php_wrappers
 */
function drupal_unlink($uri, $context = NULL) {
  $scheme = file_uri_scheme($uri);
  if ((!$scheme || !file_stream_wrapper_valid_scheme($scheme)) && (substr(PHP_OS, 0, 3) == 'WIN')) {
    chmod($uri, 0600);
  }
  if ($context) {
    return unlink($uri, $context);
  }
  else {
    return unlink($uri);
  }
}

/**
 * Returns the absolute path of a file or directory
 *
 * PHP's realpath() does not properly support streams, so this function
 * fills that gap. If a stream wrapped URI is provided, it will be passed
 * to the registered wrapper for handling. If the URI does not contain a
 * scheme or the wrapper implementation does not implement realpath, then
 * FALSE will be returned.
 *
 * @see http://php.net/manual/en/function.realpath.php
 *
 * Compatibility: normal paths and stream wrappers.
 * @see http://drupal.org/node/515192
 *
 * @param $uri
 *   A string containing the URI to verify. If this value is omitted,
 *   Drupal's public files directory will be used [public://].
 *
 * @return
 *   The absolute pathname, or FALSE on failure.
 *
 * @see realpath()
 * @ingroup php_wrappers
 */
function drupal_realpath($uri) {
  // If this URI is a stream, pass it off to the appropriate stream wrapper.
  // Otherwise, attempt PHP's realpath. This allows use of drupal_realpath even
  // for unmanaged files outside of the stream wrapper interface.
  if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
    return $wrapper->realpath();
  }
  // Check that the uri has a value. There is a bug in PHP 5.2 on *BSD systems
  // that makes realpath not return FALSE as expected when passing an empty
  // variable.
  // @todo Remove when Drupal drops support for PHP 5.2.
  elseif (!empty($uri)) {
    return realpath($uri);
  }
  return FALSE;
}

/**
 * Gets the name of the directory from a given path.
 *
 * PHP's dirname() does not properly pass streams, so this function fills
 * that gap. It is backwards compatible with normal paths and will use
 * PHP's dirname() as a fallback.
 *
 * Compatibility: normal paths and stream wrappers.
 * @see http://drupal.org/node/515192
 *
 * @param $uri
 *   A URI or path.
 *
 * @return
 *   A string containing the directory name.
 *
 * @see dirname()
 * @ingroup php_wrappers
 */
function drupal_dirname($uri) {
  $scheme = file_uri_scheme($uri);

  if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
    return file_stream_wrapper_get_instance_by_scheme($scheme)->dirname($uri);
  }
  else {
    return dirname($uri);
  }
}

/**
 * Creates a directory using Drupal's default mode.
 *
 * PHP's mkdir() does not respect Drupal's default permissions mode. If a mode
 * is not provided, this function will make sure that Drupal's is used.
 *
 * Compatibility: normal paths and stream wrappers.
 * @see http://drupal.org/node/515192
 *
 * @param $uri
 *   A URI or pathname.
 * @param $mode
 *   By default the Drupal mode is used.
 * @param $recursive
 *   Default to FALSE.
 * @param $context
 *   Refer to http://php.net/manual/en/ref.stream.php
 *
 * @return
 *   Boolean TRUE on success, or FALSE on failure.
 *
 * @see mkdir()
 * @ingroup php_wrappers
 */
function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
  if (!isset($mode)) {
    $mode = variable_get('file_chmod_directory', 0775);
  }

  if (!isset($context)) {
    return mkdir($uri, $mode, $recursive);
  }
  else {
    return mkdir($uri, $mode, $recursive, $context);
  }
}

/**
 * Remove a directory.
 *
 * PHP's rmdir() is broken on Windows, as it can fail to remove a directory
 * when it has a read-only flag set.
 *
 * @param $uri
 *   A URI or pathname.
 * @param $context
 *   Refer to http://php.net/manual/en/ref.stream.php
 *
 * @return
 *   Boolean TRUE on success, or FALSE on failure.
 *
 * @see rmdir()
 * @ingroup php_wrappers
 */
function drupal_rmdir($uri, $context = NULL) {
  $scheme = file_uri_scheme($uri);
  if ((!$scheme || !file_stream_wrapper_valid_scheme($scheme)) && (substr(PHP_OS, 0, 3) == 'WIN')) {
    chmod($uri, 0700);
  }
  if ($context) {
    return rmdir($uri, $context);
  }
  else {
    return rmdir($uri);
  }
}

/**
 * Creates a file with a unique filename in the specified directory.
 *
 * PHP's tempnam() does not return a URI like we want. This function
 * will return a URI if given a URI, or it will return a filepath if
 * given a filepath.
 *
 * Compatibility: normal paths and stream wrappers.
 * @see http://drupal.org/node/515192
 *
 * @param $directory
 *   The directory where the temporary filename will be created.
 * @param $prefix
 *   The prefix of the generated temporary filename.
 *   Note: Windows uses only the first three characters of prefix.
 *
 * @return
 *   The new temporary filename, or FALSE on failure.
 *
 * @see tempnam()
 * @ingroup php_wrappers
 */
function drupal_tempnam($directory, $prefix) {
  $scheme = file_uri_scheme($directory);

  if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
    $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);

    if ($filename = tempnam($wrapper->getDirectoryPath(), $prefix)) {
      return $scheme . '://' . basename($filename);
    }
    else {
      return FALSE;
    }
  }
  else {
    // Handle as a normal tempnam() call.
    return tempnam($directory, $prefix);
  }
}

/**
 * Get the path of system-appropriate temporary directory.
 */
function file_directory_temp() {
  $temporary_directory = variable_get('file_temporary_path', NULL);

  if (empty($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';
      $path_delimiter = '\\';
    }
    else {
      $directories[] = '/tmp';
      $path_delimiter = '/';
    }
    // PHP may be able to find an alternative tmp directory.
    // This function exists in PHP 5 >= 5.2.1, but Drupal
    // requires PHP 5 >= 5.2.0, so we check for it.
    if (function_exists('sys_get_temp_dir')) {
      $directories[] = sys_get_temp_dir();
    }

    foreach ($directories as $directory) {
      if (is_dir($directory) && is_writable($directory)) {
        $temporary_directory = $directory;
        break;
      }
    }

    if (empty($temporary_directory)) {
      // If no directory has been found default to 'files/tmp' or 'files\\tmp'.
      $temporary_directory = variable_get('file_public_path', conf_path() . '/files') . $path_delimiter . 'tmp';
    }
    // Save the path of the discovered directory.
    variable_set('file_temporary_path', $temporary_directory);
  }

  return $temporary_directory;
}

/**
 * Examines a file object and returns appropriate content headers for download.
 *
 * @param $file
 *   A file object.
 * @return
 *   An associative array of headers, as expected by file_transfer().
 */
function file_get_content_headers($file) {
  $name = mime_header_encode($file->filename);
  $type = mime_header_encode($file->filemime);
  // Serve images, text, and flash content for display rather than download.
  $inline_types = variable_get('file_inline_types', array('^text/', '^image/', 'flash$'));
  $disposition = 'attachment';
  foreach ($inline_types as $inline_type) {
    // Exclamation marks are used as delimiters to avoid escaping slashes.
    if (preg_match('!' . $inline_type . '!', $file->filemime)) {
      $disposition = 'inline';
    }
  }

  return array(
    'Content-Type' => $type . '; name="' . $name . '"',
    'Content-Length' => $file->filesize,
    'Content-Disposition' => $disposition . '; filename="' . $name . '"',
    'Cache-Control' => 'private',
  );
}

/**
 * @} End of "defgroup file".
 */

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal file.inc source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.