Completed
Pull Request — master (#4023)
by Robin
25:06 queued 10:31
created
apps/dav/lib/Connector/Sabre/File.php 3 patches
Doc Comments   +4 added lines, -1 removed lines patch added patch discarded remove patch
@@ -245,6 +245,9 @@  discard block
 block discarded – undo
245 245
 		return '"' . $this->info->getEtag() . '"';
246 246
 	}
247 247
 
248
+	/**
249
+	 * @param string $path
250
+	 */
248 251
 	private function getPartFileBasePath($path) {
249 252
 		$partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true);
250 253
 		if ($partFileInStorage) {
@@ -607,7 +610,7 @@  discard block
 block discarded – undo
607 610
 	/**
608 611
 	 * Set $algo to get a specific checksum, leave null to get all checksums
609 612
 	 * (space seperated)
610
-	 * @param null $algo
613
+	 * @param string $algo
611 614
 	 * @return string
612 615
 	 */
613 616
 	public function getChecksum($algo = null) {
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -93,7 +93,7 @@  discard block
 block discarded – undo
93 93
 				throw new Forbidden();
94 94
 			}
95 95
 		} catch (StorageNotAvailableException $e) {
96
-			throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
96
+			throw new ServiceUnavailable("File is not updatable: ".$e->getMessage());
97 97
 		}
98 98
 
99 99
 		// verify path of the target
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
 
114 114
 		if ($needsPartFile) {
115 115
 			// mark file as partial while uploading (ignored by the scanner)
116
-			$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
116
+			$partFilePath = $this->getPartFileBasePath($this->path).'.ocTransferId'.rand().'.part';
117 117
 		} else {
118 118
 			// upload file directly as the final path
119 119
 			$partFilePath = $this->path;
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
 				if (isset($_SERVER['CONTENT_LENGTH'])) {
144 144
 					$expected = $_SERVER['CONTENT_LENGTH'];
145 145
 				}
146
-				throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
146
+				throw new Exception('Error while copying file to target location (copied bytes: '.$count.', expected filesize: '.$expected.' )');
147 147
 			}
148 148
 
149 149
 			// if content length is sent by client:
@@ -152,7 +152,7 @@  discard block
 block discarded – undo
152 152
 			if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
153 153
 				$expected = $_SERVER['CONTENT_LENGTH'];
154 154
 				if ($count != $expected) {
155
-					throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
155
+					throw new BadRequest('expected filesize '.$expected.' got '.$count);
156 156
 				}
157 157
 			}
158 158
 
@@ -239,10 +239,10 @@  discard block
 block discarded – undo
239 239
 			$this->refreshInfo();
240 240
 
241 241
 		} catch (StorageNotAvailableException $e) {
242
-			throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
242
+			throw new ServiceUnavailable("Failed to check file size: ".$e->getMessage());
243 243
 		}
244 244
 
245
-		return '"' . $this->info->getEtag() . '"';
245
+		return '"'.$this->info->getEtag().'"';
246 246
 	}
247 247
 
248 248
 	private function getPartFileBasePath($path) {
@@ -321,9 +321,9 @@  discard block
 block discarded – undo
321 321
 			return $res;
322 322
 		} catch (GenericEncryptionException $e) {
323 323
 			// returning 503 will allow retry of the operation at a later point in time
324
-			throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
324
+			throw new ServiceUnavailable("Encryption not ready: ".$e->getMessage());
325 325
 		} catch (StorageNotAvailableException $e) {
326
-			throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
326
+			throw new ServiceUnavailable("Failed to open file: ".$e->getMessage());
327 327
 		} catch (ForbiddenException $ex) {
328 328
 			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
329 329
 		} catch (LockedException $e) {
@@ -348,7 +348,7 @@  discard block
 block discarded – undo
348 348
 				throw new Forbidden();
349 349
 			}
350 350
 		} catch (StorageNotAvailableException $e) {
351
-			throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
351
+			throw new ServiceUnavailable("Failed to unlink: ".$e->getMessage());
352 352
 		} catch (ForbiddenException $ex) {
353 353
 			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
354 354
 		} catch (LockedException $e) {
@@ -415,7 +415,7 @@  discard block
 block discarded – undo
415 415
 				if ($bytesWritten != $expected) {
416 416
 					$chunk_handler->remove($info['index']);
417 417
 					throw new BadRequest(
418
-						'expected filesize ' . $expected . ' got ' . $bytesWritten);
418
+						'expected filesize '.$expected.' got '.$bytesWritten);
419 419
 				}
420 420
 			}
421 421
 		}
@@ -425,7 +425,7 @@  discard block
 block discarded – undo
425 425
 			$needsPartFile = $this->needsPartFile($storage);
426 426
 			$partFile = null;
427 427
 
428
-			$targetPath = $path . '/' . $info['name'];
428
+			$targetPath = $path.'/'.$info['name'];
429 429
 			/** @var \OC\Files\Storage\Storage $targetStorage */
430 430
 			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
431 431
 
@@ -441,7 +441,7 @@  discard block
 block discarded – undo
441 441
 
442 442
 				if ($needsPartFile) {
443 443
 					// we first assembly the target file as a part file
444
-					$partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
444
+					$partFile = $this->getPartFileBasePath($path.'/'.$info['name']).'.ocTransferId'.$info['transferid'].'.part';
445 445
 					/** @var \OC\Files\Storage\Storage $targetStorage */
446 446
 					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
447 447
 
@@ -594,10 +594,10 @@  discard block
 block discarded – undo
594 594
 		}
595 595
 		if ($e instanceof GenericEncryptionException) {
596 596
 			// returning 503 will allow retry of the operation at a later point in time
597
-			throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
597
+			throw new ServiceUnavailable('Encryption not ready: '.$e->getMessage(), 0, $e);
598 598
 		}
599 599
 		if ($e instanceof StorageNotAvailableException) {
600
-			throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
600
+			throw new ServiceUnavailable('Failed to write file contents: '.$e->getMessage(), 0, $e);
601 601
 		}
602 602
 
603 603
 		throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
@@ -618,7 +618,7 @@  discard block
 block discarded – undo
618 618
 		}
619 619
 
620 620
 		$checksums = explode(' ', $allChecksums);
621
-		$algoPrefix = strtoupper($algo) . ':';
621
+		$algoPrefix = strtoupper($algo).':';
622 622
 
623 623
 		foreach ($checksums as $checksum) {
624 624
 			// starts with $algoPrefix
Please login to merge, or discard this patch.
Indentation   +570 added lines, -570 removed lines patch added patch discarded remove patch
@@ -58,575 +58,575 @@
 block discarded – undo
58 58
 
59 59
 class File extends Node implements IFile {
60 60
 
61
-	/**
62
-	 * Updates the data
63
-	 *
64
-	 * The data argument is a readable stream resource.
65
-	 *
66
-	 * After a successful put operation, you may choose to return an ETag. The
67
-	 * etag must always be surrounded by double-quotes. These quotes must
68
-	 * appear in the actual string you're returning.
69
-	 *
70
-	 * Clients may use the ETag from a PUT request to later on make sure that
71
-	 * when they update the file, the contents haven't changed in the mean
72
-	 * time.
73
-	 *
74
-	 * If you don't plan to store the file byte-by-byte, and you return a
75
-	 * different object on a subsequent GET you are strongly recommended to not
76
-	 * return an ETag, and just return null.
77
-	 *
78
-	 * @param resource $data
79
-	 *
80
-	 * @throws Forbidden
81
-	 * @throws UnsupportedMediaType
82
-	 * @throws BadRequest
83
-	 * @throws Exception
84
-	 * @throws EntityTooLarge
85
-	 * @throws ServiceUnavailable
86
-	 * @throws FileLocked
87
-	 * @return string|null
88
-	 */
89
-	public function put($data) {
90
-		try {
91
-			$exists = $this->fileView->file_exists($this->path);
92
-			if ($this->info && $exists && !$this->info->isUpdateable()) {
93
-				throw new Forbidden();
94
-			}
95
-		} catch (StorageNotAvailableException $e) {
96
-			throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
97
-		}
98
-
99
-		// verify path of the target
100
-		$this->verifyPath();
101
-
102
-		// chunked handling
103
-		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
104
-			try {
105
-				return $this->createFileChunked($data);
106
-			} catch (\Exception $e) {
107
-				$this->convertToSabreException($e);
108
-			}
109
-		}
110
-
111
-		list($partStorage) = $this->fileView->resolvePath($this->path);
112
-		$needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1);
113
-
114
-		if ($needsPartFile) {
115
-			// mark file as partial while uploading (ignored by the scanner)
116
-			$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
117
-		} else {
118
-			// upload file directly as the final path
119
-			$partFilePath = $this->path;
120
-		}
121
-
122
-		// the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
123
-		/** @var \OC\Files\Storage\Storage $partStorage */
124
-		list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
125
-		/** @var \OC\Files\Storage\Storage $storage */
126
-		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
127
-		try {
128
-			$target = $partStorage->fopen($internalPartPath, 'wb');
129
-			if ($target === false) {
130
-				\OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::fopen() failed', \OCP\Util::ERROR);
131
-				// because we have no clue about the cause we can only throw back a 500/Internal Server Error
132
-				throw new Exception('Could not write file contents');
133
-			}
134
-			list($count, $result) = \OC_Helper::streamCopy($data, $target);
135
-			fclose($target);
136
-
137
-			if (!self::isChecksumValid($partStorage, $internalPartPath)) {
138
-				throw new BadRequest('The computed checksum does not match the one received from the client.');
139
-			}
140
-
141
-			if ($result === false) {
142
-				$expected = -1;
143
-				if (isset($_SERVER['CONTENT_LENGTH'])) {
144
-					$expected = $_SERVER['CONTENT_LENGTH'];
145
-				}
146
-				throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
147
-			}
148
-
149
-			// if content length is sent by client:
150
-			// double check if the file was fully received
151
-			// compare expected and actual size
152
-			if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
153
-				$expected = $_SERVER['CONTENT_LENGTH'];
154
-				if ($count != $expected) {
155
-					throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
156
-				}
157
-			}
158
-
159
-		} catch (\Exception $e) {
160
-			if ($needsPartFile) {
161
-				$partStorage->unlink($internalPartPath);
162
-			}
163
-			$this->convertToSabreException($e);
164
-		}
165
-
166
-		try {
167
-			$view = \OC\Files\Filesystem::getView();
168
-			if ($view) {
169
-				$run = $this->emitPreHooks($exists);
170
-			} else {
171
-				$run = true;
172
-			}
173
-
174
-			try {
175
-				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
176
-			} catch (LockedException $e) {
177
-				if ($needsPartFile) {
178
-					$partStorage->unlink($internalPartPath);
179
-				}
180
-				throw new FileLocked($e->getMessage(), $e->getCode(), $e);
181
-			}
182
-
183
-			if ($needsPartFile) {
184
-				// rename to correct path
185
-				try {
186
-					if ($run) {
187
-						$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
188
-						$fileExists = $storage->file_exists($internalPath);
189
-					}
190
-					if (!$run || $renameOkay === false || $fileExists === false) {
191
-						\OCP\Util::writeLog('webdav', 'renaming part file to final file failed', \OCP\Util::ERROR);
192
-						throw new Exception('Could not rename part file to final file');
193
-					}
194
-				} catch (ForbiddenException $ex) {
195
-					throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
196
-				} catch (\Exception $e) {
197
-					$partStorage->unlink($internalPartPath);
198
-					$this->convertToSabreException($e);
199
-				}
200
-			}
201
-
202
-			// since we skipped the view we need to scan and emit the hooks ourselves
203
-			$storage->getUpdater()->update($internalPath);
204
-
205
-			try {
206
-				$this->changeLock(ILockingProvider::LOCK_SHARED);
207
-			} catch (LockedException $e) {
208
-				throw new FileLocked($e->getMessage(), $e->getCode(), $e);
209
-			}
210
-
211
-			// allow sync clients to send the mtime along in a header
212
-			$request = \OC::$server->getRequest();
213
-			if (isset($request->server['HTTP_X_OC_MTIME'])) {
214
-				$mtimeStr = $request->server['HTTP_X_OC_MTIME'];
215
-				if (!is_numeric($mtimeStr)) {
216
-					throw new \InvalidArgumentException('X-OC-Mtime header must be an integer (unix timestamp).');
217
-				}
218
-				$mtime = intval($mtimeStr);
219
-				if ($this->fileView->touch($this->path, $mtime)) {
220
-					header('X-OC-MTime: accepted');
221
-				}
222
-			}
61
+    /**
62
+     * Updates the data
63
+     *
64
+     * The data argument is a readable stream resource.
65
+     *
66
+     * After a successful put operation, you may choose to return an ETag. The
67
+     * etag must always be surrounded by double-quotes. These quotes must
68
+     * appear in the actual string you're returning.
69
+     *
70
+     * Clients may use the ETag from a PUT request to later on make sure that
71
+     * when they update the file, the contents haven't changed in the mean
72
+     * time.
73
+     *
74
+     * If you don't plan to store the file byte-by-byte, and you return a
75
+     * different object on a subsequent GET you are strongly recommended to not
76
+     * return an ETag, and just return null.
77
+     *
78
+     * @param resource $data
79
+     *
80
+     * @throws Forbidden
81
+     * @throws UnsupportedMediaType
82
+     * @throws BadRequest
83
+     * @throws Exception
84
+     * @throws EntityTooLarge
85
+     * @throws ServiceUnavailable
86
+     * @throws FileLocked
87
+     * @return string|null
88
+     */
89
+    public function put($data) {
90
+        try {
91
+            $exists = $this->fileView->file_exists($this->path);
92
+            if ($this->info && $exists && !$this->info->isUpdateable()) {
93
+                throw new Forbidden();
94
+            }
95
+        } catch (StorageNotAvailableException $e) {
96
+            throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
97
+        }
98
+
99
+        // verify path of the target
100
+        $this->verifyPath();
101
+
102
+        // chunked handling
103
+        if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
104
+            try {
105
+                return $this->createFileChunked($data);
106
+            } catch (\Exception $e) {
107
+                $this->convertToSabreException($e);
108
+            }
109
+        }
110
+
111
+        list($partStorage) = $this->fileView->resolvePath($this->path);
112
+        $needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1);
113
+
114
+        if ($needsPartFile) {
115
+            // mark file as partial while uploading (ignored by the scanner)
116
+            $partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
117
+        } else {
118
+            // upload file directly as the final path
119
+            $partFilePath = $this->path;
120
+        }
121
+
122
+        // the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
123
+        /** @var \OC\Files\Storage\Storage $partStorage */
124
+        list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
125
+        /** @var \OC\Files\Storage\Storage $storage */
126
+        list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
127
+        try {
128
+            $target = $partStorage->fopen($internalPartPath, 'wb');
129
+            if ($target === false) {
130
+                \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::fopen() failed', \OCP\Util::ERROR);
131
+                // because we have no clue about the cause we can only throw back a 500/Internal Server Error
132
+                throw new Exception('Could not write file contents');
133
+            }
134
+            list($count, $result) = \OC_Helper::streamCopy($data, $target);
135
+            fclose($target);
136
+
137
+            if (!self::isChecksumValid($partStorage, $internalPartPath)) {
138
+                throw new BadRequest('The computed checksum does not match the one received from the client.');
139
+            }
140
+
141
+            if ($result === false) {
142
+                $expected = -1;
143
+                if (isset($_SERVER['CONTENT_LENGTH'])) {
144
+                    $expected = $_SERVER['CONTENT_LENGTH'];
145
+                }
146
+                throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
147
+            }
148
+
149
+            // if content length is sent by client:
150
+            // double check if the file was fully received
151
+            // compare expected and actual size
152
+            if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
153
+                $expected = $_SERVER['CONTENT_LENGTH'];
154
+                if ($count != $expected) {
155
+                    throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
156
+                }
157
+            }
158
+
159
+        } catch (\Exception $e) {
160
+            if ($needsPartFile) {
161
+                $partStorage->unlink($internalPartPath);
162
+            }
163
+            $this->convertToSabreException($e);
164
+        }
165
+
166
+        try {
167
+            $view = \OC\Files\Filesystem::getView();
168
+            if ($view) {
169
+                $run = $this->emitPreHooks($exists);
170
+            } else {
171
+                $run = true;
172
+            }
173
+
174
+            try {
175
+                $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
176
+            } catch (LockedException $e) {
177
+                if ($needsPartFile) {
178
+                    $partStorage->unlink($internalPartPath);
179
+                }
180
+                throw new FileLocked($e->getMessage(), $e->getCode(), $e);
181
+            }
182
+
183
+            if ($needsPartFile) {
184
+                // rename to correct path
185
+                try {
186
+                    if ($run) {
187
+                        $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
188
+                        $fileExists = $storage->file_exists($internalPath);
189
+                    }
190
+                    if (!$run || $renameOkay === false || $fileExists === false) {
191
+                        \OCP\Util::writeLog('webdav', 'renaming part file to final file failed', \OCP\Util::ERROR);
192
+                        throw new Exception('Could not rename part file to final file');
193
+                    }
194
+                } catch (ForbiddenException $ex) {
195
+                    throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
196
+                } catch (\Exception $e) {
197
+                    $partStorage->unlink($internalPartPath);
198
+                    $this->convertToSabreException($e);
199
+                }
200
+            }
201
+
202
+            // since we skipped the view we need to scan and emit the hooks ourselves
203
+            $storage->getUpdater()->update($internalPath);
204
+
205
+            try {
206
+                $this->changeLock(ILockingProvider::LOCK_SHARED);
207
+            } catch (LockedException $e) {
208
+                throw new FileLocked($e->getMessage(), $e->getCode(), $e);
209
+            }
210
+
211
+            // allow sync clients to send the mtime along in a header
212
+            $request = \OC::$server->getRequest();
213
+            if (isset($request->server['HTTP_X_OC_MTIME'])) {
214
+                $mtimeStr = $request->server['HTTP_X_OC_MTIME'];
215
+                if (!is_numeric($mtimeStr)) {
216
+                    throw new \InvalidArgumentException('X-OC-Mtime header must be an integer (unix timestamp).');
217
+                }
218
+                $mtime = intval($mtimeStr);
219
+                if ($this->fileView->touch($this->path, $mtime)) {
220
+                    header('X-OC-MTime: accepted');
221
+                }
222
+            }
223 223
 					
224
-			if ($view) {
225
-				$this->emitPostHooks($exists);
226
-			}
227
-
228
-			$this->refreshInfo();
229
-
230
-			$meta = $partStorage->getCache()->get($internalPartPath);
231
-
232
-			if (isset($meta['checksum'])) {
233
-				$this->fileView->putFileInfo(
234
-					$this->path,
235
-					['checksum' => $meta['checksum']]
236
-				);
237
-			}
238
-
239
-			$this->refreshInfo();
240
-
241
-		} catch (StorageNotAvailableException $e) {
242
-			throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
243
-		}
244
-
245
-		return '"' . $this->info->getEtag() . '"';
246
-	}
247
-
248
-	private function getPartFileBasePath($path) {
249
-		$partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true);
250
-		if ($partFileInStorage) {
251
-			return $path;
252
-		} else {
253
-			return md5($path); // will place it in the root of the view with a unique name
254
-		}
255
-	}
256
-
257
-	/**
258
-	 * @param string $path
259
-	 */
260
-	private function emitPreHooks($exists, $path = null) {
261
-		if (is_null($path)) {
262
-			$path = $this->path;
263
-		}
264
-		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
265
-		$run = true;
266
-
267
-		if (!$exists) {
268
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(
269
-				\OC\Files\Filesystem::signal_param_path => $hookPath,
270
-				\OC\Files\Filesystem::signal_param_run => &$run,
271
-			));
272
-		} else {
273
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(
274
-				\OC\Files\Filesystem::signal_param_path => $hookPath,
275
-				\OC\Files\Filesystem::signal_param_run => &$run,
276
-			));
277
-		}
278
-		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(
279
-			\OC\Files\Filesystem::signal_param_path => $hookPath,
280
-			\OC\Files\Filesystem::signal_param_run => &$run,
281
-		));
282
-		return $run;
283
-	}
284
-
285
-	/**
286
-	 * @param string $path
287
-	 */
288
-	private function emitPostHooks($exists, $path = null) {
289
-		if (is_null($path)) {
290
-			$path = $this->path;
291
-		}
292
-		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
293
-		if (!$exists) {
294
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
295
-				\OC\Files\Filesystem::signal_param_path => $hookPath
296
-			));
297
-		} else {
298
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(
299
-				\OC\Files\Filesystem::signal_param_path => $hookPath
300
-			));
301
-		}
302
-		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(
303
-			\OC\Files\Filesystem::signal_param_path => $hookPath
304
-		));
305
-	}
306
-
307
-	/**
308
-	 * Returns the data
309
-	 *
310
-	 * @return resource
311
-	 * @throws Forbidden
312
-	 * @throws ServiceUnavailable
313
-	 */
314
-	public function get() {
315
-		//throw exception if encryption is disabled but files are still encrypted
316
-		try {
317
-			$res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb');
318
-			if ($res === false) {
319
-				throw new ServiceUnavailable("Could not open file");
320
-			}
321
-			return $res;
322
-		} catch (GenericEncryptionException $e) {
323
-			// returning 503 will allow retry of the operation at a later point in time
324
-			throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
325
-		} catch (StorageNotAvailableException $e) {
326
-			throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
327
-		} catch (ForbiddenException $ex) {
328
-			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
329
-		} catch (LockedException $e) {
330
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
331
-		}
332
-	}
333
-
334
-	/**
335
-	 * Delete the current file
336
-	 *
337
-	 * @throws Forbidden
338
-	 * @throws ServiceUnavailable
339
-	 */
340
-	public function delete() {
341
-		if (!$this->info->isDeletable()) {
342
-			throw new Forbidden();
343
-		}
344
-
345
-		try {
346
-			if (!$this->fileView->unlink($this->path)) {
347
-				// assume it wasn't possible to delete due to permissions
348
-				throw new Forbidden();
349
-			}
350
-		} catch (StorageNotAvailableException $e) {
351
-			throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
352
-		} catch (ForbiddenException $ex) {
353
-			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
354
-		} catch (LockedException $e) {
355
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
356
-		}
357
-	}
358
-
359
-	/**
360
-	 * Returns the mime-type for a file
361
-	 *
362
-	 * If null is returned, we'll assume application/octet-stream
363
-	 *
364
-	 * @return string
365
-	 */
366
-	public function getContentType() {
367
-		$mimeType = $this->info->getMimetype();
368
-
369
-		// PROPFIND needs to return the correct mime type, for consistency with the web UI
370
-		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
371
-			return $mimeType;
372
-		}
373
-		return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType);
374
-	}
375
-
376
-	/**
377
-	 * @return array|false
378
-	 */
379
-	public function getDirectDownload() {
380
-		if (\OCP\App::isEnabled('encryption')) {
381
-			return [];
382
-		}
383
-		/** @var \OCP\Files\Storage $storage */
384
-		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
385
-		if (is_null($storage)) {
386
-			return [];
387
-		}
388
-
389
-		return $storage->getDirectDownload($internalPath);
390
-	}
391
-
392
-	/**
393
-	 * @param resource $data
394
-	 * @return null|string
395
-	 * @throws Exception
396
-	 * @throws BadRequest
397
-	 * @throws NotImplemented
398
-	 * @throws ServiceUnavailable
399
-	 */
400
-	private function createFileChunked($data) {
401
-		list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path);
402
-
403
-		$info = \OC_FileChunking::decodeName($name);
404
-		if (empty($info)) {
405
-			throw new NotImplemented('Invalid chunk name');
406
-		}
407
-
408
-		$chunk_handler = new \OC_FileChunking($info);
409
-		$bytesWritten = $chunk_handler->store($info['index'], $data);
410
-
411
-		//detect aborted upload
412
-		if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
413
-			if (isset($_SERVER['CONTENT_LENGTH'])) {
414
-				$expected = $_SERVER['CONTENT_LENGTH'];
415
-				if ($bytesWritten != $expected) {
416
-					$chunk_handler->remove($info['index']);
417
-					throw new BadRequest(
418
-						'expected filesize ' . $expected . ' got ' . $bytesWritten);
419
-				}
420
-			}
421
-		}
422
-
423
-		if ($chunk_handler->isComplete()) {
424
-			list($storage,) = $this->fileView->resolvePath($path);
425
-			$needsPartFile = $this->needsPartFile($storage);
426
-			$partFile = null;
427
-
428
-			$targetPath = $path . '/' . $info['name'];
429
-			/** @var \OC\Files\Storage\Storage $targetStorage */
430
-			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
431
-
432
-			$exists = $this->fileView->file_exists($targetPath);
433
-
434
-			try {
435
-				$this->fileView->lockFile($targetPath, ILockingProvider::LOCK_SHARED);
436
-
437
-				$this->emitPreHooks($exists, $targetPath);
438
-				$this->fileView->changeLock($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
439
-				/** @var \OC\Files\Storage\Storage $targetStorage */
440
-				list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
441
-
442
-				if ($needsPartFile) {
443
-					// we first assembly the target file as a part file
444
-					$partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
445
-					/** @var \OC\Files\Storage\Storage $targetStorage */
446
-					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
447
-
448
-
449
-					$chunk_handler->file_assemble($partStorage, $partInternalPath);
450
-
451
-					if (!self::isChecksumValid($partStorage, $partInternalPath)) {
452
-						throw new BadRequest('The computed checksum does not match the one received from the client.');
453
-					}
454
-
455
-					// here is the final atomic rename
456
-					$renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);
457
-					$fileExists = $targetStorage->file_exists($targetInternalPath);
458
-					if ($renameOkay === false || $fileExists === false) {
459
-						\OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR);
460
-						// only delete if an error occurred and the target file was already created
461
-						if ($fileExists) {
462
-							// set to null to avoid double-deletion when handling exception
463
-							// stray part file
464
-							$partFile = null;
465
-							$targetStorage->unlink($targetInternalPath);
466
-						}
467
-						$this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
468
-						throw new Exception('Could not rename part file assembled from chunks');
469
-					}
470
-				} else {
471
-					// assemble directly into the final file
472
-					$chunk_handler->file_assemble($targetStorage, $targetInternalPath);
473
-				}
474
-
475
-				// allow sync clients to send the mtime along in a header
476
-				$request = \OC::$server->getRequest();
477
-				if (isset($request->server['HTTP_X_OC_MTIME'])) {
478
-					if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) {
479
-						header('X-OC-MTime: accepted');
480
-					}
481
-				}
482
-
483
-				// since we skipped the view we need to scan and emit the hooks ourselves
484
-				$targetStorage->getUpdater()->update($targetInternalPath);
485
-
486
-				$this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
487
-
488
-				$this->emitPostHooks($exists, $targetPath);
489
-
490
-				// FIXME: should call refreshInfo but can't because $this->path is not the of the final file
491
-				$info = $this->fileView->getFileInfo($targetPath);
492
-
493
-
494
-				if (isset($partStorage) && isset($partInternalPath)) {
495
-					$checksums = $partStorage->getCache()->get($partInternalPath)['checksum'];
496
-				} else {
497
-					$checksums = $targetStorage->getCache()->get($targetInternalPath)['checksum'];
498
-				}
499
-
500
-				$this->fileView->putFileInfo(
501
-					$targetPath,
502
-					['checksum' => $checksums]
503
-				);
504
-
505
-				$this->refreshInfo();
506
-
507
-				$this->fileView->unlockFile($targetPath, ILockingProvider::LOCK_SHARED);
508
-
509
-				return $info->getEtag();
510
-			} catch (\Exception $e) {
511
-				if ($partFile !== null) {
512
-					$targetStorage->unlink($targetInternalPath);
513
-				}
514
-				$this->convertToSabreException($e);
515
-			}
516
-		}
517
-
518
-		return null;
519
-	}
520
-
521
-	/**
522
-	 * will return true if checksum was not provided in request
523
-	 *
524
-	 * @param Storage $storage
525
-	 * @param $path
526
-	 * @return bool
527
-	 */
528
-	private static function isChecksumValid(Storage $storage, $path) {
529
-		$meta = $storage->getMetaData($path);
530
-		$request = \OC::$server->getRequest();
531
-
532
-		if (!isset($request->server['HTTP_OC_CHECKSUM']) || !isset($meta['checksum'])) {
533
-			// No comparison possible, skip the check
534
-			return true;
535
-		}
536
-
537
-		$expectedChecksum = trim($request->server['HTTP_OC_CHECKSUM']);
538
-		$computedChecksums = $meta['checksum'];
539
-
540
-		return strpos($computedChecksums, $expectedChecksum) !== false;
541
-
542
-	}
543
-
544
-	/**
545
-	 * Returns whether a part file is needed for the given storage
546
-	 * or whether the file can be assembled/uploaded directly on the
547
-	 * target storage.
548
-	 *
549
-	 * @param \OCP\Files\Storage $storage
550
-	 * @return bool true if the storage needs part file handling
551
-	 */
552
-	private function needsPartFile($storage) {
553
-		// TODO: in the future use ChunkHandler provided by storage
554
-		return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') &&
555
-			!$storage->instanceOfStorage('OC\Files\Storage\OwnCloud') &&
556
-			$storage->needsPartFile();
557
-	}
558
-
559
-	/**
560
-	 * Convert the given exception to a SabreException instance
561
-	 *
562
-	 * @param \Exception $e
563
-	 *
564
-	 * @throws \Sabre\DAV\Exception
565
-	 */
566
-	private function convertToSabreException(\Exception $e) {
567
-		if ($e instanceof \Sabre\DAV\Exception) {
568
-			throw $e;
569
-		}
570
-		if ($e instanceof NotPermittedException) {
571
-			// a more general case - due to whatever reason the content could not be written
572
-			throw new Forbidden($e->getMessage(), 0, $e);
573
-		}
574
-		if ($e instanceof ForbiddenException) {
575
-			// the path for the file was forbidden
576
-			throw new DAVForbiddenException($e->getMessage(), $e->getRetry(), $e);
577
-		}
578
-		if ($e instanceof EntityTooLargeException) {
579
-			// the file is too big to be stored
580
-			throw new EntityTooLarge($e->getMessage(), 0, $e);
581
-		}
582
-		if ($e instanceof InvalidContentException) {
583
-			// the file content is not permitted
584
-			throw new UnsupportedMediaType($e->getMessage(), 0, $e);
585
-		}
586
-		if ($e instanceof InvalidPathException) {
587
-			// the path for the file was not valid
588
-			// TODO: find proper http status code for this case
589
-			throw new Forbidden($e->getMessage(), 0, $e);
590
-		}
591
-		if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) {
592
-			// the file is currently being written to by another process
593
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
594
-		}
595
-		if ($e instanceof GenericEncryptionException) {
596
-			// returning 503 will allow retry of the operation at a later point in time
597
-			throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
598
-		}
599
-		if ($e instanceof StorageNotAvailableException) {
600
-			throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
601
-		}
602
-
603
-		throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
604
-	}
605
-
606
-
607
-	/**
608
-	 * Set $algo to get a specific checksum, leave null to get all checksums
609
-	 * (space seperated)
610
-	 * @param null $algo
611
-	 * @return string
612
-	 */
613
-	public function getChecksum($algo = null) {
614
-		$allChecksums = $this->info->getChecksum();
615
-
616
-		if (!$algo) {
617
-			return $allChecksums;
618
-		}
619
-
620
-		$checksums = explode(' ', $allChecksums);
621
-		$algoPrefix = strtoupper($algo) . ':';
622
-
623
-		foreach ($checksums as $checksum) {
624
-			// starts with $algoPrefix
625
-			if (substr($checksum, 0, strlen($algoPrefix)) === $algoPrefix) {
626
-				return $checksum;
627
-			}
628
-		}
629
-
630
-		return '';
631
-	}
224
+            if ($view) {
225
+                $this->emitPostHooks($exists);
226
+            }
227
+
228
+            $this->refreshInfo();
229
+
230
+            $meta = $partStorage->getCache()->get($internalPartPath);
231
+
232
+            if (isset($meta['checksum'])) {
233
+                $this->fileView->putFileInfo(
234
+                    $this->path,
235
+                    ['checksum' => $meta['checksum']]
236
+                );
237
+            }
238
+
239
+            $this->refreshInfo();
240
+
241
+        } catch (StorageNotAvailableException $e) {
242
+            throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
243
+        }
244
+
245
+        return '"' . $this->info->getEtag() . '"';
246
+    }
247
+
248
+    private function getPartFileBasePath($path) {
249
+        $partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true);
250
+        if ($partFileInStorage) {
251
+            return $path;
252
+        } else {
253
+            return md5($path); // will place it in the root of the view with a unique name
254
+        }
255
+    }
256
+
257
+    /**
258
+     * @param string $path
259
+     */
260
+    private function emitPreHooks($exists, $path = null) {
261
+        if (is_null($path)) {
262
+            $path = $this->path;
263
+        }
264
+        $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
265
+        $run = true;
266
+
267
+        if (!$exists) {
268
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(
269
+                \OC\Files\Filesystem::signal_param_path => $hookPath,
270
+                \OC\Files\Filesystem::signal_param_run => &$run,
271
+            ));
272
+        } else {
273
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(
274
+                \OC\Files\Filesystem::signal_param_path => $hookPath,
275
+                \OC\Files\Filesystem::signal_param_run => &$run,
276
+            ));
277
+        }
278
+        \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(
279
+            \OC\Files\Filesystem::signal_param_path => $hookPath,
280
+            \OC\Files\Filesystem::signal_param_run => &$run,
281
+        ));
282
+        return $run;
283
+    }
284
+
285
+    /**
286
+     * @param string $path
287
+     */
288
+    private function emitPostHooks($exists, $path = null) {
289
+        if (is_null($path)) {
290
+            $path = $this->path;
291
+        }
292
+        $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
293
+        if (!$exists) {
294
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
295
+                \OC\Files\Filesystem::signal_param_path => $hookPath
296
+            ));
297
+        } else {
298
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(
299
+                \OC\Files\Filesystem::signal_param_path => $hookPath
300
+            ));
301
+        }
302
+        \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(
303
+            \OC\Files\Filesystem::signal_param_path => $hookPath
304
+        ));
305
+    }
306
+
307
+    /**
308
+     * Returns the data
309
+     *
310
+     * @return resource
311
+     * @throws Forbidden
312
+     * @throws ServiceUnavailable
313
+     */
314
+    public function get() {
315
+        //throw exception if encryption is disabled but files are still encrypted
316
+        try {
317
+            $res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb');
318
+            if ($res === false) {
319
+                throw new ServiceUnavailable("Could not open file");
320
+            }
321
+            return $res;
322
+        } catch (GenericEncryptionException $e) {
323
+            // returning 503 will allow retry of the operation at a later point in time
324
+            throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
325
+        } catch (StorageNotAvailableException $e) {
326
+            throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
327
+        } catch (ForbiddenException $ex) {
328
+            throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
329
+        } catch (LockedException $e) {
330
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
331
+        }
332
+    }
333
+
334
+    /**
335
+     * Delete the current file
336
+     *
337
+     * @throws Forbidden
338
+     * @throws ServiceUnavailable
339
+     */
340
+    public function delete() {
341
+        if (!$this->info->isDeletable()) {
342
+            throw new Forbidden();
343
+        }
344
+
345
+        try {
346
+            if (!$this->fileView->unlink($this->path)) {
347
+                // assume it wasn't possible to delete due to permissions
348
+                throw new Forbidden();
349
+            }
350
+        } catch (StorageNotAvailableException $e) {
351
+            throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
352
+        } catch (ForbiddenException $ex) {
353
+            throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
354
+        } catch (LockedException $e) {
355
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
356
+        }
357
+    }
358
+
359
+    /**
360
+     * Returns the mime-type for a file
361
+     *
362
+     * If null is returned, we'll assume application/octet-stream
363
+     *
364
+     * @return string
365
+     */
366
+    public function getContentType() {
367
+        $mimeType = $this->info->getMimetype();
368
+
369
+        // PROPFIND needs to return the correct mime type, for consistency with the web UI
370
+        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
371
+            return $mimeType;
372
+        }
373
+        return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType);
374
+    }
375
+
376
+    /**
377
+     * @return array|false
378
+     */
379
+    public function getDirectDownload() {
380
+        if (\OCP\App::isEnabled('encryption')) {
381
+            return [];
382
+        }
383
+        /** @var \OCP\Files\Storage $storage */
384
+        list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
385
+        if (is_null($storage)) {
386
+            return [];
387
+        }
388
+
389
+        return $storage->getDirectDownload($internalPath);
390
+    }
391
+
392
+    /**
393
+     * @param resource $data
394
+     * @return null|string
395
+     * @throws Exception
396
+     * @throws BadRequest
397
+     * @throws NotImplemented
398
+     * @throws ServiceUnavailable
399
+     */
400
+    private function createFileChunked($data) {
401
+        list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path);
402
+
403
+        $info = \OC_FileChunking::decodeName($name);
404
+        if (empty($info)) {
405
+            throw new NotImplemented('Invalid chunk name');
406
+        }
407
+
408
+        $chunk_handler = new \OC_FileChunking($info);
409
+        $bytesWritten = $chunk_handler->store($info['index'], $data);
410
+
411
+        //detect aborted upload
412
+        if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
413
+            if (isset($_SERVER['CONTENT_LENGTH'])) {
414
+                $expected = $_SERVER['CONTENT_LENGTH'];
415
+                if ($bytesWritten != $expected) {
416
+                    $chunk_handler->remove($info['index']);
417
+                    throw new BadRequest(
418
+                        'expected filesize ' . $expected . ' got ' . $bytesWritten);
419
+                }
420
+            }
421
+        }
422
+
423
+        if ($chunk_handler->isComplete()) {
424
+            list($storage,) = $this->fileView->resolvePath($path);
425
+            $needsPartFile = $this->needsPartFile($storage);
426
+            $partFile = null;
427
+
428
+            $targetPath = $path . '/' . $info['name'];
429
+            /** @var \OC\Files\Storage\Storage $targetStorage */
430
+            list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
431
+
432
+            $exists = $this->fileView->file_exists($targetPath);
433
+
434
+            try {
435
+                $this->fileView->lockFile($targetPath, ILockingProvider::LOCK_SHARED);
436
+
437
+                $this->emitPreHooks($exists, $targetPath);
438
+                $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
439
+                /** @var \OC\Files\Storage\Storage $targetStorage */
440
+                list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
441
+
442
+                if ($needsPartFile) {
443
+                    // we first assembly the target file as a part file
444
+                    $partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
445
+                    /** @var \OC\Files\Storage\Storage $targetStorage */
446
+                    list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
447
+
448
+
449
+                    $chunk_handler->file_assemble($partStorage, $partInternalPath);
450
+
451
+                    if (!self::isChecksumValid($partStorage, $partInternalPath)) {
452
+                        throw new BadRequest('The computed checksum does not match the one received from the client.');
453
+                    }
454
+
455
+                    // here is the final atomic rename
456
+                    $renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);
457
+                    $fileExists = $targetStorage->file_exists($targetInternalPath);
458
+                    if ($renameOkay === false || $fileExists === false) {
459
+                        \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR);
460
+                        // only delete if an error occurred and the target file was already created
461
+                        if ($fileExists) {
462
+                            // set to null to avoid double-deletion when handling exception
463
+                            // stray part file
464
+                            $partFile = null;
465
+                            $targetStorage->unlink($targetInternalPath);
466
+                        }
467
+                        $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
468
+                        throw new Exception('Could not rename part file assembled from chunks');
469
+                    }
470
+                } else {
471
+                    // assemble directly into the final file
472
+                    $chunk_handler->file_assemble($targetStorage, $targetInternalPath);
473
+                }
474
+
475
+                // allow sync clients to send the mtime along in a header
476
+                $request = \OC::$server->getRequest();
477
+                if (isset($request->server['HTTP_X_OC_MTIME'])) {
478
+                    if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) {
479
+                        header('X-OC-MTime: accepted');
480
+                    }
481
+                }
482
+
483
+                // since we skipped the view we need to scan and emit the hooks ourselves
484
+                $targetStorage->getUpdater()->update($targetInternalPath);
485
+
486
+                $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
487
+
488
+                $this->emitPostHooks($exists, $targetPath);
489
+
490
+                // FIXME: should call refreshInfo but can't because $this->path is not the of the final file
491
+                $info = $this->fileView->getFileInfo($targetPath);
492
+
493
+
494
+                if (isset($partStorage) && isset($partInternalPath)) {
495
+                    $checksums = $partStorage->getCache()->get($partInternalPath)['checksum'];
496
+                } else {
497
+                    $checksums = $targetStorage->getCache()->get($targetInternalPath)['checksum'];
498
+                }
499
+
500
+                $this->fileView->putFileInfo(
501
+                    $targetPath,
502
+                    ['checksum' => $checksums]
503
+                );
504
+
505
+                $this->refreshInfo();
506
+
507
+                $this->fileView->unlockFile($targetPath, ILockingProvider::LOCK_SHARED);
508
+
509
+                return $info->getEtag();
510
+            } catch (\Exception $e) {
511
+                if ($partFile !== null) {
512
+                    $targetStorage->unlink($targetInternalPath);
513
+                }
514
+                $this->convertToSabreException($e);
515
+            }
516
+        }
517
+
518
+        return null;
519
+    }
520
+
521
+    /**
522
+     * will return true if checksum was not provided in request
523
+     *
524
+     * @param Storage $storage
525
+     * @param $path
526
+     * @return bool
527
+     */
528
+    private static function isChecksumValid(Storage $storage, $path) {
529
+        $meta = $storage->getMetaData($path);
530
+        $request = \OC::$server->getRequest();
531
+
532
+        if (!isset($request->server['HTTP_OC_CHECKSUM']) || !isset($meta['checksum'])) {
533
+            // No comparison possible, skip the check
534
+            return true;
535
+        }
536
+
537
+        $expectedChecksum = trim($request->server['HTTP_OC_CHECKSUM']);
538
+        $computedChecksums = $meta['checksum'];
539
+
540
+        return strpos($computedChecksums, $expectedChecksum) !== false;
541
+
542
+    }
543
+
544
+    /**
545
+     * Returns whether a part file is needed for the given storage
546
+     * or whether the file can be assembled/uploaded directly on the
547
+     * target storage.
548
+     *
549
+     * @param \OCP\Files\Storage $storage
550
+     * @return bool true if the storage needs part file handling
551
+     */
552
+    private function needsPartFile($storage) {
553
+        // TODO: in the future use ChunkHandler provided by storage
554
+        return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') &&
555
+            !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud') &&
556
+            $storage->needsPartFile();
557
+    }
558
+
559
+    /**
560
+     * Convert the given exception to a SabreException instance
561
+     *
562
+     * @param \Exception $e
563
+     *
564
+     * @throws \Sabre\DAV\Exception
565
+     */
566
+    private function convertToSabreException(\Exception $e) {
567
+        if ($e instanceof \Sabre\DAV\Exception) {
568
+            throw $e;
569
+        }
570
+        if ($e instanceof NotPermittedException) {
571
+            // a more general case - due to whatever reason the content could not be written
572
+            throw new Forbidden($e->getMessage(), 0, $e);
573
+        }
574
+        if ($e instanceof ForbiddenException) {
575
+            // the path for the file was forbidden
576
+            throw new DAVForbiddenException($e->getMessage(), $e->getRetry(), $e);
577
+        }
578
+        if ($e instanceof EntityTooLargeException) {
579
+            // the file is too big to be stored
580
+            throw new EntityTooLarge($e->getMessage(), 0, $e);
581
+        }
582
+        if ($e instanceof InvalidContentException) {
583
+            // the file content is not permitted
584
+            throw new UnsupportedMediaType($e->getMessage(), 0, $e);
585
+        }
586
+        if ($e instanceof InvalidPathException) {
587
+            // the path for the file was not valid
588
+            // TODO: find proper http status code for this case
589
+            throw new Forbidden($e->getMessage(), 0, $e);
590
+        }
591
+        if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) {
592
+            // the file is currently being written to by another process
593
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
594
+        }
595
+        if ($e instanceof GenericEncryptionException) {
596
+            // returning 503 will allow retry of the operation at a later point in time
597
+            throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
598
+        }
599
+        if ($e instanceof StorageNotAvailableException) {
600
+            throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
601
+        }
602
+
603
+        throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
604
+    }
605
+
606
+
607
+    /**
608
+     * Set $algo to get a specific checksum, leave null to get all checksums
609
+     * (space seperated)
610
+     * @param null $algo
611
+     * @return string
612
+     */
613
+    public function getChecksum($algo = null) {
614
+        $allChecksums = $this->info->getChecksum();
615
+
616
+        if (!$algo) {
617
+            return $allChecksums;
618
+        }
619
+
620
+        $checksums = explode(' ', $allChecksums);
621
+        $algoPrefix = strtoupper($algo) . ':';
622
+
623
+        foreach ($checksums as $checksum) {
624
+            // starts with $algoPrefix
625
+            if (substr($checksum, 0, strlen($algoPrefix)) === $algoPrefix) {
626
+                return $checksum;
627
+            }
628
+        }
629
+
630
+        return '';
631
+    }
632 632
 }
