2012-09-07 12:09:41 +00:00
< ? php
/**
2016-07-21 14:49:16 +00:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2015-10-05 18:54:56 +00:00
* @ author Andreas Fischer < bantu @ owncloud . com >
2015-03-26 10:44:34 +00:00
* @ author Bart Visscher < bartv @ thisnet . nl >
2016-05-26 17:56:05 +00:00
* @ author Björn Schießle < bjoern @ schiessle . org >
* @ author Frank Karlitschek < frank @ karlitschek . de >
2016-01-12 14:02:16 +00:00
* @ author Jesús Macias < jmacias @ solidgear . es >
2016-07-21 14:49:16 +00:00
* @ author Joas Schilling < coding @ schilljs . com >
* @ author Juan Pablo Villafáñez < jvillafanez @ solidgear . es >
2016-05-26 17:56:05 +00:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2015-03-26 10:44:34 +00:00
* @ author Michael Gapczynski < GapczynskiM @ gmail . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Philipp Kapfer < philipp . kapfer @ gmx . at >
2016-07-21 16:13:36 +00:00
* @ author Robin Appelman < robin @ icewind . nl >
2016-01-12 14:02:16 +00:00
* @ author Robin McCorkell < robin @ mccorkell . me . uk >
2017-11-06 14:56:42 +00:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2015-03-26 10:44:34 +00:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
* @ author Vincent Petry < pvince81 @ owncloud . com >
2014-12-11 16:35:11 +00:00
*
2015-03-26 10:44:34 +00:00
* @ license AGPL - 3.0
2014-12-11 16:35:11 +00:00
*
2015-03-26 10:44:34 +00: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 16:35:11 +00:00
*
2015-03-26 10:44:34 +00:00
* This program is distributed in the hope that it will be useful ,
2014-12-11 16:35:11 +00:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2015-03-26 10:44:34 +00: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 ,
* along with this program . If not , see < http :// www . gnu . org / licenses />
2014-12-11 16:35:11 +00:00
*
2015-02-26 10:37:37 +00:00
*/
2015-08-03 04:39:53 +00:00
use phpseclib\Crypt\AES ;
2016-05-13 09:56:47 +00:00
use \OCA\Files_External\AppInfo\Application ;
2015-08-12 08:21:09 +00:00
use \OCA\Files_External\Lib\Backend\LegacyBackend ;
use \OCA\Files_External\Lib\StorageConfig ;
2015-11-28 12:17:34 +00:00
use \OCA\Files_External\Lib\Backend\Backend ;
2015-11-26 16:51:47 +00:00
use \OCP\Files\StorageNotAvailableException ;
2018-01-25 22:16:13 +00:00
use OCA\Files_External\Service\BackendService ;
use OCA\Files_External\Lib\Auth\Builtin ;
use OCA\Files_External\Service\UserGlobalStoragesService ;
use OCP\IUserManager ;
use OCA\Files_External\Service\GlobalStoragesService ;
use OCA\Files_External\Service\UserStoragesService ;
2015-08-03 04:39:53 +00:00
2015-02-26 10:37:37 +00:00
/**
* Class to configure mount . json globally and for users
2014-03-18 20:15:11 +00:00
*/
2012-09-07 12:09:41 +00:00
class OC_Mount_Config {
2014-03-19 11:20:48 +00:00
// TODO: make this class non-static and give it a proper namespace
2012-09-07 12:09:41 +00:00
const MOUNT_TYPE_GLOBAL = 'global' ;
const MOUNT_TYPE_GROUP = 'group' ;
const MOUNT_TYPE_USER = 'user' ;
2014-05-09 19:59:56 +00:00
const MOUNT_TYPE_PERSONAL = 'personal' ;
2012-09-07 12:09:41 +00:00
2014-03-18 17:29:08 +00:00
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false ;
2015-08-12 20:43:29 +00:00
/** @var Application */
public static $app ;
2014-04-07 18:18:57 +00:00
2015-08-12 08:21:09 +00:00
/**
* @ param string $class
* @ param array $definition
* @ return bool
* @ deprecated 8.2 . 0 use \OCA\Files_External\Service\BackendService :: registerBackend ()
*/
public static function registerBackend ( $class , $definition ) {
2018-01-25 22:16:13 +00:00
$backendService = self :: $app -> getContainer () -> query ( BackendService :: class );
$auth = self :: $app -> getContainer () -> query ( Builtin :: class );
2015-08-12 08:21:09 +00:00
$backendService -> registerBackend ( new LegacyBackend ( $class , $definition , $auth ));
return true ;
}
2014-03-19 13:23:36 +00:00
/**
* Returns the mount points for the given user .
* The mount point is relative to the data directory .
*
2015-08-12 08:21:09 +00:00
* @ param string $uid user
2014-03-19 13:23:36 +00:00
* @ return array of mount point string as key , mountpoint config as value
2015-08-12 08:21:09 +00:00
*
2015-09-23 14:35:17 +00:00
* @ deprecated 8.2 . 0 use UserGlobalStoragesService :: getStorages () and UserStoragesService :: getStorages ()
2014-03-19 13:23:36 +00:00
*/
2015-08-12 08:21:09 +00:00
public static function getAbsoluteMountPoints ( $uid ) {
2014-03-21 14:22:48 +00:00
$mountPoints = array ();
2014-03-28 14:24:38 +00:00
2018-01-25 22:16:13 +00: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 19:59:56 +00:00
2015-08-12 08:21:09 +00:00
$userGlobalStoragesService -> setUser ( $user );
$userStoragesService -> setUser ( $user );
2015-08-11 17:45:07 +00:00
2015-09-23 14:35:17 +00:00
foreach ( $userGlobalStoragesService -> getStorages () as $storage ) {
2016-05-13 09:56:47 +00:00
/** @var \OCA\Files_External\Lib\StorageConfig $storage */
2015-08-12 08:21:09 +00:00
$mountPoint = '/' . $uid . '/files' . $storage -> getMountPoint ();
$mountEntry = self :: prepareMountPointEntry ( $storage , false );
foreach ( $mountEntry [ 'options' ] as & $option ) {
$option = self :: setUserVars ( $uid , $option );
2014-03-19 11:20:48 +00:00
}
2015-08-12 08:21:09 +00:00
$mountPoints [ $mountPoint ] = $mountEntry ;
2014-03-19 11:20:48 +00:00
}
2014-03-28 15:22:35 +00:00
2015-09-23 14:35:17 +00:00
foreach ( $userStoragesService -> getStorages () as $storage ) {
2015-08-12 08:21:09 +00:00
$mountPoint = '/' . $uid . '/files' . $storage -> getMountPoint ();
$mountEntry = self :: prepareMountPointEntry ( $storage , true );
foreach ( $mountEntry [ 'options' ] as & $option ) {
$option = self :: setUserVars ( $uid , $option );
2014-03-28 15:22:35 +00:00
}
2015-08-12 08:21:09 +00:00
$mountPoints [ $mountPoint ] = $mountEntry ;
2014-03-28 15:22:35 +00:00
}
2014-03-28 14:24:38 +00:00
2015-08-12 08:21:09 +00:00
$userGlobalStoragesService -> resetUser ();
$userStoragesService -> resetUser ();
2014-03-28 14:24:38 +00:00
2015-08-12 08:21:09 +00:00
return $mountPoints ;
}
/**
* Get the system mount points
*
* @ return array
*
2015-09-23 14:35:17 +00:00
* @ deprecated 8.2 . 0 use GlobalStoragesService :: getStorages ()
2015-08-12 08:21:09 +00:00
*/
public static function getSystemMountPoints () {
$mountPoints = [];
2018-01-25 22:16:13 +00:00
$service = self :: $app -> getContainer () -> query ( GlobalStoragesService :: class );
2015-08-12 08:21:09 +00:00
2015-09-23 14:35:17 +00:00
foreach ( $service -> getStorages () as $storage ) {
2015-08-12 08:21:09 +00:00
$mountPoints [] = self :: prepareMountPointEntry ( $storage , false );
2014-03-19 11:20:48 +00:00
}
2015-08-19 14:26:33 +00:00
return $mountPoints ;
2015-08-12 08:21:09 +00:00
}
2014-03-19 11:20:48 +00:00
2015-08-12 08:21:09 +00:00
/**
* Get the personal mount points of the current user
*
* @ return array
*
2015-09-23 14:35:17 +00:00
* @ deprecated 8.2 . 0 use UserStoragesService :: getStorages ()
2015-08-12 08:21:09 +00:00
*/
public static function getPersonalMountPoints () {
$mountPoints = [];
2018-01-25 22:16:13 +00:00
$service = self :: $app -> getContainer () -> query ( UserStoragesService :: class );
2014-03-28 14:24:38 +00:00
2015-09-23 14:35:17 +00:00
foreach ( $service -> getStorages () as $storage ) {
2015-08-12 08:21:09 +00:00
$mountPoints [] = self :: prepareMountPointEntry ( $storage , true );
2014-03-19 11:20:48 +00:00
}
2015-08-19 14:26:33 +00:00
return $mountPoints ;
2015-08-12 08:21:09 +00:00
}
2014-03-19 13:23:36 +00:00
2015-08-12 08:21:09 +00: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 14:04:04 +00: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 08:21:09 +00:00
$mountEntry [ 'id' ] = $storage -> getId ();
return $mountEntry ;
2014-03-19 11:20:48 +00:00
}
/**
* fill in the correct values for $user
*
2015-03-26 18:24:37 +00:00
* @ param string $user user value
* @ param string | array $input
2014-03-19 11:20:48 +00:00
* @ return string
*/
2015-08-12 18:51:09 +00:00
public static function setUserVars ( $user , $input ) {
2015-03-26 18:24:37 +00:00
if ( is_array ( $input )) {
foreach ( $input as & $value ) {
if ( is_string ( $value )) {
$value = str_replace ( '$user' , $user , $value );
}
}
} else {
2015-08-19 23:37:15 +00:00
if ( is_string ( $input )) {
$input = str_replace ( '$user' , $user , $input );
}
2015-03-26 18:24:37 +00:00
}
return $input ;
2014-03-19 11:20:48 +00:00
}
/**
* Test connecting using the given backend configuration
2014-12-11 16:35:11 +00:00
*
2014-03-19 11:20:48 +00:00
* @ param string $class backend class name
* @ param array $options backend configuration options
2015-11-30 10:29:06 +00:00
* @ param boolean $isPersonal
2014-10-31 10:41:07 +00:00
* @ return int see self :: STATUS_ *
2015-11-28 12:17:34 +00:00
* @ throws Exception
2014-03-19 11:20:48 +00:00
*/
2016-06-08 10:48:33 +00:00
public static function getBackendStatus ( $class , $options , $isPersonal , $testOnly = true ) {
2014-03-18 17:29:08 +00:00
if ( self :: $skipTest ) {
2015-11-26 16:51:47 +00:00
return StorageNotAvailableException :: STATUS_SUCCESS ;
2014-03-18 17:29:08 +00:00
}
2012-12-24 18:45:52 +00:00
foreach ( $options as & $option ) {
2014-03-19 11:20:48 +00:00
$option = self :: setUserVars ( OCP\User :: getUser (), $option );
2012-12-24 18:45:52 +00:00
}
if ( class_exists ( $class )) {
try {
2015-11-28 12:17:34 +00:00
/** @var \OC\Files\Storage\Common $storage */
2012-12-28 17:00:48 +00:00
$storage = new $class ( $options );
2015-01-23 21:53:21 +00:00
try {
2016-06-07 16:25:17 +00:00
$result = $storage -> test ( $isPersonal , $testOnly );
2015-01-23 21:53:21 +00:00
$storage -> setAvailability ( $result );
if ( $result ) {
2015-11-26 16:51:47 +00:00
return StorageNotAvailableException :: STATUS_SUCCESS ;
2015-01-23 21:53:21 +00:00
}
} catch ( \Exception $e ) {
$storage -> setAvailability ( false );
throw $e ;
2014-10-31 10:41:07 +00:00
}
2012-12-24 18:45:52 +00:00
} catch ( Exception $exception ) {
2013-12-02 15:55:43 +00:00
\OCP\Util :: logException ( 'files_external' , $exception );
2015-11-06 11:29:24 +00:00
throw $exception ;
2012-12-24 18:45:52 +00:00
}
}
2015-11-26 16:51:47 +00:00
return StorageNotAvailableException :: STATUS_ERROR ;
2012-12-24 18:45:52 +00:00
}
2012-09-07 12:09:41 +00:00
/**
2014-12-11 16:35:11 +00: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 10:41:07 +00:00
public static function readData ( $user = null ) {
2014-04-21 20:41:45 +00:00
if ( isset ( $user )) {
2015-08-18 21:49:29 +00:00
$jsonFile = \OC :: $server -> getUserManager () -> get ( $user ) -> getHome () . '/mount.json' ;
2012-09-07 12:09:41 +00:00
} else {
2015-08-18 21:49:29 +00:00
$config = \OC :: $server -> getConfig ();
$datadir = $config -> getSystemValue ( 'datadirectory' , \OC :: $SERVERROOT . '/data/' );
$jsonFile = $config -> getSystemValue ( 'mount_file' , $datadir . '/mount.json' );
2012-09-07 12:09:41 +00:00
}
2013-02-16 00:50:40 +00:00
if ( is_file ( $jsonFile )) {
$mountPoints = json_decode ( file_get_contents ( $jsonFile ), true );
if ( is_array ( $mountPoints )) {
return $mountPoints ;
}
2012-09-07 12:09:41 +00:00
}
return array ();
}
2012-12-10 20:10:28 +00:00
/**
2015-08-11 17:45:07 +00:00
* Get backend dependency message
* TODO : move into AppFramework along with templates
*
2015-11-28 12:17:34 +00:00
* @ param Backend [] $backends
2015-08-11 17:45:07 +00:00
* @ return string
2012-12-10 20:10:28 +00:00
*/
2015-08-11 17:45:07 +00:00
public static function dependencyMessage ( $backends ) {
2015-08-18 21:49:29 +00:00
$l = \OC :: $server -> getL10N ( 'files_external' );
2015-08-11 17:45:07 +00:00
$message = '' ;
$dependencyGroups = [];
foreach ( $backends as $backend ) {
foreach ( $backend -> checkDependencies () as $dependency ) {
if ( $message = $dependency -> getMessage ()) {
2016-08-09 23:01:39 +00:00
$message .= '<p>' . $message . '</p>' ;
2013-08-02 13:44:56 +00:00
} else {
2015-08-11 17:45:07 +00:00
$dependencyGroups [ $dependency -> getDependency ()][] = $backend ;
2013-05-30 15:37:47 +00:00
}
}
2015-08-11 17:45:07 +00:00
}
2013-05-30 15:37:47 +00:00
2015-08-11 17:45:07 +00:00
foreach ( $dependencyGroups as $module => $dependants ) {
$backends = implode ( ', ' , array_map ( function ( $backend ) {
2016-08-09 23:01:39 +00:00
return '"' . $backend -> getText () . '"' ;
2015-08-11 17:45:07 +00:00
}, $dependants ));
2016-08-09 23:01:39 +00:00
$message .= '<p>' . OC_Mount_Config :: getSingleDependencyMessage ( $l , $module , $backends ) . '</p>' ;
2013-05-30 15:37:47 +00:00
}
2015-08-11 17:45:07 +00:00
return $message ;
2013-08-02 13:44:56 +00:00
}
2013-05-30 15:37:47 +00:00
2013-08-02 13:44:56 +00:00
/**
* Returns a dependency missing message
2014-12-11 16:35:11 +00:00
*
2015-08-18 21:49:29 +00:00
* @ param \OCP\IL10N $l
2014-05-13 11:29:25 +00:00
* @ param string $module
* @ param string $backend
2015-11-30 22:00:51 +00:00
* @ return string
2013-08-02 13:44:56 +00:00
*/
2015-08-18 21:49:29 +00:00
private static function getSingleDependencyMessage ( \OCP\IL10N $l , $module , $backend ) {
2013-08-02 13:44:56 +00:00
switch ( strtolower ( $module )) {
case 'curl' :
2017-07-24 09:36:20 +00: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 13:44:56 +00:00
case 'ftp' :
2017-07-24 09:36:20 +00: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 13:44:56 +00:00
default :
2017-07-24 09:36:20 +00:00
return ( string ) $l -> t ( '"%s" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it.' , [ $module , $backend ]);
2013-08-02 13:44:56 +00:00
}
2012-12-10 20:10:28 +00:00
}
2014-03-18 20:15:11 +00:00
/**
* Encrypt passwords in the given config options
2014-12-11 16:35:11 +00:00
*
2014-03-18 20:15:11 +00:00
* @ param array $options mount options
* @ return array updated options
*/
2014-10-31 10:41:07 +00:00
public static function encryptPasswords ( $options ) {
2014-03-18 20:15:11 +00:00
if ( isset ( $options [ 'password' ])) {
2014-03-19 10:42:22 +00:00
$options [ 'password_encrypted' ] = self :: encryptPassword ( $options [ 'password' ]);
2014-03-19 16:56:36 +00: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-18 20:15:11 +00:00
}
return $options ;
}
/**
* Decrypt passwords in the given config options
2014-12-11 16:35:11 +00:00
*
2014-03-18 20:15:11 +00:00
* @ param array $options mount options
* @ return array updated options
*/
2014-10-31 10:41:07 +00:00
public static function decryptPasswords ( $options ) {
2014-03-18 20:15:11 +00:00
// note: legacy options might still have the unencrypted password in the "password" field
if ( isset ( $options [ 'password_encrypted' ])) {
2014-03-19 10:42:22 +00:00
$options [ 'password' ] = self :: decryptPassword ( $options [ 'password_encrypted' ]);
2014-03-18 20:15:11 +00:00
unset ( $options [ 'password_encrypted' ]);
}
return $options ;
}
2014-03-19 10:42:22 +00:00
/**
* Encrypt a single password
2014-12-11 16:35:11 +00:00
*
2014-03-19 10:42:22 +00:00
* @ param string $password plain text password
2014-05-13 11:29:25 +00:00
* @ return string encrypted password
2014-03-19 10:42:22 +00:00
*/
private static function encryptPassword ( $password ) {
$cipher = self :: getCipher ();
2018-01-13 18:41:34 +00:00
$iv = \OC :: $server -> getSecureRandom () -> generate ( 16 );
2014-03-19 10:42:22 +00:00
$cipher -> setIV ( $iv );
return base64_encode ( $iv . $cipher -> encrypt ( $password ));
}
/**
* Decrypts a single password
2014-12-11 16:35:11 +00:00
*
2014-03-19 10:42:22 +00:00
* @ param string $encryptedPassword encrypted password
2014-05-13 11:29:25 +00:00
* @ return string plain text password
2014-03-19 10:42:22 +00: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-18 20:15:11 +00:00
/**
* Returns the encryption cipher
2015-12-09 06:53:09 +00:00
*
* @ return AES
2014-03-18 20:15:11 +00:00
*/
private static function getCipher () {
2015-08-03 04:39:53 +00:00
$cipher = new AES ( AES :: MODE_CBC );
2014-12-17 10:12:37 +00:00
$cipher -> setKey ( \OC :: $server -> getConfig () -> getSystemValue ( 'passwordsalt' , null ));
2014-03-19 10:42:22 +00:00
return $cipher ;
2014-03-18 20:15:11 +00:00
}
2014-03-26 11:10:17 +00:00
/**
* Computes a hash based on the given configuration .
* This is mostly used to find out whether configurations
* are the same .
2015-12-09 06:53:09 +00:00
*
* @ param array $config
* @ return string
2014-03-26 11:10:17 +00:00
*/
2015-03-16 11:18:01 +00:00
public static function makeConfigHash ( $config ) {
2014-03-26 11:10:17 +00:00
$data = json_encode (
array (
2015-08-12 19:03:11 +00:00
'c' => $config [ 'backend' ],
'a' => $config [ 'authMechanism' ],
2014-03-26 11:10:17 +00:00
'm' => $config [ 'mountpoint' ],
2015-03-16 11:18:01 +00:00
'o' => $config [ 'options' ],
'p' => isset ( $config [ 'priority' ]) ? $config [ 'priority' ] : - 1 ,
'mo' => isset ( $config [ 'mountOptions' ]) ? $config [ 'mountOptions' ] : [],
2014-03-26 11:10:17 +00:00
)
);
return hash ( 'md5' , $data );
}
2012-09-07 12:09:41 +00:00
}