Completed
Push — stable13 ( 0404ac...57ca18 )
by Morris
158:38 queued 134:00
created
lib/private/Files/SimpleFS/SimpleFile.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -26,7 +26,7 @@
 block discarded – undo
26 26
 use OCP\Files\NotPermittedException;
27 27
 use OCP\Files\SimpleFS\ISimpleFile;
28 28
 
29
-class SimpleFile implements ISimpleFile  {
29
+class SimpleFile implements ISimpleFile {
30 30
 
31 31
 	/** @var File $file */
32 32
 	private $file;
Please login to merge, or discard this patch.
Indentation   +117 added lines, -117 removed lines patch added patch discarded remove patch
@@ -29,121 +29,121 @@
 block discarded – undo
29 29
 
30 30
 class SimpleFile implements ISimpleFile  {
31 31
 
32
-	/** @var File $file */
33
-	private $file;
34
-
35
-	/**
36
-	 * File constructor.
37
-	 *
38
-	 * @param File $file
39
-	 */
40
-	public function __construct(File $file) {
41
-		$this->file = $file;
42
-	}
43
-
44
-	/**
45
-	 * Get the name
46
-	 *
47
-	 * @return string
48
-	 */
49
-	public function getName() {
50
-		return $this->file->getName();
51
-	}
52
-
53
-	/**
54
-	 * Get the size in bytes
55
-	 *
56
-	 * @return int
57
-	 */
58
-	public function getSize() {
59
-		return $this->file->getSize();
60
-	}
61
-
62
-	/**
63
-	 * Get the ETag
64
-	 *
65
-	 * @return string
66
-	 */
67
-	public function getETag() {
68
-		return $this->file->getEtag();
69
-	}
70
-
71
-	/**
72
-	 * Get the last modification time
73
-	 *
74
-	 * @return int
75
-	 */
76
-	public function getMTime() {
77
-		return $this->file->getMTime();
78
-	}
79
-
80
-	/**
81
-	 * Get the content
82
-	 *
83
-	 * @throws NotPermittedException
84
-	 * @throws NotFoundException
85
-	 * @return string
86
-	 */
87
-	public function getContent() {
88
-		$result = $this->file->getContent();
89
-
90
-		if ($result === false) {
91
-			$this->checkFile();
92
-		}
93
-
94
-		return $result;
95
-	}
96
-
97
-	/**
98
-	 * Overwrite the file
99
-	 *
100
-	 * @param string $data
101
-	 * @throws NotPermittedException
102
-	 */
103
-	public function putContent($data) {
104
-		$this->file->putContent($data);
105
-	}
106
-
107
-	/**
108
-	 * Sometimes there are some issues with the AppData. Most of them are from
109
-	 * user error. But we should handle them gracefull anyway.
110
-	 *
111
-	 * If for some reason the current file can't be found. We remove it.
112
-	 * Then traverse up and check all folders if they exists. This so that the
113
-	 * next request will have a valid appdata structure again.
114
-	 *
115
-	 * @throws NotFoundException
116
-	 */
117
-	private function checkFile() {
118
-		$cur = $this->file;
119
-
120
-		while ($cur->stat() === false) {
121
-			$parent = $cur->getParent();
122
-			$cur->delete();
123
-			$cur = $parent;
124
-		}
125
-
126
-		if ($cur !== $this->file) {
127
-			throw new NotFoundException('File does not exist');
128
-		}
129
-	}
130
-
131
-
132
-	/**
133
-	 * Delete the file
134
-	 *
135
-	 * @throws NotPermittedException
136
-	 */
137
-	public function delete() {
138
-		$this->file->delete();
139
-	}
140
-
141
-	/**
142
-	 * Get the MimeType
143
-	 *
144
-	 * @return string
145
-	 */
146
-	public function getMimeType() {
147
-		return $this->file->getMimeType();
148
-	}
32
+    /** @var File $file */
33
+    private $file;
34
+
35
+    /**
36
+     * File constructor.
37
+     *
38
+     * @param File $file
39
+     */
40
+    public function __construct(File $file) {
41
+        $this->file = $file;
42
+    }
43
+
44
+    /**
45
+     * Get the name
46
+     *
47
+     * @return string
48
+     */
49
+    public function getName() {
50
+        return $this->file->getName();
51
+    }
52
+
53
+    /**
54
+     * Get the size in bytes
55
+     *
56
+     * @return int
57
+     */
58
+    public function getSize() {
59
+        return $this->file->getSize();
60
+    }
61
+
62
+    /**
63
+     * Get the ETag
64
+     *
65
+     * @return string
66
+     */
67
+    public function getETag() {
68
+        return $this->file->getEtag();
69
+    }
70
+
71
+    /**
72
+     * Get the last modification time
73
+     *
74
+     * @return int
75
+     */
76
+    public function getMTime() {
77
+        return $this->file->getMTime();
78
+    }
79
+
80
+    /**
81
+     * Get the content
82
+     *
83
+     * @throws NotPermittedException
84
+     * @throws NotFoundException
85
+     * @return string
86
+     */
87
+    public function getContent() {
88
+        $result = $this->file->getContent();
89
+
90
+        if ($result === false) {
91
+            $this->checkFile();
92
+        }
93
+
94
+        return $result;
95
+    }
96
+
97
+    /**
98
+     * Overwrite the file
99
+     *
100
+     * @param string $data
101
+     * @throws NotPermittedException
102
+     */
103
+    public function putContent($data) {
104
+        $this->file->putContent($data);
105
+    }
106
+
107
+    /**
108
+     * Sometimes there are some issues with the AppData. Most of them are from
109
+     * user error. But we should handle them gracefull anyway.
110
+     *
111
+     * If for some reason the current file can't be found. We remove it.
112
+     * Then traverse up and check all folders if they exists. This so that the
113
+     * next request will have a valid appdata structure again.
114
+     *
115
+     * @throws NotFoundException
116
+     */
117
+    private function checkFile() {
118
+        $cur = $this->file;
119
+
120
+        while ($cur->stat() === false) {
121
+            $parent = $cur->getParent();
122
+            $cur->delete();
123
+            $cur = $parent;
124
+        }
125
+
126
+        if ($cur !== $this->file) {
127
+            throw new NotFoundException('File does not exist');
128
+        }
129
+    }
130
+
131
+
132
+    /**
133
+     * Delete the file
134
+     *
135
+     * @throws NotPermittedException
136
+     */
137
+    public function delete() {
138
+        $this->file->delete();
139
+    }
140
+
141
+    /**
142
+     * Get the MimeType
143
+     *
144
+     * @return string
145
+     */
146
+    public function getMimeType() {
147
+        return $this->file->getMimeType();
148
+    }
149 149
 }
Please login to merge, or discard this patch.
lib/private/Files/SimpleFS/SimpleFolder.php 2 patches
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -30,58 +30,58 @@
 block discarded – undo
30 30
 
31 31
 class SimpleFolder implements ISimpleFolder   {
32 32
 
33
-	/** @var Folder */
34
-	private $folder;
33
+    /** @var Folder */
34
+    private $folder;
35 35
 
36
-	/**
37
-	 * Folder constructor.
38
-	 *
39
-	 * @param Folder $folder
40
-	 */
41
-	public function __construct(Folder $folder) {
42
-		$this->folder = $folder;
43
-	}
36
+    /**
37
+     * Folder constructor.
38
+     *
39
+     * @param Folder $folder
40
+     */
41
+    public function __construct(Folder $folder) {
42
+        $this->folder = $folder;
43
+    }
44 44
 
45
-	public function getName() {
46
-		return $this->folder->getName();
47
-	}
45
+    public function getName() {
46
+        return $this->folder->getName();
47
+    }
48 48
 
49
-	public function getDirectoryListing() {
50
-		$listing = $this->folder->getDirectoryListing();
49
+    public function getDirectoryListing() {
50
+        $listing = $this->folder->getDirectoryListing();
51 51
 
52
-		$fileListing = array_map(function(Node $file) {
53
-			if ($file instanceof File) {
54
-				return new SimpleFile($file);
55
-			}
56
-			return null;
57
-		}, $listing);
52
+        $fileListing = array_map(function(Node $file) {
53
+            if ($file instanceof File) {
54
+                return new SimpleFile($file);
55
+            }
56
+            return null;
57
+        }, $listing);
58 58
 
59
-		$fileListing = array_filter($fileListing);
59
+        $fileListing = array_filter($fileListing);
60 60
 
61
-		return array_values($fileListing);
62
-	}
61
+        return array_values($fileListing);
62
+    }
63 63
 
64
-	public function delete() {
65
-		$this->folder->delete();
66
-	}
64
+    public function delete() {
65
+        $this->folder->delete();
66
+    }
67 67
 
68
-	public function fileExists($name) {
69
-		return $this->folder->nodeExists($name);
70
-	}
68
+    public function fileExists($name) {
69
+        return $this->folder->nodeExists($name);
70
+    }
71 71
 
72
-	public function getFile($name) {
73
-		$file = $this->folder->get($name);
72
+    public function getFile($name) {
73
+        $file = $this->folder->get($name);
74 74
 
75
-		if (!($file instanceof File)) {
76
-			throw new NotFoundException();
77
-		}
75
+        if (!($file instanceof File)) {
76
+            throw new NotFoundException();
77
+        }
78 78
 
79
-		return new SimpleFile($file);
80
-	}
79
+        return new SimpleFile($file);
80
+    }
81 81
 
82
-	public function newFile($name) {
83
-		$file = $this->folder->newFile($name);
82
+    public function newFile($name) {
83
+        $file = $this->folder->newFile($name);
84 84
 
85
-		return new SimpleFile($file);
86
-	}
85
+        return new SimpleFile($file);
86
+    }
87 87
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -28,7 +28,7 @@
 block discarded – undo
28 28
 use OCP\Files\NotFoundException;
29 29
 use OCP\Files\SimpleFS\ISimpleFolder;
30 30
 
31
-class SimpleFolder implements ISimpleFolder   {
31
+class SimpleFolder implements ISimpleFolder {
32 32
 
33 33
 	/** @var Folder */
34 34
 	private $folder;
Please login to merge, or discard this patch.
lib/private/Files/Storage/Wrapper/Encoding.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
 				// no point in continuing if the section was not found, use original path
81 81
 				return $fullPath;
82 82
 			}
83
-			$path = $convertedPath . '/';
83
+			$path = $convertedPath.'/';
84 84
 		}
85 85
 		$path = rtrim($path, '/');
86 86
 		return $path;
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
 	 * @return string|null original or converted path, or null if none of the forms was found
97 97
 	 */
98 98
 	private function findPathToUseLastSection($basePath, $lastSection) {
99
-		$fullPath = $basePath . $lastSection;
99
+		$fullPath = $basePath.$lastSection;
100 100
 		if ($lastSection === '' || $this->isAscii($lastSection) || $this->storage->file_exists($fullPath)) {
101 101
 			$this->namesCache[$fullPath] = $fullPath;
102 102
 			return $fullPath;
@@ -108,7 +108,7 @@  discard block
 block discarded – undo
108 108
 		} else {
109 109
 			$otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_C);
110 110
 		}
111
-		$otherFullPath = $basePath . $otherFormPath;
111
+		$otherFullPath = $basePath.$otherFormPath;
112 112
 		if ($this->storage->file_exists($otherFullPath)) {
113 113
 			$this->namesCache[$fullPath] = $otherFullPath;
114 114
 			return $otherFullPath;
Please login to merge, or discard this patch.
Indentation   +498 added lines, -498 removed lines patch added patch discarded remove patch
@@ -34,502 +34,502 @@
 block discarded – undo
34 34
  */
35 35
 class Encoding extends Wrapper {
36 36
 
37
-	/**
38
-	 * @var ICache
39
-	 */
40
-	private $namesCache;
41
-
42
-	/**
43
-	 * @param array $parameters
44
-	 */
45
-	public function __construct($parameters) {
46
-		$this->storage = $parameters['storage'];
47
-		$this->namesCache = new CappedMemoryCache();
48
-	}
49
-
50
-	/**
51
-	 * Returns whether the given string is only made of ASCII characters
52
-	 *
53
-	 * @param string $str string
54
-	 *
55
-	 * @return bool true if the string is all ASCII, false otherwise
56
-	 */
57
-	private function isAscii($str) {
58
-		return (bool) !preg_match('/[\\x80-\\xff]+/', $str);
59
-	}
60
-
61
-	/**
62
-	 * Checks whether the given path exists in NFC or NFD form after checking
63
-	 * each form for each path section and returns the correct form.
64
-	 * If no existing path found, returns the path as it was given.
65
-	 *
66
-	 * @param string $fullPath path to check
67
-	 *
68
-	 * @return string original or converted path
69
-	 */
70
-	private function findPathToUse($fullPath) {
71
-		$cachedPath = $this->namesCache[$fullPath];
72
-		if ($cachedPath !== null) {
73
-			return $cachedPath;
74
-		}
75
-
76
-		$sections = explode('/', $fullPath);
77
-		$path = '';
78
-		foreach ($sections as $section) {
79
-			$convertedPath = $this->findPathToUseLastSection($path, $section);
80
-			if ($convertedPath === null) {
81
-				// no point in continuing if the section was not found, use original path
82
-				return $fullPath;
83
-			}
84
-			$path = $convertedPath . '/';
85
-		}
86
-		$path = rtrim($path, '/');
87
-		return $path;
88
-	}
89
-
90
-	/**
91
-	 * Checks whether the last path section of the given path exists in NFC or NFD form
92
-	 * and returns the correct form. If no existing path found, returns null.
93
-	 *
94
-	 * @param string $basePath base path to check
95
-	 * @param string $lastSection last section of the path to check for NFD/NFC variations
96
-	 *
97
-	 * @return string|null original or converted path, or null if none of the forms was found
98
-	 */
99
-	private function findPathToUseLastSection($basePath, $lastSection) {
100
-		$fullPath = $basePath . $lastSection;
101
-		if ($lastSection === '' || $this->isAscii($lastSection) || $this->storage->file_exists($fullPath)) {
102
-			$this->namesCache[$fullPath] = $fullPath;
103
-			return $fullPath;
104
-		}
105
-
106
-		// swap encoding
107
-		if (\Normalizer::isNormalized($lastSection, \Normalizer::FORM_C)) {
108
-			$otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_D);
109
-		} else {
110
-			$otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_C);
111
-		}
112
-		$otherFullPath = $basePath . $otherFormPath;
113
-		if ($this->storage->file_exists($otherFullPath)) {
114
-			$this->namesCache[$fullPath] = $otherFullPath;
115
-			return $otherFullPath;
116
-		}
117
-
118
-		// return original path, file did not exist at all
119
-		$this->namesCache[$fullPath] = $fullPath;
120
-		return null;
121
-	}
122
-
123
-	/**
124
-	 * see http://php.net/manual/en/function.mkdir.php
125
-	 *
126
-	 * @param string $path
127
-	 * @return bool
128
-	 */
129
-	public function mkdir($path) {
130
-		// note: no conversion here, method should not be called with non-NFC names!
131
-		$result = $this->storage->mkdir($path);
132
-		if ($result) {
133
-			$this->namesCache[$path] = $path;
134
-		}
135
-		return $result;
136
-	}
137
-
138
-	/**
139
-	 * see http://php.net/manual/en/function.rmdir.php
140
-	 *
141
-	 * @param string $path
142
-	 * @return bool
143
-	 */
144
-	public function rmdir($path) {
145
-		$result = $this->storage->rmdir($this->findPathToUse($path));
146
-		if ($result) {
147
-			unset($this->namesCache[$path]);
148
-		}
149
-		return $result;
150
-	}
151
-
152
-	/**
153
-	 * see http://php.net/manual/en/function.opendir.php
154
-	 *
155
-	 * @param string $path
156
-	 * @return resource
157
-	 */
158
-	public function opendir($path) {
159
-		return $this->storage->opendir($this->findPathToUse($path));
160
-	}
161
-
162
-	/**
163
-	 * see http://php.net/manual/en/function.is_dir.php
164
-	 *
165
-	 * @param string $path
166
-	 * @return bool
167
-	 */
168
-	public function is_dir($path) {
169
-		return $this->storage->is_dir($this->findPathToUse($path));
170
-	}
171
-
172
-	/**
173
-	 * see http://php.net/manual/en/function.is_file.php
174
-	 *
175
-	 * @param string $path
176
-	 * @return bool
177
-	 */
178
-	public function is_file($path) {
179
-		return $this->storage->is_file($this->findPathToUse($path));
180
-	}
181
-
182
-	/**
183
-	 * see http://php.net/manual/en/function.stat.php
184
-	 * only the following keys are required in the result: size and mtime
185
-	 *
186
-	 * @param string $path
187
-	 * @return array
188
-	 */
189
-	public function stat($path) {
190
-		return $this->storage->stat($this->findPathToUse($path));
191
-	}
192
-
193
-	/**
194
-	 * see http://php.net/manual/en/function.filetype.php
195
-	 *
196
-	 * @param string $path
197
-	 * @return bool
198
-	 */
199
-	public function filetype($path) {
200
-		return $this->storage->filetype($this->findPathToUse($path));
201
-	}
202
-
203
-	/**
204
-	 * see http://php.net/manual/en/function.filesize.php
205
-	 * The result for filesize when called on a folder is required to be 0
206
-	 *
207
-	 * @param string $path
208
-	 * @return int
209
-	 */
210
-	public function filesize($path) {
211
-		return $this->storage->filesize($this->findPathToUse($path));
212
-	}
213
-
214
-	/**
215
-	 * check if a file can be created in $path
216
-	 *
217
-	 * @param string $path
218
-	 * @return bool
219
-	 */
220
-	public function isCreatable($path) {
221
-		return $this->storage->isCreatable($this->findPathToUse($path));
222
-	}
223
-
224
-	/**
225
-	 * check if a file can be read
226
-	 *
227
-	 * @param string $path
228
-	 * @return bool
229
-	 */
230
-	public function isReadable($path) {
231
-		return $this->storage->isReadable($this->findPathToUse($path));
232
-	}
233
-
234
-	/**
235
-	 * check if a file can be written to
236
-	 *
237
-	 * @param string $path
238
-	 * @return bool
239
-	 */
240
-	public function isUpdatable($path) {
241
-		return $this->storage->isUpdatable($this->findPathToUse($path));
242
-	}
243
-
244
-	/**
245
-	 * check if a file can be deleted
246
-	 *
247
-	 * @param string $path
248
-	 * @return bool
249
-	 */
250
-	public function isDeletable($path) {
251
-		return $this->storage->isDeletable($this->findPathToUse($path));
252
-	}
253
-
254
-	/**
255
-	 * check if a file can be shared
256
-	 *
257
-	 * @param string $path
258
-	 * @return bool
259
-	 */
260
-	public function isSharable($path) {
261
-		return $this->storage->isSharable($this->findPathToUse($path));
262
-	}
263
-
264
-	/**
265
-	 * get the full permissions of a path.
266
-	 * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php
267
-	 *
268
-	 * @param string $path
269
-	 * @return int
270
-	 */
271
-	public function getPermissions($path) {
272
-		return $this->storage->getPermissions($this->findPathToUse($path));
273
-	}
274
-
275
-	/**
276
-	 * see http://php.net/manual/en/function.file_exists.php
277
-	 *
278
-	 * @param string $path
279
-	 * @return bool
280
-	 */
281
-	public function file_exists($path) {
282
-		return $this->storage->file_exists($this->findPathToUse($path));
283
-	}
284
-
285
-	/**
286
-	 * see http://php.net/manual/en/function.filemtime.php
287
-	 *
288
-	 * @param string $path
289
-	 * @return int
290
-	 */
291
-	public function filemtime($path) {
292
-		return $this->storage->filemtime($this->findPathToUse($path));
293
-	}
294
-
295
-	/**
296
-	 * see http://php.net/manual/en/function.file_get_contents.php
297
-	 *
298
-	 * @param string $path
299
-	 * @return string
300
-	 */
301
-	public function file_get_contents($path) {
302
-		return $this->storage->file_get_contents($this->findPathToUse($path));
303
-	}
304
-
305
-	/**
306
-	 * see http://php.net/manual/en/function.file_put_contents.php
307
-	 *
308
-	 * @param string $path
309
-	 * @param string $data
310
-	 * @return bool
311
-	 */
312
-	public function file_put_contents($path, $data) {
313
-		return $this->storage->file_put_contents($this->findPathToUse($path), $data);
314
-	}
315
-
316
-	/**
317
-	 * see http://php.net/manual/en/function.unlink.php
318
-	 *
319
-	 * @param string $path
320
-	 * @return bool
321
-	 */
322
-	public function unlink($path) {
323
-		$result = $this->storage->unlink($this->findPathToUse($path));
324
-		if ($result) {
325
-			unset($this->namesCache[$path]);
326
-		}
327
-		return $result;
328
-	}
329
-
330
-	/**
331
-	 * see http://php.net/manual/en/function.rename.php
332
-	 *
333
-	 * @param string $path1
334
-	 * @param string $path2
335
-	 * @return bool
336
-	 */
337
-	public function rename($path1, $path2) {
338
-		// second name always NFC
339
-		return $this->storage->rename($this->findPathToUse($path1), $this->findPathToUse($path2));
340
-	}
341
-
342
-	/**
343
-	 * see http://php.net/manual/en/function.copy.php
344
-	 *
345
-	 * @param string $path1
346
-	 * @param string $path2
347
-	 * @return bool
348
-	 */
349
-	public function copy($path1, $path2) {
350
-		return $this->storage->copy($this->findPathToUse($path1), $this->findPathToUse($path2));
351
-	}
352
-
353
-	/**
354
-	 * see http://php.net/manual/en/function.fopen.php
355
-	 *
356
-	 * @param string $path
357
-	 * @param string $mode
358
-	 * @return resource
359
-	 */
360
-	public function fopen($path, $mode) {
361
-		$result = $this->storage->fopen($this->findPathToUse($path), $mode);
362
-		if ($result && $mode !== 'r' && $mode !== 'rb') {
363
-			unset($this->namesCache[$path]);
364
-		}
365
-		return $result;
366
-	}
367
-
368
-	/**
369
-	 * get the mimetype for a file or folder
370
-	 * The mimetype for a folder is required to be "httpd/unix-directory"
371
-	 *
372
-	 * @param string $path
373
-	 * @return string
374
-	 */
375
-	public function getMimeType($path) {
376
-		return $this->storage->getMimeType($this->findPathToUse($path));
377
-	}
378
-
379
-	/**
380
-	 * see http://php.net/manual/en/function.hash.php
381
-	 *
382
-	 * @param string $type
383
-	 * @param string $path
384
-	 * @param bool $raw
385
-	 * @return string
386
-	 */
387
-	public function hash($type, $path, $raw = false) {
388
-		return $this->storage->hash($type, $this->findPathToUse($path), $raw);
389
-	}
390
-
391
-	/**
392
-	 * see http://php.net/manual/en/function.free_space.php
393
-	 *
394
-	 * @param string $path
395
-	 * @return int
396
-	 */
397
-	public function free_space($path) {
398
-		return $this->storage->free_space($this->findPathToUse($path));
399
-	}
400
-
401
-	/**
402
-	 * search for occurrences of $query in file names
403
-	 *
404
-	 * @param string $query
405
-	 * @return array
406
-	 */
407
-	public function search($query) {
408
-		return $this->storage->search($query);
409
-	}
410
-
411
-	/**
412
-	 * see http://php.net/manual/en/function.touch.php
413
-	 * If the backend does not support the operation, false should be returned
414
-	 *
415
-	 * @param string $path
416
-	 * @param int $mtime
417
-	 * @return bool
418
-	 */
419
-	public function touch($path, $mtime = null) {
420
-		return $this->storage->touch($this->findPathToUse($path), $mtime);
421
-	}
422
-
423
-	/**
424
-	 * get the path to a local version of the file.
425
-	 * The local version of the file can be temporary and doesn't have to be persistent across requests
426
-	 *
427
-	 * @param string $path
428
-	 * @return string
429
-	 */
430
-	public function getLocalFile($path) {
431
-		return $this->storage->getLocalFile($this->findPathToUse($path));
432
-	}
433
-
434
-	/**
435
-	 * check if a file or folder has been updated since $time
436
-	 *
437
-	 * @param string $path
438
-	 * @param int $time
439
-	 * @return bool
440
-	 *
441
-	 * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed.
442
-	 * returning true for other changes in the folder is optional
443
-	 */
444
-	public function hasUpdated($path, $time) {
445
-		return $this->storage->hasUpdated($this->findPathToUse($path), $time);
446
-	}
447
-
448
-	/**
449
-	 * get a cache instance for the storage
450
-	 *
451
-	 * @param string $path
452
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
453
-	 * @return \OC\Files\Cache\Cache
454
-	 */
455
-	public function getCache($path = '', $storage = null) {
456
-		if (!$storage) {
457
-			$storage = $this;
458
-		}
459
-		return $this->storage->getCache($this->findPathToUse($path), $storage);
460
-	}
461
-
462
-	/**
463
-	 * get a scanner instance for the storage
464
-	 *
465
-	 * @param string $path
466
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
467
-	 * @return \OC\Files\Cache\Scanner
468
-	 */
469
-	public function getScanner($path = '', $storage = null) {
470
-		if (!$storage) {
471
-			$storage = $this;
472
-		}
473
-		return $this->storage->getScanner($this->findPathToUse($path), $storage);
474
-	}
475
-
476
-	/**
477
-	 * get the ETag for a file or folder
478
-	 *
479
-	 * @param string $path
480
-	 * @return string
481
-	 */
482
-	public function getETag($path) {
483
-		return $this->storage->getETag($this->findPathToUse($path));
484
-	}
485
-
486
-	/**
487
-	 * @param IStorage $sourceStorage
488
-	 * @param string $sourceInternalPath
489
-	 * @param string $targetInternalPath
490
-	 * @return bool
491
-	 */
492
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
493
-		if ($sourceStorage === $this) {
494
-			return $this->copy($sourceInternalPath, $this->findPathToUse($targetInternalPath));
495
-		}
496
-
497
-		$result = $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $this->findPathToUse($targetInternalPath));
498
-		if ($result) {
499
-			unset($this->namesCache[$targetInternalPath]);
500
-		}
501
-		return $result;
502
-	}
503
-
504
-	/**
505
-	 * @param IStorage $sourceStorage
506
-	 * @param string $sourceInternalPath
507
-	 * @param string $targetInternalPath
508
-	 * @return bool
509
-	 */
510
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
511
-		if ($sourceStorage === $this) {
512
-			$result = $this->rename($sourceInternalPath, $this->findPathToUse($targetInternalPath));
513
-			if ($result) {
514
-				unset($this->namesCache[$sourceInternalPath]);
515
-				unset($this->namesCache[$targetInternalPath]);
516
-			}
517
-			return $result;
518
-		}
519
-
520
-		$result = $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $this->findPathToUse($targetInternalPath));
521
-		if ($result) {
522
-			unset($this->namesCache[$sourceInternalPath]);
523
-			unset($this->namesCache[$targetInternalPath]);
524
-		}
525
-		return $result;
526
-	}
527
-
528
-	/**
529
-	 * @param string $path
530
-	 * @return array
531
-	 */
532
-	public function getMetaData($path) {
533
-		return $this->storage->getMetaData($this->findPathToUse($path));
534
-	}
37
+    /**
38
+     * @var ICache
39
+     */
40
+    private $namesCache;
41
+
42
+    /**
43
+     * @param array $parameters
44
+     */
45
+    public function __construct($parameters) {
46
+        $this->storage = $parameters['storage'];
47
+        $this->namesCache = new CappedMemoryCache();
48
+    }
49
+
50
+    /**
51
+     * Returns whether the given string is only made of ASCII characters
52
+     *
53
+     * @param string $str string
54
+     *
55
+     * @return bool true if the string is all ASCII, false otherwise
56
+     */
57
+    private function isAscii($str) {
58
+        return (bool) !preg_match('/[\\x80-\\xff]+/', $str);
59
+    }
60
+
61
+    /**
62
+     * Checks whether the given path exists in NFC or NFD form after checking
63
+     * each form for each path section and returns the correct form.
64
+     * If no existing path found, returns the path as it was given.
65
+     *
66
+     * @param string $fullPath path to check
67
+     *
68
+     * @return string original or converted path
69
+     */
70
+    private function findPathToUse($fullPath) {
71
+        $cachedPath = $this->namesCache[$fullPath];
72
+        if ($cachedPath !== null) {
73
+            return $cachedPath;
74
+        }
75
+
76
+        $sections = explode('/', $fullPath);
77
+        $path = '';
78
+        foreach ($sections as $section) {
79
+            $convertedPath = $this->findPathToUseLastSection($path, $section);
80
+            if ($convertedPath === null) {
81
+                // no point in continuing if the section was not found, use original path
82
+                return $fullPath;
83
+            }
84
+            $path = $convertedPath . '/';
85
+        }
86
+        $path = rtrim($path, '/');
87
+        return $path;
88
+    }
89
+
90
+    /**
91
+     * Checks whether the last path section of the given path exists in NFC or NFD form
92
+     * and returns the correct form. If no existing path found, returns null.
93
+     *
94
+     * @param string $basePath base path to check
95
+     * @param string $lastSection last section of the path to check for NFD/NFC variations
96
+     *
97
+     * @return string|null original or converted path, or null if none of the forms was found
98
+     */
99
+    private function findPathToUseLastSection($basePath, $lastSection) {
100
+        $fullPath = $basePath . $lastSection;
101
+        if ($lastSection === '' || $this->isAscii($lastSection) || $this->storage->file_exists($fullPath)) {
102
+            $this->namesCache[$fullPath] = $fullPath;
103
+            return $fullPath;
104
+        }
105
+
106
+        // swap encoding
107
+        if (\Normalizer::isNormalized($lastSection, \Normalizer::FORM_C)) {
108
+            $otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_D);
109
+        } else {
110
+            $otherFormPath = \Normalizer::normalize($lastSection, \Normalizer::FORM_C);
111
+        }
112
+        $otherFullPath = $basePath . $otherFormPath;
113
+        if ($this->storage->file_exists($otherFullPath)) {
114
+            $this->namesCache[$fullPath] = $otherFullPath;
115
+            return $otherFullPath;
116
+        }
117
+
118
+        // return original path, file did not exist at all
119
+        $this->namesCache[$fullPath] = $fullPath;
120
+        return null;
121
+    }
122
+
123
+    /**
124
+     * see http://php.net/manual/en/function.mkdir.php
125
+     *
126
+     * @param string $path
127
+     * @return bool
128
+     */
129
+    public function mkdir($path) {
130
+        // note: no conversion here, method should not be called with non-NFC names!
131
+        $result = $this->storage->mkdir($path);
132
+        if ($result) {
133
+            $this->namesCache[$path] = $path;
134
+        }
135
+        return $result;
136
+    }
137
+
138
+    /**
139
+     * see http://php.net/manual/en/function.rmdir.php
140
+     *
141
+     * @param string $path
142
+     * @return bool
143
+     */
144
+    public function rmdir($path) {
145
+        $result = $this->storage->rmdir($this->findPathToUse($path));
146
+        if ($result) {
147
+            unset($this->namesCache[$path]);
148
+        }
149
+        return $result;
150
+    }
151
+
152
+    /**
153
+     * see http://php.net/manual/en/function.opendir.php
154
+     *
155
+     * @param string $path
156
+     * @return resource
157
+     */
158
+    public function opendir($path) {
159
+        return $this->storage->opendir($this->findPathToUse($path));
160
+    }
161
+
162
+    /**
163
+     * see http://php.net/manual/en/function.is_dir.php
164
+     *
165
+     * @param string $path
166
+     * @return bool
167
+     */
168
+    public function is_dir($path) {
169
+        return $this->storage->is_dir($this->findPathToUse($path));
170
+    }
171
+
172
+    /**
173
+     * see http://php.net/manual/en/function.is_file.php
174
+     *
175
+     * @param string $path
176
+     * @return bool
177
+     */
178
+    public function is_file($path) {
179
+        return $this->storage->is_file($this->findPathToUse($path));
180
+    }
181
+
182
+    /**
183
+     * see http://php.net/manual/en/function.stat.php
184
+     * only the following keys are required in the result: size and mtime
185
+     *
186
+     * @param string $path
187
+     * @return array
188
+     */
189
+    public function stat($path) {
190
+        return $this->storage->stat($this->findPathToUse($path));
191
+    }
192
+
193
+    /**
194
+     * see http://php.net/manual/en/function.filetype.php
195
+     *
196
+     * @param string $path
197
+     * @return bool
198
+     */
199
+    public function filetype($path) {
200
+        return $this->storage->filetype($this->findPathToUse($path));
201
+    }
202
+
203
+    /**
204
+     * see http://php.net/manual/en/function.filesize.php
205
+     * The result for filesize when called on a folder is required to be 0
206
+     *
207
+     * @param string $path
208
+     * @return int
209
+     */
210
+    public function filesize($path) {
211
+        return $this->storage->filesize($this->findPathToUse($path));
212
+    }
213
+
214
+    /**
215
+     * check if a file can be created in $path
216
+     *
217
+     * @param string $path
218
+     * @return bool
219
+     */
220
+    public function isCreatable($path) {
221
+        return $this->storage->isCreatable($this->findPathToUse($path));
222
+    }
223
+
224
+    /**
225
+     * check if a file can be read
226
+     *
227
+     * @param string $path
228
+     * @return bool
229
+     */
230
+    public function isReadable($path) {
231
+        return $this->storage->isReadable($this->findPathToUse($path));
232
+    }
233
+
234
+    /**
235
+     * check if a file can be written to
236
+     *
237
+     * @param string $path
238
+     * @return bool
239
+     */
240
+    public function isUpdatable($path) {
241
+        return $this->storage->isUpdatable($this->findPathToUse($path));
242
+    }
243
+
244
+    /**
245
+     * check if a file can be deleted
246
+     *
247
+     * @param string $path
248
+     * @return bool
249
+     */
250
+    public function isDeletable($path) {
251
+        return $this->storage->isDeletable($this->findPathToUse($path));
252
+    }
253
+
254
+    /**
255
+     * check if a file can be shared
256
+     *
257
+     * @param string $path
258
+     * @return bool
259
+     */
260
+    public function isSharable($path) {
261
+        return $this->storage->isSharable($this->findPathToUse($path));
262
+    }
263
+
264
+    /**
265
+     * get the full permissions of a path.
266
+     * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php
267
+     *
268
+     * @param string $path
269
+     * @return int
270
+     */
271
+    public function getPermissions($path) {
272
+        return $this->storage->getPermissions($this->findPathToUse($path));
273
+    }
274
+
275
+    /**
276
+     * see http://php.net/manual/en/function.file_exists.php
277
+     *
278
+     * @param string $path
279
+     * @return bool
280
+     */
281
+    public function file_exists($path) {
282
+        return $this->storage->file_exists($this->findPathToUse($path));
283
+    }
284
+
285
+    /**
286
+     * see http://php.net/manual/en/function.filemtime.php
287
+     *
288
+     * @param string $path
289
+     * @return int
290
+     */
291
+    public function filemtime($path) {
292
+        return $this->storage->filemtime($this->findPathToUse($path));
293
+    }
294
+
295
+    /**
296
+     * see http://php.net/manual/en/function.file_get_contents.php
297
+     *
298
+     * @param string $path
299
+     * @return string
300
+     */
301
+    public function file_get_contents($path) {
302
+        return $this->storage->file_get_contents($this->findPathToUse($path));
303
+    }
304
+
305
+    /**
306
+     * see http://php.net/manual/en/function.file_put_contents.php
307
+     *
308
+     * @param string $path
309
+     * @param string $data
310
+     * @return bool
311
+     */
312
+    public function file_put_contents($path, $data) {
313
+        return $this->storage->file_put_contents($this->findPathToUse($path), $data);
314
+    }
315
+
316
+    /**
317
+     * see http://php.net/manual/en/function.unlink.php
318
+     *
319
+     * @param string $path
320
+     * @return bool
321
+     */
322
+    public function unlink($path) {
323
+        $result = $this->storage->unlink($this->findPathToUse($path));
324
+        if ($result) {
325
+            unset($this->namesCache[$path]);
326
+        }
327
+        return $result;
328
+    }
329
+
330
+    /**
331
+     * see http://php.net/manual/en/function.rename.php
332
+     *
333
+     * @param string $path1
334
+     * @param string $path2
335
+     * @return bool
336
+     */
337
+    public function rename($path1, $path2) {
338
+        // second name always NFC
339
+        return $this->storage->rename($this->findPathToUse($path1), $this->findPathToUse($path2));
340
+    }
341
+
342
+    /**
343
+     * see http://php.net/manual/en/function.copy.php
344
+     *
345
+     * @param string $path1
346
+     * @param string $path2
347
+     * @return bool
348
+     */
349
+    public function copy($path1, $path2) {
350
+        return $this->storage->copy($this->findPathToUse($path1), $this->findPathToUse($path2));
351
+    }
352
+
353
+    /**
354
+     * see http://php.net/manual/en/function.fopen.php
355
+     *
356
+     * @param string $path
357
+     * @param string $mode
358
+     * @return resource
359
+     */
360
+    public function fopen($path, $mode) {
361
+        $result = $this->storage->fopen($this->findPathToUse($path), $mode);
362
+        if ($result && $mode !== 'r' && $mode !== 'rb') {
363
+            unset($this->namesCache[$path]);
364
+        }
365
+        return $result;
366
+    }
367
+
368
+    /**
369
+     * get the mimetype for a file or folder
370
+     * The mimetype for a folder is required to be "httpd/unix-directory"
371
+     *
372
+     * @param string $path
373
+     * @return string
374
+     */
375
+    public function getMimeType($path) {
376
+        return $this->storage->getMimeType($this->findPathToUse($path));
377
+    }
378
+
379
+    /**
380
+     * see http://php.net/manual/en/function.hash.php
381
+     *
382
+     * @param string $type
383
+     * @param string $path
384
+     * @param bool $raw
385
+     * @return string
386
+     */
387
+    public function hash($type, $path, $raw = false) {
388
+        return $this->storage->hash($type, $this->findPathToUse($path), $raw);
389
+    }
390
+
391
+    /**
392
+     * see http://php.net/manual/en/function.free_space.php
393
+     *
394
+     * @param string $path
395
+     * @return int
396
+     */
397
+    public function free_space($path) {
398
+        return $this->storage->free_space($this->findPathToUse($path));
399
+    }
400
+
401
+    /**
402
+     * search for occurrences of $query in file names
403
+     *
404
+     * @param string $query
405
+     * @return array
406
+     */
407
+    public function search($query) {
408
+        return $this->storage->search($query);
409
+    }
410
+
411
+    /**
412
+     * see http://php.net/manual/en/function.touch.php
413
+     * If the backend does not support the operation, false should be returned
414
+     *
415
+     * @param string $path
416
+     * @param int $mtime
417
+     * @return bool
418
+     */
419
+    public function touch($path, $mtime = null) {
420
+        return $this->storage->touch($this->findPathToUse($path), $mtime);
421
+    }
422
+
423
+    /**
424
+     * get the path to a local version of the file.
425
+     * The local version of the file can be temporary and doesn't have to be persistent across requests
426
+     *
427
+     * @param string $path
428
+     * @return string
429
+     */
430
+    public function getLocalFile($path) {
431
+        return $this->storage->getLocalFile($this->findPathToUse($path));
432
+    }
433
+
434
+    /**
435
+     * check if a file or folder has been updated since $time
436
+     *
437
+     * @param string $path
438
+     * @param int $time
439
+     * @return bool
440
+     *
441
+     * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed.
442
+     * returning true for other changes in the folder is optional
443
+     */
444
+    public function hasUpdated($path, $time) {
445
+        return $this->storage->hasUpdated($this->findPathToUse($path), $time);
446
+    }
447
+
448
+    /**
449
+     * get a cache instance for the storage
450
+     *
451
+     * @param string $path
452
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
453
+     * @return \OC\Files\Cache\Cache
454
+     */
455
+    public function getCache($path = '', $storage = null) {
456
+        if (!$storage) {
457
+            $storage = $this;
458
+        }
459
+        return $this->storage->getCache($this->findPathToUse($path), $storage);
460
+    }
461
+
462
+    /**
463
+     * get a scanner instance for the storage
464
+     *
465
+     * @param string $path
466
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
467
+     * @return \OC\Files\Cache\Scanner
468
+     */
469
+    public function getScanner($path = '', $storage = null) {
470
+        if (!$storage) {
471
+            $storage = $this;
472
+        }
473
+        return $this->storage->getScanner($this->findPathToUse($path), $storage);
474
+    }
475
+
476
+    /**
477
+     * get the ETag for a file or folder
478
+     *
479
+     * @param string $path
480
+     * @return string
481
+     */
482
+    public function getETag($path) {
483
+        return $this->storage->getETag($this->findPathToUse($path));
484
+    }
485
+
486
+    /**
487
+     * @param IStorage $sourceStorage
488
+     * @param string $sourceInternalPath
489
+     * @param string $targetInternalPath
490
+     * @return bool
491
+     */
492
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
493
+        if ($sourceStorage === $this) {
494
+            return $this->copy($sourceInternalPath, $this->findPathToUse($targetInternalPath));
495
+        }
496
+
497
+        $result = $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $this->findPathToUse($targetInternalPath));
498
+        if ($result) {
499
+            unset($this->namesCache[$targetInternalPath]);
500
+        }
501
+        return $result;
502
+    }
503
+
504
+    /**
505
+     * @param IStorage $sourceStorage
506
+     * @param string $sourceInternalPath
507
+     * @param string $targetInternalPath
508
+     * @return bool
509
+     */
510
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
511
+        if ($sourceStorage === $this) {
512
+            $result = $this->rename($sourceInternalPath, $this->findPathToUse($targetInternalPath));
513
+            if ($result) {
514
+                unset($this->namesCache[$sourceInternalPath]);
515
+                unset($this->namesCache[$targetInternalPath]);
516
+            }
517
+            return $result;
518
+        }
519
+
520
+        $result = $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $this->findPathToUse($targetInternalPath));
521
+        if ($result) {
522
+            unset($this->namesCache[$sourceInternalPath]);
523
+            unset($this->namesCache[$targetInternalPath]);
524
+        }
525
+        return $result;
526
+    }
527
+
528
+    /**
529
+     * @param string $path
530
+     * @return array
531
+     */
532
+    public function getMetaData($path) {
533
+        return $this->storage->getMetaData($this->findPathToUse($path));
534
+    }
535 535
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/Home.php 2 patches
Indentation   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -31,78 +31,78 @@
 block discarded – undo