Please login to merge, or discard this patch.
lib/private/legacy/util.php 2 patches
Indentation   +1391 added lines, -1391 removed lines patch added patch discarded remove patch
@@ -61,1399 +61,1399 @@
 block discarded – undo
61 61
 use OCP\IUser;
62 62
 
63 63
 class OC_Util {
64
-	public static $scripts = array();
65
-	public static $styles = array();
66
-	public static $headers = array();
67
-	private static $rootMounted = false;
68
-	private static $fsSetup = false;
69
-
70
-	/** @var array Local cache of version.php */
71
-	private static $versionCache = null;
72
-
73
-	protected static function getAppManager() {
74
-		return \OC::$server->getAppManager();
75
-	}
76
-
77
-	private static function initLocalStorageRootFS() {
78
-		// mount local file backend as root
79
-		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
80
-		//first set up the local "root" storage
81
-		\OC\Files\Filesystem::initMountManager();
82
-		if (!self::$rootMounted) {
83
-			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
84
-			self::$rootMounted = true;
85
-		}
86
-	}
87
-
88
-	/**
89
-	 * mounting an object storage as the root fs will in essence remove the
90
-	 * necessity of a data folder being present.
91
-	 * TODO make home storage aware of this and use the object storage instead of local disk access
92
-	 *
93
-	 * @param array $config containing 'class' and optional 'arguments'
94
-	 */
95
-	private static function initObjectStoreRootFS($config) {
96
-		// check misconfiguration
97
-		if (empty($config['class'])) {
98
-			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
99
-		}
100
-		if (!isset($config['arguments'])) {
101
-			$config['arguments'] = array();
102
-		}
103
-
104
-		// instantiate object store implementation
105
-		$name = $config['class'];
106
-		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
107
-			$segments = explode('\\', $name);
108
-			OC_App::loadApp(strtolower($segments[1]));
109
-		}
110
-		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
111
-		// mount with plain / root object store implementation
112
-		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
113
-
114
-		// mount object storage as root
115
-		\OC\Files\Filesystem::initMountManager();
116
-		if (!self::$rootMounted) {
117
-			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
118
-			self::$rootMounted = true;
119
-		}
120
-	}
121
-
122
-	/**
123
-	 * Can be set up
124
-	 *
125
-	 * @param string $user
126
-	 * @return boolean
127
-	 * @description configure the initial filesystem based on the configuration
128
-	 */
129
-	public static function setupFS($user = '') {
130
-		//setting up the filesystem twice can only lead to trouble
131
-		if (self::$fsSetup) {
132
-			return false;
133
-		}
134
-
135
-		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
136
-
137
-		// If we are not forced to load a specific user we load the one that is logged in
138
-		if ($user === null) {
139
-			$user = '';
140
-		} else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) {
141
-			$user = OC_User::getUser();
142
-		}
143
-
144
-		// load all filesystem apps before, so no setup-hook gets lost
145
-		OC_App::loadApps(array('filesystem'));
146
-
147
-		// the filesystem will finish when $user is not empty,
148
-		// mark fs setup here to avoid doing the setup from loading
149
-		// OC_Filesystem
150
-		if ($user != '') {
151
-			self::$fsSetup = true;
152
-		}
153
-
154
-		\OC\Files\Filesystem::initMountManager();
155
-
156
-		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
157
-		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
158
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
159
-				/** @var \OC\Files\Storage\Common $storage */
160
-				$storage->setMountOptions($mount->getOptions());
161
-			}
162
-			return $storage;
163
-		});
164
-
165
-		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
166
-			if (!$mount->getOption('enable_sharing', true)) {
167
-				return new \OC\Files\Storage\Wrapper\PermissionsMask([
168
-					'storage' => $storage,
169
-					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
170
-				]);
171
-			}
172
-			return $storage;
173
-		});
174
-
175
-		// install storage availability wrapper, before most other wrappers
176
-		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
177
-			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
178
-				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
179
-			}
180
-			return $storage;
181
-		});
182
-
183
-		// install storage checksum wrapper
184
-		\OC\Files\Filesystem::addStorageWrapper('oc_checksum', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
185
-			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
186
-				return new \OC\Files\Storage\Wrapper\Checksum(['storage' => $storage]);
187
-			}
188
-
189
-			return $storage;
190
-
191
-		}, 1);
192
-
193
-
194
-		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
195
-			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
196
-				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
197
-			}
198
-			return $storage;
199
-		});
200
-
201
-		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
202
-			// set up quota for home storages, even for other users
203
-			// which can happen when using sharing
204
-
205
-			/**
206
-			 * @var \OC\Files\Storage\Storage $storage
207
-			 */
208
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
209
-				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
210
-			) {
211
-				/** @var \OC\Files\Storage\Home $storage */
212
-				if (is_object($storage->getUser())) {
213
-					$user = $storage->getUser()->getUID();
214
-					$quota = OC_Util::getUserQuota($user);
215
-					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
216
-						return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
217
-					}
218
-				}
219
-			}
220
-
221
-			return $storage;
222
-		});
223
-
224
-		OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
225
-		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
226
-
227
-		//check if we are using an object storage
228
-		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
229
-		if (isset($objectStore)) {
230
-			self::initObjectStoreRootFS($objectStore);
231
-		} else {
232
-			self::initLocalStorageRootFS();
233
-		}
234
-
235
-		if ($user != '' && !OCP\User::userExists($user)) {
236
-			\OC::$server->getEventLogger()->end('setup_fs');
237
-			return false;
238
-		}
239
-
240
-		//if we aren't logged in, there is no use to set up the filesystem
241
-		if ($user != "") {
242
-
243
-			$userDir = '/' . $user . '/files';
244
-
245
-			//jail the user into his "home" directory
246
-			\OC\Files\Filesystem::init($user, $userDir);
247
-
248
-			OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
249
-		}
250
-		\OC::$server->getEventLogger()->end('setup_fs');
251
-		return true;
252
-	}
253
-
254
-	/**
255
-	 * check if a password is required for each public link
256
-	 *
257
-	 * @return boolean
258
-	 */
259
-	public static function isPublicLinkPasswordRequired() {
260
-		$appConfig = \OC::$server->getAppConfig();
261
-		$enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no');
262
-		return ($enforcePassword === 'yes') ? true : false;
263
-	}
264
-
265
-	/**
266
-	 * check if sharing is disabled for the current user
267
-	 * @param IConfig $config
268
-	 * @param IGroupManager $groupManager
269
-	 * @param IUser|null $user
270
-	 * @return bool
271
-	 */
272
-	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
273
-		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
274
-			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
275
-			$excludedGroups = json_decode($groupsList);
276
-			if (is_null($excludedGroups)) {
277
-				$excludedGroups = explode(',', $groupsList);
278
-				$newValue = json_encode($excludedGroups);
279
-				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
280
-			}
281
-			$usersGroups = $groupManager->getUserGroupIds($user);
282
-			if (!empty($usersGroups)) {
283
-				$remainingGroups = array_diff($usersGroups, $excludedGroups);
284
-				// if the user is only in groups which are disabled for sharing then
285
-				// sharing is also disabled for the user
286
-				if (empty($remainingGroups)) {
287
-					return true;
288
-				}
289
-			}
290
-		}
291
-		return false;
292
-	}
293
-
294
-	/**
295
-	 * check if share API enforces a default expire date
296
-	 *
297
-	 * @return boolean
298
-	 */
299
-	public static function isDefaultExpireDateEnforced() {
300
-		$isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
301
-		$enforceDefaultExpireDate = false;
302
-		if ($isDefaultExpireDateEnabled === 'yes') {
303
-			$value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
304
-			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
305
-		}
306
-
307
-		return $enforceDefaultExpireDate;
308
-	}
309
-
310
-	/**
311
-	 * Get the quota of a user
312
-	 *
313
-	 * @param string $userId
314
-	 * @return int Quota bytes
315
-	 */
316
-	public static function getUserQuota($userId) {
317
-		$user = \OC::$server->getUserManager()->get($userId);
318
-		if (is_null($user)) {
319
-			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
320
-		}
321
-		$userQuota = $user->getQuota();
322
-		if($userQuota === 'none') {
323
-			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
324
-		}
325
-		return OC_Helper::computerFileSize($userQuota);
326
-	}
327
-
328
-	/**
329
-	 * copies the skeleton to the users /files
330
-	 *
331
-	 * @param String $userId
332
-	 * @param \OCP\Files\Folder $userDirectory
333
-	 * @throws \RuntimeException
334
-	 */
335
-	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
336
-
337
-		$skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
338
-		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
339
-
340
-		if ($instanceId === null) {
341
-			throw new \RuntimeException('no instance id!');
342
-		}
343
-		$appdata = 'appdata_' . $instanceId;
344
-		if ($userId === $appdata) {
345
-			throw new \RuntimeException('username is reserved name: ' . $appdata);
346
-		}
347
-
348
-		if (!empty($skeletonDirectory)) {
349
-			\OCP\Util::writeLog(
350
-				'files_skeleton',
351
-				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
352
-				\OCP\Util::DEBUG
353
-			);
354
-			self::copyr($skeletonDirectory, $userDirectory);
355
-			// update the file cache
356
-			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
357
-		}
358
-	}
359
-
360
-	/**
361
-	 * copies a directory recursively by using streams
362
-	 *
363
-	 * @param string $source
364
-	 * @param \OCP\Files\Folder $target
365
-	 * @return void
366
-	 */
367
-	public static function copyr($source, \OCP\Files\Folder $target) {
368
-		$logger = \OC::$server->getLogger();
369
-
370
-		// Verify if folder exists
371
-		$dir = opendir($source);
372
-		if($dir === false) {
373
-			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
374
-			return;
375
-		}
376
-
377
-		// Copy the files
378
-		while (false !== ($file = readdir($dir))) {
379
-			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
380
-				if (is_dir($source . '/' . $file)) {
381
-					$child = $target->newFolder($file);
382
-					self::copyr($source . '/' . $file, $child);
383
-				} else {
384
-					$child = $target->newFile($file);
385
-					$sourceStream = fopen($source . '/' . $file, 'r');
386
-					if($sourceStream === false) {
387
-						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
388
-						closedir($dir);
389
-						return;
390
-					}
391
-					stream_copy_to_stream($sourceStream, $child->fopen('w'));
392
-				}
393
-			}
394
-		}
395
-		closedir($dir);
396
-	}
397
-
398
-	/**
399
-	 * @return void
400
-	 */
401
-	public static function tearDownFS() {
402
-		\OC\Files\Filesystem::tearDown();
403
-		\OC::$server->getRootFolder()->clearCache();
404
-		self::$fsSetup = false;
405
-		self::$rootMounted = false;
406
-	}
407
-
408
-	/**
409
-	 * get the current installed version of ownCloud
410
-	 *
411
-	 * @return array
412
-	 */
413
-	public static function getVersion() {
414
-		OC_Util::loadVersion();
415
-		return self::$versionCache['OC_Version'];
416
-	}
417
-
418
-	/**
419
-	 * get the current installed version string of ownCloud
420
-	 *
421
-	 * @return string
422
-	 */
423
-	public static function getVersionString() {
424
-		OC_Util::loadVersion();
425
-		return self::$versionCache['OC_VersionString'];
426
-	}
427
-
428
-	/**
429
-	 * @deprecated the value is of no use anymore
430
-	 * @return string
431
-	 */
432
-	public static function getEditionString() {
433
-		return '';
434
-	}
435
-
436
-	/**
437
-	 * @description get the update channel of the current installed of ownCloud.
438
-	 * @return string
439
-	 */
440
-	public static function getChannel() {
441
-		OC_Util::loadVersion();
442
-		return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
443
-	}
444
-
445
-	/**
446
-	 * @description get the build number of the current installed of ownCloud.
447
-	 * @return string
448
-	 */
449
-	public static function getBuild() {
450
-		OC_Util::loadVersion();
451
-		return self::$versionCache['OC_Build'];
452
-	}
453
-
454
-	/**
455
-	 * @description load the version.php into the session as cache
456
-	 */
457
-	private static function loadVersion() {
458
-		if (self::$versionCache !== null) {
459
-			return;
460
-		}
461
-
462
-		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
463
-		require OC::$SERVERROOT . '/version.php';
464
-		/** @var $timestamp int */
465
-		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
466
-		/** @var $OC_Version string */
467
-		self::$versionCache['OC_Version'] = $OC_Version;
468
-		/** @var $OC_VersionString string */
469
-		self::$versionCache['OC_VersionString'] = $OC_VersionString;
470
-		/** @var $OC_Build string */
471
-		self::$versionCache['OC_Build'] = $OC_Build;
472
-
473
-		/** @var $OC_Channel string */
474
-		self::$versionCache['OC_Channel'] = $OC_Channel;
475
-	}
476
-
477
-	/**
478
-	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
479
-	 *
480
-	 * @param string $application application to get the files from
481
-	 * @param string $directory directory within this application (css, js, vendor, etc)
482
-	 * @param string $file the file inside of the above folder
483
-	 * @return string the path
484
-	 */
485
-	private static function generatePath($application, $directory, $file) {
486
-		if (is_null($file)) {
487
-			$file = $application;
488
-			$application = "";
489
-		}
490
-		if (!empty($application)) {
491
-			return "$application/$directory/$file";
492
-		} else {
493
-			return "$directory/$file";
494
-		}
495
-	}
496
-
497
-	/**
498
-	 * add a javascript file
499
-	 *
500
-	 * @param string $application application id
501
-	 * @param string|null $file filename
502
-	 * @param bool $prepend prepend the Script to the beginning of the list
503
-	 * @return void
504
-	 */
505
-	public static function addScript($application, $file = null, $prepend = false) {
506
-		$path = OC_Util::generatePath($application, 'js', $file);
507
-
508
-		// core js files need separate handling
509
-		if ($application !== 'core' && $file !== null) {
510
-			self::addTranslations ( $application );
511
-		}
512
-		self::addExternalResource($application, $prepend, $path, "script");
513
-	}
514
-
515
-	/**
516
-	 * add a javascript file from the vendor sub folder
517
-	 *
518
-	 * @param string $application application id
519
-	 * @param string|null $file filename
520
-	 * @param bool $prepend prepend the Script to the beginning of the list
521
-	 * @return void
522
-	 */
523
-	public static function addVendorScript($application, $file = null, $prepend = false) {
524
-		$path = OC_Util::generatePath($application, 'vendor', $file);
525
-		self::addExternalResource($application, $prepend, $path, "script");
526
-	}
527
-
528
-	/**
529
-	 * add a translation JS file
530
-	 *
531
-	 * @param string $application application id
532
-	 * @param string $languageCode language code, defaults to the current language
533
-	 * @param bool $prepend prepend the Script to the beginning of the list
534
-	 */
535
-	public static function addTranslations($application, $languageCode = null, $prepend = false) {
536
-		if (is_null($languageCode)) {
537
-			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
538
-		}
539
-		if (!empty($application)) {
540
-			$path = "$application/l10n/$languageCode";
541
-		} else {
542
-			$path = "l10n/$languageCode";
543
-		}
544
-		self::addExternalResource($application, $prepend, $path, "script");
545
-	}
546
-
547
-	/**
548
-	 * add a css file
549
-	 *
550
-	 * @param string $application application id
551
-	 * @param string|null $file filename
552
-	 * @param bool $prepend prepend the Style to the beginning of the list
553
-	 * @return void
554
-	 */
555
-	public static function addStyle($application, $file = null, $prepend = false) {
556
-		$path = OC_Util::generatePath($application, 'css', $file);
557
-		self::addExternalResource($application, $prepend, $path, "style");
558
-	}
559
-
560
-	/**
561
-	 * add a css file from the vendor sub folder
562
-	 *
563
-	 * @param string $application application id
564
-	 * @param string|null $file filename
565
-	 * @param bool $prepend prepend the Style to the beginning of the list
566
-	 * @return void
567
-	 */
568
-	public static function addVendorStyle($application, $file = null, $prepend = false) {
569
-		$path = OC_Util::generatePath($application, 'vendor', $file);
570
-		self::addExternalResource($application, $prepend, $path, "style");
571
-	}
572
-
573
-	/**
574
-	 * add an external resource css/js file
575
-	 *
576
-	 * @param string $application application id
577
-	 * @param bool $prepend prepend the file to the beginning of the list
578
-	 * @param string $path
579
-	 * @param string $type (script or style)
580
-	 * @return void
581
-	 */
582
-	private static function addExternalResource($application, $prepend, $path, $type = "script") {
583
-
584
-		if ($type === "style") {
585
-			if (!in_array($path, self::$styles)) {
586
-				if ($prepend === true) {
587
-					array_unshift ( self::$styles, $path );
588
-				} else {
589
-					self::$styles[] = $path;
590
-				}
591
-			}
592
-		} elseif ($type === "script") {
593
-			if (!in_array($path, self::$scripts)) {
594
-				if ($prepend === true) {
595
-					array_unshift ( self::$scripts, $path );
596
-				} else {
597
-					self::$scripts [] = $path;
598
-				}
599
-			}
600
-		}
601
-	}
602
-
603
-	/**
604
-	 * Add a custom element to the header
605
-	 * If $text is null then the element will be written as empty element.
606
-	 * So use "" to get a closing tag.
607
-	 * @param string $tag tag name of the element
608
-	 * @param array $attributes array of attributes for the element
609
-	 * @param string $text the text content for the element
610
-	 */
611
-	public static function addHeader($tag, $attributes, $text=null) {
612
-		self::$headers[] = array(
613
-			'tag' => $tag,
614
-			'attributes' => $attributes,
615
-			'text' => $text
616
-		);
617
-	}
618
-
619
-	/**
620
-	 * formats a timestamp in the "right" way
621
-	 *
622
-	 * @param int $timestamp
623
-	 * @param bool $dateOnly option to omit time from the result
624
-	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
625
-	 * @return string timestamp
626
-	 *
627
-	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
628
-	 */
629
-	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
630
-		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
631
-			$timeZone = new \DateTimeZone($timeZone);
632
-		}
633
-
634
-		/** @var \OC\DateTimeFormatter $formatter */
635
-		$formatter = \OC::$server->query('DateTimeFormatter');
636
-		if ($dateOnly) {
637
-			return $formatter->formatDate($timestamp, 'long', $timeZone);
638
-		}
639
-		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
640
-	}
641
-
642
-	/**
643
-	 * check if the current server configuration is suitable for ownCloud
644
-	 *
645
-	 * @param \OC\SystemConfig $config
646
-	 * @return array arrays with error messages and hints
647
-	 */
648
-	public static function checkServer(\OC\SystemConfig $config) {
649
-		$l = \OC::$server->getL10N('lib');
650
-		$errors = array();
651
-		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
652
-
653
-		if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
654
-			// this check needs to be done every time
655
-			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
656
-		}
657
-
658
-		// Assume that if checkServer() succeeded before in this session, then all is fine.
659
-		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
660
-			return $errors;
661
-		}
662
-
663
-		$webServerRestart = false;
664
-		$setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
665
-			\OC::$server->getThemingDefaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
666
-
667
-		$urlGenerator = \OC::$server->getURLGenerator();
668
-
669
-		$availableDatabases = $setup->getSupportedDatabases();
670
-		if (empty($availableDatabases)) {
671
-			$errors[] = array(
672
-				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
673
-				'hint' => '' //TODO: sane hint
674
-			);
675
-			$webServerRestart = true;
676
-		}
677
-
678
-		// Check if config folder is writable.
679
-		if(!OC_Helper::isReadOnlyConfigEnabled()) {
680
-			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
681
-				$errors[] = array(
682
-					'error' => $l->t('Cannot write into "config" directory'),
683
-					'hint' => $l->t('This can usually be fixed by '
684
-						. '%sgiving the webserver write access to the config directory%s.',
685
-						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
686
-				);
687
-			}
688
-		}
689
-
690
-		// Check if there is a writable install folder.
691
-		if ($config->getValue('appstoreenabled', true)) {
692
-			if (OC_App::getInstallPath() === null
693
-				|| !is_writable(OC_App::getInstallPath())
694
-				|| !is_readable(OC_App::getInstallPath())
695
-			) {
696
-				$errors[] = array(
697
-					'error' => $l->t('Cannot write into "apps" directory'),
698
-					'hint' => $l->t('This can usually be fixed by '
699
-						. '%sgiving the webserver write access to the apps directory%s'
700
-						. ' or disabling the appstore in the config file.',
701
-						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
702
-				);
703
-			}
704
-		}
705
-		// Create root dir.
706
-		if ($config->getValue('installed', false)) {
707
-			if (!is_dir($CONFIG_DATADIRECTORY)) {
708
-				$success = @mkdir($CONFIG_DATADIRECTORY);
709
-				if ($success) {
710
-					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
711
-				} else {
712
-					$errors[] = array(
713
-						'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)),
714
-						'hint' => $l->t('This can usually be fixed by '
715
-							. '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
716
-							array($urlGenerator->linkToDocs('admin-dir_permissions')))
717
-					);
718
-				}
719
-			} else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
720
-				//common hint for all file permissions error messages
721
-				$permissionsHint = $l->t('Permissions can usually be fixed by '
722
-					. '%sgiving the webserver write access to the root directory%s.',
723
-					array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
724
-				$errors[] = array(
725
-					'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable',
726
-					'hint' => $permissionsHint
727
-				);
728
-			} else {
729
-				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
730
-			}
731
-		}
732
-
733
-		if (!OC_Util::isSetLocaleWorking()) {
734
-			$errors[] = array(
735
-				'error' => $l->t('Setting locale to %s failed',
736
-					array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
737
-						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
738
-				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
739
-			);
740
-		}
741
-
742
-		// Contains the dependencies that should be checked against
743
-		// classes = class_exists
744
-		// functions = function_exists
745
-		// defined = defined
746
-		// ini = ini_get
747
-		// If the dependency is not found the missing module name is shown to the EndUser
748
-		// When adding new checks always verify that they pass on Travis as well
749
-		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
750
-		$dependencies = array(
751
-			'classes' => array(
752
-				'ZipArchive' => 'zip',
753
-				'DOMDocument' => 'dom',
754
-				'XMLWriter' => 'XMLWriter',
755
-				'XMLReader' => 'XMLReader',
756
-			),
757
-			'functions' => [
758
-				'xml_parser_create' => 'libxml',
759
-				'mb_strcut' => 'mb multibyte',
760
-				'ctype_digit' => 'ctype',
761
-				'json_encode' => 'JSON',
762
-				'gd_info' => 'GD',
763
-				'gzencode' => 'zlib',
764
-				'iconv' => 'iconv',
765
-				'simplexml_load_string' => 'SimpleXML',
766
-				'hash' => 'HASH Message Digest Framework',
767
-				'curl_init' => 'cURL',
768
-				'openssl_verify' => 'OpenSSL',
769
-			],
770
-			'defined' => array(
771
-				'PDO::ATTR_DRIVER_NAME' => 'PDO'
772
-			),
773
-			'ini' => [
774
-				'default_charset' => 'UTF-8',
775
-			],
776
-		);
777
-		$missingDependencies = array();
778
-		$invalidIniSettings = [];
779
-		$moduleHint = $l->t('Please ask your server administrator to install the module.');
780
-
781
-		/**
782
-		 * FIXME: The dependency check does not work properly on HHVM on the moment
783
-		 *        and prevents installation. Once HHVM is more compatible with our
784
-		 *        approach to check for these values we should re-enable those
785
-		 *        checks.
786
-		 */
787
-		$iniWrapper = \OC::$server->getIniWrapper();
788
-		if (!self::runningOnHhvm()) {
789
-			foreach ($dependencies['classes'] as $class => $module) {
790
-				if (!class_exists($class)) {
791
-					$missingDependencies[] = $module;
792
-				}
793
-			}
794
-			foreach ($dependencies['functions'] as $function => $module) {
795
-				if (!function_exists($function)) {
796
-					$missingDependencies[] = $module;
797
-				}
798
-			}
799
-			foreach ($dependencies['defined'] as $defined => $module) {
800
-				if (!defined($defined)) {
801
-					$missingDependencies[] = $module;
802
-				}
803
-			}
804
-			foreach ($dependencies['ini'] as $setting => $expected) {
805
-				if (is_bool($expected)) {
806
-					if ($iniWrapper->getBool($setting) !== $expected) {
807
-						$invalidIniSettings[] = [$setting, $expected];
808
-					}
809
-				}
810
-				if (is_int($expected)) {
811
-					if ($iniWrapper->getNumeric($setting) !== $expected) {
812
-						$invalidIniSettings[] = [$setting, $expected];
813
-					}
814
-				}
815
-				if (is_string($expected)) {
816
-					if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
817
-						$invalidIniSettings[] = [$setting, $expected];
818
-					}
819
-				}
820
-			}
821
-		}
822
-
823
-		foreach($missingDependencies as $missingDependency) {
824
-			$errors[] = array(
825
-				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
826
-				'hint' => $moduleHint
827
-			);
828
-			$webServerRestart = true;
829
-		}
830
-		foreach($invalidIniSettings as $setting) {
831
-			if(is_bool($setting[1])) {
832
-				$setting[1] = ($setting[1]) ? 'on' : 'off';
833
-			}
834
-			$errors[] = [
835
-				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
836
-				'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
837
-			];
838
-			$webServerRestart = true;
839
-		}
840
-
841
-		/**
842
-		 * The mbstring.func_overload check can only be performed if the mbstring
843
-		 * module is installed as it will return null if the checking setting is
844
-		 * not available and thus a check on the boolean value fails.
845
-		 *
846
-		 * TODO: Should probably be implemented in the above generic dependency
847
-		 *       check somehow in the long-term.
848
-		 */
849
-		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
850
-			$iniWrapper->getBool('mbstring.func_overload') === true) {
851
-			$errors[] = array(
852
-				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
853
-				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
854
-			);
855
-		}
856
-
857
-		if(function_exists('xml_parser_create') &&
858
-			LIBXML_LOADED_VERSION < 20700 ) {
859
-			$version = LIBXML_LOADED_VERSION;
860
-			$major = floor($version/10000);
861
-			$version -= ($major * 10000);
862
-			$minor = floor($version/100);
863
-			$version -= ($minor * 100);
864
-			$patch = $version;
865
-			$errors[] = array(
866
-				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
867
-				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
868
-			);
869
-		}
870
-
871
-		if (!self::isAnnotationsWorking()) {
872
-			$errors[] = array(
873
-				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
874
-				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
875
-			);
876
-		}
877
-
878
-		if (!\OC::$CLI && $webServerRestart) {
879
-			$errors[] = array(
880
-				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
881
-				'hint' => $l->t('Please ask your server administrator to restart the web server.')
882
-			);
883
-		}
884
-
885
-		$errors = array_merge($errors, self::checkDatabaseVersion());
886
-
887
-		// Cache the result of this function
888
-		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
889
-
890
-		return $errors;
891
-	}
892
-
893
-	/**
894
-	 * Check the database version
895
-	 *
896
-	 * @return array errors array
897
-	 */
898
-	public static function checkDatabaseVersion() {
899
-		$l = \OC::$server->getL10N('lib');
900
-		$errors = array();
901
-		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
902
-		if ($dbType === 'pgsql') {
903
-			// check PostgreSQL version
904
-			try {
905
-				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
906
-				$data = $result->fetchRow();
907
-				if (isset($data['server_version'])) {
908
-					$version = $data['server_version'];
909
-					if (version_compare($version, '9.0.0', '<')) {
910
-						$errors[] = array(
911
-							'error' => $l->t('PostgreSQL >= 9 required'),
912
-							'hint' => $l->t('Please upgrade your database version')
913
-						);
914
-					}
915
-				}
916
-			} catch (\Doctrine\DBAL\DBALException $e) {
917
-				$logger = \OC::$server->getLogger();
918
-				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
919
-				$logger->logException($e);
920
-			}
921
-		}
922
-		return $errors;
923
-	}
924
-
925
-	/**
926
-	 * Check for correct file permissions of data directory
927
-	 *
928
-	 * @param string $dataDirectory
929
-	 * @return array arrays with error messages and hints
930
-	 */
931
-	public static function checkDataDirectoryPermissions($dataDirectory) {
932
-		$l = \OC::$server->getL10N('lib');
933
-		$errors = array();
934
-		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
935
-			. ' cannot be listed by other users.');
936
-		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
937
-		if (substr($perms, -1) != '0') {
938
-			chmod($dataDirectory, 0770);
939
-			clearstatcache();
940
-			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
941
-			if (substr($perms, 2, 1) != '0') {
942
-				$errors[] = array(
943
-					'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)),
944
-					'hint' => $permissionsModHint
945
-				);
946
-			}
947
-		}
948
-		return $errors;
949
-	}
950
-
951
-	/**
952
-	 * Check that the data directory exists and is valid by
953
-	 * checking the existence of the ".ocdata" file.
954
-	 *
955
-	 * @param string $dataDirectory data directory path
956
-	 * @return array errors found
957
-	 */
958
-	public static function checkDataDirectoryValidity($dataDirectory) {
959
-		$l = \OC::$server->getL10N('lib');
960
-		$errors = [];
961
-		if ($dataDirectory[0] !== '/') {
962
-			$errors[] = [
963
-				'error' => $l->t('Data directory (%s) must be an absolute path', [$dataDirectory]),
964
-				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
965
-			];
966
-		}
967
-		if (!file_exists($dataDirectory . '/.ocdata')) {
968
-			$errors[] = [
969
-				'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
970
-				'hint' => $l->t('Please check that the data directory contains a file' .
971
-					' ".ocdata" in its root.')
972
-			];
973
-		}
974
-		return $errors;
975
-	}
976
-
977
-	/**
978
-	 * Check if the user is logged in, redirects to home if not. With
979
-	 * redirect URL parameter to the request URI.
980
-	 *
981
-	 * @return void
982
-	 */
983
-	public static function checkLoggedIn() {
984
-		// Check if we are a user
985
-		if (!\OC::$server->getUserSession()->isLoggedIn()) {
986
-			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
987
-						'core.login.showLoginForm',
988
-						[
989
-							'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
990
-						]
991
-					)
992
-			);
993
-			exit();
994
-		}
995
-		// Redirect to index page if 2FA challenge was not solved yet
996
-		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
997
-			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
998
-			exit();
999
-		}
1000
-	}
1001
-
1002
-	/**
1003
-	 * Check if the user is a admin, redirects to home if not
1004
-	 *
1005
-	 * @return void
1006
-	 */
1007
-	public static function checkAdminUser() {
1008
-		OC_Util::checkLoggedIn();
1009
-		if (!OC_User::isAdminUser(OC_User::getUser())) {
1010
-			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1011
-			exit();
1012
-		}
1013
-	}
1014
-
1015
-	/**
1016
-	 * Check if the user is a subadmin, redirects to home if not
1017
-	 *
1018
-	 * @return null|boolean $groups where the current user is subadmin
1019
-	 */
1020
-	public static function checkSubAdminUser() {
1021
-		OC_Util::checkLoggedIn();
1022
-		$userObject = \OC::$server->getUserSession()->getUser();
1023
-		$isSubAdmin = false;
1024
-		if($userObject !== null) {
1025
-			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1026
-		}
1027
-
1028
-		if (!$isSubAdmin) {
1029
-			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1030
-			exit();
1031
-		}
1032
-		return true;
1033
-	}
1034
-
1035
-	/**
1036
-	 * Returns the URL of the default page
1037
-	 * based on the system configuration and
1038
-	 * the apps visible for the current user
1039
-	 *
1040
-	 * @return string URL
1041
-	 */
1042
-	public static function getDefaultPageUrl() {
1043
-		$urlGenerator = \OC::$server->getURLGenerator();
1044
-		// Deny the redirect if the URL contains a @
1045
-		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1046
-		if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1047
-			$location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1048
-		} else {
1049
-			$defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
1050
-			if ($defaultPage) {
1051
-				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1052
-			} else {
1053
-				$appId = 'files';
1054
-				$defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files'));
1055
-				// find the first app that is enabled for the current user
1056
-				foreach ($defaultApps as $defaultApp) {
1057
-					$defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1058
-					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1059
-						$appId = $defaultApp;
1060
-						break;
1061
-					}
1062
-				}
1063
-
1064
-				if(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1065
-					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1066
-				} else {
1067
-					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1068
-				}
1069
-			}
1070
-		}
1071
-		return $location;
1072
-	}
1073
-
1074
-	/**
1075
-	 * Redirect to the user default page
1076
-	 *
1077
-	 * @return void
1078
-	 */
1079
-	public static function redirectToDefaultPage() {
1080
-		$location = self::getDefaultPageUrl();
1081
-		header('Location: ' . $location);
1082
-		exit();
1083
-	}
1084
-
1085
-	/**
1086
-	 * get an id unique for this instance
1087
-	 *
1088
-	 * @return string
1089
-	 */
1090
-	public static function getInstanceId() {
1091
-		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1092
-		if (is_null($id)) {
1093
-			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1094
-			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1095
-			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1096
-		}
1097
-		return $id;
1098
-	}
1099
-
1100
-	/**
1101
-	 * Public function to sanitize HTML
1102
-	 *
1103
-	 * This function is used to sanitize HTML and should be applied on any
1104
-	 * string or array of strings before displaying it on a web page.
1105
-	 *
1106
-	 * @param string|array $value
1107
-	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1108
-	 */
1109
-	public static function sanitizeHTML($value) {
1110
-		if (is_array($value)) {
1111
-			$value = array_map(function($value) {
1112
-				return self::sanitizeHTML($value);
1113
-			}, $value);
1114
-		} else {
1115
-			// Specify encoding for PHP<5.4
1116
-			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1117
-		}
1118
-		return $value;
1119
-	}
1120
-
1121
-	/**
1122
-	 * Public function to encode url parameters
1123
-	 *
1124
-	 * This function is used to encode path to file before output.
1125
-	 * Encoding is done according to RFC 3986 with one exception:
1126
-	 * Character '/' is preserved as is.
1127
-	 *
1128
-	 * @param string $component part of URI to encode
1129
-	 * @return string
1130
-	 */
1131
-	public static function encodePath($component) {
1132
-		$encoded = rawurlencode($component);
1133
-		$encoded = str_replace('%2F', '/', $encoded);
1134
-		return $encoded;
1135
-	}
1136
-
1137
-
1138
-	public function createHtaccessTestFile(\OCP\IConfig $config) {
1139
-		// php dev server does not support htaccess
1140
-		if (php_sapi_name() === 'cli-server') {
1141
-			return false;
1142
-		}
1143
-
1144
-		// testdata
1145
-		$fileName = '/htaccesstest.txt';
1146
-		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1147
-
1148
-		// creating a test file
1149
-		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1150
-
1151
-		if (file_exists($testFile)) {// already running this test, possible recursive call
1152
-			return false;
1153
-		}
1154
-
1155
-		$fp = @fopen($testFile, 'w');
1156
-		if (!$fp) {
1157
-			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1158
-				'Make sure it is possible for the webserver to write to ' . $testFile);
1159
-		}
1160
-		fwrite($fp, $testContent);
1161
-		fclose($fp);
1162
-
1163
-		return $testContent;
1164
-	}
1165
-
1166
-	/**
1167
-	 * Check if the .htaccess file is working
1168
-	 * @param \OCP\IConfig $config
1169
-	 * @return bool
1170
-	 * @throws Exception
1171
-	 * @throws \OC\HintException If the test file can't get written.
1172
-	 */
1173
-	public function isHtaccessWorking(\OCP\IConfig $config) {
1174
-
1175
-		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1176
-			return true;
1177
-		}
1178
-
1179
-		$testContent = $this->createHtaccessTestFile($config);
1180
-		if ($testContent === false) {
1181
-			return false;
1182
-		}
1183
-
1184
-		$fileName = '/htaccesstest.txt';
1185
-		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1186
-
1187
-		// accessing the file via http
1188
-		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1189
-		try {
1190
-			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1191
-		} catch (\Exception $e) {
1192
-			$content = false;
1193
-		}
1194
-
1195
-		// cleanup
1196
-		@unlink($testFile);
1197
-
1198
-		/*
64
+    public static $scripts = array();
65
+    public static $styles = array();
66
+    public static $headers = array();
67
+    private static $rootMounted = false;
68
+    private static $fsSetup = false;
69
+
70
+    /** @var array Local cache of version.php */
71
+    private static $versionCache = null;
72
+
73
+    protected static function getAppManager() {
74
+        return \OC::$server->getAppManager();
75
+    }
76
+
77
+    private static function initLocalStorageRootFS() {
78
+        // mount local file backend as root
79
+        $configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
80
+        //first set up the local "root" storage
81
+        \OC\Files\Filesystem::initMountManager();
82
+        if (!self::$rootMounted) {
83
+            \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
84
+            self::$rootMounted = true;
85
+        }
86
+    }
87
+
88
+    /**
89
+     * mounting an object storage as the root fs will in essence remove the
90
+     * necessity of a data folder being present.
91
+     * TODO make home storage aware of this and use the object storage instead of local disk access
92
+     *
93
+     * @param array $config containing 'class' and optional 'arguments'
94
+     */
95
+    private static function initObjectStoreRootFS($config) {
96
+        // check misconfiguration
97
+        if (empty($config['class'])) {
98
+            \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
99
+        }
100
+        if (!isset($config['arguments'])) {
101
+            $config['arguments'] = array();
102
+        }
103
+
104
+        // instantiate object store implementation
105
+        $name = $config['class'];
106
+        if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
107
+            $segments = explode('\\', $name);
108
+            OC_App::loadApp(strtolower($segments[1]));
109
+        }
110
+        $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
111
+        // mount with plain / root object store implementation
112
+        $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
113
+
114
+        // mount object storage as root
115
+        \OC\Files\Filesystem::initMountManager();
116
+        if (!self::$rootMounted) {
117
+            \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
118
+            self::$rootMounted = true;
119
+        }
120
+    }
121
+
122
+    /**
123
+     * Can be set up
124
+     *
125
+     * @param string $user
126
+     * @return boolean
127
+     * @description configure the initial filesystem based on the configuration
128
+     */
129
+    public static function setupFS($user = '') {
130
+        //setting up the filesystem twice can only lead to trouble
131
+        if (self::$fsSetup) {
132
+            return false;
133
+        }
134
+
135
+        \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
136
+
137
+        // If we are not forced to load a specific user we load the one that is logged in
138
+        if ($user === null) {
139
+            $user = '';
140
+        } else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) {
141
+            $user = OC_User::getUser();
142
+        }
143
+
144
+        // load all filesystem apps before, so no setup-hook gets lost
145
+        OC_App::loadApps(array('filesystem'));
146
+
147
+        // the filesystem will finish when $user is not empty,
148
+        // mark fs setup here to avoid doing the setup from loading
149
+        // OC_Filesystem
150
+        if ($user != '') {
151
+            self::$fsSetup = true;
152
+        }
153
+
154
+        \OC\Files\Filesystem::initMountManager();
155
+
156
+        \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
157
+        \OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
158
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
159
+                /** @var \OC\Files\Storage\Common $storage */
160
+                $storage->setMountOptions($mount->getOptions());
161
+            }
162
+            return $storage;
163
+        });
164
+
165
+        \OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
166
+            if (!$mount->getOption('enable_sharing', true)) {
167
+                return new \OC\Files\Storage\Wrapper\PermissionsMask([
168
+                    'storage' => $storage,
169
+                    'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
170
+                ]);
171
+            }
172
+            return $storage;
173
+        });
174
+
175
+        // install storage availability wrapper, before most other wrappers
176
+        \OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
177
+            if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
178
+                return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
179
+            }
180
+            return $storage;
181
+        });
182
+
183
+        // install storage checksum wrapper
184
+        \OC\Files\Filesystem::addStorageWrapper('oc_checksum', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
185
+            if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
186
+                return new \OC\Files\Storage\Wrapper\Checksum(['storage' => $storage]);
187
+            }
188
+
189
+            return $storage;
190
+
191
+        }, 1);
192
+
193
+
194
+        \OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
195
+            if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
196
+                return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
197
+            }
198
+            return $storage;
199
+        });
200
+
201
+        \OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
202
+            // set up quota for home storages, even for other users
203
+            // which can happen when using sharing
204
+
205
+            /**
206
+             * @var \OC\Files\Storage\Storage $storage
207
+             */
208
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
209
+                || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
210
+            ) {
211
+                /** @var \OC\Files\Storage\Home $storage */
212
+                if (is_object($storage->getUser())) {
213
+                    $user = $storage->getUser()->getUID();
214
+                    $quota = OC_Util::getUserQuota($user);
215
+                    if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
216
+                        return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
217
+                    }
218
+                }
219
+            }
220
+
221
+            return $storage;
222
+        });
223
+
224
+        OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
225
+        \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
226
+
227
+        //check if we are using an object storage
228
+        $objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
229
+        if (isset($objectStore)) {
230
+            self::initObjectStoreRootFS($objectStore);
231
+        } else {
232
+            self::initLocalStorageRootFS();
233
+        }
234
+
235
+        if ($user != '' && !OCP\User::userExists($user)) {
236
+            \OC::$server->getEventLogger()->end('setup_fs');
237
+            return false;
238
+        }
239
+
240
+        //if we aren't logged in, there is no use to set up the filesystem
241
+        if ($user != "") {
242
+
243
+            $userDir = '/' . $user . '/files';
244
+
245
+            //jail the user into his "home" directory
246
+            \OC\Files\Filesystem::init($user, $userDir);
247
+
248
+            OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
249
+        }
250
+        \OC::$server->getEventLogger()->end('setup_fs');
251
+        return true;
252
+    }
253
+
254
+    /**
255
+     * check if a password is required for each public link
256
+     *
257
+     * @return boolean
258
+     */
259
+    public static function isPublicLinkPasswordRequired() {
260
+        $appConfig = \OC::$server->getAppConfig();
261
+        $enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no');
262
+        return ($enforcePassword === 'yes') ? true : false;
263
+    }
264
+
265
+    /**
266
+     * check if sharing is disabled for the current user
267
+     * @param IConfig $config
268
+     * @param IGroupManager $groupManager
269
+     * @param IUser|null $user
270
+     * @return bool
271
+     */
272
+    public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
273
+        if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
274
+            $groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
275
+            $excludedGroups = json_decode($groupsList);
276
+            if (is_null($excludedGroups)) {
277
+                $excludedGroups = explode(',', $groupsList);
278
+                $newValue = json_encode($excludedGroups);
279
+                $config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
280
+            }
281
+            $usersGroups = $groupManager->getUserGroupIds($user);
282
+            if (!empty($usersGroups)) {
283
+                $remainingGroups = array_diff($usersGroups, $excludedGroups);
284
+                // if the user is only in groups which are disabled for sharing then
285
+                // sharing is also disabled for the user
286
+                if (empty($remainingGroups)) {
287
+                    return true;
288
+                }
289
+            }
290
+        }
291
+        return false;
292
+    }
293
+
294
+    /**
295
+     * check if share API enforces a default expire date
296
+     *
297
+     * @return boolean
298
+     */
299
+    public static function isDefaultExpireDateEnforced() {
300
+        $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
301
+        $enforceDefaultExpireDate = false;
302
+        if ($isDefaultExpireDateEnabled === 'yes') {
303
+            $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
304
+            $enforceDefaultExpireDate = ($value === 'yes') ? true : false;
305
+        }
306
+
307
+        return $enforceDefaultExpireDate;
308
+    }
309
+
310
+    /**
311
+     * Get the quota of a user
312
+     *
313
+     * @param string $userId
314
+     * @return int Quota bytes
315
+     */
316
+    public static function getUserQuota($userId) {
317
+        $user = \OC::$server->getUserManager()->get($userId);
318
+        if (is_null($user)) {
319
+            return \OCP\Files\FileInfo::SPACE_UNLIMITED;
320
+        }
321
+        $userQuota = $user->getQuota();
322
+        if($userQuota === 'none') {
323
+            return \OCP\Files\FileInfo::SPACE_UNLIMITED;
324
+        }
325
+        return OC_Helper::computerFileSize($userQuota);
326
+    }
327
+
328
+    /**
329
+     * copies the skeleton to the users /files
330
+     *
331
+     * @param String $userId
332
+     * @param \OCP\Files\Folder $userDirectory
333
+     * @throws \RuntimeException
334
+     */
335
+    public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
336
+
337
+        $skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
338
+        $instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
339
+
340
+        if ($instanceId === null) {
341
+            throw new \RuntimeException('no instance id!');
342
+        }
343
+        $appdata = 'appdata_' . $instanceId;
344
+        if ($userId === $appdata) {
345
+            throw new \RuntimeException('username is reserved name: ' . $appdata);
346
+        }
347
+
348
+        if (!empty($skeletonDirectory)) {
349
+            \OCP\Util::writeLog(
350
+                'files_skeleton',
351
+                'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
352
+                \OCP\Util::DEBUG
353
+            );
354
+            self::copyr($skeletonDirectory, $userDirectory);
355
+            // update the file cache
356
+            $userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
357
+        }
358
+    }
359
+
360
+    /**
361
+     * copies a directory recursively by using streams
362
+     *
363
+     * @param string $source
364
+     * @param \OCP\Files\Folder $target
365
+     * @return void
366
+     */
367
+    public static function copyr($source, \OCP\Files\Folder $target) {
368
+        $logger = \OC::$server->getLogger();
369
+
370
+        // Verify if folder exists
371
+        $dir = opendir($source);
372
+        if($dir === false) {
373
+            $logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
374
+            return;
375
+        }
376
+
377
+        // Copy the files
378
+        while (false !== ($file = readdir($dir))) {
379
+            if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
380
+                if (is_dir($source . '/' . $file)) {
381
+                    $child = $target->newFolder($file);
382
+                    self::copyr($source . '/' . $file, $child);
383
+                } else {
384
+                    $child = $target->newFile($file);
385
+                    $sourceStream = fopen($source . '/' . $file, 'r');
386
+                    if($sourceStream === false) {
387
+                        $logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
388
+                        closedir($dir);
389
+                        return;
390
+                    }
391
+                    stream_copy_to_stream($sourceStream, $child->fopen('w'));
392
+                }
393
+            }
394
+        }
395
+        closedir($dir);
396
+    }
397
+
398
+    /**
399
+     * @return void
400
+     */
401
+    public static function tearDownFS() {
402
+        \OC\Files\Filesystem::tearDown();
403
+        \OC::$server->getRootFolder()->clearCache();
404
+        self::$fsSetup = false;
405
+        self::$rootMounted = false;
406
+    }
407
+
408
+    /**
409
+     * get the current installed version of ownCloud
410
+     *
411
+     * @return array
412
+     */
413
+    public static function getVersion() {
414
+        OC_Util::loadVersion();
415
+        return self::$versionCache['OC_Version'];
416
+    }
417
+
418
+    /**
419
+     * get the current installed version string of ownCloud
420
+     *
421
+     * @return string
422
+     */
423
+    public static function getVersionString() {
424
+        OC_Util::loadVersion();
425
+        return self::$versionCache['OC_VersionString'];
426
+    }
427
+
428
+    /**
429
+     * @deprecated the value is of no use anymore
430
+     * @return string
431
+     */
432
+    public static function getEditionString() {
433
+        return '';
434
+    }
435
+
436
+    /**
437
+     * @description get the update channel of the current installed of ownCloud.
438
+     * @return string
439
+     */
440
+    public static function getChannel() {
441
+        OC_Util::loadVersion();
442
+        return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
443
+    }
444
+
445
+    /**
446
+     * @description get the build number of the current installed of ownCloud.
447
+     * @return string
448
+     */
449
+    public static function getBuild() {
450
+        OC_Util::loadVersion();
451
+        return self::$versionCache['OC_Build'];
452
+    }
453
+
454
+    /**
455
+     * @description load the version.php into the session as cache
456
+     */
457
+    private static function loadVersion() {
458
+        if (self::$versionCache !== null) {
459
+            return;
460
+        }
461
+
462
+        $timestamp = filemtime(OC::$SERVERROOT . '/version.php');
463
+        require OC::$SERVERROOT . '/version.php';
464
+        /** @var $timestamp int */
465
+        self::$versionCache['OC_Version_Timestamp'] = $timestamp;
466
+        /** @var $OC_Version string */
467
+        self::$versionCache['OC_Version'] = $OC_Version;
468
+        /** @var $OC_VersionString string */
469
+        self::$versionCache['OC_VersionString'] = $OC_VersionString;
470
+        /** @var $OC_Build string */
471
+        self::$versionCache['OC_Build'] = $OC_Build;
472
+
473
+        /** @var $OC_Channel string */
474
+        self::$versionCache['OC_Channel'] = $OC_Channel;
475
+    }
476
+
477
+    /**
478
+     * generates a path for JS/CSS files. If no application is provided it will create the path for core.
479
+     *
480
+     * @param string $application application to get the files from
481
+     * @param string $directory directory within this application (css, js, vendor, etc)
482
+     * @param string $file the file inside of the above folder
483
+     * @return string the path
484
+     */
485
+    private static function generatePath($application, $directory, $file) {
486
+        if (is_null($file)) {
487
+            $file = $application;
488
+            $application = "";
489
+        }
490
+        if (!empty($application)) {
491
+            return "$application/$directory/$file";
492
+        } else {
493
+            return "$directory/$file";
494
+        }
495
+    }
496
+
497
+    /**
498
+     * add a javascript file
499
+     *
500
+     * @param string $application application id
501
+     * @param string|null $file filename
502
+     * @param bool $prepend prepend the Script to the beginning of the list
503
+     * @return void
504
+     */
505
+    public static function addScript($application, $file = null, $prepend = false) {
506
+        $path = OC_Util::generatePath($application, 'js', $file);
507
+
508
+        // core js files need separate handling
509
+        if ($application !== 'core' && $file !== null) {
510
+            self::addTranslations ( $application );
511
+        }
512
+        self::addExternalResource($application, $prepend, $path, "script");
513
+    }
514
+
515
+    /**
516
+     * add a javascript file from the vendor sub folder
517
+     *
518
+     * @param string $application application id
519
+     * @param string|null $file filename
520
+     * @param bool $prepend prepend the Script to the beginning of the list
521
+     * @return void
522
+     */
523
+    public static function addVendorScript($application, $file = null, $prepend = false) {
524
+        $path = OC_Util::generatePath($application, 'vendor', $file);
525
+        self::addExternalResource($application, $prepend, $path, "script");
526
+    }
527
+
528
+    /**
529
+     * add a translation JS file
530
+     *
531
+     * @param string $application application id
532
+     * @param string $languageCode language code, defaults to the current language
533
+     * @param bool $prepend prepend the Script to the beginning of the list
534
+     */
535
+    public static function addTranslations($application, $languageCode = null, $prepend = false) {
536
+        if (is_null($languageCode)) {
537
+            $languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
538
+        }
539
+        if (!empty($application)) {
540
+            $path = "$application/l10n/$languageCode";
541
+        } else {
542
+            $path = "l10n/$languageCode";
543
+        }
544
+        self::addExternalResource($application, $prepend, $path, "script");
545
+    }
546
+
547
+    /**
548
+     * add a css file
549
+     *
550
+     * @param string $application application id
551
+     * @param string|null $file filename
552
+     * @param bool $prepend prepend the Style to the beginning of the list
553
+     * @return void
554
+     */
555
+    public static function addStyle($application, $file = null, $prepend = false) {
556
+        $path = OC_Util::generatePath($application, 'css', $file);
557
+        self::addExternalResource($application, $prepend, $path, "style");
558
+    }
559
+
560
+    /**
561
+     * add a css file from the vendor sub folder
562
+     *
563
+     * @param string $application application id
564
+     * @param string|null $file filename
565
+     * @param bool $prepend prepend the Style to the beginning of the list
566
+     * @return void
567
+     */
568
+    public static function addVendorStyle($application, $file = null, $prepend = false) {
569
+        $path = OC_Util::generatePath($application, 'vendor', $file);
570
+        self::addExternalResource($application, $prepend, $path, "style");
571
+    }
572
+
573
+    /**
574
+     * add an external resource css/js file
575
+     *
576
+     * @param string $application application id
577
+     * @param bool $prepend prepend the file to the beginning of the list
578
+     * @param string $path
579
+     * @param string $type (script or style)
580
+     * @return void
581
+     */
582
+    private static function addExternalResource($application, $prepend, $path, $type = "script") {
583
+
584
+        if ($type === "style") {
585
+            if (!in_array($path, self::$styles)) {
586
+                if ($prepend === true) {
587
+                    array_unshift ( self::$styles, $path );
588
+                } else {
589
+                    self::$styles[] = $path;
590
+                }
591
+            }
592
+        } elseif ($type === "script") {
593
+            if (!in_array($path, self::$scripts)) {
594
+                if ($prepend === true) {
595
+                    array_unshift ( self::$scripts, $path );
596
+                } else {
597
+                    self::$scripts [] = $path;
598
+                }
599
+            }
600
+        }
601
+    }
602
+
603
+    /**
604
+     * Add a custom element to the header
605
+     * If $text is null then the element will be written as empty element.
606
+     * So use "" to get a closing tag.
607
+     * @param string $tag tag name of the element
608
+     * @param array $attributes array of attributes for the element
609
+     * @param string $text the text content for the element
610
+     */
611
+    public static function addHeader($tag, $attributes, $text=null) {
612
+        self::$headers[] = array(
613
+            'tag' => $tag,
614
+            'attributes' => $attributes,
615
+            'text' => $text
616
+        );
617
+    }
618
+
619
+    /**
620
+     * formats a timestamp in the "right" way
621
+     *
622
+     * @param int $timestamp
623
+     * @param bool $dateOnly option to omit time from the result
624
+     * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
625
+     * @return string timestamp
626
+     *
627
+     * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
628
+     */
629
+    public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
630
+        if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
631
+            $timeZone = new \DateTimeZone($timeZone);
632
+        }
633
+
634
+        /** @var \OC\DateTimeFormatter $formatter */
635
+        $formatter = \OC::$server->query('DateTimeFormatter');
636
+        if ($dateOnly) {
637
+            return $formatter->formatDate($timestamp, 'long', $timeZone);
638
+        }
639
+        return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
640
+    }
641
+
642
+    /**
643
+     * check if the current server configuration is suitable for ownCloud
644
+     *
645
+     * @param \OC\SystemConfig $config
646
+     * @return array arrays with error messages and hints
647
+     */
648
+    public static function checkServer(\OC\SystemConfig $config) {
649
+        $l = \OC::$server->getL10N('lib');
650
+        $errors = array();
651
+        $CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
652
+
653
+        if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
654
+            // this check needs to be done every time
655
+            $errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
656
+        }
657
+
658
+        // Assume that if checkServer() succeeded before in this session, then all is fine.
659
+        if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
660
+            return $errors;
661
+        }
662
+
663
+        $webServerRestart = false;
664
+        $setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
665
+            \OC::$server->getThemingDefaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
666
+
667
+        $urlGenerator = \OC::$server->getURLGenerator();
668
+
669
+        $availableDatabases = $setup->getSupportedDatabases();
670
+        if (empty($availableDatabases)) {
671
+            $errors[] = array(
672
+                'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
673
+                'hint' => '' //TODO: sane hint
674
+            );
675
+            $webServerRestart = true;
676
+        }
677
+
678
+        // Check if config folder is writable.
679
+        if(!OC_Helper::isReadOnlyConfigEnabled()) {
680
+            if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
681
+                $errors[] = array(
682
+                    'error' => $l->t('Cannot write into "config" directory'),
683
+                    'hint' => $l->t('This can usually be fixed by '
684
+                        . '%sgiving the webserver write access to the config directory%s.',
685
+                        array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
686
+                );
687
+            }
688
+        }
689
+
690
+        // Check if there is a writable install folder.
691
+        if ($config->getValue('appstoreenabled', true)) {
692
+            if (OC_App::getInstallPath() === null
693
+                || !is_writable(OC_App::getInstallPath())
694
+                || !is_readable(OC_App::getInstallPath())
695
+            ) {
696
+                $errors[] = array(
697
+                    'error' => $l->t('Cannot write into "apps" directory'),
698
+                    'hint' => $l->t('This can usually be fixed by '
699
+                        . '%sgiving the webserver write access to the apps directory%s'
700
+                        . ' or disabling the appstore in the config file.',
701
+                        array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
702
+                );
703
+            }
704
+        }
705
+        // Create root dir.
706
+        if ($config->getValue('installed', false)) {
707
+            if (!is_dir($CONFIG_DATADIRECTORY)) {
708
+                $success = @mkdir($CONFIG_DATADIRECTORY);
709
+                if ($success) {
710
+                    $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
711
+                } else {
712
+                    $errors[] = array(
713
+                        'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)),
714
+                        'hint' => $l->t('This can usually be fixed by '
715
+                            . '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
716
+                            array($urlGenerator->linkToDocs('admin-dir_permissions')))
717
+                    );
718
+                }
719
+            } else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
720
+                //common hint for all file permissions error messages
721
+                $permissionsHint = $l->t('Permissions can usually be fixed by '
722
+                    . '%sgiving the webserver write access to the root directory%s.',
723
+                    array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
724
+                $errors[] = array(
725
+                    'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable',
726
+                    'hint' => $permissionsHint
727
+                );
728
+            } else {
729
+                $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
730
+            }
731
+        }
732
+
733
+        if (!OC_Util::isSetLocaleWorking()) {
734
+            $errors[] = array(
735
+                'error' => $l->t('Setting locale to %s failed',
736
+                    array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
737
+                        . 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
738
+                'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
739
+            );
740
+        }
741
+
742
+        // Contains the dependencies that should be checked against
743
+        // classes = class_exists
744
+        // functions = function_exists
745
+        // defined = defined
746
+        // ini = ini_get
747
+        // If the dependency is not found the missing module name is shown to the EndUser
748
+        // When adding new checks always verify that they pass on Travis as well
749
+        // for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
750
+        $dependencies = array(
751
+            'classes' => array(
752
+                'ZipArchive' => 'zip',
753
+                'DOMDocument' => 'dom',
754
+                'XMLWriter' => 'XMLWriter',
755
+                'XMLReader' => 'XMLReader',
756
+            ),
757
+            'functions' => [
758
+                'xml_parser_create' => 'libxml',
759
+                'mb_strcut' => 'mb multibyte',
760
+                'ctype_digit' => 'ctype',
761
+                'json_encode' => 'JSON',
762
+                'gd_info' => 'GD',
763
+                'gzencode' => 'zlib',
764
+                'iconv' => 'iconv',
765
+                'simplexml_load_string' => 'SimpleXML',
766
+                'hash' => 'HASH Message Digest Framework',
767
+                'curl_init' => 'cURL',
768
+                'openssl_verify' => 'OpenSSL',
769
+            ],
770
+            'defined' => array(
771
+                'PDO::ATTR_DRIVER_NAME' => 'PDO'
772
+            ),
773
+            'ini' => [
774
+                'default_charset' => 'UTF-8',
775
+            ],
776
+        );
777
+        $missingDependencies = array();
778
+        $invalidIniSettings = [];
779
+        $moduleHint = $l->t('Please ask your server administrator to install the module.');
780
+
781
+        /**
782
+         * FIXME: The dependency check does not work properly on HHVM on the moment
783
+         *        and prevents installation. Once HHVM is more compatible with our
784
+         *        approach to check for these values we should re-enable those
785
+         *        checks.
786
+         */
787
+        $iniWrapper = \OC::$server->getIniWrapper();
788
+        if (!self::runningOnHhvm()) {
789
+            foreach ($dependencies['classes'] as $class => $module) {
790
+                if (!class_exists($class)) {
791
+                    $missingDependencies[] = $module;
792
+                }
793
+            }
794
+            foreach ($dependencies['functions'] as $function => $module) {
795
+                if (!function_exists($function)) {
796
+                    $missingDependencies[] = $module;
797
+                }
798
+            }
799
+            foreach ($dependencies['defined'] as $defined => $module) {
800
+                if (!defined($defined)) {
801
+                    $missingDependencies[] = $module;
802
+                }
803
+            }
804
+            foreach ($dependencies['ini'] as $setting => $expected) {
805
+                if (is_bool($expected)) {
806
+                    if ($iniWrapper->getBool($setting) !== $expected) {
807
+                        $invalidIniSettings[] = [$setting, $expected];
808
+                    }
809
+                }
810
+                if (is_int($expected)) {
811
+                    if ($iniWrapper->getNumeric($setting) !== $expected) {
812
+                        $invalidIniSettings[] = [$setting, $expected];
813
+                    }
814
+                }
815
+                if (is_string($expected)) {
816
+                    if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
817
+                        $invalidIniSettings[] = [$setting, $expected];
818
+                    }
819
+                }
820
+            }
821
+        }
822
+
823
+        foreach($missingDependencies as $missingDependency) {
824
+            $errors[] = array(
825
+                'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
826
+                'hint' => $moduleHint
827
+            );
828
+            $webServerRestart = true;
829
+        }
830
+        foreach($invalidIniSettings as $setting) {
831
+            if(is_bool($setting[1])) {
832
+                $setting[1] = ($setting[1]) ? 'on' : 'off';
833
+            }
834
+            $errors[] = [
835
+                'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
836
+                'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
837
+            ];
838
+            $webServerRestart = true;
839
+        }
840
+
841
+        /**
842
+         * The mbstring.func_overload check can only be performed if the mbstring
843
+         * module is installed as it will return null if the checking setting is
844
+         * not available and thus a check on the boolean value fails.
845
+         *
846
+         * TODO: Should probably be implemented in the above generic dependency
847
+         *       check somehow in the long-term.
848
+         */
849
+        if($iniWrapper->getBool('mbstring.func_overload') !== null &&
850
+            $iniWrapper->getBool('mbstring.func_overload') === true) {
851
+            $errors[] = array(
852
+                'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
853
+                'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
854
+            );
855
+        }
856
+
857
+        if(function_exists('xml_parser_create') &&
858
+            LIBXML_LOADED_VERSION < 20700 ) {
859
+            $version = LIBXML_LOADED_VERSION;
860
+            $major = floor($version/10000);
861
+            $version -= ($major * 10000);
862
+            $minor = floor($version/100);
863
+            $version -= ($minor * 100);
864
+            $patch = $version;
865
+            $errors[] = array(
866
+                'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
867
+                'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
868
+            );
869
+        }
870
+
871
+        if (!self::isAnnotationsWorking()) {
872
+            $errors[] = array(
873
+                'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
874
+                'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
875
+            );
876
+        }
877
+
878
+        if (!\OC::$CLI && $webServerRestart) {
879
+            $errors[] = array(
880
+                'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
881
+                'hint' => $l->t('Please ask your server administrator to restart the web server.')
882
+            );
883
+        }
884
+
885
+        $errors = array_merge($errors, self::checkDatabaseVersion());
886
+
887
+        // Cache the result of this function
888
+        \OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
889
+
890
+        return $errors;
891
+    }
892
+
893
+    /**
894
+     * Check the database version
895
+     *
896
+     * @return array errors array
897
+     */
898
+    public static function checkDatabaseVersion() {
899
+        $l = \OC::$server->getL10N('lib');
900
+        $errors = array();
901
+        $dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
902
+        if ($dbType === 'pgsql') {
903
+            // check PostgreSQL version
904
+            try {
905
+                $result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
906
+                $data = $result->fetchRow();
907
+                if (isset($data['server_version'])) {
908
+                    $version = $data['server_version'];
909
+                    if (version_compare($version, '9.0.0', '<')) {
910
+                        $errors[] = array(
911
+                            'error' => $l->t('PostgreSQL >= 9 required'),
912
+                            'hint' => $l->t('Please upgrade your database version')
913
+                        );
914
+                    }
915
+                }
916
+            } catch (\Doctrine\DBAL\DBALException $e) {
917
+                $logger = \OC::$server->getLogger();
918
+                $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
919
+                $logger->logException($e);
920
+            }
921
+        }
922
+        return $errors;
923
+    }
924
+
925
+    /**
926
+     * Check for correct file permissions of data directory
927
+     *
928
+     * @param string $dataDirectory
929
+     * @return array arrays with error messages and hints
930
+     */
931
+    public static function checkDataDirectoryPermissions($dataDirectory) {
932
+        $l = \OC::$server->getL10N('lib');
933
+        $errors = array();
934
+        $permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
935
+            . ' cannot be listed by other users.');
936
+        $perms = substr(decoct(@fileperms($dataDirectory)), -3);
937
+        if (substr($perms, -1) != '0') {
938
+            chmod($dataDirectory, 0770);
939
+            clearstatcache();
940
+            $perms = substr(decoct(@fileperms($dataDirectory)), -3);
941
+            if (substr($perms, 2, 1) != '0') {
942
+                $errors[] = array(
943
+                    'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)),
944
+                    'hint' => $permissionsModHint
945
+                );
946
+            }
947
+        }
948
+        return $errors;
949
+    }
950
+
951
+    /**
952
+     * Check that the data directory exists and is valid by
953
+     * checking the existence of the ".ocdata" file.
954
+     *
955
+     * @param string $dataDirectory data directory path
956
+     * @return array errors found
957
+     */
958
+    public static function checkDataDirectoryValidity($dataDirectory) {
959
+        $l = \OC::$server->getL10N('lib');
960
+        $errors = [];
961
+        if ($dataDirectory[0] !== '/') {
962
+            $errors[] = [
963
+                'error' => $l->t('Data directory (%s) must be an absolute path', [$dataDirectory]),
964
+                'hint' => $l->t('Check the value of "datadirectory" in your configuration')
965
+            ];
966
+        }
967
+        if (!file_exists($dataDirectory . '/.ocdata')) {
968
+            $errors[] = [
969
+                'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
970
+                'hint' => $l->t('Please check that the data directory contains a file' .
971
+                    ' ".ocdata" in its root.')
972
+            ];
973
+        }
974
+        return $errors;
975
+    }
976
+
977
+    /**
978
+     * Check if the user is logged in, redirects to home if not. With
979
+     * redirect URL parameter to the request URI.
980
+     *
981
+     * @return void
982
+     */
983
+    public static function checkLoggedIn() {
984
+        // Check if we are a user
985
+        if (!\OC::$server->getUserSession()->isLoggedIn()) {
986
+            header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
987
+                        'core.login.showLoginForm',
988
+                        [
989
+                            'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
990
+                        ]
991
+                    )
992
+            );
993
+            exit();
994
+        }
995
+        // Redirect to index page if 2FA challenge was not solved yet
996
+        if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
997
+            header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
998
+            exit();
999
+        }
1000
+    }
1001
+
1002
+    /**
1003
+     * Check if the user is a admin, redirects to home if not
1004
+     *
1005
+     * @return void
1006
+     */
1007
+    public static function checkAdminUser() {
1008
+        OC_Util::checkLoggedIn();
1009
+        if (!OC_User::isAdminUser(OC_User::getUser())) {
1010
+            header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1011
+            exit();
1012
+        }
1013
+    }
1014
+
1015
+    /**
1016
+     * Check if the user is a subadmin, redirects to home if not
1017
+     *
1018
+     * @return null|boolean $groups where the current user is subadmin
1019
+     */
1020
+    public static function checkSubAdminUser() {
1021
+        OC_Util::checkLoggedIn();
1022
+        $userObject = \OC::$server->getUserSession()->getUser();
1023
+        $isSubAdmin = false;
1024
+        if($userObject !== null) {
1025
+            $isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1026
+        }
1027
+
1028
+        if (!$isSubAdmin) {
1029
+            header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1030
+            exit();
1031
+        }
1032
+        return true;
1033
+    }
1034
+
1035
+    /**
1036
+     * Returns the URL of the default page
1037
+     * based on the system configuration and
1038
+     * the apps visible for the current user
1039
+     *
1040
+     * @return string URL
1041
+     */
1042
+    public static function getDefaultPageUrl() {
1043
+        $urlGenerator = \OC::$server->getURLGenerator();
1044
+        // Deny the redirect if the URL contains a @
1045
+        // This prevents unvalidated redirects like ?redirect_url=:[email protected]
1046
+        if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1047
+            $location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1048
+        } else {
1049
+            $defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
1050
+            if ($defaultPage) {
1051
+                $location = $urlGenerator->getAbsoluteURL($defaultPage);
1052
+            } else {
1053
+                $appId = 'files';
1054
+                $defaultApps = explode(',', \OCP\Config::getSystemValue('defaultapp', 'files'));
1055
+                // find the first app that is enabled for the current user
1056
+                foreach ($defaultApps as $defaultApp) {
1057
+                    $defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1058
+                    if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1059
+                        $appId = $defaultApp;
1060
+                        break;
1061
+                    }
1062
+                }
1063
+
1064
+                if(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1065
+                    $location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1066
+                } else {
1067
+                    $location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1068
+                }
1069
+            }
1070
+        }
1071
+        return $location;
1072
+    }
1073
+
1074
+    /**
1075
+     * Redirect to the user default page
1076
+     *
1077
+     * @return void
1078
+     */
1079
+    public static function redirectToDefaultPage() {
1080
+        $location = self::getDefaultPageUrl();
1081
+        header('Location: ' . $location);
1082
+        exit();
1083
+    }
1084
+
1085
+    /**
1086
+     * get an id unique for this instance
1087
+     *
1088
+     * @return string
1089
+     */
1090
+    public static function getInstanceId() {
1091
+        $id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1092
+        if (is_null($id)) {
1093
+            // We need to guarantee at least one letter in instanceid so it can be used as the session_name
1094
+            $id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1095
+            \OC::$server->getSystemConfig()->setValue('instanceid', $id);
1096
+        }
1097
+        return $id;
1098
+    }
1099
+
1100
+    /**
1101
+     * Public function to sanitize HTML
1102
+     *
1103
+     * This function is used to sanitize HTML and should be applied on any
1104
+     * string or array of strings before displaying it on a web page.
1105
+     *
1106
+     * @param string|array $value
1107
+     * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1108
+     */
1109
+    public static function sanitizeHTML($value) {
1110
+        if (is_array($value)) {
1111
+            $value = array_map(function($value) {
1112
+                return self::sanitizeHTML($value);
1113
+            }, $value);
1114
+        } else {
1115
+            // Specify encoding for PHP<5.4
1116
+            $value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1117
+        }
1118
+        return $value;
1119
+    }
1120
+
1121
+    /**
1122
+     * Public function to encode url parameters
1123
+     *
1124
+     * This function is used to encode path to file before output.
1125
+     * Encoding is done according to RFC 3986 with one exception:
1126
+     * Character '/' is preserved as is.
1127
+     *
1128
+     * @param string $component part of URI to encode
1129
+     * @return string
1130
+     */
1131
+    public static function encodePath($component) {
1132
+        $encoded = rawurlencode($component);
1133
+        $encoded = str_replace('%2F', '/', $encoded);
1134
+        return $encoded;
1135
+    }
1136
+
1137
+
1138
+    public function createHtaccessTestFile(\OCP\IConfig $config) {
1139
+        // php dev server does not support htaccess
1140
+        if (php_sapi_name() === 'cli-server') {
1141
+            return false;
1142
+        }
1143
+
1144
+        // testdata
1145
+        $fileName = '/htaccesstest.txt';
1146
+        $testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1147
+
1148
+        // creating a test file
1149
+        $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1150
+
1151
+        if (file_exists($testFile)) {// already running this test, possible recursive call
1152
+            return false;
1153
+        }
1154
+
1155
+        $fp = @fopen($testFile, 'w');
1156
+        if (!$fp) {
1157
+            throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1158
+                'Make sure it is possible for the webserver to write to ' . $testFile);
1159
+        }
1160
+        fwrite($fp, $testContent);
1161
+        fclose($fp);
1162
+
1163
+        return $testContent;
1164
+    }
1165
+
1166
+    /**
1167
+     * Check if the .htaccess file is working
1168
+     * @param \OCP\IConfig $config
1169
+     * @return bool
1170
+     * @throws Exception
1171
+     * @throws \OC\HintException If the test file can't get written.
1172
+     */
1173
+    public function isHtaccessWorking(\OCP\IConfig $config) {
1174
+
1175
+        if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1176
+            return true;
1177
+        }
1178
+
1179
+        $testContent = $this->createHtaccessTestFile($config);
1180
+        if ($testContent === false) {
1181
+            return false;
1182
+        }
1183
+
1184
+        $fileName = '/htaccesstest.txt';
1185
+        $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1186
+
1187
+        // accessing the file via http
1188
+        $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1189
+        try {
1190
+            $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1191
+        } catch (\Exception $e) {
1192
+            $content = false;
1193
+        }
1194
+
1195
+        // cleanup
1196
+        @unlink($testFile);
1197
+
1198
+        /*
1199 1199
 		 * If the content is not equal to test content our .htaccess
1200 1200
 		 * is working as required
1201 1201
 		 */
