Blame view

sources/apps/files_sharing/lib/cache.php 13.5 KB
03e52840d   Kload   Init
1
2
3
4
  <?php
  /**
   * ownCloud
   *
6d9380f96   Cédric Dupont   Update sources OC...
5
6
7
   * @author Bjoern Schiessle, Michael Gapczynski
   * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com>
   *            2014 Bjoern Schiessle <schiessle@owncloud.com>
03e52840d   Kload   Init
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
   * License as published by the Free Software Foundation; either
   * version 3 of the License, or any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
   *
   * You should have received a copy of the GNU Affero General Public
   * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  namespace OC\Files\Cache;
837968727   Kload   [enh] Upgrade to ...
24

03e52840d   Kload   Init
25
26
27
28
29
30
31
32
33
34
35
  use OCP\Share_Backend_Collection;
  
  /**
   * Metadata cache for shared files
   *
   * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead
   */
  class Shared_Cache extends Cache {
  
  	private $storage;
  	private $files = array();
6d9380f96   Cédric Dupont   Update sources OC...
36
37
38
  	/**
  	 * @param \OC\Files\Storage\Shared $storage
  	 */
03e52840d   Kload   Init
39
40
41
42
43
  	public function __construct($storage) {
  		$this->storage = $storage;
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
44
  	 * Get the source cache of a shared file or folder
03e52840d   Kload   Init
45
46
47
48
  	 * @param string $target Shared target file path
  	 * @return \OC\Files\Cache\Cache
  	 */
  	private function getSourceCache($target) {
6d9380f96   Cédric Dupont   Update sources OC...
49
50
51
52
  		if ($target === false || $target === $this->storage->getMountPoint()) {
  			$target = '';
  		}
  		$source = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType());
03e52840d   Kload   Init
53
54
  		if (isset($source['path']) && isset($source['fileOwner'])) {
  			\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
6d9380f96   Cédric Dupont   Update sources OC...
55
56
57
  			$mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']);
  			if (is_array($mounts) and !empty($mounts)) {
  				$fullPath = $mounts[0]->getMountPoint() . $source['path'];
03e52840d   Kload   Init
58
59
60
61
62
63
64
65
66
67
68
69
  				list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath);
  				if ($storage) {
  					$this->files[$target] = $internalPath;
  					$cache = $storage->getCache();
  					$this->storageId = $storage->getId();
  					$this->numericId = $cache->getNumericStorageId();
  					return $cache;
  				}
  			}
  		}
  		return false;
  	}
31b7f2792   Kload   Upgrade to ownclo...
70
71
72
73
74
75
76
  	public function getNumericStorageId() {
  		if (isset($this->numericId)) {
  			return $this->numericId;
  		} else {
  			return false;
  		}
  	}
