The EncryptStreamWrapper is detected as a remote stream

Created on 5 September 2018, almost 6 years ago
Updated 14 March 2024, 3 months ago

The PHP function stream_is_local[0] returns FALSE when checking an encrypt stream wrapper.

The problem can be reproduced using the TCPDF[1] module, which uses the TCPDF[2] library.

The code below should generate a PDF file, but it fails since the TCPDF library verifies that the destination file is local using the stream_is_local>strong> function.

  $tcpdf = tcpdf_get_instance();
  $tcpdf->DrupalInitialize([]);
  $tcpdf->writeHTML('<h1>Test!</h1>');
  $tcpdf->Output('encrypt://encryption_profile/test.pdf', 'F');

This fails with the error Unable to create output file: encrypt://encryption_profile/test.pdf, because TCPDF checks if the output file is a local file or stream.

This happens on the fopenLocal method of the TCPDF_STATIC class (File: include/tcpdf_static.php).

  public static function fopenLocal($filename, $mode) {
    if (strpos($filename, '://') === false) {
      $filename = 'file://'.$filename;
    } elseif (stream_is_local($filename) !== true) {
      return false;
    }
    return fopen($filename, $mode);
}

The expected result is TRUE since the encrypt stream wrapper is a local location, regardless of whether the files are encrypted.

A stream wrapper is defined as local when it is registered using the stream_wrapper_register[3], leaving the flags parameter with its default value (0).

Drupal register the stream wrappers the DrupalKernel invokes the registerWrapper method of the StreamWrapperManager class.

This method registers as local stream wrappers those wrappers whose type includes the StreamWrapperInterface::LOCAL type.

  public function registerWrapper($scheme, $class, $type) {
    if (in_array($scheme, stream_get_wrappers(), TRUE)) {
      stream_wrapper_unregister($scheme);
    }

    if (($type & StreamWrapperInterface::LOCAL) == StreamWrapperInterface::LOCAL) {
      stream_wrapper_register($scheme, $class);
    }
    else {
      stream_wrapper_register($scheme, $class, STREAM_IS_URL);
    }

    // Pre-populate the static cache with the filters most typically used.
    $info = ['type' => $type, 'class' => $class];
    $this->wrappers[StreamWrapperInterface::ALL][$scheme] = $info;

    if (($type & StreamWrapperInterface::WRITE_VISIBLE) == StreamWrapperInterface::WRITE_VISIBLE) {
      $this->wrappers[StreamWrapperInterface::WRITE_VISIBLE][$scheme] = $info;
    }
  }

The StreamWrapperInterface defines the following local types:

  • LOCAL
  • LOCAL_HIDDEN
  • LOCAL_NORMAL

The EncryptStreamWrapper uses StreamWrapperInterface::NORMAL as its type, so it is defined as an URL protocol by the StreamWrapperManager.

The EncryptStreamWrapper should use StreamWrapperInterface::LOCAL_NORMAL as its type, so it would be considered a local stream.

[0] https://secure.php.net/stream_is_local
[1] https://www.drupal.org/project/tcpdf β†’
[2] https://tcpdf.org/
[3] https://secure.php.net/stream_wrapper_register

πŸ› Bug report
Status

RTBC

Version

1.0

Component

Code

Created by

πŸ‡¦πŸ‡·Argentina tucho Buenos Aires

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024