Example #1
0
 /**
  * This method is called before any HTTP method and validates there is enough free space to store the file
  *
  * @param string $method
  * @throws Sabre_DAV_Exception
  * @return bool
  */
 public function checkQuota($uri, $data = null)
 {
     $length = $this->getLength();
     if ($length) {
         if (substr($uri, 0, 1) !== '/') {
             $uri = '/' . $uri;
         }
         list($parentUri, $newName) = Sabre_DAV_URLUtil::splitPath($uri);
         $req = $this->server->httpRequest;
         if ($req->getHeader('OC-Chunked')) {
             $info = OC_FileChunking::decodeName($newName);
             $chunkHandler = new OC_FileChunking($info);
             // substract the already uploaded size to see whether
             // there is still enough space for the remaining chunks
             $length -= $chunkHandler->getCurrentSize();
         }
         $freeSpace = $this->getFreeSpace($parentUri);
         if ($freeSpace !== \OC\Files\SPACE_UNKNOWN && $length > $freeSpace) {
             if (isset($chunkHandler)) {
                 $chunkHandler->cleanup();
             }
             throw new Sabre_DAV_Exception_InsufficientStorage();
         }
     }
     return true;
 }
 /**
  * Creates a new file in the directory
  *
  * Data will either be supplied as a stream resource, or in certain cases
  * as a string. Keep in mind that you may have to support either.
  *
  * After successful creation of the file, you may choose to return the ETag
  * of the new file here.
  *
  * The returned ETag must be surrounded by double-quotes (The quotes should
  * be part of the actual string).
  *
  * If you cannot accurately determine the ETag, you should not return it.
  * If you don't store the file exactly as-is (you're transforming it
  * somehow) you should also not return an ETag.
  *
  * This means that if a subsequent GET to this new file does not exactly
  * return the same contents of what was submitted here, you are strongly
  * recommended to omit the ETag.
  *
  * @param string $name Name of the file
  * @param resource|string $data Initial payload
  * @throws \Sabre\DAV\Exception\Forbidden
  * @return null|string
  */
 public function createFile($name, $data = null)
 {
     try {
         // for chunked upload also updating a existing file is a "createFile"
         // because we create all the chunks before re-assemble them to the existing file.
         if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
             // exit if we can't create a new file and we don't updatable existing file
             $info = OC_FileChunking::decodeName($name);
             if (!$this->fileView->isCreatable($this->path) && !$this->fileView->isUpdatable($this->path . '/' . $info['name'])) {
                 throw new \Sabre\DAV\Exception\Forbidden();
             }
         } else {
             // For non-chunked upload it is enough to check if we can create a new file
             if (!$this->fileView->isCreatable($this->path)) {
                 throw new \Sabre\DAV\Exception\Forbidden();
             }
         }
         $path = $this->fileView->getAbsolutePath($this->path) . '/' . $name;
         // using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete
         $info = new \OC\Files\FileInfo($path, null, null, array(), null);
         $node = new OC_Connector_Sabre_File($this->fileView, $info);
         return $node->put($data);
     } catch (\OCP\Files\StorageNotAvailableException $e) {
         throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
     }
 }
