~pvigo/+junk/owncloud-14.04

« back to all changes in this revision

Viewing changes to usr/share/owncloud/lib/private/files/view.php

  • Committer: Pablo Vigo
  • Date: 2014-12-15 13:36:46 UTC
  • Revision ID: pvigo@xtec.cat-20141215133646-7d6it90e1dbsijc2
2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
/**
3
 
 * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
4
 
 * This file is licensed under the Affero General Public License version 3 or
5
 
 * later.
6
 
 * See the COPYING-README file.
7
 
 */
8
 
 
9
 
/**
10
 
 * Class to provide access to ownCloud filesystem via a "view", and methods for
11
 
 * working with files within that view (e.g. read, write, delete, etc.). Each
12
 
 * view is restricted to a set of directories via a virtual root. The default view
13
 
 * uses the currently logged in user's data directory as root (parts of
14
 
 * OC_Filesystem are merely a wrapper for OC_FilesystemView).
15
 
 *
16
 
 * Apps that need to access files outside of the user data folders (to modify files
17
 
 * belonging to a user other than the one currently logged in, for example) should
18
 
 * use this class directly rather than using OC_Filesystem, or making use of PHP's
19
 
 * built-in file manipulation functions. This will ensure all hooks and proxies
20
 
 * are triggered correctly.
21
 
 *
22
 
 * Filesystem functions are not called directly; they are passed to the correct
23
 
 * \OC\Files\Storage\Storage object
24
 
 */
25
 
 
26
 
namespace OC\Files;
27
 
 
28
 
class View {
29
 
        private $fakeRoot = '';
30
 
        private $internal_path_cache = array();
31
 
        private $storage_cache = array();
32
 
 
33
 
        public function __construct($root = '') {
34
 
                $this->fakeRoot = $root;
35
 
        }
36
 
 
37
 
        public function getAbsolutePath($path = '/') {
38
 
                if (!$path) {
39
 
                        $path = '/';
40
 
                }
41
 
                if ($path[0] !== '/') {
42
 
                        $path = '/' . $path;
43
 
                }
44
 
                return $this->fakeRoot . $path;
45
 
        }
46
 
 
47
 
        /**
48
 
         * change the root to a fake root
49
 
         *
50
 
         * @param string $fakeRoot
51
 
         * @return bool
52
 
         */
53
 
        public function chroot($fakeRoot) {
54
 
                if (!$fakeRoot == '') {
55
 
                        if ($fakeRoot[0] !== '/') {
56
 
                                $fakeRoot = '/' . $fakeRoot;
57
 
                        }
58
 
                }
59
 
                $this->fakeRoot = $fakeRoot;
60
 
        }
61
 
 
62
 
        /**
63
 
         * get the fake root
64
 
         *
65
 
         * @return string
66
 
         */
67
 
        public function getRoot() {
68
 
                return $this->fakeRoot;
69
 
        }
70
 
 
71
 
        /**
72
 
         * get path relative to the root of the view
73
 
         *
74
 
         * @param string $path
75
 
         * @return string
76
 
         */
77
 
        public function getRelativePath($path) {
78
 
                if ($this->fakeRoot == '') {
79
 
                        return $path;
80
 
                }
81
 
                if (strpos($path, $this->fakeRoot) !== 0) {
82
 
                        return null;
83
 
                } else {
84
 
                        $path = substr($path, strlen($this->fakeRoot));
85
 
                        if (strlen($path) === 0) {
86
 
                                return '/';
87
 
                        } else {
88
 
                                return $path;
89
 
                        }
90
 
                }
91
 
        }
92
 
 
93
 
        /**
94
 
         * get the mountpoint of the storage object for a path
95
 
         * ( note: because a storage is not always mounted inside the fakeroot, the
96
 
         * returned mountpoint is relative to the absolute root of the filesystem
97
 
         * and doesn't take the chroot into account )
98
 
         *
99
 
         * @param string $path
100
 
         * @return string
101
 
         */
102
 
        public function getMountPoint($path) {
103
 
                return Filesystem::getMountPoint($this->getAbsolutePath($path));
104
 
        }
105
 
 
106
 
        /**
107
 
         * resolve a path to a storage and internal path
108
 
         *
109
 
         * @param string $path
110
 
         * @return array consisting of the storage and the internal path
111
 
         */
112
 
        public function resolvePath($path) {
113
 
                $a = $this->getAbsolutePath($path);
114
 
                $p = Filesystem::normalizePath($a);
115
 
                return Filesystem::resolvePath($p);
116
 
        }
117
 
 
118
 
        /**
119
 
         * return the path to a local version of the file
120
 
         * we need this because we can't know if a file is stored local or not from
121
 
         * outside the filestorage and for some purposes a local file is needed
122
 
         *
123
 
         * @param string $path
124
 
         * @return string
125
 
         */
126
 
        public function getLocalFile($path) {
127
 
                $parent = substr($path, 0, strrpos($path, '/'));
128
 
                $path = $this->getAbsolutePath($path);
129
 
                list($storage, $internalPath) = Filesystem::resolvePath($path);
130
 
                if (Filesystem::isValidPath($parent) and $storage) {
131
 
                        return $storage->getLocalFile($internalPath);
132
 
                } else {
133
 
                        return null;
134
 
                }
135
 
        }
136
 
 
137
 
        /**
138
 
         * @param string $path
139
 
         * @return string
140
 
         */
141
 
