You are here

function jplayer_protect_file_download in jPlayer 6

Same name and namespace in other branches
  1. 7.2 jplayer_protect/jplayer_protect.module \jplayer_protect_file_download()

Implements hook_file_download().

File

jplayer_protect/jplayer_protect.module, line 60
Provides basic content protection for media files accessed with jPlayer.

Code

function jplayer_protect_file_download($filepath) {
  if (!variable_get('jplayer_protect', FALSE)) {
    return NULL;
  }

  // We need to determine if we are responsible for this file.
  $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 NULL;
  }

  // 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 any of the displays for this field are for jPlayer, then we need to
  // protect the file.
  foreach ($cck_files as $field_name => $field_files) {
    foreach ($field_files as $revision_id => $content) {
      $teaser_format = $content['field']['display_settings']['teaser']['format'];
      $full_format = $content['field']['display_settings']['full']['format'];

      // Neither the teaser or the full formatter for this field is a jPlayer
      // display.
      if (!($teaser_format == 'single' || $teaser_format == 'playlist' || $full_format == 'single' || $full_format == 'playlist')) {
        return NULL;
      }
    }
  }
  $access_key = $GLOBALS['base_url'] . '/' . $filepath;
  if (isset($_SESSION['jplayer_protect'][$access_key])) {
    $started = (int) $_SESSION['jplayer_protect'][$access_key];
  }
  else {

    // We need to figure out how the browser would have URL-encoded the file
    // name. If mod_rewrite is modifying the URL, it will decode URL-encoded
    // characters, so we need to check both.
    $encoded = str_replace($file->filename, rawurlencode($file->filename), $filepath);

    // TODO replace this with the path to the files directory?
    $encoded = str_replace('sites/default/files', 'system/files', $encoded);

    // For some reason ampersands are encoded twice by the browser.
    $encoded = str_replace("%26", "%2526", $encoded);
    $encoded_access_key = $GLOBALS['base_url'] . '/' . $encoded;
    if (isset($_SESSION['jplayer_protect'][$encoded_access_key])) {
      $access_key = $encoded_access_key;
      $started = (int) $_SESSION['jplayer_protect'][$access_key];
    }
  }

  // Now we know that content protection is enabled, at least one display for
  // the field uses jPlayer, and we know when the player last started to access
  // the file.
  if (isset($started) && $started) {
    if (time() <= $started + variable_get('jplayer_access_time', 30)) {

      // Allow access, and immediately expire access to the file. Some browsers
      // (such as Chrome) send multiple HTTP requests for an <audio> element,
      // so if the RANGE header is set we continue to allow access. Also,
      // AppleCoreMedia in OS X 10.7 makes multiple requests in an attempt to
      // fetch metadata about the audio. So, we ignore those requests until the
      // agent indicates that the connection can be closed.
      if (!isset($_SERVER['HTTP_RANGE']) && !(strpos($_SERVER['HTTP_USER_AGENT'], 'AppleCoreMedia') !== FALSE && strpos($_SERVER['HTTP_CONNECTION'], "keep-alive") !== FALSE)) {
        unset($_SESSION['jplayer_protect'][$access_key]);
      }
      return NULL;
    }
  }

  // Otherwise, deny access as the last played time is too far in the past.
  $denied = new stdClass();
  $denied->uid = $GLOBALS['user']->uid;
  $denied->fid = $file->fid;
  $denied->hostname = ip_address();
  $denied->timestamp = time();
  drupal_write_record('jplayer_protect_denied', $denied);
  return -1;
}