Example #3
0
 /**
  * Creates a new file in the directory
  *
  * Data will either be supplied as a stream resource, or in certain cases
  * as a string. Keep in mind that you may have to support either.
  *
  * After succesful creation of the file, you may choose to return the ETag
  * of the new file here.
  *
  * The returned ETag must be surrounded by double-quotes (The quotes should
  * be part of the actual string).
  *
  * If you cannot accurately determine the ETag, you should not return it.
  * If you don't store the file exactly as-is (you're transforming it
  * somehow) you should also not return an ETag.
  *
  * This means that if a subsequent GET to this new file does not exactly
  * return the same contents of what was submitted here, you are strongly
  * recommended to omit the ETag.
  *
  * @param string $name Name of the file
  * @param resource|string $data Initial payload
  * @throws Sabre_DAV_Exception_Forbidden
  * @return null|string
  */
 public function createFile($name, $data = null)
 {
     if (!\OC\Files\Filesystem::isCreatable($this->path)) {
         throw new \Sabre_DAV_Exception_Forbidden();
     }
     if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
         $info = OC_FileChunking::decodeName($name);
         if (empty($info)) {
             throw new Sabre_DAV_Exception_NotImplemented();
         }
         $chunk_handler = new OC_FileChunking($info);
         $chunk_handler->store($info['index'], $data);
         if ($chunk_handler->isComplete()) {
             $newPath = $this->path . '/' . $info['name'];
             $chunk_handler->file_assemble($newPath);
             return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
         }
     } else {
         $newPath = $this->path . '/' . $name;
         // mark file as partial while uploading (ignored by the scanner)
         $partpath = $newPath . '.part';
         \OC\Files\Filesystem::file_put_contents($partpath, $data);
         //detect aborted upload
         if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
             if (isset($_SERVER['CONTENT_LENGTH'])) {
                 $expected = $_SERVER['CONTENT_LENGTH'];
                 $actual = \OC\Files\Filesystem::filesize($partpath);
                 if ($actual != $expected) {
                     \OC\Files\Filesystem::unlink($partpath);
                     throw new Sabre_DAV_Exception_BadRequest('expected filesize ' . $expected . ' got ' . $actual);
                 }
             }
         }
         // rename to correct path
         \OC\Files\Filesystem::rename($partpath, $newPath);
         // allow sync clients to send the mtime along in a header
         $mtime = OC_Request::hasModificationTime();
         if ($mtime !== false) {
             if (\OC\Files\Filesystem::touch($newPath, $mtime)) {
                 header('X-OC-MTime: accepted');
             }
         }
         return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
     }
     return null;
 }
Example #4
0
 /**
  * Creates a new file in the directory
  *
  * Data will either be supplied as a stream resource, or in certain cases
  * as a string. Keep in mind that you may have to support either.
  *
  * After succesful creation of the file, you may choose to return the ETag
  * of the new file here.
  *
  * The returned ETag must be surrounded by double-quotes (The quotes should
  * be part of the actual string).
  *
  * If you cannot accurately determine the ETag, you should not return it.
  * If you don't store the file exactly as-is (you're transforming it
  * somehow) you should also not return an ETag.
  *
  * This means that if a subsequent GET to this new file does not exactly
  * return the same contents of what was submitted here, you are strongly
  * recommended to omit the ETag.
  *
  * @param string $name Name of the file
  * @param resource|string $data Initial payload
  * @return null|string
  */
 public function createFile($name, $data = null)
 {
     if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
         $info = OC_FileChunking::decodeName($name);
         if (empty($info)) {
             throw new Sabre_DAV_Exception_NotImplemented();
         }
         $chunk_handler = new OC_FileChunking($info);
         $chunk_handler->store($info['index'], $data);
         if ($chunk_handler->isComplete()) {
             $newPath = $this->path . '/' . $info['name'];
             $chunk_handler->file_assemble($newPath);
             return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
         }
     } else {
         $newPath = $this->path . '/' . $name;
         OC_Filesystem::file_put_contents($newPath, $data);
         return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
     }
     return null;
 }
Example #5
0
 public function checkPreconditions($handleAsGET = false)
 {
     // chunked upload handling
     if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
         $filePath = parent::getRequestUri();
         list($path, $name) = \Sabre\DAV\URLUtil::splitPath($filePath);
         $info = OC_FileChunking::decodeName($name);
         if (!empty($info)) {
             $filePath = $path . '/' . $info['name'];
             $this->overLoadedUri = $filePath;
         }
     }
     $result = parent::checkPreconditions($handleAsGET);
     $this->overLoadedUri = null;
     return $result;
 }