03e52840d   Kload   Init
77
78
79
  	/**
  	 * get the stored metadata of a file or folder
  	 *
6d9380f96   Cédric Dupont   Update sources OC...
80
  	 * @param string|int $file
03e52840d   Kload   Init
81
82
83
  	 * @return array
  	 */
  	public function get($file) {
6d9380f96   Cédric Dupont   Update sources OC...
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  		if (is_string($file)) {
  			$cache = $this->getSourceCache($file);
  			if ($cache) {
  				$data = $cache->get($this->files[$file]);
  				$data['displayname_owner'] = \OC_User::getDisplayName($this->storage->getSharedFrom());
  				$data['path'] = $file;
  				if ($file === '') {
  					$data['is_share_mount_point'] = true;
  				}
  				$data['uid_owner'] = $this->storage->getOwner($file);
  				if (isset($data['permissions'])) {
  					$data['permissions'] &= $this->storage->getPermissions($file);
  				} else {
  					$data['permissions'] = $this->storage->getPermissions($file);
  				}
  				return $data;
03e52840d   Kload   Init
100
101
  			}
  		} else {
6d9380f96   Cédric Dupont   Update sources OC...
102
103
104
105
106
107
  			$sourceId = $file;
  			// if we are at the root of the mount point we want to return the
  			// cache information for the source item
  			if (!is_int($sourceId) || $sourceId === 0) {
  				$sourceId = $this->storage->getSourceId();
  			}
03e52840d   Kload   Init
108
109
  			$query = \OC_DB::prepare(
  				'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,'
6d9380f96   Cédric Dupont   Update sources OC...
110
  				. ' `size`, `mtime`, `encrypted`, `unencrypted_size`, `storage_mtime`, `etag`, `permissions`'
837968727   Kload   [enh] Upgrade to ...
111
  				. ' FROM `*PREFIX*filecache` WHERE `fileid` = ?');
6d9380f96   Cédric Dupont   Update sources OC...
112
  			$result = $query->execute(array($sourceId));
03e52840d   Kload   Init
113
114
  			$data = $result->fetchRow();
  			$data['fileid'] = (int)$data['fileid'];
03e52840d   Kload   Init
115
  			$data['mtime'] = (int)$data['mtime'];
31b7f2792   Kload   Upgrade to ownclo...
116
  			$data['storage_mtime'] = (int)$data['storage_mtime'];
03e52840d   Kload   Init
117
118
119
  			$data['encrypted'] = (bool)$data['encrypted'];
  			$data['mimetype'] = $this->getMimetype($data['mimetype']);
  			$data['mimepart'] = $this->getMimetype($data['mimepart']);
31b7f2792   Kload   Upgrade to ownclo...
120
121
122
  			if ($data['storage_mtime'] === 0) {
  				$data['storage_mtime'] = $data['mtime'];
  			}
a293d369c   Kload   Update sources to...
123
124
125
126
127
128
  			if ($data['encrypted'] or ($data['unencrypted_size'] > 0 and $data['mimetype'] === 'httpd/unix-directory')) {
  				$data['encrypted_size'] = (int)$data['size'];
  				$data['size'] = (int)$data['unencrypted_size'];
  			} else {
  				$data['size'] = (int)$data['size'];
  			}
6d9380f96   Cédric Dupont   Update sources OC...
129
130
131
132
133
134
135
  			$data['permissions'] = (int)$data['permissions'];
  			if (!is_int($file) || $file === 0) {
  				$data['path'] = '';
  				$data['name'] = basename($this->storage->getMountPoint());
  				$data['is_share_mount_point'] = true;
  			}
  			$data['permissions'] &= $this->storage->getPermissions('');
03e52840d   Kload   Init
136
137
138
139
140
141
142
143
144
145
146
147
  			return $data;
  		}
  		return false;
  	}
  
  	/**
  	 * get the metadata of all files stored in $folder
  	 *
  	 * @param string $folder
  	 * @return array
  	 */
  	public function getFolderContents($folder) {
a293d369c   Kload   Update sources to...
148

6d9380f96   Cédric Dupont   Update sources OC...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  		if ($folder === false) {
  			$folder = '';
  		}
  
  		$dir = ($folder !== '') ? $folder . '/' : '';
  
  		$cache = $this->getSourceCache($folder);
  		if ($cache) {
  			$parent = $this->storage->getFile($folder);
  			$sourceFolderContent = $cache->getFolderContents($this->files[$folder]);
  			foreach ($sourceFolderContent as $key => $c) {
  				$sourceFolderContent[$key]['path'] = $dir . $c['name'];
  				$sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner'];
  				$sourceFolderContent[$key]['displayname_owner'] = \OC_User::getDisplayName($parent['uid_owner']);
  				$sourceFolderContent[$key]['permissions'] = $sourceFolderContent[$key]['permissions'] & $this->storage->getPermissions($dir . $c['name']);
03e52840d   Kload   Init
164
  			}
6d9380f96   Cédric Dupont   Update sources OC...
165
166
  
  			return $sourceFolderContent;
03e52840d   Kload   Init
167
  		}
6d9380f96   Cédric Dupont   Update sources OC...
168

03e52840d   Kload   Init
169
170
171
172
173
174
175
176
177
178
179
180
  		return false;
  	}
  
  	/**
  	 * store meta data for a file or folder
  	 *
  	 * @param string $file
  	 * @param array $data
  	 *
  	 * @return int file id
  	 */
  	public function put($file, array $data) {
6d9380f96   Cédric Dupont   Update sources OC...
181
182
  		$file = ($file === false) ? '' : $file;
  		if ($cache = $this->getSourceCache($file)) {
03e52840d   Kload   Init
183
184
185
186
187
188
189
190
191
192
193
194
  			return $cache->put($this->files[$file], $data);
  		}
  		return false;
  	}
  
  	/**
  	 * get the file id for a file
  	 *
  	 * @param string $file
  	 * @return int
  	 */
  	public function getId($file) {
6d9380f96   Cédric Dupont   Update sources OC...
195
196
197
198
199
  		if ($file === false) {
  			return $this->storage->getSourceId();
  		}
  		$cache = $this->getSourceCache($file);
  		if ($cache) {
03e52840d   Kload   Init
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  			return $cache->getId($this->files[$file]);
  		}
  		return -1;
  	}
  
  	/**
  	 * check if a file is available in the cache
  	 *
  	 * @param string $file
  	 * @return bool
  	 */
  	public function inCache($file) {
  		if ($file == '') {
  			return true;
  		}
  		return parent::inCache($file);
  	}
  
  	/**
  	 * remove a file or folder from the cache
  	 *
  	 * @param string $file
  	 */
  	public function remove($file) {
6d9380f96   Cédric Dupont   Update sources OC...
224
  		$file = ($file === false) ? '' : $file;
03e52840d   Kload   Init
225
226
227
228
229
230
231
232
233
234
235
236
237
  		if ($cache = $this->getSourceCache($file)) {
  			$cache->remove($this->files[$file]);
  		}
  	}
  
  	/**
  	 * Move a file or folder in the cache
  	 *
  	 * @param string $source
  	 * @param string $target
  	 */
  	public function move($source, $target) {
  		if ($cache = $this->getSourceCache($source)) {
6d9380f96   Cédric Dupont   Update sources OC...
238
  			$file = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType());
03e52840d   Kload   Init
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  			if ($file && isset($file['path'])) {
  				$cache->move($this->files[$source], $file['path']);
  			}
  		}
  	}
  
  	/**
  	 * remove all entries for files that are stored on the storage from the cache
  	 */
  	public function clear() {
  		// Not a valid action for Shared Cache
  	}
  
  	/**
  	 * @param string $file
  	 *
  	 * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
  	 */
  	public function getStatus($file) {
  		if ($file == '') {
  			return self::COMPLETE;
  		}
  		if ($cache = $this->getSourceCache($file)) {
  			return $cache->getStatus($this->files[$file]);
  		}
  		return self::NOT_FOUND;
  	}
  
  	/**
  	 * search for files matching $pattern
  	 *
  	 * @param string $pattern
  	 * @return array of file data
  	 */
  	public function search($pattern) {
  
  		$where = '`name` LIKE ? AND ';
  
  		// normalize pattern
  		$value = $this->normalize($pattern);
31b7f2792   Kload   Upgrade to ownclo...
279
  		return $this->searchWithWhere($where, $value);
03e52840d   Kload   Init
280
281
282
283
284
285
286
287
288
289
  
  	}
  
  	/**
  	 * search for files by mimetype
  	 *
  	 * @param string $mimetype
  	 * @return array
  	 */
  	public function searchByMime($mimetype) {
a293d369c   Kload   Update sources to...
290
291
292
293
  		$mimepart = null;
  		if (strpos($mimetype, '/') === false) {
  			$mimepart = $mimetype;
  			$mimetype = null;
03e52840d   Kload   Init
294
  		}
a293d369c   Kload   Update sources to...
295
296
297
  		// note: searchWithWhere is currently broken as it doesn't
  		// recurse into subdirs nor returns the correct
  		// file paths, so using getFolderContents() for now
03e52840d   Kload   Init
298

a293d369c   Kload   Update sources to...
299
300
301
302
303
304
305
  		$result = array();
  		$exploreDirs = array('');
  		while (count($exploreDirs) > 0) {
  			$dir = array_pop($exploreDirs);
  			$files = $this->getFolderContents($dir);
  			// no results?
  			if (!$files) {
6d9380f96   Cédric Dupont   Update sources OC...
306
307
308
309
310
  				// maybe it's a single shared file
  				$file = $this->get('');
  				if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) {
  					$result[] = $file;
  				}
a293d369c   Kload   Update sources to...
311
312
313
314
315
  				continue;
  			}
  			foreach ($files as $file) {
  				if ($file['mimetype'] === 'httpd/unix-directory') {
  					$exploreDirs[] = ltrim($dir . '/' . $file['name'], '/');
837968727   Kload   [enh] Upgrade to ...
316
  				} else if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) {
a293d369c   Kload   Update sources to...
317
318
319
320
321
  					$result[] = $file;
  				}
  			}
  		}
  		return $result;