31 31
  * Specialized version of Local storage for home directory usage
32 32
  */
33 33
 class Home extends Local implements \OCP\Files\IHomeStorage {
34
-	/**
35
-	 * @var string
36
-	 */
37
-	protected $id;
34
+    /**
35
+     * @var string
36
+     */
37
+    protected $id;
38 38
 
39
-	/**
40
-	 * @var \OC\User\User $user
41
-	 */
42
-	protected $user;
39
+    /**
40
+     * @var \OC\User\User $user
41
+     */
42
+    protected $user;
43 43
 
44
-	/**
45
-	 * Construct a Home storage instance
46
-	 * @param array $arguments array with "user" containing the
47
-	 * storage owner
48
-	 */
49
-	public function __construct($arguments) {
50
-		$this->user = $arguments['user'];
51
-		$datadir = $this->user->getHome();
52
-		$this->id = 'home::' . $this->user->getUID();
44
+    /**
45
+     * Construct a Home storage instance
46
+     * @param array $arguments array with "user" containing the
47
+     * storage owner
48
+     */
49
+    public function __construct($arguments) {
50
+        $this->user = $arguments['user'];
51
+        $datadir = $this->user->getHome();
52
+        $this->id = 'home::' . $this->user->getUID();
53 53
 
54
-		parent::__construct(array('datadir' => $datadir));
55
-	}
54
+        parent::__construct(array('datadir' => $datadir));
55
+    }
56 56
 
57
-	public function getId() {
58
-		return $this->id;
59
-	}
57
+    public function getId() {
58
+        return $this->id;
59
+    }
60 60
 
61
-	/**
62
-	 * @return \OC\Files\Cache\HomeCache
63
-	 */
64
-	public function getCache($path = '', $storage = null) {
65
-		if (!$storage) {
66
-			$storage = $this;
67
-		}
68
-		if (!isset($this->cache)) {
69
-			$this->cache = new \OC\Files\Cache\HomeCache($storage);
70
-		}
71
-		return $this->cache;
72
-	}
61
+    /**
62
+     * @return \OC\Files\Cache\HomeCache
63
+     */
64
+    public function getCache($path = '', $storage = null) {
65
+        if (!$storage) {
66
+            $storage = $this;
67
+        }
68
+        if (!isset($this->cache)) {
69
+            $this->cache = new \OC\Files\Cache\HomeCache($storage);
70
+        }
71
+        return $this->cache;
72
+    }
73 73
 
74
-	/**
75
-	 * get a propagator instance for the cache
76
-	 *
77
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
78
-	 * @return \OC\Files\Cache\Propagator
79
-	 */
80
-	public function getPropagator($storage = null) {
81
-		if (!$storage) {
82
-			$storage = $this;
83
-		}
84
-		if (!isset($this->propagator)) {
85
-			$this->propagator = new HomePropagator($storage, \OC::$server->getDatabaseConnection());
86
-		}
87
-		return $this->propagator;
88
-	}
74
+    /**
75
+     * get a propagator instance for the cache
76
+     *
77
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
78
+     * @return \OC\Files\Cache\Propagator
79
+     */
80
+    public function getPropagator($storage = null) {
81
+        if (!$storage) {
82
+            $storage = $this;
83
+        }
84
+        if (!isset($this->propagator)) {
85
+            $this->propagator = new HomePropagator($storage, \OC::$server->getDatabaseConnection());
86
+        }
87
+        return $this->propagator;
88
+    }
89 89
 
90 90
 
91
-	/**
92
-	 * Returns the owner of this home storage
93
-	 * @return \OC\User\User owner of this home storage
94
-	 */
95
-	public function getUser() {
96
-		return $this->user;
97
-	}
91
+    /**
92
+     * Returns the owner of this home storage
93
+     * @return \OC\User\User owner of this home storage
94
+     */
95
+    public function getUser() {
96
+        return $this->user;
97
+    }
98 98
 
99
-	/**
100
-	 * get the owner of a path
101
-	 *
102
-	 * @param string $path The path to get the owner
103
-	 * @return string uid or false
104
-	 */
105
-	public function getOwner($path) {
106
-		return $this->user->getUID();
107
-	}
99
+    /**
100
+     * get the owner of a path
101
+     *
102
+     * @param string $path The path to get the owner
103
+     * @return string uid or false
104
+     */
105
+    public function getOwner($path) {
106
+        return $this->user->getUID();
107
+    }
108 108
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -49,7 +49,7 @@
 block discarded – undo
49 49
 	public function __construct($arguments) {
50 50
 		$this->user = $arguments['user'];
51 51
 		$datadir = $this->user->getHome();
52
-		$this->id = 'home::' . $this->user->getUID();
52
+		$this->id = 'home::'.$this->user->getUID();
53 53
 
54 54
 		parent::__construct(array('datadir' => $datadir));
55 55
 	}
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 4 patches
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,8 +90,11 @@
 block discarded – undo
90 90
 		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91 91
 			$host = $params['host'];
92 92
 			//remove leading http[s], will be generated in createBaseUri()
93
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
93
+			if (substr($host, 0, 8) == "https://") {
94
+			    $host = substr($host, 8);
95
+			} else if (substr($host, 0, 7) == "http://") {
96
+			    $host = substr($host, 7);
97
+			}
95 98
 			$this->host = $host;
96 99
 			$this->user = $params['user'];
97 100
 			$this->password = $params['password'];
Please login to merge, or discard this patch.
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -598,6 +598,9 @@
 block discarded – undo
598 598
 		}