        public function getLocalFolder($path) {
142
 
                $parent = substr($path, 0, strrpos($path, '/'));
143
 
                $path = $this->getAbsolutePath($path);
144
 
                list($storage, $internalPath) = Filesystem::resolvePath($path);
145
 
                if (Filesystem::isValidPath($parent) and $storage) {
146
 
                        return $storage->getLocalFolder($internalPath);
147
 
                } else {
148
 
                        return null;
149
 
                }
150
 
        }
151
 
 
152
 
        /**
153
 
         * the following functions operate with arguments and return values identical
154
 
         * to those of their PHP built-in equivalents. Mostly they are merely wrappers
155
 
         * for \OC\Files\Storage\Storage via basicOperation().
156
 
         */
157
 
        public function mkdir($path) {
158
 
                return $this->basicOperation('mkdir', $path, array('create', 'write'));
159
 
        }
160
 
 
161
 
        public function rmdir($path) {
162
 
                if ($this->is_dir($path)) {
163
 
                        return $this->basicOperation('rmdir', $path, array('delete'));
164
 
                } else {
165
 
                        return false;
166
 
                }
167
 
        }
168
 
 
169
 
        public function opendir($path) {
170
 
                return $this->basicOperation('opendir', $path, array('read'));
171
 
        }
172
 
 
173
 
        public function readdir($handle) {
174
 
                $fsLocal = new Storage\Local(array('datadir' => '/'));
175
 
                return $fsLocal->readdir($handle);
176
 
        }
177
 
 
178
 
        public function is_dir($path) {
179
 
                if ($path == '/') {
180
 
                        return true;
181
 
                }
182
 
                return $this->basicOperation('is_dir', $path);
183
 
        }
184
 
 
185
 
        public function is_file($path) {
186
 
                if ($path == '/') {
187
 
                        return false;
188
 
                }
189
 
                return $this->basicOperation('is_file', $path);
190
 
        }
191
 
 
192
 
        public function stat($path) {
193
 
                return $this->basicOperation('stat', $path);
194
 
        }
195
 
 
196
 
        public function filetype($path) {
197
 
                return $this->basicOperation('filetype', $path);
198
 
        }
199
 
 
200
 
        public function filesize($path) {
201
 
                return $this->basicOperation('filesize', $path);
202
 
        }
203
 
 
204
 
        public function readfile($path) {
205
 
                @ob_end_clean();
206
 
                $handle = $this->fopen($path, 'rb');
207
 
                if ($handle) {
208
 
                        $chunkSize = 8192; // 8 kB chunks
209
 
                        while (!feof($handle)) {
210
 
                                echo fread($handle, $chunkSize);
211
 
                                flush();
212
 
                        }
213
 
                        $size = $this->filesize($path);
214
 
                        return $size;
215
 
                }
216
 
                return false;
217
 
        }
218
 
 
219
 
        public function isCreatable($path) {
220
 
                return $this->basicOperation('isCreatable', $path);
221
 
        }
222
 
 
223
 
        public function isReadable($path) {
224
 
                return $this->basicOperation('isReadable', $path);
225
 
        }
226
 
 
227
 
        public function isUpdatable($path) {
228
 
                return $this->basicOperation('isUpdatable', $path);
229
 
        }
230
 
 
231
 
        public function isDeletable($path) {
232
 
                return $this->basicOperation('isDeletable', $path);
233
 
        }
234
 
 
235
 
        public function isSharable($path) {
236
 
                return $this->basicOperation('isSharable', $path);
237
 
        }
238
 
 
239
 
        public function file_exists($path) {
240
 
                if ($path == '/') {
241
 
                        return true;
242
 
                }
243
 
                return $this->basicOperation('file_exists', $path);
244
 
        }
245
 
 
246
 
        public function filemtime($path) {
247
 
                return $this->basicOperation('filemtime', $path);
248
 
        }
249
 
 
250
 
        public function touch($path, $mtime = null) {
251
 
                if (!is_null($mtime) and !is_numeric($mtime)) {
252
 
                        $mtime = strtotime($mtime);
253
 
                }
254
 
 
255
 
                $hooks = array('touch');
256
 
 
257
 
                if (!$this->file_exists($path)) {
258
 
                        $hooks[] = 'create';
259
 
                        $hooks[] = 'write';
260
 
                }
261
 
                $result = $this->basicOperation('touch', $path, $hooks, $mtime);
262
 
                if (!$result) { //if native touch fails, we emulate it by changing the mtime in the cache
263
 
                        $this->putFileInfo($path, array('mtime' => $mtime));
264
 
                }
265
 
                return true;
266
 
        }
267
 
 
268
 
        public function file_get_contents($path) {
269
 
                return $this->basicOperation('file_get_contents', $path, array('read'));
270
 
        }
271
 
 
272
 
