2003-12-26 23:03:21 +00:00
< ? php
2003-12-27 21:29:20 +00:00
2004-08-21 06:42:38 +00:00
/**
* @ file
* API for handling file uploads and server file management .
*/
Issue #2244513 by kim.pepper, phenaproxima, 20th, andrei.dincu, beejeebus, Berdir, alexpott, jibran, andypost, larowlan, Chadwick Wood, acbramley, Wim Leers, sun, xjm, YesCT, chx, tim.plunkett: Move the unmanaged file APIs to the file_system service (file.inc)
2019-02-23 22:35:15 +00:00
use Drupal\Component\Utility\UrlHelper ;
Issue #3032390 by alexpott, dww, Pancho, 3CWebDev, kim.pepper, larowlan, Berdir, catch, andypost, chr.fritsch, Wim Leers: Add an event to sanitize filenames during upload
2021-02-24 16:02:18 +00:00
use Drupal\Core\File\FileSystemInterface ;
2019-06-02 07:53:50 +00:00
use Drupal\Core\StreamWrapper\StreamWrapperManager ;
2014-01-24 09:45:47 +00:00
2003-12-27 21:29:20 +00:00
/**
2004-01-06 19:52:14 +00:00
* @ defgroup file File interface
2003-12-27 21:29:20 +00:00
* @ {
2004-09-09 05:51:08 +00:00
* Common file handling functions .
2003-12-26 23:03:21 +00:00
*/
2007-05-30 08:08:59 +00:00
/**
2009-12-30 08:16:55 +00:00
* Indicates that the file is permanent and should not be deleted .
*
2014-04-13 17:52:04 +00:00
* Temporary files older than the system . file . temporary_maximum_age
* configuration value will be , if clean - up not disabled , removed during cron
* runs , but permanent files will not be removed during the file garbage
* collection process .
2008-05-26 17:12:55 +00:00
*/
2011-11-29 09:56:53 +00:00
const FILE_STATUS_PERMANENT = 1 ;
2007-05-30 08:08:59 +00:00
2003-12-26 23:03:21 +00:00
/**
2009-08-29 06:57:27 +00:00
* Creates a web - accessible URL for a stream to an external or local file .
2004-09-17 18:18:04 +00:00
*
2009-08-17 19:14:42 +00:00
* Compatibility : normal paths and stream wrappers .
2004-09-17 18:18:04 +00:00
*
2009-08-29 06:57:27 +00:00
* There are two kinds of local files :
2010-09-01 20:08:17 +00:00
* - " managed files " , i . e . those stored by a Drupal - compatible stream wrapper .
* These are files that have either been uploaded by users or were generated
* automatically ( for example through CSS aggregation ) .
2009-08-29 06:57:27 +00:00
* - " shipped files " , i . e . those outside of the files directory , which ship as
* part of Drupal core or contributed modules or themes .
*
2014-09-22 11:55:48 +00:00
* @ param string $uri
2009-08-29 06:57:27 +00:00
* The URI to a file for which we need an external URL , or the path to a
* shipped file .
2010-07-07 01:10:35 +00:00
*
2014-09-22 11:55:48 +00:00
* @ return string
2009-08-17 19:14:42 +00:00
* A string containing a URL that may be used to access the file .
2010-08-17 21:31:13 +00:00
* If the provided string already contains a preceding 'http' , 'https' , or
* '/' , nothing is done and the same string is returned . If a stream wrapper
* could not be found to generate an external URL , then FALSE is returned .
2012-02-19 03:41:24 +00:00
*
2015-05-24 20:08:46 +00:00
* @ see https :// www . drupal . org / node / 515192
2013-11-23 20:12:07 +00:00
* @ see file_url_transform_relative ()
2003-12-26 23:03:21 +00:00
*/
2009-08-17 19:14:42 +00:00
function file_create_url ( $uri ) {
2009-08-29 06:57:27 +00:00
// Allow the URI to be altered, e.g. to serve a file from a CDN or static
// file server.
2014-02-24 10:10:52 +00:00
\Drupal :: moduleHandler () -> alter ( 'file_url' , $uri );
2009-09-28 22:22:54 +00:00
2019-06-02 07:53:50 +00:00
$scheme = StreamWrapperManager :: getScheme ( $uri );
2009-08-17 19:14:42 +00:00
if ( ! $scheme ) {
2010-05-11 10:56:04 +00:00
// Allow for:
// - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
// - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
// http://example.com/bar.jpg by the browser when viewing a page over
// HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
// Both types of relative URIs are characterized by a leading slash, hence
// we can use a single check.
2018-05-08 16:23:11 +00:00
if ( mb_substr ( $uri , 0 , 1 ) == '/' ) {
2010-05-11 10:56:04 +00:00
return $uri ;
}
else {
// If this is not a properly formatted stream, then it is a shipped file.
2010-08-17 21:31:13 +00:00
// Therefore, return the urlencoded URI with the base URL prepended.
2014-10-29 09:31:01 +00:00
$options = UrlHelper :: parse ( $uri );
$path = $GLOBALS [ 'base_url' ] . '/' . UrlHelper :: encodePath ( $options [ 'path' ]);
// Append the query.
if ( $options [ 'query' ]) {
$path .= '?' . UrlHelper :: buildQuery ( $options [ 'query' ]);
}
// Append fragment.
if ( $options [ 'fragment' ]) {
$path .= '#' . $options [ 'fragment' ];
}
return $path ;
2010-05-11 10:56:04 +00:00
}
2009-08-17 19:14:42 +00:00
}
2014-09-22 11:55:48 +00:00
elseif ( $scheme == 'http' || $scheme == 'https' || $scheme == 'data' ) {
// Check for HTTP and data URI-encoded URLs so that we don't have to
// implement getExternalUrl() for the HTTP and data schemes.
2009-08-17 19:14:42 +00:00
return $uri ;
}
else {
// Attempt to return an external URL using the appropriate wrapper.
2015-07-14 07:24:29 +00:00
if ( $wrapper = \Drupal :: service ( 'stream_wrapper_manager' ) -> getViaUri ( $uri )) {
2009-08-17 19:14:42 +00:00
return $wrapper -> getExternalUrl ();
}
else {
return FALSE ;
}
}
2003-12-26 23:03:21 +00:00
}
2013-11-23 20:12:07 +00:00
/**
* Transforms an absolute URL of a local file to a relative URL .
*
* May be useful to prevent problems on multisite set - ups and prevent mixed
* content errors when using HTTPS + HTTP .
*
* @ param string $file_url
* A file URL of a local file as generated by file_create_url () .
*
* @ return string
* If the file URL indeed pointed to a local file and was indeed absolute ,
* then the transformed , relative URL to the local file . Otherwise : the
* original value of $file_url .
*
* @ see file_create_url ()
*/
function file_url_transform_relative ( $file_url ) {
// Unfortunately, we pretty much have to duplicate Symfony's
// Request::getHttpHost() method because Request::getPort() may return NULL
// instead of a port number.
$request = \Drupal :: request ();
$host = $request -> getHost ();
$scheme = $request -> getScheme ();
$port = $request -> getPort () ? : 80 ;
2021-02-27 22:47:24 +00:00
// Files may be accessible on a different port than the web request.
$file_url_port = parse_url ( $file_url , PHP_URL_PORT ) ? ? $port ;
if ( $file_url_port != $port ) {
return $file_url ;
}
2013-11-23 20:12:07 +00:00
if (( 'http' == $scheme && $port == 80 ) || ( 'https' == $scheme && $port == 443 )) {
$http_host = $host ;
}
else {
$http_host = $host . ':' . $port ;
}
2018-03-29 16:16:28 +00:00
return preg_replace ( '|^https?://' . preg_quote ( $http_host , '|' ) . '|' , '' , $file_url );
2013-11-23 20:12:07 +00:00
}
2009-08-17 19:14:42 +00:00
/**
2012-02-19 03:41:24 +00:00
* Constructs a URI to Drupal ' s default files location given a relative path .
2009-08-17 19:14:42 +00:00
*/
function file_build_uri ( $path ) {
2019-05-17 02:15:25 +00:00
$uri = \Drupal :: config ( 'system.file' ) -> get ( 'default_scheme' ) . '://' . $path ;
2019-06-02 07:53:50 +00:00
/** @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager */
$stream_wrapper_manager = \Drupal :: service ( 'stream_wrapper_manager' );
return $stream_wrapper_manager -> normalizeUri ( $uri );
2009-08-17 19:14:42 +00:00
}
2007-05-30 08:08:59 +00:00
/**
2012-02-19 03:41:24 +00:00
* Modifies a filename as needed for security purposes .
2008-10-09 20:07:00 +00:00
*
2010-08-08 01:37:34 +00:00
* Munging a file name prevents unknown file extensions from masking exploit
* files . When web servers such as Apache decide how to process a URL request ,
* they use the file extension . If the extension is not recognized , Apache
* skips that extension and uses the previous file extension . For example , if
* the file being requested is exploit . php . pps , and Apache does not recognize
* the '.pps' extension , it treats the file as PHP and executes it . To make
* this file name safe for Apache and prevent it from executing as PHP , the
* . php extension is " munged " into . php_ , making the safe file name
* exploit . php_ . pps .
*
* Specifically , this function adds an underscore to all extensions that are
SA-CORE-2020-012 by ufku, mrf, fgm, samuel.mortenson, dww, Heine, mlhess, David_Rothstein, pwolanin, xjm, fgm, stefan.r, dsnopek, rickmanelius, David Strauss, tedbow, alexpott, dww, larowlan, kim.pepper, Wim Leers, quicksketch, mcdruid, Fabianx, effulgentsia, drumm, pandaski, Mixologic
2020-11-17 22:16:53 +00:00
* between 2 and 5 characters in length , internal to the file name , and either
* included in the list of unsafe extensions , or not included in $extensions .
2010-08-08 01:37:34 +00:00
*
Issue #1496480 by mrf, disasm, kbasarab, pfrenssen, cam8001, typhonius, Letharion, ACF, vijaycs85, heyrocker, Berdir, alexpott: Convert file system settings to the new configuration system.
2013-02-08 23:36:06 +00:00
* Function behavior is also controlled by the configuration
* 'system.file:allow_insecure_uploads' . If it evaluates to TRUE , no alterations
* will be made , if it evaluates to FALSE , the filename is 'munged' . *
2008-10-09 20:07:00 +00:00
* @ param $filename
2009-10-18 18:36:24 +00:00
* File name to modify .
2008-10-09 20:07:00 +00:00
* @ param $extensions
SA-CORE-2020-012 by ufku, mrf, fgm, samuel.mortenson, dww, Heine, mlhess, David_Rothstein, pwolanin, xjm, fgm, stefan.r, dsnopek, rickmanelius, David Strauss, tedbow, alexpott, dww, larowlan, kim.pepper, Wim Leers, quicksketch, mcdruid, Fabianx, effulgentsia, drumm, pandaski, Mixologic
2020-11-17 22:16:53 +00:00
* A space - separated list of extensions that should not be altered . Note that
* extensions that are unsafe will be altered regardless of this parameter .
2008-10-09 20:07:00 +00:00
* @ param $alerts
2018-01-22 15:28:16 +00:00
* If TRUE , \Drupal :: messenger () -> addStatus () will be called to display
* a message if the file name was changed .
2009-10-18 18:36:24 +00:00
*
Issue #1496480 by mrf, disasm, kbasarab, pfrenssen, cam8001, typhonius, Letharion, ACF, vijaycs85, heyrocker, Berdir, alexpott: Convert file system settings to the new configuration system.
2013-02-08 23:36:06 +00:00
* @ return string
2009-10-18 18:36:24 +00:00
* The potentially modified $filename .
Issue #3032390 by alexpott, dww, Pancho, 3CWebDev, kim.pepper, larowlan, Berdir, catch, andypost, chr.fritsch, Wim Leers: Add an event to sanitize filenames during upload
2021-02-24 16:02:18 +00:00
*
* @ deprecated in drupal : 9.2 . 0 and is removed from drupal : 10.0 . 0. Dispatch a
* \Drupal\Core\File\Event\FileUploadSanitizeNameEvent event instead .
*
* @ see https :// www . drupal . org / node / 3032541
2007-05-30 08:08:59 +00:00
*/
function file_munge_filename ( $filename , $extensions , $alerts = TRUE ) {
Issue #3032390 by alexpott, dww, Pancho, 3CWebDev, kim.pepper, larowlan, Berdir, catch, andypost, chr.fritsch, Wim Leers: Add an event to sanitize filenames during upload
2021-02-24 16:02:18 +00:00
@ trigger_error ( 'file_munge_filename() is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Dispatch a \Drupal\Core\File\Event\FileUploadSanitizeNameEvent event instead. See https://www.drupal.org/node/3032541' , E_USER_DEPRECATED );
2007-05-30 08:08:59 +00:00
$original = $filename ;
// Allow potentially insecure uploads for very savvy users and admin
2013-09-16 03:58:06 +00:00
if ( ! \Drupal :: config ( 'system.file' ) -> get ( 'allow_insecure_uploads' )) {
2016-01-05 14:31:39 +00:00
// Remove any null bytes. See
// http://php.net/manual/security.filesystem.nullbytes.php
2012-12-27 02:27:47 +00:00
$filename = str_replace ( chr ( 0 ), '' , $filename );
2020-06-12 15:42:28 +00:00
$allowed_extensions = array_unique ( explode ( ' ' , strtolower ( trim ( $extensions ))));
2007-05-30 08:08:59 +00:00
SA-CORE-2020-012 by ufku, mrf, fgm, samuel.mortenson, dww, Heine, mlhess, David_Rothstein, pwolanin, xjm, fgm, stefan.r, dsnopek, rickmanelius, David Strauss, tedbow, alexpott, dww, larowlan, kim.pepper, Wim Leers, quicksketch, mcdruid, Fabianx, effulgentsia, drumm, pandaski, Mixologic
2020-11-17 22:16:53 +00:00
// Remove unsafe extensions from the allowed list of extensions.
Issue #3032390 by alexpott, dww, Pancho, 3CWebDev, kim.pepper, larowlan, Berdir, catch, andypost, chr.fritsch, Wim Leers: Add an event to sanitize filenames during upload
2021-02-24 16:02:18 +00:00
$allowed_extensions = array_diff ( $allowed_extensions , FileSystemInterface :: INSECURE_EXTENSIONS );
SA-CORE-2020-012 by ufku, mrf, fgm, samuel.mortenson, dww, Heine, mlhess, David_Rothstein, pwolanin, xjm, fgm, stefan.r, dsnopek, rickmanelius, David Strauss, tedbow, alexpott, dww, larowlan, kim.pepper, Wim Leers, quicksketch, mcdruid, Fabianx, effulgentsia, drumm, pandaski, Mixologic
2020-11-17 22:16:53 +00:00
2007-05-30 08:08:59 +00:00
// Split the filename up by periods. The first part becomes the basename
// the last part the final extension.
$filename_parts = explode ( '.' , $filename );
2017-10-13 12:43:02 +00:00
// Remove file basename.
$new_filename = array_shift ( $filename_parts );
// Remove final extension.
$final_extension = array_pop ( $filename_parts );
2007-05-30 08:08:59 +00:00
// Loop through the middle parts of the name and add an underscore to the
// end of each section that could be a file extension but isn't in the list
// of allowed extensions.
foreach ( $filename_parts as $filename_part ) {
2008-04-14 17:48:46 +00:00
$new_filename .= '.' . $filename_part ;
2020-06-12 15:42:28 +00:00
if ( ! in_array ( strtolower ( $filename_part ), $allowed_extensions ) && preg_match ( " /^[a-zA-Z] { 2,5} \ d? $ / " , $filename_part )) {
2007-05-30 08:08:59 +00:00
$new_filename .= '_' ;
}
}
2008-04-14 17:48:46 +00:00
$filename = $new_filename . '.' . $final_extension ;
2007-05-30 08:08:59 +00:00
if ( $alerts && $original != $filename ) {
2018-01-22 15:28:16 +00:00
\Drupal :: messenger () -> addStatus ( t ( 'For security reasons, your upload has been renamed to %filename.' , [ '%filename' => $filename ]));
2007-05-30 08:08:59 +00:00
}
}
return $filename ;
}
/**
2012-02-19 03:41:24 +00:00
* Undoes the effect of file_munge_filename () .
2007-05-30 08:08:59 +00:00
*
2008-10-09 20:07:00 +00:00
* @ param $filename
* String with the filename to be unmunged .
2010-07-07 01:10:35 +00:00
*
2008-10-09 20:07:00 +00:00
* @ return
* An unmunged filename string .
Issue #3032390 by alexpott, dww, Pancho, 3CWebDev, kim.pepper, larowlan, Berdir, catch, andypost, chr.fritsch, Wim Leers: Add an event to sanitize filenames during upload
2021-02-24 16:02:18 +00:00
*
* @ deprecated in drupal : 9.2 . 0 and is removed from drupal : 10.0 . 0. Use
* str_replace () instead .
*
* @ see https :// www . drupal . org / node / 3032541
2007-05-30 08:08:59 +00:00
*/
function file_unmunge_filename ( $filename ) {
Issue #3032390 by alexpott, dww, Pancho, 3CWebDev, kim.pepper, larowlan, Berdir, catch, andypost, chr.fritsch, Wim Leers: Add an event to sanitize filenames during upload
2021-02-24 16:02:18 +00:00
@ trigger_error ( 'file_unmunge_filename() is deprecated in drupal:9.2.0 and is removed from drupal:10.0.0. Use str_replace() instead. See https://www.drupal.org/node/3032541' , E_USER_DEPRECATED );
2007-05-30 08:08:59 +00:00
return str_replace ( '_.' , '.' , $filename );
}
2009-04-20 20:02:31 +00:00
/**
* @ } End of " defgroup file " .
2009-06-09 21:33:12 +00:00
*/