599 599
 	}
600 600
 
601
+	/**
602
+	 * @param string $path
603
+	 */
601 604
 	public function getMimeTypeFromRemote($path) {
602 605
 		try {
603 606
 			$response = $this->propfind($path);
Please login to merge, or discard this patch.
Indentation   +801 added lines, -801 removed lines patch added patch discarded remove patch
@@ -57,806 +57,806 @@
 block discarded – undo
57 57
  * @package OC\Files\Storage
58 58
  */
59 59
 class DAV extends Common {
60
-	/** @var string */
61
-	protected $password;
62
-	/** @var string */
63
-	protected $user;
64
-	/** @var string */
65
-	protected $authType;
66
-	/** @var string */
67
-	protected $host;
68
-	/** @var bool */
69
-	protected $secure;
70
-	/** @var string */
71
-	protected $root;
72
-	/** @var string */
73
-	protected $certPath;
74
-	/** @var bool */
75
-	protected $ready;
76
-	/** @var Client */
77
-	protected $client;
78
-	/** @var ArrayCache */
79
-	protected $statCache;
80
-	/** @var \OCP\Http\Client\IClientService */
81
-	protected $httpClientService;
82
-
83
-	/**
84
-	 * @param array $params
85
-	 * @throws \Exception
86
-	 */
87
-	public function __construct($params) {
88
-		$this->statCache = new ArrayCache();
89
-		$this->httpClientService = \OC::$server->getHTTPClientService();
90
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91
-			$host = $params['host'];
92
-			//remove leading http[s], will be generated in createBaseUri()
93
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
95
-			$this->host = $host;
96
-			$this->user = $params['user'];
97
-			$this->password = $params['password'];
98
-			if (isset($params['authType'])) {
99
-				$this->authType = $params['authType'];
100
-			}
101
-			if (isset($params['secure'])) {
102
-				if (is_string($params['secure'])) {
103
-					$this->secure = ($params['secure'] === 'true');
104
-				} else {
105
-					$this->secure = (bool)$params['secure'];
106
-				}
107
-			} else {
108
-				$this->secure = false;
109
-			}
110
-			if ($this->secure === true) {
111
-				// inject mock for testing
112
-				$certManager = \OC::$server->getCertificateManager();
113
-				if (is_null($certManager)) { //no user
114
-					$certManager = \OC::$server->getCertificateManager(null);
115
-				}
116
-				$certPath = $certManager->getAbsoluteBundlePath();
117
-				if (file_exists($certPath)) {
118
-					$this->certPath = $certPath;
119
-				}
120
-			}
121
-			$this->root = isset($params['root']) ? $params['root'] : '/';
122
-			if (!$this->root || $this->root[0] != '/') {
123
-				$this->root = '/' . $this->root;
124
-			}
125
-			if (substr($this->root, -1, 1) != '/') {
126
-				$this->root .= '/';
127
-			}
128
-		} else {
129
-			throw new \Exception('Invalid webdav storage configuration');
130
-		}
131
-	}
132
-
133
-	protected function init() {
134
-		if ($this->ready) {
135
-			return;
136
-		}
137
-		$this->ready = true;
138
-
139
-		$settings = [
140
-			'baseUri' => $this->createBaseUri(),
141
-			'userName' => $this->user,
142
-			'password' => $this->password,
143
-		];
144
-		if (isset($this->authType)) {
145
-			$settings['authType'] = $this->authType;
146
-		}
147
-
148
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
149
-		if ($proxy !== '') {
150
-			$settings['proxy'] = $proxy;
151
-		}
152
-
153
-		$this->client = new Client($settings);
154
-		$this->client->setThrowExceptions(true);
155
-		if ($this->secure === true && $this->certPath) {
156
-			$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
157
-		}
158
-	}
159
-
160
-	/**
161
-	 * Clear the stat cache
162
-	 */
163
-	public function clearStatCache() {
164
-		$this->statCache->clear();
165
-	}
166
-
167
-	/** {@inheritdoc} */
168
-	public function getId() {
169
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
170
-	}
171
-
172
-	/** {@inheritdoc} */
173
-	public function createBaseUri() {
174
-		$baseUri = 'http';
175
-		if ($this->secure) {
176
-			$baseUri .= 's';
177
-		}
178
-		$baseUri .= '://' . $this->host . $this->root;
179
-		return $baseUri;
180
-	}
181
-
182
-	/** {@inheritdoc} */
183
-	public function mkdir($path) {
184
-		$this->init();
185
-		$path = $this->cleanPath($path);
186
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
187
-		if ($result) {
188
-			$this->statCache->set($path, true);
189
-		}
190
-		return $result;
191
-	}
192
-
193
-	/** {@inheritdoc} */
194
-	public function rmdir($path) {
195
-		$this->init();
196
-		$path = $this->cleanPath($path);
197
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
198
-		// a non-empty folder
199
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
200
-		$this->statCache->clear($path . '/');
201
-		$this->statCache->remove($path);
202
-		return $result;
203
-	}
204
-
205
-	/** {@inheritdoc} */
206
-	public function opendir($path) {
207
-		$this->init();
208
-		$path = $this->cleanPath($path);
209
-		try {
210
-			$response = $this->client->propFind(
211
-				$this->encodePath($path),
212
-				['{DAV:}href'],
213
-				1
214
-			);
215
-			if ($response === false) {
216
-				return false;
217
-			}
218
-			$content = [];
219
-			$files = array_keys($response);
220
-			array_shift($files); //the first entry is the current directory
221
-
222
-			if (!$this->statCache->hasKey($path)) {
223
-				$this->statCache->set($path, true);
224
-			}
225
-			foreach ($files as $file) {
226
-				$file = urldecode($file);
227
-				// do not store the real entry, we might not have all properties
228
-				if (!$this->statCache->hasKey($path)) {
229
-					$this->statCache->set($file, true);
230
-				}
231
-				$file = basename($file);
232
-				$content[] = $file;
233
-			}
234
-			return IteratorDirectory::wrap($content);
235
-		} catch (\Exception $e) {
236
-			$this->convertException($e, $path);
237
-		}
238
-		return false;
239
-	}
240
-
241
-	/**
242
-	 * Propfind call with cache handling.
243
-	 *
244
-	 * First checks if information is cached.
245
-	 * If not, request it from the server then store to cache.
246
-	 *
247
-	 * @param string $path path to propfind
248
-	 *
249
-	 * @return array|boolean propfind response or false if the entry was not found
250
-	 *
251
-	 * @throws ClientHttpException
252
-	 */
253
-	protected function propfind($path) {
254
-		$path = $this->cleanPath($path);
255
-		$cachedResponse = $this->statCache->get($path);
256
-		// we either don't know it, or we know it exists but need more details
257
-		if (is_null($cachedResponse) || $cachedResponse === true) {
258
-			$this->init();
259
-			try {
260
-				$response = $this->client->propFind(
261
-					$this->encodePath($path),
262
-					array(
263
-						'{DAV:}getlastmodified',
264
-						'{DAV:}getcontentlength',
265
-						'{DAV:}getcontenttype',
266
-						'{http://owncloud.org/ns}permissions',
267
-						'{http://open-collaboration-services.org/ns}share-permissions',
268
-						'{DAV:}resourcetype',
269
-						'{DAV:}getetag',
270
-					)
271
-				);
272
-				$this->statCache->set($path, $response);
273
-			} catch (ClientHttpException $e) {
274
-				if ($e->getHttpStatus() === 404) {
275
-					$this->statCache->clear($path . '/');
276
-					$this->statCache->set($path, false);
277
-					return false;
278
-				}
279
-				$this->convertException($e, $path);
280
-			} catch (\Exception $e) {
281
-				$this->convertException($e, $path);
282
-			}
283
-		} else {
284
-			$response = $cachedResponse;
285
-		}
286
-		return $response;
287
-	}
288
-
289
-	/** {@inheritdoc} */
290
-	public function filetype($path) {
291
-		try {
292
-			$response = $this->propfind($path);
293
-			if ($response === false) {
294
-				return false;
295
-			}
296
-			$responseType = [];
297
-			if (isset($response["{DAV:}resourcetype"])) {
298
-				/** @var ResourceType[] $response */
299
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
300
-			}
301
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
302
-		} catch (\Exception $e) {
303
-			$this->convertException($e, $path);
304
-		}
305
-		return false;
306
-	}
307
-
308
-	/** {@inheritdoc} */
309
-	public function file_exists($path) {
310
-		try {
311
-			$path = $this->cleanPath($path);
312
-			$cachedState = $this->statCache->get($path);
313
-			if ($cachedState === false) {
314
-				// we know the file doesn't exist
315
-				return false;
316
-			} else if (!is_null($cachedState)) {
317
-				return true;
318
-			}
319
-			// need to get from server
320
-			return ($this->propfind($path) !== false);
321
-		} catch (\Exception $e) {
322
-			$this->convertException($e, $path);
323
-		}
324
-		return false;
325
-	}
326
-
327
-	/** {@inheritdoc} */
328
-	public function unlink($path) {
329
-		$this->init();
330
-		$path = $this->cleanPath($path);
331
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
332
-		$this->statCache->clear($path . '/');
333
-		$this->statCache->remove($path);
334
-		return $result;
335
-	}
336
-
337
-	/** {@inheritdoc} */
338
-	public function fopen($path, $mode) {
339
-		$this->init();
340
-		$path = $this->cleanPath($path);
341
-		switch ($mode) {
342
-			case 'r':
343
-			case 'rb':
344
-				try {
345
-					$response = $this->httpClientService
346
-						->newClient()
347
-						->get($this->createBaseUri() . $this->encodePath($path), [
348
-							'auth' => [$this->user, $this->password],
349
-							'stream' => true
350
-						]);
351
-				} catch (RequestException $e) {
352
-					if ($e->getResponse() instanceof ResponseInterface
353
-						&& $e->getResponse()->getStatusCode() === 404) {
354
-						return false;
355
-					} else {
356
-						throw $e;
357
-					}
358
-				}
359
-
360
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
361
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
362
-						throw new \OCP\Lock\LockedException($path);
363
-					} else {
364
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
365
-					}
366
-				}
367
-
368
-				return $response->getBody();
369
-			case 'w':
370
-			case 'wb':
371
-			case 'a':
372
-			case 'ab':
373
-			case 'r+':
374
-			case 'w+':
375
-			case 'wb+':
376
-			case 'a+':
377
-			case 'x':
378
-			case 'x+':
379
-			case 'c':
380
-			case 'c+':
381
-				//emulate these
382
-				$tempManager = \OC::$server->getTempManager();
383
-				if (strrpos($path, '.') !== false) {
384
-					$ext = substr($path, strrpos($path, '.'));
385
-				} else {
386
-					$ext = '';
387
-				}
388
-				if ($this->file_exists($path)) {
389
-					if (!$this->isUpdatable($path)) {
390
-						return false;
391
-					}
392
-					if ($mode === 'w' or $mode === 'w+') {
393
-						$tmpFile = $tempManager->getTemporaryFile($ext);
394
-					} else {
395
-						$tmpFile = $this->getCachedFile($path);
396
-					}
397
-				} else {
398
-					if (!$this->isCreatable(dirname($path))) {
399
-						return false;
400
-					}
401
-					$tmpFile = $tempManager->getTemporaryFile($ext);
402
-				}
403
-				$handle = fopen($tmpFile, $mode);
404
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
405
-					$this->writeBack($tmpFile, $path);
406
-				});
407
-		}
408
-	}
409
-
410
-	/**
411
-	 * @param string $tmpFile
412
-	 */
413
-	public function writeBack($tmpFile, $path) {
414
-		$this->uploadFile($tmpFile, $path);
415
-		unlink($tmpFile);
416
-	}
417
-
418
-	/** {@inheritdoc} */
419
-	public function free_space($path) {
420
-		$this->init();
421
-		$path = $this->cleanPath($path);
422
-		try {
423
-			// TODO: cacheable ?
424
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
425
-			if ($response === false) {
426
-				return FileInfo::SPACE_UNKNOWN;
427
-			}
428
-			if (isset($response['{DAV:}quota-available-bytes'])) {
429
-				return (int)$response['{DAV:}quota-available-bytes'];
430
-			} else {
431
-				return FileInfo::SPACE_UNKNOWN;
432
-			}
433
-		} catch (\Exception $e) {
434
-			return FileInfo::SPACE_UNKNOWN;
435
-		}
436
-	}
437
-
438
-	/** {@inheritdoc} */
439
-	public function touch($path, $mtime = null) {
440
-		$this->init();
441
-		if (is_null($mtime)) {
442
-			$mtime = time();
443
-		}
444
-		$path = $this->cleanPath($path);
445
-
446
-		// if file exists, update the mtime, else create a new empty file
447
-		if ($this->file_exists($path)) {
448
-			try {
449
-				$this->statCache->remove($path);
450
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
451
-				// non-owncloud clients might not have accepted the property, need to recheck it
452
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
453
-				if ($response === false) {
454
-					return false;
455
-				}
456
-				if (isset($response['{DAV:}getlastmodified'])) {
457
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
458
-					if ($remoteMtime !== $mtime) {
459
-						// server has not accepted the mtime
460
-						return false;
461
-					}
462
-				}
463
-			} catch (ClientHttpException $e) {
464
-				if ($e->getHttpStatus() === 501) {
465
-					return false;
466
-				}
467
-				$this->convertException($e, $path);
468
-				return false;
469
-			} catch (\Exception $e) {
470
-				$this->convertException($e, $path);
471
-				return false;
472
-			}
473
-		} else {
474
-			$this->file_put_contents($path, '');
475
-		}
476
-		return true;
477
-	}
478
-
479
-	/**
480
-	 * @param string $path
481
-	 * @param string $data
482
-	 * @return int
483
-	 */
484
-	public function file_put_contents($path, $data) {
485
-		$path = $this->cleanPath($path);
486
-		$result = parent::file_put_contents($path, $data);
487
-		$this->statCache->remove($path);
488
-		return $result;
489
-	}
490
-
491
-	/**
492
-	 * @param string $path
493
-	 * @param string $target
494
-	 */
495
-	protected function uploadFile($path, $target) {
496
-		$this->init();
497
-
498
-		// invalidate
499
-		$target = $this->cleanPath($target);
500
-		$this->statCache->remove($target);
501
-		$source = fopen($path, 'r');
502
-
503
-		$this->httpClientService
504
-			->newClient()
505
-			->put($this->createBaseUri() . $this->encodePath($target), [
506
-				'body' => $source,
507
-				'auth' => [$this->user, $this->password]
508
-			]);
509
-
510
-		$this->removeCachedFile($target);
511
-	}
512
-
513
-	/** {@inheritdoc} */
514
-	public function rename($path1, $path2) {
515
-		$this->init();
516
-		$path1 = $this->cleanPath($path1);
517
-		$path2 = $this->cleanPath($path2);
518
-		try {
519
-			// overwrite directory ?
520
-			if ($this->is_dir($path2)) {
521
-				// needs trailing slash in destination
522
-				$path2 = rtrim($path2, '/') . '/';
523
-			}
524
-			$this->client->request(
525
-				'MOVE',
526
-				$this->encodePath($path1),
527
-				null,
528
-				[
529
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
530
-				]
531
-			);
532
-			$this->statCache->clear($path1 . '/');
533
-			$this->statCache->clear($path2 . '/');
534
-			$this->statCache->set($path1, false);
535
-			$this->statCache->set($path2, true);
536
-			$this->removeCachedFile($path1);
537
-			$this->removeCachedFile($path2);
538
-			return true;
539
-		} catch (\Exception $e) {
540
-			$this->convertException($e);
541
-		}
542
-		return false;
543
-	}
544
-
545
-	/** {@inheritdoc} */
546
-	public function copy($path1, $path2) {
547
-		$this->init();
548
-		$path1 = $this->cleanPath($path1);
549
-		$path2 = $this->cleanPath($path2);
550
-		try {
551
-			// overwrite directory ?
552
-			if ($this->is_dir($path2)) {
553
-				// needs trailing slash in destination
554
-				$path2 = rtrim($path2, '/') . '/';
555
-			}
556
-			$this->client->request(
557
-				'COPY',
558
-				$this->encodePath($path1),
559
-				null,
560
-				[
561
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
562
-				]
563
-			);
564
-			$this->statCache->clear($path2 . '/');
565
-			$this->statCache->set($path2, true);
566
-			$this->removeCachedFile($path2);
567
-			return true;
568
-		} catch (\Exception $e) {
569
-			$this->convertException($e);
570
-		}
571
-		return false;
572
-	}
573
-
574
-	/** {@inheritdoc} */
575
-	public function stat($path) {
576
-		try {
577
-			$response = $this->propfind($path);
578
-			if (!$response) {
579
-				return false;
580
-			}
581
-			return [
582
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
583
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
584
-			];
585
-		} catch (\Exception $e) {
586
-			$this->convertException($e, $path);
587
-		}
588
-		return array();
589
-	}
590
-
591
-	/** {@inheritdoc} */
592
-	public function getMimeType($path) {
593
-		$remoteMimetype = $this->getMimeTypeFromRemote($path);
594
-		if ($remoteMimetype === 'application/octet-stream') {
595
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
596
-		} else {
597
-			return $remoteMimetype;
598
-		}
599
-	}
600
-
601
-	public function getMimeTypeFromRemote($path) {
602
-		try {
603
-			$response = $this->propfind($path);
604
-			if ($response === false) {
605
-				return false;
606
-			}
607
-			$responseType = [];
608
-			if (isset($response["{DAV:}resourcetype"])) {
609
-				/** @var ResourceType[] $response */
610
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
611
-			}
612
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
613
-			if ($type == 'dir') {
614
-				return 'httpd/unix-directory';
615
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
616
-				return $response['{DAV:}getcontenttype'];
617
-			} else {
618
-				return 'application/octet-stream';
619
-			}
620
-		} catch (\Exception $e) {
621
-			return false;
622
-		}
623
-	}
624
-
625
-	/**
626
-	 * @param string $path
627
-	 * @return string
628
-	 */
629
-	public function cleanPath($path) {
630
-		if ($path === '') {
631
-			return $path;
632
-		}
633
-		$path = Filesystem::normalizePath($path);
634
-		// remove leading slash
635
-		return substr($path, 1);
636
-	}
637
-
638
-	/**
639
-	 * URL encodes the given path but keeps the slashes
640
-	 *
641
-	 * @param string $path to encode
642
-	 * @return string encoded path
643
-	 */
644
-	protected function encodePath($path) {
645
-		// slashes need to stay
646
-		return str_replace('%2F', '/', rawurlencode($path));
647
-	}
648
-
649
-	/**
650
-	 * @param string $method
651
-	 * @param string $path
652
-	 * @param string|resource|null $body
653
-	 * @param int $expected
654
-	 * @return bool
655
-	 * @throws StorageInvalidException
656
-	 * @throws StorageNotAvailableException
657
-	 */
658
-	protected function simpleResponse($method, $path, $body, $expected) {
659
-		$path = $this->cleanPath($path);
660
-		try {
661
-			$response = $this->client->request($method, $this->encodePath($path), $body);
662
-			return $response['statusCode'] == $expected;
663
-		} catch (ClientHttpException $e) {
664
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
665
-				$this->statCache->clear($path . '/');
666
-				$this->statCache->set($path, false);
667
-				return false;
668
-			}
669
-
670
-			$this->convertException($e, $path);
671
-		} catch (\Exception $e) {
672
-			$this->convertException($e, $path);
673
-		}
674
-		return false;
675
-	}
676
-
677
-	/**
678
-	 * check if curl is installed
679
-	 */
680
-	public static function checkDependencies() {
681
-		return true;
682
-	}
683
-
684
-	/** {@inheritdoc} */
685
-	public function isUpdatable($path) {
686
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
687
-	}
688
-
689
-	/** {@inheritdoc} */
690
-	public function isCreatable($path) {
691
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
692
-	}
693
-
694
-	/** {@inheritdoc} */
695
-	public function isSharable($path) {
696
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
697
-	}
698
-
699
-	/** {@inheritdoc} */
700
-	public function isDeletable($path) {
701
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
702
-	}
703
-
704
-	/** {@inheritdoc} */
705
-	public function getPermissions($path) {
706
-		$this->init();
707
-		$path = $this->cleanPath($path);
708
-		$response = $this->propfind($path);
709
-		if ($response === false) {
710
-			return 0;
711
-		}
712
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
713
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
714
-		} else if ($this->is_dir($path)) {
715
-			return Constants::PERMISSION_ALL;
716
-		} else if ($this->file_exists($path)) {
717
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
718
-		} else {
719
-			return 0;
720
-		}
721
-	}
722
-
723
-	/** {@inheritdoc} */
724
-	public function getETag($path) {
725
-		$this->init();
726
-		$path = $this->cleanPath($path);
727
-		$response = $this->propfind($path);
728
-		if ($response === false) {
729
-			return null;
730
-		}
731
-		if (isset($response['{DAV:}getetag'])) {
732
-			return trim($response['{DAV:}getetag'], '"');
733
-		}
734
-		return parent::getEtag($path);
735
-	}
736
-
737
-	/**
738
-	 * @param string $permissionsString
739
-	 * @return int
740
-	 */
741
-	protected function parsePermissions($permissionsString) {
742
-		$permissions = Constants::PERMISSION_READ;
743
-		if (strpos($permissionsString, 'R') !== false) {
744
-			$permissions |= Constants::PERMISSION_SHARE;
745
-		}
746
-		if (strpos($permissionsString, 'D') !== false) {
747
-			$permissions |= Constants::PERMISSION_DELETE;
748
-		}
749
-		if (strpos($permissionsString, 'W') !== false) {
750
-			$permissions |= Constants::PERMISSION_UPDATE;
751
-		}
752
-		if (strpos($permissionsString, 'CK') !== false) {
753
-			$permissions |= Constants::PERMISSION_CREATE;
754
-			$permissions |= Constants::PERMISSION_UPDATE;
755
-		}
756
-		return $permissions;
757
-	}
758
-
759
-	/**
760
-	 * check if a file or folder has been updated since $time
761
-	 *
762
-	 * @param string $path
763
-	 * @param int $time
764
-	 * @throws \OCP\Files\StorageNotAvailableException
765
-	 * @return bool
766
-	 */
767
-	public function hasUpdated($path, $time) {
768
-		$this->init();
769
-		$path = $this->cleanPath($path);
770
-		try {
771
-			// force refresh for $path
772
-			$this->statCache->remove($path);
773
-			$response = $this->propfind($path);
774
-			if ($response === false) {
775
-				if ($path === '') {
776
-					// if root is gone it means the storage is not available
777
-					throw new StorageNotAvailableException('root is gone');
778
-				}
779
-				return false;
780
-			}
781
-			if (isset($response['{DAV:}getetag'])) {
782
-				$cachedData = $this->getCache()->get($path);
783
-				$etag = null;
784
-				if (isset($response['{DAV:}getetag'])) {
785
-					$etag = trim($response['{DAV:}getetag'], '"');
786
-				}
787
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
788
-					return true;
789
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
790
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
791
-					return $sharePermissions !== $cachedData['permissions'];
792
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
793
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
794
-					return $permissions !== $cachedData['permissions'];
795
-				} else {
796
-					return false;
797
-				}
798
-			} else {
799
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
800
-				return $remoteMtime > $time;
801
-			}
802
-		} catch (ClientHttpException $e) {
803
-			if ($e->getHttpStatus() === 405) {
804
-				if ($path === '') {
805
-					// if root is gone it means the storage is not available
806
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
807
-				}
808
-				return false;
809
-			}
810
-			$this->convertException($e, $path);
811
-			return false;
812
-		} catch (\Exception $e) {
813
-			$this->convertException($e, $path);
814
-			return false;
815
-		}
816
-	}
817
-
818
-	/**
819
-	 * Interpret the given exception and decide whether it is due to an
820
-	 * unavailable storage, invalid storage or other.
821
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
822
-	 * or do nothing.
823
-	 *
824
-	 * @param Exception $e sabre exception
825
-	 * @param string $path optional path from the operation
826
-	 *
827
-	 * @throws StorageInvalidException if the storage is invalid, for example
828
-	 * when the authentication expired or is invalid
829
-	 * @throws StorageNotAvailableException if the storage is not available,
830
-	 * which might be temporary
831
-	 */
832
-	protected function convertException(Exception $e, $path = '') {
833
-		\OC::$server->getLogger()->logException($e);
834
-		Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
835
-		if ($e instanceof ClientHttpException) {
836
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
837
-				throw new \OCP\Lock\LockedException($path);
838
-			}
839
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
840
-				// either password was changed or was invalid all along
841
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
842
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
843
-				// ignore exception for MethodNotAllowed, false will be returned
844
-				return;
845
-			}
846
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
847
-		} else if ($e instanceof ClientException) {
848
-			// connection timeout or refused, server could be temporarily down
849
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
850
-		} else if ($e instanceof \InvalidArgumentException) {
851
-			// parse error because the server returned HTML instead of XML,
852
-			// possibly temporarily down
853
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
854
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
855
-			// rethrow
856
-			throw $e;
857
-		}
858
-
859
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
860
-	}
60
+    /** @var string */
61
+    protected $password;
62
+    /** @var string */
63
+    protected $user;
64
+    /** @var string */
65
+    protected $authType;
66
+    /** @var string */
67
+    protected $host;
68
+    /** @var bool */
69
+    protected $secure;
70
+    /** @var string */
71
+    protected $root;
72
+    /** @var string */
73
+    protected $certPath;
74
+    /** @var bool */
75
+    protected $ready;
76
+    /** @var Client */
77
+    protected $client;
78
+    /** @var ArrayCache */
79
+    protected $statCache;
80
+    /** @var \OCP\Http\Client\IClientService */
81
+    protected $httpClientService;
82
+
83
+    /**
84
+     * @param array $params
85
+     * @throws \Exception
86
+     */
87
+    public function __construct($params) {
88
+        $this->statCache = new ArrayCache();
89
+        $this->httpClientService = \OC::$server->getHTTPClientService();
90
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91
+            $host = $params['host'];
92
+            //remove leading http[s], will be generated in createBaseUri()
93
+            if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
+            else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
95
+            $this->host = $host;
96
+            $this->user = $params['user'];
97
+            $this->password = $params['password'];
98
+            if (isset($params['authType'])) {
99
+                $this->authType = $params['authType'];
100
+            }
101
+            if (isset($params['secure'])) {
102
+                if (is_string($params['secure'])) {
103
+                    $this->secure = ($params['secure'] === 'true');
104
+                } else {
105
+                    $this->secure = (bool)$params['secure'];
106
+                }
107
+            } else {
108
+                $this->secure = false;
109
+            }
110
+            if ($this->secure === true) {
111
+                // inject mock for testing
112
+                $certManager = \OC::$server->getCertificateManager();
113
+                if (is_null($certManager)) { //no user
114
+                    $certManager = \OC::$server->getCertificateManager(null);
115
+                }
116
+                $certPath = $certManager->getAbsoluteBundlePath();
117
+                if (file_exists($certPath)) {
118
+                    $this->certPath = $certPath;
119
+                }
120
+            }
121
+            $this->root = isset($params['root']) ? $params['root'] : '/';
122
+            if (!$this->root || $this->root[0] != '/') {
123
+                $this->root = '/' . $this->root;
124
+            }
125
+            if (substr($this->root, -1, 1) != '/') {
126
+                $this->root .= '/';
127
+            }
128
+        } else {
129
+            throw new \Exception('Invalid webdav storage configuration');
130
+        }
131
+    }
132
+
133
+    protected function init() {
134
+        if ($this->ready) {
135
+            return;
136
+        }
137
+        $this->ready = true;
138
+
139
+        $settings = [
140
+            'baseUri' => $this->createBaseUri(),
141
+            'userName' => $this->user,
142
+            'password' => $this->password,
143
+        ];
144
+        if (isset($this->authType)) {
145
+            $settings['authType'] = $this->authType;
146
+        }
147
+
148
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
149
+        if ($proxy !== '') {
150
+            $settings['proxy'] = $proxy;
151
+        }
152
+
153
+        $this->client = new Client($settings);
154
+        $this->client->setThrowExceptions(true);
155
+        if ($this->secure === true && $this->certPath) {
156
+            $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
157
+        }
158
+    }
159
+
160
+    /**
161
+     * Clear the stat cache
162
+     */
163
+    public function clearStatCache() {
164
+        $this->statCache->clear();
165
+    }
166
+
167
+    /** {@inheritdoc} */
168
+    public function getId() {
169
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
170
+    }
171
+
172
+    /** {@inheritdoc} */
173
+    public function createBaseUri() {
174
+        $baseUri = 'http';
175
+        if ($this->secure) {
176
+            $baseUri .= 's';
177
+        }
178
+        $baseUri .= '://' . $this->host . $this->root;
179
+        return $baseUri;
180
+    }
181
+
182
+    /** {@inheritdoc} */
183
+    public function mkdir($path) {
184
+        $this->init();
185
+        $path = $this->cleanPath($path);
186
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
187
+        if ($result) {
188
+            $this->statCache->set($path, true);
189
+        }
190
+        return $result;
191
+    }
192
+
193
+    /** {@inheritdoc} */
194
+    public function rmdir($path) {
195
+        $this->init();
196
+        $path = $this->cleanPath($path);
197
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
198
+        // a non-empty folder
199
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
200
+        $this->statCache->clear($path . '/');
201
+        $this->statCache->remove($path);
202
+        return $result;
203
+    }
204
+
205
+    /** {@inheritdoc} */
206
+    public function opendir($path) {
207
+        $this->init();
208
+        $path = $this->cleanPath($path);
209
+        try {
210
+            $response = $this->client->propFind(
211
+                $this->encodePath($path),
212
+                ['{DAV:}href'],
213
+                1
214
+            );
215
+            if ($response === false) {
216
+                return false;
217
+            }
218
+            $content = [];
219
+            $files = array_keys($response);
220
+            array_shift($files); //the first entry is the current directory
221
+
222
+            if (!$this->statCache->hasKey($path)) {
223
+                $this->statCache->set($path, true);
224
+            }
225
+            foreach ($files as $file) {
226
+                $file = urldecode($file);
227
+                // do not store the real entry, we might not have all properties
228
+                if (!$this->statCache->hasKey($path)) {
229
+                    $this->statCache->set($file, true);
230
+                }
231
+                $file = basename($file);
232
+                $content[] = $file;
233
+            }
234
+            return IteratorDirectory::wrap($content);
235
+        } catch (\Exception $e) {
236
+            $this->convertException($e, $path);
237
+        }
238
+        return false;
239
+    }
240
+
241
+    /**
242
+     * Propfind call with cache handling.
243
+     *
244
+     * First checks if information is cached.
245
+     * If not, request it from the server then store to cache.
246
+     *
247
+     * @param string $path path to propfind
248
+     *
249
+     * @return array|boolean propfind response or false if the entry was not found
250
+     *
251
+     * @throws ClientHttpException
252
+     */
253
+    protected function propfind($path) {
254
+        $path = $this->cleanPath($path);
255
+        $cachedResponse = $this->statCache->get($path);
256
+        // we either don't know it, or we know it exists but need more details
257
+        if (is_null($cachedResponse) || $cachedResponse === true) {
258
+            $this->init();
259
+            try {
260
+                $response = $this->client->propFind(
261
+                    $this->encodePath($path),
262
+                    array(
263
+                        '{DAV:}getlastmodified',
264
+                        '{DAV:}getcontentlength',
265
+                        '{DAV:}getcontenttype',
266
+                        '{http://owncloud.org/ns}permissions',
267
+                        '{http://open-collaboration-services.org/ns}share-permissions',
268
+                        '{DAV:}resourcetype',
269
+                        '{DAV:}getetag',
270
+                    )
271
+                );
272
+                $this->statCache->set($path, $response);
273
+            } catch (ClientHttpException $e) {
274
+                if ($e->getHttpStatus() === 404) {
275
+                    $this->statCache->clear($path . '/');
276
+                    $this->statCache->set($path, false);
277
+                    return false;
278
+                }
279
+                $this->convertException($e, $path);
280
+            } catch (\Exception $e) {
281
+                $this->convertException($e, $path);
282
+            }
283
+        } else {
284
+            $response = $cachedResponse;
285
+        }
286
+        return $response;
287
+    }
288
+
289
+    /** {@inheritdoc} */
290
+    public function filetype($path) {
291
+        try {
292
+            $response = $this->propfind($path);
293
+            if ($response === false) {
294
+                return false;
295
+            }
296
+            $responseType = [];
297
+            if (isset($response["{DAV:}resourcetype"])) {
298
+                /** @var ResourceType[] $response */
299
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
300
+            }
301
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
302
+        } catch (\Exception $e) {
303
+            $this->convertException($e, $path);
304
+        }
305
+        return false;
306
+    }
307
+
308
+    /** {@inheritdoc} */
309
+    public function file_exists($path) {
310
+        try {
311
+            $path = $this->cleanPath($path);
312
+            $cachedState = $this->statCache->get($path);
313
+            if ($cachedState === false) {
314
+                // we know the file doesn't exist
315
+                return false;
316
+            } else if (!is_null($cachedState)) {
317
+                return true;
318
+            }
319
+            // need to get from server
320
+            return ($this->propfind($path) !== false);
321
+        } catch (\Exception $e) {
322
+            $this->convertException($e, $path);
323
+        }
324
+        return false;
325
+    }
326
+
327
+    /** {@inheritdoc} */
328
+    public function unlink($path) {
329
+        $this->init();
330
+        $path = $this->cleanPath($path);
331
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
332
+        $this->statCache->clear($path . '/');
333
+        $this->statCache->remove($path);
334
+        return $result;
335
+    }
336
+
337
+    /** {@inheritdoc} */
338
+    public function fopen($path, $mode) {
339
+        $this->init();
340
+        $path = $this->cleanPath($path);
341
+        switch ($mode) {
342
+            case 'r':
343
+            case 'rb':
344
+                try {
345
+                    $response = $this->httpClientService
346
+                        ->newClient()
347
+                        ->get($this->createBaseUri() . $this->encodePath($path), [
348
+                            'auth' => [$this->user, $this->password],
349
+                            'stream' => true
350
+                        ]);
351
+                } catch (RequestException $e) {
352
+                    if ($e->getResponse() instanceof ResponseInterface
353
+                        && $e->getResponse()->getStatusCode() === 404) {
354
+                        return false;
355
+                    } else {
356
+                        throw $e;
357
+                    }
358
+                }
359
+
360
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
361
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
362
+                        throw new \OCP\Lock\LockedException($path);
363
+                    } else {
364
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
365
+                    }
366
+                }
367
+
368
+                return $response->getBody();
369
+            case 'w':
370
+            case 'wb':
371
+            case 'a':
372
+            case 'ab':
373
+            case 'r+':
374
+            case 'w+':
375
+            case 'wb+':
376
+            case 'a+':
377
+            case 'x':
378
+            case 'x+':
379
+            case 'c':
380
+            case 'c+':
381
+                //emulate these
382
+                $tempManager = \OC::$server->getTempManager();
383
+                if (strrpos($path, '.') !== false) {
384
+                    $ext = substr($path, strrpos($path, '.'));
385
+                } else {
386
+                    $ext = '';
387
+                }
388
+                if ($this->file_exists($path)) {
389
+                    if (!$this->isUpdatable($path)) {
390
+                        return false;
391
+                    }
392
+                    if ($mode === 'w' or $mode === 'w+') {
393
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
394
+                    } else {
395
+                        $tmpFile = $this->getCachedFile($path);
396
+                    }
397
+                } else {
398
+                    if (!$this->isCreatable(dirname($path))) {
399
+                        return false;
400
+                    }
401
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
402
+                }
403
+                $handle = fopen($tmpFile, $mode);
404
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
405
+                    $this->writeBack($tmpFile, $path);
406
+                });
407
+        }
408
+    }
409
+
410
+    /**
411
+     * @param string $tmpFile
412
+     */
413
+    public function writeBack($tmpFile, $path) {
414
+        $this->uploadFile($tmpFile, $path);
415
+        unlink($tmpFile);
416
+    }
417
+
418
+    /** {@inheritdoc} */
419
+    public function free_space($path) {
420
+        $this->init();
421
+        $path = $this->cleanPath($path);
422
+        try {
423
+            // TODO: cacheable ?
424
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
425
+            if ($response === false) {
426
+                return FileInfo::SPACE_UNKNOWN;
427
+            }
428
+            if (isset($response['{DAV:}quota-available-bytes'])) {
429
+                return (int)$response['{DAV:}quota-available-bytes'];
430
+            } else {
431
+                return FileInfo::SPACE_UNKNOWN;
432
+            }
433
+        } catch (\Exception $e) {
434
+            return FileInfo::SPACE_UNKNOWN;
435
+        }
436
+    }
437
+
438
+    /** {@inheritdoc} */
439
+    public function touch($path, $mtime = null) {
440
+        $this->init();
441
+        if (is_null($mtime)) {
442
+            $mtime = time();
443
+        }
444
+        $path = $this->cleanPath($path);
445
+
446
+        // if file exists, update the mtime, else create a new empty file
447
+        if ($this->file_exists($path)) {
448
+            try {
449
+                $this->statCache->remove($path);
450
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
451
+                // non-owncloud clients might not have accepted the property, need to recheck it
452
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
453
+                if ($response === false) {
454
+                    return false;
455
+                }
456
+                if (isset($response['{DAV:}getlastmodified'])) {
457
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
458
+                    if ($remoteMtime !== $mtime) {
459
+                        // server has not accepted the mtime
460
+                        return false;
461
+                    }
462
+                }
463
+            } catch (ClientHttpException $e) {
464
+                if ($e->getHttpStatus() === 501) {
465
+                    return false;
466
+                }
467
+                $this->convertException($e, $path);
468
+                return false;
469
+            } catch (\Exception $e) {
470
+                $this->convertException($e, $path);
471
+                return false;
472
+            }
473
+        } else {
474
+            $this->file_put_contents($path, '');
475
+        }
476
+        return true;
477
+    }
478
+
479
+    /**
480
+     * @param string $path
481
+     * @param string $data
482
+     * @return int
483
+     */
484
+    public function file_put_contents($path, $data) {
485
+        $path = $this->cleanPath($path);
486
+        $result = parent::file_put_contents($path, $data);
487
+        $this->statCache->remove($path);
488
+        return $result;
489
+    }
490
+
491
+    /**
492
+     * @param string $path
493
+     * @param string $target
494
+     */
495
+    protected function uploadFile($path, $target) {
496
+        $this->init();
497
+
498
+        // invalidate
499
+        $target = $this->cleanPath($target);
500
+        $this->statCache->remove($target);
501
+        $source = fopen($path, 'r');
502
+
503
+        $this->httpClientService
504
+            ->newClient()
505
+            ->put($this->createBaseUri() . $this->encodePath($target), [
506
+                'body' => $source,
507
+                'auth' => [$this->user, $this->password]
508
+            ]);
509
+
510
+        $this->removeCachedFile($target);
511
+    }
512
+
513
+    /** {@inheritdoc} */
514
+    public function rename($path1, $path2) {
515
+        $this->init();
516
+        $path1 = $this->cleanPath($path1);
517
+        $path2 = $this->cleanPath($path2);
518
+        try {
519
+            // overwrite directory ?
520
+            if ($this->is_dir($path2)) {
521
+                // needs trailing slash in destination
522
+                $path2 = rtrim($path2, '/') . '/';
523
+            }
524
+            $this->client->request(
525
+                'MOVE',
526
+                $this->encodePath($path1),
527
+                null,
528
+                [
529
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
530
+                ]
531
+            );
532
+            $this->statCache->clear($path1 . '/');
533
+            $this->statCache->clear($path2 . '/');
534
+            $this->statCache->set($path1, false);
535
+            $this->statCache->set($path2, true);
536
+            $this->removeCachedFile($path1);
537
+            $this->removeCachedFile($path2);
538
+            return true;
539
+        } catch (\Exception $e) {
540
+            $this->convertException($e);
541
+        }
542
+        return false;
543
+    }
544
+
545
+    /** {@inheritdoc} */
546
+    public function copy($path1, $path2) {
547
+        $this->init();
548
+        $path1 = $this->cleanPath($path1);
549
+        $path2 = $this->cleanPath($path2);
550
+        try {
551
+            // overwrite directory ?
552
+            if ($this->is_dir($path2)) {
553
+                // needs trailing slash in destination
554
+                $path2 = rtrim($path2, '/') . '/';
555
+            }
556
+            $this->client->request(
557
+                'COPY',
558
+                $this->encodePath($path1),
559
+                null,
560
+                [
561
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
562
+                ]
563
+            );
564
+            $this->statCache->clear($path2 . '/');
565
+            $this->statCache->set($path2, true);
566
+            $this->removeCachedFile($path2);
567
+            return true;
568
+        } catch (\Exception $e) {
569
+            $this->convertException($e);
570
+        }
571
+        return false;
572
+    }
573
+
574
+    /** {@inheritdoc} */
575
+    public function stat($path) {
576
+        try {
577
+            $response = $this->propfind($path);
578
+            if (!$response) {
579
+                return false;
580
+            }
581
+            return [
582
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
583
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
584
+            ];
585
+        } catch (\Exception $e) {
586
+            $this->convertException($e, $path);
587
+        }
588
+        return array();
589
+    }
590
+
591
+    /** {@inheritdoc} */
592
+    public function getMimeType($path) {
593
+        $remoteMimetype = $this->getMimeTypeFromRemote($path);
594
+        if ($remoteMimetype === 'application/octet-stream') {
595
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
596
+        } else {
597
+            return $remoteMimetype;
598
+        }
599
+    }
600
+
601
+    public function getMimeTypeFromRemote($path) {
602
+        try {
603
+            $response = $this->propfind($path);
604
+            if ($response === false) {
605
+                return false;
606
+            }
607
+            $responseType = [];
608
+            if (isset($response["{DAV:}resourcetype"])) {
609
+                /** @var ResourceType[] $response */
610
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
611
+            }
612
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
613
+            if ($type == 'dir') {
614
+                return 'httpd/unix-directory';
615
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
616
+                return $response['{DAV:}getcontenttype'];
617
+            } else {
618
+                return 'application/octet-stream';
619
+            }
620
+        } catch (\Exception $e) {
621
+            return false;
622
+        }
623
+    }
624
+
625
+    /**
626
+     * @param string $path
627
+     * @return string
628
+     */
629
+    public function cleanPath($path) {
630
+        if ($path === '') {
631
+            return $path;
632
+        }
633
+        $path = Filesystem::normalizePath($path);
634
+        // remove leading slash
635
+        return substr($path, 1);
636
+    }
637
+
638
+    /**
639
+     * URL encodes the given path but keeps the slashes
640
+     *
641
+     * @param string $path to encode
642
+     * @return string encoded path
643
+     */
644
+    protected function encodePath($path) {
645
+        // slashes need to stay
646
+        return str_replace('%2F', '/', rawurlencode($path));
647
+    }
648
+
649
+    /**
650
+     * @param string $method
651
+     * @param string $path
652
+     * @param string|resource|null $body
653
+     * @param int $expected
654
+     * @return bool
655
+     * @throws StorageInvalidException
656
+     * @throws StorageNotAvailableException
657
+     */
658
+    protected function simpleResponse($method, $path, $body, $expected) {
659
+        $path = $this->cleanPath($path);
660
+        try {
661
+            $response = $this->client->request($method, $this->encodePath($path), $body);
662
+            return $response['statusCode'] == $expected;
663
+        } catch (ClientHttpException $e) {
664
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
665
+                $this->statCache->clear($path . '/');
666
+                $this->statCache->set($path, false);
667
+                return false;
668
+            }
669
+
670
+            $this->convertException($e, $path);
671
+        } catch (\Exception $e) {
672
+            $this->convertException($e, $path);
673
+        }
674
+        return false;
675
+    }
676
+
677
+    /**
678
+     * check if curl is installed
679
+     */
680
+    public static function checkDependencies() {
681
+        return true;
682
+    }
683
+
684
+    /** {@inheritdoc} */
685
+    public function isUpdatable($path) {
686
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
687
+    }
688
+
689
+    /** {@inheritdoc} */
690
+    public function isCreatable($path) {
691
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
692
+    }
693
+
694
+    /** {@inheritdoc} */
695
+    public function isSharable($path) {
696
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
697
+    }
698
+
699
+    /** {@inheritdoc} */
700
+    public function isDeletable($path) {
701
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
702
+    }
703
+
704
+    /** {@inheritdoc} */
705
+    public function getPermissions($path) {
706
+        $this->init();
707
+        $path = $this->cleanPath($path);
708
+        $response = $this->propfind($path);
709
+        if ($response === false) {
710
+            return 0;
711
+        }
712
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
713
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
714
+        } else if ($this->is_dir($path)) {
715
+            return Constants::PERMISSION_ALL;
716
+        } else if ($this->file_exists($path)) {
717
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
718
+        } else {
719
+            return 0;
720
+        }
721
+    }
722
+
723
+    /** {@inheritdoc} */
724
+    public function getETag($path) {
725
+        $this->init();
726
+        $path = $this->cleanPath($path);
727
+        $response = $this->propfind($path);
728
+        if ($response === false) {
729
+            return null;
730
+        }
731
+        if (isset($response['{DAV:}getetag'])) {
732
+            return trim($response['{DAV:}getetag'], '"');
733
+        }
734
+        return parent::getEtag($path);
735
+    }
736
+
737
+    /**
738
+     * @param string $permissionsString
739
+     * @return int
740
+     */
741
+    protected function parsePermissions($permissionsString) {
742
+        $permissions = Constants::PERMISSION_READ;
743
+        if (strpos($permissionsString, 'R') !== false) {
744
+            $permissions |= Constants::PERMISSION_SHARE;
745
+        }
746
+        if (strpos($permissionsString, 'D') !== false) {
747
+            $permissions |= Constants::PERMISSION_DELETE;
748
+        }
749
+        if (strpos($permissionsString, 'W') !== false) {
750
+            $permissions |= Constants::PERMISSION_UPDATE;
751
+        }
752
+        if (strpos($permissionsString, 'CK') !== false) {
753
+            $permissions |= Constants::PERMISSION_CREATE;
754
+            $permissions |= Constants::PERMISSION_UPDATE;
755
+        }
756
+        return $permissions;
757
+    }
758
+
759
+    /**
760
+     * check if a file or folder has been updated since $time
761
+     *
762
+     * @param string $path
763
+     * @param int $time
764
+     * @throws \OCP\Files\StorageNotAvailableException
765
+     * @return bool
766
+     */
767
+    public function hasUpdated($path, $time) {
768
+        $this->init();
769
+        $path = $this->cleanPath($path);
770
+        try {
771
+            // force refresh for $path
772
+            $this->statCache->remove($path);
773
+            $response = $this->propfind($path);
774
+            if ($response === false) {
775
+                if ($path === '') {
776
+                    // if root is gone it means the storage is not available
777
+                    throw new StorageNotAvailableException('root is gone');
778
+                }
779
+                return false;
780
+            }
781
+            if (isset($response['{DAV:}getetag'])) {
782
+                $cachedData = $this->getCache()->get($path);
783
+                $etag = null;
784
+                if (isset($response['{DAV:}getetag'])) {
785
+                    $etag = trim($response['{DAV:}getetag'], '"');
786
+                }
787
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
788
+                    return true;
789
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
790
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
791
+                    return $sharePermissions !== $cachedData['permissions'];
792
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
793
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
794
+                    return $permissions !== $cachedData['permissions'];
795
+                } else {
796
+                    return false;
797
+                }
798
+            } else {
799
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
800
+                return $remoteMtime > $time;
801
+            }
802
+        } catch (ClientHttpException $e) {
803
+            if ($e->getHttpStatus() === 405) {
804
+                if ($path === '') {
805
+                    // if root is gone it means the storage is not available
806
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
807
+                }
808
+                return false;
809
+            }
810
+            $this->convertException($e, $path);
811
+            return false;
812
+        } catch (\Exception $e) {
813
+            $this->convertException($e, $path);
814
+            return false;
815
+        }
816
+    }
817
+
818
+    /**
819
+     * Interpret the given exception and decide whether it is due to an
820
+     * unavailable storage, invalid storage or other.
821
+     * This will either throw StorageInvalidException, StorageNotAvailableException
822
+     * or do nothing.
823
+     *
824
+     * @param Exception $e sabre exception
825
+     * @param string $path optional path from the operation
826
+     *
827
+     * @throws StorageInvalidException if the storage is invalid, for example
828
+     * when the authentication expired or is invalid
829
+     * @throws StorageNotAvailableException if the storage is not available,
830
+     * which might be temporary
831
+     */
832
+    protected function convertException(Exception $e, $path = '') {
833
+        \OC::$server->getLogger()->logException($e);
834
+        Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
835
+        if ($e instanceof ClientHttpException) {
836
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
837
+                throw new \OCP\Lock\LockedException($path);
838
+            }
839
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
840
+                // either password was changed or was invalid all along
841
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
842
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
843
+                // ignore exception for MethodNotAllowed, false will be returned
844
+                return;
845
+            }
846
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
847
+        } else if ($e instanceof ClientException) {
848
+            // connection timeout or refused, server could be temporarily down
849
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
850
+        } else if ($e instanceof \InvalidArgumentException) {
851
+            // parse error because the server returned HTML instead of XML,
852
+            // possibly temporarily down
853
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
854
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
855
+            // rethrow
856
+            throw $e;
857
+        }
858
+
859
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
860
+    }
861 861
 }