        public function file_put_contents($path, $data) {
273
 
                if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
274
 
                        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
275
 
                        if (\OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data)
276
 
                                and Filesystem::isValidPath($path)
277
 
                                and !Filesystem::isFileBlacklisted($path)
278
 
                        ) {
279
 
                                $path = $this->getRelativePath($absolutePath);
280
 
                                $exists = $this->file_exists($path);
281
 
                                $run = true;
282
 
                                if ($this->shouldEmitHooks($path)) {
283
 
                                        if (!$exists) {
284
 
                                                \OC_Hook::emit(
285
 
                                                        Filesystem::CLASSNAME,
286
 
                                                        Filesystem::signal_create,
287
 
                                                        array(
288
 
                                                                Filesystem::signal_param_path => $this->getHookPath($path),
289
 
                                                                Filesystem::signal_param_run => &$run
290
 
                                                        )
291
 
                                                );
292
 
                                        }
293
 
                                        \OC_Hook::emit(
294
 
                                                Filesystem::CLASSNAME,
295
 
                                                Filesystem::signal_write,
296
 
                                                array(
297
 
                                                        Filesystem::signal_param_path => $this->getHookPath($path),
298
 
                                                        Filesystem::signal_param_run => &$run
299
 
                                                )
300
 
                                        );
301
 
                                }
302
 
                                if (!$run) {
303
 
                                        return false;
304
 
                                }
305
 
                                $target = $this->fopen($path, 'w');
306
 
                                if ($target) {
307
 
                                        list ($count, $result) = \OC_Helper::streamCopy($data, $target);
308
 
                                        fclose($target);
309
 
                                        fclose($data);
310
 
                                        if ($this->shouldEmitHooks($path) && $result !== false) {
311
 
                                                if (!$exists) {
312
 
                                                        \OC_Hook::emit(
313
 
                                                                Filesystem::CLASSNAME,
314
 
                                                                Filesystem::signal_post_create,
315
 
                                                                array(Filesystem::signal_param_path => $this->getHookPath($path))
316
 
                                                        );
317
 
                                                }
318
 
                                                \OC_Hook::emit(
319
 
                                                        Filesystem::CLASSNAME,
320
 
                                                        Filesystem::signal_post_write,
321
 
                                                        array(Filesystem::signal_param_path => $this->getHookPath($path))
322
 
                                                );
323
 
                                        }
324
 
                                        \OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count);
325
 
                                        return $result;
326
 
                                } else {
327
 
                                        return false;
328
 
                                }
329
 
                        } else {
330
 
                                return false;
331
 
                        }
332
 
                } else {
333
 
                        $hooks = ($this->file_exists($path)) ? array('write') : array('create', 'write');
334
 
                        return $this->basicOperation('file_put_contents', $path, $hooks, $data);
335
 
                }
336
 
        }
337
 
 
338
 
        public function unlink($path) {
339
 
                if ($path === '' || $path === '/') {
340
 
                        // do not allow deleting the root
341
 
                        return false;
342
 
                }
343
 
                $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
344
 
                $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
345
 
                list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
346
 
                if (!$internalPath || $internalPath === '' || $internalPath === '/') {
347
 
                        // do not allow deleting the storage's root / the mount point
348
 
                        // because for some storages it might delete the whole contents
349
 
                        // but isn't supposed to work that way
350
 
                        return false;
351
 
                }
352
 
                return $this->basicOperation('unlink', $path, array('delete'));
353
 
        }
354
 
 
355
 
        public function deleteAll($directory, $empty = false) {
356
 
                return $this->rmdir($directory);
357
 
        }
358
 
 
359
 
        public function rename($path1, $path2) {
360
 
                $postFix1 = (substr($path1, -1, 1) === '/') ? '/' : '';
361
 
                $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : '';
362
 
                $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
363
 
                $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
364
 
                if (
365
 
                        \OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2)
366
 
                        and Filesystem::isValidPath($path2)
367
 
                        and Filesystem::isValidPath($path1)
368
 
                        and !Filesystem::isFileBlacklisted($path2)
369
 
                ) {
370
 
                        $path1 = $this->getRelativePath($absolutePath1);
371
 
                        $path2 = $this->getRelativePath($absolutePath2);
372
 
 
373
 
                        if ($path1 == null or $path2 == null) {
374
 
                                return false;
375
 
                        }
376
 
                        $run = true;
377
 
                        if ($this->shouldEmitHooks() && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
378
 
                                // if it was a rename from a part file to a regular file it was a write and not a rename operation
379
 
                                \OC_Hook::emit(
380
 
                                        Filesystem::CLASSNAME, Filesystem::signal_write,
381
 
                                        array(
382
 
                                                Filesystem::signal_param_path => $this->getHookPath($path2),
383
 
                                                Filesystem::signal_param_run => &$run
384
 
                                        )
385
 
                                );
386
 
                        } elseif ($this->shouldEmitHooks()) {
387
 
                                \OC_Hook::emit(
388
 
                                        Filesystem::CLASSNAME, Filesystem::signal_rename,
389
 
                                        array(
390
 
                                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
391
 
                                                Filesystem::signal_param_newpath => $this->getHookPath($path2),
392
 
                                                Filesystem::signal_param_run => &$run
393
 
                                        )
394
 
                                );
395
 
                        }
396
 
                        if ($run) {
397
 
                                $mp1 = $this->getMountPoint($path1 . $postFix1);
398
 
                                $mp2 = $this->getMountPoint($path2 . $postFix2);
399
 
                                if ($mp1 == $mp2) {
400
 
                                        list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1);
401
 
                                        list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2);
402
 
                                        if ($storage) {
403
 
                                                $result = $storage->rename($internalPath1, $internalPath2);
404
 
                                                \OC_FileProxy::runPostProxies('rename', $absolutePath1, $absolutePath2);
405
 
                                        } else {
406
 
                                                $result = false;
407
 
                                        }
408
 
                                } else {
409
 
                                        if ($this->is_dir($path1)) {
410
 
                                                $result = $this->copy($path1, $path2);
411
 
                                                if ($result === true) {
412
 
                                                        list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1);
413
 
                                                        $result = $storage1->deleteAll($internalPath1);
414
 
                                                }
415
 
                                        } else {
416
 
                                                $source = $this->fopen($path1 . $postFix1, 'r');
417
 
                                                $target = $this->fopen($path2 . $postFix2, 'w');
418
 
                                                list($count, $result) = \OC_Helper::streamCopy($source, $target);
419
 
 
420
 
                                                // close open handle - especially $source is necessary because unlink below will
421
 
                                                // throw an exception on windows because the file is locked
422
 
                                                fclose($source);
423
 
                                                fclose($target);
424
 
 
425
 
                                                if ($result !== false) {
426
 
                                                        list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1);
427
 
                                                        $storage1->unlink($internalPath1);
428
 
                                                }
