You are here

function filefield_source_remote_value in FileField Sources 7

Same name and namespace in other branches
  1. 6 sources/remote.inc \filefield_source_remote_value()

A #filefield_value_callback function.

1 string reference to 'filefield_source_remote_value'
filefield_source_remote_info in sources/remote.inc
Implements hook_filefield_source_info().

File

sources/remote.inc, line 118
A FileField extension to allow referencing of existing files.

Code

function filefield_source_remote_value($element, &$item) {
  if (isset($item['filefield_remote']['url']) && strlen($item['filefield_remote']['url']) > 0 && valid_url($item['filefield_remote']['url']) && $item['filefield_remote']['url'] != FILEFIELD_SOURCE_REMOTE_HINT_TEXT) {
    $field = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
    $url = $item['filefield_remote']['url'];

    // Check that the destination is writable.
    $temporary_directory = 'temporary://';
    if (!file_prepare_directory($temporary_directory, FILE_MODIFY_PERMISSIONS)) {
      watchdog('file', 'The directory %directory is not writable, because it does not have the correct permissions set.', array(
        '%directory' => drupal_realpath($temporary_directory),
      ));
      drupal_set_message(t('The file could not be transferred because the temporary directory is not writable.'), 'error');
      return;
    }

    // Check that the destination is writable.
    $directory = $element['#upload_location'];
    $mode = variable_get('file_chmod_directory', 0775);

    // This first chmod check is for other systems such as S3, which don't work
    // with file_prepare_directory().
    if (!drupal_chmod($directory, $mode) && !file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
      watchdog('file', 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array(
        '%file' => $url,
        '%destination' => drupal_realpath($directory),
      ));
      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' => $url,
      )), 'error');
      return;
    }

    // Check the headers to make sure it exists and is within the allowed size.
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, TRUE);
    curl_setopt($ch, CURLOPT_NOBODY, TRUE);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, '_filefield_source_remote_parse_header');

    // Causes a warning if PHP safe mode is on.
    @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);

    // Set a user agent - some hosts block requests unless header is present.
    $curl_version = curl_version();
    curl_setopt($ch, CURLOPT_USERAGENT, 'PHP-curl/' . $curl_version['version']);
    curl_exec($ch);
    $info = curl_getinfo($ch);
    if ($info['http_code'] != 200) {
      curl_setopt($ch, CURLOPT_HTTPGET, TRUE);
      $file_contents = curl_exec($ch);
      $info = curl_getinfo($ch);
    }
    curl_close($ch);
    if ($info['http_code'] != 200) {
      switch ($info['http_code']) {
        case 403:
          form_error($element, t('The remote file could not be transferred because access to the file was denied.'));
          break;
        case 404:
          form_error($element, t('The remote file could not be transferred because it was not found.'));
          break;
        default:
          form_error($element, t('The remote file could not be transferred due to an HTTP error (@code).', array(
            '@code' => $info['http_code'],
          )));
      }
      return;
    }

    // Update the $url variable to reflect any redirects.
    $url = $info['url'];
    $url_info = parse_url($url);

    // Determine the proper filename by reading the filename given in the
    // Content-Disposition header. If the server fails to send this header,
    // fall back on the basename of the URL.
    //
    // We prefer to use the Content-Disposition header, because we can then
    // use URLs like http://example.com/get_file/23 which would otherwise be
    // rejected because the URL basename lacks an extension.
    $filename = _filefield_source_remote_filename();
    if (empty($filename)) {
      $filename = rawurldecode(basename($url_info['path']));
    }
    $pathinfo = pathinfo($filename);

    // Create the file extension from the MIME header if all else has failed.
    if (empty($pathinfo['extension']) && ($extension = _filefield_source_remote_mime_extension())) {
      $filename = $filename . '.' . $extension;
      $pathinfo = pathinfo($filename);
    }
    $filename = filefield_sources_clean_filename($filename, $field['settings']['file_extensions']);
    $filepath = file_create_filename($filename, $temporary_directory);
    if (empty($pathinfo['extension'])) {
      form_error($element, t('The remote URL must be a file and have an extension.'));
      return;
    }

    // Perform basic extension check on the file before trying to transfer.
    $extensions = $field['settings']['file_extensions'];
    $regex = '/\\.(' . preg_replace('/[ +]/', '|', preg_quote($extensions)) . ')$/i';
    if (!empty($extensions) && !preg_match($regex, $filename)) {
      form_error($element, t('Only files with the following extensions are allowed: %files-allowed.', array(
        '%files-allowed' => $extensions,
      )));
      return;
    }

    // Check file size based off of header information.
    if (!empty($element['#upload_validators']['file_validate_size'][0])) {
      $max_size = $element['#upload_validators']['file_validate_size'][0];
      $file_size = $info['download_content_length'];
      if ($file_size > $max_size) {
        form_error($element, t('The remote file is %filesize exceeding the maximum file size of %maxsize.', array(
          '%filesize' => format_size($file_size),
          '%maxsize' => format_size($max_size),
        )));
        return;
      }
    }

    // Set progress bar information.
    $options = array(
      'key' => $element['#entity_type'] . '_' . $element['#bundle'] . '_' . $element['#field_name'] . '_' . $element['#delta'],
      'filepath' => $filepath,
    );
    filefield_source_remote_set_transfer_options($options);
    $transfer_success = FALSE;

    // If we've already downloaded the entire file because the header-retrieval
    // failed, just ave the contents we have.
    if (isset($file_contents)) {
      if ($fp = @fopen($filepath, 'w')) {
        fwrite($fp, $file_contents);
        fclose($fp);
        $transfer_success = TRUE;
      }
    }
    else {
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_HEADER, FALSE);
      curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'filefield_source_remote_curl_write');

      // Causes a warning if PHP safe mode is on.
      @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);

      // Set a user agent - some hosts block requests unless header is present.
      $curl_version = curl_version();
      curl_setopt($ch, CURLOPT_USERAGENT, 'PHP-curl/' . $curl_version['version']);
      $transfer_success = curl_exec($ch);
      curl_close($ch);
    }
    if ($transfer_success && ($file = filefield_sources_save_file($filepath, $element['#upload_validators'], $element['#upload_location']))) {
      $item = array_merge($item, (array) $file);
    }

    // Delete the temporary file.
    if ($filepath !== $item['uri']) {
      @unlink($filepath);
    }
  }
}