862 862
 
Please login to merge, or discard this patch.
Spacing   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 				if (is_string($params['secure'])) {
103 103
 					$this->secure = ($params['secure'] === 'true');
104 104
 				} else {
105
-					$this->secure = (bool)$params['secure'];
105
+					$this->secure = (bool) $params['secure'];
106 106
 				}
107 107
 			} else {
108 108
 				$this->secure = false;
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 			}
121 121
 			$this->root = isset($params['root']) ? $params['root'] : '/';
122 122
 			if (!$this->root || $this->root[0] != '/') {
123
-				$this->root = '/' . $this->root;
123
+				$this->root = '/'.$this->root;
124 124
 			}
125 125
 			if (substr($this->root, -1, 1) != '/') {
126 126
 				$this->root .= '/';
@@ -166,7 +166,7 @@  discard block
 block discarded – undo
166 166
 
167 167
 	/** {@inheritdoc} */
168 168
 	public function getId() {
169
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
169
+		return 'webdav::'.$this->user.'@'.$this->host.'/'.$this->root;
170 170
 	}
171 171
 
172 172
 	/** {@inheritdoc} */
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
 		if ($this->secure) {
176 176
 			$baseUri .= 's';
177 177
 		}
178
-		$baseUri .= '://' . $this->host . $this->root;
178
+		$baseUri .= '://'.$this->host.$this->root;
179 179
 		return $baseUri;
180 180
 	}