429
 
                                        }
430
 
                                }
431
 
                                if ($this->shouldEmitHooks() && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
432
 
                                        // if it was a rename from a part file to a regular file it was a write and not a rename operation
433
 
                                        \OC_Hook::emit(
434
 
                                                Filesystem::CLASSNAME,
435
 
                                                Filesystem::signal_post_write,
436
 
                                                array(
437
 
                                                        Filesystem::signal_param_path => $this->getHookPath($path2),
438
 
                                                )
439
 
                                        );
440
 
                                } elseif ($this->shouldEmitHooks() && $result !== false) {
441
 
                                        \OC_Hook::emit(
442
 
                                                Filesystem::CLASSNAME,
443
 
                                                Filesystem::signal_post_rename,
444
 
                                                array(
445
 
                                                        Filesystem::signal_param_oldpath => $this->getHookPath($path1),
446
 
                                                        Filesystem::signal_param_newpath => $this->getHookPath($path2)
447
 
                                                )
448
 
                                        );
449
 
                                }
450
 
                                return $result;
451
 
                        } else {
452
 
                                return false;
453
 
                        }
454
 
                } else {
455
 
                        return false;
456
 
                }
457
 
        }
458
 
 
459
 
        public function copy($path1, $path2) {
460
 
                $postFix1 = (substr($path1, -1, 1) === '/') ? '/' : '';
461
 
                $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : '';
462
 
                $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
463
 
                $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
464
 
                if (
465
 
                        \OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2)
466
 
                        and Filesystem::isValidPath($path2)
467
 
                        and Filesystem::isValidPath($path1)
468
 
                        and !Filesystem::isFileBlacklisted($path2)
469
 
                ) {
470
 
                        $path1 = $this->getRelativePath($absolutePath1);
471
 
                        $path2 = $this->getRelativePath($absolutePath2);
472
 
 
473
 
                        if ($path1 == null or $path2 == null) {
474
 
                                return false;
475
 
                        }
476
 
                        $run = true;
477
 
                        $exists = $this->file_exists($path2);
478
 
                        if ($this->shouldEmitHooks()) {
479
 
                                \OC_Hook::emit(
480
 
                                        Filesystem::CLASSNAME,
481
 
                                        Filesystem::signal_copy,
482
 
                                        array(
483
 
                                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
484
 
                                                Filesystem::signal_param_newpath => $this->getHookPath($path2),
485
 
                                                Filesystem::signal_param_run => &$run
486
 
                                        )
487
 
                                );
488
 
                                if ($run and !$exists) {
489
 
                                        \OC_Hook::emit(
490
 
                                                Filesystem::CLASSNAME,
491
 
                                                Filesystem::signal_create,
492
 
                                                array(
493
 
                                                        Filesystem::signal_param_path => $this->getHookPath($path2),
494
 
                                                        Filesystem::signal_param_run => &$run
495
 
                                                )
496
 
                                        );
497
 
                                }
498
 
                                if ($run) {
499
 
                                        \OC_Hook::emit(
500
 
                                                Filesystem::CLASSNAME,
501
 
                                                Filesystem::signal_write,
502
 
                                                array(
503
 
                                                        Filesystem::signal_param_path => $this->getHookPath($path2),
504
 
                                                        Filesystem::signal_param_run => &$run
505
 
                                                )
506
 
                                        );
507
 
                                }
508
 
                        }
509
 
                        if ($run) {
510
 
                                $mp1 = $this->getMountPoint($path1 . $postFix1);
511
 
                                $mp2 = $this->getMountPoint($path2 . $postFix2);
512
 
                                if ($mp1 == $mp2) {
513
 
                                        list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1);
514
 
                                        list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2);
515
 
                                        if ($storage) {
516
 
                                                $result = $storage->copy($internalPath1, $internalPath2);
517
 
                                        } else {
518
 
                                                $result = false;
519
 
                                        }
520
 
                                } else {
521
 
                                        if ($this->is_dir($path1) && ($dh = $this->opendir($path1))) {
522
 
                                                $result = $this->mkdir($path2);
523
 
                                                if (is_resource($dh)) {
524
 
                                                        while (($file = readdir($dh)) !== false) {
525
 
                                                                if (!Filesystem::isIgnoredDir($file)) {
526
 
                                                                        $result = $this->copy($path1 . '/' . $file, $path2 . '/' . $file);
527
 
                                                                }
528
 
                                                        }
529
 
                                                }
530
 
                                        } else {
531
 
                                                $source = $this->fopen($path1 . $postFix1, 'r');
532
 
                                                $target = $this->fopen($path2 . $postFix2, 'w');
533
 
                                                list($count, $result) = \OC_Helper::streamCopy($source, $target);
534
 
                                        }
535
 
                                }
