mirror of
https://github.com/salesagility/SuiteCRM.git
synced 2024-11-21 15:37:57 +00:00
319 lines
9.2 KiB
PHP
319 lines
9.2 KiB
PHP
<?php
|
|
/**
|
|
*
|
|
* SugarCRM Community Edition is a customer relationship management program developed by
|
|
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
|
|
*
|
|
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
|
|
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation with the addition of the following permission added
|
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
|
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
|
|
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
* this program; if not, see http://www.gnu.org/licenses or write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA.
|
|
*
|
|
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
|
|
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of this program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU Affero General Public License version 3.
|
|
*
|
|
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
|
* these Appropriate Legal Notices must retain the display of the "Powered by
|
|
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
|
|
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
|
|
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
|
|
*/
|
|
|
|
if (!defined('sugarEntry') || !sugarEntry) {
|
|
die('Not A Valid Entry Point');
|
|
}
|
|
|
|
require_once('include/externalAPI/ExternalAPIFactory.php');
|
|
|
|
/**
|
|
* @internal
|
|
* Upload file stream handler
|
|
*/
|
|
#[\AllowDynamicProperties]
|
|
class UploadStream
|
|
{
|
|
public const STREAM_NAME = "upload";
|
|
protected static $upload_dir;
|
|
|
|
/**
|
|
* Method checks Suhosin restrictions to use streams in php
|
|
*
|
|
* @static
|
|
* @return bool is allowed stream or not
|
|
*/
|
|
public static function getSuhosinStatus()
|
|
{
|
|
// looks like suhosin patch doesn't block protocols, only suhosin extension (tested on FreeBSD)
|
|
// if suhosin is not installed it is okay for us
|
|
if (extension_loaded('suhosin') == false) {
|
|
return true;
|
|
}
|
|
$configuration = ini_get_all('suhosin', false);
|
|
|
|
// suhosin simulation is okay for us
|
|
if ($configuration['suhosin.simulation'] == true) {
|
|
return true;
|
|
}
|
|
|
|
// checking that UploadStream::STREAM_NAME is allowed by white list
|
|
$streams = $configuration['suhosin.executor.include.whitelist'];
|
|
if ($streams != '') {
|
|
$streams = explode(',', $streams);
|
|
foreach ($streams as $stream) {
|
|
$stream = explode('://', $stream, 2);
|
|
if (count($stream) == 1) {
|
|
if ($stream[0] == UploadStream::STREAM_NAME) {
|
|
return true;
|
|
}
|
|
} elseif ($stream[1] == '' && $stream[0] == UploadStream::STREAM_NAME) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$GLOBALS['log']->fatal('Stream ' . UploadStream::STREAM_NAME . ' is not listed in suhosin.executor.include.whitelist and blocked because of it');
|
|
|
|
return false;
|
|
}
|
|
|
|
// checking that UploadStream::STREAM_NAME is not blocked by black list
|
|
$streams = $configuration['suhosin.executor.include.blacklist'];
|
|
if ($streams != '') {
|
|
$streams = explode(',', $streams);
|
|
foreach ($streams as $stream) {
|
|
$stream = explode('://', $stream, 2);
|
|
if ($stream[0] == UploadStream::STREAM_NAME) {
|
|
$GLOBALS['log']->fatal('Stream ' . UploadStream::STREAM_NAME . 'is listed in suhosin.executor.include.blacklist and blocked because of it');
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
$GLOBALS['log']->fatal('Suhosin blocks all streams, please define ' . UploadStream::STREAM_NAME . ' stream in suhosin.executor.include.whitelist');
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get upload directory
|
|
* @return string
|
|
*/
|
|
public static function getDir()
|
|
{
|
|
if (empty(self::$upload_dir)) {
|
|
self::$upload_dir = rtrim($GLOBALS['sugar_config']['upload_dir'], '/\\');
|
|
if (empty(self::$upload_dir)) {
|
|
self::$upload_dir = "upload";
|
|
}
|
|
if (!file_exists(self::$upload_dir)) {
|
|
sugar_mkdir(self::$upload_dir, 0755, true);
|
|
}
|
|
}
|
|
|
|
return self::$upload_dir;
|
|
}
|
|
|
|
/**
|
|
* Check if upload dir is writable
|
|
* @return bool
|
|
*/
|
|
public static function writable()
|
|
{
|
|
return is_writable(self::getDir());
|
|
}
|
|
|
|
/**
|
|
* Register the stream
|
|
*/
|
|
public static function register()
|
|
{
|
|
stream_wrapper_register(self::STREAM_NAME, self::class);
|
|
}
|
|
|
|
/**
|
|
* Get real FS path of the upload stream file
|
|
* @param string $path Upload stream path (with upload://)
|
|
* @return string FS path
|
|
*/
|
|
public static function path($path)
|
|
{
|
|
$path = substr($path, strlen(self::STREAM_NAME) + 3); // cut off upload://
|
|
$path = str_replace("\\", "/", $path); // canonicalize path
|
|
if ($path == ".." || substr($path, 0, 3) == "../" || substr($path, -3, 3) == "/.." || strstr($path, "/../")) {
|
|
return null;
|
|
}
|
|
|
|
return self::getDir() . "/" . $path;
|
|
}
|
|
|
|
/**
|
|
* Ensure upload subdir exists
|
|
* @param string $path Upload stream path (with upload://)
|
|
* @param bool $writable
|
|
* @return boolean
|
|
*/
|
|
public static function ensureDir($path, $writable = true)
|
|
{
|
|
$path = self::path($path);
|
|
if (!is_dir($path)) {
|
|
return sugar_mkdir($path, 0755, true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function dir_closedir()
|
|
{
|
|
closedir($this->dirp);
|
|
}
|
|
|
|
public function dir_opendir($path, $options)
|
|
{
|
|
$this->dirp = opendir(self::path($path));
|
|
|
|
return !empty($this->dirp);
|
|
}
|
|
|
|
public function dir_readdir()
|
|
{
|
|
return readdir($this->dirp);
|
|
}
|
|
|
|
public function dir_rewinddir()
|
|
{
|
|
return rewinddir($this->dirp);
|
|
}
|
|
|
|
public function mkdir($path, $mode, $options)
|
|
{
|
|
return mkdir(self::path($path), $mode, ($options & STREAM_MKDIR_RECURSIVE) != 0);
|
|
}
|
|
|
|
public function rename($path_from, $path_to)
|
|
{
|
|
return rename(self::path($path_from), self::path($path_to));
|
|
}
|
|
|
|
public function rmdir($path, $options)
|
|
{
|
|
return rmdir(self::path($path));
|
|
}
|
|
|
|
public function stream_cast($cast_as)
|
|
{
|
|
return $this->fp;
|
|
}
|
|
|
|
public function stream_close()
|
|
{
|
|
fclose($this->fp);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function stream_eof()
|
|
{
|
|
return feof($this->fp);
|
|
}
|
|
|
|
public function stream_flush()
|
|
{
|
|
return fflush($this->fp);
|
|
}
|
|
|
|
public function stream_lock($operation)
|
|
{
|
|
return flock($this->fp, $operation);
|
|
}
|
|
|
|
public function stream_open($path, $mode)
|
|
{
|
|
$fullpath = self::path($path);
|
|
if (empty($fullpath)) {
|
|
return false;
|
|
}
|
|
if ($mode == 'r') {
|
|
$this->fp = fopen($fullpath, $mode);
|
|
} else {
|
|
// if we will be writing, try to transparently create the directory
|
|
$this->fp = @fopen($fullpath, $mode);
|
|
if (!$this->fp && !file_exists(dirname($fullpath))) {
|
|
if (!mkdir($concurrentDirectory = dirname($fullpath), 0755, true) && !is_dir($concurrentDirectory)) {
|
|
throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
|
|
}
|
|
$this->fp = fopen($fullpath, $mode);
|
|
}
|
|
}
|
|
|
|
return !empty($this->fp);
|
|
}
|
|
|
|
public function stream_read($count)
|
|
{
|
|
return fread($this->fp, $count);
|
|
}
|
|
|
|
public function stream_seek($offset, $whence = SEEK_SET)
|
|
{
|
|
return fseek($this->fp, $offset, $whence) == 0;
|
|
}
|
|
|
|
public function stream_set_option($option, $arg1, $arg2)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function stream_stat()
|
|
{
|
|
return fstat($this->fp);
|
|
}
|
|
|
|
public function stream_tell()
|
|
{
|
|
return ftell($this->fp);
|
|
}
|
|
|
|
public function stream_write($data)
|
|
{
|
|
return fwrite($this->fp, $data);
|
|
}
|
|
|
|
public function unlink($path)
|
|
{
|
|
unlink(self::path($path));
|
|
|
|
return true;
|
|
}
|
|
|
|
public function url_stat($path, $flags)
|
|
{
|
|
return @stat(self::path($path));
|
|
}
|
|
|
|
public static function move_uploaded_file($upload, $path)
|
|
{
|
|
return move_uploaded_file($upload, self::path($path));
|
|
}
|
|
}
|