181 181
 
@@ -196,8 +196,8 @@  discard block
 block discarded – undo
196 196
 		$path = $this->cleanPath($path);
197 197
 		// FIXME: some WebDAV impl return 403 when trying to DELETE
198 198
 		// a non-empty folder
199
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
200
-		$this->statCache->clear($path . '/');
199
+		$result = $this->simpleResponse('DELETE', $path.'/', null, 204);
200
+		$this->statCache->clear($path.'/');
201 201
 		$this->statCache->remove($path);
202 202
 		return $result;
203 203
 	}
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 				$this->statCache->set($path, $response);
273 273
 			} catch (ClientHttpException $e) {
274 274
 				if ($e->getHttpStatus() === 404) {
275
-					$this->statCache->clear($path . '/');
275
+					$this->statCache->clear($path.'/');
276 276
 					$this->statCache->set($path, false);
277 277
 					return false;
278 278
 				}
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 		$this->init();
330 330
 		$path = $this->cleanPath($path);
331 331
 		$result = $this->simpleResponse('DELETE', $path, null, 204);
332
-		$this->statCache->clear($path . '/');
332
+		$this->statCache->clear($path.'/');
333 333
 		$this->statCache->remove($path);
334 334
 		return $result;
335 335
 	}
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
 				try {
345 345
 					$response = $this->httpClientService
346 346
 						->newClient()
347
-						->get($this->createBaseUri() . $this->encodePath($path), [
347
+						->get($this->createBaseUri().$this->encodePath($path), [
348 348
 							'auth' => [$this->user, $this->password],
349 349
 							'stream' => true
350 350
 						]);
@@ -361,7 +361,7 @@  discard block
 block discarded – undo
361 361
 					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
362 362
 						throw new \OCP\Lock\LockedException($path);
363 363
 					} else {
364
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
364
+						Util::writeLog("webdav client", 'Guzzle get returned status code '.$response->getStatusCode(), Util::ERROR);
365 365
 					}
366 366
 				}
367 367
 
@@ -401,7 +401,7 @@  discard block
 block discarded – undo
401 401
 					$tmpFile = $tempManager->getTemporaryFile($ext);
402 402
 				}
403 403
 				$handle = fopen($tmpFile, $mode);
404
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
404
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
405 405
 					$this->writeBack($tmpFile, $path);
406 406
 				});
407 407
 		}
@@ -426,7 +426,7 @@  discard block
 block discarded – undo
426 426
 				return FileInfo::SPACE_UNKNOWN;
427 427
 			}
428 428
 			if (isset($response['{DAV:}quota-available-bytes'])) {
429
-				return (int)$response['{DAV:}quota-available-bytes'];
429
+				return (int) $response['{DAV:}quota-available-bytes'];
430 430
 			} else {
431 431
 				return FileInfo::SPACE_UNKNOWN;
432 432
 			}
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
 
503 503
 		$this->httpClientService
504 504
 			->newClient()
505
-			->put($this->createBaseUri() . $this->encodePath($target), [
505
+			->put($this->createBaseUri().$this->encodePath($target), [
506 506
 				'body' => $source,
507 507
 				'auth' => [$this->user, $this->password]
508 508
 			]);
@@ -519,18 +519,18 @@  discard block
 block discarded – undo
519 519
 			// overwrite directory ?
520 520
 			if ($this->is_dir($path2)) {
521 521
 				// needs trailing slash in destination
522
-				$path2 = rtrim($path2, '/') . '/';
522
+				$path2 = rtrim($path2, '/').'/';
523 523
 			}
524 524
 			$this->client->request(
525 525
 				'MOVE',
526 526
 				$this->encodePath($path1),
527 527
 				null,
528 528
 				[
529
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
529
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
530 530
 				]
531 531
 			);
532
-			$this->statCache->clear($path1 . '/');
533
-			$this->statCache->clear($path2 . '/');
532
+			$this->statCache->clear($path1.'/');
533
+			$this->statCache->clear($path2.'/');
534 534
 			$this->statCache->set($path1, false);
535 535
 			$this->statCache->set($path2, true);
536 536
 			$this->removeCachedFile($path1);
@@ -551,17 +551,17 @@  discard block
 block discarded – undo
551 551
 			// overwrite directory ?
552 552
 			if ($this->is_dir($path2)) {
553 553
 				// needs trailing slash in destination
554
-				$path2 = rtrim($path2, '/') . '/';
554
+				$path2 = rtrim($path2, '/').'/';
555 555
 			}
556 556
 			$this->client->request(
557 557
 				'COPY',
558 558
 				$this->encodePath($path1),
559 559
 				null,
560 560
 				[
561
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
561
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
562 562
 				]
563 563
 			);
564
-			$this->statCache->clear($path2 . '/');
564
+			$this->statCache->clear($path2.'/');
565 565
 			$this->statCache->set($path2, true);
566 566
 			$this->removeCachedFile($path2);
567 567
 			return true;
@@ -580,7 +580,7 @@  discard block
 block discarded – undo
580 580
 			}
581 581
 			return [
582 582
 				'mtime' => strtotime($response['{DAV:}getlastmodified']),
583
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
583
+				'size' => (int) isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
584 584
 			];
585 585
 		} catch (\Exception $e) {
586 586
 			$this->convertException($e, $path);
@@ -662,7 +662,7 @@  discard block
 block discarded – undo
662 662
 			return $response['statusCode'] == $expected;
663 663
 		} catch (ClientHttpException $e) {
664 664
 			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
665
-				$this->statCache->clear($path . '/');
665
+				$this->statCache->clear($path.'/');
666 666
 				$this->statCache->set($path, false);
667 667
 				return false;
668 668
 			}
@@ -683,22 +683,22 @@  discard block
 block discarded – undo
683 683
 
684 684
 	/** {@inheritdoc} */
685 685
 	public function isUpdatable($path) {
686
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
686
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
687 687
 	}
688 688
 
689 689
 	/** {@inheritdoc} */
690 690
 	public function isCreatable($path) {
691
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
691
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_CREATE);
692 692
 	}
693 693
 
694 694
 	/** {@inheritdoc} */
695 695
 	public function isSharable($path) {
696
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
696
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
697 697
 	}
698 698
 
699 699
 	/** {@inheritdoc} */
700 700
 	public function isDeletable($path) {
701
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
701
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_DELETE);
702 702
 	}
703 703
 
704 704
 	/** {@inheritdoc} */
@@ -787,7 +787,7 @@  discard block
 block discarded – undo
787 787
 				if (!empty($etag) && $cachedData['etag'] !== $etag) {
788 788
 					return true;
789 789
 				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
790
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
790
+					$sharePermissions = (int) $response['{http://open-collaboration-services.org/ns}share-permissions'];
791 791
 					return $sharePermissions !== $cachedData['permissions'];
792 792
 				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
793 793
 					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
@@ -803,7 +803,7 @@  discard block
 block discarded – undo
803 803
 			if ($e->getHttpStatus() === 405) {
804 804
 				if ($path === '') {
805 805
 					// if root is gone it means the storage is not available
806
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
806
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
807 807
 				}
808 808
 				return false;
809 809
 			}
@@ -838,19 +838,19 @@  discard block
 block discarded – undo
838 838
 			}
839 839
 			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
840 840
 				// either password was changed or was invalid all along
841
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
841
+				throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
842 842
 			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
843 843
 				// ignore exception for MethodNotAllowed, false will be returned
844 844
 				return;
845 845
 			}
846
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
846
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
847 847
 		} else if ($e instanceof ClientException) {
848 848
 			// connection timeout or refused, server could be temporarily down
849
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
849
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
850 850
 		} else if ($e instanceof \InvalidArgumentException) {
851 851
 			// parse error because the server returned HTML instead of XML,
852 852
 			// possibly temporarily down
853
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
853
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
854 854
 		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
855 855
 			// rethrow
856 856
 			throw $e;
Please login to merge, or discard this patch.
lib/private/Files/Storage/Temporary.php 2 patches
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -29,20 +29,20 @@
 block discarded – undo
29 29
  * local storage backend in temporary folder for testing purpose
30 30
  */
31 31
 class Temporary extends Local{
32
-	public function __construct($arguments = null) {
33
-		parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder()));
34
-	}
32
+    public function __construct($arguments = null) {
33
+        parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder()));
34
+    }
35 35
 
36
-	public function cleanUp() {
37
-		\OC_Helper::rmdirr($this->datadir);
38
-	}
36
+    public function cleanUp() {
37
+        \OC_Helper::rmdirr($this->datadir);
38
+    }
39 39
 
40
-	public function __destruct() {
41
-		parent::__destruct();
42
-		$this->cleanUp();
43
-	}
40
+    public function __destruct() {
41
+        parent::__destruct();
42
+        $this->cleanUp();
43
+    }
44 44
 
45
-	public function getDataDir() {
46
-		return $this->datadir;
47
-	}
45
+    public function getDataDir() {
46
+        return $this->datadir;
47
+    }
48 48
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -28,7 +28,7 @@
 block discarded – undo
28 28
 /**
29 29
  * local storage backend in temporary folder for testing purpose
30 30
  */
31
-class Temporary extends Local{
31
+class Temporary extends Local {
32 32
 	public function __construct($arguments = null) {
33 33
 		parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder()));
34 34
 	}
Please login to merge, or discard this patch.
lib/private/Files/Storage/Local.php 3 patches
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -58,7 +58,7 @@  discard block
 block discarded – undo
58 58
 		if ($this->datadir === '/') {
59 59
 			$this->realDataDir = $this->datadir;
60 60
 		} else {
61
-			$this->realDataDir = rtrim(realpath($this->datadir), '/') . '/';
61
+			$this->realDataDir = rtrim(realpath($this->datadir), '/').'/';
62 62
 		}
63 63
 		if (substr($this->datadir, -1) !== '/') {
64 64
 			$this->datadir .= '/';
@@ -70,7 +70,7 @@  discard block
 block discarded – undo
70 70
 	}
71 71
 
72 72
 	public function getId() {
73
-		return 'local::' . $this->datadir;
73
+		return 'local::'.$this->datadir;
74 74
 	}
75 75
 
76 76
 	public function mkdir($path) {
@@ -228,17 +228,17 @@  discard block
 block discarded – undo
228 228
 		$dstParent = dirname($path2);
229 229
 
230 230
 		if (!$this->isUpdatable($srcParent)) {
231
-			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OCP\Util::ERROR);
231
+			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : '.$srcParent, \OCP\Util::ERROR);
232 232
 			return false;
233 233
 		}
234 234
 
235 235
 		if (!$this->isUpdatable($dstParent)) {
236
-			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OCP\Util::ERROR);
236
+			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : '.$dstParent, \OCP\Util::ERROR);
237 237
 			return false;
238 238
 		}
239 239
 
240 240
 		if (!$this->file_exists($path1)) {
241
-			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, \OCP\Util::ERROR);
241
+			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : '.$path1, \OCP\Util::ERROR);
242 242
 			return false;
243 243
 		}
244 244
 
@@ -319,13 +319,13 @@  discard block
 block discarded – undo
319 319
 		foreach (scandir($physicalDir) as $item) {
320 320
 			if (\OC\Files\Filesystem::isIgnoredDir($item))
321 321
 				continue;
322
-			$physicalItem = $physicalDir . '/' . $item;
322
+			$physicalItem = $physicalDir.'/'.$item;
323 323
 
324 324
 			if (strstr(strtolower($item), strtolower($query)) !== false) {
325
-				$files[] = $dir . '/' . $item;
325
+				$files[] = $dir.'/'.$item;
326 326
 			}
327 327
 			if (is_dir($physicalItem)) {
328
-				$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
328
+				$files = array_merge($files, $this->searchInDir($query, $dir.'/'.$item));
329 329
 			}
330 330
 		}
331 331
 		return $files;
@@ -354,7 +354,7 @@  discard block
 block discarded – undo
354 354
 	 * @throws ForbiddenException
355 355
 	 */
356 356
 	public function getSourcePath($path) {
357
-		$fullPath = $this->datadir . $path;
357
+		$fullPath = $this->datadir.$path;
358 358
 		if ($this->allowSymlinks || $path === '') {
359 359
 			return $fullPath;
360 360
 		}
@@ -365,7 +365,7 @@  discard block
 block discarded – undo
365 365
 			$realPath = realpath($pathToResolve);
366 366
 		}
367 367
 		if ($realPath) {
368
-			$realPath = $realPath . '/';
368
+			$realPath = $realPath.'/';
369 369
 		}
370 370
 		if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
371 371
 			return $fullPath;
@@ -391,9 +391,9 @@  discard block
 block discarded – undo