536
 
                                if ($this->shouldEmitHooks() && $result !== false) {
537
 
                                        \OC_Hook::emit(
538
 
                                                Filesystem::CLASSNAME,
539
 
                                                Filesystem::signal_post_copy,
540
 
                                                array(
541
 
                                                        Filesystem::signal_param_oldpath => $this->getHookPath($path1),
542
 
                                                        Filesystem::signal_param_newpath => $this->getHookPath($path2)
543
 
                                                )
544
 
                                        );
545
 
                                        if (!$exists) {
546
 
                                                \OC_Hook::emit(
547
 
                                                        Filesystem::CLASSNAME,
548
 
                                                        Filesystem::signal_post_create,
549
 
                                                        array(Filesystem::signal_param_path => $this->getHookPath($path2))
550
 
                                                );
551
 
                                        }
552
 
                                        \OC_Hook::emit(
553
 
                                                Filesystem::CLASSNAME,
554
 
                                                Filesystem::signal_post_write,
555
 
                                                array(Filesystem::signal_param_path => $this->getHookPath($path2))
556
 
                                        );
557
 
                                }
558
 
                                return $result;
559
 
                        } else {
560
 
                                return false;
561
 
                        }
562
 
                } else {
563
 
                        return false;
564
 
                }
565
 
        }
566
 
 
567
 
        public function fopen($path, $mode) {
568
 
                $hooks = array();
569
 
                switch ($mode) {
570
 
                        case 'r':
571
 
                        case 'rb':
572
 
                                $hooks[] = 'read';
573
 
                                break;
574
 
                        case 'r+':
575
 
                        case 'rb+':
576
 
                        case 'w+':
577
 
                        case 'wb+':
578
 
                        case 'x+':
579
 
                        case 'xb+':
580
 
                        case 'a+':
581
 
                        case 'ab+':
582
 
                                $hooks[] = 'read';
583
 
                                $hooks[] = 'write';
584
 
                                break;
585
 
                        case 'w':
586
 
                        case 'wb':
587
 
                        case 'x':
588
 
                        case 'xb':
589
 
                        case 'a':
590
 
                        case 'ab':
591
 
                                $hooks[] = 'write';
592
 
                                break;
593
 
                        default:
594
 
                                \OC_Log::write('core', 'invalid mode (' . $mode . ') for ' . $path, \OC_Log::ERROR);
595
 
                }
596
 
 
597
 
                return $this->basicOperation('fopen', $path, $hooks, $mode);
598
 
        }
599
 
 
600
 
        public function toTmpFile($path) {
601
 
                if (Filesystem::isValidPath($path)) {
602
 
                        $source = $this->fopen($path, 'r');
603
 
                        if ($source) {
604
 
                                $extension = pathinfo($path, PATHINFO_EXTENSION);
605
 
                                $tmpFile = \OC_Helper::tmpFile($extension);
606
 
                                file_put_contents($tmpFile, $source);
607
 
                                return $tmpFile;
608
 
                        } else {
609
 
                                return false;
610
 
                        }
611
 
                } else {
612
 
                        return false;
613
 
                }
614
 
        }
615
 
 
616
 
        public function fromTmpFile($tmpFile, $path) {
617
 
                if (Filesystem::isValidPath($path)) {
618
 
                        if (!$tmpFile) {
619
 
                                debug_print_backtrace();
620
 
                        }
621
 
                        $source = fopen($tmpFile, 'r');
622
 
                        if ($source) {
623
 
                                $this->file_put_contents($path, $source);
624
 
                                unlink($tmpFile);
625
 
                                return true;
626
 
                        } else {
627
 
                                return false;
628
 
                        }
629
 
                } else {
630
 
                        return false;
631
 
                }
632
 
        }
633
 
 
634
 
        public function getMimeType($path) {
635
 
                return $this->basicOperation('getMimeType', $path);
636
 
        }
637
 
 
638
 
        public function hash($type, $path, $raw = false) {
639
 
                $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
640
 
                $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
641
 
                if (\OC_FileProxy::runPreProxies('hash', $absolutePath) && Filesystem::isValidPath($path)) {
642
 
                        $path = $this->getRelativePath($absolutePath);
643
 
                        if ($path == null) {
644
 
                                return false;
645
 
                        }
646
 
                        if ($this->shouldEmitHooks($path)) {
647
 
                                \OC_Hook::emit(
648
 
                                        Filesystem::CLASSNAME,
649
 
                                        Filesystem::signal_read,
650
 
                                        array(Filesystem::signal_param_path => $this->getHookPath($path))
651
 
                                );
652
 
                        }
653
 
                        list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
654
 
                        if ($storage) {
655
 
                                $result = $storage->hash($type, $internalPath, $raw);
656
 
                                $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result);
657
 
                                return $result;
658
 
                        }
659
 
                }
660
 
                return null;
661
 
        }
662
 
 
663
 
        public function free_space($path = '/') {
664
 
                return $this->basicOperation('free_space', $path);
665
 
        }
666
 
 
667
 
        /**
668
 
         * @brief abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
669
 
         * @param string $operation
670
 
         * @param string $path
671
 
         * @param array $hooks (optional)
672
 
         * @param mixed $extraParam (optional)
673
 
         * @return mixed
674
 
         *
675
 
         * This method takes requests for basic filesystem functions (e.g. reading & writing
676
 
         * files), processes hooks and proxies, sanitises paths, and finally passes them on to
677
 
         * \OC\Files\Storage\Storage for delegation to a storage backend for execution
678
 
         */