03e52840d   Kload   Init
322
  	}
a293d369c   Kload   Update sources to...
323

03e52840d   Kload   Init
324
325
  	/**
  	 * The maximum number of placeholders that can be used in an SQL query.
31b7f2792   Kload   Upgrade to ownclo...
326
327
328
  	 * Value MUST be <= 1000 for oracle:
  	 * see ORA-01795 maximum number of expressions in a list is 1000
  	 * FIXME we should get this from doctrine as other DBs allow a lot more placeholders
03e52840d   Kload   Init
329
  	 */
31b7f2792   Kload   Upgrade to ownclo...
330
  	const MAX_SQL_CHUNK_SIZE = 1000;
a293d369c   Kload   Update sources to...
331

03e52840d   Kload   Init
332
333
334
335
336
337
338
339
340
341
342
343
344
  	/**
  	 * search for files with a custom where clause and value
  	 * the $wherevalue will be array_merge()d with the file id chunks
  	 *
  	 * @param string $sqlwhere
  	 * @param string $wherevalue
  	 * @return array
  	 */
  	private function searchWithWhere($sqlwhere, $wherevalue, $chunksize = self::MAX_SQL_CHUNK_SIZE) {
  
  		$ids = $this->getAll();
  
  		$files = array();
a293d369c   Kload   Update sources to...
345

31b7f2792   Kload   Upgrade to ownclo...
346
  		// divide into chunks
03e52840d   Kload   Init
347
  		$chunks = array_chunk($ids, $chunksize);
a293d369c   Kload   Update sources to...
348

03e52840d   Kload   Init
349
350
351
352
353
  		foreach ($chunks as $chunk) {
  			$placeholders = join(',', array_fill(0, count($chunk), '?'));
  			$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
  					`encrypted`, `unencrypted_size`, `etag`
  					FROM `*PREFIX*filecache` WHERE ' . $sqlwhere . ' `fileid` IN (' . $placeholders . ')';
a293d369c   Kload   Update sources to...
354

31b7f2792   Kload   Upgrade to ownclo...
355
  			$stmt = \OC_DB::prepare($sql);
03e52840d   Kload   Init
356
357
358
359
360
361
362
363
  			$result = $stmt->execute(array_merge(array($wherevalue), $chunk));
  
  			while ($row = $result->fetchRow()) {
  				if (substr($row['path'], 0, 6) === 'files/') {
  					$row['path'] = substr($row['path'], 6); // remove 'files/' from path as it's relative to '/Shared'
  				}
  				$row['mimetype'] = $this->getMimetype($row['mimetype']);
  				$row['mimepart'] = $this->getMimetype($row['mimepart']);
a293d369c   Kload   Update sources to...
364
365
366
  				if ($row['encrypted'] or ($row['unencrypted_size'] > 0 and $row['mimetype'] === 'httpd/unix-directory')) {
  					$row['encrypted_size'] = $row['size'];
  					$row['size'] = $row['unencrypted_size'];
a293d369c   Kload   Update sources to...
367
  				}
03e52840d   Kload   Init
368
369
370
371
372
373
374
375
376
377
  				$files[] = $row;
  			}
  		}
  		return $files;
  	}
  
  	/**
  	 * get the size of a folder and set it in the cache
  	 *
  	 * @param string $path
6d9380f96   Cédric Dupont   Update sources OC...
378
  	 * @param array $entry (optional) meta data of the folder
03e52840d   Kload   Init
379
380
  	 * @return int
  	 */