391 391
 		if ($this->is_file($path)) {
392 392
 			$stat = $this->stat($path);
393 393
 			return md5(
394
-				$stat['mtime'] .
395
-				$stat['ino'] .
396
-				$stat['dev'] .
394
+				$stat['mtime'].
395
+				$stat['ino'].
396
+				$stat['dev'].
397 397
 				$stat['size']
398 398
 			);
399 399
 		} else {
Please login to merge, or discard this patch.
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -317,8 +317,9 @@
 block discarded – undo
317 317
 		$files = array();
318 318
 		$physicalDir = $this->getSourcePath($dir);
319 319
 		foreach (scandir($physicalDir) as $item) {
320
-			if (\OC\Files\Filesystem::isIgnoredDir($item))
321
-				continue;
320
+			if (\OC\Files\Filesystem::isIgnoredDir($item)) {
321
+							continue;
322
+			}
322 323
 			$physicalItem = $physicalDir . '/' . $item;
323 324
 
324 325
 			if (strstr(strtolower($item), strtolower($query)) !== false) {
Please login to merge, or discard this patch.
Indentation   +409 added lines, -409 removed lines patch added patch discarded remove patch
@@ -43,413 +43,413 @@
 block discarded – undo
43 43
  * for local filestore, we only have to map the paths
44 44
  */
45 45
 class Local extends \OC\Files\Storage\Common {
46
-	protected $datadir;
47
-
48
-	protected $dataDirLength;
49
-
50
-	protected $allowSymlinks = false;
51
-
52
-	protected $realDataDir;
53
-
54
-	public function __construct($arguments) {
55
-		if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
56
-			throw new \InvalidArgumentException('No data directory set for local storage');
57
-		}
58
-		$this->datadir = $arguments['datadir'];
59
-		// some crazy code uses a local storage on root...
60
-		if ($this->datadir === '/') {
61
-			$this->realDataDir = $this->datadir;
62
-		} else {
63
-			$this->realDataDir = rtrim(realpath($this->datadir), '/') . '/';
64
-		}
65
-		if (substr($this->datadir, -1) !== '/') {
66
-			$this->datadir .= '/';
67
-		}
68
-		$this->dataDirLength = strlen($this->realDataDir);
69
-	}
70
-
71
-	public function __destruct() {
72
-	}
73
-
74
-	public function getId() {
75
-		return 'local::' . $this->datadir;
76
-	}
77
-
78
-	public function mkdir($path) {
79
-		return @mkdir($this->getSourcePath($path), 0777, true);
80
-	}
81
-
82
-	public function rmdir($path) {
83
-		if (!$this->isDeletable($path)) {
84
-			return false;
85
-		}
86
-		try {
87
-			$it = new \RecursiveIteratorIterator(
88
-				new \RecursiveDirectoryIterator($this->getSourcePath($path)),
89
-				\RecursiveIteratorIterator::CHILD_FIRST
90
-			);
91
-			/**
92
-			 * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
93
-			 * This bug is fixed in PHP 5.5.9 or before
94
-			 * See #8376
95
-			 */
96
-			$it->rewind();
97
-			while ($it->valid()) {
98
-				/**
99
-				 * @var \SplFileInfo $file
100
-				 */
101
-				$file = $it->current();
102
-				if (in_array($file->getBasename(), array('.', '..'))) {
103
-					$it->next();
104
-					continue;
105
-				} elseif ($file->isDir()) {
106
-					rmdir($file->getPathname());
107
-				} elseif ($file->isFile() || $file->isLink()) {
108
-					unlink($file->getPathname());
109
-				}
110
-				$it->next();
111
-			}
112
-			return rmdir($this->getSourcePath($path));
113
-		} catch (\UnexpectedValueException $e) {
114
-			return false;
115
-		}
116
-	}
117
-
118
-	public function opendir($path) {
119
-		return opendir($this->getSourcePath($path));
120
-	}
121
-
122
-	public function is_dir($path) {
123
-		if (substr($path, -1) == '/') {
124
-			$path = substr($path, 0, -1);
125
-		}
126
-		return is_dir($this->getSourcePath($path));
127
-	}
128
-
129
-	public function is_file($path) {
130
-		return is_file($this->getSourcePath($path));
131
-	}
132
-
133
-	public function stat($path) {
134
-		clearstatcache();
135
-		$fullPath = $this->getSourcePath($path);
136
-		$statResult = stat($fullPath);
137
-		if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
138
-			$filesize = $this->filesize($path);
139
-			$statResult['size'] = $filesize;
140
-			$statResult[7] = $filesize;
141
-		}
142
-		return $statResult;
143
-	}
144
-
145
-	public function filetype($path) {
146
-		$filetype = filetype($this->getSourcePath($path));
147
-		if ($filetype == 'link') {
148
-			$filetype = filetype(realpath($this->getSourcePath($path)));
149
-		}
150
-		return $filetype;
151
-	}
152
-
153
-	public function filesize($path) {
154
-		if ($this->is_dir($path)) {
155
-			return 0;
156
-		}
157
-		$fullPath = $this->getSourcePath($path);
158
-		if (PHP_INT_SIZE === 4) {
159
-			$helper = new \OC\LargeFileHelper;
160
-			return $helper->getFileSize($fullPath);
161
-		}
162
-		return filesize($fullPath);
163
-	}
164
-
165
-	public function isReadable($path) {
166
-		return is_readable($this->getSourcePath($path));
167
-	}
168
-
169
-	public function isUpdatable($path) {
170
-		return is_writable($this->getSourcePath($path));
171
-	}
172
-
173
-	public function file_exists($path) {
174
-		return file_exists($this->getSourcePath($path));
175
-	}
176
-
177
-	public function filemtime($path) {
178
-		$fullPath = $this->getSourcePath($path);
179
-		clearstatcache($fullPath);
180
-		if (!$this->file_exists($path)) {
181
-			return false;
182
-		}
183
-		if (PHP_INT_SIZE === 4) {
184
-			$helper = new \OC\LargeFileHelper();
185
-			return $helper->getFileMtime($fullPath);
186
-		}
187
-		return filemtime($fullPath);
188
-	}
189
-
190
-	public function touch($path, $mtime = null) {
191
-		// sets the modification time of the file to the given value.
192
-		// If mtime is nil the current time is set.
193
-		// note that the access time of the file always changes to the current time.
194
-		if ($this->file_exists($path) and !$this->isUpdatable($path)) {
195
-			return false;
196
-		}
197
-		if (!is_null($mtime)) {
198
-			$result = touch($this->getSourcePath($path), $mtime);
199
-		} else {
200
-			$result = touch($this->getSourcePath($path));
201
-		}
202
-		if ($result) {
203
-			clearstatcache(true, $this->getSourcePath($path));
204
-		}
205
-
206
-		return $result;
207
-	}
208
-
209
-	public function file_get_contents($path) {
210
-		return file_get_contents($this->getSourcePath($path));
211
-	}
212
-
213
-	public function file_put_contents($path, $data) {
214
-		return file_put_contents($this->getSourcePath($path), $data);
215
-	}
216
-
217
-	public function unlink($path) {
218
-		if ($this->is_dir($path)) {
219
-			return $this->rmdir($path);
220
-		} else if ($this->is_file($path)) {
221
-			return unlink($this->getSourcePath($path));
222
-		} else {
223
-			return false;
224
-		}
225
-
226
-	}
227
-
228
-	public function rename($path1, $path2) {
229
-		$srcParent = dirname($path1);
230
-		$dstParent = dirname($path2);
231
-
232
-		if (!$this->isUpdatable($srcParent)) {
233
-			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OCP\Util::ERROR);
234
-			return false;
235
-		}
236
-
237
-		if (!$this->isUpdatable($dstParent)) {
238
-			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OCP\Util::ERROR);
239
-			return false;
240
-		}
241
-
242
-		if (!$this->file_exists($path1)) {
243
-			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, \OCP\Util::ERROR);
244
-			return false;
245
-		}
246
-
247
-		if ($this->is_dir($path2)) {
248
-			$this->rmdir($path2);
249
-		} else if ($this->is_file($path2)) {
250
-			$this->unlink($path2);
251
-		}
252
-
253
-		if ($this->is_dir($path1)) {
254
-			// we can't move folders across devices, use copy instead
255
-			$stat1 = stat(dirname($this->getSourcePath($path1)));
256
-			$stat2 = stat(dirname($this->getSourcePath($path2)));
257
-			if ($stat1['dev'] !== $stat2['dev']) {
258
-				$result = $this->copy($path1, $path2);
259
-				if ($result) {
260
-					$result &= $this->rmdir($path1);
261
-				}
262
-				return $result;
263
-			}
264
-		}
265
-
266
-		return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
267
-	}
268
-
269
-	public function copy($path1, $path2) {
270
-		if ($this->is_dir($path1)) {
271
-			return parent::copy($path1, $path2);
272
-		} else {
273
-			return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
274
-		}
275
-	}
276
-
277
-	public function fopen($path, $mode) {
278
-		return fopen($this->getSourcePath($path), $mode);
279
-	}
280
-
281
-	public function hash($type, $path, $raw = false) {
282
-		return hash_file($type, $this->getSourcePath($path), $raw);
283
-	}
284
-
285
-	public function free_space($path) {
286
-		$sourcePath = $this->getSourcePath($path);
287
-		// using !is_dir because $sourcePath might be a part file or
288
-		// non-existing file, so we'd still want to use the parent dir
289
-		// in such cases
290
-		if (!is_dir($sourcePath)) {
291
-			// disk_free_space doesn't work on files
292
-			$sourcePath = dirname($sourcePath);
293
-		}
294
-		$space = @disk_free_space($sourcePath);
295
-		if ($space === false || is_null($space)) {
296
-			return \OCP\Files\FileInfo::SPACE_UNKNOWN;
297
-		}
298
-		return $space;
299
-	}
300
-
301
-	public function search($query) {
302
-		return $this->searchInDir($query);
303
-	}
304
-
305
-	public function getLocalFile($path) {
306
-		return $this->getSourcePath($path);
307
-	}
308
-
309
-	public function getLocalFolder($path) {
310
-		return $this->getSourcePath($path);
311
-	}
312
-
313
-	/**
314
-	 * @param string $query
315
-	 * @param string $dir
316
-	 * @return array
317
-	 */
318
-	protected function searchInDir($query, $dir = '') {
319
-		$files = array();
320
-		$physicalDir = $this->getSourcePath($dir);
321
-		foreach (scandir($physicalDir) as $item) {
322
-			if (\OC\Files\Filesystem::isIgnoredDir($item))
323
-				continue;
324
-			$physicalItem = $physicalDir . '/' . $item;
325
-
326
-			if (strstr(strtolower($item), strtolower($query)) !== false) {
327
-				$files[] = $dir . '/' . $item;
328
-			}
329
-			if (is_dir($physicalItem)) {
330
-				$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
331
-			}
332
-		}
333
-		return $files;
334
-	}
335
-
336
-	/**
337
-	 * check if a file or folder has been updated since $time
338
-	 *
339
-	 * @param string $path
340
-	 * @param int $time
341
-	 * @return bool
342
-	 */
343
-	public function hasUpdated($path, $time) {
344
-		if ($this->file_exists($path)) {
345
-			return $this->filemtime($path) > $time;
346
-		} else {
347
-			return true;
348
-		}
349
-	}
350
-
351
-	/**
352
-	 * Get the source path (on disk) of a given path
353
-	 *
354
-	 * @param string $path
355
-	 * @return string
356
-	 * @throws ForbiddenException
357
-	 */
358
-	public function getSourcePath($path) {
359
-		$fullPath = $this->datadir . $path;
360
-		if ($this->allowSymlinks || $path === '') {
361
-			return $fullPath;
362
-		}
363
-		$pathToResolve = $fullPath;
364
-		$realPath = realpath($pathToResolve);
365
-		while ($realPath === false) { // for non existing files check the parent directory
366
-			$pathToResolve = dirname($pathToResolve);
367
-			$realPath = realpath($pathToResolve);
368
-		}
369
-		if ($realPath) {
370
-			$realPath = $realPath . '/';
371
-		}
372
-		if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
373
-			return $fullPath;
374
-		}
375
-
376
-		\OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", \OCP\Util::ERROR);
377
-		throw new ForbiddenException('Following symlinks is not allowed', false);
378
-	}
379
-
380
-	/**
381
-	 * {@inheritdoc}
382
-	 */
383
-	public function isLocal() {
384
-		return true;
385
-	}
386
-
387
-	/**
388
-	 * get the ETag for a file or folder
389
-	 *
390
-	 * @param string $path
391
-	 * @return string
392
-	 */
393
-	public function getETag($path) {
394
-		if ($this->is_file($path)) {
395
-			$stat = $this->stat($path);
396
-			return md5(
397
-				$stat['mtime'] .
398
-				$stat['ino'] .
399
-				$stat['dev'] .
400
-				$stat['size']
401
-			);
402
-		} else {
403
-			return parent::getETag($path);
404
-		}
405
-	}
406
-
407
-	/**
408
-	 * @param IStorage $sourceStorage
409
-	 * @param string $sourceInternalPath
410
-	 * @param string $targetInternalPath
411
-	 * @param bool $preserveMtime
412
-	 * @return bool
413
-	 */
414
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
415
-		if ($sourceStorage->instanceOfStorage(Local::class)) {
416
-			if ($sourceStorage->instanceOfStorage(Jail::class)) {
417
-				/**
418
-				 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
419
-				 */
420
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
421
-			}
422
-			/**
423
-			 * @var \OC\Files\Storage\Local $sourceStorage
424
-			 */
425
-			$rootStorage = new Local(['datadir' => '/']);
426
-			return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
427
-		} else {
428
-			return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
429
-		}
430
-	}
431
-
432
-	/**
433
-	 * @param IStorage $sourceStorage
434
-	 * @param string $sourceInternalPath
435
-	 * @param string $targetInternalPath
436
-	 * @return bool
437
-	 */
438
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
439
-		if ($sourceStorage->instanceOfStorage(Local::class)) {
440
-			if ($sourceStorage->instanceOfStorage(Jail::class)) {
441
-				/**
442
-				 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
443
-				 */
444
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
445
-			}
446
-			/**
447
-			 * @var \OC\Files\Storage\Local $sourceStorage
448
-			 */
449
-			$rootStorage = new Local(['datadir' => '/']);
450
-			return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
451
-		} else {
452
-			return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
453
-		}
454
-	}
46
+    protected $datadir;
47
+
48
+    protected $dataDirLength;
49
+
50
+    protected $allowSymlinks = false;
51
+
52
+    protected $realDataDir;
53
+
54
+    public function __construct($arguments) {
55
+        if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
56
+            throw new \InvalidArgumentException('No data directory set for local storage');
57
+        }
58
+        $this->datadir = $arguments['datadir'];
59
+        // some crazy code uses a local storage on root...
60
+        if ($this->datadir === '/') {
61
+            $this->realDataDir = $this->datadir;
62
+        } else {
63
+            $this->realDataDir = rtrim(realpath($this->datadir), '/') . '/';
64
+        }
65
+        if (substr($this->datadir, -1) !== '/') {
66
+            $this->datadir .= '/';
67
+        }
68
+        $this->dataDirLength = strlen($this->realDataDir);
69
+    }
70
+
71
+    public function __destruct() {
72
+    }
73
+
74
+    public function getId() {
75
+        return 'local::' . $this->datadir;
76
+    }
77
+
78
+    public function mkdir($path) {
79
+        return @mkdir($this->getSourcePath($path), 0777, true);
80
+    }
81
+
82
+    public function rmdir($path) {
83
+        if (!$this->isDeletable($path)) {
84
+            return false;
85
+        }
86
+        try {
87
+            $it = new \RecursiveIteratorIterator(
88
+                new \RecursiveDirectoryIterator($this->getSourcePath($path)),
89
+                \RecursiveIteratorIterator::CHILD_FIRST
90
+            );
91
+            /**
92
+             * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
93
+             * This bug is fixed in PHP 5.5.9 or before
94
+             * See #8376
95
+             */
96
+            $it->rewind();
97
+            while ($it->valid()) {
98
+                /**
99
+                 * @var \SplFileInfo $file
100
+                 */
101
+                $file = $it->current();
102
+                if (in_array($file->getBasename(), array('.', '..'))) {
103
+                    $it->next();
104
+                    continue;
105
+                } elseif ($file->isDir()) {
106
+                    rmdir($file->getPathname());
107
+                } elseif ($file->isFile() || $file->isLink()) {
108
+                    unlink($file->getPathname());
109
+                }
110
+                $it->next();
111
+            }
112
+            return rmdir($this->getSourcePath($path));
113
+        } catch (\UnexpectedValueException $e) {
114
+            return false;
115
+        }
116
+    }
117
+
118
+    public function opendir($path) {
119
+        return opendir($this->getSourcePath($path));
120
+    }
121
+
122
+    public function is_dir($path) {
123
+        if (substr($path, -1) == '/') {
124
+            $path = substr($path, 0, -1);
125
+        }
126
+        return is_dir($this->getSourcePath($path));
127
+    }
128
+
129
+    public function is_file($path) {
130
+        return is_file($this->getSourcePath($path));
131
+    }
132
+
133
+    public function stat($path) {
134
+        clearstatcache();
135
+        $fullPath = $this->getSourcePath($path);
136
+        $statResult = stat($fullPath);
137
+        if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
138
+            $filesize = $this->filesize($path);
139
+            $statResult['size'] = $filesize;
140
+            $statResult[7] = $filesize;
141
+        }
142
+        return $statResult;
143
+    }
144
+
145
+    public function filetype($path) {
146
+        $filetype = filetype($this->getSourcePath($path));
147
+        if ($filetype == 'link') {
148
+            $filetype = filetype(realpath($this->getSourcePath($path)));
149
+        }
150
+        return $filetype;
151
+    }
152
+
153
+    public function filesize($path) {
154
+        if ($this->is_dir($path)) {
155
+            return 0;
156
+        }
157
+        $fullPath = $this->getSourcePath($path);
158
+        if (PHP_INT_SIZE === 4) {
159
+            $helper = new \OC\LargeFileHelper;
160
+            return $helper->getFileSize($fullPath);
161
+        }
162
+        return filesize($fullPath);
163
+    }
164
+
165
+    public function isReadable($path) {
166
+        return is_readable($this->getSourcePath($path));
167
+    }
168
+
169
+    public function isUpdatable($path) {
170
+        return is_writable($this->getSourcePath($path));
171
+    }
172
+
173
+    public function file_exists($path) {
174
+        return file_exists($this->getSourcePath($path));
175
+    }
176
+
177
+    public function filemtime($path) {
178
+        $fullPath = $this->getSourcePath($path);
179
+        clearstatcache($fullPath);
180
+        if (!$this->file_exists($path)) {
181
+            return false;
182
+        }
183
+        if (PHP_INT_SIZE === 4) {
184
+            $helper = new \OC\LargeFileHelper();
185
+            return $helper->getFileMtime($fullPath);
186
+        }
187
+        return filemtime($fullPath);
188
+    }
189
+
190
+    public function touch($path, $mtime = null) {
191
+        // sets the modification time of the file to the given value.
192
+        // If mtime is nil the current time is set.
193
+        // note that the access time of the file always changes to the current time.
194
+        if ($this->file_exists($path) and !$this->isUpdatable($path)) {
195
+            return false;
196
+        }
197
+        if (!is_null($mtime)) {
198
+            $result = touch($this->getSourcePath($path), $mtime);
199
+        } else {
200
+            $result = touch($this->getSourcePath($path));
201
+        }
202
+        if ($result) {
203
+            clearstatcache(true, $this->getSourcePath($path));
204
+        }
205
+
206
+        return $result;
207
+    }
208
+
209
+    public function file_get_contents($path) {
210
+        return file_get_contents($this->getSourcePath($path));
211
+    }
212
+
213
+    public function file_put_contents($path, $data) {
214
+        return file_put_contents($this->getSourcePath($path), $data);
215
+    }
216
+
217
+    public function unlink($path) {
218
+        if ($this->is_dir($path)) {
219
+            return $this->rmdir($path);
220
+        } else if ($this->is_file($path)) {
221
+            return unlink($this->getSourcePath($path));
222
+        } else {
223
+            return false;
224
+        }
225
+
226
+    }
227
+
228
+    public function rename($path1, $path2) {
229
+        $srcParent = dirname($path1);
230
+        $dstParent = dirname($path2);
231
+
232
+        if (!$this->isUpdatable($srcParent)) {
233
+            \OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OCP\Util::ERROR);
234
+            return false;
235
+        }
236
+
237
+        if (!$this->isUpdatable($dstParent)) {
238
+            \OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OCP\Util::ERROR);
239
+            return false;
240
+        }
241
+
242
+        if (!$this->file_exists($path1)) {
243
+            \OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, \OCP\Util::ERROR);
244
+            return false;
245
+        }
246
+
247
+        if ($this->is_dir($path2)) {
248
+            $this->rmdir($path2);
249
+        } else if ($this->is_file($path2)) {
250
+            $this->unlink($path2);
251
+        }
252
+
253
+        if ($this->is_dir($path1)) {
254
+            // we can't move folders across devices, use copy instead
255
+            $stat1 = stat(dirname($this->getSourcePath($path1)));
256
+            $stat2 = stat(dirname($this->getSourcePath($path2)));
257
+            if ($stat1['dev'] !== $stat2['dev']) {
258
+                $result = $this->copy($path1, $path2);
259
+                if ($result) {
260
+                    $result &= $this->rmdir($path1);
261
+                }
262
+                return $result;
263
+            }
264
+        }
265
+
266
+        return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
267
+    }
268
+
269
+    public function copy($path1, $path2) {
270
+        if ($this->is_dir($path1)) {
271
+            return parent::copy($path1, $path2);
272
+        } else {
273
+            return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
274
+        }
275
+    }
276
+
277
+    public function fopen($path, $mode) {
278
+        return fopen($this->getSourcePath($path), $mode);
279
+    }
280
+
281
+    public function hash($type, $path, $raw = false) {
282
+        return hash_file($type, $this->getSourcePath($path), $raw);
283
+    }
284
+
285
+    public function free_space($path) {
286
+        $sourcePath = $this->getSourcePath($path);
287
+        // using !is_dir because $sourcePath might be a part file or
288
+        // non-existing file, so we'd still want to use the parent dir
289
+        // in such cases
290
+        if (!is_dir($sourcePath)) {
291
+            // disk_free_space doesn't work on files
292
+            $sourcePath = dirname($sourcePath);
293
+        }
294
+        $space = @disk_free_space($sourcePath);
295
+        if ($space === false || is_null($space)) {
296
+            return \OCP\Files\FileInfo::SPACE_UNKNOWN;
297
+        }
298
+        return $space;
299
+    }
300
+
301
+    public function search($query) {
302
+        return $this->searchInDir($query);
303
+    }
304
+
305
+    public function getLocalFile($path) {
306
+        return $this->getSourcePath($path);
307
+    }
308
+
309
+    public function getLocalFolder($path) {
310
+        return $this->getSourcePath($path);
311
+    }
312
+
313
+    /**
314
+     * @param string $query
315
+     * @param string $dir
316
+     * @return array
317
+     */
318
+    protected function searchInDir($query, $dir = '') {
319
+        $files = array();
320
+        $physicalDir = $this->getSourcePath($dir);
321
+        foreach (scandir($physicalDir) as $item) {
322
+            if (\OC\Files\Filesystem::isIgnoredDir($item))
323
+                continue;
324
+            $physicalItem = $physicalDir . '/' . $item;
325
+
326
+            if (strstr(strtolower($item), strtolower($query)) !== false) {
327
+                $files[] = $dir . '/' . $item;
328
+            }
329
+            if (is_dir($physicalItem)) {
330
+                $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
331
+            }
332
+        }
333
+        return $files;
334
+    }
335
+
336
+    /**
337
+     * check if a file or folder has been updated since $time
338
+     *
339
+     * @param string $path
340
+     * @param int $time
341
+     * @return bool
342
+     */
343
+    public function hasUpdated($path, $time) {
344
+        if ($this->file_exists($path)) {
345
+            return $this->filemtime($path) > $time;
346
+        } else {
347
+            return true;
348
+        }
349
+    }
350
+
351
+    /**
352
+     * Get the source path (on disk) of a given path
353
+     *
354
+     * @param string $path
355
+     * @return string
356
+     * @throws ForbiddenException
357
+     */
358
+    public function getSourcePath($path) {
359
+        $fullPath = $this->datadir . $path;
360
+        if ($this->allowSymlinks || $path === '') {
361
+            return $fullPath;
362
+        }
363
+        $pathToResolve = $fullPath;
364
+        $realPath = realpath($pathToResolve);
365
+        while ($realPath === false) { // for non existing files check the parent directory
366
+            $pathToResolve = dirname($pathToResolve);
367
+            $realPath = realpath($pathToResolve);
368
+        }
369
+        if ($realPath) {
370
+            $realPath = $realPath . '/';
371
+        }
372
+        if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
373
+            return $fullPath;
374
+        }
375
+
376
+        \OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", \OCP\Util::ERROR);
377
+        throw new ForbiddenException('Following symlinks is not allowed', false);
378
+    }
379
+
380
+    /**
381
+     * {@inheritdoc}
382
+     */
383
+    public function isLocal() {
384
+        return true;
385
+    }
386
+
387
+    /**
388
+     * get the ETag for a file or folder
389
+     *
390
+     * @param string $path
391
+     * @return string
392
+     */
393
+    public function getETag($path) {
394
+        if ($this->is_file($path)) {
395
+            $stat = $this->stat($path);
396
+            return md5(
397
+                $stat['mtime'] .
398
+                $stat['ino'] .
399
+                $stat['dev'] .
400
+                $stat['size']
401
+            );
402
+        } else {
403
+            return parent::getETag($path);
404
+        }
405
+    }
406
+
407
+    /**
408
+     * @param IStorage $sourceStorage
409
+     * @param string $sourceInternalPath
410
+     * @param string $targetInternalPath
411
+     * @param bool $preserveMtime
412
+     * @return bool
413
+     */
414
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
415
+        if ($sourceStorage->instanceOfStorage(Local::class)) {
416
+            if ($sourceStorage->instanceOfStorage(Jail::class)) {
417
+                /**
418
+                 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
419
+                 */
420
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
421
+            }
422
+            /**
423
+             * @var \OC\Files\Storage\Local $sourceStorage
424
+             */
425
+            $rootStorage = new Local(['datadir' => '/']);
426
+            return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
427
+        } else {
428
+            return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
429
+        }
430
+    }
431
+
432
+    /**
433
+     * @param IStorage $sourceStorage
434
+     * @param string $sourceInternalPath
435
+     * @param string $targetInternalPath
436
+     * @return bool
437
+     */
438
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
439
+        if ($sourceStorage->instanceOfStorage(Local::class)) {
440
+            if ($sourceStorage->instanceOfStorage(Jail::class)) {
441
+                /**
442
+                 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
443
+                 */
444
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
445
+            }
446
+            /**
447
+             * @var \OC\Files\Storage\Local $sourceStorage
448
+             */
449
+            $rootStorage = new Local(['datadir' => '/']);
450
+            return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
451
+        } else {
452
+            return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
453
+        }
454
+    }
455 455
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/LocalTempFileTrait.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -37,45 +37,45 @@
 block discarded – undo
37 37
  */