679
 
        private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) {
680
 
                $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
681
 
                $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
682
 
                if (\OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam)
683
 
                        and Filesystem::isValidPath($path)
684
 
                        and !Filesystem::isFileBlacklisted($path)
685
 
                ) {
686
 
                        $path = $this->getRelativePath($absolutePath);
687
 
                        if ($path == null) {
688
 
                                return false;
689
 
                        }
690
 
 
691
 
                        $run = $this->runHooks($hooks, $path);
692
 
                        list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
693
 
                        if ($run and $storage) {
694
 
                                if (!is_null($extraParam)) {
695
 
                                        $result = $storage->$operation($internalPath, $extraParam);
696
 
                                } else {
697
 
                                        $result = $storage->$operation($internalPath);
698
 
                                }
699
 
                                $result = \OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result);
700
 
                                if ($this->shouldEmitHooks($path) && $result !== false) {
701
 
                                        if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
702
 
                                                $this->runHooks($hooks, $path, true);
703
 
                                        }
704
 
                                }
705
 
                                return $result;
706
 
                        }
707
 
                }
708
 
                return null;
709
 
        }
710
 
 
711
 
        /**
712
 
         * get the path relative to the default root for hook usage
713
 
         *
714
 
         * @param string $path
715
 
         * @return string
716
 
         */
717
 
        private function getHookPath($path) {
718
 
                if (!Filesystem::getView()) {
719
 
                        return $path;
720
 
                }
721
 
                return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
722
 
        }