1202
-		return $content !== $testContent;
1203
-	}
1204
-
1205
-	/**
1206
-	 * Check if the setlocal call does not work. This can happen if the right
1207
-	 * local packages are not available on the server.
1208
-	 *
1209
-	 * @return bool
1210
-	 */
1211
-	public static function isSetLocaleWorking() {
1212
-		\Patchwork\Utf8\Bootup::initLocale();
1213
-		if ('' === basename('§')) {
1214
-			return false;
1215
-		}
1216
-		return true;
1217
-	}
1218
-
1219
-	/**
1220
-	 * Check if it's possible to get the inline annotations
1221
-	 *
1222
-	 * @return bool
1223
-	 */
1224
-	public static function isAnnotationsWorking() {
1225
-		$reflection = new \ReflectionMethod(__METHOD__);
1226
-		$docs = $reflection->getDocComment();
1227
-
1228
-		return (is_string($docs) && strlen($docs) > 50);
1229
-	}
1230
-
1231
-	/**
1232
-	 * Check if the PHP module fileinfo is loaded.
1233
-	 *
1234
-	 * @return bool
1235
-	 */
1236
-	public static function fileInfoLoaded() {
1237
-		return function_exists('finfo_open');
1238
-	}
1239
-
1240
-	/**
1241
-	 * clear all levels of output buffering
1242
-	 *
1243
-	 * @return void
1244
-	 */
1245
-	public static function obEnd() {
1246
-		while (ob_get_level()) {
1247
-			ob_end_clean();
1248
-		}
1249
-	}
1250
-
1251
-	/**
1252
-	 * Checks whether the server is running on Mac OS X
1253
-	 *
1254
-	 * @return bool true if running on Mac OS X, false otherwise
1255
-	 */
1256
-	public static function runningOnMac() {
1257
-		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1258
-	}
1259
-
1260
-	/**
1261
-	 * Checks whether server is running on HHVM
1262
-	 *
1263
-	 * @return bool True if running on HHVM, false otherwise
1264
-	 */
1265
-	public static function runningOnHhvm() {
1266
-		return defined('HHVM_VERSION');
1267
-	}
1268
-
1269
-	/**
1270
-	 * Handles the case that there may not be a theme, then check if a "default"
1271
-	 * theme exists and take that one
1272
-	 *
1273
-	 * @return string the theme
1274
-	 */
1275
-	public static function getTheme() {
1276
-		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1277
-
1278
-		if ($theme === '') {
1279
-			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1280
-				$theme = 'default';
1281
-			}
1282
-		}
1283
-
1284
-		return $theme;
1285
-	}
1286
-
1287
-	/**
1288
-	 * Clear a single file from the opcode cache
1289
-	 * This is useful for writing to the config file
1290
-	 * in case the opcode cache does not re-validate files
1291
-	 * Returns true if successful, false if unsuccessful:
1292
-	 * caller should fall back on clearing the entire cache
1293
-	 * with clearOpcodeCache() if unsuccessful
1294
-	 *
1295
-	 * @param string $path the path of the file to clear from the cache
1296
-	 * @return bool true if underlying function returns true, otherwise false
1297
-	 */
1298
-	public static function deleteFromOpcodeCache($path) {
1299
-		$ret = false;
1300
-		if ($path) {
1301
-			// APC >= 3.1.1
1302
-			if (function_exists('apc_delete_file')) {
1303
-				$ret = @apc_delete_file($path);
1304
-			}
1305
-			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1306
-			if (function_exists('opcache_invalidate')) {
1307
-				$ret = opcache_invalidate($path);
1308
-			}
1309
-		}
1310
-		return $ret;
1311
-	}
1312
-
1313
-	/**
1314
-	 * Clear the opcode cache if one exists
1315
-	 * This is necessary for writing to the config file
1316
-	 * in case the opcode cache does not re-validate files
1317
-	 *
1318
-	 * @return void
1319
-	 */
1320
-	public static function clearOpcodeCache() {
1321
-		// APC
1322
-		if (function_exists('apc_clear_cache')) {
1323
-			apc_clear_cache();
1324
-		}
1325
-		// Zend Opcache
1326
-		if (function_exists('accelerator_reset')) {
1327
-			accelerator_reset();
1328
-		}
1329
-		// XCache
1330
-		if (function_exists('xcache_clear_cache')) {
1331
-			if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1332
-				\OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1333
-			} else {
1334
-				@xcache_clear_cache(XC_TYPE_PHP, 0);
1335
-			}
1336
-		}
1337
-		// Opcache (PHP >= 5.5)
1338
-		if (function_exists('opcache_reset')) {
1339
-			opcache_reset();
1340
-		}
1341
-	}
1342
-
1343
-	/**
1344
-	 * Normalize a unicode string
1345
-	 *
1346
-	 * @param string $value a not normalized string
1347
-	 * @return bool|string
1348
-	 */
1349
-	public static function normalizeUnicode($value) {
1350
-		if(Normalizer::isNormalized($value)) {
1351
-			return $value;
1352
-		}
1353
-
1354
-		$normalizedValue = Normalizer::normalize($value);
1355
-		if ($normalizedValue === null || $normalizedValue === false) {
1356
-			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1357
-			return $value;
1358
-		}
1359
-
1360
-		return $normalizedValue;
1361
-	}
1362
-
1363
-	/**
1364
-	 * @param boolean|string $file
1365
-	 * @return string
1366
-	 */
1367
-	public static function basename($file) {
1368
-		$file = rtrim($file, '/');
1369
-		$t = explode('/', $file);
1370
-		return array_pop($t);
1371
-	}
1372
-
1373
-	/**
1374
-	 * A human readable string is generated based on version and build number
1375
-	 *
1376
-	 * @return string
1377
-	 */
1378
-	public static function getHumanVersion() {
1379
-		$version = OC_Util::getVersionString();
1380
-		$build = OC_Util::getBuild();
1381
-		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1382
-			$version .= ' Build:' . $build;
1383
-		}
1384
-		return $version;
1385
-	}
1386
-
1387
-	/**
1388
-	 * Returns whether the given file name is valid
1389
-	 *
1390
-	 * @param string $file file name to check
1391
-	 * @return bool true if the file name is valid, false otherwise
1392
-	 * @deprecated use \OC\Files\View::verifyPath()
1393
-	 */
1394
-	public static function isValidFileName($file) {
1395
-		$trimmed = trim($file);
1396
-		if ($trimmed === '') {
1397
-			return false;
1398
-		}
1399
-		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1400
-			return false;
1401
-		}
1402
-		foreach (str_split($trimmed) as $char) {
1403
-			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1404
-				return false;
1405
-			}
1406
-		}
1407
-		return true;
1408
-	}
1409
-
1410
-	/**
1411
-	 * Check whether the instance needs to perform an upgrade,
1412
-	 * either when the core version is higher or any app requires
1413
-	 * an upgrade.
1414
-	 *
1415
-	 * @param \OC\SystemConfig $config
1416
-	 * @return bool whether the core or any app needs an upgrade
1417
-	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1418
-	 */
1419
-	public static function needUpgrade(\OC\SystemConfig $config) {
1420
-		if ($config->getValue('installed', false)) {
1421
-			$installedVersion = $config->getValue('version', '0.0.0');
1422
-			$currentVersion = implode('.', \OCP\Util::getVersion());
1423
-			$versionDiff = version_compare($currentVersion, $installedVersion);
1424
-			if ($versionDiff > 0) {
1425
-				return true;
1426
-			} else if ($config->getValue('debug', false) && $versionDiff < 0) {
1427
-				// downgrade with debug
1428
-				$installedMajor = explode('.', $installedVersion);
1429
-				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1430
-				$currentMajor = explode('.', $currentVersion);
1431
-				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1432
-				if ($installedMajor === $currentMajor) {
1433
-					// Same major, allow downgrade for developers
1434
-					return true;
1435
-				} else {
1436
-					// downgrade attempt, throw exception
1437
-					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1438
-				}
1439
-			} else if ($versionDiff < 0) {
1440
-				// downgrade attempt, throw exception
1441
-				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1442
-			}
1443
-
1444
-			// also check for upgrades for apps (independently from the user)
1445
-			$apps = \OC_App::getEnabledApps(false, true);
1446
-			$shouldUpgrade = false;
1447
-			foreach ($apps as $app) {
1448
-				if (\OC_App::shouldUpgrade($app)) {
1449
-					$shouldUpgrade = true;
1450
-					break;
1451
-				}
1452
-			}
1453
-			return $shouldUpgrade;
1454
-		} else {
1455
-			return false;
1456
-		}
1457
-	}
1202
+        return $content !== $testContent;
1203
+    }
1204
+
1205
+    /**
1206
+     * Check if the setlocal call does not work. This can happen if the right
1207
+     * local packages are not available on the server.
1208
+     *
1209
+     * @return bool
1210
+     */
1211
+    public static function isSetLocaleWorking() {
1212
+        \Patchwork\Utf8\Bootup::initLocale();
1213
+        if ('' === basename('§')) {
1214
+            return false;
1215
+        }
1216
+        return true;
1217
+    }
1218
+
1219
+    /**
1220
+     * Check if it's possible to get the inline annotations
1221
+     *
1222
+     * @return bool
1223
+     */
1224
+    public static function isAnnotationsWorking() {
1225
+        $reflection = new \ReflectionMethod(__METHOD__);
1226
+        $docs = $reflection->getDocComment();
1227
+
1228
+        return (is_string($docs) && strlen($docs) > 50);
1229
+    }
1230
+
1231
+    /**
1232
+     * Check if the PHP module fileinfo is loaded.
1233
+     *
1234
+     * @return bool
1235
+     */
1236
+    public static function fileInfoLoaded() {
1237
+        return function_exists('finfo_open');
1238
+    }
1239
+
1240
+    /**
1241
+     * clear all levels of output buffering
1242
+     *
1243
+     * @return void
1244
+     */
1245
+    public static function obEnd() {
1246
+        while (ob_get_level()) {
1247
+            ob_end_clean();
1248
+        }
1249
+    }
1250
+
1251
+    /**
1252
+     * Checks whether the server is running on Mac OS X
1253
+     *
1254
+     * @return bool true if running on Mac OS X, false otherwise
1255
+     */
1256
+    public static function runningOnMac() {
1257
+        return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1258
+    }
1259
+
1260
+    /**
1261
+     * Checks whether server is running on HHVM
1262
+     *
1263
+     * @return bool True if running on HHVM, false otherwise
1264
+     */
1265
+    public static function runningOnHhvm() {
1266
+        return defined('HHVM_VERSION');
1267
+    }
1268
+
1269
+    /**
1270
+     * Handles the case that there may not be a theme, then check if a "default"
1271
+     * theme exists and take that one
1272
+     *
1273
+     * @return string the theme
1274
+     */
1275
+    public static function getTheme() {
1276
+        $theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1277
+
1278
+        if ($theme === '') {
1279
+            if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1280
+                $theme = 'default';
1281
+            }
1282
+        }
1283
+
1284
+        return $theme;
1285
+    }
1286
+
1287
+    /**
1288
+     * Clear a single file from the opcode cache
1289
+     * This is useful for writing to the config file
1290
+     * in case the opcode cache does not re-validate files
1291
+     * Returns true if successful, false if unsuccessful:
1292
+     * caller should fall back on clearing the entire cache
1293
+     * with clearOpcodeCache() if unsuccessful
1294
+     *
1295
+     * @param string $path the path of the file to clear from the cache
1296
+     * @return bool true if underlying function returns true, otherwise false
1297
+     */
1298
+    public static function deleteFromOpcodeCache($path) {
1299
+        $ret = false;
1300
+        if ($path) {
1301
+            // APC >= 3.1.1
1302
+            if (function_exists('apc_delete_file')) {
1303
+                $ret = @apc_delete_file($path);
1304
+            }
1305
+            // Zend OpCache >= 7.0.0, PHP >= 5.5.0
1306
+            if (function_exists('opcache_invalidate')) {
1307
+                $ret = opcache_invalidate($path);
1308
+            }
1309
+        }
1310
+        return $ret;
1311
+    }
1312
+
1313
+    /**
1314
+     * Clear the opcode cache if one exists
1315
+     * This is necessary for writing to the config file
1316
+     * in case the opcode cache does not re-validate files
1317
+     *
1318
+     * @return void
1319
+     */
1320
+    public static function clearOpcodeCache() {
1321
+        // APC
1322
+        if (function_exists('apc_clear_cache')) {
1323
+            apc_clear_cache();
1324
+        }
1325
+        // Zend Opcache
1326
+        if (function_exists('accelerator_reset')) {
1327
+            accelerator_reset();
1328
+        }
1329
+        // XCache
1330
+        if (function_exists('xcache_clear_cache')) {
1331
+            if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1332
+                \OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1333
+            } else {
1334
+                @xcache_clear_cache(XC_TYPE_PHP, 0);
1335
+            }
1336
+        }
1337
+        // Opcache (PHP >= 5.5)
1338
+        if (function_exists('opcache_reset')) {
1339
+            opcache_reset();
1340
+        }
1341
+    }
1342
+
1343
+    /**
1344
+     * Normalize a unicode string
1345
+     *
1346
+     * @param string $value a not normalized string
1347
+     * @return bool|string
1348
+     */
1349
+    public static function normalizeUnicode($value) {
1350
+        if(Normalizer::isNormalized($value)) {
1351
+            return $value;
1352
+        }
1353
+
1354
+        $normalizedValue = Normalizer::normalize($value);
1355
+        if ($normalizedValue === null || $normalizedValue === false) {
1356
+            \OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1357
+            return $value;
1358
+        }
1359
+
1360
+        return $normalizedValue;
1361
+    }
1362
+
1363
+    /**
1364
+     * @param boolean|string $file
1365
+     * @return string
1366
+     */
1367
+    public static function basename($file) {
1368
+        $file = rtrim($file, '/');
1369
+        $t = explode('/', $file);
1370
+        return array_pop($t);
1371
+    }
1372
+
1373
+    /**
1374
+     * A human readable string is generated based on version and build number
1375
+     *
1376
+     * @return string
1377
+     */
1378
+    public static function getHumanVersion() {
1379
+        $version = OC_Util::getVersionString();
1380
+        $build = OC_Util::getBuild();
1381
+        if (!empty($build) and OC_Util::getChannel() === 'daily') {
1382
+            $version .= ' Build:' . $build;
1383
+        }
1384
+        return $version;
1385
+    }
1386
+
1387
+    /**
1388
+     * Returns whether the given file name is valid
1389
+     *
1390
+     * @param string $file file name to check
1391
+     * @return bool true if the file name is valid, false otherwise
1392
+     * @deprecated use \OC\Files\View::verifyPath()
1393
+     */
1394
+    public static function isValidFileName($file) {
1395
+        $trimmed = trim($file);
1396
+        if ($trimmed === '') {
1397
+            return false;
1398
+        }
1399
+        if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1400
+            return false;
1401
+        }
1402
+        foreach (str_split($trimmed) as $char) {
1403
+            if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1404
+                return false;
1405
+            }
1406
+        }
1407
+        return true;
1408
+    }
1409
+
1410
+    /**
1411
+     * Check whether the instance needs to perform an upgrade,
1412
+     * either when the core version is higher or any app requires
1413
+     * an upgrade.
1414
+     *
1415
+     * @param \OC\SystemConfig $config
1416
+     * @return bool whether the core or any app needs an upgrade
1417
+     * @throws \OC\HintException When the upgrade from the given version is not allowed
1418
+     */
1419
+    public static function needUpgrade(\OC\SystemConfig $config) {
1420
+        if ($config->getValue('installed', false)) {
1421
+            $installedVersion = $config->getValue('version', '0.0.0');
1422
+            $currentVersion = implode('.', \OCP\Util::getVersion());
1423
+            $versionDiff = version_compare($currentVersion, $installedVersion);
1424
+            if ($versionDiff > 0) {
1425
+                return true;
1426
+            } else if ($config->getValue('debug', false) && $versionDiff < 0) {
1427
+                // downgrade with debug
1428
+                $installedMajor = explode('.', $installedVersion);
1429
+                $installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1430
+                $currentMajor = explode('.', $currentVersion);
1431
+                $currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1432
+                if ($installedMajor === $currentMajor) {
1433
+                    // Same major, allow downgrade for developers
1434
+                    return true;
1435
+                } else {
1436
+                    // downgrade attempt, throw exception
1437
+                    throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1438
+                }
1439
+            } else if ($versionDiff < 0) {
1440
+                // downgrade attempt, throw exception
1441
+                throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1442
+            }
1443
+
1444
+            // also check for upgrades for apps (independently from the user)
1445
+            $apps = \OC_App::getEnabledApps(false, true);
1446
+            $shouldUpgrade = false;
1447
+            foreach ($apps as $app) {
1448
+                if (\OC_App::shouldUpgrade($app)) {
1449
+                    $shouldUpgrade = true;
1450
+                    break;
1451
+                }
1452
+            }
1453
+            return $shouldUpgrade;
1454
+        } else {
1455
+            return false;
1456
+        }
1457
+    }
1458 1458
 