38 38
 trait LocalTempFileTrait {
39 39
 
40
-	/** @var string[] */
41
-	protected $cachedFiles = [];
40
+    /** @var string[] */
41
+    protected $cachedFiles = [];
42 42
 
43
-	/**
44
-	 * @param string $path
45
-	 * @return string
46
-	 */
47
-	protected function getCachedFile($path) {
48
-		if (!isset($this->cachedFiles[$path])) {
49
-			$this->cachedFiles[$path] = $this->toTmpFile($path);
50
-		}
51
-		return $this->cachedFiles[$path];
52
-	}
43
+    /**
44
+     * @param string $path
45
+     * @return string
46
+     */
47
+    protected function getCachedFile($path) {
48
+        if (!isset($this->cachedFiles[$path])) {
49
+            $this->cachedFiles[$path] = $this->toTmpFile($path);
50
+        }
51
+        return $this->cachedFiles[$path];
52
+    }
53 53
 
54
-	/**
55
-	 * @param string $path
56
-	 */
57
-	protected function removeCachedFile($path) {
58
-		unset($this->cachedFiles[$path]);
59
-	}
54
+    /**
55
+     * @param string $path
56
+     */
57
+    protected function removeCachedFile($path) {
58
+        unset($this->cachedFiles[$path]);
59
+    }
60 60
 
61
-	/**
62
-	 * @param string $path
63
-	 * @return string
64
-	 */
65
-	protected function toTmpFile($path) { //no longer in the storage api, still useful here
66
-		$source = $this->fopen($path, 'r');
67
-		if (!$source) {
68
-			return false;
69
-		}
70
-		if ($pos = strrpos($path, '.')) {
71
-			$extension = substr($path, $pos);
72
-		} else {
73
-			$extension = '';
74
-		}
75
-		$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
76
-		$target = fopen($tmpFile, 'w');
77
-		\OC_Helper::streamCopy($source, $target);
78
-		fclose($target);
79
-		return $tmpFile;
80
-	}
61
+    /**
62
+     * @param string $path
63
+     * @return string
64
+     */
65
+    protected function toTmpFile($path) { //no longer in the storage api, still useful here
66
+        $source = $this->fopen($path, 'r');
67
+        if (!$source) {
68
+            return false;
69
+        }
70
+        if ($pos = strrpos($path, '.')) {
71
+            $extension = substr($path, $pos);
72
+        } else {
73
+            $extension = '';
74
+        }
75
+        $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
76
+        $target = fopen($tmpFile, 'w');
77
+        \OC_Helper::streamCopy($source, $target);
78
+        fclose($target);
79
+        return $tmpFile;
80
+    }
81 81
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/Common.php 3 patches
Braces   +3 added lines, -1 removed lines patch added patch discarded remove patch
@@ -289,7 +289,9 @@
 block discarded – undo
289 289
 		$dh = $this->opendir($dir);
290 290
 		if (is_resource($dh)) {
291 291
 			while (($item = readdir($dh)) !== false) {
292
-				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
292
+				if (\OC\Files\Filesystem::isIgnoredDir($item)) {
293
+				    continue;
294
+				}
293 295
 				if (strstr(strtolower($item), strtolower($query)) !== false) {
294 296
 					$files[] = $dir . '/' . $item;
295 297
 				}
Please login to merge, or discard this patch.
Indentation   +731 added lines, -731 removed lines patch added patch discarded remove patch
@@ -70,739 +70,739 @@
 block discarded – undo
70 70
  */
71 71
 abstract class Common implements Storage, ILockingStorage {
72 72
 
73
-	use LocalTempFileTrait;
74
-
75
-	protected $cache;
76
-	protected $scanner;
77
-	protected $watcher;
78
-	protected $propagator;
79
-	protected $storageCache;
80
-	protected $updater;
81
-
82
-	protected $mountOptions = [];
83
-	protected $owner = null;
84
-
85
-	private $shouldLogLocks = null;
86
-	private $logger;
87
-
88
-	public function __construct($parameters) {
89
-	}
90
-
91
-	/**
92
-	 * Remove a file or folder
93
-	 *
94
-	 * @param string $path
95
-	 * @return bool
96
-	 */
97
-	protected function remove($path) {
98
-		if ($this->is_dir($path)) {
99
-			return $this->rmdir($path);
100
-		} else if ($this->is_file($path)) {
101
-			return $this->unlink($path);
102
-		} else {
103
-			return false;
104
-		}
105
-	}
106
-
107
-	public function is_dir($path) {
108
-		return $this->filetype($path) === 'dir';
109
-	}
110
-
111
-	public function is_file($path) {
112
-		return $this->filetype($path) === 'file';
113
-	}
114
-
115
-	public function filesize($path) {
116
-		if ($this->is_dir($path)) {
117
-			return 0; //by definition
118
-		} else {
119
-			$stat = $this->stat($path);
120
-			if (isset($stat['size'])) {
121
-				return $stat['size'];
122
-			} else {
123
-				return 0;
124
-			}
125
-		}
126
-	}
127
-
128
-	public function isReadable($path) {
129
-		// at least check whether it exists
130
-		// subclasses might want to implement this more thoroughly
131
-		return $this->file_exists($path);
132
-	}
133
-
134
-	public function isUpdatable($path) {
135
-		// at least check whether it exists
136
-		// subclasses might want to implement this more thoroughly
137
-		// a non-existing file/folder isn't updatable
138
-		return $this->file_exists($path);
139
-	}
140
-
141
-	public function isCreatable($path) {
142
-		if ($this->is_dir($path) && $this->isUpdatable($path)) {
143
-			return true;
144
-		}
145
-		return false;
146
-	}
147
-
148
-	public function isDeletable($path) {
149
-		if ($path === '' || $path === '/') {
150
-			return false;
151
-		}
152
-		$parent = dirname($path);
153
-		return $this->isUpdatable($parent) && $this->isUpdatable($path);
154
-	}
155
-
156
-	public function isSharable($path) {
157
-		return $this->isReadable($path);
158
-	}
159
-
160
-	public function getPermissions($path) {
161
-		$permissions = 0;
162
-		if ($this->isCreatable($path)) {
163
-			$permissions |= \OCP\Constants::PERMISSION_CREATE;
164
-		}
165
-		if ($this->isReadable($path)) {
166
-			$permissions |= \OCP\Constants::PERMISSION_READ;
167
-		}
168
-		if ($this->isUpdatable($path)) {
169
-			$permissions |= \OCP\Constants::PERMISSION_UPDATE;
170
-		}
171
-		if ($this->isDeletable($path)) {
172
-			$permissions |= \OCP\Constants::PERMISSION_DELETE;
173
-		}
174
-		if ($this->isSharable($path)) {
175
-			$permissions |= \OCP\Constants::PERMISSION_SHARE;
176
-		}
177
-		return $permissions;
178
-	}
179
-
180
-	public function filemtime($path) {
181
-		$stat = $this->stat($path);
182
-		if (isset($stat['mtime']) && $stat['mtime'] > 0) {
183
-			return $stat['mtime'];
184
-		} else {
185
-			return 0;
186
-		}
187
-	}
188
-
189
-	public function file_get_contents($path) {
190
-		$handle = $this->fopen($path, "r");
191
-		if (!$handle) {
192
-			return false;
193
-		}
194
-		$data = stream_get_contents($handle);
195
-		fclose($handle);
196
-		return $data;
197
-	}
198
-
199
-	public function file_put_contents($path, $data) {
200
-		$handle = $this->fopen($path, "w");
201
-		$this->removeCachedFile($path);
202
-		$count = fwrite($handle, $data);
203
-		fclose($handle);
204
-		return $count;
205
-	}
206
-
207
-	public function rename($path1, $path2) {
208
-		$this->remove($path2);
209
-
210
-		$this->removeCachedFile($path1);
211
-		return $this->copy($path1, $path2) and $this->remove($path1);
212
-	}
213
-
214
-	public function copy($path1, $path2) {
215
-		if ($this->is_dir($path1)) {
216
-			$this->remove($path2);
217
-			$dir = $this->opendir($path1);
218
-			$this->mkdir($path2);
219
-			while ($file = readdir($dir)) {
220
-				if (!Filesystem::isIgnoredDir($file)) {
221
-					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
222
-						return false;
223
-					}
224
-				}
225
-			}
226
-			closedir($dir);
227
-			return true;
228
-		} else {
229
-			$source = $this->fopen($path1, 'r');
230
-			$target = $this->fopen($path2, 'w');
231
-			list(, $result) = \OC_Helper::streamCopy($source, $target);
232
-			if (!$result) {
233
-				\OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
234
-			}
235
-			$this->removeCachedFile($path2);
236
-			return $result;
237
-		}
238
-	}
239
-
240
-	public function getMimeType($path) {
241
-		if ($this->is_dir($path)) {
242
-			return 'httpd/unix-directory';
243
-		} elseif ($this->file_exists($path)) {
244
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
245
-		} else {
246
-			return false;
247
-		}
248
-	}
249
-
250
-	public function hash($type, $path, $raw = false) {
251
-		$fh = $this->fopen($path, 'rb');
252
-		$ctx = hash_init($type);
253
-		hash_update_stream($ctx, $fh);
254
-		fclose($fh);
255
-		return hash_final($ctx, $raw);
256
-	}
257
-
258
-	public function search($query) {
259
-		return $this->searchInDir($query);
260
-	}
261
-
262
-	public function getLocalFile($path) {
263
-		return $this->getCachedFile($path);
264
-	}
265
-
266
-	/**
267
-	 * @param string $path
268
-	 * @param string $target
269
-	 */
270
-	private function addLocalFolder($path, $target) {
271
-		$dh = $this->opendir($path);
272
-		if (is_resource($dh)) {
273
-			while (($file = readdir($dh)) !== false) {
274
-				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
275
-					if ($this->is_dir($path . '/' . $file)) {
276
-						mkdir($target . '/' . $file);
277
-						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
278
-					} else {
279
-						$tmp = $this->toTmpFile($path . '/' . $file);
280
-						rename($tmp, $target . '/' . $file);
281
-					}
282
-				}
283
-			}
284
-		}
285
-	}
286
-
287
-	/**
288
-	 * @param string $query
289
-	 * @param string $dir
290
-	 * @return array
291
-	 */
292
-	protected function searchInDir($query, $dir = '') {
293
-		$files = array();
294
-		$dh = $this->opendir($dir);
295
-		if (is_resource($dh)) {
296
-			while (($item = readdir($dh)) !== false) {
297
-				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
298
-				if (strstr(strtolower($item), strtolower($query)) !== false) {
299
-					$files[] = $dir . '/' . $item;
300
-				}
301
-				if ($this->is_dir($dir . '/' . $item)) {
302
-					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
303
-				}
304
-			}
305
-		}
306
-		closedir($dh);
307
-		return $files;
308
-	}
309
-
310
-	/**
311
-	 * check if a file or folder has been updated since $time
312
-	 *
313
-	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
314
-	 * the mtime should always return false here. As a result storage implementations that always return false expect
315
-	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
316
-	 * ownClouds filesystem.
317
-	 *
318
-	 * @param string $path
319
-	 * @param int $time
320
-	 * @return bool
321
-	 */
322
-	public function hasUpdated($path, $time) {
323
-		return $this->filemtime($path) > $time;
324
-	}
325
-
326
-	public function getCache($path = '', $storage = null) {
327
-		if (!$storage) {
328
-			$storage = $this;
329
-		}
330
-		if (!isset($storage->cache)) {
331
-			$storage->cache = new Cache($storage);
332
-		}
333
-		return $storage->cache;
334
-	}
335
-
336
-	public function getScanner($path = '', $storage = null) {
337
-		if (!$storage) {
338
-			$storage = $this;
339
-		}
340
-		if (!isset($storage->scanner)) {
341
-			$storage->scanner = new Scanner($storage);
342
-		}
343
-		return $storage->scanner;
344
-	}
345
-
346
-	public function getWatcher($path = '', $storage = null) {
347
-		if (!$storage) {
348
-			$storage = $this;
349
-		}
350
-		if (!isset($this->watcher)) {
351
-			$this->watcher = new Watcher($storage);
352
-			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
353
-			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
354
-		}
355
-		return $this->watcher;
356
-	}
357
-
358
-	/**
359
-	 * get a propagator instance for the cache
360
-	 *
361
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
362
-	 * @return \OC\Files\Cache\Propagator
363
-	 */
364
-	public function getPropagator($storage = null) {
365
-		if (!$storage) {
366
-			$storage = $this;
367
-		}
368
-		if (!isset($storage->propagator)) {
369
-			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection());
370
-		}
371
-		return $storage->propagator;
372
-	}
373
-
374
-	public function getUpdater($storage = null) {
375
-		if (!$storage) {
376
-			$storage = $this;
377
-		}
378
-		if (!isset($storage->updater)) {
379
-			$storage->updater = new Updater($storage);
380
-		}
381
-		return $storage->updater;
382
-	}
383
-
384
-	public function getStorageCache($storage = null) {
385
-		if (!$storage) {
386
-			$storage = $this;
387
-		}
388
-		if (!isset($this->storageCache)) {
389
-			$this->storageCache = new \OC\Files\Cache\Storage($storage);
390
-		}
391
-		return $this->storageCache;
392
-	}
393
-
394
-	/**
395
-	 * get the owner of a path
396
-	 *
397
-	 * @param string $path The path to get the owner
398
-	 * @return string|false uid or false
399
-	 */
400
-	public function getOwner($path) {
401
-		if ($this->owner === null) {
402
-			$this->owner = \OC_User::getUser();
403
-		}
404
-
405
-		return $this->owner;
406
-	}
407
-
408
-	/**
409
-	 * get the ETag for a file or folder
410
-	 *
411
-	 * @param string $path
412
-	 * @return string
413
-	 */
414
-	public function getETag($path) {
415
-		return uniqid();
416
-	}
417
-
418
-	/**
419
-	 * clean a path, i.e. remove all redundant '.' and '..'
420
-	 * making sure that it can't point to higher than '/'
421
-	 *
422
-	 * @param string $path The path to clean
423
-	 * @return string cleaned path
424
-	 */
425
-	public function cleanPath($path) {
426
-		if (strlen($path) == 0 or $path[0] != '/') {
427
-			$path = '/' . $path;
428
-		}
429
-
430
-		$output = array();
431
-		foreach (explode('/', $path) as $chunk) {
432
-			if ($chunk == '..') {
433
-				array_pop($output);
434
-			} else if ($chunk == '.') {
435
-			} else {
436
-				$output[] = $chunk;
437
-			}
438
-		}
439
-		return implode('/', $output);
440
-	}
441
-
442
-	/**
443
-	 * Test a storage for availability
444
-	 *
445
-	 * @return bool
446
-	 */
447
-	public function test() {
448
-		try {
449
-			if ($this->stat('')) {
450
-				return true;
451
-			}
452
-			return false;
453
-		} catch (\Exception $e) {
454
-			return false;
455
-		}
456
-	}
457
-
458
-	/**
459
-	 * get the free space in the storage
460
-	 *
461
-	 * @param string $path
462
-	 * @return int|false
463
-	 */
464
-	public function free_space($path) {
465
-		return \OCP\Files\FileInfo::SPACE_UNKNOWN;
466
-	}
467
-
468
-	/**
469
-	 * {@inheritdoc}
470
-	 */
471
-	public function isLocal() {
472
-		// the common implementation returns a temporary file by
473
-		// default, which is not local
474
-		return false;
475
-	}
476
-
477
-	/**
478
-	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
479
-	 *
480
-	 * @param string $class
481
-	 * @return bool
482
-	 */
483
-	public function instanceOfStorage($class) {
484
-		if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
485
-			// FIXME Temporary fix to keep existing checks working
486
-			$class = '\OCA\Files_Sharing\SharedStorage';
487
-		}
488
-		return is_a($this, $class);
489
-	}
490
-
491
-	/**
492
-	 * A custom storage implementation can return an url for direct download of a give file.
493
-	 *
494
-	 * For now the returned array can hold the parameter url - in future more attributes might follow.
495
-	 *
496
-	 * @param string $path
497
-	 * @return array|false
498
-	 */
499
-	public function getDirectDownload($path) {
500
-		return [];
501
-	}
502
-
503
-	/**
504
-	 * @inheritdoc
505
-	 * @throws InvalidPathException
506
-	 */
507
-	public function verifyPath($path, $fileName) {
508
-
509
-		// verify empty and dot files
510
-		$trimmed = trim($fileName);
511
-		if ($trimmed === '') {
512
-			throw new EmptyFileNameException();
513
-		}
514
-
515
-		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
516
-			throw new InvalidDirectoryException();
517
-		}
518
-
519
-		if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
520
-			// verify database - e.g. mysql only 3-byte chars
521
-			if (preg_match('%(?:
73
+    use LocalTempFileTrait;
74
+
75
+    protected $cache;
76
+    protected $scanner;
77
+    protected $watcher;
78
+    protected $propagator;
79
+    protected $storageCache;
80
+    protected $updater;
81
+
82
+    protected $mountOptions = [];
83
+    protected $owner = null;
84
+
85
+    private $shouldLogLocks = null;
86
+    private $logger;
87
+
88
+    public function __construct($parameters) {
89
+    }
90
+
91
+    /**
92
+     * Remove a file or folder
93
+     *
94
+     * @param string $path
95
+     * @return bool
96
+     */
97
+    protected function remove($path) {
98
+        if ($this->is_dir($path)) {
99
+            return $this->rmdir($path);
100
+        } else if ($this->is_file($path)) {
101
+            return $this->unlink($path);
102
+        } else {
103
+            return false;
104
+        }
105
+    }
106
+
107
+    public function is_dir($path) {
108
+        return $this->filetype($path) === 'dir';
109
+    }
110
+
111
+    public function is_file($path) {
112
+        return $this->filetype($path) === 'file';
113
+    }
114
+
115
+    public function filesize($path) {
116
+        if ($this->is_dir($path)) {
117
+            return 0; //by definition
118
+        } else {
119
+            $stat = $this->stat($path);
120
+            if (isset($stat['size'])) {
121
+                return $stat['size'];
122
+            } else {
123
+                return 0;
124
+            }
125
+        }
126
+    }
127
+
128
+    public function isReadable($path) {
129
+        // at least check whether it exists
130
+        // subclasses might want to implement this more thoroughly
131
+        return $this->file_exists($path);
132
+    }
133
+
134
+    public function isUpdatable($path) {
135
+        // at least check whether it exists
136
+        // subclasses might want to implement this more thoroughly
137
+        // a non-existing file/folder isn't updatable
138
+        return $this->file_exists($path);
139
+    }
140
+
141
+    public function isCreatable($path) {
142
+        if ($this->is_dir($path) && $this->isUpdatable($path)) {
143
+            return true;
144
+        }
145
+        return false;
146
+    }
147
+
148
+    public function isDeletable($path) {
149
+        if ($path === '' || $path === '/') {
150
+            return false;
151
+        }
152
+        $parent = dirname($path);
153
+        return $this->isUpdatable($parent) && $this->isUpdatable($path);
154
+    }
155
+
156
+    public function isSharable($path) {
157
+        return $this->isReadable($path);
158
+    }
159
+
160
+    public function getPermissions($path) {
161
+        $permissions = 0;
162
+        if ($this->isCreatable($path)) {
163
+            $permissions |= \OCP\Constants::PERMISSION_CREATE;
164
+        }
165
+        if ($this->isReadable($path)) {
166
+            $permissions |= \OCP\Constants::PERMISSION_READ;
167
+        }
168
+        if ($this->isUpdatable($path)) {
169
+            $permissions |= \OCP\Constants::PERMISSION_UPDATE;
170
+        }
171
+        if ($this->isDeletable($path)) {
172
+            $permissions |= \OCP\Constants::PERMISSION_DELETE;
173
+        }
174
+        if ($this->isSharable($path)) {
175
+            $permissions |= \OCP\Constants::PERMISSION_SHARE;
176
+        }
177
+        return $permissions;
178
+    }
179
+
180
+    public function filemtime($path) {
181
+        $stat = $this->stat($path);
182
+        if (isset($stat['mtime']) && $stat['mtime'] > 0) {
183
+            return $stat['mtime'];
184
+        } else {
185
+            return 0;
186
+        }
187
+    }
188
+
189
+    public function file_get_contents($path) {
190
+        $handle = $this->fopen($path, "r");
191
+        if (!$handle) {
192
+            return false;
193
+        }
194
+        $data = stream_get_contents($handle);
195
+        fclose($handle);
196
+        return $data;
197
+    }
198
+
199
+    public function file_put_contents($path, $data) {
200
+        $handle = $this->fopen($path, "w");
201
+        $this->removeCachedFile($path);
202
+        $count = fwrite($handle, $data);
203
+        fclose($handle);
204
+        return $count;
205
+    }
206
+
207
+    public function rename($path1, $path2) {
208
+        $this->remove($path2);
209
+
210
+        $this->removeCachedFile($path1);
211
+        return $this->copy($path1, $path2) and $this->remove($path1);
212
+    }
213
+
214
+    public function copy($path1, $path2) {
215
+        if ($this->is_dir($path1)) {
216
+            $this->remove($path2);
217
+            $dir = $this->opendir($path1);
218
+            $this->mkdir($path2);
219
+            while ($file = readdir($dir)) {
220
+                if (!Filesystem::isIgnoredDir($file)) {
221
+                    if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
222
+                        return false;
223
+                    }
224
+                }
225
+            }
226
+            closedir($dir);
227
+            return true;
228
+        } else {
229
+            $source = $this->fopen($path1, 'r');
230
+            $target = $this->fopen($path2, 'w');
231
+            list(, $result) = \OC_Helper::streamCopy($source, $target);
232
+            if (!$result) {
233
+                \OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
234
+            }
235
+            $this->removeCachedFile($path2);
236
+            return $result;
237
+        }
238
+    }
239
+
240
+    public function getMimeType($path) {
241
+        if ($this->is_dir($path)) {
242
+            return 'httpd/unix-directory';
243
+        } elseif ($this->file_exists($path)) {
244
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
245
+        } else {
246
+            return false;
247
+        }
248
+    }
249
+
250
+    public function hash($type, $path, $raw = false) {
251
+        $fh = $this->fopen($path, 'rb');
252
+        $ctx = hash_init($type);
253
+        hash_update_stream($ctx, $fh);
254
+        fclose($fh);
255
+        return hash_final($ctx, $raw);
256
+    }
257
+
258
+    public function search($query) {
259
+        return $this->searchInDir($query);
260
+    }
261
+
262
+    public function getLocalFile($path) {
263
+        return $this->getCachedFile($path);
264
+    }
265
+
266
+    /**
267
+     * @param string $path
268
+     * @param string $target
269
+     */
270
+    private function addLocalFolder($path, $target) {
271
+        $dh = $this->opendir($path);
272
+        if (is_resource($dh)) {
273
+            while (($file = readdir($dh)) !== false) {
274
+                if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
275
+                    if ($this->is_dir($path . '/' . $file)) {
276
+                        mkdir($target . '/' . $file);
277
+                        $this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
278
+                    } else {
279
+                        $tmp = $this->toTmpFile($path . '/' . $file);
280
+                        rename($tmp, $target . '/' . $file);
281
+                    }
282
+                }
283
+            }
284
+        }
285
+    }
286
+
287
+    /**
288
+     * @param string $query
289
+     * @param string $dir
290
+     * @return array
291
+     */
292
+    protected function searchInDir($query, $dir = '') {
293
+        $files = array();
294
+        $dh = $this->opendir($dir);
295
+        if (is_resource($dh)) {
296
+            while (($item = readdir($dh)) !== false) {
297
+                if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
298
+                if (strstr(strtolower($item), strtolower($query)) !== false) {
299
+                    $files[] = $dir . '/' . $item;
300
+                }
301
+                if ($this->is_dir($dir . '/' . $item)) {
302
+                    $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
303
+                }
304
+            }
305
+        }
306
+        closedir($dh);
307
+        return $files;
308
+    }
309
+
310
+    /**
311
+     * check if a file or folder has been updated since $time
312
+     *
313
+     * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
314
+     * the mtime should always return false here. As a result storage implementations that always return false expect
315
+     * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
316
+     * ownClouds filesystem.
317
+     *
318
+     * @param string $path
319
+     * @param int $time
320
+     * @return bool
321
+     */
322
+    public function hasUpdated($path, $time) {
323
+        return $this->filemtime($path) > $time;
324
+    }
325
+
326
+    public function getCache($path = '', $storage = null) {
327
+        if (!$storage) {
328
+            $storage = $this;
329
+        }
330
+        if (!isset($storage->cache)) {
331
+            $storage->cache = new Cache($storage);
332
+        }
333
+        return $storage->cache;
334
+    }
335
+
336
+    public function getScanner($path = '', $storage = null) {
337
+        if (!$storage) {
338
+            $storage = $this;
339
+        }
340
+        if (!isset($storage->scanner)) {
341
+            $storage->scanner = new Scanner($storage);
342
+        }
343
+        return $storage->scanner;
344
+    }
345
+
346
+    public function getWatcher($path = '', $storage = null) {
347
+        if (!$storage) {
348
+            $storage = $this;
349
+        }
350
+        if (!isset($this->watcher)) {
351
+            $this->watcher = new Watcher($storage);
352
+            $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
353
+            $this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
354
+        }
355
+        return $this->watcher;
356
+    }
357
+
358
+    /**
359
+     * get a propagator instance for the cache
360
+     *
361
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
362
+     * @return \OC\Files\Cache\Propagator
363
+     */
364
+    public function getPropagator($storage = null) {
365
+        if (!$storage) {
366
+            $storage = $this;
367
+        }
368
+        if (!isset($storage->propagator)) {
369
+            $storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection());
370
+        }
371
+        return $storage->propagator;
372
+    }
373
+
374
+    public function getUpdater($storage = null) {
375
+        if (!$storage) {
376
+            $storage = $this;
377
+        }
378
+        if (!isset($storage->updater)) {
379
+            $storage->updater = new Updater($storage);
380
+        }
381
+        return $storage->updater;
382
+    }
383
+
384
+    public function getStorageCache($storage = null) {
385
+        if (!$storage) {
386
+            $storage = $this;
387
+        }
388
+        if (!isset($this->storageCache)) {
389
+            $this->storageCache = new \OC\Files\Cache\Storage($storage);
390
+        }
391
+        return $this->storageCache;
392
+    }
393
+
394
+    /**
395
+     * get the owner of a path
396
+     *
397
+     * @param string $path The path to get the owner
398
+     * @return string|false uid or false
399
+     */
400
+    public function getOwner($path) {
401
+        if ($this->owner === null) {
402
+            $this->owner = \OC_User::getUser();
403
+        }
404
+
405
+        return $this->owner;
406
+    }
407
+
408
+    /**
409
+     * get the ETag for a file or folder
410
+     *
411
+     * @param string $path
412
+     * @return string
413
+     */
414
+    public function getETag($path) {
415
+        return uniqid();
416
+    }
417
+
418
+    /**
419
+     * clean a path, i.e. remove all redundant '.' and '..'
420
+     * making sure that it can't point to higher than '/'
421
+     *
422
+     * @param string $path The path to clean
423
+     * @return string cleaned path
424
+     */
425
+    public function cleanPath($path) {
426
+        if (strlen($path) == 0 or $path[0] != '/') {
427
+            $path = '/' . $path;
428
+        }
429
+
430
+        $output = array();
431
+        foreach (explode('/', $path) as $chunk) {
432
+            if ($chunk == '..') {
433
+                array_pop($output);
434
+            } else if ($chunk == '.') {
435
+            } else {
436
+                $output[] = $chunk;
437
+            }
438
+        }
439
+        return implode('/', $output);
440
+    }
441
+
442
+    /**
443
+     * Test a storage for availability
444
+     *
445
+     * @return bool
446
+     */
447
+    public function test() {
448
+        try {
449
+            if ($this->stat('')) {
450
+                return true;
451
+            }
452
+            return false;
453
+        } catch (\Exception $e) {
454
+            return false;
455
+        }
456
+    }
457
+
458
+    /**
459
+     * get the free space in the storage
460
+     *
461
+     * @param string $path
462
+     * @return int|false
463
+     */
464
+    public function free_space($path) {
465
+        return \OCP\Files\FileInfo::SPACE_UNKNOWN;
466
+    }
467
+
468
+    /**
469
+     * {@inheritdoc}
470
+     */
471
+    public function isLocal() {
472
+        // the common implementation returns a temporary file by
473
+        // default, which is not local
474
+        return false;
475
+    }
476
+
477
+    /**
478
+     * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
479
+     *
480
+     * @param string $class
481
+     * @return bool
482
+     */
483
+    public function instanceOfStorage($class) {
484
+        if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
485
+            // FIXME Temporary fix to keep existing checks working
486
+            $class = '\OCA\Files_Sharing\SharedStorage';
487
+        }
488
+        return is_a($this, $class);
489
+    }
490
+
491
+    /**
492
+     * A custom storage implementation can return an url for direct download of a give file.
493
+     *
494
+     * For now the returned array can hold the parameter url - in future more attributes might follow.
495
+     *
496
+     * @param string $path
497
+     * @return array|false
498
+     */
499
+    public function getDirectDownload($path) {
500
+        return [];
501
+    }
502
+
503
+    /**
504
+     * @inheritdoc
505
+     * @throws InvalidPathException
506
+     */
507
+    public function verifyPath($path, $fileName) {
508
+
509
+        // verify empty and dot files
510
+        $trimmed = trim($fileName);
511
+        if ($trimmed === '') {
512
+            throw new EmptyFileNameException();
513
+        }
514
+
515
+        if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
516
+            throw new InvalidDirectoryException();
517
+        }
518
+
519
+        if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
520
+            // verify database - e.g. mysql only 3-byte chars
521
+            if (preg_match('%(?:
522 522
       \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
523 523
     | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
524 524
     | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
525 525
 )%xs', $fileName)) {
526
-				throw new InvalidCharacterInPathException();
527
-			}
528
-		}
529
-
530
-		if (isset($fileName[255])) {
531
-			throw new FileNameTooLongException();
532
-		}
533
-
534
-		// NOTE: $path will remain unverified for now
535
-		$this->verifyPosixPath($fileName);
536
-	}
537
-
538
-	/**
539
-	 * @param string $fileName
540
-	 * @throws InvalidPathException
541
-	 */
542
-	protected function verifyPosixPath($fileName) {
543
-		$fileName = trim($fileName);
544
-		$this->scanForInvalidCharacters($fileName, "\\/");
545
-		$reservedNames = ['*'];
546
-		if (in_array($fileName, $reservedNames)) {
547
-			throw new ReservedWordException();
548
-		}
549
-	}
550
-
551
-	/**
552
-	 * @param string $fileName
553
-	 * @param string $invalidChars
554
-	 * @throws InvalidPathException
555
-	 */
556
-	private function scanForInvalidCharacters($fileName, $invalidChars) {
557
-		foreach (str_split($invalidChars) as $char) {
558
-			if (strpos($fileName, $char) !== false) {
559
-				throw new InvalidCharacterInPathException();
560
-			}
561
-		}
562
-
563
-		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
564
-		if ($sanitizedFileName !== $fileName) {
565
-			throw new InvalidCharacterInPathException();
566
-		}
567
-	}
568
-
569
-	/**
570
-	 * @param array $options
571
-	 */
572
-	public function setMountOptions(array $options) {
573
-		$this->mountOptions = $options;
574
-	}
575
-
576
-	/**
577
-	 * @param string $name
578
-	 * @param mixed $default
579
-	 * @return mixed
580
-	 */
581
-	public function getMountOption($name, $default = null) {
582
-		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
583
-	}
584
-
585
-	/**
586
-	 * @param IStorage $sourceStorage
587
-	 * @param string $sourceInternalPath
588
-	 * @param string $targetInternalPath
589
-	 * @param bool $preserveMtime
590
-	 * @return bool
591
-	 */
592
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
593
-		if ($sourceStorage === $this) {
594
-			return $this->copy($sourceInternalPath, $targetInternalPath);
595
-		}
596
-
597
-		if ($sourceStorage->is_dir($sourceInternalPath)) {
598
-			$dh = $sourceStorage->opendir($sourceInternalPath);
599
-			$result = $this->mkdir($targetInternalPath);
600
-			if (is_resource($dh)) {
601
-				while ($result and ($file = readdir($dh)) !== false) {
602
-					if (!Filesystem::isIgnoredDir($file)) {
603
-						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
604
-					}
605
-				}
606
-			}
607
-		} else {
608
-			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
609
-			// TODO: call fopen in a way that we execute again all storage wrappers
610
-			// to avoid that we bypass storage wrappers which perform important actions
611
-			// for this operation. Same is true for all other operations which
612
-			// are not the same as the original one.Once this is fixed we also
613
-			// need to adjust the encryption wrapper.
614
-			$target = $this->fopen($targetInternalPath, 'w');
615
-			list(, $result) = \OC_Helper::streamCopy($source, $target);
616
-			if ($result and $preserveMtime) {
617
-				$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
618
-			}
619
-			fclose($source);
620
-			fclose($target);
621
-
622
-			if (!$result) {
623
-				// delete partially written target file
624
-				$this->unlink($targetInternalPath);
625
-				// delete cache entry that was created by fopen
626
-				$this->getCache()->remove($targetInternalPath);
627
-			}
628
-		}
629
-		return (bool)$result;
630
-	}
631
-
632
-	/**
633
-	 * @param IStorage $sourceStorage
634
-	 * @param string $sourceInternalPath
635
-	 * @param string $targetInternalPath
636
-	 * @return bool
637
-	 */
638
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
639
-		if ($sourceStorage === $this) {
640
-			return $this->rename($sourceInternalPath, $targetInternalPath);
641
-		}
642
-
643
-		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
644
-			return false;
645
-		}
646
-
647
-		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
648
-		if ($result) {
649
-			if ($sourceStorage->is_dir($sourceInternalPath)) {
650
-				$result &= $sourceStorage->rmdir($sourceInternalPath);
651
-			} else {
652
-				$result &= $sourceStorage->unlink($sourceInternalPath);
653
-			}
654
-		}
655
-		return $result;
656
-	}
657
-
658
-	/**
659
-	 * @inheritdoc
660
-	 */
661
-	public function getMetaData($path) {
662
-		$permissions = $this->getPermissions($path);
663
-		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
664
-			//can't read, nothing we can do
665
-			return null;
666
-		}
667
-
668
-		$data = [];
669
-		$data['mimetype'] = $this->getMimeType($path);
670
-		$data['mtime'] = $this->filemtime($path);
671
-		if ($data['mtime'] === false) {
672
-			$data['mtime'] = time();
673
-		}
674
-		if ($data['mimetype'] == 'httpd/unix-directory') {
675
-			$data['size'] = -1; //unknown
676
-		} else {
677
-			$data['size'] = $this->filesize($path);
678
-		}
679
-		$data['etag'] = $this->getETag($path);
680
-		$data['storage_mtime'] = $data['mtime'];
681
-		$data['permissions'] = $permissions;
682
-
683
-		return $data;
684
-	}
685
-
686
-	/**
687
-	 * @param string $path
688
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
689
-	 * @param \OCP\Lock\ILockingProvider $provider
690
-	 * @throws \OCP\Lock\LockedException
691
-	 */
692
-	public function acquireLock($path, $type, ILockingProvider $provider) {
693
-		$logger = $this->getLockLogger();
694
-		if ($logger) {
695
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
696
-			$logger->info(
697
-				sprintf(
698
-					'acquire %s lock on "%s" on storage "%s"',
699
-					$typeString,
700
-					$path,
701
-					$this->getId()
702
-				),
703
-				[
704
-					'app' => 'locking',
705
-				]
706
-			);
707
-		}
708
-		try {
709
-			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
710
-		} catch (LockedException $e) {
711
-			if ($logger) {
712
-				$logger->logException($e);
713
-			}
714
-			throw $e;
715
-		}
716
-	}
717
-
718
-	/**
719
-	 * @param string $path
720
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
721
-	 * @param \OCP\Lock\ILockingProvider $provider
722
-	 * @throws \OCP\Lock\LockedException
723
-	 */
724
-	public function releaseLock($path, $type, ILockingProvider $provider) {
725
-		$logger = $this->getLockLogger();
726
-		if ($logger) {
727
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
728
-			$logger->info(
729
-				sprintf(
730
-					'release %s lock on "%s" on storage "%s"',
731
-					$typeString,
732
-					$path,
733
-					$this->getId()
734
-				),
735
-				[
736
-					'app' => 'locking',
737
-				]
738
-			);
739
-		}
740
-		try {
741
-			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
742
-		} catch (LockedException $e) {
743
-			if ($logger) {
744
-				$logger->logException($e);
745
-			}
746
-			throw $e;
747
-		}
748
-	}
749
-
750
-	/**
751
-	 * @param string $path
752
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
753
-	 * @param \OCP\Lock\ILockingProvider $provider
754
-	 * @throws \OCP\Lock\LockedException
755
-	 */
756
-	public function changeLock($path, $type, ILockingProvider $provider) {
757
-		$logger = $this->getLockLogger();
758
-		if ($logger) {
759
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
760
-			$logger->info(
761
-				sprintf(
762
-					'change lock on "%s" to %s on storage "%s"',
763
-					$path,
764
-					$typeString,
765
-					$this->getId()
766
-				),
767
-				[
768
-					'app' => 'locking',
769
-				]
770
-			);
771
-		}
772
-		try {
773
-			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
774
-		} catch (LockedException $e) {
775
-			\OC::$server->getLogger()->logException($e);
776
-			throw $e;
777
-		}
778
-	}
779
-
780
-	private function getLockLogger() {
781
-		if (is_null($this->shouldLogLocks)) {
782
-			$this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
783
-			$this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
784
-		}
785
-		return $this->logger;
786
-	}
787
-
788
-	/**
789
-	 * @return array [ available, last_checked ]
790
-	 */
791
-	public function getAvailability() {
792
-		return $this->getStorageCache()->getAvailability();
793
-	}
794
-
795
-	/**
796
-	 * @param bool $isAvailable
797
-	 */
798
-	public function setAvailability($isAvailable) {
799
-		$this->getStorageCache()->setAvailability($isAvailable);
800
-	}
801
-
802
-	/**
803
-	 * @return bool
804
-	 */
805
-	public function needsPartFile() {
806
-		return true;
807
-	}
526
+                throw new InvalidCharacterInPathException();
527
+            }
528
+        }
529
+
530
+        if (isset($fileName[255])) {
531
+            throw new FileNameTooLongException();
532
+        }
533
+
534
+        // NOTE: $path will remain unverified for now
535
+        $this->verifyPosixPath($fileName);
536
+    }
537
+
538
+    /**
539
+     * @param string $fileName
540
+     * @throws InvalidPathException
541
+     */
542
+    protected function verifyPosixPath($fileName) {
543
+        $fileName = trim($fileName);
544
+        $this->scanForInvalidCharacters($fileName, "\\/");
545
+        $reservedNames = ['*'];
546
+        if (in_array($fileName, $reservedNames)) {
547
+            throw new ReservedWordException();
548
+        }
549
+    }
550
+
551
+    /**
552
+     * @param string $fileName
553
+     * @param string $invalidChars
554
+     * @throws InvalidPathException
555
+     */
556
+    private function scanForInvalidCharacters($fileName, $invalidChars) {
557
+        foreach (str_split($invalidChars) as $char) {
558
+            if (strpos($fileName, $char) !== false) {
559
+                throw new InvalidCharacterInPathException();
560
+            }
561
+        }
562
+
563
+        $sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
564
+        if ($sanitizedFileName !== $fileName) {
565
+            throw new InvalidCharacterInPathException();
566
+        }
567
+    }
568
+
569
+    /**
570
+     * @param array $options
571
+     */
572
+    public function setMountOptions(array $options) {
573
+        $this->mountOptions = $options;
574
+    }
575
+
576
+    /**
577
+     * @param string $name
578
+     * @param mixed $default
579
+     * @return mixed
580
+     */
581
+    public function getMountOption($name, $default = null) {
582
+        return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
583
+    }
584
+
585
+    /**
586
+     * @param IStorage $sourceStorage
587
+     * @param string $sourceInternalPath
588
+     * @param string $targetInternalPath
589
+     * @param bool $preserveMtime
590
+     * @return bool
591
+     */
592
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
593
+        if ($sourceStorage === $this) {
594
+            return $this->copy($sourceInternalPath, $targetInternalPath);
595
+        }
596
+
597
+        if ($sourceStorage->is_dir($sourceInternalPath)) {
598
+            $dh = $sourceStorage->opendir($sourceInternalPath);
599
+            $result = $this->mkdir($targetInternalPath);
600
+            if (is_resource($dh)) {
601
+                while ($result and ($file = readdir($dh)) !== false) {
602
+                    if (!Filesystem::isIgnoredDir($file)) {
603
+                        $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
604
+                    }
605
+                }
606
+            }
607
+        } else {
608
+            $source = $sourceStorage->fopen($sourceInternalPath, 'r');
609
+            // TODO: call fopen in a way that we execute again all storage wrappers
610
+            // to avoid that we bypass storage wrappers which perform important actions
611
+            // for this operation. Same is true for all other operations which
612
+            // are not the same as the original one.Once this is fixed we also
613
+            // need to adjust the encryption wrapper.
614
+            $target = $this->fopen($targetInternalPath, 'w');
615
+            list(, $result) = \OC_Helper::streamCopy($source, $target);
616
+            if ($result and $preserveMtime) {
617
+                $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
618
+            }
619
+            fclose($source);
620
+            fclose($target);
621
+
622
+            if (!$result) {
623
+                // delete partially written target file
624
+                $this->unlink($targetInternalPath);
625
+                // delete cache entry that was created by fopen
626
+                $this->getCache()->remove($targetInternalPath);
627
+            }
628
+        }
629
+        return (bool)$result;
630
+    }
631
+
632
+    /**
633
+     * @param IStorage $sourceStorage
634
+     * @param string $sourceInternalPath
635
+     * @param string $targetInternalPath
636
+     * @return bool
637
+     */
638
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
639
+        if ($sourceStorage === $this) {
640
+            return $this->rename($sourceInternalPath, $targetInternalPath);
641
+        }
642
+
643
+        if (!$sourceStorage->isDeletable($sourceInternalPath)) {
644
+            return false;
645
+        }
646
+
647
+        $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
648
+        if ($result) {
649
+            if ($sourceStorage->is_dir($sourceInternalPath)) {
650
+                $result &= $sourceStorage->rmdir($sourceInternalPath);
651
+            } else {
652
+                $result &= $sourceStorage->unlink($sourceInternalPath);
653
+            }
654
+        }
655
+        return $result;
656
+    }
657
+
658
+    /**
659
+     * @inheritdoc
660
+     */
661
+    public function getMetaData($path) {
662
+        $permissions = $this->getPermissions($path);
663
+        if (!$permissions & \OCP\Constants::PERMISSION_READ) {
664
+            //can't read, nothing we can do
665
+            return null;
666
+        }
667
+
668
+        $data = [];
669
+        $data['mimetype'] = $this->getMimeType($path);
670
+        $data['mtime'] = $this->filemtime($path);
671
+        if ($data['mtime'] === false) {
672
+            $data['mtime'] = time();
673
+        }
674
+        if ($data['mimetype'] == 'httpd/unix-directory') {
675
+            $data['size'] = -1; //unknown
676
+        } else {
677
+            $data['size'] = $this->filesize($path);
678
+        }
679
+        $data['etag'] = $this->getETag($path);
680
+        $data['storage_mtime'] = $data['mtime'];
681
+        $data['permissions'] = $permissions;
682
+
683
+        return $data;
684
+    }
685
+
686
+    /**
687
+     * @param string $path
688
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
689
+     * @param \OCP\Lock\ILockingProvider $provider
690
+     * @throws \OCP\Lock\LockedException
691
+     */
692
+    public function acquireLock($path, $type, ILockingProvider $provider) {
693
+        $logger = $this->getLockLogger();
694
+        if ($logger) {
695
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
696
+            $logger->info(
697
+                sprintf(
698
+                    'acquire %s lock on "%s" on storage "%s"',
699
+                    $typeString,
700
+                    $path,
701
+                    $this->getId()
702
+                ),
703
+                [
704
+                    'app' => 'locking',
705
+                ]
706
+            );
707
+        }
708
+        try {
709
+            $provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
710
+        } catch (LockedException $e) {
711
+            if ($logger) {
712
+                $logger->logException($e);
713
+            }
714
+            throw $e;
715
+        }
716
+    }
717
+
718
+    /**
719
+     * @param string $path
720
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
721
+     * @param \OCP\Lock\ILockingProvider $provider
722
+     * @throws \OCP\Lock\LockedException
723
+     */
724
+    public function releaseLock($path, $type, ILockingProvider $provider) {
725
+        $logger = $this->getLockLogger();
726
+        if ($logger) {
727
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
728
+            $logger->info(
729
+                sprintf(
730
+                    'release %s lock on "%s" on storage "%s"',
731
+                    $typeString,
732
+                    $path,
733
+                    $this->getId()
734
+                ),
735
+                [
736
+                    'app' => 'locking',
737
+                ]
738
+            );
739
+        }
740
+        try {
741
+            $provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
742
+        } catch (LockedException $e) {
743
+            if ($logger) {
744
+                $logger->logException($e);
745
+            }
746
+            throw $e;
747
+        }
748
+    }
749
+
750
+    /**
751
+     * @param string $path
752
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
753
+     * @param \OCP\Lock\ILockingProvider $provider
754
+     * @throws \OCP\Lock\LockedException
755
+     */
756
+    public function changeLock($path, $type, ILockingProvider $provider) {
757
+        $logger = $this->getLockLogger();
758
+        if ($logger) {
759
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
760
+            $logger->info(
761
+                sprintf(
762
+                    'change lock on "%s" to %s on storage "%s"',
763
+                    $path,
764
+                    $typeString,
765
+                    $this->getId()
766
+                ),
767
+                [
768
+                    'app' => 'locking',
769
+                ]
770
+            );
771
+        }
772
+        try {
773
+            $provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
774
+        } catch (LockedException $e) {
775
+            \OC::$server->getLogger()->logException($e);
776
+            throw $e;
777
+        }
778
+    }
779
+
780
+    private function getLockLogger() {
781
+        if (is_null($this->shouldLogLocks)) {
782
+            $this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
783
+            $this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
784
+        }
785
+        return $this->logger;
786
+    }
787
+
788
+    /**
789
+     * @return array [ available, last_checked ]
790
+     */
791
+    public function getAvailability() {
792
+        return $this->getStorageCache()->getAvailability();
793
+    }
794
+
795
+    /**
796
+     * @param bool $isAvailable
797
+     */
798
+    public function setAvailability($isAvailable) {
799
+        $this->getStorageCache()->setAvailability($isAvailable);
800
+    }
801
+
802
+    /**
803
+     * @return bool
804
+     */
805
+    public function needsPartFile() {
806
+        return true;
807
+    }
808 808
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
 			$this->mkdir($path2);