723
 
 
724
 
        private function shouldEmitHooks($path = '') {
725
 
                if ($path && Cache\Scanner::isPartialFile($path)) {
726
 
                        return false;
727
 
                }
728
 
                if (!Filesystem::$loaded) {
729
 
                        return false;
730
 
                }
731
 
                $defaultRoot = Filesystem::getRoot();
732
 
                if ($this->fakeRoot === $defaultRoot) {
733
 
                        return true;
734
 
                }
735
 
                return (strlen($this->fakeRoot) > strlen($defaultRoot)) && (substr($this->fakeRoot, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
736
 
        }
737
 
 
738
 
        private function runHooks($hooks, $path, $post = false) {
739
 
                $path = $this->getHookPath($path);
740
 
                $prefix = ($post) ? 'post_' : '';
741
 
                $run = true;
742
 
                if ($this->shouldEmitHooks($path)) {
743
 
                        foreach ($hooks as $hook) {
744
 
                                if ($hook != 'read') {
745
 
                                        \OC_Hook::emit(
746
 
                                                Filesystem::CLASSNAME,
747
 
                                                $prefix . $hook,
748
 
                                                array(
749
 
                                                        Filesystem::signal_param_run => &$run,
750
 
                                                        Filesystem::signal_param_path => $path
751
 
                                                )
752
 
                                        );
753
 
                                } elseif (!$post) {
754
 
                                        \OC_Hook::emit(
755
 
                                                Filesystem::CLASSNAME,
756
 
                                                $prefix . $hook,
757
 
                                                array(
758
 
                                                        Filesystem::signal_param_path => $path
759
 
                                                )
760
 
                                        );
761
 
                                }
762
 
                        }
763
 
                }
764
 
                return $run;
765
 
        }
766
 
 
767
 
        /**
768
 
         * check if a file or folder has been updated since $time
769
 
         *
770
 
         * @param string $path
771
 
         * @param int $time
772
 
         * @return bool
773
 
         */
774
 
        public function hasUpdated($path, $time) {
775
 
                return $this->basicOperation('hasUpdated', $path, array(), $time);
776
 
        }
777
 
 
778
 
        /**
779
 
         * get the filesystem info
780
 
         *
781
 
         * @param string $path
782
 
         * @param boolean $includeMountPoints whether to add mountpoint sizes,
783
 
         * defaults to true
784
 
         * @return array
785
 
         *
786
 
         * returns an associative array with the following keys:
787
 
         * - size
788
 
         * - mtime
789
 
         * - mimetype
790
 
         * - encrypted
791
 
         * - versioned
792
 
         */
793
 
        public function getFileInfo($path, $includeMountPoints = true) {
794
 
                $data = array();
795
 
                if (!Filesystem::isValidPath($path)) {
796
 
                        return $data;
797
 
                }
798
 
                $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
799
 
                /**
800
 
                 * @var \OC\Files\Storage\Storage $storage
801
 
                 * @var string $internalPath
802
 
                 */
803
 
                list($storage, $internalPath) = Filesystem::resolvePath($path);
804
 
                if ($storage) {
805
 
                        $cache = $storage->getCache($internalPath);
806
 
                        $permissionsCache = $storage->getPermissionsCache($internalPath);
807
 
                        $user = \OC_User::getUser();
808
 
 
809
 
                        if (!$cache->inCache($internalPath)) {
810
 
                                $scanner = $storage->getScanner($internalPath);
811
 
                                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
812
 
                        } else {
813
 
                                $watcher = $storage->getWatcher($internalPath);
814
 
                                $watcher->checkUpdate($internalPath);
815
 
                        }
816
 
 
817
 
                        $data = $cache->get($internalPath);
818
 
 
819
 
                        if ($data and $data['fileid']) {
820
 
                                if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
821
 
                                        //add the sizes of other mountpoints to the folder
822
 
                                        $mountPoints = Filesystem::getMountPoints($path);
823
 
                                        foreach ($mountPoints as $mountPoint) {
824
 
                                                $subStorage = Filesystem::getStorage($mountPoint);
825
 
                                                if ($subStorage) {
826
 
                                                        $subCache = $subStorage->getCache('');
827
 
                                                        $rootEntry = $subCache->get('');
828
 
                                                        $data['size'] += isset($rootEntry['size']) ? $rootEntry['size'] : 0;
829
 
                                                }
830
 
                                        }
831
 
                                }
832
 
 
833
 
                                $permissions = $permissionsCache->get($data['fileid'], $user);
834
 
                                if ($permissions === -1) {
835
 
                                        $permissions = $storage->getPermissions($internalPath);
836
 
                                        $permissionsCache->set($data['fileid'], $user, $permissions);
837
 
                                }
838
 
                                $data['permissions'] = $permissions;
839
 
                        }
840
 
                }
841
 
 
842
 
                $data = \OC_FileProxy::runPostProxies('getFileInfo', $path, $data);
843
 
 
844
 
                return $data;
845
 
        }
846
 
 
847
 
        /**
848
 
         * get the content of a directory
849
 
         *
850
 
         * @param string $directory path under datadirectory
851
 
         * @param string $mimetype_filter limit returned content to this mimetype or mimepart
852
 
         * @return array
853
 
         */
854
 
        public function getDirectoryContent($directory, $mimetype_filter = '') {
855
 
                $result = array();
856
 
                if (!Filesystem::isValidPath($directory)) {
857
 
                        return $result;
858
 
                }
859
 
                $path = Filesystem::normalizePath($this->fakeRoot . '/' . $directory);
860
 
                /**
861
 
                 * @var \OC\Files\Storage\Storage $storage
862
 
                 * @var string $internalPath
863
 
                 */
864
 
                list($storage, $internalPath) = Filesystem::resolvePath($path);
865
 
                if ($storage) {
866
 
                        $cache = $storage->getCache($internalPath);
867
 
                        $permissionsCache = $storage->getPermissionsCache($internalPath);
868
 
                        $user = \OC_User::getUser();
869
 
 
870
 
                        if ($cache->getStatus($internalPath) < Cache\Cache::COMPLETE) {
871
 
                                $scanner = $storage->getScanner($internalPath);
872
 
                                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
873
 
                        } else {
874
 
                                $watcher = $storage->getWatcher($internalPath);
875
 
                                $watcher->checkUpdate($internalPath);
876
 
                        }
877
 
 
878
 
                        $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter
879
 
                        $permissions = $permissionsCache->getDirectoryPermissions($cache->getId($internalPath), $user);
880
 
 
881
 
                        $ids = array();
882
 
                        foreach ($files as $i => $file) {
883
 
                                $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
884
 
                                $ids[] = $file['fileid'];
885
 
 
886
 
                                if (!isset($permissions[$file['fileid']])) {
887
 
                                        $permissions[$file['fileid']] = $storage->getPermissions($file['path']);
888
 
                                        $permissionsCache->set($file['fileid'], $user, $permissions[$file['fileid']]);
889
 
                                }
890
 
                                $files[$i]['permissions'] = $permissions[$file['fileid']];
891
 
                        }
892
 
 
893
 
                        //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
894
 
                        $mountPoints = Filesystem::getMountPoints($path);
895
 
                        $dirLength = strlen($path);
896
 
                        foreach ($mountPoints as $mountPoint) {
897
 
                                $subStorage = Filesystem::getStorage($mountPoint);
898
 
                                if ($subStorage) {
899
 
                                        $subCache = $subStorage->getCache('');
900
 
 
901
 
                                        if ($subCache->getStatus('') === Cache\Cache::NOT_FOUND) {
902
 
                                                $subScanner = $subStorage->getScanner('');
903
 
                                                $subScanner->scanFile('');
904
 
                                        }
905
 
 
906
 
                                        $rootEntry = $subCache->get('');
907
 
                                        if ($rootEntry) {
908
 
                                                $relativePath = trim(substr($mountPoint, $dirLength), '/');
909
 
                                                if ($pos = strpos($relativePath, '/')) {
910
 
                                                        //mountpoint inside subfolder add size to the correct folder
911
 
                                                        $entryName = substr($relativePath, 0, $pos);
912
 
                                                        foreach ($files as &$entry) {
913
 
                                                                if ($entry['name'] === $entryName) {
914
 
                                                                        $entry['size'] += $rootEntry['size'];
915
 
                                                                }
916
 
                                                        }
917
 
                                                } else { //mountpoint in this folder, add an entry for it
918
 
                                                        $rootEntry['name'] = $relativePath;
919
 
                                                        $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
920
 
                                                        $subPermissionsCache = $subStorage->getPermissionsCache('');
921
 
                                                        $permissions = $subPermissionsCache->get($rootEntry['fileid'], $user);
922
 
                                                        if ($permissions === -1) {
923
 
                                                                $permissions = $subStorage->getPermissions($rootEntry['path']);
924
 
                                                                $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions);
925
 
                                                        }
926
 
                                                        // do not allow renaming/deleting the mount point
927
 
                                                        $rootEntry['permissions'] = $permissions & (\OCP\PERMISSION_ALL - (\OCP\PERMISSION_UPDATE | \OCP\PERMISSION_DELETE));
928
 
 
929
 
                                                        //remove any existing entry with the same name
930
 
                                                        foreach ($files as $i => $file) {
931
 
                                                                if ($file['name'] === $rootEntry['name']) {
932
 
                                                                        unset($files[$i]);
933
 
                                                                        break;
934
 
                                                                }
935
 
                                                        }
936
 
                                                        $files[] = $rootEntry;
937
 
                                                }
938
 
                                        }
939
 
                                }
940
 
                        }
941
 
 
942
 
                        if ($mimetype_filter) {
943
 
                                foreach ($files as $file) {
944
 
                                        if (strpos($mimetype_filter, '/')) {
945
 
                                                if ($file['mimetype'] === $mimetype_filter) {
946
 
                                                        $result[] = $file;
947
 
                                                }
948
 
                                        } else {
949
 
                                                if ($file['mimepart'] === $mimetype_filter) {
950
 
                                                        $result[] = $file;
951
 
                                                }
952
 
                                        }
953
 
                                }
954
 
                        } else {
955
 
                                $result = $files;
956
 
                        }
957
 
                }
958
 
                return $result;
959
 
        }