6d9380f96   Cédric Dupont   Update sources OC...
381
382
  	public function calculateFolderSize($path, $entry = null) {
  		$path = ($path === false) ? '' : $path;
03e52840d   Kload   Init
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  		if ($cache = $this->getSourceCache($path)) {
  			return $cache->calculateFolderSize($this->files[$path]);
  		}
  		return 0;
  	}
  
  	/**
  	 * get all file ids on the files on the storage
  	 *
  	 * @return int[]
  	 */
  	public function getAll() {
  		$ids = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL);
  		$folderBackend = \OCP\Share::getBackend('folder');
  		if ($folderBackend instanceof Share_Backend_Collection) {
  			foreach ($ids as $file) {
  				/** @var $folderBackend Share_Backend_Collection */
  				$children = $folderBackend->getChildren($file);
  				foreach ($children as $child) {
  					$ids[] = (int)$child['source'];
  				}
  
  			}
  		}
  
  		return $ids;
  	}
31b7f2792   Kload   Upgrade to ownclo...
410
411
412
413
414
415
416
  	/**
  	 * find a folder in the cache which has not been fully scanned
  	 *
  	 * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
  	 * use the one with the highest id gives the best result with the background scanner, since that is most
  	 * likely the folder where we stopped scanning previously
  	 *
6d9380f96   Cédric Dupont   Update sources OC...
417
  	 * @return boolean the path of the folder or false when no folder matched
31b7f2792   Kload   Upgrade to ownclo...
418
419
420
421
  	 */
  	public function getIncomplete() {
  		return false;
  	}
