/** * 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; }
/** * 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; }
/** * @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; }
/** * @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; }
/** * @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; }