UploadStream::stream_metadata is not implemented #4990

Open
opened 2026-02-20 16:27:30 -05:00 by deekerman · 10 comments
Owner

Originally created by @kale1d0code on GitHub (Apr 5, 2023).

Issue

UploadStream class hasn't implemented the stream_metadata static method and so anytime the crm needs to run touch, chmod, chown, or chgrp against a stream, it will fail.

Expected Behavior

import records and import files without issues or warnings

Actual Behavior

error message mentioned before is printed, and CRM record values are not updated.

Possible Fix

implement stream_metadata on UploadStream class

I have done the minimum to implement touch

    public static function stream_metadata(string $path, int $option, $value): bool
    {
        $fp = self::path($path);
        switch ($option) {
          case 1:
            return @touch($fp, (is_array($value) && array_key_exists(0, $value)) ? $value[0] : null, (is_array($value) && array_key_exists(1, $value)) ? $value[1] : null);
        }
        return true;
    }

Steps to Reproduce

  1. upload record csv for module

Context

unable to import csv files to update records on mass

Your Environment

  • SuiteCRM Version used:
  • Browser name and version (e.g. Chrome Version 51.0.2704.63 (64-bit)):
  • Environment name and version (e.g. MySQL, PHP 7):
  • Operating System and version (e.g Ubuntu 16.04):
    7.12.2
    PHP
Originally created by @kale1d0code on GitHub (Apr 5, 2023). <!--- Provide a general summary of the issue in the **Title** above --> <!--- Before you open an issue, please check if a similar issue already exists or has been closed before. ---> <!--- If you have discovered a security risk please report it by emailing security@suitecrm.com. This will be delivered to the product team who handle security issues. Please don't disclose security bugs publicly until they have been handled by the security team. ---> <!--- Please be aware that as of the 31st January 2022 we no longer support 7.10.x. New issues referring to 7.10.x will only be valid if applicable to 7.12.x and above. If your issue is still applicable in 7.12.x, please create the issue following the template below --> #### Issue <!--- Provide a more detailed introduction to the issue itself, and why you consider it to be a bug --> <!--- Ensure that all code ``` is surrounded ``` by triple back quotes. This can also be done over multiple lines --> UploadStream class hasn't implemented the stream_metadata static method and so anytime the crm needs to run touch, chmod, chown, or chgrp against a stream, it will fail. #### Expected Behavior <!--- Tell us what should happen --> import records and import files without issues or warnings #### Actual Behavior <!--- Tell us what happens instead --> <!--- Also please check relevant logs (suitecrm.log, php error.log etc.) --> error message mentioned before is printed, and CRM record values are not updated. #### Possible Fix <!--- Not obligatory, but suggest a fix or reason for the bug --> implement stream_metadata on UploadStream class I have done the minimum to implement touch ``` public static function stream_metadata(string $path, int $option, $value): bool { $fp = self::path($path); switch ($option) { case 1: return @touch($fp, (is_array($value) && array_key_exists(0, $value)) ? $value[0] : null, (is_array($value) && array_key_exists(1, $value)) ? $value[1] : null); } return true; } ``` #### Steps to Reproduce <!--- Provide a link to a live example, or an unambiguous set of steps to --> <!--- reproduce this bug include code to reproduce, if relevant --> 1. upload record csv for module 2. 3. 4. #### Context <!--- How has this bug affected you? What were you trying to accomplish? --> <!--- If you feel this should be a low/medium/high priority then please state so --> unable to import csv files to update records on mass #### Your Environment <!--- Include as many relevant details about the environment you experienced the bug in --> * SuiteCRM Version used: * Browser name and version (e.g. Chrome Version 51.0.2704.63 (64-bit)): * Environment name and version (e.g. MySQL, PHP 7): * Operating System and version (e.g Ubuntu 16.04): 7.12.2 PHP
Author
Owner

@pgorod commented on GitHub (Apr 5, 2023):

What is the PHP version where this doesn't currently work?

And do you have any special setting for the upload directory, or are you using the defaults?

I am not surprised at the incomplete implementation, and I agree it might be good to complete it (along with other fixes necessary in that class, it was actually better before but there have been regressions...). But I am surprised to hear that a simple CSV import doesn't work. Lots of people use it.

@pgorod commented on GitHub (Apr 5, 2023): What is the PHP version where this doesn't currently work? And do you have any special setting for the upload directory, or are you using the defaults? I am not surprised at the incomplete implementation, and I agree it might be good to complete it (along with other fixes necessary in that class, it was actually better before but there have been regressions...). But I am surprised to hear that a simple CSV import doesn't work. Lots of people use it.
Author
Owner

@chris001 commented on GitHub (Apr 5, 2023):

@kale1d0code Great find!
@pgorod It's required for PHP 5.4.0+, 7, 8.
Other PHP projects mention this issue and fixed it in 2017!

