2011-07-20 13:53:34 +00:00
< ? php
2012-05-05 16:13:40 +00:00
2011-07-20 13:53:34 +00:00
/**
2012-05-05 16:13:40 +00:00
* ownCloud
*
* @ author Jakob Sack
* @ copyright 2011 Jakob Sack kde @ jakobsack . de
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation ; either
* version 3 of the License , or any later version .
*
* This library 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 along with this library . If not , see < http :// www . gnu . org / licenses />.
*
2011-07-20 13:53:34 +00:00
*/
2012-05-05 16:13:40 +00:00
2011-07-22 12:38:42 +00:00
class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_DAV_IFile {
2011-07-20 13:53:34 +00:00
/**
* Updates the data
*
2012-07-20 21:52:47 +00:00
* The data argument is a readable stream resource .
*
2013-09-30 08:46:50 +00:00
* After a successful put operation , you may choose to return an ETag . The
2012-07-20 21:52:47 +00:00
* etag must always be surrounded by double - quotes . These quotes must
* appear in the actual string you ' re returning .
*
* Clients may use the ETag from a PUT request to later on make sure that
* when they update the file , the contents haven ' t changed in the mean
* time .
*
* If you don ' t plan to store the file byte - by - byte , and you return a
* different object on a subsequent GET you are strongly recommended to not
* return an ETag , and just return null .
*
2011-07-20 13:53:34 +00:00
* @ param resource $data
2013-06-25 15:04:25 +00:00
* @ throws Sabre_DAV_Exception_Forbidden
2014-04-23 13:34:04 +00:00
* @ throws OC_Connector_Sabre_Exception_UnsupportedMediaType
* @ throws Sabre_DAV_Exception_BadRequest
* @ throws Sabre_DAV_Exception
* @ throws OC_Connector_Sabre_Exception_EntityTooLarge
* @ throws Sabre_DAV_Exception_ServiceUnavailable
2012-07-20 21:52:47 +00:00
* @ return string | null
2011-07-20 13:53:34 +00:00
*/
public function put ( $data ) {
2014-02-25 15:23:09 +00:00
if ( $this -> info && $this -> fileView -> file_exists ( $this -> path ) &&
! $this -> info -> isUpdateable ()) {
2013-06-25 15:04:25 +00:00
throw new \Sabre_DAV_Exception_Forbidden ();
}
2013-08-14 07:44:29 +00:00
// throw an exception if encryption was disabled but the files are still encrypted
if ( \OC_Util :: encryptedFiles ()) {
throw new \Sabre_DAV_Exception_ServiceUnavailable ();
}
2013-09-24 13:14:42 +00:00
2014-01-13 12:14:05 +00:00
$fileName = basename ( $this -> path );
if ( ! \OCP\Util :: isValidFileName ( $fileName )) {
throw new \Sabre_DAV_Exception_BadRequest ();
}
2013-09-24 13:14:42 +00:00
// chunked handling
if ( isset ( $_SERVER [ 'HTTP_OC_CHUNKED' ])) {
2013-10-07 15:49:21 +00:00
return $this -> createFileChunked ( $data );
2013-09-24 13:14:42 +00:00
}
2013-02-10 13:16:45 +00:00
// mark file as partial while uploading (ignored by the scanner)
2014-01-07 10:53:33 +00:00
$partpath = $this -> path . '.ocTransferId' . rand () . '.part' ;
2013-02-22 16:21:57 +00:00
2013-09-26 09:50:46 +00:00
try {
2014-02-25 15:23:09 +00:00
$putOkay = $this -> fileView -> file_put_contents ( $partpath , $data );
2013-09-26 09:50:46 +00:00
if ( $putOkay === false ) {
\OC_Log :: write ( 'webdav' , '\OC\Files\Filesystem::file_put_contents() failed' , \OC_Log :: ERROR );
2014-02-25 15:23:09 +00:00
$this -> fileView -> unlink ( $partpath );
2013-09-26 09:50:46 +00:00
// because we have no clue about the cause we can only throw back a 500/Internal Server Error
2014-01-23 11:11:53 +00:00
throw new Sabre_DAV_Exception ( 'Could not write file contents' );
2013-09-26 09:50:46 +00:00
}
} catch ( \OCP\Files\NotPermittedException $e ) {
2013-10-08 13:04:31 +00:00
// a more general case - due to whatever reason the content could not be written
throw new Sabre_DAV_Exception_Forbidden ( $e -> getMessage ());
2013-10-07 13:11:47 +00:00
} catch ( \OCP\Files\EntityTooLargeException $e ) {
2013-10-08 13:04:31 +00:00
// the file is too big to be stored
throw new OC_Connector_Sabre_Exception_EntityTooLarge ( $e -> getMessage ());
2013-10-07 13:11:47 +00:00
} catch ( \OCP\Files\InvalidContentException $e ) {
2013-10-08 13:04:31 +00:00
// the file content is not permitted
throw new OC_Connector_Sabre_Exception_UnsupportedMediaType ( $e -> getMessage ());
2013-10-07 13:11:47 +00:00
} catch ( \OCP\Files\InvalidPathException $e ) {
2013-10-08 13:04:31 +00:00
// the path for the file was not valid
// TODO: find proper http status code for this case
throw new Sabre_DAV_Exception_Forbidden ( $e -> getMessage ());
2014-05-24 13:18:24 +00:00
} catch ( \OCP\Files\LockNotAcquiredException $e ) {
// the file is currently being written to by another process
throw new OC_Connector_Sabre_Exception_FileLocked ( sprintf ( 'Target file %s is locked by another process. %s' , $e -> path , $e -> getMessage ()), $e -> getCode (), $e );
2013-09-24 12:25:56 +00:00
}
2013-02-22 16:21:57 +00:00
2013-02-10 13:16:45 +00:00
// rename to correct path
2014-05-24 13:18:24 +00:00
try {
$renameOkay = $this -> fileView -> rename ( $partpath , $this -> path );
$fileExists = $this -> fileView -> file_exists ( $this -> path );
if ( $renameOkay === false || $fileExists === false ) {
\OC_Log :: write ( 'webdav' , '\OC\Files\Filesystem::rename() failed' , \OC_Log :: ERROR );
$this -> fileView -> unlink ( $partpath );
throw new Sabre_DAV_Exception ( 'Could not rename part file to final file' );
}
}
catch ( \OCP\Files\LockNotAcquiredException $e ) {
// the file is currently being written to by another process
throw new OC_Connector_Sabre_Exception_FileLocked ( $e -> getMessage (), $e -> getCode (), $e );
2013-09-24 11:54:18 +00:00
}
2013-02-22 16:21:57 +00:00
2013-09-24 13:14:42 +00:00
// allow sync clients to send the mtime along in a header
2013-02-10 10:05:43 +00:00
$mtime = OC_Request :: hasModificationTime ();
if ( $mtime !== false ) {
2014-02-25 15:23:09 +00:00
if ( $this -> fileView -> touch ( $this -> path , $mtime )) {
2013-02-10 10:44:34 +00:00
header ( 'X-OC-MTime: accepted' );
}
2013-02-10 10:05:43 +00:00
}
2014-02-25 15:23:09 +00:00
$this -> refreshInfo ();
2011-07-20 13:53:34 +00:00
2014-02-25 15:23:09 +00:00
return '"' . $this -> info -> getEtag () . '"' ;
2011-07-20 13:53:34 +00:00
}
/**
* Returns the data
*
2014-05-11 17:28:45 +00:00
* @ return string | resource
2011-07-20 13:53:34 +00:00
*/
public function get () {
2013-09-24 13:14:42 +00:00
//throw exception if encryption is disabled but files are still encrypted
2013-08-14 07:44:29 +00:00
if ( \OC_Util :: encryptedFiles ()) {
throw new \Sabre_DAV_Exception_ServiceUnavailable ();
} else {
2014-02-25 15:23:09 +00:00
return $this -> fileView -> fopen ( $this -> path , 'rb' );
2013-08-14 07:44:29 +00:00
}
2011-07-20 13:53:34 +00:00
}
/**
* Delete the current file
*
* @ return void
2013-06-25 15:04:25 +00:00
* @ throws Sabre_DAV_Exception_Forbidden
2011-07-20 13:53:34 +00:00
*/
public function delete () {
2014-02-25 15:23:09 +00:00
if ( ! $this -> info -> isDeletable ()) {
2013-06-25 15:04:25 +00:00
throw new \Sabre_DAV_Exception_Forbidden ();
}
2014-02-25 15:23:09 +00:00
$this -> fileView -> unlink ( $this -> path );
2011-07-20 13:53:34 +00:00
2013-10-22 17:41:26 +00:00
// remove properties
$this -> removeProperties ();
2011-07-20 13:53:34 +00:00
}
/**
* Returns the size of the node , in bytes
*
2014-02-16 13:42:59 +00:00
* @ return int | float
2011-07-20 13:53:34 +00:00
*/
public function getSize () {
2014-02-25 15:23:09 +00:00
return $this -> info -> getSize ();
2011-07-20 13:53:34 +00:00
}
/**
* Returns the ETag for a file
*
2013-02-11 16:44:02 +00:00
* An ETag is a unique identifier representing the current version of the
* file . If the file changes , the ETag MUST change . The ETag is an
2013-09-30 09:36:08 +00:00
* arbitrary string , but MUST be surrounded by double - quotes .
2011-07-20 13:53:34 +00:00
*
* Return null if the ETag can not effectively be determined
*
* @ return mixed
*/
public function getETag () {
2014-04-24 09:10:46 +00:00
return '"' . $this -> info -> getEtag () . '"' ;
2011-07-20 13:53:34 +00:00
}
/**
* Returns the mime - type for a file
*
* If null is returned , we ' ll assume application / octet - stream
*
* @ return mixed
*/
public function getContentType () {
2014-04-15 18:05:43 +00:00
$mimeType = $this -> info -> getMimetype ();
2011-07-20 13:53:34 +00:00
2014-04-14 15:17:50 +00:00
return \OC_Helper :: getSecureMimeType ( $mimeType );
2011-07-20 13:53:34 +00:00
}
2013-10-07 15:49:21 +00:00
2014-02-06 15:30:58 +00:00
/**
* @ param resource $data
2014-04-23 13:34:04 +00:00
* @ return null | string
2014-02-06 15:30:58 +00:00
*/
2013-10-07 15:49:21 +00:00
private function createFileChunked ( $data )
{
list ( $path , $name ) = \Sabre_DAV_URLUtil :: splitPath ( $this -> path );
$info = OC_FileChunking :: decodeName ( $name );
if ( empty ( $info )) {
throw new Sabre_DAV_Exception_NotImplemented ();
}
$chunk_handler = new OC_FileChunking ( $info );
$bytesWritten = $chunk_handler -> store ( $info [ 'index' ], $data );
//detect aborted upload
if ( isset ( $_SERVER [ 'REQUEST_METHOD' ]) && $_SERVER [ 'REQUEST_METHOD' ] === 'PUT' ) {
if ( isset ( $_SERVER [ 'CONTENT_LENGTH' ])) {
$expected = $_SERVER [ 'CONTENT_LENGTH' ];
if ( $bytesWritten != $expected ) {
2013-10-17 18:20:13 +00:00
$chunk_handler -> remove ( $info [ 'index' ]);
2013-10-07 15:49:21 +00:00
throw new Sabre_DAV_Exception_BadRequest (
'expected filesize ' . $expected . ' got ' . $bytesWritten );
}
}
}
if ( $chunk_handler -> isComplete ()) {
2013-10-21 11:21:39 +00:00
// we first assembly the target file as a part file
2013-11-11 16:47:46 +00:00
$partFile = $path . '/' . $info [ 'name' ] . '.ocTransferId' . $info [ 'transferid' ] . '.part' ;
2013-10-21 11:21:39 +00:00
$chunk_handler -> file_assemble ( $partFile );
// here is the final atomic rename
$targetPath = $path . '/' . $info [ 'name' ];
2014-02-25 15:23:09 +00:00
$renameOkay = $this -> fileView -> rename ( $partFile , $targetPath );
$fileExists = $this -> fileView -> file_exists ( $targetPath );
2013-10-21 11:21:39 +00:00
if ( $renameOkay === false || $fileExists === false ) {
\OC_Log :: write ( 'webdav' , '\OC\Files\Filesystem::rename() failed' , \OC_Log :: ERROR );
2014-01-08 17:43:20 +00:00
// only delete if an error occurred and the target file was already created
if ( $fileExists ) {
2014-02-25 15:23:09 +00:00
$this -> fileView -> unlink ( $targetPath );
2014-01-08 17:43:20 +00:00
}
2014-01-23 11:11:53 +00:00
throw new Sabre_DAV_Exception ( 'Could not rename part file assembled from chunks' );
2013-10-21 11:21:39 +00:00
}
2013-10-21 13:00:28 +00:00
// allow sync clients to send the mtime along in a header
$mtime = OC_Request :: hasModificationTime ();
if ( $mtime !== false ) {
2014-02-25 15:23:09 +00:00
if ( $this -> fileView -> touch ( $targetPath , $mtime )) {
2013-10-21 13:00:28 +00:00
header ( 'X-OC-MTime: accepted' );
}
}
2014-02-25 15:23:09 +00:00
$info = $this -> fileView -> getFileInfo ( $targetPath );
return $info -> getEtag ();
2013-10-07 15:49:21 +00:00
}
return null ;
}
2011-07-20 13:53:34 +00:00
}