Example #6
0
 /**
  * If the given path is a chunked file name, converts it
  * to the real file name. Only applies if the OC-CHUNKED header
  * is present.
  *
  * @param string $path chunk file path to convert
  * 
  * @return string path to real file
  */
 private function resolveChunkFile($path)
 {
     if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
         // resolve to real file name to find the proper node
         list($dir, $name) = \Sabre\HTTP\URLUtil::splitPath($path);
         if ($dir == '/' || $dir == '.') {
             $dir = '';
         }
         $info = \OC_FileChunking::decodeName($name);
         // only replace path if it was really the chunked file
         if (isset($info['transferid'])) {
             // getNodePath is called for multiple nodes within a chunk
             // upload call
             $path = $dir . '/' . $info['name'];
             $path = ltrim($path, '/');
         }
     }
     return $path;
 }
Example #7
0
 /**
  * @param string $filePath
  * @param \Sabre\DAV\INode $node
  * @throws \Sabre\DAV\Exception\BadRequest
  */
 public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null)
 {
     // chunked upload handling
     if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
         list($path, $name) = \Sabre\DAV\URLUtil::splitPath($filePath);
         $info = OC_FileChunking::decodeName($name);
         if (!empty($info)) {
             $filePath = $path . '/' . $info['name'];
         }
     }
     // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
     if (!$this->server->tree->nodeExists($filePath)) {
         return;
     }
     $node = $this->server->tree->getNodeForPath($filePath);
     if ($node instanceof OC_Connector_Sabre_Node) {
         $fileId = $node->getFileId();
         if (!is_null($fileId)) {
             $this->server->httpResponse->setHeader('OC-FileId', $fileId);
         }
     }
 }
Example #8
0
 /**
  * Creates a new file in the directory
  *
  * Data will either be supplied as a stream resource, or in certain cases
  * as a string. Keep in mind that you may have to support either.
  *
  * After succesful creation of the file, you may choose to return the ETag
  * of the new file here.
  *
  * The returned ETag must be surrounded by double-quotes (The quotes should
  * be part of the actual string).
  *
  * If you cannot accurately determine the ETag, you should not return it.
  * If you don't store the file exactly as-is (you're transforming it
  * somehow) you should also not return an ETag.
  *
  * This means that if a subsequent GET to this new file does not exactly
  * return the same contents of what was submitted here, you are strongly
  * recommended to omit the ETag.
  *
  * @param string $name Name of the file
  * @param resource|string $data Initial payload
  * @throws Sabre_DAV_Exception_Forbidden
  * @return null|string
  */
 public function createFile($name, $data = null)
 {
     if ($name === 'Shared' && empty($this->path)) {
         throw new \Sabre_DAV_Exception_Forbidden();
     }
     // for chunked upload also updating a existing file is a "createFile"
     // because we create all the chunks before reasamble them to the existing file.
     if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
         // exit if we can't create a new file and we don't updatable existing file
         $info = OC_FileChunking::decodeName($name);
         if (!\OC\Files\Filesystem::isCreatable($this->path) && !\OC\Files\Filesystem::isUpdatable($this->path . '/' . $info['name'])) {
             throw new \Sabre_DAV_Exception_Forbidden();
         }
     } else {
         // For non-chunked upload it is enough to check if we can create a new file
         if (!\OC\Files\Filesystem::isCreatable($this->path)) {
             throw new \Sabre_DAV_Exception_Forbidden();
         }
     }
     $path = $this->path . '/' . $name;
     $node = new OC_Connector_Sabre_File($path);
     return $node->put($data);
 }
