Blame view

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