837968727   Kload   [enh] Upgrade to ...
422
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
423
  	 * get the path of a file on this storage relative to the mount point by it's id
837968727   Kload   [enh] Upgrade to ...
424
425
426
  	 *
  	 * @param int $id
  	 * @param string $pathEnd (optional) used internally for recursive calls
6d9380f96   Cédric Dupont   Update sources OC...
427
  	 * @return string|null
837968727   Kload   [enh] Upgrade to ...
428
429
430
  	 */
  	public function getPathById($id, $pathEnd = '') {
  		// direct shares are easy
6d9380f96   Cédric Dupont   Update sources OC...
431
432
433
  		$path = $this->getShareById($id);
  		if (is_string($path)) {
  			return ltrim($pathEnd, '/');
837968727   Kload   [enh] Upgrade to ...
434
435
436
437
438
439
440
441
442
443
  		} else {
  			// if the item is a direct share we try and get the path of the parent and append the name of the item to it
  			list($parent, $name) = $this->getParentInfo($id);
  			if ($parent > 0) {
  				return $this->getPathById($parent, '/' . $name . $pathEnd);
  			} else {
  				return null;
  			}
  		}
  	}
6d9380f96   Cédric Dupont   Update sources OC...
444
445
446
  	/**
  	 * @param integer $id
  	 */
837968727   Kload   [enh] Upgrade to ...
447
448
449
450
451
452
453
454
455
456
457
  	private function getShareById($id) {
  		$item = \OCP\Share::getItemSharedWithBySource('file', $id);
  		if ($item) {
  			return trim($item['file_target'], '/');
  		}
  		$item = \OCP\Share::getItemSharedWithBySource('folder', $id);
  		if ($item) {
  			return trim($item['file_target'], '/');
  		}
  		return null;
  	}
6d9380f96   Cédric Dupont   Update sources OC...
458
459
460
  	/**
  	 * @param integer $id
  	 */
837968727   Kload   [enh] Upgrade to ...
461
462
463
464
465
466
467
468
469
470
  	private function getParentInfo($id) {
  		$sql = 'SELECT `parent`, `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?';
  		$query = \OC_DB::prepare($sql);
  		$result = $query->execute(array($id));
  		if ($row = $result->fetchRow()) {
  			return array($row['parent'], $row['name']);
  		} else {
  			return array(-1, '');
  		}
  	}
03e52840d   Kload   Init
471
  }