Example #9
0
	/**
	 * @param resource $data
	 * @return null|string
	 * @throws Exception
	 * @throws BadRequest
	 * @throws NotImplemented
	 * @throws ServiceUnavailable
	 */
	private function createFileChunked($data) {
		list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path);

		$info = \OC_FileChunking::decodeName($name);
		if (empty($info)) {
			throw new NotImplemented('Invalid chunk name');
		}
		$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) {
					$chunk_handler->remove($info['index']);
					throw new BadRequest(
						'expected filesize ' . $expected . ' got ' . $bytesWritten);
				}
			}
		}

		if ($chunk_handler->isComplete()) {
			list($storage,) = $this->fileView->resolvePath($path);
			$needsPartFile = $this->needsPartFile($storage);
			$partFile = null;

			$targetPath = $path . '/' . $info['name'];
			/** @var \OC\Files\Storage\Storage $targetStorage */
			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);

			$exists = $this->fileView->file_exists($targetPath);

			try {
				$this->emitPreHooks($exists, $targetPath);

				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);

				if ($needsPartFile) {
					// we first assembly the target file as a part file
					$partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
					/** @var \OC\Files\Storage\Storage $targetStorage */
					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);


					$chunk_handler->file_assemble($partStorage, $partInternalPath, $this->fileView->getAbsolutePath($targetPath));

					// here is the final atomic rename
					$renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);

					$fileExists = $this->fileView->file_exists($targetPath);
					if ($renameOkay === false || $fileExists === false) {
						\OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR);
						// only delete if an error occurred and the target file was already created
						if ($fileExists) {
							// set to null to avoid double-deletion when handling exception
							// stray part file
							$partFile = null;
							$targetStorage->unlink($targetInternalPath);
						}
						$this->changeLock(ILockingProvider::LOCK_SHARED);
						throw new Exception('Could not rename part file assembled from chunks');
					}
				} else {
					// assemble directly into the final file
					$chunk_handler->file_assemble($targetStorage, $targetInternalPath, $this->fileView->getAbsolutePath($targetPath));
				}

				// allow sync clients to send the mtime along in a header
				$request = \OC::$server->getRequest();
				if (isset($request->server['HTTP_X_OC_MTIME'])) {
					if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) {
						header('X-OC-MTime: accepted');
					}
				}

				// since we skipped the view we need to scan and emit the hooks ourselves
				$this->fileView->getUpdater()->update($targetPath);

				$this->changeLock(ILockingProvider::LOCK_SHARED);

				$this->emitPostHooks($exists, $targetPath);

				$info = $this->fileView->getFileInfo($targetPath);
				return $info->getEtag();
			} catch (\Exception $e) {
				if ($partFile !== null) {
					$targetStorage->unlink($targetInternalPath);
				}
				$this->convertToSabreException($e);
			}
		}

		return null;
	}
Example #10
0
 /**
  * @param resource $data
  * @return null|string
  */
 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) {
                 $chunk_handler->remove($info['index']);
                 throw new \Sabre\DAV\Exception\BadRequest('expected filesize ' . $expected . ' got ' . $bytesWritten);
             }
         }
     }
     if ($chunk_handler->isComplete()) {
         // we first assembly the target file as a part file
         $partFile = $path . '/' . $info['name'] . '.ocTransferId' . $info['transferid'] . '.part';
         $chunk_handler->file_assemble($partFile);
         // here is the final atomic rename
         $targetPath = $path . '/' . $info['name'];
         $renameOkay = $this->fileView->rename($partFile, $targetPath);
         $fileExists = $this->fileView->file_exists($targetPath);
         if ($renameOkay === false || $fileExists === false) {
             \OC_Log::write('webdav', '\\OC\\Files\\Filesystem::rename() failed', \OC_Log::ERROR);
             // only delete if an error occurred and the target file was already created
             if ($fileExists) {
                 $this->fileView->unlink($targetPath);
             }
             throw new \Sabre\DAV\Exception('Could not rename part file assembled from chunks');
         }
         // allow sync clients to send the mtime along in a header
         $mtime = OC_Request::hasModificationTime();
         if ($mtime !== false) {
             if ($this->fileView->touch($targetPath, $mtime)) {
                 header('X-OC-MTime: accepted');
             }
         }
         $info = $this->fileView->getFileInfo($targetPath);
         return $info->getEtag();
     }
     return null;
 }