This should fix it:

  /**
   * Sets metadata on the stream.
   *
   * WARNING: Do not call this method directly! It will be called internally by
   * PHP itself when one of the following functions is called on a stream URL:
   *
   * @param string $uri
   *   A string containing the URI to the file to set metadata on.
   * @param int $option
   *   One of:
   *   - STREAM_META_TOUCH: The method was called in response to touch().
   *   - STREAM_META_OWNER_NAME: The method was called in response to chown()
   *     with string parameter.
   *   - STREAM_META_OWNER: The method was called in response to chown().
   *   - STREAM_META_GROUP_NAME: The method was called in response to chgrp().
   *   - STREAM_META_GROUP: The method was called in response to chgrp().
   *   - STREAM_META_ACCESS: The method was called in response to chmod().
   * @param mixed $value
   *   If option is:
   *   - STREAM_META_TOUCH: Array consisting of two arguments of the touch()
   *     function.
   *   - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner
   *     user/group as string.
   *   - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner
   *     user/group as integer.
   *   - STREAM_META_ACCESS: The argument of the chmod() as integer.
   *
   * @return bool
   *   Returns TRUE on success or FALSE on failure. If $option is not
   *   implemented, FALSE should be returned.
   *
   * @see touch()
   * @see chmod()
   * @see chown()
   * @see chgrp()
   * @link http://php.net/manual/streamwrapper.stream-metadata.php
   */
  public static function streamWrapper::stream_metadata(string $uri, int $option, $value) {
    $target = $this->getLocalPath($uri);
    $return = FALSE;
    switch ($option) {
      case STREAM_META_TOUCH:
        if (!empty($value)) {
          $return = touch($target, $value[0], $value[1]);
        }
        else {
          $return = touch($target);
        }
        break;

      case STREAM_META_OWNER_NAME:
      case STREAM_META_OWNER:
        $return = chown($target, $value);
        break;

      case STREAM_META_GROUP_NAME:
      case STREAM_META_GROUP:
        $return = chgrp($target, $value);
        break;

      case STREAM_META_ACCESS:
        $return = chmod($target, $value);
        break;
    }
    if ($return) {
      // For convenience clear the file status cache of the underlying file,
      // since metadata operations are often followed by file status checks.
      clearstatcache(TRUE, $target);
    }
    return $return;
  }

@chris001 commented on GitHub (Apr 5, 2023): @kale1d0code Great find! @pgorod It's [required for PHP 5.4.0+, 7, 8](https://www.php.net/manual/en/streamwrapper.stream-metadata.php). Other PHP projects [mention this issue](https://www.drupal.org/project/drupal/issues/2107287) and fixed it in 2017! This should fix it: ``` /** * Sets metadata on the stream. * * WARNING: Do not call this method directly! It will be called internally by * PHP itself when one of the following functions is called on a stream URL: * * @param string $uri * A string containing the URI to the file to set metadata on. * @param int $option * One of: * - STREAM_META_TOUCH: The method was called in response to touch(). * - STREAM_META_OWNER_NAME: The method was called in response to chown() * with string parameter. * - STREAM_META_OWNER: The method was called in response to chown(). * - STREAM_META_GROUP_NAME: The method was called in response to chgrp(). * - STREAM_META_GROUP: The method was called in response to chgrp(). * - STREAM_META_ACCESS: The method was called in response to chmod(). * @param mixed $value * If option is: * - STREAM_META_TOUCH: Array consisting of two arguments of the touch() * function. * - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner * user/group as string. * - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner * user/group as integer. * - STREAM_META_ACCESS: The argument of the chmod() as integer. * * @return bool * Returns TRUE on success or FALSE on failure. If $option is not * implemented, FALSE should be returned. * * @see touch() * @see chmod() * @see chown() * @see chgrp() * @link http://php.net/manual/streamwrapper.stream-metadata.php */ public static function streamWrapper::stream_metadata(string $uri, int $option, $value) { $target = $this->getLocalPath($uri); $return = FALSE; switch ($option) { case STREAM_META_TOUCH: if (!empty($value)) { $return = touch($target, $value[0], $value[1]); } else { $return = touch($target); } break; case STREAM_META_OWNER_NAME: case STREAM_META_OWNER: $return = chown($target, $value); break; case STREAM_META_GROUP_NAME: case STREAM_META_GROUP: $return = chgrp($target, $value); break; case STREAM_META_ACCESS: $return = chmod($target, $value); break; } if ($return) { // For convenience clear the file status cache of the underlying file, // since metadata operations are often followed by file status checks. clearstatcache(TRUE, $target); } return $return; } ```
Author
Owner

@pgorod commented on GitHub (Apr 5, 2023):

I'm totally ok with this issue and with the proposed fixes.

My only question is this: have our CSV imports really been broken since PHP 5.4? I doubt that a lot and would love to know exactly what has been happening. And if they really are broken, there is likely an open issue somewhere.

@kale1d0code do you have a precise error message form the logs that we can use to search?

Thanks