1459 1459
 }
Please login to merge, or discard this patch.
Spacing   +64 added lines, -64 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
 
77 77
 	private static function initLocalStorageRootFS() {
78 78
 		// mount local file backend as root
79
-		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
79
+		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT."/data");
80 80
 		//first set up the local "root" storage
81 81
 		\OC\Files\Filesystem::initMountManager();
82 82
 		if (!self::$rootMounted) {
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 		\OC\Files\Filesystem::initMountManager();
155 155
 
156 156
 		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
157
-		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
157
+		\OC\Files\Filesystem::addStorageWrapper('mount_options', function($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
158 158
 			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
159 159
 				/** @var \OC\Files\Storage\Common $storage */
160 160
 				$storage->setMountOptions($mount->getOptions());
@@ -162,7 +162,7 @@  discard block
 block discarded – undo
162 162
 			return $storage;
163 163
 		});
164 164
 
165
-		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
165
+		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
166 166
 			if (!$mount->getOption('enable_sharing', true)) {
167 167
 				return new \OC\Files\Storage\Wrapper\PermissionsMask([
168 168
 					'storage' => $storage,
@@ -173,7 +173,7 @@  discard block
 block discarded – undo
173 173
 		});
174 174
 
175 175
 		// install storage availability wrapper, before most other wrappers
176
-		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
176
+		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function($mountPoint, $storage) {
177 177
 			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
178 178
 				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
179 179
 			}
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
 		});
