You are here

function filefield_file_download in FileField 6.3

Same name and namespace in other branches
  1. 5.2 filefield.module \filefield_file_download()
  2. 6.2 filefield.module \filefield_file_download()

Implementation of hook_file_download().

File

./filefield.module, line 120
FileField: Defines a CCK file field type.

Code

function filefield_file_download($filepath) {
  $filepath = file_create_path($filepath);
  $result = db_query("SELECT * FROM {files} WHERE filepath = '%s'", $filepath);

  // Ensure case-sensitivity of uploaded file names.
  while ($file = db_fetch_object($result)) {
    if (strcmp($file->filepath, $filepath) == 0) {
      break;
    }
  }

  // If the file is not found in the database, we're not responsible for it.
  if (empty($file)) {
    return;
  }

  // See if this is a file on a newly created node, on which the user who
  // uploaded it will immediately have access.
  $new_node_file = $file->status == 0 && isset($_SESSION['filefield_access']) && in_array($file->fid, $_SESSION['filefield_access']);
  if ($new_node_file) {
    $denied = FALSE;
  }
  else {

    // Find out if any file field contains this file, and if so, which field
    // and node it belongs to. Required for later access checking.
    $cck_files = array();
    foreach (content_fields() as $field) {
      if ($field['type'] == 'filefield' || $field['type'] == 'image') {
        $db_info = content_database_info($field);
        $table = $db_info['table'];
        $fid_column = $db_info['columns']['fid']['column'];
        $columns = array(
          'vid',
          'nid',
        );
        foreach ($db_info['columns'] as $property_name => $column_info) {
          $columns[] = $column_info['column'] . ' AS ' . $property_name;
        }
        $result = db_query("SELECT " . implode(', ', $columns) . "\n                            FROM {" . $table . "}\n                            WHERE " . $fid_column . " = %d", $file->fid);
        while ($content = db_fetch_array($result)) {
          $content['field'] = $field;
          $cck_files[$field['field_name']][$content['vid']] = $content;
        }
      }
    }

    // If no file field item is involved with this file, we don't care about it.
    if (empty($cck_files)) {
      return;
    }

    // So the overall field view permissions are not denied, but if access is
    // denied for ALL nodes containing the file, deny the download as well.
    // Node access checks also include checking for 'access content'.
    $nodes = array();
    $denied = TRUE;
    $revision_access = FALSE;
    foreach ($cck_files as $field_name => $field_files) {
      foreach ($field_files as $revision_id => $content) {

        // Checking separately for each revision is probably not the best idea -
        // what if 'view revisions' is disabled? So, let's just check for the
        // current revision of that node.
        if (isset($nodes[$content['nid']])) {
          continue;

          // Don't check the same node twice.
        }
        if (($node = node_load($content['nid'])) && (node_access('view', $node) && filefield_view_access($field_name, $node))) {

          // They have access to the node.
          $denied = FALSE;
        }
        if (!$denied && $node->vid == $revision_id) {

          // If revision is the current revision, grant access.
          $revision_access = TRUE;
        }
        elseif (!$denied) {
          $revision_node = node_load($content['nid'], $revision_id);

          // You have access to the node as well as that particular revision.
          $revision_access = $revision_node && _node_revision_access($revision_node, 'view');
        }

        // If node access denied, skip other revisions; or if we have node
        // access and access to at least one revision the file is present on,
        // skip other revision checks.
        if ($denied || $revision_access) {
          $nodes[$content['nid']] = $node;
          break 2;
        }
      }
    }
  }

  // If they don't have access to the node or file, or if the file is only
  // attached to revisions that they don't have access to, deny access.
  if ($denied || !$revision_access) {
    return -1;
  }

  // Access is granted.
  $name = mime_header_encode($file->filename);
  $type = mime_header_encode($file->filemime);

  // By default, serve images, text, and flash content for display rather than
  // download. Or if variable 'filefield_inline_types' is set, use its patterns.
  $inline_types = variable_get('filefield_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',
  );
}