Blame view
sources/lib/private/connector/sabre/file.php
8.59 KB
|
03e52840d
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php /** * 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/>. * */ |
|
6d9380f96
|
23 |
class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\DAV\IFile {
|
|
03e52840d
|
24 25 26 27 28 29 |
/** * Updates the data * * The data argument is a readable stream resource. * |
|
31b7f2792
|
30 |
* After a successful put operation, you may choose to return an ETag. The |
|
03e52840d
|
31 32 33 34 35 36 37 38 39 40 41 42 |
* 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. * * @param resource $data |
|
6d9380f96
|
43 44 45 46 47 48 |
* @throws \Sabre\DAV\Exception\Forbidden * @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 |
|
03e52840d
|
49 50 51 |
* @return string|null
*/
public function put($data) {
|
|
6d9380f96
|
52 53 54 |
if ($this->info && $this->fileView->file_exists($this->path) &&
!$this->info->isUpdateable()) {
throw new \Sabre\DAV\Exception\Forbidden();
|
|
03e52840d
|
55 |
} |
|
31b7f2792
|
56 57 |
// throw an exception if encryption was disabled but the files are still encrypted
if (\OC_Util::encryptedFiles()) {
|
|
6d9380f96
|
58 59 60 61 62 63 |
throw new \Sabre\DAV\Exception\ServiceUnavailable();
}
$fileName = basename($this->path);
if (!\OCP\Util::isValidFileName($fileName)) {
throw new \Sabre\DAV\Exception\BadRequest();
|
|
31b7f2792
|
64 65 66 67 68 69 |
}
// chunked handling
if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
return $this->createFileChunked($data);
}
|
|
03e52840d
|
70 |
// mark file as partial while uploading (ignored by the scanner) |
|
6d9380f96
|
71 |
$partFilePath = $this->path . '.ocTransferId' . rand() . '.part'; |
|
03e52840d
|
72 |
|
|
31b7f2792
|
73 |
try {
|
|
6d9380f96
|
74 |
$putOkay = $this->fileView->file_put_contents($partFilePath, $data); |
|
31b7f2792
|
75 76 |
if ($putOkay === false) {
\OC_Log::write('webdav', '\OC\Files\Filesystem::file_put_contents() failed', \OC_Log::ERROR);
|
|
6d9380f96
|
77 |
$this->fileView->unlink($partFilePath); |
|
31b7f2792
|
78 |
// because we have no clue about the cause we can only throw back a 500/Internal Server Error |
|
6d9380f96
|
79 |
throw new \Sabre\DAV\Exception('Could not write file contents');
|
|
03e52840d
|
80 |
} |
|
31b7f2792
|
81 82 |
} catch (\OCP\Files\NotPermittedException $e) {
// a more general case - due to whatever reason the content could not be written
|
|
6d9380f96
|
83 |
throw new \Sabre\DAV\Exception\Forbidden($e->getMessage()); |
|
31b7f2792
|
84 85 86 87 88 89 90 91 92 93 94 95 |
} catch (\OCP\Files\EntityTooLargeException $e) {
// the file is too big to be stored
throw new OC_Connector_Sabre_Exception_EntityTooLarge($e->getMessage());
} catch (\OCP\Files\InvalidContentException $e) {
// the file content is not permitted
throw new OC_Connector_Sabre_Exception_UnsupportedMediaType($e->getMessage());
} catch (\OCP\Files\InvalidPathException $e) {
// the path for the file was not valid
// TODO: find proper http status code for this case
|
|
6d9380f96
|
96 97 98 99 100 101 102 103 104 105 106 107 108 |
throw new \Sabre\DAV\Exception\Forbidden($e->getMessage());
} 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);
}
// double check if the file was fully received
// compare expected and actual size
$expected = $_SERVER['CONTENT_LENGTH'];
$actual = $this->fileView->filesize($partFilePath);
if ($actual != $expected) {
$this->fileView->unlink($partFilePath);
throw new \Sabre\DAV\Exception\BadRequest('expected filesize ' . $expected . ' got ' . $actual);
|
|
03e52840d
|
109 110 111 |
} // rename to correct path |
|
6d9380f96
|
112 113 114 115 116 117 118 119 120 121 122 123 |
try {
$renameOkay = $this->fileView->rename($partFilePath, $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($partFilePath);
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);
|
|
03e52840d
|
124 |
} |
|
31b7f2792
|
125 |
// allow sync clients to send the mtime along in a header |
|
03e52840d
|
126 127 |
$mtime = OC_Request::hasModificationTime();
if ($mtime !== false) {
|
|
6d9380f96
|
128 |
if($this->fileView->touch($this->path, $mtime)) {
|
|
03e52840d
|
129 130 131 |
header('X-OC-MTime: accepted');
}
}
|
|
6d9380f96
|
132 |
$this->refreshInfo(); |
|
03e52840d
|
133 |
|
|
6d9380f96
|
134 |
return '"' . $this->info->getEtag() . '"'; |
|
03e52840d
|
135 136 137 138 139 |
} /** * Returns the data * |
|
6d9380f96
|
140 |
* @return string|resource |
|
03e52840d
|
141 142 |
*/
public function get() {
|
|
31b7f2792
|
143 144 |
//throw exception if encryption is disabled but files are still encrypted
if (\OC_Util::encryptedFiles()) {
|
|
6d9380f96
|
145 |
throw new \Sabre\DAV\Exception\ServiceUnavailable(); |
|
31b7f2792
|
146 |
} else {
|
|
6d9380f96
|
147 |
return $this->fileView->fopen(ltrim($this->path, '/'), 'rb'); |
|
31b7f2792
|
148 |
} |
|
03e52840d
|
149 150 151 152 153 154 155 |
} /** * Delete the current file * * @return void |
|
6d9380f96
|
156 |
* @throws \Sabre\DAV\Exception\Forbidden |
|
03e52840d
|
157 158 |
*/
public function delete() {
|
|
6d9380f96
|
159 160 |
if (!$this->info->isDeletable()) {
throw new \Sabre\DAV\Exception\Forbidden();
|
|
03e52840d
|
161 |
} |
|
6d9380f96
|
162 |
$this->fileView->unlink($this->path); |
|
03e52840d
|
163 |
|
|
31b7f2792
|
164 165 |
// remove properties $this->removeProperties(); |
|
03e52840d
|
166 167 168 169 170 |
} /** * Returns the size of the node, in bytes * |
|
6d9380f96
|
171 |
* @return int|float |
|
03e52840d
|
172 173 |
*/
public function getSize() {
|
|
6d9380f96
|
174 |
return $this->info->getSize(); |
|
03e52840d
|
175 176 177 178 179 180 181 |
} /** * Returns the ETag for a file * * 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 |
|
31b7f2792
|
182 |
* arbitrary string, but MUST be surrounded by double-quotes. |
|
03e52840d
|
183 184 185 186 187 188 |
*
* Return null if the ETag can not effectively be determined
*
* @return mixed
*/
public function getETag() {
|
|
6d9380f96
|
189 |
return '"' . $this->info->getEtag() . '"'; |
|
03e52840d
|
190 191 192 193 194 195 196 197 198 199 |
}
/**
* Returns the mime-type for a file
*
* If null is returned, we'll assume application/octet-stream
*
* @return mixed
*/
public function getContentType() {
|
|
6d9380f96
|
200 |
$mimeType = $this->info->getMimetype(); |
|
03e52840d
|
201 |
|
|
837968727
|
202 |
return \OC_Helper::getSecureMimeType($mimeType); |
|
03e52840d
|
203 |
} |
|
31b7f2792
|
204 |
|
|
6d9380f96
|
205 206 207 208 |
/** * @param resource $data * @return null|string */ |
|
31b7f2792
|
209 210 |
private function createFileChunked($data)
{
|
|
6d9380f96
|
211 |
list($path, $name) = \Sabre\DAV\URLUtil::splitPath($this->path); |
|
31b7f2792
|
212 213 214 |
$info = OC_FileChunking::decodeName($name);
if (empty($info)) {
|
|
6d9380f96
|
215 |
throw new \Sabre\DAV\Exception\NotImplemented(); |
|
31b7f2792
|
216 217 218 219 220 221 222 223 224 225 |
}
$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']);
|
|
6d9380f96
|
226 |
throw new \Sabre\DAV\Exception\BadRequest( |
|
31b7f2792
|
227 228 229 230 231 232 233 234 235 236 237 238 |
'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
|
|
31b7f2792
|
239 |
$targetPath = $path . '/' . $info['name']; |
|
6d9380f96
|
240 241 |
$renameOkay = $this->fileView->rename($partFile, $targetPath); $fileExists = $this->fileView->file_exists($targetPath); |
|
31b7f2792
|
242 243 |
if ($renameOkay === false || $fileExists === false) {
\OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR);
|
|
a293d369c
|
244 245 |
// only delete if an error occurred and the target file was already created
if ($fileExists) {
|
|
6d9380f96
|
246 |
$this->fileView->unlink($targetPath); |
|
a293d369c
|
247 |
} |
|
6d9380f96
|
248 |
throw new \Sabre\DAV\Exception('Could not rename part file assembled from chunks');
|
|
31b7f2792
|
249 250 251 252 253 |
}
// allow sync clients to send the mtime along in a header
$mtime = OC_Request::hasModificationTime();
if ($mtime !== false) {
|
|
6d9380f96
|
254 |
if($this->fileView->touch($targetPath, $mtime)) {
|
|
31b7f2792
|
255 256 257 |
header('X-OC-MTime: accepted');
}
}
|
|
6d9380f96
|
258 259 |
$info = $this->fileView->getFileInfo($targetPath); return $info->getEtag(); |
|
31b7f2792
|
260 261 262 263 |
} return null; } |
|
03e52840d
|
264 |
} |