182 182
 
183 183
 		// install storage checksum wrapper
184
-		\OC\Files\Filesystem::addStorageWrapper('oc_checksum', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
184
+		\OC\Files\Filesystem::addStorageWrapper('oc_checksum', function($mountPoint, \OCP\Files\Storage\IStorage $storage) {
185 185
 			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
186 186
 				return new \OC\Files\Storage\Wrapper\Checksum(['storage' => $storage]);
187 187
 			}
@@ -191,14 +191,14 @@  discard block
 block discarded – undo
191 191
 		}, 1);
192 192
 
193 193
 
194
-		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
194
+		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
195 195
 			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
196 196
 				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
197 197
 			}
198 198
 			return $storage;
199 199
 		});
200 200
 
201
-		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
201
+		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function($mountPoint, $storage) {
202 202
 			// set up quota for home storages, even for other users
203 203
 			// which can happen when using sharing
204 204
 
@@ -240,7 +240,7 @@  discard block
 block discarded – undo
240 240
 		//if we aren't logged in, there is no use to set up the filesystem
241 241
 		if ($user != "") {
242 242
 
243
-			$userDir = '/' . $user . '/files';
243
+			$userDir = '/'.$user.'/files';
244 244
 
245 245
 			//jail the user into his "home" directory
246 246
 			\OC\Files\Filesystem::init($user, $userDir);
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
 			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
320 320
 		}
321 321
 		$userQuota = $user->getQuota();
322
-		if($userQuota === 'none') {
322
+		if ($userQuota === 'none') {
323 323
 			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
324 324
 		}
325 325
 		return OC_Helper::computerFileSize($userQuota);
@@ -334,15 +334,15 @@  discard block
 block discarded – undo
334 334
 	 */
335 335
 	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
336 336
 
337
-		$skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
337
+		$skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT.'/core/skeleton');
338 338
 		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
339 339
 
340 340
 		if ($instanceId === null) {
341 341
 			throw new \RuntimeException('no instance id!');
342 342
 		}
343
-		$appdata = 'appdata_' . $instanceId;
343
+		$appdata = 'appdata_'.$instanceId;
344 344
 		if ($userId === $appdata) {
345
-			throw new \RuntimeException('username is reserved name: ' . $appdata);
345
+			throw new \RuntimeException('username is reserved name: '.$appdata);
346 346
 		}
347 347
 
348 348
 		if (!empty($skeletonDirectory)) {
@@ -369,7 +369,7 @@  discard block
 block discarded – undo
369 369
 
370 370
 		// Verify if folder exists
371 371
 		$dir = opendir($source);
372
-		if($dir === false) {
372
+		if ($dir === false) {
373 373
 			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
374 374
 			return;
375 375
 		}
@@ -377,14 +377,14 @@  discard block
 block discarded – undo
377 377
 		// Copy the files
378 378
 		while (false !== ($file = readdir($dir))) {
379 379
 			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
380
-				if (is_dir($source . '/' . $file)) {
380
+				if (is_dir($source.'/'.$file)) {
381 381
 					$child = $target->newFolder($file);
382
-					self::copyr($source . '/' . $file, $child);
382
+					self::copyr($source.'/'.$file, $child);
383 383
 				} else {
384 384
 					$child = $target->newFile($file);
385
-					$sourceStream = fopen($source . '/' . $file, 'r');
386
-					if($sourceStream === false) {
387
-						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
385
+					$sourceStream = fopen($source.'/'.$file, 'r');
386
+					if ($sourceStream === false) {
387
+						$logger->error(sprintf('Could not fopen "%s"', $source.'/'.$file), ['app' => 'core']);
388 388
 						closedir($dir);
389 389
 						return;
390 390
 					}
@@ -459,8 +459,8 @@  discard block
 block discarded – undo
459 459
 			return;
460 460
 		}
461 461
 
462
-		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
463
-		require OC::$SERVERROOT . '/version.php';
462
+		$timestamp = filemtime(OC::$SERVERROOT.'/version.php');
463
+		require OC::$SERVERROOT.'/version.php';
464 464
 		/** @var $timestamp int */
465 465
 		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
466 466
 		/** @var $OC_Version string */
@@ -507,7 +507,7 @@  discard block
 block discarded – undo
507 507
 
508 508
 		// core js files need separate handling
509 509
 		if ($application !== 'core' && $file !== null) {
510
-			self::addTranslations ( $application );
510
+			self::addTranslations($application);
511 511
 		}
512 512
 		self::addExternalResource($application, $prepend, $path, "script");
513 513
 	}
@@ -584,7 +584,7 @@  discard block
 block discarded – undo
584 584
 		if ($type === "style") {
585 585
 			if (!in_array($path, self::$styles)) {
586 586
 				if ($prepend === true) {
587
-					array_unshift ( self::$styles, $path );
587
+					array_unshift(self::$styles, $path);
588 588
 				} else {
589 589
 					self::$styles[] = $path;
590 590
 				}
@@ -592,7 +592,7 @@  discard block
 block discarded – undo
592 592
 		} elseif ($type === "script") {
593 593
 			if (!in_array($path, self::$scripts)) {
594 594
 				if ($prepend === true) {
595
-					array_unshift ( self::$scripts, $path );
595
+					array_unshift(self::$scripts, $path);
596 596
 				} else {
597 597
 					self::$scripts [] = $path;
598 598
 				}
@@ -608,7 +608,7 @@  discard block
 block discarded – undo
608 608
 	 * @param array $attributes array of attributes for the element
609 609
 	 * @param string $text the text content for the element
610 610
 	 */
611
-	public static function addHeader($tag, $attributes, $text=null) {
611
+	public static function addHeader($tag, $attributes, $text = null) {
612 612
 		self::$headers[] = array(
613 613
 			'tag' => $tag,
614 614
 			'attributes' => $attributes,
@@ -648,7 +648,7 @@  discard block
 block discarded – undo
648 648
 	public static function checkServer(\OC\SystemConfig $config) {
649 649
 		$l = \OC::$server->getL10N('lib');
650 650
 		$errors = array();
651
-		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
651
+		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT.'/data');
652 652
 
653 653
 		if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
654 654
 			// this check needs to be done every time
@@ -676,13 +676,13 @@  discard block
 block discarded – undo
676 676
 		}
677 677
 
678 678
 		// Check if config folder is writable.
679
-		if(!OC_Helper::isReadOnlyConfigEnabled()) {
679
+		if (!OC_Helper::isReadOnlyConfigEnabled()) {
680 680
 			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
681 681
 				$errors[] = array(
682 682
 					'error' => $l->t('Cannot write into "config" directory'),
683 683
 					'hint' => $l->t('This can usually be fixed by '
684 684
 						. '%sgiving the webserver write access to the config directory%s.',
685
-						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
685
+						array('<a href="'.$urlGenerator->linkToDocs('admin-dir_permissions').'" target="_blank" rel="noreferrer">', '</a>'))
686 686
 				);
687 687
 			}
688 688
 		}
@@ -698,7 +698,7 @@  discard block
 block discarded – undo
698 698
 					'hint' => $l->t('This can usually be fixed by '
699 699
 						. '%sgiving the webserver write access to the apps directory%s'
700 700
 						. ' or disabling the appstore in the config file.',
701
-						array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
701
+						array('<a href="'.$urlGenerator->linkToDocs('admin-dir_permissions').'" target="_blank" rel="noreferrer">', '</a>'))
702 702
 				);
703 703
 			}
704 704
 		}
@@ -720,9 +720,9 @@  discard block
 block discarded – undo
720 720
 				//common hint for all file permissions error messages
721 721
 				$permissionsHint = $l->t('Permissions can usually be fixed by '
722 722
 					. '%sgiving the webserver write access to the root directory%s.',
723
-					array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'));
723
+					array('<a href="'.$urlGenerator->linkToDocs('admin-dir_permissions').'" target="_blank" rel="noreferrer">', '</a>'));
724 724
 				$errors[] = array(
725
-					'error' => 'Data directory (' . $CONFIG_DATADIRECTORY . ') not writable',
725
+					'error' => 'Data directory ('.$CONFIG_DATADIRECTORY.') not writable',
726 726
 					'hint' => $permissionsHint
727 727
 				);
728 728
 			} else {
@@ -820,15 +820,15 @@  discard block
 block discarded – undo
820 820
 			}
821 821
 		}
822 822
 
823
-		foreach($missingDependencies as $missingDependency) {
823
+		foreach ($missingDependencies as $missingDependency) {
824 824
 			$errors[] = array(
825 825
 				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
826 826
 				'hint' => $moduleHint
827 827
 			);
828 828
 			$webServerRestart = true;
829 829
 		}
830
-		foreach($invalidIniSettings as $setting) {
831
-			if(is_bool($setting[1])) {
830
+		foreach ($invalidIniSettings as $setting) {
831
+			if (is_bool($setting[1])) {
832 832
 				$setting[1] = ($setting[1]) ? 'on' : 'off';
833 833
 			}
834 834
 			$errors[] = [
@@ -846,7 +846,7 @@  discard block
 block discarded – undo
846 846
 		 * TODO: Should probably be implemented in the above generic dependency
847 847
 		 *       check somehow in the long-term.
848 848
 		 */
849
-		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
849
+		if ($iniWrapper->getBool('mbstring.func_overload') !== null &&
850 850
 			$iniWrapper->getBool('mbstring.func_overload') === true) {
851 851
 			$errors[] = array(
852 852
 				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
@@ -854,16 +854,16 @@  discard block
 block discarded – undo
854 854
 			);
855 855
 		}
856 856
 
857
-		if(function_exists('xml_parser_create') &&
858
-			LIBXML_LOADED_VERSION < 20700 ) {
857
+		if (function_exists('xml_parser_create') &&
858
+			LIBXML_LOADED_VERSION < 20700) {
859 859
 			$version = LIBXML_LOADED_VERSION;
860
-			$major = floor($version/10000);
860
+			$major = floor($version / 10000);
861 861
 			$version -= ($major * 10000);
862
-			$minor = floor($version/100);
862
+			$minor = floor($version / 100);
863 863
 			$version -= ($minor * 100);
864 864
 			$patch = $version;
865 865
 			$errors[] = array(
866
-				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
866
+				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major.'.'.$minor.'.'.$patch]),
867 867
 				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
868 868
 			);
869 869
 		}
@@ -964,10 +964,10 @@  discard block
 block discarded – undo
964 964
 				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
965 965
 			];
966 966
 		}
967
-		if (!file_exists($dataDirectory . '/.ocdata')) {
967
+		if (!file_exists($dataDirectory.'/.ocdata')) {
968 968
 			$errors[] = [
969 969
 				'error' => $l->t('Data directory (%s) is invalid', [$dataDirectory]),
970
-				'hint' => $l->t('Please check that the data directory contains a file' .
970
+				'hint' => $l->t('Please check that the data directory contains a file'.
971 971
 					' ".ocdata" in its root.')
972 972
 			];
973 973
 		}
@@ -983,7 +983,7 @@  discard block
 block discarded – undo
983 983
 	public static function checkLoggedIn() {
984 984
 		// Check if we are a user
985 985
 		if (!\OC::$server->getUserSession()->isLoggedIn()) {
986
-			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
986
+			header('Location: '.\OC::$server->getURLGenerator()->linkToRoute(
987 987
 						'core.login.showLoginForm',
988 988
 						[
989 989
 							'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
@@ -994,7 +994,7 @@  discard block
 block discarded – undo
994 994
 		}
995 995
 		// Redirect to index page if 2FA challenge was not solved yet
996 996
 		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
997
-			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
997
+			header('Location: '.\OCP\Util::linkToAbsolute('', 'index.php'));
998 998
 			exit();
999 999
 		}
1000 1000
 	}
@@ -1007,7 +1007,7 @@  discard block
 block discarded – undo
1007 1007
 	public static function checkAdminUser() {
1008 1008
 		OC_Util::checkLoggedIn();
1009 1009
 		if (!OC_User::isAdminUser(OC_User::getUser())) {
1010
-			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1010
+			header('Location: '.\OCP\Util::linkToAbsolute('', 'index.php'));
1011 1011
 			exit();
1012 1012
 		}
1013 1013
 	}
@@ -1021,12 +1021,12 @@  discard block
 block discarded – undo
1021 1021
 		OC_Util::checkLoggedIn();
1022 1022
 		$userObject = \OC::$server->getUserSession()->getUser();
1023 1023
 		$isSubAdmin = false;
1024
-		if($userObject !== null) {
1024
+		if ($userObject !== null) {
1025 1025
 			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1026 1026
 		}
1027 1027
 
1028 1028
 		if (!$isSubAdmin) {
1029
-			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1029
+			header('Location: '.\OCP\Util::linkToAbsolute('', 'index.php'));
1030 1030
 			exit();
1031 1031
 		}
1032 1032
 		return true;
@@ -1061,10 +1061,10 @@  discard block
 block discarded – undo
1061 1061
 					}
1062 1062
 				}
1063 1063
 
1064
-				if(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1065
-					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1064
+				if (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1065
+					$location = $urlGenerator->getAbsoluteURL('/apps/'.$appId.'/');
1066 1066
 				} else {
1067
-					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1067
+					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/'.$appId.'/');
1068 1068
 				}
1069 1069
 			}
1070 1070
 		}
@@ -1078,7 +1078,7 @@  discard block
 block discarded – undo
1078 1078
 	 */
1079 1079
 	public static function redirectToDefaultPage() {
1080 1080
 		$location = self::getDefaultPageUrl();
1081
-		header('Location: ' . $location);
1081
+		header('Location: '.$location);
1082 1082
 		exit();
1083 1083
 	}
1084 1084
 
@@ -1091,7 +1091,7 @@  discard block
 block discarded – undo
1091 1091
 		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1092 1092
 		if (is_null($id)) {
1093 1093
 			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1094
-			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1094
+			$id = 'oc'.\OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1095 1095
 			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1096 1096
 		}
1097 1097
 		return $id;
@@ -1113,7 +1113,7 @@  discard block
 block discarded – undo
1113 1113
 			}, $value);
1114 1114
 		} else {
1115 1115
 			// Specify encoding for PHP<5.4
1116
-			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1116
+			$value = htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
1117 1117
 		}
1118 1118
 		return $value;
1119 1119
 	}
@@ -1146,7 +1146,7 @@  discard block
 block discarded – undo
1146 1146
 		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1147 1147
 
1148 1148
 		// creating a test file
1149
-		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1149
+		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT.'/data').'/'.$fileName;
1150 1150
 
1151 1151
 		if (file_exists($testFile)) {// already running this test, possible recursive call
1152 1152
 			return false;
@@ -1155,7 +1155,7 @@  discard block
 block discarded – undo
1155 1155
 		$fp = @fopen($testFile, 'w');
1156 1156
 		if (!$fp) {
1157 1157
 			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1158
-				'Make sure it is possible for the webserver to write to ' . $testFile);
1158
+				'Make sure it is possible for the webserver to write to '.$testFile);
1159 1159
 		}
1160 1160
 		fwrite($fp, $testContent);
1161 1161
 		fclose($fp);
@@ -1182,10 +1182,10 @@  discard block
 block discarded – undo
1182 1182
 		}
1183 1183
 
1184 1184
 		$fileName = '/htaccesstest.txt';
1185
-		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1185
+		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT.'/data').'/'.$fileName;
1186 1186
 
1187 1187
 		// accessing the file via http
1188
-		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1188
+		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT.'/data'.$fileName);
1189 1189
 		try {
1190 1190
 			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1191 1191
 		} catch (\Exception $e) {
@@ -1276,7 +1276,7 @@  discard block
 block discarded – undo
1276 1276
 		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1277 1277
 
1278 1278
 		if ($theme === '') {
1279
-			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1279
+			if (is_dir(OC::$SERVERROOT.'/themes/default')) {
1280 1280
 				$theme = 'default';
1281 1281
 			}
1282 1282
 		}
@@ -1347,13 +1347,13 @@  discard block
 block discarded – undo
1347 1347
 	 * @return bool|string
1348 1348
 	 */
1349 1349
 	public static function normalizeUnicode($value) {
1350
-		if(Normalizer::isNormalized($value)) {
1350
+		if (Normalizer::isNormalized($value)) {
1351 1351
 			return $value;
1352 1352
 		}
1353 1353
 
1354 1354
 		$normalizedValue = Normalizer::normalize($value);
1355 1355
 		if ($normalizedValue === null || $normalizedValue === false) {
1356
-			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1356
+			\OC::$server->getLogger()->warning('normalizing failed for "'.$value.'"', ['app' => 'core']);
1357 1357
 			return $value;
1358 1358
 		}
1359 1359
 
@@ -1379,7 +1379,7 @@  discard block
 block discarded – undo
1379 1379
 		$version = OC_Util::getVersionString();
1380 1380
 		$build = OC_Util::getBuild();
1381 1381
 		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1382
-			$version .= ' Build:' . $build;
1382
+			$version .= ' Build:'.$build;
1383 1383
 		}
1384 1384
 		return $version;
1385 1385
 	}
@@ -1426,19 +1426,19 @@  discard block
 block discarded – undo
1426 1426
 			} else if ($config->getValue('debug', false) && $versionDiff < 0) {
1427 1427
 				// downgrade with debug
1428 1428
 				$installedMajor = explode('.', $installedVersion);
1429
-				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1429
+				$installedMajor = $installedMajor[0].'.'.$installedMajor[1];
1430 1430
 				$currentMajor = explode('.', $currentVersion);
1431
-				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1431
+				$currentMajor = $currentMajor[0].'.'.$currentMajor[1];
1432 1432
 				if ($installedMajor === $currentMajor) {
1433 1433
 					// Same major, allow downgrade for developers
1434 1434
 					return true;
1435 1435
 				} else {
1436 1436
 					// downgrade attempt, throw exception
1437
-					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1437
+					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from '.$installedVersion.' to '.$currentVersion.')');
1438 1438
 				}
1439 1439
 			} else if ($versionDiff < 0) {
1440 1440
 				// downgrade attempt, throw exception
1441
-				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1441
+				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from '.$installedVersion.' to '.$currentVersion.')');
1442 1442
 			}
1443 1443
 
1444 1444
 			// also check for upgrades for apps (independently from the user)
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/FilesPlugin.php 1 patch
Indentation   +391 added lines, -391 removed lines patch added patch discarded remove patch
@@ -51,395 +51,395 @@
 block discarded – undo
51 51
 
