2012-09-07 16:09:41 +04:00
< ? php
/**
2016-07-21 17:49:16 +03:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2015-10-05 21:54:56 +03:00
* @ author Andreas Fischer < bantu @ owncloud . com >
2019-02-16 00:41:29 +03:00
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
2015-03-26 13:44:34 +03:00
* @ author Bart Visscher < bartv @ thisnet . nl >
2016-05-26 20:56:05 +03:00
* @ author Björn Schießle < bjoern @ schiessle . org >
2020-03-31 11:49:10 +03:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2016-05-26 20:56:05 +03:00
* @ author Frank Karlitschek < frank @ karlitschek . de >
2016-01-12 17:02:16 +03:00
* @ author Jesús Macias < jmacias @ solidgear . es >
2016-07-21 17:49:16 +03:00
* @ author Joas Schilling < coding @ schilljs . com >
* @ author Juan Pablo Villafáñez < jvillafanez @ solidgear . es >
2019-12-03 21:57:53 +03:00
* @ author Julius Härtl < jus @ bitgrid . net >
2016-05-26 20:56:05 +03:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2015-03-26 13:44:34 +03:00
* @ author Michael Gapczynski < GapczynskiM @ gmail . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Philipp Kapfer < philipp . kapfer @ gmx . at >
2016-07-21 19:13:36 +03:00
* @ author Robin Appelman < robin @ icewind . nl >
2016-01-12 17:02:16 +03:00
* @ author Robin McCorkell < robin @ mccorkell . me . uk >
2017-11-06 17:56:42 +03:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2015-03-26 13:44:34 +03:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
2020-12-16 16:54:15 +03:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2014-12-11 19:35:11 +03:00
*
2015-03-26 13:44:34 +03:00
* @ license AGPL - 3.0
2014-12-11 19:35:11 +03:00
*
2015-03-26 13:44:34 +03:00
* This code 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 .
2014-12-11 19:35:11 +03:00
*
2015-03-26 13:44:34 +03:00
* This program is distributed in the hope that it will be useful ,
2014-12-11 19:35:11 +03:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2015-03-26 13:44:34 +03:00
* 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 , version 3 ,
2019-12-03 21:57:53 +03:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2014-12-11 19:35:11 +03:00
*
2015-02-26 13:37:37 +03:00
*/
2020-07-10 00:39:58 +03:00
namespace OCA\Files_External ;
2019-11-22 22:52:10 +03:00
use OCA\Files_External\AppInfo\Application ;
2019-02-12 01:18:08 +03:00
use OCA\Files_External\Config\IConfigHandler ;
2019-11-22 22:52:10 +03:00
use OCA\Files_External\Config\UserContext ;
use OCA\Files_External\Lib\Backend\Backend ;
use OCA\Files_External\Lib\StorageConfig ;
use OCA\Files_External\Service\BackendService ;
2018-01-26 01:16:13 +03:00
use OCA\Files_External\Service\GlobalStoragesService ;
2019-11-22 22:52:10 +03:00
use OCA\Files_External\Service\UserGlobalStoragesService ;
2018-01-26 01:16:13 +03:00
use OCA\Files_External\Service\UserStoragesService ;
2019-11-22 22:52:10 +03:00
use OCP\Files\StorageNotAvailableException ;
use OCP\IUserManager ;
use phpseclib\Crypt\AES ;
2015-08-03 07:39:53 +03:00
2015-02-26 13:37:37 +03:00
/**
* Class to configure mount . json globally and for users
2014-03-19 00:15:11 +04:00
*/
2020-07-10 00:39:58 +03:00
class MountConfig {
2014-03-19 15:20:48 +04:00
// TODO: make this class non-static and give it a proper namespace
2012-09-07 16:09:41 +04:00
2020-04-10 17:54:27 +03:00
public const MOUNT_TYPE_GLOBAL = 'global' ;
public const MOUNT_TYPE_GROUP = 'group' ;
public const MOUNT_TYPE_USER = 'user' ;
public const MOUNT_TYPE_PERSONAL = 'personal' ;
2012-09-07 16:09:41 +04:00
2014-03-18 21:29:08 +04:00
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false ;
2015-08-12 23:43:29 +03:00
/** @var Application */
public static $app ;
2014-04-07 22:18:57 +04:00
2014-03-19 17:23:36 +04:00
/**
* Returns the mount points for the given user .
* The mount point is relative to the data directory .
*
2015-08-12 11:21:09 +03:00
* @ param string $uid user
2014-03-19 17:23:36 +04:00
* @ return array of mount point string as key , mountpoint config as value
2015-08-12 11:21:09 +03:00
*
2015-09-23 17:35:17 +03:00
* @ deprecated 8.2 . 0 use UserGlobalStoragesService :: getStorages () and UserStoragesService :: getStorages ()
2014-03-19 17:23:36 +04:00
*/
2015-08-12 11:21:09 +03:00
public static function getAbsoluteMountPoints ( $uid ) {
2020-03-26 11:30:18 +03:00
$mountPoints = [];
2014-03-28 18:24:38 +04:00
2018-01-26 01:16:13 +03:00
$userGlobalStoragesService = self :: $app -> getContainer () -> query ( UserGlobalStoragesService :: class );
$userStoragesService = self :: $app -> getContainer () -> query ( UserStoragesService :: class );
$user = self :: $app -> getContainer () -> query ( IUserManager :: class ) -> get ( $uid );
2014-05-09 23:59:56 +04:00
2015-08-12 11:21:09 +03:00
$userGlobalStoragesService -> setUser ( $user );
$userStoragesService -> setUser ( $user );
2015-08-11 20:45:07 +03:00
2015-09-23 17:35:17 +03:00
foreach ( $userGlobalStoragesService -> getStorages () as $storage ) {
2016-05-13 12:56:47 +03:00
/** @var \OCA\Files_External\Lib\StorageConfig $storage */
2015-08-12 11:21:09 +03:00
$mountPoint = '/' . $uid . '/files' . $storage -> getMountPoint ();
$mountEntry = self :: prepareMountPointEntry ( $storage , false );
foreach ( $mountEntry [ 'options' ] as & $option ) {
2019-07-25 10:31:39 +03:00
$option = self :: substitutePlaceholdersInConfig ( $option , $uid );
2014-03-19 15:20:48 +04:00
}
2015-08-12 11:21:09 +03:00
$mountPoints [ $mountPoint ] = $mountEntry ;
2014-03-19 15:20:48 +04:00
}
2014-03-28 19:22:35 +04:00
2015-09-23 17:35:17 +03:00
foreach ( $userStoragesService -> getStorages () as $storage ) {
2015-08-12 11:21:09 +03:00
$mountPoint = '/' . $uid . '/files' . $storage -> getMountPoint ();
$mountEntry = self :: prepareMountPointEntry ( $storage , true );
foreach ( $mountEntry [ 'options' ] as & $option ) {
2019-07-25 10:31:39 +03:00
$option = self :: substitutePlaceholdersInConfig ( $option , $uid );
2014-03-28 19:22:35 +04:00
}
2015-08-12 11:21:09 +03:00
$mountPoints [ $mountPoint ] = $mountEntry ;
2014-03-28 19:22:35 +04:00
}
2014-03-28 18:24:38 +04:00
2015-08-12 11:21:09 +03:00
$userGlobalStoragesService -> resetUser ();
$userStoragesService -> resetUser ();
2014-03-28 18:24:38 +04:00
2015-08-12 11:21:09 +03:00
return $mountPoints ;
}
/**
* Get the system mount points
*
* @ return array
*
2015-09-23 17:35:17 +03:00
* @ deprecated 8.2 . 0 use GlobalStoragesService :: getStorages ()
2015-08-12 11:21:09 +03:00
*/
public static function getSystemMountPoints () {
$mountPoints = [];
2018-01-26 01:16:13 +03:00
$service = self :: $app -> getContainer () -> query ( GlobalStoragesService :: class );
2015-08-12 11:21:09 +03:00
2015-09-23 17:35:17 +03:00
foreach ( $service -> getStorages () as $storage ) {
2015-08-12 11:21:09 +03:00
$mountPoints [] = self :: prepareMountPointEntry ( $storage , false );
2014-03-19 15:20:48 +04:00
}
2015-08-19 17:26:33 +03:00
return $mountPoints ;
2015-08-12 11:21:09 +03:00
}
2014-03-19 15:20:48 +04:00
2015-08-12 11:21:09 +03:00
/**
* Convert a StorageConfig to the legacy mountPoints array format
* There ' s a lot of extra information in here , to satisfy all of the legacy functions
*
* @ param StorageConfig $storage
* @ param bool $isPersonal
* @ return array
*/
private static function prepareMountPointEntry ( StorageConfig $storage , $isPersonal ) {
$mountEntry = [];
$mountEntry [ 'mountpoint' ] = substr ( $storage -> getMountPoint (), 1 ); // remove leading slash
$mountEntry [ 'class' ] = $storage -> getBackend () -> getIdentifier ();
$mountEntry [ 'backend' ] = $storage -> getBackend () -> getText ();
$mountEntry [ 'authMechanism' ] = $storage -> getAuthMechanism () -> getIdentifier ();
$mountEntry [ 'personal' ] = $isPersonal ;
$mountEntry [ 'options' ] = self :: decryptPasswords ( $storage -> getBackendOptions ());
$mountEntry [ 'mountOptions' ] = $storage -> getMountOptions ();
$mountEntry [ 'priority' ] = $storage -> getPriority ();
$mountEntry [ 'applicable' ] = [
'groups' => $storage -> getApplicableGroups (),
'users' => $storage -> getApplicableUsers (),
];
2015-10-02 17:04:04 +03:00
// if mountpoint is applicable to all users the old API expects ['all']
if ( empty ( $mountEntry [ 'applicable' ][ 'groups' ]) && empty ( $mountEntry [ 'applicable' ][ 'users' ])) {
$mountEntry [ 'applicable' ][ 'users' ] = [ 'all' ];
}
2015-08-12 11:21:09 +03:00
$mountEntry [ 'id' ] = $storage -> getId ();
return $mountEntry ;
2014-03-19 15:20:48 +04:00
}
2019-02-12 01:18:08 +03:00
/**
* @ param mixed $input
2019-07-25 18:57:22 +03:00
* @ param string | null $userId
2019-02-12 01:18:08 +03:00
* @ return mixed
* @ throws \OCP\AppFramework\QueryException
* @ since 16.0 . 0
*/
2019-07-25 18:57:22 +03:00
public static function substitutePlaceholdersInConfig ( $input , string $userId = null ) {
2019-02-12 01:18:08 +03:00
/** @var BackendService $backendService */
$backendService = self :: $app -> getContainer () -> query ( BackendService :: class );
/** @var IConfigHandler[] $handlers */
$handlers = $backendService -> getConfigHandlers ();
foreach ( $handlers as $handler ) {
2019-07-25 19:50:28 +03:00
if ( $handler instanceof UserContext && $userId !== null ) {
$handler -> setUserId ( $userId );
2019-07-25 10:31:39 +03:00
}
2019-02-12 01:18:08 +03:00
$input = $handler -> handle ( $input );
2015-03-26 21:24:37 +03:00
}
return $input ;
2014-03-19 15:20:48 +04:00
}
/**
* Test connecting using the given backend configuration
2014-12-11 19:35:11 +03:00
*
2014-03-19 15:20:48 +04:00
* @ param string $class backend class name
* @ param array $options backend configuration options
2015-11-30 13:29:06 +03:00
* @ param boolean $isPersonal
2014-10-31 13:41:07 +03:00
* @ return int see self :: STATUS_ *
2015-11-28 15:17:34 +03:00
* @ throws Exception
2014-03-19 15:20:48 +04:00
*/
2016-06-08 13:48:33 +03:00
public static function getBackendStatus ( $class , $options , $isPersonal , $testOnly = true ) {
2014-03-18 21:29:08 +04:00
if ( self :: $skipTest ) {
2015-11-26 19:51:47 +03:00
return StorageNotAvailableException :: STATUS_SUCCESS ;
2014-03-18 21:29:08 +04:00
}
2019-04-03 00:08:34 +03:00
foreach ( $options as $key => & $option ) {
2020-04-10 15:19:56 +03:00
if ( $key === 'password' ) {
2019-04-03 00:08:34 +03:00
// no replacements in passwords
continue ;
}
2019-02-12 01:18:08 +03:00
$option = self :: substitutePlaceholdersInConfig ( $option );
2012-12-24 22:45:52 +04:00
}
if ( class_exists ( $class )) {
try {
2015-11-28 15:17:34 +03:00
/** @var \OC\Files\Storage\Common $storage */
2012-12-28 21:00:48 +04:00
$storage = new $class ( $options );
2015-01-24 00:53:21 +03:00
try {
2016-06-07 19:25:17 +03:00
$result = $storage -> test ( $isPersonal , $testOnly );
2015-01-24 00:53:21 +03:00
$storage -> setAvailability ( $result );
if ( $result ) {
2015-11-26 19:51:47 +03:00
return StorageNotAvailableException :: STATUS_SUCCESS ;
2015-01-24 00:53:21 +03:00
}
} catch ( \Exception $e ) {
$storage -> setAvailability ( false );
throw $e ;
2014-10-31 13:41:07 +03:00
}
2012-12-24 22:45:52 +04:00
} catch ( Exception $exception ) {
2018-03-12 20:10:59 +03:00
\OC :: $server -> getLogger () -> logException ( $exception , [ 'app' => 'files_external' ]);
2015-11-06 14:29:24 +03:00
throw $exception ;
2012-12-24 22:45:52 +04:00
}
}
2015-11-26 19:51:47 +03:00
return StorageNotAvailableException :: STATUS_ERROR ;
2012-12-24 22:45:52 +04:00
}
2012-09-07 16:09:41 +04:00
/**
2014-12-11 19:35:11 +03:00
* Read the mount points in the config file into an array
*
* @ param string | null $user If not null , personal for $user , otherwise system
* @ return array
*/
2014-10-31 13:41:07 +03:00
public static function readData ( $user = null ) {
2014-04-22 00:41:45 +04:00
if ( isset ( $user )) {
2015-08-19 00:49:29 +03:00
$jsonFile = \OC :: $server -> getUserManager () -> get ( $user ) -> getHome () . '/mount.json' ;
2012-09-07 16:09:41 +04:00
} else {
2015-08-19 00:49:29 +03:00
$config = \OC :: $server -> getConfig ();
$datadir = $config -> getSystemValue ( 'datadirectory' , \OC :: $SERVERROOT . '/data/' );
$jsonFile = $config -> getSystemValue ( 'mount_file' , $datadir . '/mount.json' );
2012-09-07 16:09:41 +04:00
}
2013-02-16 04:50:40 +04:00
if ( is_file ( $jsonFile )) {
$mountPoints = json_decode ( file_get_contents ( $jsonFile ), true );
if ( is_array ( $mountPoints )) {
return $mountPoints ;
}
2012-09-07 16:09:41 +04:00
}
2020-03-26 11:30:18 +03:00
return [];
2012-09-07 16:09:41 +04:00
}
2012-12-11 00:10:28 +04:00
/**
2015-08-11 20:45:07 +03:00
* Get backend dependency message
* TODO : move into AppFramework along with templates
*
2015-11-28 15:17:34 +03:00
* @ param Backend [] $backends
2015-08-11 20:45:07 +03:00
* @ return string
2012-12-11 00:10:28 +04:00
*/
2015-08-11 20:45:07 +03:00
public static function dependencyMessage ( $backends ) {
2015-08-19 00:49:29 +03:00
$l = \OC :: $server -> getL10N ( 'files_external' );
2015-08-11 20:45:07 +03:00
$message = '' ;
$dependencyGroups = [];
foreach ( $backends as $backend ) {
foreach ( $backend -> checkDependencies () as $dependency ) {
if ( $message = $dependency -> getMessage ()) {
2016-08-10 02:01:39 +03:00
$message .= '<p>' . $message . '</p>' ;
2013-08-02 17:44:56 +04:00
} else {
2015-08-11 20:45:07 +03:00
$dependencyGroups [ $dependency -> getDependency ()][] = $backend ;
2013-05-30 19:37:47 +04:00
}
}
2015-08-11 20:45:07 +03:00
}
2013-05-30 19:37:47 +04:00
2015-08-11 20:45:07 +03:00
foreach ( $dependencyGroups as $module => $dependants ) {
2020-04-09 14:53:40 +03:00
$backends = implode ( ', ' , array_map ( function ( $backend ) {
2016-08-10 02:01:39 +03:00
return '"' . $backend -> getText () . '"' ;
2015-08-11 20:45:07 +03:00
}, $dependants ));
2020-07-10 00:39:58 +03:00
$message .= '<p>' . MountConfig :: getSingleDependencyMessage ( $l , $module , $backends ) . '</p>' ;
2013-05-30 19:37:47 +04:00
}
2015-08-11 20:45:07 +03:00
return $message ;
2013-08-02 17:44:56 +04:00
}
2013-05-30 19:37:47 +04:00
2013-08-02 17:44:56 +04:00
/**
* Returns a dependency missing message
2014-12-11 19:35:11 +03:00
*
2015-08-19 00:49:29 +03:00
* @ param \OCP\IL10N $l
2014-05-13 15:29:25 +04:00
* @ param string $module
* @ param string $backend
2015-12-01 01:00:51 +03:00
* @ return string
2013-08-02 17:44:56 +04:00
*/
2015-08-19 00:49:29 +03:00
private static function getSingleDependencyMessage ( \OCP\IL10N $l , $module , $backend ) {
2013-08-02 17:44:56 +04:00
switch ( strtolower ( $module )) {
case 'curl' :
2017-07-24 12:36:20 +03:00
return ( string ) $l -> t ( 'The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.' , [ $backend ]);
2013-08-02 17:44:56 +04:00
case 'ftp' :
2017-07-24 12:36:20 +03:00
return ( string ) $l -> t ( 'The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.' , [ $backend ]);
2013-08-02 17:44:56 +04:00
default :
2018-10-09 15:32:14 +03:00
return ( string ) $l -> t ( '"%1$s" is not installed. Mounting of %2$s is not possible. Please ask your system administrator to install it.' , [ $module , $backend ]);
2013-08-02 17:44:56 +04:00
}
2012-12-11 00:10:28 +04:00
}
2014-03-19 00:15:11 +04:00
/**
* Encrypt passwords in the given config options
2014-12-11 19:35:11 +03:00
*
2014-03-19 00:15:11 +04:00
* @ param array $options mount options
* @ return array updated options
*/
2014-10-31 13:41:07 +03:00
public static function encryptPasswords ( $options ) {
2014-03-19 00:15:11 +04:00
if ( isset ( $options [ 'password' ])) {
2014-03-19 14:42:22 +04:00
$options [ 'password_encrypted' ] = self :: encryptPassword ( $options [ 'password' ]);
2014-03-19 20:56:36 +04:00
// do not unset the password, we want to keep the keys order
// on load... because that's how the UI currently works
$options [ 'password' ] = '' ;
2014-03-19 00:15:11 +04:00
}
return $options ;
}
/**
* Decrypt passwords in the given config options
2014-12-11 19:35:11 +03:00
*
2014-03-19 00:15:11 +04:00
* @ param array $options mount options
* @ return array updated options
*/
2014-10-31 13:41:07 +03:00
public static function decryptPasswords ( $options ) {
2014-03-19 00:15:11 +04:00
// note: legacy options might still have the unencrypted password in the "password" field
if ( isset ( $options [ 'password_encrypted' ])) {
2014-03-19 14:42:22 +04:00
$options [ 'password' ] = self :: decryptPassword ( $options [ 'password_encrypted' ]);
2014-03-19 00:15:11 +04:00
unset ( $options [ 'password_encrypted' ]);
}
return $options ;
}
2014-03-19 14:42:22 +04:00
/**
* Encrypt a single password
2014-12-11 19:35:11 +03:00
*
2014-03-19 14:42:22 +04:00
* @ param string $password plain text password
2014-05-13 15:29:25 +04:00
* @ return string encrypted password
2014-03-19 14:42:22 +04:00
*/
private static function encryptPassword ( $password ) {
$cipher = self :: getCipher ();
2018-01-13 21:41:34 +03:00
$iv = \OC :: $server -> getSecureRandom () -> generate ( 16 );
2014-03-19 14:42:22 +04:00
$cipher -> setIV ( $iv );
return base64_encode ( $iv . $cipher -> encrypt ( $password ));
}
/**
* Decrypts a single password
2014-12-11 19:35:11 +03:00
*
2014-03-19 14:42:22 +04:00
* @ param string $encryptedPassword encrypted password
2014-05-13 15:29:25 +04:00
* @ return string plain text password
2014-03-19 14:42:22 +04:00
*/
private static function decryptPassword ( $encryptedPassword ) {
$cipher = self :: getCipher ();
$binaryPassword = base64_decode ( $encryptedPassword );
$iv = substr ( $binaryPassword , 0 , 16 );
$cipher -> setIV ( $iv );
$binaryPassword = substr ( $binaryPassword , 16 );
return $cipher -> decrypt ( $binaryPassword );
}
2014-03-19 00:15:11 +04:00
/**
* Returns the encryption cipher
2015-12-09 09:53:09 +03:00
*
* @ return AES
2014-03-19 00:15:11 +04:00
*/
private static function getCipher () {
2015-08-03 07:39:53 +03:00
$cipher = new AES ( AES :: MODE_CBC );
2014-12-17 13:12:37 +03:00
$cipher -> setKey ( \OC :: $server -> getConfig () -> getSystemValue ( 'passwordsalt' , null ));
2014-03-19 14:42:22 +04:00
return $cipher ;
2014-03-19 00:15:11 +04:00
}
2014-03-26 15:10:17 +04:00
/**
* Computes a hash based on the given configuration .
* This is mostly used to find out whether configurations
* are the same .
2015-12-09 09:53:09 +03:00
*
* @ param array $config
* @ return string
2014-03-26 15:10:17 +04:00
*/
2015-03-16 14:18:01 +03:00
public static function makeConfigHash ( $config ) {
2014-03-26 15:10:17 +04:00
$data = json_encode (
2020-03-26 11:30:18 +03:00
[
2015-08-12 22:03:11 +03:00
'c' => $config [ 'backend' ],
'a' => $config [ 'authMechanism' ],
2014-03-26 15:10:17 +04:00
'm' => $config [ 'mountpoint' ],
2015-03-16 14:18:01 +03:00
'o' => $config [ 'options' ],
'p' => isset ( $config [ 'priority' ]) ? $config [ 'priority' ] : - 1 ,
'mo' => isset ( $config [ 'mountOptions' ]) ? $config [ 'mountOptions' ] : [],
2020-03-26 11:30:18 +03:00
]
2014-03-26 15:10:17 +04:00
);
return hash ( 'md5' , $data );
}
2012-09-07 16:09:41 +04:00
}