2012-03-23 13:54:38 -04:00
< ? php
/**
2016-07-21 11:07:57 -04:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2019-12-03 13:57:53 -05:00
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
2015-03-26 06:44:34 -04:00
* @ author Bart Visscher < bartv @ thisnet . nl >
2016-05-26 13:56:05 -04:00
* @ author Björn Schießle < bjoern @ schiessle . org >
2015-03-26 06:44:34 -04:00
* @ author Carlos Cerrillo < ccerrillo @ gmail . com >
2020-03-31 04:49:10 -04:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2020-08-24 08:54:25 -04:00
* @ author Daniel Kesselberg < mail @ danielkesselberg . de >
2019-12-03 13:57:53 -05:00
* @ author Joas Schilling < coding @ schilljs . com >
2015-03-26 06:44:34 -04:00
* @ author Jörn Friedrich Dreyer < jfd @ butonic . de >
2016-05-26 13:56:05 -04:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2015-03-26 06:44:34 -04:00
* @ author Michael Gapczynski < GapczynskiM @ gmail . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Philipp Kapfer < philipp . kapfer @ gmx . at >
2016-07-21 12:13:36 -04:00
* @ author Robin Appelman < robin @ icewind . nl >
2019-12-03 13:57:53 -05:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2015-03-26 06:44:34 -04:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
2020-12-30 08:07:05 -05:00
* @ author Tigran Mkrtchyan < tigran . mkrtchyan @ desy . de >
2020-12-16 08:54:15 -05:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2015-03-26 06:44:34 -04:00
*
* @ license AGPL - 3.0
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License , version 3 ,
2019-12-03 13:57:53 -05:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2015-03-26 06:44:34 -04:00
*
2012-03-23 13:54:38 -04:00
*/
2012-09-07 12:30:48 -04:00
namespace OC\Files\Storage ;
2015-04-13 03:26:30 -04:00
use Exception ;
2017-01-03 11:26:44 -05:00
use Icewind\Streams\CallbackWrapper ;
2015-03-10 11:30:13 -04:00
use Icewind\Streams\IteratorDirectory ;
2019-11-22 14:52:10 -05:00
use OC\Files\Filesystem ;
2015-04-13 03:26:30 -04:00
use OC\MemCache\ArrayCache ;
2015-08-29 08:53:59 -04:00
use OCP\AppFramework\Http ;
2015-04-13 03:26:30 -04:00
use OCP\Constants ;
2023-04-21 12:17:28 -04:00
use OCP\Diagnostics\IEventLogger ;
2015-04-13 03:26:30 -04:00
use OCP\Files\FileInfo ;
2019-11-22 14:52:10 -05:00
use OCP\Files\ForbiddenException ;
2023-06-22 08:33:18 -04:00
use OCP\Files\IMimeTypeDetector ;
2015-01-20 13:45:32 -05:00
use OCP\Files\StorageInvalidException ;
2014-06-30 09:46:37 -04:00
use OCP\Files\StorageNotAvailableException ;
2019-11-28 07:56:35 -05:00
use OCP\Http\Client\IClientService ;
use OCP\ICertificateManager ;
2023-06-12 13:40:39 -04:00
use OCP\IConfig ;
2023-05-11 06:46:16 -04:00
use OCP\Util ;
2019-11-22 14:52:10 -05:00
use Psr\Http\Message\ResponseInterface ;
2023-11-23 04:22:34 -05:00
use Psr\Log\LoggerInterface ;
2015-04-13 03:26:30 -04:00
use Sabre\DAV\Client ;
2015-11-20 07:35:23 -05:00
use Sabre\DAV\Xml\Property\ResourceType ;
2015-04-10 05:55:58 -04:00
use Sabre\HTTP\ClientException ;
2015-03-09 06:48:55 -04:00
use Sabre\HTTP\ClientHttpException ;
2023-04-21 12:17:28 -04:00
use Sabre\HTTP\RequestInterface ;
2014-06-30 09:46:37 -04:00
2015-03-09 06:48:55 -04:00
/**
* Class DAV
*
* @ package OC\Files\Storage
*/
2015-04-13 03:26:30 -04:00
class DAV extends Common {
/** @var string */
2014-04-29 09:14:48 -04:00
protected $password ;
2015-04-13 03:26:30 -04:00
/** @var string */
2014-04-29 09:14:48 -04:00
protected $user ;
2021-03-04 08:49:58 -05:00
/** @var string|null */
2017-02-22 15:47:49 -05:00
protected $authType ;
/** @var string */
2014-04-29 09:14:48 -04:00
protected $host ;
2015-04-13 03:26:30 -04:00
/** @var bool */
2014-04-29 09:14:48 -04:00
protected $secure ;
2015-04-13 03:26:30 -04:00
/** @var string */
2014-04-29 09:14:48 -04:00
protected $root ;
2015-04-13 03:26:30 -04:00
/** @var string */
2014-04-29 09:14:48 -04:00
protected $certPath ;
2015-04-13 03:26:30 -04:00
/** @var bool */
2014-04-29 09:14:48 -04:00
protected $ready ;
2015-04-13 03:26:30 -04:00
/** @var Client */
2017-02-15 08:00:00 -05:00
protected $client ;
2015-04-13 03:26:30 -04:00
/** @var ArrayCache */
2017-02-15 08:00:00 -05:00
protected $statCache ;
2019-11-28 07:56:35 -05:00
/** @var IClientService */
2017-02-15 08:00:00 -05:00
protected $httpClientService ;
2019-11-28 07:56:35 -05:00
/** @var ICertificateManager */
protected $certManager ;
2023-04-21 12:17:28 -04:00
protected LoggerInterface $logger ;
protected IEventLogger $eventLogger ;
2023-06-22 08:33:18 -04:00
protected IMimeTypeDetector $mimeTypeDetector ;
2012-08-29 02:42:49 -04:00
2023-06-11 09:16:38 -04:00
/** @var int */
private $timeout ;
2023-06-22 08:33:18 -04:00
protected const PROPFIND_PROPS = [
'{DAV:}getlastmodified' ,
'{DAV:}getcontentlength' ,
'{DAV:}getcontenttype' ,
'{http://owncloud.org/ns}permissions' ,
'{http://open-collaboration-services.org/ns}share-permissions' ,
'{DAV:}resourcetype' ,
'{DAV:}getetag' ,
'{DAV:}quota-available-bytes' ,
];
2015-03-09 06:48:55 -04:00
/**
* @ param array $params
* @ throws \Exception
*/
2012-09-07 09:22:01 -04:00
public function __construct ( $params ) {
2015-04-13 03:26:30 -04:00
$this -> statCache = new ArrayCache ();
2015-09-01 12:01:27 -04:00
$this -> httpClientService = \OC :: $server -> getHTTPClientService ();
2012-12-24 13:45:52 -05:00
if ( isset ( $params [ 'host' ]) && isset ( $params [ 'user' ]) && isset ( $params [ 'password' ])) {
$host = $params [ 'host' ];
//remove leading http[s], will be generated in createBaseUri()
2023-07-06 21:24:20 -04:00
if ( str_starts_with ( $host , " https:// " )) {
2020-04-10 08:19:56 -04:00
$host = substr ( $host , 8 );
2023-07-06 21:24:20 -04:00
} elseif ( str_starts_with ( $host , " http:// " )) {
2020-04-10 08:19:56 -04:00
$host = substr ( $host , 7 );
}
2014-03-04 09:44:58 -05:00
$this -> host = $host ;
$this -> user = $params [ 'user' ];
$this -> password = $params [ 'password' ];
2017-02-22 15:47:49 -05:00
if ( isset ( $params [ 'authType' ])) {
$this -> authType = $params [ 'authType' ];
}
2012-12-24 13:45:52 -05:00
if ( isset ( $params [ 'secure' ])) {
if ( is_string ( $params [ 'secure' ])) {
$this -> secure = ( $params [ 'secure' ] === 'true' );
} else {
$this -> secure = ( bool ) $params [ 'secure' ];
}
2012-11-30 10:27:11 -05:00
} else {
2012-12-24 13:45:52 -05:00
$this -> secure = false ;
}
2013-12-15 11:22:52 -05:00
if ( $this -> secure === true ) {
2015-11-25 10:58:54 -05:00
// inject mock for testing
2019-11-28 07:56:35 -05:00
$this -> certManager = \OC :: $server -> getCertificateManager ();
2013-12-15 11:22:52 -05:00
}
2018-01-25 16:26:47 -05:00
$this -> root = $params [ 'root' ] ? ? '/' ;
$this -> root = '/' . ltrim ( $this -> root , '/' );
$this -> root = rtrim ( $this -> root , '/' ) . '/' ;
2012-11-30 10:27:11 -05:00
} else {
2014-10-13 11:15:58 -04:00
throw new \Exception ( 'Invalid webdav storage configuration' );
2012-03-23 13:54:38 -04:00
}
2023-04-21 12:17:28 -04:00
$this -> logger = \OC :: $server -> get ( LoggerInterface :: class );
$this -> eventLogger = \OC :: $server -> get ( IEventLogger :: class );
2023-06-11 09:16:38 -04:00
// This timeout value will be used for the download and upload of files
2023-06-12 13:40:39 -04:00
$this -> timeout = \OC :: $server -> get ( IConfig :: class ) -> getSystemValueInt ( 'davstorage.request_timeout' , 30 );
2023-06-22 08:33:18 -04:00
$this -> mimeTypeDetector = \OC :: $server -> getMimeTypeDetector ();
2012-10-21 16:04:45 -04:00
}
2017-02-15 08:00:00 -05:00
protected function init () {
2014-03-04 09:44:58 -05:00
if ( $this -> ready ) {
2012-10-21 16:04:45 -04:00
return ;
}
$this -> ready = true ;
2012-08-29 02:42:49 -04:00
2017-02-22 15:47:49 -05:00
$settings = [
2013-12-17 03:38:43 -05:00
'baseUri' => $this -> createBaseUri (),
'userName' => $this -> user ,
'password' => $this -> password ,
2017-02-22 15:47:49 -05:00
];
2021-03-04 08:49:58 -05:00
if ( $this -> authType !== null ) {
2017-02-22 15:47:49 -05:00
$settings [ 'authType' ] = $this -> authType ;
}
2012-03-23 13:54:38 -04:00
2023-04-05 06:50:08 -04:00
$proxy = \OC :: $server -> getConfig () -> getSystemValueString ( 'proxy' , '' );
2017-09-28 08:22:42 -04:00
if ( $proxy !== '' ) {
2016-02-24 13:48:47 -05:00
$settings [ 'proxy' ] = $proxy ;
}
2015-04-13 03:26:30 -04:00
$this -> client = new Client ( $settings );
2015-02-12 06:29:01 -05:00
$this -> client -> setThrowExceptions ( true );
2019-11-28 07:56:35 -05:00
2020-04-10 08:19:56 -04:00
if ( $this -> secure === true ) {
2019-11-28 07:56:35 -05:00
$certPath = $this -> certManager -> getAbsoluteBundlePath ();
if ( file_exists ( $certPath )) {
$this -> certPath = $certPath ;
}
if ( $this -> certPath ) {
$this -> client -> addCurlSetting ( CURLOPT_CAINFO , $this -> certPath );
}
2012-07-09 04:19:19 -04:00
}
2023-04-21 12:17:28 -04:00
$lastRequestStart = 0 ;
$this -> client -> on ( 'beforeRequest' , function ( RequestInterface $request ) use ( & $lastRequestStart ) {
$this -> logger -> debug ( " sending dav " . $request -> getMethod () . " request to external storage: " . $request -> getAbsoluteUrl (), [ 'app' => 'dav' ]);
$lastRequestStart = microtime ( true );
$this -> eventLogger -> start ( 'fs:storage:dav:request' , " Sending dav request to external storage " );
});
$this -> client -> on ( 'afterRequest' , function ( RequestInterface $request ) use ( & $lastRequestStart ) {
$elapsed = microtime ( true ) - $lastRequestStart ;
$this -> logger -> debug ( " dav " . $request -> getMethod () . " request to external storage: " . $request -> getAbsoluteUrl () . " took " . round ( $elapsed * 1000 , 1 ) . " ms " , [ 'app' => 'dav' ]);
$this -> eventLogger -> end ( 'fs:storage:dav:request' );
});
2012-03-23 13:54:38 -04:00
}
2015-02-11 12:16:01 -05:00
/**
* Clear the stat cache
*/
public function clearStatCache () {
$this -> statCache -> clear ();
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-03-04 09:44:58 -05:00
public function getId () {
2012-10-11 17:06:57 -04:00
return 'webdav::' . $this -> user . '@' . $this -> host . '/' . $this -> root ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-10-13 11:15:58 -04:00
public function createBaseUri () {
2014-03-04 09:44:58 -05:00
$baseUri = 'http' ;
2012-11-30 10:27:11 -05:00
if ( $this -> secure ) {
2014-03-04 09:44:58 -05:00
$baseUri .= 's' ;
2012-03-23 13:54:38 -04:00
}
2014-03-04 09:44:58 -05:00
$baseUri .= '://' . $this -> host . $this -> root ;
2012-03-23 13:54:38 -04:00
return $baseUri ;
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function mkdir ( $path ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2014-03-04 09:44:58 -05:00
$path = $this -> cleanPath ( $path );
2015-02-11 12:16:01 -05:00
$result = $this -> simpleResponse ( 'MKCOL' , $path , null , 201 );
if ( $result ) {
$this -> statCache -> set ( $path , true );
}
return $result ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function rmdir ( $path ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2015-02-11 12:16:01 -05:00
$path = $this -> cleanPath ( $path );
2014-01-31 10:06:11 -05:00
// FIXME: some WebDAV impl return 403 when trying to DELETE
// a non-empty folder
2015-02-11 12:16:01 -05:00
$result = $this -> simpleResponse ( 'DELETE' , $path . '/' , null , 204 );
$this -> statCache -> clear ( $path . '/' );
$this -> statCache -> remove ( $path );
return $result ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function opendir ( $path ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2014-03-04 09:44:58 -05:00
$path = $this -> cleanPath ( $path );
2012-11-30 10:27:11 -05:00
try {
2023-06-22 08:33:18 -04:00
$content = $this -> getDirectoryContent ( $path );
$files = [];
foreach ( $content as $child ) {
$files [] = $child [ 'name' ];
2012-03-23 13:54:38 -04:00
}
2023-06-22 08:33:18 -04:00
return IteratorDirectory :: wrap ( $files );
2014-03-04 09:44:58 -05:00
} catch ( \Exception $e ) {
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2012-03-23 13:54:38 -04:00
}
2015-04-10 05:55:58 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
2015-02-11 12:16:01 -05:00
/**
* Propfind call with cache handling .
*
* First checks if information is cached .
* If not , request it from the server then store to cache .
*
* @ param string $path path to propfind
2015-09-01 12:01:27 -04:00
*
2016-10-10 06:29:06 -04:00
* @ return array | boolean propfind response or false if the entry was not found
2015-02-11 12:16:01 -05:00
*
2016-10-10 06:29:06 -04:00
* @ throws ClientHttpException
2015-02-11 12:16:01 -05:00
*/
2016-04-13 11:31:47 -04:00
protected function propfind ( $path ) {
2015-02-11 12:16:01 -05:00
$path = $this -> cleanPath ( $path );
$cachedResponse = $this -> statCache -> get ( $path );
// we either don't know it, or we know it exists but need more details
if ( is_null ( $cachedResponse ) || $cachedResponse === true ) {
$this -> init ();
try {
2016-10-10 06:29:06 -04:00
$response = $this -> client -> propFind (
2015-02-11 12:16:01 -05:00
$this -> encodePath ( $path ),
2023-06-22 08:33:18 -04:00
self :: PROPFIND_PROPS
2015-02-11 12:16:01 -05:00
);
$this -> statCache -> set ( $path , $response );
2016-10-10 06:29:06 -04:00
} catch ( ClientHttpException $e ) {
2019-08-20 06:54:58 -04:00
if ( $e -> getHttpStatus () === 404 || $e -> getHttpStatus () === 405 ) {
2016-10-10 06:29:06 -04:00
$this -> statCache -> clear ( $path . '/' );
$this -> statCache -> set ( $path , false );
return false ;
}
$this -> convertException ( $e , $path );
} catch ( \Exception $e ) {
$this -> convertException ( $e , $path );
2015-02-11 12:16:01 -05:00
}
} else {
$response = $cachedResponse ;
}
return $response ;
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function filetype ( $path ) {
2012-11-30 10:27:11 -05:00
try {
2015-02-11 12:16:01 -05:00
$response = $this -> propfind ( $path );
2016-10-10 06:29:06 -04:00
if ( $response === false ) {
return false ;
}
$responseType = [];
2014-01-31 10:06:11 -05:00
if ( isset ( $response [ " { DAV:}resourcetype " ])) {
2015-11-20 07:35:23 -05:00
/** @var ResourceType[] $response */
$responseType = $response [ " { DAV:}resourcetype " ] -> getValue ();
2014-01-31 10:06:11 -05:00
}
2014-03-04 09:44:58 -05:00
return ( count ( $responseType ) > 0 and $responseType [ 0 ] == " { DAV:}collection " ) ? 'dir' : 'file' ;
} catch ( \Exception $e ) {
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2012-03-23 13:54:38 -04:00
}
2015-04-10 05:55:58 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function file_exists ( $path ) {
2012-11-30 10:27:11 -05:00
try {
2015-02-11 12:16:01 -05:00
$path = $this -> cleanPath ( $path );
$cachedState = $this -> statCache -> get ( $path );
if ( $cachedState === false ) {
// we know the file doesn't exist
return false ;
2020-04-10 04:35:09 -04:00
} elseif ( ! is_null ( $cachedState )) {
2015-02-11 12:16:01 -05:00
return true ;
}
// need to get from server
2016-10-10 06:29:06 -04:00
return ( $this -> propfind ( $path ) !== false );
2014-03-04 09:44:58 -05:00
} catch ( \Exception $e ) {
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2012-03-23 13:54:38 -04:00
}
2015-04-10 05:55:58 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function unlink ( $path ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2015-02-11 12:16:01 -05:00
$path = $this -> cleanPath ( $path );
$result = $this -> simpleResponse ( 'DELETE' , $path , null , 204 );
$this -> statCache -> clear ( $path . '/' );
$this -> statCache -> remove ( $path );
return $result ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2013-02-09 11:35:47 -05:00
public function fopen ( $path , $mode ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2014-03-04 09:44:58 -05:00
$path = $this -> cleanPath ( $path );
switch ( $mode ) {
2012-03-23 13:54:38 -04:00
case 'r' :
case 'rb' :
2015-12-10 11:13:02 -05:00
try {
$response = $this -> httpClientService
2017-09-28 08:22:42 -04:00
-> newClient ()
-> get ( $this -> createBaseUri () . $this -> encodePath ( $path ), [
'auth' => [ $this -> user , $this -> password ],
2023-05-15 17:35:12 -04:00
'stream' => true ,
// set download timeout for users with slow connections or large files
2023-06-11 09:16:38 -04:00
'timeout' => $this -> timeout
2017-09-28 08:22:42 -04:00
]);
2018-02-09 07:09:03 -05:00
} catch ( \GuzzleHttp\Exception\ClientException $e ) {
2016-03-12 16:45:07 -05:00
if ( $e -> getResponse () instanceof ResponseInterface
&& $e -> getResponse () -> getStatusCode () === 404 ) {
2015-12-10 11:13:02 -05:00
return false ;
} else {
throw $e ;
}
2012-10-11 15:13:19 -04:00
}
2014-03-04 09:44:58 -05:00
2015-08-29 08:53:59 -04:00
if ( $response -> getStatusCode () !== Http :: STATUS_OK ) {
if ( $response -> getStatusCode () === Http :: STATUS_LOCKED ) {
2015-07-01 07:17:29 -04:00
throw new \OCP\Lock\LockedException ( $path );
2015-08-29 08:53:59 -04:00
} else {
2022-03-31 04:57:10 -04:00
\OC :: $server -> get ( LoggerInterface :: class ) -> error ( 'Guzzle get returned status code ' . $response -> getStatusCode (), [ 'app' => 'webdav client' ]);
2015-07-01 07:17:29 -04:00
}
2014-01-31 10:06:11 -05:00
}
2015-08-29 08:53:59 -04:00
return $response -> getBody ();
2012-03-23 13:54:38 -04:00
case 'w' :
case 'wb' :
case 'a' :
case 'ab' :
case 'r+' :
case 'w+' :
case 'wb+' :
case 'a+' :
case 'x' :
case 'x+' :
case 'c' :
case 'c+' :
//emulate these
2015-07-02 10:31:10 -04:00
$tempManager = \OC :: $server -> getTempManager ();
2014-03-04 09:44:58 -05:00
if ( strrpos ( $path , '.' ) !== false ) {
$ext = substr ( $path , strrpos ( $path , '.' ));
2012-11-30 10:27:11 -05:00
} else {
2014-03-04 09:44:58 -05:00
$ext = '' ;
2012-03-23 13:54:38 -04:00
}
2014-03-04 09:44:58 -05:00
if ( $this -> file_exists ( $path )) {
2014-06-20 10:09:38 -04:00
if ( ! $this -> isUpdatable ( $path )) {
return false ;
}
2015-07-02 10:31:10 -04:00
if ( $mode === 'w' or $mode === 'w+' ) {
$tmpFile = $tempManager -> getTemporaryFile ( $ext );
} else {
$tmpFile = $this -> getCachedFile ( $path );
}
2014-03-04 09:44:58 -05:00
} else {
2014-06-20 10:09:38 -04:00
if ( ! $this -> isCreatable ( dirname ( $path ))) {
return false ;
}
2015-07-02 10:31:10 -04:00
$tmpFile = $tempManager -> getTemporaryFile ( $ext );
2012-03-23 13:54:38 -04:00
}
2017-01-03 11:26:44 -05:00
$handle = fopen ( $tmpFile , $mode );
return CallbackWrapper :: wrap ( $handle , null , null , function () use ( $path , $tmpFile ) {
$this -> writeBack ( $tmpFile , $path );
});
2012-03-23 13:54:38 -04:00
}
}
2015-03-09 06:48:55 -04:00
/**
* @ param string $tmpFile
*/
2017-01-03 11:26:44 -05:00
public function writeBack ( $tmpFile , $path ) {
$this -> uploadFile ( $tmpFile , $path );
unlink ( $tmpFile );
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2012-09-07 09:22:01 -04:00
public function free_space ( $path ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2014-03-04 09:44:58 -05:00
$path = $this -> cleanPath ( $path );
2012-11-30 10:27:11 -05:00
try {
2022-03-25 10:24:38 -04:00
$response = $this -> propfind ( $path );
2016-10-10 06:29:06 -04:00
if ( $response === false ) {
return FileInfo :: SPACE_UNKNOWN ;
}
2012-11-30 10:27:11 -05:00
if ( isset ( $response [ '{DAV:}quota-available-bytes' ])) {
2023-05-11 06:46:16 -04:00
return Util :: numericToNumber ( $response [ '{DAV:}quota-available-bytes' ]);
2012-11-30 10:27:11 -05:00
} else {
2015-04-13 03:26:30 -04:00
return FileInfo :: SPACE_UNKNOWN ;
2012-03-23 13:54:38 -04:00
}
2014-03-04 09:44:58 -05:00
} catch ( \Exception $e ) {
2015-04-13 03:26:30 -04:00
return FileInfo :: SPACE_UNKNOWN ;
2012-03-23 13:54:38 -04:00
}
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-03-04 09:44:58 -05:00
public function touch ( $path , $mtime = null ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2012-11-30 10:27:11 -05:00
if ( is_null ( $mtime )) {
2014-03-04 09:44:58 -05:00
$mtime = time ();
2012-03-23 13:54:38 -04:00
}
2014-03-04 09:44:58 -05:00
$path = $this -> cleanPath ( $path );
2013-07-12 07:25:37 -04:00
// if file exists, update the mtime, else create a new empty file
if ( $this -> file_exists ( $path )) {
2014-01-31 10:06:11 -05:00
try {
2015-02-11 12:16:01 -05:00
$this -> statCache -> remove ( $path );
2016-04-01 15:16:29 -04:00
$this -> client -> proppatch ( $this -> encodePath ( $path ), [ '{DAV:}lastmodified' => $mtime ]);
// non-owncloud clients might not have accepted the property, need to recheck it
$response = $this -> client -> propfind ( $this -> encodePath ( $path ), [ '{DAV:}getlastmodified' ], 0 );
2016-10-10 06:29:06 -04:00
if ( $response === false ) {
return false ;
}
2016-04-01 15:16:29 -04:00
if ( isset ( $response [ '{DAV:}getlastmodified' ])) {
$remoteMtime = strtotime ( $response [ '{DAV:}getlastmodified' ]);
if ( $remoteMtime !== $mtime ) {
// server has not accepted the mtime
return false ;
}
}
2015-02-12 06:29:01 -05:00
} catch ( ClientHttpException $e ) {
if ( $e -> getHttpStatus () === 501 ) {
return false ;
}
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2015-01-30 06:02:23 -05:00
return false ;
2014-12-03 16:20:00 -05:00
} catch ( \Exception $e ) {
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2014-01-31 10:06:11 -05:00
return false ;
}
2013-07-12 07:25:37 -04:00
} else {
$this -> file_put_contents ( $path , '' );
}
2013-11-25 06:44:27 -05:00
return true ;
2012-03-23 13:54:38 -04:00
}
2015-02-11 12:16:01 -05:00
/**
* @ param string $path
2020-12-21 07:43:51 -05:00
* @ param mixed $data
2022-12-19 08:03:09 -05:00
* @ return int | float | false
2015-02-11 12:16:01 -05:00
*/
public function file_put_contents ( $path , $data ) {
$path = $this -> cleanPath ( $path );
$result = parent :: file_put_contents ( $path , $data );
$this -> statCache -> remove ( $path );
return $result ;
}
2015-03-09 06:48:55 -04:00
/**
* @ param string $path
* @ param string $target
*/
2014-03-04 09:44:58 -05:00
protected function uploadFile ( $path , $target ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2015-09-01 12:01:27 -04:00
2015-02-11 12:16:01 -05:00
// invalidate
$target = $this -> cleanPath ( $target );
$this -> statCache -> remove ( $target );
2014-03-04 09:44:58 -05:00
$source = fopen ( $path , 'r' );
2012-03-23 13:54:38 -04:00
2015-09-01 12:01:27 -04:00
$this -> httpClientService
-> newClient ()
-> put ( $this -> createBaseUri () . $this -> encodePath ( $target ), [
'body' => $source ,
2023-05-15 17:35:12 -04:00
'auth' => [ $this -> user , $this -> password ],
// set upload timeout for users with slow connections or large files
2023-06-11 09:16:38 -04:00
'timeout' => $this -> timeout
2015-09-01 12:01:27 -04:00
]);
2014-03-04 09:44:58 -05:00
$this -> removeCachedFile ( $target );
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2022-10-18 06:49:34 -04:00
public function rename ( $source , $target ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2022-10-18 06:49:34 -04:00
$source = $this -> cleanPath ( $source );
$target = $this -> cleanPath ( $target );
2012-11-30 10:27:11 -05:00
try {
2015-07-03 13:49:25 -04:00
// overwrite directory ?
2022-10-18 06:49:34 -04:00
if ( $this -> is_dir ( $target )) {
2015-07-03 13:49:25 -04:00
// needs trailing slash in destination
2022-10-18 06:49:34 -04:00
$target = rtrim ( $target , '/' ) . '/' ;
2015-07-03 13:49:25 -04:00
}
2015-02-11 12:16:01 -05:00
$this -> client -> request (
'MOVE' ,
2022-10-18 06:49:34 -04:00
$this -> encodePath ( $source ),
2015-02-11 12:16:01 -05:00
null ,
2015-07-03 13:49:25 -04:00
[
2022-10-18 06:49:34 -04:00
'Destination' => $this -> createBaseUri () . $this -> encodePath ( $target ),
2015-07-03 13:49:25 -04:00
]
2015-02-11 12:16:01 -05:00
);
2022-10-18 06:49:34 -04:00
$this -> statCache -> clear ( $source . '/' );
$this -> statCache -> clear ( $target . '/' );
$this -> statCache -> set ( $source , false );
$this -> statCache -> set ( $target , true );
$this -> removeCachedFile ( $source );
$this -> removeCachedFile ( $target );
2012-03-23 13:54:38 -04:00
return true ;
2014-03-04 09:44:58 -05:00
} catch ( \Exception $e ) {
2015-04-10 05:55:58 -04:00
$this -> convertException ( $e );
2012-03-23 13:54:38 -04:00
}
2015-04-10 05:55:58 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2022-10-18 06:49:34 -04:00
public function copy ( $source , $target ) {
2012-10-21 16:04:45 -04:00
$this -> init ();
2022-10-18 06:49:34 -04:00
$source = $this -> cleanPath ( $source );
$target = $this -> cleanPath ( $target );
2012-11-30 10:27:11 -05:00
try {
2015-07-03 13:49:25 -04:00
// overwrite directory ?
2022-10-18 06:49:34 -04:00
if ( $this -> is_dir ( $target )) {
2015-07-03 13:49:25 -04:00
// needs trailing slash in destination
2022-10-18 06:49:34 -04:00
$target = rtrim ( $target , '/' ) . '/' ;
2015-07-03 13:49:25 -04:00
}
$this -> client -> request (
'COPY' ,
2022-10-18 06:49:34 -04:00
$this -> encodePath ( $source ),
2015-07-03 13:49:25 -04:00
null ,
[
2022-10-18 06:49:34 -04:00
'Destination' => $this -> createBaseUri () . $this -> encodePath ( $target ),
2015-07-03 13:49:25 -04:00
]
);
2022-10-18 06:49:34 -04:00
$this -> statCache -> clear ( $target . '/' );
$this -> statCache -> set ( $target , true );
$this -> removeCachedFile ( $target );
2012-03-23 13:54:38 -04:00
return true ;
2014-03-04 09:44:58 -05:00
} catch ( \Exception $e ) {
2015-04-10 05:55:58 -04:00
$this -> convertException ( $e );
2012-03-23 13:54:38 -04:00
}
2015-04-10 05:55:58 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
2023-06-22 08:33:18 -04:00
public function getMetaData ( $path ) {
if ( Filesystem :: isFileBlacklisted ( $path )) {
throw new ForbiddenException ( 'Invalid path: ' . $path , false );
}
$response = $this -> propfind ( $path );
if ( ! $response ) {
return null ;
} else {
return $this -> getMetaFromPropfind ( $path , $response );
}
}
private function getMetaFromPropfind ( string $path , array $response ) : array {
if ( isset ( $response [ '{DAV:}getetag' ])) {
$etag = trim ( $response [ '{DAV:}getetag' ], '"' );
if ( strlen ( $etag ) > 40 ) {
$etag = md5 ( $etag );
2016-10-10 06:29:06 -04:00
}
2023-06-22 08:33:18 -04:00
} else {
$etag = parent :: getETag ( $path );
}
$responseType = [];
if ( isset ( $response [ " { DAV:}resourcetype " ])) {
/** @var ResourceType[] $response */
$responseType = $response [ " { DAV:}resourcetype " ] -> getValue ();
}
$type = ( count ( $responseType ) > 0 and $responseType [ 0 ] == " { DAV:}collection " ) ? 'dir' : 'file' ;
if ( $type === 'dir' ) {
$mimeType = 'httpd/unix-directory' ;
} elseif ( isset ( $response [ '{DAV:}getcontenttype' ])) {
$mimeType = $response [ '{DAV:}getcontenttype' ];
} else {
$mimeType = $this -> mimeTypeDetector -> detectPath ( $path );
2012-03-23 13:54:38 -04:00
}
2023-06-22 08:33:18 -04:00
if ( isset ( $response [ '{http://owncloud.org/ns}permissions' ])) {
$permissions = $this -> parsePermissions ( $response [ '{http://owncloud.org/ns}permissions' ]);
} elseif ( $type === 'dir' ) {
$permissions = Constants :: PERMISSION_ALL ;
} else {
$permissions = Constants :: PERMISSION_ALL - Constants :: PERMISSION_CREATE ;
}
$mtime = isset ( $response [ '{DAV:}getlastmodified' ]) ? strtotime ( $response [ '{DAV:}getlastmodified' ]) : null ;
if ( $type === 'dir' ) {
$size = - 1 ;
} else {
$size = Util :: numericToNumber ( $response [ '{DAV:}getcontentlength' ] ? ? 0 );
}
return [
'name' => basename ( $path ),
'mtime' => $mtime ,
'storage_mtime' => $mtime ,
'size' => $size ,
'permissions' => $permissions ,
'etag' => $etag ,
'mimetype' => $mimeType ,
];
2012-03-23 13:54:38 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2023-06-22 08:33:18 -04:00
public function stat ( $path ) {
$meta = $this -> getMetaData ( $path );
if ( ! $meta ) {
return false ;
2017-09-28 08:22:42 -04:00
} else {
2023-06-22 08:33:18 -04:00
return $meta ;
2017-09-28 08:22:42 -04:00
}
}
2023-06-22 08:33:18 -04:00
/** {@inheritdoc} */
public function getMimeType ( $path ) {
$meta = $this -> getMetaData ( $path );
if ( $meta ) {
return $meta [ 'mimetype' ];
} else {
2017-09-28 08:22:42 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
}
2014-02-19 03:31:54 -05:00
/**
* @ param string $path
2015-03-09 06:48:55 -04:00
* @ return string
2014-02-19 03:31:54 -05:00
*/
2013-02-12 05:05:12 -05:00
public function cleanPath ( $path ) {
2015-02-11 12:16:01 -05:00
if ( $path === '' ) {
2014-05-16 06:30:08 -04:00
return $path ;
}
2015-04-13 03:26:30 -04:00
$path = Filesystem :: normalizePath ( $path );
2013-11-20 12:14:42 -05:00
// remove leading slash
return substr ( $path , 1 );
2012-03-23 13:54:38 -04:00
}
2014-01-31 10:06:11 -05:00
/**
* URL encodes the given path but keeps the slashes
2014-03-04 09:44:58 -05:00
*
2014-01-31 10:06:11 -05:00
* @ param string $path to encode
* @ return string encoded path
*/
2017-02-15 08:00:00 -05:00
protected function encodePath ( $path ) {
2014-01-31 10:06:11 -05:00
// slashes need to stay
return str_replace ( '%2F' , '/' , rawurlencode ( $path ));
}
2014-02-06 10:30:58 -05:00
/**
* @ param string $method
* @ param string $path
2015-03-09 06:48:55 -04:00
* @ param string | resource | null $body
* @ param int $expected
* @ return bool
* @ throws StorageInvalidException
* @ throws StorageNotAvailableException
2014-02-06 10:30:58 -05:00
*/
2017-02-15 08:00:00 -05:00
protected function simpleResponse ( $method , $path , $body , $expected ) {
2014-03-04 09:44:58 -05:00
$path = $this -> cleanPath ( $path );
2012-11-30 10:27:11 -05:00
try {
2014-03-04 09:44:58 -05:00
$response = $this -> client -> request ( $method , $this -> encodePath ( $path ), $body );
return $response [ 'statusCode' ] == $expected ;
2015-02-12 06:29:01 -05:00
} catch ( ClientHttpException $e ) {
if ( $e -> getHttpStatus () === 404 && $method === 'DELETE' ) {
2015-02-11 12:16:01 -05:00
$this -> statCache -> clear ( $path . '/' );
$this -> statCache -> set ( $path , false );
2015-01-30 06:02:23 -05:00
return false ;
}
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2014-03-04 09:44:58 -05:00
} catch ( \Exception $e ) {
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2012-03-23 13:54:38 -04:00
}
2015-04-10 05:55:58 -04:00
return false ;
2012-03-23 13:54:38 -04:00
}
2013-08-02 09:44:56 -04:00
/**
* check if curl is installed
*/
public static function checkDependencies () {
2015-03-12 16:43:41 -04:00
return true ;
2013-08-02 09:44:56 -04:00
}
2014-06-04 12:19:52 -04:00
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-06-13 09:28:24 -04:00
public function isUpdatable ( $path ) {
2015-04-13 03:26:30 -04:00
return ( bool )( $this -> getPermissions ( $path ) & Constants :: PERMISSION_UPDATE );
2014-06-13 09:28:24 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-06-13 09:28:24 -04:00
public function isCreatable ( $path ) {
2015-04-13 03:26:30 -04:00
return ( bool )( $this -> getPermissions ( $path ) & Constants :: PERMISSION_CREATE );
2014-06-13 09:28:24 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-06-13 09:28:24 -04:00
public function isSharable ( $path ) {
2015-04-13 03:26:30 -04:00
return ( bool )( $this -> getPermissions ( $path ) & Constants :: PERMISSION_SHARE );
2014-06-13 09:28:24 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-06-13 09:28:24 -04:00
public function isDeletable ( $path ) {
2015-04-13 03:26:30 -04:00
return ( bool )( $this -> getPermissions ( $path ) & Constants :: PERMISSION_DELETE );
2014-06-13 09:28:24 -04:00
}
2015-03-09 06:48:55 -04:00
/** {@inheritdoc} */
2014-06-04 12:19:52 -04:00
public function getPermissions ( $path ) {
2023-06-22 08:33:18 -04:00
$stat = $this -> getMetaData ( $path );
if ( $stat ) {
return $stat [ 'permissions' ];
2014-06-04 12:19:52 -04:00
} else {
2014-06-13 09:28:24 -04:00
return 0 ;
2014-06-04 12:19:52 -04:00
}
}
2014-06-27 11:10:46 -04:00
2015-04-27 11:41:02 -04:00
/** {@inheritdoc} */
public function getETag ( $path ) {
2023-06-22 08:33:18 -04:00
$meta = $this -> getMetaData ( $path );
if ( $meta ) {
return $meta [ 'etag' ];
} else {
2016-10-10 06:29:06 -04:00
return null ;
}
2015-04-27 11:41:02 -04:00
}
2014-06-27 11:27:47 -04:00
/**
* @ param string $permissionsString
* @ return int
*/
protected function parsePermissions ( $permissionsString ) {
2015-04-13 03:26:30 -04:00
$permissions = Constants :: PERMISSION_READ ;
2023-05-15 07:47:19 -04:00
if ( str_contains ( $permissionsString , 'R' )) {
2015-04-13 03:26:30 -04:00
$permissions |= Constants :: PERMISSION_SHARE ;
2014-06-27 11:27:47 -04:00
}
2023-05-15 07:47:19 -04:00
if ( str_contains ( $permissionsString , 'D' )) {
2015-04-13 03:26:30 -04:00
$permissions |= Constants :: PERMISSION_DELETE ;
2014-06-27 11:27:47 -04:00
}
2023-05-15 07:47:19 -04:00
if ( str_contains ( $permissionsString , 'W' )) {
2015-04-13 03:26:30 -04:00
$permissions |= Constants :: PERMISSION_UPDATE ;
2014-06-27 11:27:47 -04:00
}
2023-05-15 07:47:19 -04:00
if ( str_contains ( $permissionsString , 'CK' )) {
2015-04-13 03:26:30 -04:00
$permissions |= Constants :: PERMISSION_CREATE ;
$permissions |= Constants :: PERMISSION_UPDATE ;
2014-06-27 11:27:47 -04:00
}
return $permissions ;
}
2014-06-27 11:10:46 -04:00
/**
* check if a file or folder has been updated since $time
*
* @ param string $path
* @ param int $time
2014-06-30 09:46:37 -04:00
* @ throws \OCP\Files\StorageNotAvailableException
2014-06-27 11:10:46 -04:00
* @ return bool
*/
public function hasUpdated ( $path , $time ) {
$this -> init ();
2014-11-11 09:20:36 -05:00
$path = $this -> cleanPath ( $path );
2014-06-30 09:46:37 -04:00
try {
2015-02-11 12:16:01 -05:00
// force refresh for $path
$this -> statCache -> remove ( $path );
$response = $this -> propfind ( $path );
2016-10-10 06:29:06 -04:00
if ( $response === false ) {
if ( $path === '' ) {
// if root is gone it means the storage is not available
2018-04-23 05:30:22 -04:00
throw new StorageNotAvailableException ( 'root is gone' );
2016-10-10 06:29:06 -04:00
}
return false ;
}
2014-06-30 09:46:37 -04:00
if ( isset ( $response [ '{DAV:}getetag' ])) {
$cachedData = $this -> getCache () -> get ( $path );
2022-01-25 03:50:26 -05:00
$etag = trim ( $response [ '{DAV:}getetag' ], '"' );
2022-01-25 03:36:22 -05:00
if (( $cachedData === false ) || ( ! empty ( $etag ) && ( $cachedData [ 'etag' ] !== $etag ))) {
2014-06-30 09:46:37 -04:00
return true ;
2020-04-10 04:35:09 -04:00
} elseif ( isset ( $response [ '{http://open-collaboration-services.org/ns}share-permissions' ])) {
2016-04-15 04:52:53 -04:00
$sharePermissions = ( int ) $response [ '{http://open-collaboration-services.org/ns}share-permissions' ];
return $sharePermissions !== $cachedData [ 'permissions' ];
2020-04-10 04:35:09 -04:00
} elseif ( isset ( $response [ '{http://owncloud.org/ns}permissions' ])) {
2014-06-30 09:46:37 -04:00
$permissions = $this -> parsePermissions ( $response [ '{http://owncloud.org/ns}permissions' ]);
return $permissions !== $cachedData [ 'permissions' ];
} else {
return false ;
}
2022-03-19 19:38:07 -04:00
} elseif ( isset ( $response [ '{DAV:}getlastmodified' ])) {
2014-06-30 09:46:37 -04:00
$remoteMtime = strtotime ( $response [ '{DAV:}getlastmodified' ]);
return $remoteMtime > $time ;
2022-03-19 19:38:07 -04:00
} else {
// neither `getetag` nor `getlastmodified` is set
return false ;
2014-06-27 11:27:47 -04:00
}
2015-04-10 05:55:58 -04:00
} catch ( ClientHttpException $e ) {
2016-10-10 06:29:06 -04:00
if ( $e -> getHttpStatus () === 405 ) {
2015-07-13 12:28:53 -04:00
if ( $path === '' ) {
// if root is gone it means the storage is not available
2015-09-01 12:01:27 -04:00
throw new StorageNotAvailableException ( get_class ( $e ) . ': ' . $e -> getMessage ());
2015-07-13 12:28:53 -04:00
}
2015-02-12 06:29:01 -05:00
return false ;
}
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2015-04-10 05:55:58 -04:00
return false ;
} catch ( \Exception $e ) {
2015-07-01 07:17:29 -04:00
$this -> convertException ( $e , $path );
2015-01-30 06:02:23 -05:00
return false ;
2014-06-27 11:10:46 -04:00
}
}
2015-01-20 13:45:32 -05:00
/**
2015-04-10 05:55:58 -04:00
* Interpret the given exception and decide whether it is due to an
* unavailable storage , invalid storage or other .
* This will either throw StorageInvalidException , StorageNotAvailableException
* or do nothing .
*
* @ param Exception $e sabre exception
2015-07-01 07:17:29 -04:00
* @ param string $path optional path from the operation
2015-01-20 13:45:32 -05:00
*
* @ throws StorageInvalidException if the storage is invalid , for example
* when the authentication expired or is invalid
* @ throws StorageNotAvailableException if the storage is not available ,
* which might be temporary
2019-11-05 13:19:16 -05:00
* @ throws ForbiddenException if the action is not allowed
2015-01-20 13:45:32 -05:00
*/
2017-02-15 08:00:00 -05:00
protected function convertException ( Exception $e , $path = '' ) {
2022-03-31 04:57:10 -04:00
\OC :: $server -> get ( LoggerInterface :: class ) -> debug ( $e -> getMessage (), [ 'app' => 'files_external' , 'exception' => $e ]);
2015-04-10 05:55:58 -04:00
if ( $e instanceof ClientHttpException ) {
2016-06-27 15:34:28 -04:00
if ( $e -> getHttpStatus () === Http :: STATUS_LOCKED ) {
2015-07-01 07:17:29 -04:00
throw new \OCP\Lock\LockedException ( $path );
}
2016-06-27 15:34:28 -04:00
if ( $e -> getHttpStatus () === Http :: STATUS_UNAUTHORIZED ) {
2015-04-10 05:55:58 -04:00
// either password was changed or was invalid all along
2015-09-01 12:01:27 -04:00
throw new StorageInvalidException ( get_class ( $e ) . ': ' . $e -> getMessage ());
2020-04-10 04:35:09 -04:00
} elseif ( $e -> getHttpStatus () === Http :: STATUS_METHOD_NOT_ALLOWED ) {
2015-04-10 05:55:58 -04:00
// ignore exception for MethodNotAllowed, false will be returned
return ;
2020-04-10 08:19:56 -04:00
} elseif ( $e -> getHttpStatus () === Http :: STATUS_FORBIDDEN ) {
2019-11-05 13:19:16 -05:00
// The operation is forbidden. Fail somewhat gracefully
2020-06-25 17:33:14 -04:00
throw new ForbiddenException ( get_class ( $e ) . ':' . $e -> getMessage (), false );
2015-04-10 05:55:58 -04:00
}
2015-09-01 12:01:27 -04:00
throw new StorageNotAvailableException ( get_class ( $e ) . ': ' . $e -> getMessage ());
2020-04-10 04:35:09 -04:00
} elseif ( $e instanceof ClientException ) {
2015-04-10 05:55:58 -04:00
// connection timeout or refused, server could be temporarily down
2015-09-01 12:01:27 -04:00
throw new StorageNotAvailableException ( get_class ( $e ) . ': ' . $e -> getMessage ());
2020-04-10 04:35:09 -04:00
} elseif ( $e instanceof \InvalidArgumentException ) {
2015-04-10 05:55:58 -04:00
// parse error because the server returned HTML instead of XML,
// possibly temporarily down
2015-09-01 12:01:27 -04:00
throw new StorageNotAvailableException ( get_class ( $e ) . ': ' . $e -> getMessage ());
2020-04-10 04:35:09 -04:00
} elseif (( $e instanceof StorageNotAvailableException ) || ( $e instanceof StorageInvalidException )) {
2015-04-10 05:55:58 -04:00
// rethrow
throw $e ;
2015-01-20 13:45:32 -05:00
}
2015-04-10 05:55:58 -04:00
// TODO: only log for now, but in the future need to wrap/rethrow exception
2015-01-20 13:45:32 -05:00
}
2023-06-22 08:33:18 -04:00
public function getDirectoryContent ( $directory ) : \Traversable {
$this -> init ();
$directory = $this -> cleanPath ( $directory );
try {
$responses = $this -> client -> propFind (
$this -> encodePath ( $directory ),
self :: PROPFIND_PROPS ,
1
);
if ( $responses === false ) {
return ;
}
array_shift ( $responses ); //the first entry is the current directory
if ( ! $this -> statCache -> hasKey ( $directory )) {
$this -> statCache -> set ( $directory , true );
}
foreach ( $responses as $file => $response ) {
2023-09-22 19:40:52 -04:00
$file = rawurldecode ( $file );
2023-06-22 08:33:18 -04:00
$file = substr ( $file , strlen ( $this -> root ));
2023-06-23 08:39:17 -04:00
$file = $this -> cleanPath ( $file );
2023-06-22 08:33:18 -04:00
$this -> statCache -> set ( $file , $response );
yield $this -> getMetaFromPropfind ( $file , $response );
}
} catch ( \Exception $e ) {
$this -> convertException ( $e , $directory );
}
}
2012-03-23 13:54:38 -04:00
}