2011-07-27 13:07:28 -04:00
< ? php
2024-05-23 03:26:56 -04:00
2011-07-27 13:07:28 -04:00
/**
2024-05-23 03:26:56 -04:00
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - only
2011-07-27 13:07:28 -04:00
*/
2020-08-20 08:08:18 -04:00
use bantu\IniGetWrapper\IniGetWrapper ;
2023-08-29 18:18:12 -04:00
use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager ;
2025-11-17 09:32:54 -05:00
use OC\Files\Cache\Scanner ;
use OC\Files\Filesystem ;
2022-02-10 11:36:46 -05:00
use OC\Files\SetupManager ;
2025-11-17 09:32:54 -05:00
use OC\Setup ;
use OC\SystemConfig ;
use OCP\Files\FileInfo ;
use OCP\Files\Folder ;
use OCP\Files\NotFoundException ;
use OCP\Files\NotPermittedException ;
2021-01-12 05:28:04 -05:00
use OCP\Files\Template\ITemplateManager ;
2025-11-17 09:32:54 -05:00
use OCP\HintException ;
2015-11-19 09:35:58 -05:00
use OCP\IConfig ;
use OCP\IGroupManager ;
2025-11-17 09:32:54 -05:00
use OCP\IRequest ;
use OCP\ISession ;
2021-07-01 09:12:15 -04:00
use OCP\IURLGenerator ;
2015-11-19 09:35:58 -05:00
use OCP\IUser ;
2025-11-17 09:32:54 -05:00
use OCP\IUserManager ;
use OCP\IUserSession ;
2023-08-29 16:25:03 -04:00
use OCP\L10N\IFactory ;
2023-08-29 17:29:33 -04:00
use OCP\Security\ISecureRandom ;
2025-11-17 09:32:54 -05:00
use OCP\Server ;
2021-04-23 11:29:34 -04:00
use OCP\Share\IManager ;
2025-11-17 09:32:54 -05:00
use OCP\Util ;
2021-01-19 05:20:50 -05:00
use Psr\Log\LoggerInterface ;
2015-11-19 09:35:58 -05:00
2025-04-01 09:36:38 -04:00
/**
* @ deprecated 32.0 . 0 Use \OCP\Util or any appropriate official API instead
*/
2011-07-29 15:36:03 -04:00
class OC_Util {
2020-03-26 04:30:18 -04:00
public static $styles = [];
public static $headers = [];
2011-07-27 13:07:28 -04:00
2022-01-31 09:53:28 -05:00
/**
2022-02-02 10:25:47 -05:00
* Setup the file system
2022-01-31 09:53:28 -05:00
*
2022-02-02 10:25:47 -05:00
* @ param string | null $user
2022-01-31 09:53:28 -05:00
* @ return boolean
* @ description configure the initial filesystem based on the configuration
* @ suppress PhanDeprecatedFunction
* @ suppress PhanAccessMethodInternal
*/
2022-02-02 10:25:47 -05:00
public static function setupFS ( ? string $user = '' ) {
2022-01-31 09:53:28 -05:00
// If we are not forced to load a specific user we load the one that is logged in
if ( $user === '' ) {
2025-11-17 09:32:54 -05:00
$userObject = Server :: get ( IUserSession :: class ) -> getUser ();
2022-01-31 09:53:28 -05:00
} else {
2025-11-17 09:32:54 -05:00
$userObject = Server :: get ( IUserManager :: class ) -> get ( $user );
2022-01-31 09:53:28 -05:00
}
2022-02-10 11:36:46 -05:00
/** @var SetupManager $setupManager */
2025-11-17 09:32:54 -05:00
$setupManager = Server :: get ( SetupManager :: class );
2022-02-01 13:05:23 -05:00
2022-02-10 11:36:46 -05:00
if ( $userObject ) {
$setupManager -> setupForUser ( $userObject );
} else {
$setupManager -> setupRoot ();
2011-07-27 13:07:28 -04:00
}
2022-02-23 12:11:46 -05:00
return true ;
2011-07-27 13:07:28 -04:00
}
2014-05-12 06:19:07 -04:00
/**
2022-02-15 11:45:09 -05:00
* Check if a password is required for each public link
2014-08-19 12:01:26 -04:00
*
2022-02-15 11:45:09 -05:00
* @ param bool $checkGroupMembership Check group membership exclusion
2025-04-01 10:07:27 -04:00
* @ return bool
2025-04-08 11:56:14 -04:00
* @ deprecated 32.0 . 0 use OCP\Share\IManager ' s shareApiLinkEnforcePassword directly
2014-05-12 06:19:07 -04:00
*/
2022-02-15 11:45:09 -05:00
public static function isPublicLinkPasswordRequired ( bool $checkGroupMembership = true ) {
2021-04-23 11:29:34 -04:00
/** @var IManager $shareManager */
2025-11-17 09:32:54 -05:00
$shareManager = Server :: get ( IManager :: class );
2022-02-15 11:45:09 -05:00
return $shareManager -> shareApiLinkEnforcePassword ( $checkGroupMembership );
2014-05-12 06:19:07 -04:00
}
2014-05-13 09:22:18 -04:00
/**
* check if sharing is disabled for the current user
2015-11-19 09:35:58 -05:00
* @ param IConfig $config
* @ param IGroupManager $groupManager
* @ param IUser | null $user
* @ return bool
2025-04-08 11:56:14 -04:00
* @ deprecated 32.0 . 0 use OCP\Share\IManager ' s sharingDisabledForUser directly
2014-05-13 09:22:18 -04:00
*/
2015-11-19 09:35:58 -05:00
public static function isSharingDisabledForUser ( IConfig $config , IGroupManager $groupManager , $user ) {
2021-04-23 11:29:34 -04:00
/** @var IManager $shareManager */
2025-11-17 09:32:54 -05:00
$shareManager = Server :: get ( IManager :: class );
2021-04-23 11:29:34 -04:00
$userId = $user ? $user -> getUID () : null ;
return $shareManager -> sharingDisabledForUser ( $userId );
2014-05-13 09:22:18 -04:00
}
2014-07-23 10:42:33 -04:00
/**
* check if share API enforces a default expire date
2014-08-19 12:01:26 -04:00
*
2023-01-23 05:11:26 -05:00
* @ return bool
2025-04-08 11:56:14 -04:00
* @ deprecated 32.0 . 0 use OCP\Share\IManager ' s shareApiLinkDefaultExpireDateEnforced directly
2014-07-23 10:42:33 -04:00
*/
public static function isDefaultExpireDateEnforced () {
2021-04-23 11:29:34 -04:00
/** @var IManager $shareManager */
2025-11-17 09:32:54 -05:00
$shareManager = Server :: get ( IManager :: class );
2021-04-23 11:29:34 -04:00
return $shareManager -> shareApiLinkDefaultExpireDateEnforced ();
2014-07-23 10:42:33 -04:00
}
2013-10-03 17:22:11 -04:00
/**
2014-08-29 08:36:00 -04:00
* copies the skeleton to the users / files
2014-08-19 12:01:26 -04:00
*
2018-11-15 05:51:28 -05:00
* @ param string $userId
2025-11-17 09:32:54 -05:00
* @ param Folder $userDirectory
* @ throws NotFoundException
* @ throws NotPermittedException
2017-07-19 13:44:10 -04:00
* @ suppress PhanDeprecatedFunction
2013-10-03 17:22:11 -04:00
*/
2025-11-17 09:32:54 -05:00
public static function copySkeleton ( $userId , Folder $userDirectory ) {
2021-01-19 05:20:50 -05:00
/** @var LoggerInterface $logger */
2025-11-17 09:32:54 -05:00
$logger = Server :: get ( LoggerInterface :: class );
2021-01-19 05:20:50 -05:00
2025-11-17 09:32:54 -05:00
$plainSkeletonDirectory = Server :: get ( IConfig :: class ) -> getSystemValueString ( 'skeletondirectory' , \OC :: $SERVERROOT . '/core/skeleton' );
$userLang = Server :: get ( IFactory :: class ) -> findLanguage ();
2017-11-29 03:50:40 -05:00
$skeletonDirectory = str_replace ( '{lang}' , $userLang , $plainSkeletonDirectory );
if ( ! file_exists ( $skeletonDirectory )) {
$dialectStart = strpos ( $userLang , '_' );
if ( $dialectStart !== false ) {
$skeletonDirectory = str_replace ( '{lang}' , substr ( $userLang , 0 , $dialectStart ), $plainSkeletonDirectory );
}
if ( $dialectStart === false || ! file_exists ( $skeletonDirectory )) {
2017-12-08 06:28:41 -05:00
$skeletonDirectory = str_replace ( '{lang}' , 'default' , $plainSkeletonDirectory );
}
if ( ! file_exists ( $skeletonDirectory )) {
$skeletonDirectory = '' ;
2017-11-29 03:50:40 -05:00
}
}
2025-11-17 09:32:54 -05:00
$instanceId = Server :: get ( IConfig :: class ) -> getSystemValue ( 'instanceid' , '' );
2016-09-12 02:57:40 -04:00
if ( $instanceId === null ) {
throw new \RuntimeException ( 'no instance id!' );
}
$appdata = 'appdata_' . $instanceId ;
if ( $userId === $appdata ) {
throw new \RuntimeException ( 'username is reserved name: ' . $appdata );
}
2014-08-29 08:36:00 -04:00
2014-08-15 19:07:42 -04:00
if ( ! empty ( $skeletonDirectory )) {
2021-01-19 05:20:50 -05:00
$logger -> debug ( 'copying skeleton for ' . $userId . ' from ' . $skeletonDirectory . ' to ' . $userDirectory -> getFullPath ( '/' ), [ 'app' => 'files_skeleton' ]);
2014-08-29 08:36:00 -04:00
self :: copyr ( $skeletonDirectory , $userDirectory );
// update the file cache
2025-11-17 09:32:54 -05:00
$userDirectory -> getStorage () -> getScanner () -> scan ( '' , Scanner :: SCAN_RECURSIVE );
2021-01-19 05:20:50 -05:00
2021-01-19 10:38:51 -05:00
/** @var ITemplateManager $templateManager */
2025-11-17 09:32:54 -05:00
$templateManager = Server :: get ( ITemplateManager :: class );
2021-01-19 10:38:51 -05:00
$templateManager -> initializeTemplateDirectory ( null , $userId );
2014-08-15 19:07:42 -04:00
}
2013-10-03 17:22:11 -04:00
}
/**
2014-08-29 08:36:00 -04:00
* copies a directory recursively by using streams
2014-08-19 12:01:26 -04:00
*
2013-10-03 17:22:11 -04:00
* @ param string $source
2025-11-17 09:32:54 -05:00
* @ param Folder $target
2013-10-03 17:22:11 -04:00
* @ return void
*/
2025-11-17 09:32:54 -05:00
public static function copyr ( $source , Folder $target ) {
$logger = Server :: get ( LoggerInterface :: class );
2016-09-25 12:58:05 -04:00
// Verify if folder exists
2013-10-03 17:22:11 -04:00
$dir = opendir ( $source );
2016-09-25 12:58:05 -04:00
if ( $dir === false ) {
$logger -> error ( sprintf ( 'Could not opendir "%s"' , $source ), [ 'app' => 'core' ]);
return ;
}
// Copy the files
2014-08-19 12:01:26 -04:00
while ( false !== ( $file = readdir ( $dir ))) {
2025-11-17 09:32:54 -05:00
if ( ! Filesystem :: isIgnoredDir ( $file )) {
2014-08-19 12:01:26 -04:00
if ( is_dir ( $source . '/' . $file )) {
2014-08-29 08:36:00 -04:00
$child = $target -> newFolder ( $file );
self :: copyr ( $source . '/' . $file , $child );
2013-10-03 17:22:11 -04:00
} else {
2016-09-25 12:58:05 -04:00
$sourceStream = fopen ( $source . '/' . $file , 'r' );
if ( $sourceStream === false ) {
$logger -> error ( sprintf ( 'Could not fopen "%s"' , $source . '/' . $file ), [ 'app' => 'core' ]);
closedir ( $dir );
return ;
}
2025-06-12 13:48:32 -04:00
$target -> newFile ( $file , $sourceStream );
2013-10-03 17:22:11 -04:00
}
}
}
closedir ( $dir );
}
2013-08-15 02:49:19 -04:00
/**
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0 Call tearDown directly on SetupManager
2013-08-18 04:33:09 -04:00
*/
2025-04-01 10:07:27 -04:00
public static function tearDownFS () : void {
2025-11-17 09:32:54 -05:00
$setupManager = Server :: get ( SetupManager :: class );
2022-02-23 12:11:46 -05:00
$setupManager -> tearDown ();
2011-07-27 13:07:28 -04:00
}
2012-10-28 08:45:37 -04:00
2011-07-27 13:07:28 -04:00
/**
2014-11-03 05:18:17 -05:00
* generates a path for JS / CSS files . If no application is provided it will create the path for core .
2011-07-27 13:07:28 -04:00
*
2014-12-10 09:41:38 -05:00
* @ param string $application application to get the files from
2016-04-07 13:51:27 -04:00
* @ param string $directory directory within this application ( css , js , vendor , etc )
2025-04-01 10:07:27 -04:00
* @ param ? string $file the file inside of the above folder
2011-07-27 13:07:28 -04:00
*/
2025-04-01 10:07:27 -04:00
private static function generatePath ( $application , $directory , $file ) : string {
2014-08-19 12:01:26 -04:00
if ( is_null ( $file )) {
2011-07-27 13:07:28 -04:00
$file = $application ;
$application = '' ;
}
2014-08-19 12:01:26 -04:00
if ( ! empty ( $application )) {
2014-11-03 05:18:17 -05:00
return " $application / $directory / $file " ;
2013-08-15 02:49:19 -04:00
} else {
2014-11-03 05:18:17 -05:00
return " $directory / $file " ;
2011-07-27 13:07:28 -04:00
}
}
/**
2014-05-19 11:50:53 -04:00
* add a css file
2011-07-27 13:07:28 -04:00
*
2014-10-17 13:47:37 -04:00
* @ param string $application application id
2014-02-19 03:31:54 -05:00
* @ param string | null $file filename
2015-09-25 05:56:55 -04:00
* @ param bool $prepend prepend the Style to the beginning of the list
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0 Use \OCP\Util :: addStyle
2011-07-27 13:07:28 -04:00
*/
2025-04-01 10:07:27 -04:00
public static function addStyle ( $application , $file = null , $prepend = false ) : void {
2014-12-10 09:41:38 -05:00
$path = OC_Util :: generatePath ( $application , 'css' , $file );
2015-09-30 00:09:29 -04:00
self :: addExternalResource ( $application , $prepend , $path , 'style' );
2014-11-03 05:18:17 -05:00
}
/**
* add a css file from the vendor sub folder
*
* @ param string $application application id
* @ param string | null $file filename
2015-09-25 05:56:55 -04:00
* @ param bool $prepend prepend the Style to the beginning of the list
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0
2014-11-03 05:18:17 -05:00
*/
2025-04-01 10:07:27 -04:00
public static function addVendorStyle ( $application , $file = null , $prepend = false ) : void {
2014-12-10 09:41:38 -05:00
$path = OC_Util :: generatePath ( $application , 'vendor' , $file );
2015-09-30 00:09:29 -04:00
self :: addExternalResource ( $application , $prepend , $path , 'style' );
}
/**
* add an external resource css / js file
*
* @ param string $application application id
* @ param bool $prepend prepend the file to the beginning of the list
2016-10-01 06:17:55 -04:00
* @ param string $path
2015-09-30 00:09:29 -04:00
* @ param string $type ( script or style )
*/
2025-04-01 10:07:27 -04:00
private static function addExternalResource ( $application , $prepend , $path , $type = 'script' ) : void {
2015-09-30 00:09:29 -04:00
if ( $type === 'style' ) {
if ( ! in_array ( $path , self :: $styles )) {
if ( $prepend === true ) {
2020-04-09 10:07:47 -04:00
array_unshift ( self :: $styles , $path );
2015-09-30 00:09:29 -04:00
} else {
self :: $styles [] = $path ;
}
}
2014-12-10 09:41:38 -05:00
}
2011-07-27 13:07:28 -04:00
}
/**
2014-05-19 11:50:53 -04:00
* Add a custom element to the header
2014-10-28 06:15:58 -04:00
* If $text is null then the element will be written as empty element .
* So use " " to get a closing tag .
2013-08-23 01:30:42 -04:00
* @ param string $tag tag name of the element
2012-02-07 16:33:01 -05:00
* @ param array $attributes array of attributes for the element
2011-07-27 13:07:28 -04:00
* @ param string $text the text content for the element
2018-07-04 05:19:45 -04:00
* @ param bool $prepend prepend the header to the beginning of the list
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0 Use \OCP\Util :: addHeader instead
2011-07-27 13:07:28 -04:00
*/
2025-04-01 10:07:27 -04:00
public static function addHeader ( $tag , $attributes , $text = null , $prepend = false ) : void {
2020-03-26 04:30:18 -04:00
$header = [
2014-08-19 12:01:26 -04:00
'tag' => $tag ,
'attributes' => $attributes ,
'text' => $text
2020-03-26 04:30:18 -04:00
];
2018-07-04 05:19:45 -04:00
if ( $prepend === true ) {
2020-04-09 10:05:56 -04:00
array_unshift ( self :: $headers , $header );
2018-07-04 05:19:45 -04:00
} else {
self :: $headers [] = $header ;
}
2011-07-27 13:07:28 -04:00
}
/**
2025-12-01 12:49:27 -05:00
* Check if the current server environment configuration is suitable for Nextcloud
2014-08-19 12:01:26 -04:00
*
2011-07-27 13:07:28 -04:00
* @ return array arrays with error messages and hints
*/
2025-11-17 09:32:54 -05:00
public static function checkServer ( SystemConfig $config ) {
2014-08-31 04:05:59 -04:00
$l = \OC :: $server -> getL10N ( 'lib' );
2020-03-26 04:30:18 -04:00
$errors = [];
2017-03-17 18:37:48 -04:00
$CONFIG_DATADIRECTORY = $config -> getValue ( 'datadirectory' , OC :: $SERVERROOT . '/data' );
2014-03-14 08:03:18 -04:00
2017-03-17 18:37:48 -04:00
if ( ! self :: needUpgrade ( $config ) && $config -> getValue ( 'installed' , false )) {
2014-03-14 08:03:18 -04:00
// this check needs to be done every time
$errors = self :: checkDataDirectoryValidity ( $CONFIG_DATADIRECTORY );
}
2013-08-15 05:58:09 -04:00
// Assume that if checkServer() succeeded before in this session, then all is fine.
2025-11-17 09:32:54 -05:00
if ( Server :: get ( ISession :: class ) -> exists ( 'checkServer_succeeded' ) && Server :: get ( ISession :: class ) -> get ( 'checkServer_succeeded' )) {
2014-03-14 08:03:18 -04:00
return $errors ;
2013-08-15 02:49:19 -04:00
}
2013-08-15 05:58:09 -04:00
2013-08-15 02:49:19 -04:00
$webServerRestart = false ;
2025-11-17 09:32:54 -05:00
$setup = Server :: get ( Setup :: class );
2015-11-26 04:48:08 -05:00
2025-11-17 09:32:54 -05:00
$urlGenerator = Server :: get ( IURLGenerator :: class );
2015-11-26 04:48:08 -05:00
2014-10-27 07:51:26 -04:00
$availableDatabases = $setup -> getSupportedDatabases ();
if ( empty ( $availableDatabases )) {
2020-03-26 04:30:18 -04:00
$errors [] = [
2014-08-19 12:01:26 -04:00
'error' => $l -> t ( 'No database drivers (sqlite, mysql, or postgresql) installed.' ),
'hint' => '' //TODO: sane hint
2020-03-26 04:30:18 -04:00
];
2013-08-15 02:49:19 -04:00
$webServerRestart = true ;
2011-07-27 13:07:28 -04:00
}
2012-06-19 16:12:05 -04:00
// Check if config folder is writable.
2025-05-17 07:18:49 -04:00
if ( ! ( bool ) $config -> getValue ( 'config_is_read_only' , false )) {
2015-12-09 02:54:11 -05:00
if ( ! is_writable ( OC :: $configDir ) || ! is_readable ( OC :: $configDir )) {
2020-03-26 04:30:18 -04:00
$errors [] = [
2021-12-15 11:37:00 -05:00
'error' => $l -> t ( 'Cannot write into "config" directory.' ),
'hint' => $l -> t ( 'This can usually be fixed by giving the web server write access to the config directory. See %s' ,
2017-10-27 18:54:25 -04:00
[ $urlGenerator -> linkToDocs ( 'admin-dir_permissions' ) ]) . '. '
. $l -> t ( 'Or, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it. See %s' ,
2023-01-20 05:45:08 -05:00
[ $urlGenerator -> linkToDocs ( 'admin-config' ) ])
2020-03-26 04:30:18 -04:00
];
2015-12-09 02:54:11 -05:00
}
2012-06-19 16:12:05 -04:00
}
// Create root dir.
2017-03-17 18:37:48 -04:00
if ( $config -> getValue ( 'installed' , false )) {
2014-09-08 09:05:57 -04:00
if ( ! is_dir ( $CONFIG_DATADIRECTORY )) {
$success = @ mkdir ( $CONFIG_DATADIRECTORY );
if ( $success ) {
$errors = array_merge ( $errors , self :: checkDataDirectoryPermissions ( $CONFIG_DATADIRECTORY ));
} else {
2016-11-15 05:41:33 -05:00
$errors [] = [
2021-11-02 17:11:31 -04:00
'error' => $l -> t ( 'Cannot create "data" directory.' ),
2021-12-15 11:37:00 -05:00
'hint' => $l -> t ( 'This can usually be fixed by giving the web server write access to the root directory. See %s' ,
2016-11-15 05:41:33 -05:00
[ $urlGenerator -> linkToDocs ( 'admin-dir_permissions' )])
];
2014-09-08 09:05:57 -04:00
}
2020-04-10 04:35:09 -04:00
} elseif ( ! is_writable ( $CONFIG_DATADIRECTORY ) || ! is_readable ( $CONFIG_DATADIRECTORY )) {
2018-12-22 12:14:55 -05:00
// is_writable doesn't work for NFS mounts, so try to write a file and check if it exists.
$testFile = sprintf ( '%s/%s.tmp' , $CONFIG_DATADIRECTORY , uniqid ( 'data_dir_writability_test_' ));
$handle = fopen ( $testFile , 'w' );
2020-03-25 16:53:04 -04:00
if ( ! $handle || fwrite ( $handle , 'Test write operation' ) === false ) {
2021-12-15 11:37:00 -05:00
$permissionsHint = $l -> t ( 'Permissions can usually be fixed by giving the web server write access to the root directory. See %s.' ,
2018-12-22 12:14:55 -05:00
[ $urlGenerator -> linkToDocs ( 'admin-dir_permissions' )]);
$errors [] = [
2021-12-20 02:52:12 -05:00
'error' => $l -> t ( 'Your data directory is not writable.' ),
2018-12-22 12:14:55 -05:00
'hint' => $permissionsHint
];
} else {
fclose ( $handle );
unlink ( $testFile );
}
2014-09-08 09:05:57 -04:00
} else {
$errors = array_merge ( $errors , self :: checkDataDirectoryPermissions ( $CONFIG_DATADIRECTORY ));
2012-06-19 16:12:05 -04:00
}
2011-07-27 13:07:28 -04:00
}
2013-08-15 02:49:19 -04:00
2014-08-19 12:01:26 -04:00
if ( ! OC_Util :: isSetLocaleWorking ()) {
2020-03-26 04:30:18 -04:00
$errors [] = [
2021-12-15 11:37:00 -05:00
'error' => $l -> t ( 'Setting locale to %s failed.' ,
2020-03-26 04:30:18 -04:00
[ 'en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8' ]),
2021-12-15 11:37:00 -05:00
'hint' => $l -> t ( 'Please install one of these locales on your system and restart your web server.' )
2020-03-26 04:30:18 -04:00
];
2013-11-08 08:30:08 -05:00
}
2014-09-15 07:46:43 -04:00
// Contains the dependencies that should be checked against
// classes = class_exists
// functions = function_exists
// defined = defined
2015-02-21 06:12:34 -05:00
// ini = ini_get
2014-09-15 07:46:43 -04:00
// If the dependency is not found the missing module name is shown to the EndUser
2024-09-19 12:37:09 -04:00
// When adding new checks always verify that they pass on CI as well
2020-03-26 04:30:18 -04:00
$dependencies = [
'classes' => [
2014-09-15 07:46:43 -04:00
'ZipArchive' => 'zip' ,
'DOMDocument' => 'dom' ,
2016-03-09 05:11:00 -05:00
'XMLWriter' => 'XMLWriter' ,
'XMLReader' => 'XMLReader' ,
2020-03-26 04:30:18 -04:00
],
2015-03-12 13:39:54 -04:00
'functions' => [
2014-09-15 07:46:43 -04:00
'xml_parser_create' => 'libxml' ,
2018-12-04 11:22:11 -05:00
'mb_strcut' => 'mbstring' ,
2014-09-15 07:46:43 -04:00
'ctype_digit' => 'ctype' ,
'json_encode' => 'JSON' ,
'gd_info' => 'GD' ,
'gzencode' => 'zlib' ,
2014-12-28 07:23:34 -05:00
'simplexml_load_string' => 'SimpleXML' ,
2015-03-12 13:39:54 -04:00
'hash' => 'HASH Message Digest Framework' ,
'curl_init' => 'cURL' ,
2016-10-31 06:07:54 -04:00
'openssl_verify' => 'OpenSSL' ,
2015-03-12 13:39:54 -04:00
],
2020-03-26 04:30:18 -04:00
'defined' => [
2014-09-15 07:46:43 -04:00
'PDO::ATTR_DRIVER_NAME' => 'PDO'
2020-03-26 04:30:18 -04:00
],
2015-02-21 06:12:34 -05:00
'ini' => [
2015-02-24 17:34:38 -05:00
'default_charset' => 'UTF-8' ,
2015-02-21 06:12:34 -05:00
],
2020-03-26 04:30:18 -04:00
];
$missingDependencies = [];
2015-02-21 06:12:34 -05:00
$invalidIniSettings = [];
2014-09-15 07:46:43 -04:00
2025-11-17 09:32:54 -05:00
$iniWrapper = Server :: get ( IniGetWrapper :: class );
2018-10-03 16:07:51 -04:00
foreach ( $dependencies [ 'classes' ] as $class => $module ) {
if ( ! class_exists ( $class )) {
$missingDependencies [] = $module ;
2014-09-15 07:46:43 -04:00
}
2018-10-03 16:07:51 -04:00
}
foreach ( $dependencies [ 'functions' ] as $function => $module ) {
if ( ! function_exists ( $function )) {
$missingDependencies [] = $module ;
2015-02-21 06:12:34 -05:00
}
2018-10-03 16:07:51 -04:00
}
foreach ( $dependencies [ 'defined' ] as $defined => $module ) {
if ( ! defined ( $defined )) {
$missingDependencies [] = $module ;
2015-02-21 06:12:34 -05:00
}
2018-10-03 16:07:51 -04:00
}
foreach ( $dependencies [ 'ini' ] as $setting => $expected ) {
2022-05-16 04:58:26 -04:00
if ( strtolower ( $iniWrapper -> getString ( $setting )) !== strtolower ( $expected )) {
$invalidIniSettings [] = [ $setting , $expected ];
2015-02-24 17:34:38 -05:00
}
2015-02-21 06:12:34 -05:00
}
2014-09-15 07:46:43 -04:00
foreach ( $missingDependencies as $missingDependency ) {
2020-03-26 04:30:18 -04:00
$errors [] = [
'error' => $l -> t ( 'PHP module %s not installed.' , [ $missingDependency ]),
2020-07-08 09:12:01 -04:00
'hint' => $l -> t ( 'Please ask your server administrator to install the module.' ),
2020-03-26 04:30:18 -04:00
];
2013-08-15 02:49:19 -04:00
$webServerRestart = true ;
2012-10-06 11:37:38 -04:00
}
2015-02-21 06:12:34 -05:00
foreach ( $invalidIniSettings as $setting ) {
$errors [] = [
2015-02-23 03:40:15 -05:00
'error' => $l -> t ( 'PHP setting "%s" is not set to "%s".' , [ $setting [ 0 ], var_export ( $setting [ 1 ], true )]),
2020-10-05 09:12:57 -04:00
'hint' => $l -> t ( 'Adjusting this setting in php.ini will make Nextcloud run again' )
2015-02-21 06:12:34 -05:00
];
$webServerRestart = true ;
}
2014-09-15 07:46:43 -04:00
2014-05-19 15:51:35 -04:00
if ( ! self :: isAnnotationsWorking ()) {
2020-03-26 04:30:18 -04:00
$errors [] = [
2015-05-19 15:15:22 -04:00
'error' => $l -> t ( 'PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.' ),
2015-01-22 08:52:47 -05:00
'hint' => $l -> t ( 'This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.' )
2020-03-26 04:30:18 -04:00
];
2014-05-19 15:51:35 -04:00
}
2012-09-11 17:51:12 -04:00
2015-03-10 17:59:23 -04:00
if ( ! \OC :: $CLI && $webServerRestart ) {
2020-03-26 04:30:18 -04:00
$errors [] = [
2014-08-19 12:01:26 -04:00
'error' => $l -> t ( 'PHP modules have been installed, but they are still listed as missing?' ),
'hint' => $l -> t ( 'Please ask your server administrator to restart the web server.' )
2020-03-26 04:30:18 -04:00
];
2012-06-01 14:00:33 -04:00
}
2011-07-27 13:07:28 -04:00
2022-03-09 07:36:06 -05:00
foreach ([ 'secret' , 'instanceid' , 'passwordsalt' ] as $requiredConfig ) {
2022-03-09 08:39:59 -05:00
if ( $config -> getValue ( $requiredConfig , '' ) === '' && ! \OC :: $CLI && $config -> getValue ( 'installed' , false )) {
2022-03-09 07:36:06 -05:00
$errors [] = [
2022-04-28 02:30:49 -04:00
'error' => $l -> t ( 'The required %s config variable is not configured in the config.php file.' , [ $requiredConfig ]),
2022-03-09 07:36:06 -05:00
'hint' => $l -> t ( 'Please ask your server administrator to check the Nextcloud configuration.' )
];
}
2022-03-08 07:49:08 -05:00
}
2013-08-15 05:58:09 -04:00
// Cache the result of this function
2025-11-17 09:32:54 -05:00
Server :: get ( ISession :: class ) -> set ( 'checkServer_succeeded' , count ( $errors ) == 0 );
2013-08-15 05:58:09 -04:00
2011-07-27 13:07:28 -04:00
return $errors ;
}
2011-09-18 15:31:56 -04:00
2013-01-18 23:30:56 -05:00
/**
2014-05-19 11:50:53 -04:00
* Check for correct file permissions of data directory
2014-08-19 12:01:26 -04:00
*
2014-02-06 10:30:58 -05:00
* @ param string $dataDirectory
2013-08-23 01:30:42 -04:00
* @ return array arrays with error messages and hints
2025-04-01 10:07:27 -04:00
* @ internal
2013-08-23 01:30:42 -04:00
*/
2013-01-18 23:30:56 -05:00
public static function checkDataDirectoryPermissions ( $dataDirectory ) {
2025-11-17 09:32:54 -05:00
if ( ! Server :: get ( IConfig :: class ) -> getSystemValueBool ( 'check_data_directory_permissions' , true )) {
2017-11-30 05:16:00 -05:00
return [];
}
2020-07-08 15:47:16 -04:00
2016-07-08 09:55:17 -04:00
$perms = substr ( decoct ( @ fileperms ( $dataDirectory )), - 3 );
2017-04-10 05:13:46 -04:00
if ( substr ( $perms , - 1 ) !== '0' ) {
2016-07-08 09:55:17 -04:00
chmod ( $dataDirectory , 0770 );
clearstatcache ();
2013-08-15 02:49:19 -04:00
$perms = substr ( decoct ( @ fileperms ( $dataDirectory )), - 3 );
2017-04-10 05:13:46 -04:00
if ( $perms [ 2 ] !== '0' ) {
2020-07-08 15:47:16 -04:00
$l = \OC :: $server -> getL10N ( 'lib' );
2020-07-09 03:29:44 -04:00
return [[
2022-09-21 11:44:32 -04:00
'error' => $l -> t ( 'Your data directory is readable by other people.' ),
'hint' => $l -> t ( 'Please change the permissions to 0770 so that the directory cannot be listed by other people.' ),
2020-07-09 03:29:44 -04:00
]];
2013-01-18 23:30:56 -05:00
}
}
2020-07-08 15:47:16 -04:00
return [];
2013-01-18 23:30:56 -05:00
}
2014-03-14 08:03:18 -04:00
/**
* Check that the data directory exists and is valid by
2024-07-11 14:53:37 -04:00
* checking the existence of the " .ncdata " file .
2014-03-14 08:03:18 -04:00
*
* @ param string $dataDirectory data directory path
2015-03-27 19:27:21 -04:00
* @ return array errors found
2025-04-01 10:07:27 -04:00
* @ internal
2014-03-14 08:03:18 -04:00
*/
public static function checkDataDirectoryValidity ( $dataDirectory ) {
2014-08-31 04:05:59 -04:00
$l = \OC :: $server -> getL10N ( 'lib' );
2015-03-27 19:27:21 -04:00
$errors = [];
2016-07-08 09:55:17 -04:00
if ( $dataDirectory [ 0 ] !== '/' ) {
2015-03-27 19:27:21 -04:00
$errors [] = [
2021-12-15 11:37:00 -05:00
'error' => $l -> t ( 'Your data directory must be an absolute path.' ),
'hint' => $l -> t ( 'Check the value of "datadirectory" in your configuration.' )
2015-03-27 19:27:21 -04:00
];
}
2024-07-11 14:53:37 -04:00
if ( ! file_exists ( $dataDirectory . '/.ncdata' )) {
2015-03-27 19:27:21 -04:00
$errors [] = [
2021-12-15 11:37:00 -05:00
'error' => $l -> t ( 'Your data directory is invalid.' ),
2024-07-11 14:53:37 -04:00
'hint' => $l -> t ( 'Ensure there is a file called "%1$s" in the root of the data directory. It should have the content: "%2$s"' , [ '.ncdata' , '# Nextcloud data directory' ]),
2015-03-27 19:27:21 -04:00
];
2014-03-14 08:03:18 -04:00
}
return $errors ;
}
2011-09-18 15:31:56 -04:00
/**
2013-01-02 17:02:38 -05:00
* Check if the user is logged in , redirects to home if not . With
* redirect URL parameter to the request URI .
2014-08-19 12:01:26 -04:00
*
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0
2013-01-02 17:02:38 -05:00
*/
2025-04-01 10:07:27 -04:00
public static function checkLoggedIn () : void {
2011-09-18 15:31:56 -04:00
// Check if we are a user
2025-11-17 09:32:54 -05:00
if ( ! Server :: get ( IUserSession :: class ) -> isLoggedIn ()) {
header ( 'Location: ' . Server :: get ( IURLGenerator :: class ) -> linkToRoute (
2023-01-20 05:45:08 -05:00
'core.login.showLoginForm' ,
[
2025-11-17 09:32:54 -05:00
'redirect_url' => Server :: get ( IRequest :: class ) -> getRequestUri (),
2023-01-20 05:45:08 -05:00
]
)
2015-02-10 07:02:48 -05:00
);
2011-09-18 15:31:56 -04:00
exit ();
}
2017-04-03 08:29:24 -04:00
// Redirect to 2FA challenge selection if 2FA challenge was not solved yet
2025-11-17 09:32:54 -05:00
if ( Server :: get ( TwoFactorAuthManager :: class ) -> needsSecondFactor ( Server :: get ( IUserSession :: class ) -> getUser ())) {
header ( 'Location: ' . Server :: get ( IURLGenerator :: class ) -> linkToRoute ( 'core.TwoFactorChallenge.selectChallenge' ));
2016-06-10 03:52:52 -04:00
exit ();
}
2011-09-18 15:31:56 -04:00
}
/**
2014-05-19 11:50:53 -04:00
* Check if the user is a admin , redirects to home if not
2014-08-19 12:01:26 -04:00
*
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0
2013-01-02 17:02:38 -05:00
*/
2025-04-01 10:07:27 -04:00
public static function checkAdminUser () : void {
self :: checkLoggedIn ();
2014-08-19 12:01:26 -04:00
if ( ! OC_User :: isAdminUser ( OC_User :: getUser ())) {
2025-11-17 09:32:54 -05:00
header ( 'Location: ' . Util :: linkToAbsolute ( '' , 'index.php' ));
2011-09-18 15:31:56 -04:00
exit ();
}
}
/**
2014-07-01 10:02:38 -04:00
* Returns the URL of the default page
* based on the system configuration and
* the apps visible for the current user
*
* @ return string URL
2025-04-08 11:56:14 -04:00
* @ deprecated 32.0 . 0 use IURLGenerator ' s linkToDefaultPageUrl directly
2013-01-02 17:02:38 -05:00
*/
2014-07-01 10:02:38 -04:00
public static function getDefaultPageUrl () {
2021-07-01 10:00:29 -04:00
/** @var IURLGenerator $urlGenerator */
2025-11-17 09:32:54 -05:00
$urlGenerator = Server :: get ( IURLGenerator :: class );
2021-06-30 10:16:05 -04:00
return $urlGenerator -> linkToDefaultPageUrl ();
2014-07-01 10:02:38 -04:00
}
/**
* Redirect to the user default page
2014-08-19 12:01:26 -04:00
*
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0
2014-07-01 10:02:38 -04:00
*/
2025-04-01 10:07:27 -04:00
public static function redirectToDefaultPage () : void {
2014-07-01 10:02:38 -04:00
$location = self :: getDefaultPageUrl ();
2014-08-19 12:01:26 -04:00
header ( 'Location: ' . $location );
2011-09-18 15:31:56 -04:00
exit ();
}
2012-06-05 13:32:48 -04:00
2013-08-15 02:49:19 -04:00
/**
2014-05-19 11:50:53 -04:00
* get an id unique for this instance
2014-08-19 12:01:26 -04:00
*
2013-08-15 02:49:19 -04:00
* @ return string
*/
2025-04-01 10:07:27 -04:00
public static function getInstanceId () : string {
2025-11-17 09:32:54 -05:00
$id = Server :: get ( SystemConfig :: class ) -> getValue ( 'instanceid' , null );
2014-08-19 12:01:26 -04:00
if ( is_null ( $id )) {
2013-08-15 02:49:19 -04:00
// We need to guarantee at least one letter in instanceid so it can be used as the session_name
2025-11-17 09:32:54 -05:00
$id = 'oc' . Server :: get ( ISecureRandom :: class ) -> generate ( 10 , ISecureRandom :: CHAR_LOWER . ISecureRandom :: CHAR_DIGITS );
Server :: get ( SystemConfig :: class ) -> setValue ( 'instanceid' , $id );
2013-08-15 02:49:19 -04:00
}
return $id ;
}
2012-06-09 09:05:14 -04:00
2012-06-21 08:07:04 -04:00
/**
2014-05-19 11:50:53 -04:00
* Public function to sanitize HTML
2012-06-21 08:07:04 -04:00
*
2012-06-22 02:17:35 -04:00
* This function is used to sanitize HTML and should be applied on any
* string or array of strings before displaying it on a web page .
2012-08-29 02:38:33 -04:00
*
2022-01-26 08:26:58 -05:00
* @ param string | string [] $value
2025-02-27 04:24:59 -05:00
* @ return ( $value is array ? string [] : string )
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0 use \OCP\Util :: sanitizeHTML instead
2012-06-19 11:20:19 -04:00
*/
2015-12-08 02:27:52 -05:00
public static function sanitizeHTML ( $value ) {
2013-02-21 18:51:54 -05:00
if ( is_array ( $value )) {
2020-04-09 07:53:40 -04:00
$value = array_map ( function ( $value ) {
2015-12-08 02:27:52 -05:00
return self :: sanitizeHTML ( $value );
}, $value );
2013-01-14 16:01:52 -05:00
} else {
2015-12-08 02:27:52 -05:00
// Specify encoding for PHP<5.4
2014-04-11 13:42:15 -04:00
$value = htmlspecialchars (( string ) $value , ENT_QUOTES , 'UTF-8' );
2013-01-14 16:01:52 -05:00
}
2012-06-19 11:20:19 -04:00
return $value ;
}
2013-09-27 07:34:48 -04:00
2013-07-04 12:21:49 -04:00
/**
2014-05-19 11:50:53 -04:00
* Public function to encode url parameters
2013-07-04 12:21:49 -04:00
*
* This function is used to encode path to file before output .
* Encoding is done according to RFC 3986 with one exception :
2013-09-27 07:34:48 -04:00
* Character '/' is preserved as is .
2013-07-04 12:21:49 -04:00
*
* @ param string $component part of URI to encode
2013-09-27 07:34:48 -04:00
* @ return string
2025-04-01 10:07:27 -04:00
* @ deprecated 32.0 . 0 use \OCP\Util :: encodePath instead
2013-07-04 12:21:49 -04:00
*/
public static function encodePath ( $component ) {
$encoded = rawurlencode ( $component );
$encoded = str_replace ( '%2F' , '/' , $encoded );
return $encoded ;
}
2012-06-21 08:07:04 -04:00
2021-11-15 10:40:52 -05:00
/**
* Check if current locale is non - UTF8
*
* @ return bool
*/
private static function isNonUTF8Locale () {
2021-11-16 07:05:43 -05:00
if ( function_exists ( 'escapeshellcmd' )) {
2024-03-28 11:13:19 -04:00
return escapeshellcmd ( '§' ) === '' ;
2021-11-16 07:05:43 -05:00
} elseif ( function_exists ( 'escapeshellarg' )) {
2024-03-28 11:13:19 -04:00
return escapeshellarg ( '§' ) === '\'\'' ;
2021-11-15 10:40:52 -05:00
} else {
2024-03-28 11:13:19 -04:00
return preg_match ( '/utf-?8/i' , setlocale ( LC_CTYPE , 0 )) === 0 ;
2021-11-15 10:40:52 -05:00
}
}
2013-01-02 17:02:38 -05:00
/**
2021-11-15 10:46:21 -05:00
* Check if the setlocale call does not work . This can happen if the right
2013-02-11 11:44:02 -05:00
* local packages are not available on the server .
2014-08-19 12:01:26 -04:00
*
2025-04-01 10:07:27 -04:00
* @ internal
2013-01-02 17:02:38 -05:00
*/
2025-04-01 10:07:27 -04:00
public static function isSetLocaleWorking () : bool {
2021-11-15 10:40:52 -05:00
if ( self :: isNonUTF8Locale ()) {
2021-01-07 09:55:49 -05:00
// Borrowed from \Patchwork\Utf8\Bootup::initLocale
setlocale ( LC_ALL , 'C.UTF-8' , 'C' );
setlocale ( LC_CTYPE , 'en_US.UTF-8' , 'fr_FR.UTF-8' , 'es_ES.UTF-8' , 'de_DE.UTF-8' , 'ru_RU.UTF-8' , 'pt_BR.UTF-8' , 'it_IT.UTF-8' , 'ja_JP.UTF-8' , 'zh_CN.UTF-8' , '0' );
2021-11-13 18:25:32 -05:00
// Check again
2021-11-15 10:40:52 -05:00
if ( self :: isNonUTF8Locale ()) {
2021-11-13 18:25:32 -05:00
return false ;
}
2013-04-05 14:49:57 -04:00
}
2021-11-13 18:25:32 -05:00
2013-04-05 14:49:57 -04:00
return true ;
2012-12-19 09:10:33 -05:00
}
2014-03-06 20:53:33 -05:00
/**
* Check if it ' s possible to get the inline annotations
*
2025-04-01 10:07:27 -04:00
* @ internal
2014-03-06 20:53:33 -05:00
*/
2025-04-01 10:07:27 -04:00
public static function isAnnotationsWorking () : bool {
2024-11-08 05:48:55 -05:00
if ( PHP_VERSION_ID >= 80300 ) {
/** @psalm-suppress UndefinedMethod */
$reflection = \ReflectionMethod :: createFromMethodName ( __METHOD__ );
} else {
$reflection = new \ReflectionMethod ( __METHOD__ );
}
2014-03-06 20:53:33 -05:00
$docs = $reflection -> getDocComment ();
return ( is_string ( $docs ) && strlen ( $docs ) > 50 );
}
2013-02-04 09:04:26 -05:00
/**
2014-05-19 11:50:53 -04:00
* Check if the PHP module fileinfo is loaded .
2014-08-19 12:01:26 -04:00
*
2025-04-01 10:07:27 -04:00
* @ internal
2013-02-04 09:04:26 -05:00
*/
2025-04-01 10:07:27 -04:00
public static function fileInfoLoaded () : bool {
2013-02-04 09:04:26 -05:00
return function_exists ( 'finfo_open' );
}
2012-11-29 11:58:24 -05:00
/**
2014-05-19 11:50:53 -04:00
* clear all levels of output buffering
2014-08-19 12:01:26 -04:00
*
2013-08-15 02:49:19 -04:00
* @ return void
2012-11-29 11:58:24 -05:00
*/
2014-08-19 12:01:26 -04:00
public static function obEnd () {
2012-11-29 11:58:24 -05:00
while ( ob_get_level ()) {
ob_end_clean ();
}
}
2013-04-24 07:45:40 -04:00
/**
* Handles the case that there may not be a theme , then check if a " default "
* theme exists and take that one
2014-08-19 12:01:26 -04:00
*
2013-04-24 07:45:40 -04:00
* @ return string the theme
*/
public static function getTheme () {
2025-11-17 09:32:54 -05:00
$theme = Server :: get ( SystemConfig :: class ) -> getValue ( 'theme' , '' );
2013-04-24 07:45:40 -04:00
2014-08-19 12:01:26 -04:00
if ( $theme === '' ) {
if ( is_dir ( OC :: $SERVERROOT . '/themes/default' )) {
2013-04-24 07:45:40 -04:00
$theme = 'default' ;
}
}
return $theme ;
}
2013-05-24 14:35:01 -04:00
/**
* Normalize a unicode string
2014-08-19 12:01:26 -04:00
*
2013-05-24 14:35:01 -04:00
* @ param string $value a not normalized string
2025-02-27 13:07:39 -05:00
* @ return string The normalized string or the input if the normalization failed
2013-05-24 14:35:01 -04:00
*/
2025-02-27 13:07:39 -05:00
public static function normalizeUnicode ( string $value ) : string {
2015-01-09 17:34:26 -05:00
if ( Normalizer :: isNormalized ( $value )) {
return $value ;
}
$normalizedValue = Normalizer :: normalize ( $value );
2025-02-27 13:07:39 -05:00
if ( $normalizedValue === false ) {
2025-11-17 09:32:54 -05:00
Server :: get ( LoggerInterface :: class ) -> warning ( 'normalizing failed for "' . $value . '"' , [ 'app' => 'core' ]);
2015-01-09 17:34:26 -05:00
return $value ;
2013-05-24 14:35:01 -04:00
}
2013-04-24 07:45:40 -04:00
2015-01-09 17:34:26 -05:00
return $normalizedValue ;
2013-05-24 14:35:01 -04:00
}
2013-07-29 12:24:05 -04:00
2014-06-10 12:01:07 -04:00
/**
2025-12-01 12:10:52 -05:00
* Determine whether this Nextcloud Server instance requires an upgrade .
2014-06-10 12:01:07 -04:00
*
2025-12-01 12:10:52 -05:00
* Compares the version stored in config . php ( " installed " version ) with the
* version reported by the running codebase ( " code " version ) . Returns true
* when the codebase is newer , or when any enabled app requires an upgrade .
*
* Notes :
* - " installed " refers to the version recorded in config . php .
* - " code " refers to the version of the running Nextcloud Server code ( version . php ) .
* - Blocks unsupported downgrades ( code older than installed ), but does NOT validate
* whether an upgrade path is supported ( e . g . , skipping major versions like 28 -> 30 ) .
* Callers are expected to check that on their own .
*
2025-11-17 09:32:54 -05:00
* @ param SystemConfig $config System configuration ( reads 'installed' , 'version' , 'debug' ) .
2025-12-01 12:10:52 -05:00
* @ return bool True if a core or app upgrade is required , false otherwise .
2025-11-17 09:32:54 -05:00
* @ throws HintException If a downgrade is detected and not allowed .
2025-12-01 12:10:52 -05:00
* @ deprecated 32.0 . 0 Use \OCP\Util :: needUpgrade () instead .
* @ see \OCP\Util :: needUpgrade
2014-06-10 12:01:07 -04:00
*/
2025-12-01 12:10:52 -05:00
public static function needUpgrade ( \OC\SystemConfig $config ) : bool {
if ( ! $config -> getValue ( 'installed' , false )) {
// not installed (nothing to do)
return false ;
}
$installedVersion = ( string ) $config -> getValue ( 'version' , '0.0.0' );
$codeVersion = implode ( '.' , \OCP\Util :: getVersion ());
// codebase newer: upgrade needed
if ( version_compare ( $codeVersion , $installedVersion , '>' )) {
// upgrade needed
return true ;
}
// codebase older: downgrade attempt
if ( version_compare ( $codeVersion , $installedVersion , '<' )) {
// allow downgrade only in debug and when major.minor match
if ( $config -> getValue ( 'debug' , false )) {
$installedMajorMinor = self :: getMajorMinor ( $installedVersion );
$codeMajorMinor = self :: getMajorMinor ( $codeVersion );
if ( $installedMajorMinor === $codeMajorMinor ) {
2016-01-07 08:13:46 -05:00
return true ;
}
2014-07-24 11:15:38 -04:00
}
2025-12-01 12:10:52 -05:00
// disallow downgrade (not in debug mode or major.minor mismatch)
/** @var \Psr\Log\LoggerInterface $logger */
$logger = \OCP\Server :: get ( LoggerInterface :: class );
$logger -> error (
'Detected downgrade attempt from installed {installed} to code {code}' ,
[ 'installed' => $installedVersion , 'code' => $codeVersion , 'app' => 'core' , ]
);
throw new \OCP\HintException ( sprintf (
2025-12-01 12:26:03 -05:00
'Downgrading Nextcloud from %s to %s is not supported and may corrupt your instance (database and data directory). '
. 'Restore a full backup (code, database, and data directory) taken before the change, '
. 'or restore the previous codebase so that it matches the installed version (version %s).' ,
2025-12-01 12:10:52 -05:00
$installedVersion , $codeVersion , $installedVersion
));
}
// versions are equal: check whether any enabled apps need upgrading
$appManager = \OCP\Server :: get ( \OCP\App\IAppManager :: class );
$apps = $appManager -> getEnabledApps ();
foreach ( $apps as $app ) {
if ( $appManager -> isUpgradeRequired ( $app )) {
return true ;
2014-07-24 11:15:38 -04:00
}
2014-06-10 12:01:07 -04:00
}
2025-12-01 12:10:52 -05:00
return false ;
}
/**
* Helper to return " major.minor " for a version string
*/
private static function getMajorMinor ( string $version ) : string {
$parts = explode ( '.' , $version , 3 );
// we could sanity check/guard/fallback this more but there's only so much we can do...
$major = $parts [ 0 ];
$minor = $parts [ 1 ];
return $major . '.' . $minor ;
2014-06-10 12:01:07 -04:00
}
2011-07-27 13:07:28 -04:00
}