52 52
 class FilesPlugin extends ServerPlugin {
53 53
 
54
-	// namespace
55
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
56
-	const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
57
-	const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
58
-	const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
59
-	const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
60
-	const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
61
-	const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
62
-	const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
63
-	const GETETAG_PROPERTYNAME = '{DAV:}getetag';
64
-	const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
65
-	const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
66
-	const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
67
-	const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
68
-	const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
69
-	const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
70
-
71
-	/**
72
-	 * Reference to main server object
73
-	 *
74
-	 * @var \Sabre\DAV\Server
75
-	 */
76
-	private $server;
77
-
78
-	/**
79
-	 * @var Tree
80
-	 */
81
-	private $tree;
82
-
83
-	/**
84
-	 * Whether this is public webdav.
85
-	 * If true, some returned information will be stripped off.
86
-	 *
87
-	 * @var bool
88
-	 */
89
-	private $isPublic;
90
-
91
-	/**
92
-	 * @var View
93
-	 */
94
-	private $fileView;
95
-
96
-	/**
97
-	 * @var bool
98
-	 */
99
-	private $downloadAttachment;
100
-
101
-	/**
102
-	 * @var IConfig
103
-	 */
104
-	private $config;
105
-
106
-	/**
107
-	 * @var IRequest
108
-	 */
109
-	private $request;
110
-
111
-	/**
112
-	 * @var IPreview
113
-	 */
114
-	private $previewManager;
115
-
116
-	/**
117
-	 * @param Tree $tree
118
-	 * @param IConfig $config
119
-	 * @param IRequest $request
120
-	 * @param IPreview $previewManager
121
-	 * @param bool $isPublic
122
-	 * @param bool $downloadAttachment
123
-	 */
124
-	public function __construct(Tree $tree,
125
-								IConfig $config,
126
-								IRequest $request,
127
-								IPreview $previewManager,
128
-								$isPublic = false,
129
-								$downloadAttachment = true) {
130
-		$this->tree = $tree;
131
-		$this->config = $config;
132
-		$this->request = $request;
133
-		$this->isPublic = $isPublic;
134
-		$this->downloadAttachment = $downloadAttachment;
135
-		$this->previewManager = $previewManager;
136
-	}
137
-
138
-	/**
139
-	 * This initializes the plugin.
140
-	 *
141
-	 * This function is called by \Sabre\DAV\Server, after
142
-	 * addPlugin is called.
143
-	 *
144
-	 * This method should set up the required event subscriptions.
145
-	 *
146
-	 * @param \Sabre\DAV\Server $server
147
-	 * @return void
148
-	 */
149
-	public function initialize(\Sabre\DAV\Server $server) {
150
-
151
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
152
-		$server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
153
-		$server->protectedProperties[] = self::FILEID_PROPERTYNAME;
154
-		$server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
155
-		$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
156
-		$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
157
-		$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
158
-		$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
159
-		$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
160
-		$server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
161
-		$server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
162
-		$server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
163
-		$server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
164
-
165
-		// normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
166
-		$allowedProperties = ['{DAV:}getetag'];
167
-		$server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
168
-
169
-		$this->server = $server;
170
-		$this->server->on('propFind', array($this, 'handleGetProperties'));
171
-		$this->server->on('propPatch', array($this, 'handleUpdateProperties'));
172
-		$this->server->on('afterBind', array($this, 'sendFileIdHeader'));
173
-		$this->server->on('afterWriteContent', array($this, 'sendFileIdHeader'));
174
-		$this->server->on('afterMethod:GET', [$this,'httpGet']);
175
-		$this->server->on('afterMethod:GET', array($this, 'handleDownloadToken'));
176
-		$this->server->on('afterResponse', function($request, ResponseInterface $response) {
177
-			$body = $response->getBody();
178
-			if (is_resource($body)) {
179
-				fclose($body);
180
-			}
181
-		});
182
-		$this->server->on('beforeMove', [$this, 'checkMove']);
183
-	}
184
-
185
-	/**
186
-	 * Plugin that checks if a move can actually be performed.
187
-	 *
188
-	 * @param string $source source path
189
-	 * @param string $destination destination path
190
-	 * @throws Forbidden
191
-	 * @throws NotFound
192
-	 */
193
-	function checkMove($source, $destination) {
194
-		$sourceNode = $this->tree->getNodeForPath($source);
195
-		if (!$sourceNode instanceof Node) {
196
-			return;
197
-		}
198
-		list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source);
199
-		list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
200
-
201
-		if ($sourceDir !== $destinationDir) {
202
-			$sourceNodeFileInfo = $sourceNode->getFileInfo();
203
-			if (is_null($sourceNodeFileInfo)) {
204
-				throw new NotFound($source . ' does not exist');
205
-			}
206
-
207
-			if (!$sourceNodeFileInfo->isDeletable()) {
208
-				throw new Forbidden($source . " cannot be deleted");
209
-			}
210
-		}
211
-	}
212
-
213
-	/**
214
-	 * This sets a cookie to be able to recognize the start of the download
215
-	 * the content must not be longer than 32 characters and must only contain
216
-	 * alphanumeric characters
217
-	 *
218
-	 * @param RequestInterface $request
219
-	 * @param ResponseInterface $response
220
-	 */
221
-	function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
222
-		$queryParams = $request->getQueryParameters();
223
-
224
-		/**
225
-		 * this sets a cookie to be able to recognize the start of the download
226
-		 * the content must not be longer than 32 characters and must only contain
227
-		 * alphanumeric characters
228
-		 */
229
-		if (isset($queryParams['downloadStartSecret'])) {
230
-			$token = $queryParams['downloadStartSecret'];
231
-			if (!isset($token[32])
232
-				&& preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
233
-				// FIXME: use $response->setHeader() instead
234
-				setcookie('ocDownloadStarted', $token, time() + 20, '/');
235
-			}
236
-		}
237
-	}
238
-
239
-	/**
240
-	 * Add headers to file download
241
-	 *
242
-	 * @param RequestInterface $request
243
-	 * @param ResponseInterface $response
244
-	 */
245
-	function httpGet(RequestInterface $request, ResponseInterface $response) {
246
-		// Only handle valid files
247
-		$node = $this->tree->getNodeForPath($request->getPath());
248
-		if (!($node instanceof IFile)) return;
249
-
250
-		// adds a 'Content-Disposition: attachment' header in case no disposition
251
-		// header has been set before
252
-		if ($this->downloadAttachment &&
253
-			$response->getHeader('Content-Disposition') === null) {
254
-			$filename = $node->getName();
255
-			if ($this->request->isUserAgent(
256
-				[
257
-					\OC\AppFramework\Http\Request::USER_AGENT_IE,
258
-					\OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
259
-					\OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
260
-				])) {
261
-				$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
262
-			} else {
263
-				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
264
-													 . '; filename="' . rawurlencode($filename) . '"');
265
-			}
266
-		}
267
-
268
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
269
-			//Add OC-Checksum header
270
-			/** @var $node File */
271
-			$checksum = $node->getChecksum('sha1');
272
-			if ($checksum !== null && $checksum !== '') {
273
-				$response->addHeader('OC-Checksum', $checksum);
274
-			}
275
-		}
276
-	}
277
-
278
-	/**
279
-	 * Adds all ownCloud-specific properties
280
-	 *
281
-	 * @param PropFind $propFind
282
-	 * @param \Sabre\DAV\INode $node
283
-	 * @return void
284
-	 */
285
-	public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
286
-
287
-		$httpRequest = $this->server->httpRequest;
288
-
289
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
290
-
291
-			$propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
292
-				return $node->getFileId();
293
-			});
294
-
295
-			$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
296
-				return $node->getInternalFileId();
297
-			});
298
-
299
-			$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
300
-				$perms = $node->getDavPermissions();
301
-				if ($this->isPublic) {
302
-					// remove mount information
303
-					$perms = str_replace(['S', 'M'], '', $perms);
304
-				}
305
-				return $perms;
306
-			});
307
-
308
-			$propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
309
-				return $node->getSharePermissions(
310
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
311
-				);
312
-			});
313
-
314
-			$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
315
-				return $node->getETag();
316
-			});
317
-
318
-			$propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
319
-				$owner = $node->getOwner();
320
-				if (!$owner) {
321
-					return null;
322
-				} else {
323
-					return $owner->getUID();
324
-				}
325
-			});
326
-			$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
327
-				$owner = $node->getOwner();
328
-				if (!$owner) {
329
-					return null;
330
-				} else {
331
-					return $owner->getDisplayName();
332
-				}
333
-			});
334
-
335
-			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
336
-				return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
337
-			});
338
-			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
339
-				return $node->getSize();
340
-			});
341
-		}
342
-
343
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
344
-			$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
345
-				return $this->config->getSystemValue('data-fingerprint', '');
346
-			});
347
-		}
348
-
349
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
350
-			$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
351
-				/** @var $node \OCA\DAV\Connector\Sabre\File */
352
-				try {
353
-					$directDownloadUrl = $node->getDirectDownload();
354
-					if (isset($directDownloadUrl['url'])) {
355
-						return $directDownloadUrl['url'];
356
-					}
357
-				} catch (StorageNotAvailableException $e) {
358
-					return false;
359
-				} catch (ForbiddenException $e) {
360
-					return false;
361
-				}
362
-				return false;
363
-			});
364
-
365
-			$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
366
-				$checksum = $node->getChecksum('sha1');
367
-				if ($checksum === NULL || $checksum === '') {
368
-					return null;
369
-				}
370
-
371
-				return new ChecksumList($checksum);
372
-			});
373
-
374
-		}
375
-
376
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
377
-			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
378
-				return $node->getSize();
379
-			});
380
-		}
381
-	}
382
-
383
-	/**
384
-	 * Update ownCloud-specific properties
385
-	 *
386
-	 * @param string $path
387
-	 * @param PropPatch $propPatch
388
-	 *
389
-	 * @return void
390
-	 */
391
-	public function handleUpdateProperties($path, PropPatch $propPatch) {
392
-		$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
393
-			if (empty($time)) {
394
-				return false;
395
-			}
396
-			$node = $this->tree->getNodeForPath($path);
397
-			if (is_null($node)) {
398
-				return 404;
399
-			}
400
-			$node->touch($time);
401
-			return true;
402
-		});
403
-		$propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($path) {
404
-			if (empty($etag)) {
405
-				return false;
406
-			}
407
-			$node = $this->tree->getNodeForPath($path);
408
-			if (is_null($node)) {
409
-				return 404;
410
-			}
411
-			if ($node->setEtag($etag) !== -1) {
412
-				return true;
413
-			}
414
-			return false;
415
-		});
416
-	}
417
-
418
-	/**
419
-	 * @param string $filePath
420
-	 * @param \Sabre\DAV\INode $node
421
-	 * @throws \Sabre\DAV\Exception\BadRequest
422
-	 */
423
-	public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
424
-		// chunked upload handling
425
-		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
426
-			list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($filePath);
427
-			$info = \OC_FileChunking::decodeName($name);
428
-			if (!empty($info)) {
429
-				$filePath = $path . '/' . $info['name'];
430
-			}
431
-		}
432
-
433
-		// we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
434
-		if (!$this->server->tree->nodeExists($filePath)) {
435
-			return;
436
-		}
437
-		$node = $this->server->tree->getNodeForPath($filePath);
438
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
439
-			$fileId = $node->getFileId();
440
-			if (!is_null($fileId)) {
441
-				$this->server->httpResponse->setHeader('OC-FileId', $fileId);
442
-			}
443
-		}
444
-	}
54
+    // namespace
55
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
56
+    const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
57
+    const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
58
+    const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
59
+    const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
60
+    const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
61
+    const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
62
+    const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
63
+    const GETETAG_PROPERTYNAME = '{DAV:}getetag';
64
+    const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
65
+    const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
66
+    const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
67
+    const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
68
+    const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
69
+    const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
70
+
71
+    /**
72
+     * Reference to main server object
73
+     *
74
+     * @var \Sabre\DAV\Server
75
+     */
76
+    private $server;
77
+
78
+    /**
79
+     * @var Tree
80
+     */
81
+    private $tree;
82
+
83
+    /**
84
+     * Whether this is public webdav.
85
+     * If true, some returned information will be stripped off.
86
+     *
87
+     * @var bool
88
+     */
89
+    private $isPublic;
90
+
91
+    /**
92
+     * @var View
93
+     */
94
+    private $fileView;
95
+
96
+    /**
97
+     * @var bool
98
+     */
99
+    private $downloadAttachment;
100
+
101
+    /**
102
+     * @var IConfig
103
+     */
104
+    private $config;
105
+
106
+    /**
107
+     * @var IRequest
108
+     */
109
+    private $request;
110
+
111
+    /**
112
+     * @var IPreview
113
+     */
114
+    private $previewManager;
115
+
116
+    /**
117
+     * @param Tree $tree
118
+     * @param IConfig $config
119
+     * @param IRequest $request
120
+     * @param IPreview $previewManager
121
+     * @param bool $isPublic
122
+     * @param bool $downloadAttachment
123
+     */
124
+    public function __construct(Tree $tree,
125
+                                IConfig $config,
126
+                                IRequest $request,
127
+                                IPreview $previewManager,
128
+                                $isPublic = false,
129
+                                $downloadAttachment = true) {
130
+        $this->tree = $tree;
131
+        $this->config = $config;
132
+        $this->request = $request;
133
+        $this->isPublic = $isPublic;
134
+        $this->downloadAttachment = $downloadAttachment;
135
+        $this->previewManager = $previewManager;
136
+    }
137
+
138
+    /**
139
+     * This initializes the plugin.
140
+     *
141
+     * This function is called by \Sabre\DAV\Server, after
142
+     * addPlugin is called.
143
+     *
144
+     * This method should set up the required event subscriptions.
145
+     *
146
+     * @param \Sabre\DAV\Server $server
147
+     * @return void
148
+     */
149
+    public function initialize(\Sabre\DAV\Server $server) {
150
+
151
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
152
+        $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
153
+        $server->protectedProperties[] = self::FILEID_PROPERTYNAME;
154
+        $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
155
+        $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
156
+        $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
157
+        $server->protectedProperties[] = self::SIZE_PROPERTYNAME;
158
+        $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
159
+        $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
160
+        $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
161
+        $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
162
+        $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
163
+        $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
164
+
165
+        // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
166
+        $allowedProperties = ['{DAV:}getetag'];
167
+        $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
168
+
169
+        $this->server = $server;
170
+        $this->server->on('propFind', array($this, 'handleGetProperties'));
171
+        $this->server->on('propPatch', array($this, 'handleUpdateProperties'));
172
+        $this->server->on('afterBind', array($this, 'sendFileIdHeader'));
173
+        $this->server->on('afterWriteContent', array($this, 'sendFileIdHeader'));
174
+        $this->server->on('afterMethod:GET', [$this,'httpGet']);
175
+        $this->server->on('afterMethod:GET', array($this, 'handleDownloadToken'));
176
+        $this->server->on('afterResponse', function($request, ResponseInterface $response) {
177
+            $body = $response->getBody();
178
+            if (is_resource($body)) {
179
+                fclose($body);
180
+            }
181
+        });
182
+        $this->server->on('beforeMove', [$this, 'checkMove']);
183
+    }
184
+
185
+    /**
186
+     * Plugin that checks if a move can actually be performed.
187
+     *
188
+     * @param string $source source path
189
+     * @param string $destination destination path
190
+     * @throws Forbidden
191
+     * @throws NotFound
192
+     */
193
+    function checkMove($source, $destination) {
194
+        $sourceNode = $this->tree->getNodeForPath($source);
195
+        if (!$sourceNode instanceof Node) {
196
+            return;
197
+        }
198
+        list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source);
199
+        list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
200
+
201
+        if ($sourceDir !== $destinationDir) {
202
+            $sourceNodeFileInfo = $sourceNode->getFileInfo();
203
+            if (is_null($sourceNodeFileInfo)) {
204
+                throw new NotFound($source . ' does not exist');
205
+            }
206
+
207
+            if (!$sourceNodeFileInfo->isDeletable()) {
208
+                throw new Forbidden($source . " cannot be deleted");
209
+            }
210
+        }
211
+    }
212
+
213
+    /**
214
+     * This sets a cookie to be able to recognize the start of the download
215
+     * the content must not be longer than 32 characters and must only contain
216
+     * alphanumeric characters
217
+     *
218
+     * @param RequestInterface $request
219
+     * @param ResponseInterface $response
220
+     */
221
+    function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
222
+        $queryParams = $request->getQueryParameters();
223
+
224
+        /**
225
+         * this sets a cookie to be able to recognize the start of the download
226
+         * the content must not be longer than 32 characters and must only contain
227
+         * alphanumeric characters
228
+         */
229
+        if (isset($queryParams['downloadStartSecret'])) {
230
+            $token = $queryParams['downloadStartSecret'];
231
+            if (!isset($token[32])
232
+                && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
233
+                // FIXME: use $response->setHeader() instead
234
+                setcookie('ocDownloadStarted', $token, time() + 20, '/');
235
+            }
236
+        }
237
+    }
238
+
239
+    /**
240
+     * Add headers to file download
241
+     *
242
+     * @param RequestInterface $request
243
+     * @param ResponseInterface $response
244
+     */
245
+    function httpGet(RequestInterface $request, ResponseInterface $response) {
246
+        // Only handle valid files
247
+        $node = $this->tree->getNodeForPath($request->getPath());
248
+        if (!($node instanceof IFile)) return;
249
+
250
+        // adds a 'Content-Disposition: attachment' header in case no disposition
251
+        // header has been set before
252
+        if ($this->downloadAttachment &&
253
+            $response->getHeader('Content-Disposition') === null) {
254
+            $filename = $node->getName();
255
+            if ($this->request->isUserAgent(
256
+                [
257
+                    \OC\AppFramework\Http\Request::USER_AGENT_IE,
258
+                    \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
259
+                    \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
260
+                ])) {
261
+                $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
262
+            } else {
263
+                $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
264
+                                                        . '; filename="' . rawurlencode($filename) . '"');
265
+            }
266
+        }
267
+
268
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
269
+            //Add OC-Checksum header
270
+            /** @var $node File */
271
+            $checksum = $node->getChecksum('sha1');
272
+            if ($checksum !== null && $checksum !== '') {
273
+                $response->addHeader('OC-Checksum', $checksum);
274
+            }
275
+        }
276
+    }
277
+
278
+    /**
279
+     * Adds all ownCloud-specific properties
280
+     *
281
+     * @param PropFind $propFind
282
+     * @param \Sabre\DAV\INode $node
283
+     * @return void
284
+     */
285
+    public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
286
+
287
+        $httpRequest = $this->server->httpRequest;
288
+
289
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
290
+
291
+            $propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
292
+                return $node->getFileId();
293
+            });
294
+
295
+            $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
296
+                return $node->getInternalFileId();
297
+            });
298
+
299
+            $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
300
+                $perms = $node->getDavPermissions();
301
+                if ($this->isPublic) {
302
+                    // remove mount information
303
+                    $perms = str_replace(['S', 'M'], '', $perms);
304
+                }
305
+                return $perms;
306
+            });
307
+
308
+            $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
309
+                return $node->getSharePermissions(
310
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
311
+                );
312
+            });
313
+
314
+            $propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
315
+                return $node->getETag();
316
+            });
317
+
318
+            $propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
319
+                $owner = $node->getOwner();
320
+                if (!$owner) {
321
+                    return null;
322
+                } else {
323
+                    return $owner->getUID();
324
+                }
325
+            });
326
+            $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
327
+                $owner = $node->getOwner();
328
+                if (!$owner) {
329
+                    return null;
330
+                } else {
331
+                    return $owner->getDisplayName();
332
+                }
333
+            });
334
+
335
+            $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
336
+                return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
337
+            });
338
+            $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
339
+                return $node->getSize();
340
+            });
341
+        }
342
+
343
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
344
+            $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
345
+                return $this->config->getSystemValue('data-fingerprint', '');
346
+            });
347
+        }
348
+
349
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
350
+            $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
351
+                /** @var $node \OCA\DAV\Connector\Sabre\File */
352
+                try {
353
+                    $directDownloadUrl = $node->getDirectDownload();
354
+                    if (isset($directDownloadUrl['url'])) {
355
+                        return $directDownloadUrl['url'];
356
+                    }
357
+                } catch (StorageNotAvailableException $e) {
358
+                    return false;
359
+                } catch (ForbiddenException $e) {
360
+                    return false;
361
+                }
362
+                return false;
363
+            });
364
+
365
+            $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
366
+                $checksum = $node->getChecksum('sha1');
367
+                if ($checksum === NULL || $checksum === '') {
368
+                    return null;
369
+                }
370
+
371
+                return new ChecksumList($checksum);
372
+            });
373
+
374
+        }
375
+
376
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
377
+            $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
378
+                return $node->getSize();
379
+            });
380
+        }
381
+    }
382
+
383
+    /**
384
+     * Update ownCloud-specific properties
385
+     *
386
+     * @param string $path
387
+     * @param PropPatch $propPatch
388
+     *
389
+     * @return void
390
+     */
391
+    public function handleUpdateProperties($path, PropPatch $propPatch) {
392
+        $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
393
+            if (empty($time)) {
394
+                return false;
395
+            }
396
+            $node = $this->tree->getNodeForPath($path);
397
+            if (is_null($node)) {
398
+                return 404;
399
+            }
400
+            $node->touch($time);
401
+            return true;
402
+        });
403
+        $propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($path) {
404
+            if (empty($etag)) {
405
+                return false;
406
+            }
407
+            $node = $this->tree->getNodeForPath($path);
408
+            if (is_null($node)) {
409
+                return 404;
410
+            }
411
+            if ($node->setEtag($etag) !== -1) {
412
+                return true;
413
+            }
414
+            return false;
415
+        });
416
+    }
417
+
418
+    /**
419
+     * @param string $filePath
420
+     * @param \Sabre\DAV\INode $node
421
+     * @throws \Sabre\DAV\Exception\BadRequest
422
+     */
423
+    public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
424
+        // chunked upload handling
425
+        if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
426
+            list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($filePath);
427
+            $info = \OC_FileChunking::decodeName($name);
428
+            if (!empty($info)) {
429
+                $filePath = $path . '/' . $info['name'];
430
+            }
431
+        }
432
+
433
+        // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
434
+        if (!$this->server->tree->nodeExists($filePath)) {
435
+            return;
436
+        }
437
+        $node = $this->server->tree->getNodeForPath($filePath);
438
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
439
+            $fileId = $node->getFileId();
440
+            if (!is_null($fileId)) {
441
+                $this->server->httpResponse->setHeader('OC-FileId', $fileId);
442
+            }
443
+        }
444
+    }
445 445
 }
Please login to merge, or discard this patch.
apps/files/lib/Capabilities.php 1 patch
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -34,33 +34,33 @@
 block discarded – undo
34 34
  * @package OCA\Files
35 35
  */
36 36
 class Capabilities implements ICapability {
37
-	/** @var IConfig */
38
-	protected $config;
37
+    /** @var IConfig */
38
+    protected $config;
39 39
 
40
-	/**
41
-	 * Capabilities constructor.
42
-	 *
43
-	 * @param IConfig $config
44
-	 */
45
-	public function __construct(IConfig $config) {
46
-		$this->config = $config;
47
-	}
40
+    /**
41
+     * Capabilities constructor.
42
+     *
43
+     * @param IConfig $config
44
+     */
45
+    public function __construct(IConfig $config) {
46
+        $this->config = $config;
47
+    }
48 48
 
49
-	/**
50
-	 * Return this classes capabilities
51
-	 *
52
-	 * @return array
53
-	 */
54
-	public function getCapabilities() {
55
-		return [
56
-			'checksums' => [
57
-				'supportedTypes' => ['SHA1'],
58
-				'preferredUploadType' => 'SHA1'
59
-			],
60
-			'files' => [
61
-				'bigfilechunking' => true,
62
-				'blacklisted_files' => $this->config->getSystemValue('blacklisted_files', ['.htaccess']),
63
-			],
64
-		];
65
-	}
49
+    /**
50
+     * Return this classes capabilities
51
+     *
52
+     * @return array
53
+     */
54
+    public function getCapabilities() {
55
+        return [
56
+            'checksums' => [
57
+                'supportedTypes' => ['SHA1'],
58
+                'preferredUploadType' => 'SHA1'
59
+            ],
60
+            'files' => [
61
+                'bigfilechunking' => true,
62
+                'blacklisted_files' => $this->config->getSystemValue('blacklisted_files', ['.htaccess']),
63
+            ],
64
+        ];
65
+    }
66 66
 }
Please login to merge, or discard this patch.
lib/private/Files/Stream/Checksum.php 2 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -24,7 +24,6 @@
 block discarded – undo
24 24
 
25 25
 
26 26
 use Icewind\Streams\Wrapper;
27
-use OC\Cache\CappedMemoryCache;
28 27
 
29 28
 /**
30 29
  * Computes the checksum of the wrapped stream. The checksum can be retrieved with
Please login to merge, or discard this patch.
Indentation   +115 added lines, -115 removed lines patch added patch discarded remove patch
@@ -35,119 +35,119 @@
 block discarded – undo
35 35
  */