@pgorod commented on GitHub (Apr 5, 2023): I'm totally ok with this issue and with the proposed fixes. My only question is this: have our CSV imports really been broken since PHP 5.4? I doubt that a lot and would love to know exactly what has been happening. And if they really are broken, there is likely an open issue somewhere. @kale1d0code do you have a precise error message form the logs that we can use to search? Thanks
Author
Owner

@chris001 commented on GitHub (Apr 5, 2023):

What happened was, when upgrading to PHP 5.4, which requires this new stream_metadata() function, users were getting failures on upload code from Sugar CE 6.5 written for PHP 5.3. SuiteCRM team resolved the issue on PHP 5.4+ by avoiding using stream instead using direct file system path name, not realizing that all they had to do to fix this upload stream issue was to add implement stream_metadata(), then uploads would work fine!

@chris001 commented on GitHub (Apr 5, 2023): What happened was, when upgrading to PHP 5.4, which requires this new `stream_metadata()` function, users were getting failures on upload code from Sugar CE 6.5 written for PHP 5.3. SuiteCRM team resolved the issue on PHP 5.4+ by avoiding using stream instead using direct file system path name, not realizing that all they had to do to fix this upload stream issue was to add implement `stream_metadata()`, then uploads would work fine!
Author
Owner

@kale1d0code commented on GitHub (Apr 6, 2023):

@pgorod
I've got

Caught error: stat(): stat failed for upload/import/status_1.csv

and

Caught error: touch(): UploadStream::stream_metadata is not implemented!
@kale1d0code commented on GitHub (Apr 6, 2023): @pgorod I've got ``` Caught error: stat(): stat failed for upload/import/status_1.csv ``` and ``` Caught error: touch(): UploadStream::stream_metadata is not implemented! ```
Author
Owner

@chris001 commented on GitHub (Apr 7, 2023):

Here's proof of this exact issue bug, reported on the community forum back in Feb 2020, 3+ years ago, and not resolved...
https://community.suitecrm.com/t/import-issues-stat-and-touch/71405

@chris001 commented on GitHub (Apr 7, 2023): Here's proof of this exact issue bug, reported on the community forum back in Feb 2020, 3+ years ago, and not resolved... https://community.suitecrm.com/t/import-issues-stat-and-touch/71405
Author
Owner

@pgorod commented on GitHub (Apr 8, 2023):

Nice PR, thanks 👍

Next up is going through all the instances of 'upload/' in the codebase (44 matches according to my PhpStorm) and changing them all to 'upload://', and testing to see everything works fine, including with a changed setting for $sugar_config['upload_dir'].

Some of those were working fine but got changed in the wrong way a few years ago to work around a problem with a phpmailer upgrade. But the correct approach would have been one of these two suggestions instead.

If that ever gets done, the fun part begins, which is to enjoy full file system redirection and quite easily do things like...

  • the often requested splitting up of the upload directory into more subdirectories, by module or by date
  • redirecting uploads to Amazon S3 storage or other fancy storage mechanisms
  • etc
@pgorod commented on GitHub (Apr 8, 2023): Nice PR, thanks 👍 Next up is going through all the instances of **`'upload/'`** in the codebase (44 matches according to my PhpStorm) and changing them all to **`'upload://'`**, and testing to see everything works fine, including with a changed setting for **`$sugar_config['upload_dir']`**. [Some](https://github.com/salesagility/SuiteCRM/pull/6791/files) of [those](https://github.com/salesagility/SuiteCRM/pull/7154/files) were working fine but got changed in the wrong way a few years ago to work around a problem with a phpmailer upgrade. But the correct approach would have been [one of these two suggestions](https://github.com/PHPMailer/PHPMailer/issues/2355#issuecomment-858373284) instead. If that ever gets done, the fun part begins, which is to enjoy full file system redirection and quite easily do things like... - the often requested splitting up of the upload directory into more subdirectories, by module or by date - redirecting uploads to Amazon S3 storage or other fancy storage mechanisms - etc
Author
Owner

@kale1d0code commented on GitHub (Aug 30, 2024):

This is still an issue in at least 7.14.4

@kale1d0code commented on GitHub (Aug 30, 2024): This is still an issue in at least 7.14.4
Author
Owner

@SuiteBot commented on GitHub (Apr 17, 2025):

This issue has been mentioned on SuiteCRM. There might be relevant details there:

https://community.suitecrm.com/t/support-uploading-files-outside-of-web-root/99155/1

@SuiteBot commented on GitHub (Apr 17, 2025): This issue has been mentioned on **SuiteCRM**. There might be relevant details there: https://community.suitecrm.com/t/support-uploading-files-outside-of-web-root/99155/1
Author
Owner

@kale1d0code commented on GitHub (Jul 21, 2025):

Still unable to upload files correctly in latest version of SuiteCRM

@kale1d0code commented on GitHub (Jul 21, 2025): Still unable to upload files correctly in latest version of SuiteCRM
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/SuiteCRM-SuiteCRM#4990
No description provided.