Example #11
0
 /**
  * This method is called before any HTTP method and validates there is enough free space to store the file
  *
  * @param string $uri
  * @param null $data
  * @throws \Sabre\DAV\Exception\InsufficientStorage
  * @return bool
  */
 public function checkQuota($uri, $data = null)
 {
     $length = $this->getLength();
     if ($length) {
         if (substr($uri, 0, 1) !== '/') {
             $uri = '/' . $uri;
         }
         list($parentUri, $newName) = \Sabre\HTTP\URLUtil::splitPath($uri);
         if (is_null($parentUri)) {
             $parentUri = '';
         }
         $req = $this->server->httpRequest;
         if ($req->getHeader('OC-Chunked')) {
             $info = \OC_FileChunking::decodeName($newName);
             $chunkHandler = $this->getFileChunking($info);
             // subtract the already uploaded size to see whether
             // there is still enough space for the remaining chunks
             $length -= $chunkHandler->getCurrentSize();
             // use target file name for free space check in case of shared files
             $uri = rtrim($parentUri, '/') . '/' . $info['name'];
         }
         $freeSpace = $this->getFreeSpace($uri);
         if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN && $length > $freeSpace) {
             if (isset($chunkHandler)) {
                 $chunkHandler->cleanup();
             }
             throw new \Sabre\DAV\Exception\InsufficientStorage();
         }
     }
     return true;
 }
Example #12
0
        header('WWW-Authenticate: Basic realm="ownCloud Server"');
        header('HTTP/1.0 401 Unauthorized');
        echo 'Valid credentials must be supplied';
        exit;
    } else {
        if (!OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])) {
            exit;
        }
    }
}
list($type, $file) = explode('/', substr($path_info, 1 + strlen($service) + 1), 2);
if ($type != 'oc_chunked') {
    OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
    die;
}
if (!OC_Filesystem::is_file($file)) {
    OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
    die;
}
switch ($_SERVER['REQUEST_METHOD']) {
    case 'PUT':
        $input = fopen("php://input", "r");
        $org_file = OC_Filesystem::fopen($file, 'rb');
        $info = array('name' => basename($file));
        $sync = new OC_FileChunking($info);
        $result = $sync->signature_split($org_file, $input);
        echo json_encode($result);
        break;
    default:
        OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
}
Example #13
0
 /**
  * @param resource $data
  * @return null|string
  * @throws Exception
  * @throws BadRequest
  * @throws NotImplemented
  * @throws ServiceUnavailable
  */
 private function createFileChunked($data)
 {
     list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path);
     $info = \OC_FileChunking::decodeName($name);
     if (empty($info)) {
         throw new 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) {
                 $chunk_handler->remove($info['index']);
                 throw new BadRequest('expected filesize ' . $expected . ' got ' . $bytesWritten);
             }
         }
     }
     if ($chunk_handler->isComplete()) {
         list($storage, ) = $this->fileView->resolvePath($path);
         $needsPartFile = $this->needsPartFile($storage);
         try {
             $targetPath = $path . '/' . $info['name'];
             if ($needsPartFile) {
                 // we first assembly the target file as a part file
                 $partFile = $path . '/' . $info['name'] . '.ocTransferId' . $info['transferid'] . '.part';
                 $chunk_handler->file_assemble($partFile);
                 // here is the final atomic rename
                 $renameOkay = $this->fileView->rename($partFile, $targetPath);
                 $fileExists = $this->fileView->file_exists($targetPath);
                 if ($renameOkay === false || $fileExists === false) {
                     \OC_Log::write('webdav', '\\OC\\Files\\Filesystem::rename() failed', \OC_Log::ERROR);
                     // only delete if an error occurred and the target file was already created
                     if ($fileExists) {
                         $this->fileView->unlink($targetPath);
                     }
                     throw new Exception('Could not rename part file assembled from chunks');
                 }
             } else {
                 // assemble directly into the final file
                 $chunk_handler->file_assemble($targetPath);
             }
             // allow sync clients to send the mtime along in a header
             $request = \OC::$server->getRequest();
             if (isset($request->server['HTTP_X_OC_MTIME'])) {
                 if ($this->fileView->touch($targetPath, $request->server['HTTP_X_OC_MTIME'])) {
                     header('X-OC-MTime: accepted');
                 }
             }
             $info = $this->fileView->getFileInfo($targetPath);
             return $info->getEtag();
         } catch (StorageNotAvailableException $e) {
             throw new ServiceUnavailable("Failed to put file: " . $e->getMessage());
         }
     }
     return null;
 }