219 219
 			while ($file = readdir($dir)) {
220 220
 				if (!Filesystem::isIgnoredDir($file)) {
221
-					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
221
+					if (!$this->copy($path1.'/'.$file, $path2.'/'.$file)) {
222 222
 						return false;
223 223
 					}
224 224
 				}
@@ -272,12 +272,12 @@  discard block
 block discarded – undo
272 272
 		if (is_resource($dh)) {
273 273
 			while (($file = readdir($dh)) !== false) {
274 274
 				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
275
-					if ($this->is_dir($path . '/' . $file)) {
276
-						mkdir($target . '/' . $file);
277
-						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
275
+					if ($this->is_dir($path.'/'.$file)) {
276
+						mkdir($target.'/'.$file);
277
+						$this->addLocalFolder($path.'/'.$file, $target.'/'.$file);
278 278
 					} else {
279
-						$tmp = $this->toTmpFile($path . '/' . $file);
280
-						rename($tmp, $target . '/' . $file);
279
+						$tmp = $this->toTmpFile($path.'/'.$file);
280
+						rename($tmp, $target.'/'.$file);
281 281
 					}
282 282
 				}
283 283
 			}
@@ -296,10 +296,10 @@  discard block
 block discarded – undo
296 296
 			while (($item = readdir($dh)) !== false) {
297 297
 				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
298 298
 				if (strstr(strtolower($item), strtolower($query)) !== false) {
299
-					$files[] = $dir . '/' . $item;
299
+					$files[] = $dir.'/'.$item;
300 300
 				}
301
-				if ($this->is_dir($dir . '/' . $item)) {
302
-					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
301
+				if ($this->is_dir($dir.'/'.$item)) {
302
+					$files = array_merge($files, $this->searchInDir($query, $dir.'/'.$item));
303 303
 				}
304 304
 			}
305 305
 		}
@@ -350,7 +350,7 @@  discard block
 block discarded – undo
350 350
 		if (!isset($this->watcher)) {
351 351
 			$this->watcher = new Watcher($storage);
352 352
 			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
353
-			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
353
+			$this->watcher->setPolicy((int) $this->getMountOption('filesystem_check_changes', $globalPolicy));
354 354
 		}
355 355
 		return $this->watcher;
356 356
 	}
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
 	 */
425 425
 	public function cleanPath($path) {
426 426
 		if (strlen($path) == 0 or $path[0] != '/') {
427
-			$path = '/' . $path;
427
+			$path = '/'.$path;
428 428
 		}
429 429
 
430 430
 		$output = array();
@@ -600,7 +600,7 @@  discard block
 block discarded – undo
600 600
 			if (is_resource($dh)) {
601 601
 				while ($result and ($file = readdir($dh)) !== false) {
602 602
 					if (!Filesystem::isIgnoredDir($file)) {
603
-						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
603
+						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath.'/'.$file, $targetInternalPath.'/'.$file);
604 604
 					}
605 605
 				}
606 606
 			}
@@ -626,7 +626,7 @@  discard block
 block discarded – undo
626 626
 				$this->getCache()->remove($targetInternalPath);
627 627
 			}
628 628
 		}
629
-		return (bool)$result;
629
+		return (bool) $result;
630 630
 	}
631 631
 
632 632
 	/**
@@ -706,7 +706,7 @@  discard block
 block discarded – undo
706 706
 			);
707 707
 		}
708 708
 		try {
709
-			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
709
+			$provider->acquireLock('files/'.md5($this->getId().'::'.trim($path, '/')), $type);
710 710
 		} catch (LockedException $e) {
711 711
 			if ($logger) {
712 712
 				$logger->logException($e);
@@ -738,7 +738,7 @@  discard block
 block discarded – undo
738 738
 			);
739 739
 		}
740 740
 		try {
741
-			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
741
+			$provider->releaseLock('files/'.md5($this->getId().'::'.trim($path, '/')), $type);
742 742
 		} catch (LockedException $e) {
743 743
 			if ($logger) {
744 744
 				$logger->logException($e);
@@ -770,7 +770,7 @@  discard block
 block discarded – undo
770 770
 			);
771 771
 		}
772 772
 		try {
773
-			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
773
+			$provider->changeLock('files/'.md5($this->getId().'::'.trim($path, '/')), $type);
774 774
 		} catch (LockedException $e) {
775 775
 			\OC::$server->getLogger()->logException($e);
776 776
 			throw $e;
Please login to merge, or discard this patch.