960
 
 
961
 
        /**
962
 
         * change file metadata
963
 
         *
964
 
         * @param string $path
965
 
         * @param array $data
966
 
         * @return int
967
 
         *
968
 
         * returns the fileid of the updated file
969
 
         */
970
 
        public function putFileInfo($path, $data) {
971
 
                $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
972
 
                /**
973
 
                 * @var \OC\Files\Storage\Storage $storage
974
 
                 * @var string $internalPath
975
 
                 */
976
 
                list($storage, $internalPath) = Filesystem::resolvePath($path);
977
 
                if ($storage) {
978
 
                        $cache = $storage->getCache($path);
979
 
 
980
 
                        if (!$cache->inCache($internalPath)) {
981
 
                                $scanner = $storage->getScanner($internalPath);
982
 
                                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
983
 
                        }
984
 
 
985
 
                        return $cache->put($internalPath, $data);
986
 
                } else {
987
 
                        return -1;
988
 
                }
989
 
        }
990
 
 
991
 
        /**
992
 
         * search for files with the name matching $query
993
 
         *
994
 
         * @param string $query
995
 
         * @return array
996
 
         */
997
 
        public function search($query) {
998
 
                return $this->searchCommon('%' . $query . '%', 'search');
999
 
        }
1000
 
 
1001
 
        /**
1002
 
         * search for files by mimetype
1003
 
         *
1004
 
         * @param string $mimetype
1005
 
         * @return array
1006
 
         */
1007
 
        public function searchByMime($mimetype) {
1008
 
                return $this->searchCommon($mimetype, 'searchByMime');
1009
 
        }
1010
 
 
1011
 
        /**
1012
 
         * @param string $query
1013
 
         * @param string $method
1014
 
         * @return array
1015
 
         */
1016
 
        private function searchCommon($query, $method) {
1017
 
                $files = array();
1018
 
                $rootLength = strlen($this->fakeRoot);
1019
 
 
1020
 
                $mountPoint = Filesystem::getMountPoint($this->fakeRoot);
1021
 
                $storage = Filesystem::getStorage($mountPoint);
1022
 
                if ($storage) {
1023
 
                        $cache = $storage->getCache('');
1024
 
 
1025
 
                        $results = $cache->$method($query);
1026
 
                        foreach ($results as $result) {
1027
 
                                if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1028
 
                                        $result['path'] = substr($mountPoint . $result['path'], $rootLength);
1029
 
                                        $files[] = $result;
1030
 
                                }
1031
 
                        }
1032
 
 
1033
 
                        $mountPoints = Filesystem::getMountPoints($this->fakeRoot);
1034
 
                        foreach ($mountPoints as $mountPoint) {
1035
 
                                $storage = Filesystem::getStorage($mountPoint);
1036
 
                                if ($storage) {
1037
 
                                        $cache = $storage->getCache('');
1038
 
 
1039
 
                                        $relativeMountPoint = substr($mountPoint, $rootLength);
1040
 
                                        $results = $cache->$method($query);
1041
 
                                        if ($results) {
1042
 
                                                foreach ($results as $result) {
1043
 
                                                        $result['path'] = $relativeMountPoint . $result['path'];
1044
 
                                                        $files[] = $result;
1045
 
                                                }
1046
 
                                        }
1047
 
                                }
1048
 
                        }
1049
 
                }
1050
 
                return $files;
1051
 
        }
1052
 
 
1053
 
        /**
1054
 
         * Get the owner for a file or folder
1055
 
         *
1056
 
         * @param string $path
1057
 
         * @return string
1058
 
         */
1059
 
        public function getOwner($path) {
1060
 
                return $this->basicOperation('getOwner', $path);
1061
 
        }
1062
 
 
1063
 
        /**
1064
 
         * get the ETag for a file or folder
1065
 
         *
1066
 
         * @param string $path
1067
 
         * @return string
1068
 
         */
1069
 
        public function getETag($path) {
1070
 
                /**
1071
 
                 * @var Storage\Storage $storage
1072
 
                 * @var string $internalPath
1073
 
                 */
1074
 
                list($storage, $internalPath) = $this->resolvePath($path);
1075
 
                if ($storage) {
1076
 
                        return $storage->getETag($internalPath);
1077
 
                } else {
1078
 
                        return null;
1079
 
                }
1080
 
        }
1081
 
 
1082
 
        /**
1083
 
         * Get the path of a file by id, relative to the view
1084
 
         *
1085
 
         * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1086
 
         *
1087
 
         * @param int $id
1088
 
         * @return string
1089
 
         */
1090
 
        public function getPath($id) {
1091
 
                list($storage, $internalPath) = Cache\Cache::getById($id);
1092
 
                $mounts = Filesystem::getMountByStorageId($storage);
1093
 
                foreach ($mounts as $mount) {
1094
 
                        /**
1095
 
                         * @var \OC\Files\Mount $mount
1096
 
                         */
1097
 
                        $fullPath = $mount->getMountPoint() . $internalPath;
1098
 
                        if (!is_null($path = $this->getRelativePath($fullPath))) {
1099
 
                                return $path;
1100
 
                        }
1101
 
                }
1102
 
                return null;
1103
 
        }
1104
 
}