36 36
 class Checksum extends Wrapper {
37 37
 
38
-	/**
39
-	 * To stepwise compute a hash on a continuous stream
40
-	 * of data a "context" is required which stores the intermediate
41
-	 * hash result while the stream has not finished.
42
-	 *
43
-	 * After the stream ends the hashing contexts needs to be finalized
44
-	 * to compute the final checksum.
45
-	 *
46
-	 * @var  resource[]
47
-	 */
48
-	private $hashingContexts;
49
-
50
-	/** @var callable */
51
-	private $callback;
52
-
53
-	public function __construct(array $algos = ['sha1', 'md5', 'adler32']) {
54
-
55
-		foreach ($algos as $algo) {
56
-			$this->hashingContexts[$algo] = hash_init($algo);
57
-		}
58
-	}
59
-
60
-
61
-	/**
62
-	 * @param resource $source
63
-	 * @param callable $callback
64
-	 * @return resource
65
-	 */
66
-	public static function wrap($source, callable $callback) {
67
-		$context = stream_context_create([
68
-			'occhecksum' => [
69
-				'source' => $source,
70
-				'callback' => $callback
71
-			]
72
-		]);
73
-
74
-		return Wrapper::wrapSource(
75
-			$source, $context, 'occhecksum', self::class
76
-		);
77
-	}
78
-
79
-
80
-	/**
81
-	 * @param string $path
82
-	 * @param array $options
83
-	 * @return bool
84
-	 */
85
-	public function dir_opendir($path, $options) {
86
-		return false;
87
-	}
88
-
89
-	/**
90
-	 * @param string $path
91
-	 * @param string $mode
92
-	 * @param int $options
93
-	 * @param string $opened_path
94
-	 * @return bool
95
-	 */
96
-	public function stream_open($path, $mode, $options, &$opened_path) {
97
-		$context = parent::loadContext('occhecksum');
98
-		$this->setSourceStream($context['source']);
99
-		$this->callback = $context['callback'];
100
-
101
-		return true;
102
-	}
103
-
104
-	/**
105
-	 * @param int $count
106
-	 * @return string
107
-	 */
108
-	public function stream_read($count) {
109
-		$data = parent::stream_read($count);
110
-		$this->updateHashingContexts($data);
111
-
112
-		return $data;
113
-	}
114
-
115
-	/**
116
-	 * @param string $data
117
-	 * @return int
118
-	 */
119
-	public function stream_write($data) {
120
-		$this->updateHashingContexts($data);
121
-
122
-		return parent::stream_write($data);
123
-	}
124
-
125
-	private function updateHashingContexts($data) {
126
-		foreach ($this->hashingContexts as $ctx) {
127
-			hash_update($ctx, $data);
128
-		}
129
-	}
130
-
131
-	/**
132
-	 * @return bool
133
-	 */
134
-	public function stream_close() {
135
-		$callback = $this->callback;
136
-		$callback($this->finalizeHashingContexts());
137
-
138
-		return parent::stream_close();
139
-	}
140
-
141
-	/**
142
-	 * @return array
143
-	 */
144
-	private function finalizeHashingContexts() {
145
-		$hashes = [];
146
-
147
-		foreach ($this->hashingContexts as $algo => $ctx) {
148
-			$hashes[$algo] = hash_final($ctx);
149
-		}
150
-
151
-		return $hashes;
152
-	}
38
+    /**
39
+     * To stepwise compute a hash on a continuous stream
40
+     * of data a "context" is required which stores the intermediate
41
+     * hash result while the stream has not finished.
42
+     *
43
+     * After the stream ends the hashing contexts needs to be finalized
44
+     * to compute the final checksum.
45
+     *
46
+     * @var  resource[]
47
+     */
48
+    private $hashingContexts;
49
+
50
+    /** @var callable */
51
+    private $callback;
52
+
53
+    public function __construct(array $algos = ['sha1', 'md5', 'adler32']) {
54
+
55
+        foreach ($algos as $algo) {
56
+            $this->hashingContexts[$algo] = hash_init($algo);
57
+        }
58
+    }
59
+
60
+
61
+    /**
62
+     * @param resource $source
63
+     * @param callable $callback
64
+     * @return resource
65
+     */
66
+    public static function wrap($source, callable $callback) {
67
+        $context = stream_context_create([
68
+            'occhecksum' => [
69
+                'source' => $source,
70
+                'callback' => $callback
71
+            ]
72
+        ]);
73
+
74
+        return Wrapper::wrapSource(
75
+            $source, $context, 'occhecksum', self::class
76
+        );
77
+    }
78
+
79
+
80
+    /**
81
+     * @param string $path
82
+     * @param array $options
83
+     * @return bool
84
+     */
85
+    public function dir_opendir($path, $options) {
86
+        return false;
87
+    }
88
+
89
+    /**
90
+     * @param string $path
91
+     * @param string $mode
92
+     * @param int $options
93
+     * @param string $opened_path
94
+     * @return bool
95
+     */
96
+    public function stream_open($path, $mode, $options, &$opened_path) {
97
+        $context = parent::loadContext('occhecksum');
98
+        $this->setSourceStream($context['source']);
99
+        $this->callback = $context['callback'];
100
+
101
+        return true;
102
+    }
103
+
104
+    /**
105
+     * @param int $count
106
+     * @return string
107
+     */
108
+    public function stream_read($count) {
109
+        $data = parent::stream_read($count);
110
+        $this->updateHashingContexts($data);
111
+
112
+        return $data;
113
+    }
114
+
115
+    /**
116
+     * @param string $data
117
+     * @return int
118
+     */
119
+    public function stream_write($data) {
120
+        $this->updateHashingContexts($data);
121
+
122
+        return parent::stream_write($data);
123
+    }
124
+
125
+    private function updateHashingContexts($data) {
126
+        foreach ($this->hashingContexts as $ctx) {
127
+            hash_update($ctx, $data);
128
+        }
129
+    }
130
+
131
+    /**
132
+     * @return bool
133
+     */
134
+    public function stream_close() {
135
+        $callback = $this->callback;
136
+        $callback($this->finalizeHashingContexts());
137
+
138
+        return parent::stream_close();
139
+    }
140
+
141
+    /**
142
+     * @return array
143
+     */
144
+    private function finalizeHashingContexts() {
145
+        $hashes = [];
146
+
147
+        foreach ($this->hashingContexts as $algo => $ctx) {
148
+            $hashes[$algo] = hash_final($ctx);
149
+        }
150
+
151
+        return $hashes;
152
+    }
153 153
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/Wrapper/Checksum.php 2 patches
Indentation   +91 added lines, -91 removed lines patch added patch discarded remove patch
@@ -41,95 +41,95 @@
 block discarded – undo
41 41
 class Checksum extends Wrapper {
42 42
 
43 43
 
44
-	const NOT_REQUIRED = 0;
45
-	/** Calculate checksum on write (to be stored in oc_filecache) */
46
-	const PATH_NEW_OR_UPDATED = 1;
47
-	/** File needs to be checksummed on first read because it is already in cache but has no checksum */
48
-	const PATH_IN_CACHE_WITHOUT_CHECKSUM = 2;
49
-
50
-	/**
51
-	 * @param string $path
52
-	 * @param string $mode
53
-	 * @return false|resource
54
-	 */
55
-	public function fopen($path, $mode) {
56
-		$stream = $this->getWrapperStorage()->fopen($path, $mode);
57
-		if (!is_resource($stream)) {
58
-			// don't wrap on error
59
-			return $stream;
60
-		}
61
-
62
-		$requirement = $this->getChecksumRequirement($path, $mode);
63
-
64
-		if ($requirement === self::PATH_NEW_OR_UPDATED ||
65
-			$requirement === self::PATH_IN_CACHE_WITHOUT_CHECKSUM
66
-		) {
67
-			return $this->wrapChecksumStream($stream, $path);
68
-		}
69
-
70
-		return $stream;
71
-	}
72
-
73
-	private function wrapChecksumStream($stream, $path) {
74
-		return \OC\Files\Stream\Checksum::wrap($stream, function (array $hashes) use ($path) {
75
-			$cache = $this->getCache();
76
-			$cache->put($path, [
77
-				'checksum' => self::getChecksumsInDbFormat($hashes)
78
-			]);
79
-		});
80
-	}
81
-
82
-	/**
83
-	 * @param $mode
84
-	 * @param $path
85
-	 * @return int
86
-	 */
87
-	private function getChecksumRequirement($path, $mode) {
88
-		$isNormalFile = (!$this->getWrapperStorage() instanceof IHomeStorage) || strpos($path, 'files/') === 0;
89
-		$fileIsWritten = $mode !== 'r' && $mode !== 'rb';
90
-
91
-		if ($isNormalFile && $fileIsWritten) {
92
-			return self::PATH_NEW_OR_UPDATED;
93
-		}
94
-
95
-		// file could be in cache but without checksum for example
96
-		// if mounted from ext. storage
97
-		$cache = $this->getCache($path);
98
-		$cacheEntry = $cache->get($path);
99
-
100
-		if ($cacheEntry && empty($cacheEntry['checksum'])) {
101
-			return self::PATH_IN_CACHE_WITHOUT_CHECKSUM;
102
-		}
103
-
104
-		return self::NOT_REQUIRED;
105
-	}
106
-
107
-	/**
108
-	 * @param array $hashes
109
-	 * @return string
110
-	 */
111
-	private static function getChecksumsInDbFormat(array $hashes) {
112
-		$checksumString = '';
113
-		foreach ($hashes as $algo => $checksum) {
114
-			$checksumString .= sprintf('%s:%s ', strtoupper($algo), $checksum);
115
-		}
116
-
117
-		return rtrim($checksumString);
118
-	}
119
-
120
-	/**
121
-	 * @param string $path
122
-	 * @param string $data
123
-	 * @return bool
124
-	 */
125
-	public function file_put_contents($path, $data) {
126
-		$fh = $this->fopen($path, 'w');
127
-		if (!$fh) {
128
-			return false;
129
-		}
130
-		fwrite($fh, $data);
131
-		fclose($fh);
132
-
133
-		return true;
134
-	}
44
+    const NOT_REQUIRED = 0;
45
+    /** Calculate checksum on write (to be stored in oc_filecache) */
46
+    const PATH_NEW_OR_UPDATED = 1;
47
+    /** File needs to be checksummed on first read because it is already in cache but has no checksum */
48
+    const PATH_IN_CACHE_WITHOUT_CHECKSUM = 2;
49
+
50
+    /**
51
+     * @param string $path
52
+     * @param string $mode
53
+     * @return false|resource
54
+     */
55
+    public function fopen($path, $mode) {
56
+        $stream = $this->getWrapperStorage()->fopen($path, $mode);
57
+        if (!is_resource($stream)) {
58
+            // don't wrap on error
59
+            return $stream;
60
+        }
61
+
62
+        $requirement = $this->getChecksumRequirement($path, $mode);
63
+
64
+        if ($requirement === self::PATH_NEW_OR_UPDATED ||
65
+            $requirement === self::PATH_IN_CACHE_WITHOUT_CHECKSUM
66
+        ) {
67
+            return $this->wrapChecksumStream($stream, $path);
68
+        }
69
+
70
+        return $stream;
71
+    }
72
+
73
+    private function wrapChecksumStream($stream, $path) {
74
+        return \OC\Files\Stream\Checksum::wrap($stream, function (array $hashes) use ($path) {
75
+            $cache = $this->getCache();
76
+            $cache->put($path, [
77
+                'checksum' => self::getChecksumsInDbFormat($hashes)
78
+            ]);
79
+        });
80
+    }
81
+
82
+    /**
83
+     * @param $mode
84
+     * @param $path
85
+     * @return int
86
+     */
87
+    private function getChecksumRequirement($path, $mode) {
88
+        $isNormalFile = (!$this->getWrapperStorage() instanceof IHomeStorage) || strpos($path, 'files/') === 0;
89
+        $fileIsWritten = $mode !== 'r' && $mode !== 'rb';
90
+
91
+        if ($isNormalFile && $fileIsWritten) {
92
+            return self::PATH_NEW_OR_UPDATED;
93
+        }
94
+
95
+        // file could be in cache but without checksum for example
96
+        // if mounted from ext. storage
97
+        $cache = $this->getCache($path);
98
+        $cacheEntry = $cache->get($path);
99
+
100
+        if ($cacheEntry && empty($cacheEntry['checksum'])) {
101
+            return self::PATH_IN_CACHE_WITHOUT_CHECKSUM;
102
+        }
103
+
104
+        return self::NOT_REQUIRED;
105
+    }
106
+
107
+    /**
108
+     * @param array $hashes
109
+     * @return string
110
+     */
111
+    private static function getChecksumsInDbFormat(array $hashes) {
112
+        $checksumString = '';
113
+        foreach ($hashes as $algo => $checksum) {
114
+            $checksumString .= sprintf('%s:%s ', strtoupper($algo), $checksum);
115
+        }
116
+
117
+        return rtrim($checksumString);
118
+    }
119
+
120
+    /**
121
+     * @param string $path
122
+     * @param string $data
123
+     * @return bool
124
+     */
125
+    public function file_put_contents($path, $data) {
126
+        $fh = $this->fopen($path, 'w');
127
+        if (!$fh) {
128
+            return false;
129
+        }
130
+        fwrite($fh, $data);
131
+        fclose($fh);
132
+
133
+        return true;
134
+    }
135 135
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@
 block discarded – undo
71 71
 	}
72 72
 
73 73
 	private function wrapChecksumStream($stream, $path) {
74
-		return \OC\Files\Stream\Checksum::wrap($stream, function (array $hashes) use ($path) {
74
+		return \OC\Files\Stream\Checksum::wrap($stream, function(array $hashes) use ($path) {
75 75
 			$cache = $this->getCache();
76 76
 			$cache->put($path, [
77 77
 				'checksum' => self::getChecksumsInDbFormat($hashes)
Please login to merge, or discard this patch.
lib/private/Files/Cache/Cache.php 2 patches
Doc Comments   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -411,6 +411,9 @@  discard block
 block discarded – undo
411 411
 		}
412 412
 	}
413 413
 
414
+	/**
415
+	 * @param string $path
416
+	 */
414 417
 	private function getParentPath($path) {
415 418
 		$parent = dirname($path);
416 419
 		if ($parent === '.') {
@@ -464,7 +467,7 @@  discard block
 block discarded – undo
464 467
 	/**
465 468
 	 * Recursively remove all children of a folder
466 469
 	 *
467
-	 * @param array $entry the cache entry of the folder to remove the children of
470
+	 * @param ICacheEntry $entry the cache entry of the folder to remove the children of
468 471
 	 * @throws \OC\DatabaseException
469 472
 	 */
470 473
 	private function removeChildren($entry) {
@@ -649,7 +652,7 @@  discard block
 block discarded – undo
649 652
 	 *
650 653
 	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
651 654
 	 *        where it will search for all mimetypes in the group ('image/*')
652
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
655
+	 * @return CacheEntry[] an array of cache entries where the mimetype matches the search
653 656
 	 */
654 657
 	public function searchByMime($mimetype) {
655 658
 		if (strpos($mimetype, '/')) {
Please login to merge, or discard this patch.
Indentation   +830 added lines, -830 removed lines patch added patch discarded remove patch
@@ -56,844 +56,844 @@
 block discarded – undo
56 56
  * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater
57 57
  */
58 58
 class Cache implements ICache {
59
-	use MoveFromCacheTrait {
60
-		MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
61
-	}
62
-
63
-	/**
64
-	 * @var array partial data for the cache
65
-	 */
66
-	protected $partial = array();
67
-
68
-	/**
69
-	 * @var string
70
-	 */
71
-	protected $storageId;
72
-
73
-	/**
74
-	 * @var Storage $storageCache
75
-	 */
76
-	protected $storageCache;
77
-
78
-	/** @var IMimeTypeLoader */
79
-	protected $mimetypeLoader;
80
-
81
-	/**
82
-	 * @var IDBConnection
83
-	 */
84
-	protected $connection;
85
-
86
-	/** @var QuerySearchHelper */
87
-	protected $querySearchHelper;
88
-
89
-	/**
90
-	 * @param \OC\Files\Storage\Storage|string $storage
91
-	 */
92
-	public function __construct($storage) {
93
-		if ($storage instanceof \OC\Files\Storage\Storage) {
94
-			$this->storageId = $storage->getId();
95
-		} else {
96
-			$this->storageId = $storage;
97
-		}
98
-		if (strlen($this->storageId) > 64) {
99
-			$this->storageId = md5($this->storageId);
100
-		}
101
-
102
-		$this->storageCache = new Storage($storage);
103
-		$this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
104
-		$this->connection = \OC::$server->getDatabaseConnection();
105
-		$this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
106
-	}
107
-
108
-	/**
109
-	 * Get the numeric storage id for this cache's storage
110
-	 *
111
-	 * @return int
112
-	 */
113
-	public function getNumericStorageId() {
114
-		return $this->storageCache->getNumericId();
115
-	}
116
-
117
-	/**
118
-	 * get the stored metadata of a file or folder
119
-	 *
120
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
121
-	 * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
122
-	 */
123
-	public function get($file) {
124
-		if (is_string($file) or $file == '') {
125
-			// normalize file
126
-			$file = $this->normalize($file);
127
-
128
-			$where = 'WHERE `storage` = ? AND `path_hash` = ?';
129
-			$params = array($this->getNumericStorageId(), md5($file));
130
-		} else { //file id
131
-			$where = 'WHERE `fileid` = ?';
132
-			$params = array($file);
133
-		}
134
-		$sql = 'SELECT `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
59
+    use MoveFromCacheTrait {
60
+        MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
61
+    }
62
+
63
+    /**
64
+     * @var array partial data for the cache
65
+     */
66
+    protected $partial = array();
67
+
68
+    /**
69
+     * @var string
70
+     */
71
+    protected $storageId;
72
+
73
+    /**
74
+     * @var Storage $storageCache
75
+     */
76
+    protected $storageCache;
77
+
78
+    /** @var IMimeTypeLoader */
79
+    protected $mimetypeLoader;
80
+
81
+    /**
82
+     * @var IDBConnection
83
+     */
84
+    protected $connection;
85
+
86
+    /** @var QuerySearchHelper */
87
+    protected $querySearchHelper;
88
+
89
+    /**
90
+     * @param \OC\Files\Storage\Storage|string $storage
91
+     */
92
+    public function __construct($storage) {
93
+        if ($storage instanceof \OC\Files\Storage\Storage) {
94
+            $this->storageId = $storage->getId();
95
+        } else {
96
+            $this->storageId = $storage;
97
+        }
98
+        if (strlen($this->storageId) > 64) {
99
+            $this->storageId = md5($this->storageId);
100
+        }
101
+
102
+        $this->storageCache = new Storage($storage);
103
+        $this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
104
+        $this->connection = \OC::$server->getDatabaseConnection();
105
+        $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
106
+    }
107
+
108
+    /**
109
+     * Get the numeric storage id for this cache's storage
110
+     *
111
+     * @return int
112
+     */
113
+    public function getNumericStorageId() {
114
+        return $this->storageCache->getNumericId();
115
+    }
116
+
117
+    /**
118
+     * get the stored metadata of a file or folder
119
+     *
120
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
121
+     * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
122
+     */
123
+    public function get($file) {
124
+        if (is_string($file) or $file == '') {
125
+            // normalize file
126
+            $file = $this->normalize($file);
127
+
128
+            $where = 'WHERE `storage` = ? AND `path_hash` = ?';
129
+            $params = array($this->getNumericStorageId(), md5($file));
130
+        } else { //file id
131
+            $where = 'WHERE `fileid` = ?';
132
+            $params = array($file);
133
+        }
134
+        $sql = 'SELECT `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
135 135
 					   `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum`
136 136
 				FROM `*PREFIX*filecache` ' . $where;
137
-		$result = $this->connection->executeQuery($sql, $params);
138
-		$data = $result->fetch();
139
-
140
-		//FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO
141
-		//PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false
142
-		if ($data === null) {
143
-			$data = false;
144
-		}
145
-
146
-		//merge partial data
147
-		if (!$data and is_string($file)) {
148
-			if (isset($this->partial[$file])) {
149
-				$data = $this->partial[$file];
150
-			}
151
-			return $data;
152
-		} else {
153
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
154
-		}
155
-	}
156
-
157
-	/**
158
-	 * Create a CacheEntry from database row
159
-	 *
160
-	 * @param array $data
161
-	 * @param IMimeTypeLoader $mimetypeLoader
162
-	 * @return CacheEntry
163
-	 */
164
-	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
165
-		//fix types
166
-		$data['fileid'] = (int)$data['fileid'];
167
-		$data['parent'] = (int)$data['parent'];
168
-		$data['size'] = 0 + $data['size'];
169
-		$data['mtime'] = (int)$data['mtime'];
170
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
171
-		$data['encryptedVersion'] = (int)$data['encrypted'];
172
-		$data['encrypted'] = (bool)$data['encrypted'];
173
-		$data['storage_id'] = $data['storage'];
174
-		$data['storage'] = (int)$data['storage'];
175
-		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
176
-		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
177
-		if ($data['storage_mtime'] == 0) {
178
-			$data['storage_mtime'] = $data['mtime'];
179
-		}
180
-		$data['permissions'] = (int)$data['permissions'];
181
-		return new CacheEntry($data);
182
-	}
183
-
184
-	/**
185
-	 * get the metadata of all files stored in $folder
186
-	 *
187
-	 * @param string $folder
188
-	 * @return ICacheEntry[]
189
-	 */
190
-	public function getFolderContents($folder) {
191
-		$fileId = $this->getId($folder);
192
-		return $this->getFolderContentsById($fileId);
193
-	}
194
-
195
-	/**
196
-	 * get the metadata of all files stored in $folder
197
-	 *
198
-	 * @param int $fileId the file id of the folder
199
-	 * @return ICacheEntry[]
200
-	 */
201
-	public function getFolderContentsById($fileId) {
202
-		if ($fileId > -1) {
203
-			$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
137
+        $result = $this->connection->executeQuery($sql, $params);
138
+        $data = $result->fetch();
139
+
140
+        //FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO
141
+        //PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false
142
+        if ($data === null) {
143
+            $data = false;
144
+        }
145
+
146
+        //merge partial data
147
+        if (!$data and is_string($file)) {
148
+            if (isset($this->partial[$file])) {
149
+                $data = $this->partial[$file];
150
+            }
151
+            return $data;
152
+        } else {
153
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
154
+        }
155
+    }
156
+
157
+    /**
158
+     * Create a CacheEntry from database row
159
+     *
160
+     * @param array $data
161
+     * @param IMimeTypeLoader $mimetypeLoader
162
+     * @return CacheEntry
163
+     */
164
+    public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
165
+        //fix types
166
+        $data['fileid'] = (int)$data['fileid'];
167
+        $data['parent'] = (int)$data['parent'];
168
+        $data['size'] = 0 + $data['size'];
169
+        $data['mtime'] = (int)$data['mtime'];
170
+        $data['storage_mtime'] = (int)$data['storage_mtime'];
171
+        $data['encryptedVersion'] = (int)$data['encrypted'];
172
+        $data['encrypted'] = (bool)$data['encrypted'];
173
+        $data['storage_id'] = $data['storage'];
174
+        $data['storage'] = (int)$data['storage'];
175
+        $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
176
+        $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
177
+        if ($data['storage_mtime'] == 0) {
178
+            $data['storage_mtime'] = $data['mtime'];
179
+        }
180
+        $data['permissions'] = (int)$data['permissions'];
181
+        return new CacheEntry($data);
182
+    }
183
+
184
+    /**
185
+     * get the metadata of all files stored in $folder
186
+     *
187
+     * @param string $folder
188
+     * @return ICacheEntry[]
189
+     */
190
+    public function getFolderContents($folder) {
191
+        $fileId = $this->getId($folder);
192
+        return $this->getFolderContentsById($fileId);
193
+    }
194
+
195
+    /**
196
+     * get the metadata of all files stored in $folder
197
+     *
198
+     * @param int $fileId the file id of the folder
199
+     * @return ICacheEntry[]
200
+     */
201
+    public function getFolderContentsById($fileId) {
202
+        if ($fileId > -1) {
203
+            $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
204 204
 						   `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum`
205 205
 					FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC';
206
-			$result = $this->connection->executeQuery($sql, [$fileId]);
207
-			$files = $result->fetchAll();
208
-			return array_map(function (array $data) {
209
-				return self::cacheEntryFromData($data, $this->mimetypeLoader);;
210
-			}, $files);
211
-		} else {
212
-			return array();
213
-		}
214
-	}
215
-
216
-	/**
217
-	 * insert or update meta data for a file or folder
218
-	 *
219
-	 * @param string $file
220
-	 * @param array $data
221
-	 *
222
-	 * @return int file id
223
-	 * @throws \RuntimeException
224
-	 */
225
-	public function put($file, array $data) {
226
-		if (($id = $this->getId($file)) > -1) {
227
-			$this->update($id, $data);
228
-			return $id;
229
-		} else {
230
-			return $this->insert($file, $data);
231
-		}
232
-	}
233
-
234
-	/**
235
-	 * insert meta data for a new file or folder
236
-	 *
237
-	 * @param string $file
238
-	 * @param array $data
239
-	 *
240
-	 * @return int file id
241
-	 * @throws \RuntimeException
242
-	 */
243
-	public function insert($file, array $data) {
244
-		// normalize file
245
-		$file = $this->normalize($file);
246
-
247
-		if (isset($this->partial[$file])) { //add any saved partial data
248
-			$data = array_merge($this->partial[$file], $data);
249
-			unset($this->partial[$file]);
250
-		}
251
-
252
-		$requiredFields = array('size', 'mtime', 'mimetype');
253
-		foreach ($requiredFields as $field) {
254
-			if (!isset($data[$field])) { //data not complete save as partial and return
255
-				$this->partial[$file] = $data;
256
-				return -1;
257
-			}
258
-		}
259
-		$data['path'] = $file;
260
-		$data['parent'] = $this->getParentId($file);
261
-		$data['name'] = \OC_Util::basename($file);
262
-
263
-		list($queryParts, $params) = $this->buildParts($data);
264
-		$queryParts[] = '`storage`';
265
-		$params[] = $this->getNumericStorageId();
266
-
267
-		$queryParts = array_map(function ($item) {
268
-			return trim($item, "`");
269
-		}, $queryParts);
270
-		$values = array_combine($queryParts, $params);
271
-		if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [
272
-			'storage',
273
-			'path_hash',
274
-		])
275
-		) {
276
-			return (int)$this->connection->lastInsertId('*PREFIX*filecache');
277
-		}
278
-
279
-		// The file was created in the mean time
280
-		if (($id = $this->getId($file)) > -1) {
281
-			$this->update($id, $data);
282
-			return $id;
283
-		} else {
284
-			throw new \RuntimeException('File entry could not be inserted with insertIfNotExist() but could also not be selected with getId() in order to perform an update. Please try again.');
285
-		}
286
-	}
287
-
288
-	/**
289
-	 * update the metadata of an existing file or folder in the cache
290
-	 *
291
-	 * @param int $id the fileid of the existing file or folder
292
-	 * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
293
-	 */
294
-	public function update($id, array $data) {
295
-
296
-		if (isset($data['path'])) {
297
-			// normalize path
298
-			$data['path'] = $this->normalize($data['path']);
299
-		}
300
-
301
-		if (isset($data['name'])) {
302
-			// normalize path
303
-			$data['name'] = $this->normalize($data['name']);
304
-		}
305
-
306
-		list($queryParts, $params) = $this->buildParts($data);
307
-		// duplicate $params because we need the parts twice in the SQL statement
308
-		// once for the SET part, once in the WHERE clause
309
-		$params = array_merge($params, $params);
310
-		$params[] = $id;
311
-
312
-		// don't update if the data we try to set is the same as the one in the record
313
-		// some databases (Postgres) don't like superfluous updates
314
-		$sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' .
315
-			'WHERE (' .
316
-			implode(' <> ? OR ', $queryParts) . ' <> ? OR ' .
317
-			implode(' IS NULL OR ', $queryParts) . ' IS NULL' .
318
-			') AND `fileid` = ? ';
319
-		$this->connection->executeQuery($sql, $params);
320
-
321
-	}
322
-
323
-	/**
324
-	 * extract query parts and params array from data array
325
-	 *
326
-	 * @param array $data
327
-	 * @return array [$queryParts, $params]
328
-	 *        $queryParts: string[], the (escaped) column names to be set in the query
329
-	 *        $params: mixed[], the new values for the columns, to be passed as params to the query
330
-	 */
331
-	protected function buildParts(array $data) {
332
-		$fields = array(
333
-			'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
334
-			'etag', 'permissions', 'checksum');
335
-
336
-		$doNotCopyStorageMTime = false;
337
-		if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
338
-			// this horrific magic tells it to not copy storage_mtime to mtime
339
-			unset($data['mtime']);
340
-			$doNotCopyStorageMTime = true;
341
-		}
342
-
343
-		$params = array();
344
-		$queryParts = array();
345
-		foreach ($data as $name => $value) {
346
-			if (array_search($name, $fields) !== false) {
347
-				if ($name === 'path') {
348
-					$params[] = md5($value);
349
-					$queryParts[] = '`path_hash`';
350
-				} elseif ($name === 'mimetype') {
351
-					$params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
352
-					$queryParts[] = '`mimepart`';
353
-					$value = $this->mimetypeLoader->getId($value);
354
-				} elseif ($name === 'storage_mtime') {
355
-					if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
356
-						$params[] = $value;
357
-						$queryParts[] = '`mtime`';
358
-					}
359
-				} elseif ($name === 'encrypted') {
360
-					if (isset($data['encryptedVersion'])) {
361
-						$value = $data['encryptedVersion'];
362
-					} else {
363
-						// Boolean to integer conversion
364
-						$value = $value ? 1 : 0;
365
-					}
366
-				}
367
-				$params[] = $value;
368
-				$queryParts[] = '`' . $name . '`';
369
-			}
370
-		}
371
-		return array($queryParts, $params);
372
-	}
373
-
374
-	/**
375
-	 * get the file id for a file
376
-	 *
377
-	 * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
378
-	 *
379
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
380
-	 *
381
-	 * @param string $file
382
-	 * @return int
383
-	 */
384
-	public function getId($file) {
385
-		// normalize file
386
-		$file = $this->normalize($file);
387
-
388
-		$pathHash = md5($file);
389
-
390
-		$sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
391
-		$result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
392
-		if ($row = $result->fetch()) {
393
-			return $row['fileid'];
394
-		} else {
395
-			return -1;
396
-		}
397
-	}
398
-
399
-	/**
400
-	 * get the id of the parent folder of a file
401
-	 *
402
-	 * @param string $file
403
-	 * @return int
404
-	 */
405
-	public function getParentId($file) {
406
-		if ($file === '') {
407
-			return -1;
408
-		} else {
409
-			$parent = $this->getParentPath($file);
410
-			return (int)$this->getId($parent);
411
-		}
412
-	}
413
-
414
-	private function getParentPath($path) {
415
-		$parent = dirname($path);
416
-		if ($parent === '.') {
417
-			$parent = '';
418
-		}
419
-		return $parent;
420
-	}
421
-
422
-	/**
423
-	 * check if a file is available in the cache
424
-	 *
425
-	 * @param string $file
426
-	 * @return bool
427
-	 */
428
-	public function inCache($file) {
429
-		return $this->getId($file) != -1;
430
-	}
431
-
432
-	/**
433
-	 * remove a file or folder from the cache
434
-	 *
435
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
436
-	 *
437
-	 * @param string $file
438
-	 */
439
-	public function remove($file) {
440
-		$entry = $this->get($file);
441
-		if (!$entry || !$entry['fileid']) {
442
-			return;
443
-		}
444
-		$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?';
445
-		$this->connection->executeQuery($sql, array($entry['fileid']));
446
-		if ($entry['mimetype'] === 'httpd/unix-directory') {
447
-			$this->removeChildren($entry);
448
-		}
449
-	}
450
-
451
-	/**
452
-	 * Get all sub folders of a folder
453
-	 *
454
-	 * @param array $entry the cache entry of the folder to get the subfolders for
455
-	 * @return array[] the cache entries for the subfolders
456
-	 */
457
-	private function getSubFolders($entry) {
458
-		$children = $this->getFolderContentsById($entry['fileid']);
459
-		return array_filter($children, function ($child) {
460
-			return $child['mimetype'] === 'httpd/unix-directory';
461
-		});
462
-	}
463
-
464
-	/**
465
-	 * Recursively remove all children of a folder
466
-	 *
467
-	 * @param array $entry the cache entry of the folder to remove the children of
468
-	 * @throws \OC\DatabaseException
469
-	 */
470
-	private function removeChildren($entry) {
471
-		$subFolders = $this->getSubFolders($entry);
472
-		foreach ($subFolders as $folder) {
473
-			$this->removeChildren($folder);
474
-		}
475
-		$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?';
476
-		$this->connection->executeQuery($sql, array($entry['fileid']));
477
-	}
478
-
479
-	/**
480
-	 * Move a file or folder in the cache
481
-	 *
482
-	 * @param string $source
483
-	 * @param string $target
484
-	 */
485
-	public function move($source, $target) {
486
-		$this->moveFromCache($this, $source, $target);
487
-	}
488
-
489
-	/**
490
-	 * Get the storage id and path needed for a move
491
-	 *
492
-	 * @param string $path
493
-	 * @return array [$storageId, $internalPath]
494
-	 */
495
-	protected function getMoveInfo($path) {
496
-		return [$this->getNumericStorageId(), $path];
497
-	}
498
-
499
-	/**
500
-	 * Move a file or folder in the cache
501
-	 *
502
-	 * @param \OCP\Files\Cache\ICache $sourceCache
503
-	 * @param string $sourcePath
504
-	 * @param string $targetPath
505
-	 * @throws \OC\DatabaseException
506
-	 * @throws \Exception if the given storages have an invalid id
507
-	 */
508
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
509
-		if ($sourceCache instanceof Cache) {
510
-			// normalize source and target
511
-			$sourcePath = $this->normalize($sourcePath);
512
-			$targetPath = $this->normalize($targetPath);
513
-
514
-			$sourceData = $sourceCache->get($sourcePath);
515
-			$sourceId = $sourceData['fileid'];
516
-			$newParentId = $this->getParentId($targetPath);
517
-
518
-			list($sourceStorageId, $sourcePath) = $sourceCache->getMoveInfo($sourcePath);
519
-			list($targetStorageId, $targetPath) = $this->getMoveInfo($targetPath);
520
-
521
-			if (is_null($sourceStorageId) || $sourceStorageId === false) {
522
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
523
-			}
524
-			if (is_null($targetStorageId) || $targetStorageId === false) {
525
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
526
-			}
527
-
528
-			$this->connection->beginTransaction();
529
-			if ($sourceData['mimetype'] === 'httpd/unix-directory') {
530
-				//update all child entries
531
-				$sourceLength = strlen($sourcePath);
532
-				$query = $this->connection->getQueryBuilder();
533
-
534
-				$fun = $query->func();
535
-				$newPathFunction = $fun->concat(
536
-					$query->createNamedParameter($targetPath),
537
-					$fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
538
-				);
539
-				$query->update('filecache')
540
-					->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
541
-					->set('path_hash', $fun->md5($newPathFunction))
542
-					->set('path', $newPathFunction)
543
-					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
544
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
545
-
546
-				try {
547
-					$query->execute();
548
-				} catch (\OC\DatabaseException $e) {
549
-					$this->connection->rollBack();
550
-					throw $e;
551
-				}
552
-			}
553
-
554
-			$sql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` = ? WHERE `fileid` = ?';
555
-			$this->connection->executeQuery($sql, array($targetStorageId, $targetPath, md5($targetPath), \OC_Util::basename($targetPath), $newParentId, $sourceId));
556
-			$this->connection->commit();
557
-		} else {
558
-			$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
559
-		}
560
-	}
561
-
562
-	/**
563
-	 * remove all entries for files that are stored on the storage from the cache
564
-	 */
565
-	public function clear() {
566
-		$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?';
567
-		$this->connection->executeQuery($sql, array($this->getNumericStorageId()));
568
-
569
-		$sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?';
570
-		$this->connection->executeQuery($sql, array($this->storageId));
571
-	}
572
-
573
-	/**
574
-	 * Get the scan status of a file
575
-	 *
576
-	 * - Cache::NOT_FOUND: File is not in the cache
577
-	 * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
578
-	 * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
579
-	 * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
580
-	 *
581
-	 * @param string $file
582
-	 *
583
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
584
-	 */
585
-	public function getStatus($file) {
586
-		// normalize file
587
-		$file = $this->normalize($file);
588
-
589
-		$pathHash = md5($file);
590
-		$sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
591
-		$result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
592
-		if ($row = $result->fetch()) {
593
-			if ((int)$row['size'] === -1) {
594
-				return self::SHALLOW;
595
-			} else {
596
-				return self::COMPLETE;
597
-			}
598
-		} else {
599
-			if (isset($this->partial[$file])) {
600
-				return self::PARTIAL;
601
-			} else {
602
-				return self::NOT_FOUND;
603
-			}
604
-		}
605
-	}
606
-
607
-	/**
608
-	 * search for files matching $pattern
609
-	 *
610
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
611
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
612
-	 */
613
-	public function search($pattern) {
614
-		// normalize pattern
615
-		$pattern = $this->normalize($pattern);
616
-
617
-		if ($pattern === '%%') {
618
-			return [];
619
-		}
620
-
621
-
622
-		$sql = '
206
+            $result = $this->connection->executeQuery($sql, [$fileId]);
207
+            $files = $result->fetchAll();
208
+            return array_map(function (array $data) {
209
+                return self::cacheEntryFromData($data, $this->mimetypeLoader);;
210
+            }, $files);
211
+        } else {
212
+            return array();
213
+        }
214
+    }
215
+
216
+    /**
217
+     * insert or update meta data for a file or folder
218
+     *
219
+     * @param string $file
220
+     * @param array $data
221
+     *
222
+     * @return int file id
223
+     * @throws \RuntimeException
224
+     */
225
+    public function put($file, array $data) {
226
+        if (($id = $this->getId($file)) > -1) {
227
+            $this->update($id, $data);
228
+            return $id;
229
+        } else {
230
+            return $this->insert($file, $data);
231
+        }
232
+    }
233
+
234
+    /**
235
+     * insert meta data for a new file or folder
236
+     *
237
+     * @param string $file
238
+     * @param array $data
239
+     *
240
+     * @return int file id
241
+     * @throws \RuntimeException
242
+     */
243
+    public function insert($file, array $data) {
244
+        // normalize file
245
+        $file = $this->normalize($file);
246
+
247
+        if (isset($this->partial[$file])) { //add any saved partial data
248
+            $data = array_merge($this->partial[$file], $data);
249
+            unset($this->partial[$file]);
250
+        }
251
+
252
+        $requiredFields = array('size', 'mtime', 'mimetype');
253
+        foreach ($requiredFields as $field) {
254
+            if (!isset($data[$field])) { //data not complete save as partial and return
255
+                $this->partial[$file] = $data;
256
+                return -1;
257
+            }
258
+        }
259
+        $data['path'] = $file;
260
+        $data['parent'] = $this->getParentId($file);
261
+        $data['name'] = \OC_Util::basename($file);
262
+
263
+        list($queryParts, $params) = $this->buildParts($data);
264
+        $queryParts[] = '`storage`';
265
+        $params[] = $this->getNumericStorageId();
266
+
267
+        $queryParts = array_map(function ($item) {
268
+            return trim($item, "`");
269
+        }, $queryParts);
270
+        $values = array_combine($queryParts, $params);
271
+        if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [
272
+            'storage',
273
+            'path_hash',
274
+        ])
275
+        ) {
276
+            return (int)$this->connection->lastInsertId('*PREFIX*filecache');
277
+        }
278
+
279
+        // The file was created in the mean time
280
+        if (($id = $this->getId($file)) > -1) {
281
+            $this->update($id, $data);
282
+            return $id;
283
+        } else {
284
+            throw new \RuntimeException('File entry could not be inserted with insertIfNotExist() but could also not be selected with getId() in order to perform an update. Please try again.');
285
+        }
286
+    }
287
+
288
+    /**
289
+     * update the metadata of an existing file or folder in the cache
290
+     *
291
+     * @param int $id the fileid of the existing file or folder
292
+     * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
293
+     */
294
+    public function update($id, array $data) {
295
+
296
+        if (isset($data['path'])) {
297
+            // normalize path
298
+            $data['path'] = $this->normalize($data['path']);
299
+        }
300
+
301
+        if (isset($data['name'])) {
302
+            // normalize path
303
+            $data['name'] = $this->normalize($data['name']);
304
+        }
305
+
306
+        list($queryParts, $params) = $this->buildParts($data);
307
+        // duplicate $params because we need the parts twice in the SQL statement
308
+        // once for the SET part, once in the WHERE clause
309
+        $params = array_merge($params, $params);
310
+        $params[] = $id;
311
+
312
+        // don't update if the data we try to set is the same as the one in the record
313
+        // some databases (Postgres) don't like superfluous updates
314
+        $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' .
315
+            'WHERE (' .
316
+            implode(' <> ? OR ', $queryParts) . ' <> ? OR ' .
317
+            implode(' IS NULL OR ', $queryParts) . ' IS NULL' .
318
+            ') AND `fileid` = ? ';
319
+        $this->connection->executeQuery($sql, $params);
320
+
321
+    }
322
+
323
+    /**
324
+     * extract query parts and params array from data array
325
+     *
326
+     * @param array $data
327
+     * @return array [$queryParts, $params]
328
+     *        $queryParts: string[], the (escaped) column names to be set in the query
329
+     *        $params: mixed[], the new values for the columns, to be passed as params to the query
330
+     */
331
+    protected function buildParts(array $data) {
332
+        $fields = array(
333
+            'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
334
+            'etag', 'permissions', 'checksum');
335
+
336
+        $doNotCopyStorageMTime = false;
337
+        if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
338
+            // this horrific magic tells it to not copy storage_mtime to mtime
339
+            unset($data['mtime']);
340
+            $doNotCopyStorageMTime = true;
341
+        }
342
+
343
+        $params = array();
344
+        $queryParts = array();
345
+        foreach ($data as $name => $value) {
346
+            if (array_search($name, $fields) !== false) {
347
+                if ($name === 'path') {
348
+                    $params[] = md5($value);
349
+                    $queryParts[] = '`path_hash`';
350
+                } elseif ($name === 'mimetype') {
351
+                    $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
352
+                    $queryParts[] = '`mimepart`';
353
+                    $value = $this->mimetypeLoader->getId($value);
354
+                } elseif ($name === 'storage_mtime') {
355
+                    if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
356
+                        $params[] = $value;
357
+                        $queryParts[] = '`mtime`';
358
+                    }
359
+                } elseif ($name === 'encrypted') {
360
+                    if (isset($data['encryptedVersion'])) {
361
+                        $value = $data['encryptedVersion'];
362
+                    } else {
363
+                        // Boolean to integer conversion
364
+                        $value = $value ? 1 : 0;
365
+                    }
366
+                }
367
+                $params[] = $value;
368
+                $queryParts[] = '`' . $name . '`';
369
+            }
370
+        }
371
+        return array($queryParts, $params);
372
+    }
373
+
374
+    /**
375
+     * get the file id for a file
376
+     *
377
+     * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
378
+     *
379
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
380
+     *
381
+     * @param string $file
382
+     * @return int
383
+     */
384
+    public function getId($file) {
385
+        // normalize file
386
+        $file = $this->normalize($file);
387
+
388
+        $pathHash = md5($file);
389
+
390
+        $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
391
+        $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
392
+        if ($row = $result->fetch()) {
393
+            return $row['fileid'];
394
+        } else {
395
+            return -1;
396
+        }
397
+    }
398
+
399
+    /**
400
+     * get the id of the parent folder of a file
401
+     *
402
+     * @param string $file
403
+     * @return int
404
+     */
405
+    public function getParentId($file) {
406
+        if ($file === '') {
407
+            return -1;
408
+        } else {
409
+            $parent = $this->getParentPath($file);
410
+            return (int)$this->getId($parent);
411
+        }
412
+    }
413
+
414
+    private function getParentPath($path) {
415
+        $parent = dirname($path);
416
+        if ($parent === '.') {
417
+            $parent = '';
418
+        }
419
+        return $parent;
420
+    }
421
+
422
+    /**
423
+     * check if a file is available in the cache
424
+     *
425
+     * @param string $file
426
+     * @return bool
427
+     */
428
+    public function inCache($file) {
429
+        return $this->getId($file) != -1;
430
+    }
431
+
432
+    /**
433
+     * remove a file or folder from the cache
434
+     *
435
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
436
+     *
437
+     * @param string $file
438
+     */
439
+    public function remove($file) {
440
+        $entry = $this->get($file);
441
+        if (!$entry || !$entry['fileid']) {
442
+            return;
443
+        }
444
+        $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?';
445
+        $this->connection->executeQuery($sql, array($entry['fileid']));
446
+        if ($entry['mimetype'] === 'httpd/unix-directory') {
447
+            $this->removeChildren($entry);
448
+        }
449
+    }
450
+
451
+    /**
452
+     * Get all sub folders of a folder
453
+     *
454
+     * @param array $entry the cache entry of the folder to get the subfolders for
455
+     * @return array[] the cache entries for the subfolders
456
+     */
457
+    private function getSubFolders($entry) {
458
+        $children = $this->getFolderContentsById($entry['fileid']);
459
+        return array_filter($children, function ($child) {
460
+            return $child['mimetype'] === 'httpd/unix-directory';
461
+        });
462
+    }
463
+
464
+    /**
465
+     * Recursively remove all children of a folder
466
+     *
467
+     * @param array $entry the cache entry of the folder to remove the children of
468
+     * @throws \OC\DatabaseException
469
+     */
470
+    private function removeChildren($entry) {
471
+        $subFolders = $this->getSubFolders($entry);
472
+        foreach ($subFolders as $folder) {
473
+            $this->removeChildren($folder);
474
+        }
475
+        $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?';
476
+        $this->connection->executeQuery($sql, array($entry['fileid']));
477
+    }
478
+
479
+    /**
480
+     * Move a file or folder in the cache
481
+     *
482
+     * @param string $source
483
+     * @param string $target
484
+     */
485
+    public function move($source, $target) {
486
+        $this->moveFromCache($this, $source, $target);
487
+    }
488
+
489
+    /**
490
+     * Get the storage id and path needed for a move
491
+     *
492
+     * @param string $path
493
+     * @return array [$storageId, $internalPath]
494
+     */
495
+    protected function getMoveInfo($path) {
496
+        return [$this->getNumericStorageId(), $path];
497
+    }
498
+
499
+    /**
500
+     * Move a file or folder in the cache
501
+     *
502
+     * @param \OCP\Files\Cache\ICache $sourceCache
503
+     * @param string $sourcePath
504
+     * @param string $targetPath
505
+     * @throws \OC\DatabaseException
506
+     * @throws \Exception if the given storages have an invalid id
507
+     */
508
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
509
+        if ($sourceCache instanceof Cache) {
510
+            // normalize source and target
511
+            $sourcePath = $this->normalize($sourcePath);
512
+            $targetPath = $this->normalize($targetPath);
513
+
514
+            $sourceData = $sourceCache->get($sourcePath);
515
+            $sourceId = $sourceData['fileid'];
516
+            $newParentId = $this->getParentId($targetPath);
517
+
518
+            list($sourceStorageId, $sourcePath) = $sourceCache->getMoveInfo($sourcePath);
519
+            list($targetStorageId, $targetPath) = $this->getMoveInfo($targetPath);
520
+
521
+            if (is_null($sourceStorageId) || $sourceStorageId === false) {
522
+                throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
523
+            }
524
+            if (is_null($targetStorageId) || $targetStorageId === false) {
525
+                throw new \Exception('Invalid target storage id: ' . $targetStorageId);
526
+            }
527
+
528
+            $this->connection->beginTransaction();
529
+            if ($sourceData['mimetype'] === 'httpd/unix-directory') {
530
+                //update all child entries
531
+                $sourceLength = strlen($sourcePath);
532
+                $query = $this->connection->getQueryBuilder();
533
+
534
+                $fun = $query->func();
535
+                $newPathFunction = $fun->concat(
536
+                    $query->createNamedParameter($targetPath),
537
+                    $fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
538
+                );
539
+                $query->update('filecache')
540
+                    ->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
541
+                    ->set('path_hash', $fun->md5($newPathFunction))
542
+                    ->set('path', $newPathFunction)
543
+                    ->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
544
+                    ->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
545
+
546
+                try {
547
+                    $query->execute();
548
+                } catch (\OC\DatabaseException $e) {
549
+                    $this->connection->rollBack();
550
+                    throw $e;
551
+                }
552
+            }
553
+
554
+            $sql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` = ? WHERE `fileid` = ?';
555
+            $this->connection->executeQuery($sql, array($targetStorageId, $targetPath, md5($targetPath), \OC_Util::basename($targetPath), $newParentId, $sourceId));
556
+            $this->connection->commit();
557
+        } else {
558
+            $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
559
+        }
560
+    }
561
+
562
+    /**
563
+     * remove all entries for files that are stored on the storage from the cache
564
+     */
565
+    public function clear() {
566
+        $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?';
567
+        $this->connection->executeQuery($sql, array($this->getNumericStorageId()));
568
+
569
+        $sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?';
570
+        $this->connection->executeQuery($sql, array($this->storageId));
571
+    }
572
+
573
+    /**
574
+     * Get the scan status of a file
575
+     *
576
+     * - Cache::NOT_FOUND: File is not in the cache
577
+     * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
578
+     * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
579
+     * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
580
+     *
581
+     * @param string $file
582
+     *
583
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
584
+     */
585
+    public function getStatus($file) {
586
+        // normalize file
587
+        $file = $this->normalize($file);
588
+
589
+        $pathHash = md5($file);
590
+        $sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
591
+        $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
592
+        if ($row = $result->fetch()) {
593
+            if ((int)$row['size'] === -1) {
594
+                return self::SHALLOW;
595
+            } else {
596
+                return self::COMPLETE;
597
+            }
598
+        } else {
599
+            if (isset($this->partial[$file])) {
600
+                return self::PARTIAL;
601
+            } else {
602
+                return self::NOT_FOUND;
603
+            }
604
+        }
605
+    }
606
+
607
+    /**
608
+     * search for files matching $pattern
609
+     *
610
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
611
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
612
+     */
613
+    public function search($pattern) {
614
+        // normalize pattern
615
+        $pattern = $this->normalize($pattern);
616
+
617
+        if ($pattern === '%%') {
618
+            return [];
619
+        }
620
+
621
+
622
+        $sql = '
623 623
 			SELECT `fileid`, `storage`, `path`, `parent`, `name`,
624 624
 				`mimetype`, `storage_mtime`, `mimepart`, `size`, `mtime`,
625 625
 				 `encrypted`, `etag`, `permissions`, `checksum`
626 626
 			FROM `*PREFIX*filecache`
627 627
 			WHERE `storage` = ? AND `name` ILIKE ?';
628
-		$result = $this->connection->executeQuery($sql,
629
-			[$this->getNumericStorageId(), $pattern]
630
-		);
631
-
632
-		return $this->searchResultToCacheEntries($result);
633
-	}
634
-
635
-	/**
636
-	 * @param Statement $result
637
-	 * @return CacheEntry[]
638
-	 */
639
-	private function searchResultToCacheEntries(Statement $result) {
640
-		$files = $result->fetchAll();
641
-
642
-		return array_map(function (array $data) {
643
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
644
-		}, $files);
645
-	}
646
-
647
-	/**
648
-	 * search for files by mimetype
649
-	 *
650
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
651
-	 *        where it will search for all mimetypes in the group ('image/*')
652
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
653
-	 */
654
-	public function searchByMime($mimetype) {
655
-		if (strpos($mimetype, '/')) {
656
-			$where = '`mimetype` = ?';
657
-		} else {
658
-			$where = '`mimepart` = ?';
659
-		}
660
-		$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum`
628
+        $result = $this->connection->executeQuery($sql,
629
+            [$this->getNumericStorageId(), $pattern]
630
+        );
631
+
632
+        return $this->searchResultToCacheEntries($result);
633
+    }
634
+
635
+    /**
636
+     * @param Statement $result
637
+     * @return CacheEntry[]
638
+     */
639
+    private function searchResultToCacheEntries(Statement $result) {
640
+        $files = $result->fetchAll();
641
+
642
+        return array_map(function (array $data) {
643
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
644
+        }, $files);
645
+    }
646
+
647
+    /**
648
+     * search for files by mimetype
649
+     *
650
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
651
+     *        where it will search for all mimetypes in the group ('image/*')
652
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
653
+     */
654
+    public function searchByMime($mimetype) {
655
+        if (strpos($mimetype, '/')) {
656
+            $where = '`mimetype` = ?';
657
+        } else {
658
+            $where = '`mimepart` = ?';
659
+        }
660
+        $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum`
661 661
 				FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?';
662
-		$mimetype = $this->mimetypeLoader->getId($mimetype);
663
-		$result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId()));
664
-
665
-		return $this->searchResultToCacheEntries($result);
666
-	}
667
-
668
-	public function searchQuery(ISearchQuery $searchQuery) {
669
-		$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
670
-
671
-		$query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum'])
672
-			->from('filecache', 'file');
673
-
674
-		$query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId())));
675
-
676
-		if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
677
-			$query
678
-				->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
679
-				->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
680
-					$builder->expr()->eq('tagmap.type', 'tag.type'),
681
-					$builder->expr()->eq('tagmap.categoryid', 'tag.id')
682
-				))
683
-				->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
684
-				->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
685
-		}
686
-
687
-		$query->andWhere($this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation()));
688
-
689
-		$this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
690
-
691
-		if ($searchQuery->getLimit()) {
692
-			$query->setMaxResults($searchQuery->getLimit());
693
-		}
694
-		if ($searchQuery->getOffset()) {
695
-			$query->setFirstResult($searchQuery->getOffset());
696
-		}
697
-
698
-		$result = $query->execute();
699
-		return $this->searchResultToCacheEntries($result);
700
-	}
701
-
702
-	/**
703
-	 * Search for files by tag of a given users.
704
-	 *
705
-	 * Note that every user can tag files differently.
706
-	 *
707
-	 * @param string|int $tag name or tag id
708
-	 * @param string $userId owner of the tags
709
-	 * @return ICacheEntry[] file data
710
-	 */
711
-	public function searchByTag($tag, $userId) {
712
-		$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, ' .
713
-			'`mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, ' .
714
-			'`encrypted`, `etag`, `permissions`, `checksum` ' .
715
-			'FROM `*PREFIX*filecache` `file`, ' .
716
-			'`*PREFIX*vcategory_to_object` `tagmap`, ' .
717
-			'`*PREFIX*vcategory` `tag` ' .
718
-			// JOIN filecache to vcategory_to_object
719
-			'WHERE `file`.`fileid` = `tagmap`.`objid` ' .
720
-			// JOIN vcategory_to_object to vcategory
721
-			'AND `tagmap`.`type` = `tag`.`type` ' .
722
-			'AND `tagmap`.`categoryid` = `tag`.`id` ' .
723
-			// conditions
724
-			'AND `file`.`storage` = ? ' .
725
-			'AND `tag`.`type` = \'files\' ' .
726
-			'AND `tag`.`uid` = ? ';
727
-		if (is_int($tag)) {
728
-			$sql .= 'AND `tag`.`id` = ? ';
729
-		} else {
730
-			$sql .= 'AND `tag`.`category` = ? ';
731
-		}
732
-		$result = $this->connection->executeQuery(
733
-			$sql,
734
-			[
735
-				$this->getNumericStorageId(),
736
-				$userId,
737
-				$tag
738
-			]
739
-		);
740
-
741
-		$files = $result->fetchAll();
742
-
743
-		return array_map(function (array $data) {
744
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
745
-		}, $files);
746
-	}
747
-
748
-	/**
749
-	 * Re-calculate the folder size and the size of all parent folders
750
-	 *
751
-	 * @param string|boolean $path
752
-	 * @param array $data (optional) meta data of the folder
753
-	 */
754
-	public function correctFolderSize($path, $data = null) {
755
-		$this->calculateFolderSize($path, $data);
756
-		if ($path !== '') {
757
-			$parent = dirname($path);
758
-			if ($parent === '.' or $parent === '/') {
759
-				$parent = '';
760
-			}
761
-			$this->correctFolderSize($parent);
762
-		}
763
-	}
764
-
765
-	/**
766
-	 * calculate the size of a folder and set it in the cache
767
-	 *
768
-	 * @param string $path
769
-	 * @param array $entry (optional) meta data of the folder
770
-	 * @return int
771
-	 */
772
-	public function calculateFolderSize($path, $entry = null) {
773
-		$totalSize = 0;
774
-		if (is_null($entry) or !isset($entry['fileid'])) {
775
-			$entry = $this->get($path);
776
-		}
777
-		if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') {
778
-			$id = $entry['fileid'];
779
-			$sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' .
780
-				'FROM `*PREFIX*filecache` ' .
781
-				'WHERE `parent` = ? AND `storage` = ?';
782
-			$result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
783
-			if ($row = $result->fetch()) {
784
-				$result->closeCursor();
785
-				list($sum, $min) = array_values($row);
786
-				$sum = 0 + $sum;
787
-				$min = 0 + $min;
788
-				if ($min === -1) {
789
-					$totalSize = $min;
790
-				} else {
791
-					$totalSize = $sum;
792
-				}
793
-				$update = array();
794
-				if ($entry['size'] !== $totalSize) {
795
-					$update['size'] = $totalSize;
796
-				}
797
-				if (count($update) > 0) {
798
-					$this->update($id, $update);
799
-				}
800
-			} else {
801
-				$result->closeCursor();
802
-			}
803
-		}
804
-		return $totalSize;
805
-	}
806
-
807
-	/**
808
-	 * get all file ids on the files on the storage
809
-	 *
810
-	 * @return int[]
811
-	 */
812
-	public function getAll() {
813
-		$sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?';
814
-		$result = $this->connection->executeQuery($sql, array($this->getNumericStorageId()));
815
-		$ids = array();
816
-		while ($row = $result->fetch()) {
817
-			$ids[] = $row['fileid'];
818
-		}
819
-		return $ids;
820
-	}
821
-
822
-	/**
823
-	 * find a folder in the cache which has not been fully scanned
824
-	 *
825
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
826
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
827
-	 * likely the folder where we stopped scanning previously
828
-	 *
829
-	 * @return string|bool the path of the folder or false when no folder matched
830
-	 */
831
-	public function getIncomplete() {
832
-		$query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`'
833
-			. ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1);
834
-		$query->execute([$this->getNumericStorageId()]);
835
-		if ($row = $query->fetch()) {
836
-			return $row['path'];
837
-		} else {
838
-			return false;
839
-		}
840
-	}
841
-
842
-	/**
843
-	 * get the path of a file on this storage by it's file id
844
-	 *
845
-	 * @param int $id the file id of the file or folder to search
846
-	 * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
847
-	 */
848
-	public function getPathById($id) {
849
-		$sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?';
850
-		$result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
851
-		if ($row = $result->fetch()) {
852
-			// Oracle stores empty strings as null...
853
-			if ($row['path'] === null) {
854
-				return '';
855
-			}
856
-			return $row['path'];
857
-		} else {
858
-			return null;
859
-		}
860
-	}
861
-
862
-	/**
863
-	 * get the storage id of the storage for a file and the internal path of the file
864
-	 * unlike getPathById this does not limit the search to files on this storage and
865
-	 * instead does a global search in the cache table
866
-	 *
867
-	 * @param int $id
868
-	 * @deprecated use getPathById() instead
869
-	 * @return array first element holding the storage id, second the path
870
-	 */
871
-	static public function getById($id) {
872
-		$connection = \OC::$server->getDatabaseConnection();
873
-		$sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?';
874
-		$result = $connection->executeQuery($sql, array($id));
875
-		if ($row = $result->fetch()) {
876
-			$numericId = $row['storage'];
877
-			$path = $row['path'];
878
-		} else {
879
-			return null;
880
-		}
881
-
882
-		if ($id = Storage::getStorageId($numericId)) {
883
-			return array($id, $path);
884
-		} else {
885
-			return null;
886
-		}
887
-	}
888
-
889
-	/**
890
-	 * normalize the given path
891
-	 *
892
-	 * @param string $path
893
-	 * @return string
894
-	 */
895
-	public function normalize($path) {
896
-
897
-		return trim(\OC_Util::normalizeUnicode($path), '/');
898
-	}
662
+        $mimetype = $this->mimetypeLoader->getId($mimetype);
663
+        $result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId()));
664
+
665
+        return $this->searchResultToCacheEntries($result);
666
+    }
667
+
668
+    public function searchQuery(ISearchQuery $searchQuery) {
669
+        $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
670
+
671
+        $query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum'])
672
+            ->from('filecache', 'file');
673
+
674
+        $query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId())));
675
+
676
+        if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
677
+            $query
678
+                ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
679
+                ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
680
+                    $builder->expr()->eq('tagmap.type', 'tag.type'),
681
+                    $builder->expr()->eq('tagmap.categoryid', 'tag.id')
682
+                ))
683
+                ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
684
+                ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
685
+        }
686
+
687
+        $query->andWhere($this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation()));
688
+
689
+        $this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
690
+
691
+        if ($searchQuery->getLimit()) {
692
+            $query->setMaxResults($searchQuery->getLimit());
693
+        }
694
+        if ($searchQuery->getOffset()) {
695
+            $query->setFirstResult($searchQuery->getOffset());
696
+        }
697
+
698
+        $result = $query->execute();
699
+        return $this->searchResultToCacheEntries($result);
700
+    }
701
+
702
+    /**
703
+     * Search for files by tag of a given users.
704
+     *
705
+     * Note that every user can tag files differently.
706
+     *
707
+     * @param string|int $tag name or tag id
708
+     * @param string $userId owner of the tags
709
+     * @return ICacheEntry[] file data
710
+     */
711
+    public function searchByTag($tag, $userId) {
712
+        $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, ' .
713
+            '`mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, ' .
714
+            '`encrypted`, `etag`, `permissions`, `checksum` ' .
715
+            'FROM `*PREFIX*filecache` `file`, ' .
716
+            '`*PREFIX*vcategory_to_object` `tagmap`, ' .
717
+            '`*PREFIX*vcategory` `tag` ' .
718
+            // JOIN filecache to vcategory_to_object
719
+            'WHERE `file`.`fileid` = `tagmap`.`objid` ' .
720
+            // JOIN vcategory_to_object to vcategory
721
+            'AND `tagmap`.`type` = `tag`.`type` ' .
722
+            'AND `tagmap`.`categoryid` = `tag`.`id` ' .
723
+            // conditions
724
+            'AND `file`.`storage` = ? ' .
725
+            'AND `tag`.`type` = \'files\' ' .
726
+            'AND `tag`.`uid` = ? ';
727
+        if (is_int($tag)) {
728
+            $sql .= 'AND `tag`.`id` = ? ';
729
+        } else {
730
+            $sql .= 'AND `tag`.`category` = ? ';
731
+        }
732
+        $result = $this->connection->executeQuery(
733
+            $sql,
734
+            [
735
+                $this->getNumericStorageId(),
736
+                $userId,
737
+                $tag
738
+            ]
739
+        );
740
+
741
+        $files = $result->fetchAll();
742
+
743
+        return array_map(function (array $data) {
744
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
745
+        }, $files);
746
+    }
747
+
748
+    /**
749
+     * Re-calculate the folder size and the size of all parent folders
750
+     *
751
+     * @param string|boolean $path
752
+     * @param array $data (optional) meta data of the folder
753
+     */
754
+    public function correctFolderSize($path, $data = null) {
755
+        $this->calculateFolderSize($path, $data);
756
+        if ($path !== '') {
757
+            $parent = dirname($path);
758
+            if ($parent === '.' or $parent === '/') {
759
+                $parent = '';
760
+            }
761
+            $this->correctFolderSize($parent);
762
+        }
763
+    }
764
+
765
+    /**
766
+     * calculate the size of a folder and set it in the cache
767
+     *
768
+     * @param string $path
769
+     * @param array $entry (optional) meta data of the folder
770
+     * @return int
771
+     */
772
+    public function calculateFolderSize($path, $entry = null) {
773
+        $totalSize = 0;
774
+        if (is_null($entry) or !isset($entry['fileid'])) {
775
+            $entry = $this->get($path);
776
+        }
777
+        if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') {
778
+            $id = $entry['fileid'];
779
+            $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' .
780
+                'FROM `*PREFIX*filecache` ' .
781
+                'WHERE `parent` = ? AND `storage` = ?';
782
+            $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
783
+            if ($row = $result->fetch()) {
784
+                $result->closeCursor();
785
+                list($sum, $min) = array_values($row);
786
+                $sum = 0 + $sum;
787
+                $min = 0 + $min;
788
+                if ($min === -1) {
789
+                    $totalSize = $min;
790
+                } else {
791
+                    $totalSize = $sum;
792
+                }
793
+                $update = array();
794
+                if ($entry['size'] !== $totalSize) {
795
+                    $update['size'] = $totalSize;
796
+                }
797
+                if (count($update) > 0) {
798
+                    $this->update($id, $update);
799
+                }
800
+            } else {
801
+                $result->closeCursor();
802
+            }
803
+        }
804
+        return $totalSize;
805
+    }
806
+
807
+    /**
808
+     * get all file ids on the files on the storage
809
+     *
810
+     * @return int[]
811
+     */
812
+    public function getAll() {
813
+        $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?';
814
+        $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId()));
815
+        $ids = array();
816
+        while ($row = $result->fetch()) {
817
+            $ids[] = $row['fileid'];
818
+        }
819
+        return $ids;
820
+    }
821
+
822
+    /**
823
+     * find a folder in the cache which has not been fully scanned
824
+     *
825
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
826
+     * use the one with the highest id gives the best result with the background scanner, since that is most
827
+     * likely the folder where we stopped scanning previously
828
+     *
829
+     * @return string|bool the path of the folder or false when no folder matched
830
+     */
831
+    public function getIncomplete() {
832
+        $query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`'
833
+            . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1);
834
+        $query->execute([$this->getNumericStorageId()]);
835
+        if ($row = $query->fetch()) {
836
+            return $row['path'];
837
+        } else {
838
+            return false;
839
+        }
840
+    }
841
+
842
+    /**
843
+     * get the path of a file on this storage by it's file id
844
+     *
845
+     * @param int $id the file id of the file or folder to search
846
+     * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
847
+     */
848
+    public function getPathById($id) {
849
+        $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?';
850
+        $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
851
+        if ($row = $result->fetch()) {
852
+            // Oracle stores empty strings as null...
853
+            if ($row['path'] === null) {
854
+                return '';
855
+            }
856
+            return $row['path'];
857
+        } else {
858
+            return null;
859
+        }
860
+    }
861
+
862
+    /**
863
+     * get the storage id of the storage for a file and the internal path of the file
864
+     * unlike getPathById this does not limit the search to files on this storage and
865
+     * instead does a global search in the cache table
866
+     *
867
+     * @param int $id
868
+     * @deprecated use getPathById() instead
869
+     * @return array first element holding the storage id, second the path
870
+     */
871
+    static public function getById($id) {
872
+        $connection = \OC::$server->getDatabaseConnection();
873
+        $sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?';
874
+        $result = $connection->executeQuery($sql, array($id));
875
+        if ($row = $result->fetch()) {
876
+            $numericId = $row['storage'];
877
+            $path = $row['path'];
878
+        } else {
879
+            return null;
880
+        }
881
+
882
+        if ($id = Storage::getStorageId($numericId)) {
883
+            return array($id, $path);
884
+        } else {
885
+            return null;
886
+        }
887
+    }
888
+
889
+    /**
890
+     * normalize the given path
891
+     *
892
+     * @param string $path
893
+     * @return string
894
+     */
895
+    public function normalize($path) {
896
+
897
+        return trim(\OC_Util::normalizeUnicode($path), '/');
898
+    }
899 899
 }
Please login to merge, or discard this patch.