Passed
Push — master ( d80277...8673d0 )
by Christoph
16:23 queued 12s
created
lib/private/Session/Memory.php 1 patch
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -40,76 +40,76 @@
 block discarded – undo
40 40
  * @package OC\Session
41 41
  */
42 42
 class Memory extends Session {
43
-	protected $data;
43
+    protected $data;
44 44
 
45
-	public function __construct(string $name) {
46
-		//no need to use $name since all data is already scoped to this instance
47
-		$this->data = [];
48
-	}
45
+    public function __construct(string $name) {
46
+        //no need to use $name since all data is already scoped to this instance
47
+        $this->data = [];
48
+    }
49 49
 
50
-	/**
51
-	 * @param string $key
52
-	 * @param integer $value
53
-	 */
54
-	public function set(string $key, $value) {
55
-		$this->data[$key] = $value;
56
-	}
50
+    /**
51
+     * @param string $key
52
+     * @param integer $value
53
+     */
54
+    public function set(string $key, $value) {
55
+        $this->data[$key] = $value;
56
+    }
57 57
 
58
-	/**
59
-	 * @param string $key
60
-	 * @return mixed
61
-	 */
62
-	public function get(string $key) {
63
-		if (!$this->exists($key)) {
64
-			return null;
65
-		}
66
-		return $this->data[$key];
67
-	}
58
+    /**
59
+     * @param string $key
60
+     * @return mixed
61
+     */
62
+    public function get(string $key) {
63
+        if (!$this->exists($key)) {
64
+            return null;
65
+        }
66
+        return $this->data[$key];
67
+    }
68 68
 
69
-	/**
70
-	 * @param string $key
71
-	 * @return bool
72
-	 */
73
-	public function exists(string $key): bool {
74
-		return isset($this->data[$key]);
75
-	}
69
+    /**
70
+     * @param string $key
71
+     * @return bool
72
+     */
73
+    public function exists(string $key): bool {
74
+        return isset($this->data[$key]);
75
+    }
76 76
 
77
-	/**
78
-	 * @param string $key
79
-	 */
80
-	public function remove(string $key) {
81
-		unset($this->data[$key]);
82
-	}
77
+    /**
78
+     * @param string $key
79
+     */
80
+    public function remove(string $key) {
81
+        unset($this->data[$key]);
82
+    }
83 83
 
84
-	public function clear() {
85
-		$this->data = [];
86
-	}
84
+    public function clear() {
85
+        $this->data = [];
86
+    }
87 87
 
88
-	/**
89
-	 * Stub since the session ID does not need to get regenerated for the cache
90
-	 *
91
-	 * @param bool $deleteOldSession
92
-	 */
93
-	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
94
-	}
88
+    /**
89
+     * Stub since the session ID does not need to get regenerated for the cache
90
+     *
91
+     * @param bool $deleteOldSession
92
+     */
93
+    public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
94
+    }
95 95
 
96
-	/**
97
-	 * Wrapper around session_id
98
-	 *
99
-	 * @return string
100
-	 * @throws SessionNotAvailableException
101
-	 * @since 9.1.0
102
-	 */
103
-	public function getId(): string {
104
-		throw new SessionNotAvailableException('Memory session does not have an ID');
105
-	}
96
+    /**
97
+     * Wrapper around session_id
98
+     *
99
+     * @return string
100
+     * @throws SessionNotAvailableException
101
+     * @since 9.1.0
102
+     */
103
+    public function getId(): string {
104
+        throw new SessionNotAvailableException('Memory session does not have an ID');
105
+    }
106 106
 
107
-	/**
108
-	 * Helper function for PHPUnit execution - don't use in non-test code
109
-	 */
110
-	public function reopen(): bool {
111
-		$reopened = $this->sessionClosed;
112
-		$this->sessionClosed = false;
113
-		return $reopened;
114
-	}
107
+    /**
108
+     * Helper function for PHPUnit execution - don't use in non-test code
109
+     */
110
+    public function reopen(): bool {
111
+        $reopened = $this->sessionClosed;
112
+        $this->sessionClosed = false;
113
+        return $reopened;
114
+    }
115 115
 }
Please login to merge, or discard this patch.
lib/private/Session/Internal.php 1 patch
Indentation   +179 added lines, -179 removed lines patch added patch discarded remove patch
@@ -45,183 +45,183 @@
 block discarded – undo
45 45
  * @package OC\Session
46 46
  */
47 47
 class Internal extends Session {
48
-	/**
49
-	 * @param string $name
50
-	 * @throws \Exception
51
-	 */
52
-	public function __construct(string $name) {
53
-		set_error_handler([$this, 'trapError']);
54
-		$this->invoke('session_name', [$name]);
55
-		try {
56
-			$this->startSession();
57
-		} catch (\Exception $e) {
58
-			setcookie($this->invoke('session_name'), '', -1, \OC::$WEBROOT ?: '/');
59
-		}
60
-		restore_error_handler();
61
-		if (!isset($_SESSION)) {
62
-			throw new \Exception('Failed to start session');
63
-		}
64
-	}
65
-
66
-	/**
67
-	 * @param string $key
68
-	 * @param integer $value
69
-	 */
70
-	public function set(string $key, $value) {
71
-		$reopened = $this->reopen();
72
-		$_SESSION[$key] = $value;
73
-		if ($reopened) {
74
-			$this->close();
75
-		}
76
-	}
77
-
78
-	/**
79
-	 * @param string $key
80
-	 * @return mixed
81
-	 */
82
-	public function get(string $key) {
83
-		if (!$this->exists($key)) {
84
-			return null;
85
-		}
86
-		return $_SESSION[$key];
87
-	}
88
-
89
-	/**
90
-	 * @param string $key
91
-	 * @return bool
92
-	 */
93
-	public function exists(string $key): bool {
94
-		return isset($_SESSION[$key]);
95
-	}
96
-
97
-	/**
98
-	 * @param string $key
99
-	 */
100
-	public function remove(string $key) {
101
-		if (isset($_SESSION[$key])) {
102
-			unset($_SESSION[$key]);
103
-		}
104
-	}
105
-
106
-	public function clear() {
107
-		$this->reopen();
108
-		$this->invoke('session_unset');
109
-		$this->regenerateId();
110
-		$this->invoke('session_write_close');
111
-		$this->startSession(true);
112
-		$_SESSION = [];
113
-	}
114
-
115
-	public function close() {
116
-		$this->invoke('session_write_close');
117
-		parent::close();
118
-	}
119
-
120
-	/**
121
-	 * Wrapper around session_regenerate_id
122
-	 *
123
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
124
-	 * @param bool $updateToken Wheater to update the associated auth token
125
-	 * @return void
126
-	 */
127
-	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
128
-		$this->reopen();
129
-		$oldId = null;
130
-
131
-		if ($updateToken) {
132
-			// Get the old id to update the token
133
-			try {
134
-				$oldId = $this->getId();
135
-			} catch (SessionNotAvailableException $e) {
136
-				// We can't update a token if there is no previous id
137
-				$updateToken = false;
138
-			}
139
-		}
140
-
141
-		try {
142
-			@session_regenerate_id($deleteOldSession);
143
-		} catch (\Error $e) {
144
-			$this->trapError($e->getCode(), $e->getMessage());
145
-		}
146
-
147
-		if ($updateToken) {
148
-			// Get the new id to update the token
149
-			$newId = $this->getId();
150
-
151
-			/** @var IProvider $tokenProvider */
152
-			$tokenProvider = \OC::$server->query(IProvider::class);
153
-
154
-			try {
155
-				$tokenProvider->renewSessionToken($oldId, $newId);
156
-			} catch (InvalidTokenException $e) {
157
-				// Just ignore
158
-			}
159
-		}
160
-	}
161
-
162
-	/**
163
-	 * Wrapper around session_id
164
-	 *
165
-	 * @return string
166
-	 * @throws SessionNotAvailableException
167
-	 * @since 9.1.0
168
-	 */
169
-	public function getId(): string {
170
-		$id = $this->invoke('session_id', [], true);
171
-		if ($id === '') {
172
-			throw new SessionNotAvailableException();
173
-		}
174
-		return $id;
175
-	}
176
-
177
-	/**
178
-	 * @throws \Exception
179
-	 */
180
-	public function reopen(): bool {
181
-		if ($this->sessionClosed) {
182
-			$this->startSession(false, false);
183
-			$this->sessionClosed = false;
184
-			return true;
185
-		}
186
-
187
-		return false;
188
-	}
189
-
190
-	/**
191
-	 * @param int $errorNumber
192
-	 * @param string $errorString
193
-	 * @throws \ErrorException
194
-	 */
195
-	public function trapError(int $errorNumber, string $errorString) {
196
-		if ($errorNumber & E_ERROR) {
197
-			throw new \ErrorException($errorString);
198
-		}
199
-	}
200
-
201
-	/**
202
-	 * @param string $functionName the full session_* function name
203
-	 * @param array $parameters
204
-	 * @param bool $silence whether to suppress warnings
205
-	 * @throws \ErrorException via trapError
206
-	 * @return mixed
207
-	 */
208
-	private function invoke(string $functionName, array $parameters = [], bool $silence = false) {
209
-		try {
210
-			if ($silence) {
211
-				return @call_user_func_array($functionName, $parameters);
212
-			} else {
213
-				return call_user_func_array($functionName, $parameters);
214
-			}
215
-		} catch (\Error $e) {
216
-			$this->trapError($e->getCode(), $e->getMessage());
217
-		}
218
-	}
219
-
220
-	private function startSession(bool $silence = false, bool $readAndClose = true) {
221
-		$sessionParams = ['cookie_samesite' => 'Lax'];
222
-		if (\OC::hasSessionRelaxedExpiry()) {
223
-			$sessionParams['read_and_close'] = $readAndClose;
224
-		}
225
-		$this->invoke('session_start', [$sessionParams], $silence);
226
-	}
48
+    /**
49
+     * @param string $name
50
+     * @throws \Exception
51
+     */
52
+    public function __construct(string $name) {
53
+        set_error_handler([$this, 'trapError']);
54
+        $this->invoke('session_name', [$name]);
55
+        try {
56
+            $this->startSession();
57
+        } catch (\Exception $e) {
58
+            setcookie($this->invoke('session_name'), '', -1, \OC::$WEBROOT ?: '/');
59
+        }
60
+        restore_error_handler();
61
+        if (!isset($_SESSION)) {
62
+            throw new \Exception('Failed to start session');
63
+        }
64
+    }
65
+
66
+    /**
67
+     * @param string $key
68
+     * @param integer $value
69
+     */
70
+    public function set(string $key, $value) {
71
+        $reopened = $this->reopen();
72
+        $_SESSION[$key] = $value;
73
+        if ($reopened) {
74
+            $this->close();
75
+        }
76
+    }
77
+
78
+    /**
79
+     * @param string $key
80
+     * @return mixed
81
+     */
82
+    public function get(string $key) {
83
+        if (!$this->exists($key)) {
84
+            return null;
85
+        }
86
+        return $_SESSION[$key];
87
+    }
88
+
89
+    /**
90
+     * @param string $key
91
+     * @return bool
92
+     */
93
+    public function exists(string $key): bool {
94
+        return isset($_SESSION[$key]);
95
+    }
96
+
97
+    /**
98
+     * @param string $key
99
+     */
100
+    public function remove(string $key) {
101
+        if (isset($_SESSION[$key])) {
102
+            unset($_SESSION[$key]);
103
+        }
104
+    }
105
+
106
+    public function clear() {
107
+        $this->reopen();
108
+        $this->invoke('session_unset');
109
+        $this->regenerateId();
110
+        $this->invoke('session_write_close');
111
+        $this->startSession(true);
112
+        $_SESSION = [];
113
+    }
114
+
115
+    public function close() {
116
+        $this->invoke('session_write_close');
117
+        parent::close();
118
+    }
119
+
120
+    /**
121
+     * Wrapper around session_regenerate_id
122
+     *
123
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
124
+     * @param bool $updateToken Wheater to update the associated auth token
125
+     * @return void
126
+     */
127
+    public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
128
+        $this->reopen();
129
+        $oldId = null;
130
+
131
+        if ($updateToken) {
132
+            // Get the old id to update the token
133
+            try {
134
+                $oldId = $this->getId();
135
+            } catch (SessionNotAvailableException $e) {
136
+                // We can't update a token if there is no previous id
137
+                $updateToken = false;
138
+            }
139
+        }
140
+
141
+        try {
142
+            @session_regenerate_id($deleteOldSession);
143
+        } catch (\Error $e) {
144
+            $this->trapError($e->getCode(), $e->getMessage());
145
+        }
146
+
147
+        if ($updateToken) {
148
+            // Get the new id to update the token
149
+            $newId = $this->getId();
150
+
151
+            /** @var IProvider $tokenProvider */
152
+            $tokenProvider = \OC::$server->query(IProvider::class);
153
+
154
+            try {
155
+                $tokenProvider->renewSessionToken($oldId, $newId);
156
+            } catch (InvalidTokenException $e) {
157
+                // Just ignore
158
+            }
159
+        }
160
+    }
161
+
162
+    /**
163
+     * Wrapper around session_id
164
+     *
165
+     * @return string
166
+     * @throws SessionNotAvailableException
167
+     * @since 9.1.0
168
+     */
169
+    public function getId(): string {
170
+        $id = $this->invoke('session_id', [], true);
171
+        if ($id === '') {
172
+            throw new SessionNotAvailableException();
173
+        }
174
+        return $id;
175
+    }
176
+
177
+    /**
178
+     * @throws \Exception
179
+     */
180
+    public function reopen(): bool {
181
+        if ($this->sessionClosed) {
182
+            $this->startSession(false, false);
183
+            $this->sessionClosed = false;
184
+            return true;
185
+        }
186
+
187
+        return false;
188
+    }
189
+
190
+    /**
191
+     * @param int $errorNumber
192
+     * @param string $errorString
193
+     * @throws \ErrorException
194
+     */
195
+    public function trapError(int $errorNumber, string $errorString) {
196
+        if ($errorNumber & E_ERROR) {
197
+            throw new \ErrorException($errorString);
198
+        }
199
+    }
200
+
201
+    /**
202
+     * @param string $functionName the full session_* function name
203
+     * @param array $parameters
204
+     * @param bool $silence whether to suppress warnings
205
+     * @throws \ErrorException via trapError
206
+     * @return mixed
207
+     */
208
+    private function invoke(string $functionName, array $parameters = [], bool $silence = false) {
209
+        try {
210
+            if ($silence) {
211
+                return @call_user_func_array($functionName, $parameters);
212
+            } else {
213
+                return call_user_func_array($functionName, $parameters);
214
+            }
215
+        } catch (\Error $e) {
216
+            $this->trapError($e->getCode(), $e->getMessage());
217
+        }
218
+    }
219
+
220
+    private function startSession(bool $silence = false, bool $readAndClose = true) {
221
+        $sessionParams = ['cookie_samesite' => 'Lax'];
222
+        if (\OC::hasSessionRelaxedExpiry()) {
223
+            $sessionParams['read_and_close'] = $readAndClose;
224
+        }
225
+        $this->invoke('session_start', [$sessionParams], $silence);
226
+    }
227 227
 }
Please login to merge, or discard this patch.
lib/private/Repair/NC22/LookupServerSendCheck.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -33,19 +33,19 @@
 block discarded – undo
33 33
 use OCP\Migration\IRepairStep;
34 34
 
35 35
 class LookupServerSendCheck implements IRepairStep {
36
-	private IJobList $jobList;
37
-	private IConfig $config;
36
+    private IJobList $jobList;
37
+    private IConfig $config;
38 38
 
39
-	public function __construct(IJobList $jobList, IConfig $config) {
40
-		$this->jobList = $jobList;
41
-		$this->config = $config;
42
-	}
39
+    public function __construct(IJobList $jobList, IConfig $config) {
40
+        $this->jobList = $jobList;
41
+        $this->config = $config;
42
+    }
43 43
 
44
-	public function getName(): string {
45
-		return 'Add background job to set the lookup server share state for users';
46
-	}
44
+    public function getName(): string {
45
+        return 'Add background job to set the lookup server share state for users';
46
+    }
47 47
 
48
-	public function run(IOutput $output): void {
49
-		$this->jobList->add(LookupServerSendCheckBackgroundJob::class);
50
-	}
48
+    public function run(IOutput $output): void {
49
+        $this->jobList->add(LookupServerSendCheckBackgroundJob::class);
50
+    }
51 51
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/Cache.php 2 patches
Indentation   +1085 added lines, -1085 removed lines patch added patch discarded remove patch
@@ -74,1089 +74,1089 @@
 block discarded – undo
74 74
  * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater
75 75
  */
76 76
 class Cache implements ICache {
77
-	use MoveFromCacheTrait {
78
-		MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
79
-	}
80
-
81
-	/**
82
-	 * @var array partial data for the cache
83
-	 */
84
-	protected $partial = [];
85
-
86
-	/**
87
-	 * @var string
88
-	 */
89
-	protected $storageId;
90
-
91
-	private $storage;
92
-
93
-	/**
94
-	 * @var Storage $storageCache
95
-	 */
96
-	protected $storageCache;
97
-
98
-	/** @var IMimeTypeLoader */
99
-	protected $mimetypeLoader;
100
-
101
-	/**
102
-	 * @var IDBConnection
103
-	 */
104
-	protected $connection;
105
-
106
-	/**
107
-	 * @var IEventDispatcher
108
-	 */
109
-	protected $eventDispatcher;
110
-
111
-	/** @var QuerySearchHelper */
112
-	protected $querySearchHelper;
113
-
114
-	/**
115
-	 * @param IStorage $storage
116
-	 */
117
-	public function __construct(IStorage $storage) {
118
-		$this->storageId = $storage->getId();
119
-		$this->storage = $storage;
120
-		if (strlen($this->storageId) > 64) {
121
-			$this->storageId = md5($this->storageId);
122
-		}
123
-
124
-		$this->storageCache = new Storage($storage);
125
-		$this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
126
-		$this->connection = \OC::$server->getDatabaseConnection();
127
-		$this->eventDispatcher = \OC::$server->get(IEventDispatcher::class);
128
-		$this->querySearchHelper = \OC::$server->query(QuerySearchHelper::class);
129
-	}
130
-
131
-	protected function getQueryBuilder() {
132
-		return new CacheQueryBuilder(
133
-			$this->connection,
134
-			\OC::$server->getSystemConfig(),
135
-			\OC::$server->get(LoggerInterface::class)
136
-		);
137
-	}
138
-
139
-	/**
140
-	 * Get the numeric storage id for this cache's storage
141
-	 *
142
-	 * @return int
143
-	 */
144
-	public function getNumericStorageId() {
145
-		return $this->storageCache->getNumericId();
146
-	}
147
-
148
-	/**
149
-	 * get the stored metadata of a file or folder
150
-	 *
151
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
152
-	 * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
153
-	 */
154
-	public function get($file) {
155
-		$query = $this->getQueryBuilder();
156
-		$query->selectFileCache();
157
-
158
-		if (is_string($file) || $file == '') {
159
-			// normalize file
160
-			$file = $this->normalize($file);
161
-
162
-			$query->whereStorageId($this->getNumericStorageId())
163
-				->wherePath($file);
164
-		} else { //file id
165
-			$query->whereFileId($file);
166
-		}
167
-
168
-		$result = $query->execute();
169
-		$data = $result->fetch();
170
-		$result->closeCursor();
171
-
172
-		//merge partial data
173
-		if (!$data && is_string($file) && isset($this->partial[$file])) {
174
-			return $this->partial[$file];
175
-		} elseif (!$data) {
176
-			return $data;
177
-		} else {
178
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
179
-		}
180
-	}
181
-
182
-	/**
183
-	 * Create a CacheEntry from database row
184
-	 *
185
-	 * @param array $data
186
-	 * @param IMimeTypeLoader $mimetypeLoader
187
-	 * @return CacheEntry
188
-	 */
189
-	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
190
-		//fix types
191
-		$data['name'] = (string)$data['name'];
192
-		$data['path'] = (string)$data['path'];
193
-		$data['fileid'] = (int)$data['fileid'];
194
-		$data['parent'] = (int)$data['parent'];
195
-		$data['size'] = Util::numericToNumber($data['size']);
196
-		$data['unencrypted_size'] = Util::numericToNumber($data['unencrypted_size'] ?? 0);
197
-		$data['mtime'] = (int)$data['mtime'];
198
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
199
-		$data['encryptedVersion'] = (int)$data['encrypted'];
200
-		$data['encrypted'] = (bool)$data['encrypted'];
201
-		$data['storage_id'] = $data['storage'];
202
-		$data['storage'] = (int)$data['storage'];
203
-		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
204
-		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
205
-		if ($data['storage_mtime'] == 0) {
206
-			$data['storage_mtime'] = $data['mtime'];
207
-		}
208
-		$data['permissions'] = (int)$data['permissions'];
209
-		if (isset($data['creation_time'])) {
210
-			$data['creation_time'] = (int)$data['creation_time'];
211
-		}
212
-		if (isset($data['upload_time'])) {
213
-			$data['upload_time'] = (int)$data['upload_time'];
214
-		}
215
-		return new CacheEntry($data);
216
-	}
217
-
218
-	/**
219
-	 * get the metadata of all files stored in $folder
220
-	 *
221
-	 * @param string $folder
222
-	 * @return ICacheEntry[]
223
-	 */
224
-	public function getFolderContents($folder) {
225
-		$fileId = $this->getId($folder);
226
-		return $this->getFolderContentsById($fileId);
227
-	}
228
-
229
-	/**
230
-	 * get the metadata of all files stored in $folder
231
-	 *
232
-	 * @param int $fileId the file id of the folder
233
-	 * @return ICacheEntry[]
234
-	 */
235
-	public function getFolderContentsById($fileId) {
236
-		if ($fileId > -1) {
237
-			$query = $this->getQueryBuilder();
238
-			$query->selectFileCache()
239
-				->whereParent($fileId)
240
-				->orderBy('name', 'ASC');
241
-
242
-			$result = $query->execute();
243
-			$files = $result->fetchAll();
244
-			$result->closeCursor();
245
-
246
-			return array_map(function (array $data) {
247
-				return self::cacheEntryFromData($data, $this->mimetypeLoader);
248
-			}, $files);
249
-		}
250
-		return [];
251
-	}
252
-
253
-	/**
254
-	 * insert or update meta data for a file or folder
255
-	 *
256
-	 * @param string $file
257
-	 * @param array $data
258
-	 *
259
-	 * @return int file id
260
-	 * @throws \RuntimeException
261
-	 */
262
-	public function put($file, array $data) {
263
-		if (($id = $this->getId($file)) > -1) {
264
-			$this->update($id, $data);
265
-			return $id;
266
-		} else {
267
-			return $this->insert($file, $data);
268
-		}
269
-	}
270
-
271
-	/**
272
-	 * insert meta data for a new file or folder
273
-	 *
274
-	 * @param string $file
275
-	 * @param array $data
276
-	 *
277
-	 * @return int file id
278
-	 * @throws \RuntimeException
279
-	 */
280
-	public function insert($file, array $data) {
281
-		// normalize file
282
-		$file = $this->normalize($file);
283
-
284
-		if (isset($this->partial[$file])) { //add any saved partial data
285
-			$data = array_merge($this->partial[$file], $data);
286
-			unset($this->partial[$file]);
287
-		}
288
-
289
-		$requiredFields = ['size', 'mtime', 'mimetype'];
290
-		foreach ($requiredFields as $field) {
291
-			if (!isset($data[$field])) { //data not complete save as partial and return
292
-				$this->partial[$file] = $data;
293
-				return -1;
294
-			}
295
-		}
296
-
297
-		$data['path'] = $file;
298
-		if (!isset($data['parent'])) {
299
-			$data['parent'] = $this->getParentId($file);
300
-		}
301
-		$data['name'] = basename($file);
302
-
303
-		[$values, $extensionValues] = $this->normalizeData($data);
304
-		$storageId = $this->getNumericStorageId();
305
-		$values['storage'] = $storageId;
306
-
307
-		try {
308
-			$builder = $this->connection->getQueryBuilder();
309
-			$builder->insert('filecache');
310
-
311
-			foreach ($values as $column => $value) {
312
-				$builder->setValue($column, $builder->createNamedParameter($value));
313
-			}
314
-
315
-			if ($builder->execute()) {
316
-				$fileId = $builder->getLastInsertId();
317
-
318
-				if (count($extensionValues)) {
319
-					$query = $this->getQueryBuilder();
320
-					$query->insert('filecache_extended');
321
-
322
-					$query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
323
-					foreach ($extensionValues as $column => $value) {
324
-						$query->setValue($column, $query->createNamedParameter($value));
325
-					}
326
-					$query->execute();
327
-				}
328
-
329
-				$event = new CacheEntryInsertedEvent($this->storage, $file, $fileId, $storageId);
330
-				$this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
331
-				$this->eventDispatcher->dispatchTyped($event);
332
-				return $fileId;
333
-			}
334
-		} catch (UniqueConstraintViolationException $e) {
335
-			// entry exists already
336
-			if ($this->connection->inTransaction()) {
337
-				$this->connection->commit();
338
-				$this->connection->beginTransaction();
339
-			}
340
-		}
341
-
342
-		// The file was created in the mean time
343
-		if (($id = $this->getId($file)) > -1) {
344
-			$this->update($id, $data);
345
-			return $id;
346
-		} else {
347
-			throw new \RuntimeException('File entry could not be inserted but could also not be selected with getId() in order to perform an update. Please try again.');
348
-		}
349
-	}
350
-
351
-	/**
352
-	 * update the metadata of an existing file or folder in the cache
353
-	 *
354
-	 * @param int $id the fileid of the existing file or folder
355
-	 * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
356
-	 */
357
-	public function update($id, array $data) {
358
-		if (isset($data['path'])) {
359
-			// normalize path
360
-			$data['path'] = $this->normalize($data['path']);
361
-		}
362
-
363
-		if (isset($data['name'])) {
364
-			// normalize path
365
-			$data['name'] = $this->normalize($data['name']);
366
-		}
367
-
368
-		[$values, $extensionValues] = $this->normalizeData($data);
369
-
370
-		if (count($values)) {
371
-			$query = $this->getQueryBuilder();
372
-
373
-			$query->update('filecache')
374
-				->whereFileId($id)
375
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
376
-					return $query->expr()->orX(
377
-						$query->expr()->neq($key, $query->createNamedParameter($value)),
378
-						$query->expr()->isNull($key)
379
-					);
380
-				}, array_keys($values), array_values($values))));
381
-
382
-			foreach ($values as $key => $value) {
383
-				$query->set($key, $query->createNamedParameter($value));
384
-			}
385
-
386
-			$query->execute();
387
-		}
388
-
389
-		if (count($extensionValues)) {
390
-			try {
391
-				$query = $this->getQueryBuilder();
392
-				$query->insert('filecache_extended');
393
-
394
-				$query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
395
-				foreach ($extensionValues as $column => $value) {
396
-					$query->setValue($column, $query->createNamedParameter($value));
397
-				}
398
-
399
-				$query->execute();
400
-			} catch (UniqueConstraintViolationException $e) {
401
-				$query = $this->getQueryBuilder();
402
-				$query->update('filecache_extended')
403
-					->whereFileId($id)
404
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
405
-						return $query->expr()->orX(
406
-							$query->expr()->neq($key, $query->createNamedParameter($value)),
407
-							$query->expr()->isNull($key)
408
-						);
409
-					}, array_keys($extensionValues), array_values($extensionValues))));
410
-
411
-				foreach ($extensionValues as $key => $value) {
412
-					$query->set($key, $query->createNamedParameter($value));
413
-				}
414
-
415
-				$query->execute();
416
-			}
417
-		}
418
-
419
-		$path = $this->getPathById($id);
420
-		// path can still be null if the file doesn't exist
421
-		if ($path !== null) {
422
-			$event = new CacheEntryUpdatedEvent($this->storage, $path, $id, $this->getNumericStorageId());
423
-			$this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
424
-			$this->eventDispatcher->dispatchTyped($event);
425
-		}
426
-	}
427
-
428
-	/**
429
-	 * extract query parts and params array from data array
430
-	 *
431
-	 * @param array $data
432
-	 * @return array
433
-	 */
434
-	protected function normalizeData(array $data): array {
435
-		$fields = [
436
-			'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
437
-			'etag', 'permissions', 'checksum', 'storage', 'unencrypted_size'];
438
-		$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
439
-
440
-		$doNotCopyStorageMTime = false;
441
-		if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
442
-			// this horrific magic tells it to not copy storage_mtime to mtime
443
-			unset($data['mtime']);
444
-			$doNotCopyStorageMTime = true;
445
-		}
446
-
447
-		$params = [];
448
-		$extensionParams = [];
449
-		foreach ($data as $name => $value) {
450
-			if (array_search($name, $fields) !== false) {
451
-				if ($name === 'path') {
452
-					$params['path_hash'] = md5($value);
453
-				} elseif ($name === 'mimetype') {
454
-					$params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
455
-					$value = $this->mimetypeLoader->getId($value);
456
-				} elseif ($name === 'storage_mtime') {
457
-					if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
458
-						$params['mtime'] = $value;
459
-					}
460
-				} elseif ($name === 'encrypted') {
461
-					if (isset($data['encryptedVersion'])) {
462
-						$value = $data['encryptedVersion'];
463
-					} else {
464
-						// Boolean to integer conversion
465
-						$value = $value ? 1 : 0;
466
-					}
467
-				}
468
-				$params[$name] = $value;
469
-			}
470
-			if (array_search($name, $extensionFields) !== false) {
471
-				$extensionParams[$name] = $value;
472
-			}
473
-		}
474
-		return [$params, array_filter($extensionParams)];
475
-	}
476
-
477
-	/**
478
-	 * get the file id for a file
479
-	 *
480
-	 * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
481
-	 *
482
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
483
-	 *
484
-	 * @param string $file
485
-	 * @return int
486
-	 */
487
-	public function getId($file) {
488
-		// normalize file
489
-		$file = $this->normalize($file);
490
-
491
-		$query = $this->getQueryBuilder();
492
-		$query->select('fileid')
493
-			->from('filecache')
494
-			->whereStorageId($this->getNumericStorageId())
495
-			->wherePath($file);
496
-
497
-		$result = $query->execute();
498
-		$id = $result->fetchOne();
499
-		$result->closeCursor();
500
-
501
-		return $id === false ? -1 : (int)$id;
502
-	}
503
-
504
-	/**
505
-	 * get the id of the parent folder of a file
506
-	 *
507
-	 * @param string $file
508
-	 * @return int
509
-	 */
510
-	public function getParentId($file) {
511
-		if ($file === '') {
512
-			return -1;
513
-		} else {
514
-			$parent = $this->getParentPath($file);
515
-			return (int)$this->getId($parent);
516
-		}
517
-	}
518
-
519
-	private function getParentPath($path) {
520
-		$parent = dirname($path);
521
-		if ($parent === '.') {
522
-			$parent = '';
523
-		}
524
-		return $parent;
525
-	}
526
-
527
-	/**
528
-	 * check if a file is available in the cache
529
-	 *
530
-	 * @param string $file
531
-	 * @return bool
532
-	 */
533
-	public function inCache($file) {
534
-		return $this->getId($file) != -1;
535
-	}
536
-
537
-	/**
538
-	 * remove a file or folder from the cache
539
-	 *
540
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
541
-	 *
542
-	 * @param string $file
543
-	 */
544
-	public function remove($file) {
545
-		$entry = $this->get($file);
546
-
547
-		if ($entry instanceof ICacheEntry) {
548
-			$query = $this->getQueryBuilder();
549
-			$query->delete('filecache')
550
-				->whereFileId($entry->getId());
551
-			$query->execute();
552
-
553
-			$query = $this->getQueryBuilder();
554
-			$query->delete('filecache_extended')
555
-				->whereFileId($entry->getId());
556
-			$query->execute();
557
-
558
-			if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
559
-				$this->removeChildren($entry);
560
-			}
561
-
562
-			$this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $entry->getPath(), $entry->getId(), $this->getNumericStorageId()));
563
-		}
564
-	}
565
-
566
-	/**
567
-	 * Remove all children of a folder
568
-	 *
569
-	 * @param ICacheEntry $entry the cache entry of the folder to remove the children of
570
-	 * @throws \OC\DatabaseException
571
-	 */
572
-	private function removeChildren(ICacheEntry $entry) {
573
-		$parentIds = [$entry->getId()];
574
-		$queue = [$entry->getId()];
575
-		$deletedIds = [];
576
-		$deletedPaths = [];
577
-
578
-		// we walk depth first through the file tree, removing all filecache_extended attributes while we walk
579
-		// and collecting all folder ids to later use to delete the filecache entries
580
-		while ($entryId = array_pop($queue)) {
581
-			$children = $this->getFolderContentsById($entryId);
582
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
583
-				return $cacheEntry->getId();
584
-			}, $children);
585
-			$childPaths = array_map(function (ICacheEntry $cacheEntry) {
586
-				return $cacheEntry->getPath();
587
-			}, $children);
588
-
589
-			$deletedIds = array_merge($deletedIds, $childIds);
590
-			$deletedPaths = array_merge($deletedPaths, $childPaths);
591
-
592
-			$query = $this->getQueryBuilder();
593
-			$query->delete('filecache_extended')
594
-				->where($query->expr()->in('fileid', $query->createParameter('childIds')));
595
-
596
-			foreach (array_chunk($childIds, 1000) as $childIdChunk) {
597
-				$query->setParameter('childIds', $childIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
598
-				$query->execute();
599
-			}
600
-
601
-			/** @var ICacheEntry[] $childFolders */
602
-			$childFolders = array_filter($children, function ($child) {
603
-				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
604
-			});
605
-			foreach ($childFolders as $folder) {
606
-				$parentIds[] = $folder->getId();
607
-				$queue[] = $folder->getId();
608
-			}
609
-		}
610
-
611
-		$query = $this->getQueryBuilder();
612
-		$query->delete('filecache')
613
-			->whereParentInParameter('parentIds');
614
-
615
-		foreach (array_chunk($parentIds, 1000) as $parentIdChunk) {
616
-			$query->setParameter('parentIds', $parentIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
617
-			$query->execute();
618
-		}
619
-
620
-		foreach (array_combine($deletedIds, $deletedPaths) as $fileId => $filePath) {
621
-			$cacheEntryRemovedEvent = new CacheEntryRemovedEvent(
622
-				$this->storage,
623
-				$filePath,
624
-				$fileId,
625
-				$this->getNumericStorageId()
626
-			);
627
-			$this->eventDispatcher->dispatchTyped($cacheEntryRemovedEvent);
628
-		}
629
-	}
630
-
631
-	/**
632
-	 * Move a file or folder in the cache
633
-	 *
634
-	 * @param string $source
635
-	 * @param string $target
636
-	 */
637
-	public function move($source, $target) {
638
-		$this->moveFromCache($this, $source, $target);
639
-	}
640
-
641
-	/**
642
-	 * Get the storage id and path needed for a move
643
-	 *
644
-	 * @param string $path
645
-	 * @return array [$storageId, $internalPath]
646
-	 */
647
-	protected function getMoveInfo($path) {
648
-		return [$this->getNumericStorageId(), $path];
649
-	}
650
-
651
-	protected function hasEncryptionWrapper(): bool {
652
-		return $this->storage->instanceOfStorage(Encryption::class);
653
-	}
654
-
655
-	/**
656
-	 * Move a file or folder in the cache
657
-	 *
658
-	 * @param ICache $sourceCache
659
-	 * @param string $sourcePath
660
-	 * @param string $targetPath
661
-	 * @throws \OC\DatabaseException
662
-	 * @throws \Exception if the given storages have an invalid id
663
-	 */
664
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
665
-		if ($sourceCache instanceof Cache) {
666
-			// normalize source and target
667
-			$sourcePath = $this->normalize($sourcePath);
668
-			$targetPath = $this->normalize($targetPath);
669
-
670
-			$sourceData = $sourceCache->get($sourcePath);
671
-			if ($sourceData === false) {
672
-				throw new \Exception('Invalid source storage path: ' . $sourcePath);
673
-			}
674
-
675
-			$sourceId = $sourceData['fileid'];
676
-			$newParentId = $this->getParentId($targetPath);
677
-
678
-			[$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
679
-			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
680
-
681
-			if (is_null($sourceStorageId) || $sourceStorageId === false) {
682
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
683
-			}
684
-			if (is_null($targetStorageId) || $targetStorageId === false) {
685
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
686
-			}
687
-
688
-			$this->connection->beginTransaction();
689
-			if ($sourceData['mimetype'] === 'httpd/unix-directory') {
690
-				//update all child entries
691
-				$sourceLength = mb_strlen($sourcePath);
692
-				$query = $this->connection->getQueryBuilder();
693
-
694
-				$fun = $query->func();
695
-				$newPathFunction = $fun->concat(
696
-					$query->createNamedParameter($targetPath),
697
-					$fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
698
-				);
699
-				$query->update('filecache')
700
-					->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
701
-					->set('path_hash', $fun->md5($newPathFunction))
702
-					->set('path', $newPathFunction)
703
-					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
704
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
705
-
706
-				// when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
707
-				if ($sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
708
-					$query->set('encrypted', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT));
709
-				}
710
-
711
-				try {
712
-					$query->execute();
713
-				} catch (\OC\DatabaseException $e) {
714
-					$this->connection->rollBack();
715
-					throw $e;
716
-				}
717
-			}
718
-
719
-			$query = $this->getQueryBuilder();
720
-			$query->update('filecache')
721
-				->set('storage', $query->createNamedParameter($targetStorageId))
722
-				->set('path', $query->createNamedParameter($targetPath))
723
-				->set('path_hash', $query->createNamedParameter(md5($targetPath)))
724
-				->set('name', $query->createNamedParameter(basename($targetPath)))
725
-				->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
726
-				->whereFileId($sourceId);
727
-
728
-			// when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
729
-			if ($sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
730
-				$query->set('encrypted', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT));
731
-			}
732
-
733
-			$query->execute();
734
-
735
-			$this->connection->commit();
736
-
737
-			if ($sourceCache->getNumericStorageId() !== $this->getNumericStorageId()) {
738
-				$this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $sourcePath, $sourceId, $sourceCache->getNumericStorageId()));
739
-				$event = new CacheEntryInsertedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
740
-				$this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
741
-				$this->eventDispatcher->dispatchTyped($event);
742
-			} else {
743
-				$event = new CacheEntryUpdatedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
744
-				$this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
745
-				$this->eventDispatcher->dispatchTyped($event);
746
-			}
747
-		} else {
748
-			$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
749
-		}
750
-	}
751
-
752
-	/**
753
-	 * remove all entries for files that are stored on the storage from the cache
754
-	 */
755
-	public function clear() {
756
-		$query = $this->getQueryBuilder();
757
-		$query->delete('filecache')
758
-			->whereStorageId($this->getNumericStorageId());
759
-		$query->execute();
760
-
761
-		$query = $this->connection->getQueryBuilder();
762
-		$query->delete('storages')
763
-			->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
764
-		$query->execute();
765
-	}
766
-
767
-	/**
768
-	 * Get the scan status of a file
769
-	 *
770
-	 * - Cache::NOT_FOUND: File is not in the cache
771
-	 * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
772
-	 * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
773
-	 * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
774
-	 *
775
-	 * @param string $file
776
-	 *
777
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
778
-	 */
779
-	public function getStatus($file) {
780
-		// normalize file
781
-		$file = $this->normalize($file);
782
-
783
-		$query = $this->getQueryBuilder();
784
-		$query->select('size')
785
-			->from('filecache')
786
-			->whereStorageId($this->getNumericStorageId())
787
-			->wherePath($file);
788
-
789
-		$result = $query->execute();
790
-		$size = $result->fetchOne();
791
-		$result->closeCursor();
792
-
793
-		if ($size !== false) {
794
-			if ((int)$size === -1) {
795
-				return self::SHALLOW;
796
-			} else {
797
-				return self::COMPLETE;
798
-			}
799
-		} else {
800
-			if (isset($this->partial[$file])) {
801
-				return self::PARTIAL;
802
-			} else {
803
-				return self::NOT_FOUND;
804
-			}
805
-		}
806
-	}
807
-
808
-	/**
809
-	 * search for files matching $pattern
810
-	 *
811
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
812
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
813
-	 */
814
-	public function search($pattern) {
815
-		$operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', $pattern);
816
-		return $this->searchQuery(new SearchQuery($operator, 0, 0, [], null));
817
-	}
818
-
819
-	/**
820
-	 * search for files by mimetype
821
-	 *
822
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
823
-	 *        where it will search for all mimetypes in the group ('image/*')
824
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
825
-	 */
826
-	public function searchByMime($mimetype) {
827
-		if (!str_contains($mimetype, '/')) {
828
-			$operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%');
829
-		} else {
830
-			$operator = new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype);
831
-		}
832
-		return $this->searchQuery(new SearchQuery($operator, 0, 0, [], null));
833
-	}
834
-
835
-	public function searchQuery(ISearchQuery $searchQuery) {
836
-		return current($this->querySearchHelper->searchInCaches($searchQuery, [$this]));
837
-	}
838
-
839
-	/**
840
-	 * Re-calculate the folder size and the size of all parent folders
841
-	 *
842
-	 * @param string|boolean $path
843
-	 * @param array $data (optional) meta data of the folder
844
-	 */
845
-	public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
846
-		$this->calculateFolderSize($path, $data);
847
-		if ($path !== '') {
848
-			$parent = dirname($path);
849
-			if ($parent === '.' || $parent === '/') {
850
-				$parent = '';
851
-			}
852
-			if ($isBackgroundScan) {
853
-				$parentData = $this->get($parent);
854
-				if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
855
-					$this->correctFolderSize($parent, $parentData, $isBackgroundScan);
856
-				}
857
-			} else {
858
-				$this->correctFolderSize($parent);
859
-			}
860
-		}
861
-	}
862
-
863
-	/**
864
-	 * get the incomplete count that shares parent $folder
865
-	 *
866
-	 * @param int $fileId the file id of the folder
867
-	 * @return int
868
-	 */
869
-	public function getIncompleteChildrenCount($fileId) {
870
-		if ($fileId > -1) {
871
-			$query = $this->getQueryBuilder();
872
-			$query->select($query->func()->count())
873
-				->from('filecache')
874
-				->whereParent($fileId)
875
-				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
876
-
877
-			$result = $query->execute();
878
-			$size = (int)$result->fetchOne();
879
-			$result->closeCursor();
880
-
881
-			return $size;
882
-		}
883
-		return -1;
884
-	}
885
-
886
-	/**
887
-	 * calculate the size of a folder and set it in the cache
888
-	 *
889
-	 * @param string $path
890
-	 * @param array|null|ICacheEntry $entry (optional) meta data of the folder
891
-	 * @return int|float
892
-	 */
893
-	public function calculateFolderSize($path, $entry = null) {
894
-		return $this->calculateFolderSizeInner($path, $entry);
895
-	}
896
-
897
-
898
-	/**
899
-	 * inner function because we can't add new params to the public function without breaking any child classes
900
-	 *
901
-	 * @param string $path
902
-	 * @param array|null|ICacheEntry $entry (optional) meta data of the folder
903
-	 * @param bool $ignoreUnknown don't mark the folder size as unknown if any of it's children are unknown
904
-	 * @return int|float
905
-	 */
906
-	protected function calculateFolderSizeInner(string $path, $entry = null, bool $ignoreUnknown = false) {
907
-		$totalSize = 0;
908
-		if (is_null($entry) || !isset($entry['fileid'])) {
909
-			$entry = $this->get($path);
910
-		}
911
-		if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
912
-			$id = $entry['fileid'];
913
-
914
-			$query = $this->getQueryBuilder();
915
-			$query->select('size', 'unencrypted_size')
916
-				->from('filecache')
917
-				->whereParent($id);
918
-			if ($ignoreUnknown) {
919
-				$query->andWhere($query->expr()->gte('size', $query->createNamedParameter(0)));
920
-			}
921
-
922
-			$result = $query->execute();
923
-			$rows = $result->fetchAll();
924
-			$result->closeCursor();
925
-
926
-			if ($rows) {
927
-				$sizes = array_map(function (array $row) {
928
-					return Util::numericToNumber($row['size']);
929
-				}, $rows);
930
-				$unencryptedOnlySizes = array_map(function (array $row) {
931
-					return Util::numericToNumber($row['unencrypted_size']);
932
-				}, $rows);
933
-				$unencryptedSizes = array_map(function (array $row) {
934
-					return Util::numericToNumber(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']);
935
-				}, $rows);
936
-
937
-				$sum = array_sum($sizes);
938
-				$min = min($sizes);
939
-
940
-				$unencryptedSum = array_sum($unencryptedSizes);
941
-				$unencryptedMin = min($unencryptedSizes);
942
-				$unencryptedMax = max($unencryptedOnlySizes);
943
-
944
-				$sum = 0 + $sum;
945
-				$min = 0 + $min;
946
-				if ($min === -1) {
947
-					$totalSize = $min;
948
-				} else {
949
-					$totalSize = $sum;
950
-				}
951
-				if ($unencryptedMin === -1 || $min === -1) {
952
-					$unencryptedTotal = $unencryptedMin;
953
-				} else {
954
-					$unencryptedTotal = $unencryptedSum;
955
-				}
956
-			} else {
957
-				$totalSize = 0;
958
-				$unencryptedTotal = 0;
959
-				$unencryptedMax = 0;
960
-			}
961
-
962
-			// only set unencrypted size for a folder if any child entries have it set, or the folder is empty
963
-			$shouldWriteUnEncryptedSize = $unencryptedMax > 0 || $totalSize === 0 || $entry['unencrypted_size'] > 0;
964
-			if ($entry['size'] !== $totalSize || ($entry['unencrypted_size'] !== $unencryptedTotal && $shouldWriteUnEncryptedSize)) {
965
-				if ($shouldWriteUnEncryptedSize) {
966
-					// if all children have an unencrypted size of 0, just set the folder unencrypted size to 0 instead of summing the sizes
967
-					if ($unencryptedMax === 0) {
968
-						$unencryptedTotal = 0;
969
-					}
970
-
971
-					$this->update($id, [
972
-						'size' => $totalSize,
973
-						'unencrypted_size' => $unencryptedTotal,
974
-					]);
975
-				} else {
976
-					$this->update($id, [
977
-						'size' => $totalSize,
978
-					]);
979
-				}
980
-			}
981
-		}
982
-		return $totalSize;
983
-	}
984
-
985
-	/**
986
-	 * get all file ids on the files on the storage
987
-	 *
988
-	 * @return int[]
989
-	 */
990
-	public function getAll() {
991
-		$query = $this->getQueryBuilder();
992
-		$query->select('fileid')
993
-			->from('filecache')
994
-			->whereStorageId($this->getNumericStorageId());
995
-
996
-		$result = $query->execute();
997
-		$files = $result->fetchAll(\PDO::FETCH_COLUMN);
998
-		$result->closeCursor();
999
-
1000
-		return array_map(function ($id) {
1001
-			return (int)$id;
1002
-		}, $files);
1003
-	}
1004
-
1005
-	/**
1006
-	 * find a folder in the cache which has not been fully scanned
1007
-	 *
1008
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
1009
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
1010
-	 * likely the folder where we stopped scanning previously
1011
-	 *
1012
-	 * @return string|false the path of the folder or false when no folder matched
1013
-	 */
1014
-	public function getIncomplete() {
1015
-		$query = $this->getQueryBuilder();
1016
-		$query->select('path')
1017
-			->from('filecache')
1018
-			->whereStorageId($this->getNumericStorageId())
1019
-			->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
1020
-			->orderBy('fileid', 'DESC')
1021
-			->setMaxResults(1);
1022
-
1023
-		$result = $query->execute();
1024
-		$path = $result->fetchOne();
1025
-		$result->closeCursor();
1026
-
1027
-		if ($path === false) {
1028
-			return false;
1029
-		}
1030
-
1031
-		// Make sure Oracle does not continue with null for empty strings
1032
-		return (string)$path;
1033
-	}
1034
-
1035
-	/**
1036
-	 * get the path of a file on this storage by it's file id
1037
-	 *
1038
-	 * @param int $id the file id of the file or folder to search
1039
-	 * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
1040
-	 */
1041
-	public function getPathById($id) {
1042
-		$query = $this->getQueryBuilder();
1043
-		$query->select('path')
1044
-			->from('filecache')
1045
-			->whereStorageId($this->getNumericStorageId())
1046
-			->whereFileId($id);
1047
-
1048
-		$result = $query->execute();
1049
-		$path = $result->fetchOne();
1050
-		$result->closeCursor();
1051
-
1052
-		if ($path === false) {
1053
-			return null;
1054
-		}
1055
-
1056
-		return (string)$path;
1057
-	}
1058
-
1059
-	/**
1060
-	 * get the storage id of the storage for a file and the internal path of the file
1061
-	 * unlike getPathById this does not limit the search to files on this storage and
1062
-	 * instead does a global search in the cache table
1063
-	 *
1064
-	 * @param int $id
1065
-	 * @return array first element holding the storage id, second the path
1066
-	 * @deprecated use getPathById() instead
1067
-	 */
1068
-	public static function getById($id) {
1069
-		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
1070
-		$query->select('path', 'storage')
1071
-			->from('filecache')
1072
-			->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
1073
-
1074
-		$result = $query->execute();
1075
-		$row = $result->fetch();
1076
-		$result->closeCursor();
1077
-
1078
-		if ($row) {
1079
-			$numericId = $row['storage'];
1080
-			$path = $row['path'];
1081
-		} else {
1082
-			return null;
1083
-		}
1084
-
1085
-		if ($id = Storage::getStorageId($numericId)) {
1086
-			return [$id, $path];
1087
-		} else {
1088
-			return null;
1089
-		}
1090
-	}
1091
-
1092
-	/**
1093
-	 * normalize the given path
1094
-	 *
1095
-	 * @param string $path
1096
-	 * @return string
1097
-	 */
1098
-	public function normalize($path) {
1099
-		return trim(\OC_Util::normalizeUnicode($path), '/');
1100
-	}
1101
-
1102
-	/**
1103
-	 * Copy a file or folder in the cache
1104
-	 *
1105
-	 * @param ICache $sourceCache
1106
-	 * @param ICacheEntry $sourceEntry
1107
-	 * @param string $targetPath
1108
-	 * @return int fileId of copied entry
1109
-	 */
1110
-	public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
1111
-		if ($sourceEntry->getId() < 0) {
1112
-			throw new \RuntimeException("Invalid source cache entry on copyFromCache");
1113
-		}
1114
-		$data = $this->cacheEntryToArray($sourceEntry);
1115
-
1116
-		// when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
1117
-		if ($sourceCache instanceof Cache && $sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
1118
-			$data['encrypted'] = 0;
1119
-		}
1120
-
1121
-		$fileId = $this->put($targetPath, $data);
1122
-		if ($fileId <= 0) {
1123
-			throw new \RuntimeException("Failed to copy to " . $targetPath . " from cache with source data " . json_encode($data) . " ");
1124
-		}
1125
-		if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
1126
-			$folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId());
1127
-			foreach ($folderContent as $subEntry) {
1128
-				$subTargetPath = $targetPath . '/' . $subEntry->getName();
1129
-				$this->copyFromCache($sourceCache, $subEntry, $subTargetPath);
1130
-			}
1131
-		}
1132
-		return $fileId;
1133
-	}
1134
-
1135
-	private function cacheEntryToArray(ICacheEntry $entry): array {
1136
-		return [
1137
-			'size' => $entry->getSize(),
1138
-			'mtime' => $entry->getMTime(),
1139
-			'storage_mtime' => $entry->getStorageMTime(),
1140
-			'mimetype' => $entry->getMimeType(),
1141
-			'mimepart' => $entry->getMimePart(),
1142
-			'etag' => $entry->getEtag(),
1143
-			'permissions' => $entry->getPermissions(),
1144
-			'encrypted' => $entry->isEncrypted(),
1145
-			'creation_time' => $entry->getCreationTime(),
1146
-			'upload_time' => $entry->getUploadTime(),
1147
-			'metadata_etag' => $entry->getMetadataEtag(),
1148
-		];
1149
-	}
1150
-
1151
-	public function getQueryFilterForStorage(): ISearchOperator {
1152
-		return new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', $this->getNumericStorageId());
1153
-	}
1154
-
1155
-	public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry {
1156
-		if ($rawEntry->getStorageId() === $this->getNumericStorageId()) {
1157
-			return $rawEntry;
1158
-		} else {
1159
-			return null;
1160
-		}
1161
-	}
77
+    use MoveFromCacheTrait {
78
+        MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
79
+    }
80
+
81
+    /**
82
+     * @var array partial data for the cache
83
+     */
84
+    protected $partial = [];
85
+
86
+    /**
87
+     * @var string
88
+     */
89
+    protected $storageId;
90
+
91
+    private $storage;
92
+
93
+    /**
94
+     * @var Storage $storageCache
95
+     */
96
+    protected $storageCache;
97
+
98
+    /** @var IMimeTypeLoader */
99
+    protected $mimetypeLoader;
100
+
101
+    /**
102
+     * @var IDBConnection
103
+     */
104
+    protected $connection;
105
+
106
+    /**
107
+     * @var IEventDispatcher
108
+     */
109
+    protected $eventDispatcher;
110
+
111
+    /** @var QuerySearchHelper */
112
+    protected $querySearchHelper;
113
+
114
+    /**
115
+     * @param IStorage $storage
116
+     */
117
+    public function __construct(IStorage $storage) {
118
+        $this->storageId = $storage->getId();
119
+        $this->storage = $storage;
120
+        if (strlen($this->storageId) > 64) {
121
+            $this->storageId = md5($this->storageId);
122
+        }
123
+
124
+        $this->storageCache = new Storage($storage);
125
+        $this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
126
+        $this->connection = \OC::$server->getDatabaseConnection();
127
+        $this->eventDispatcher = \OC::$server->get(IEventDispatcher::class);
128
+        $this->querySearchHelper = \OC::$server->query(QuerySearchHelper::class);
129
+    }
130
+
131
+    protected function getQueryBuilder() {
132
+        return new CacheQueryBuilder(
133
+            $this->connection,
134
+            \OC::$server->getSystemConfig(),
135
+            \OC::$server->get(LoggerInterface::class)
136
+        );
137
+    }
138
+
139
+    /**
140
+     * Get the numeric storage id for this cache's storage
141
+     *
142
+     * @return int
143
+     */
144
+    public function getNumericStorageId() {
145
+        return $this->storageCache->getNumericId();
146
+    }
147
+
148
+    /**
149
+     * get the stored metadata of a file or folder
150
+     *
151
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
152
+     * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
153
+     */
154
+    public function get($file) {
155
+        $query = $this->getQueryBuilder();
156
+        $query->selectFileCache();
157
+
158
+        if (is_string($file) || $file == '') {
159
+            // normalize file
160
+            $file = $this->normalize($file);
161
+
162
+            $query->whereStorageId($this->getNumericStorageId())
163
+                ->wherePath($file);
164
+        } else { //file id
165
+            $query->whereFileId($file);
166
+        }
167
+
168
+        $result = $query->execute();
169
+        $data = $result->fetch();
170
+        $result->closeCursor();
171
+
172
+        //merge partial data
173
+        if (!$data && is_string($file) && isset($this->partial[$file])) {
174
+            return $this->partial[$file];
175
+        } elseif (!$data) {
176
+            return $data;
177
+        } else {
178
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
179
+        }
180
+    }
181
+
182
+    /**
183
+     * Create a CacheEntry from database row
184
+     *
185
+     * @param array $data
186
+     * @param IMimeTypeLoader $mimetypeLoader
187
+     * @return CacheEntry
188
+     */
189
+    public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
190
+        //fix types
191
+        $data['name'] = (string)$data['name'];
192
+        $data['path'] = (string)$data['path'];
193
+        $data['fileid'] = (int)$data['fileid'];
194
+        $data['parent'] = (int)$data['parent'];
195
+        $data['size'] = Util::numericToNumber($data['size']);
196
+        $data['unencrypted_size'] = Util::numericToNumber($data['unencrypted_size'] ?? 0);
197
+        $data['mtime'] = (int)$data['mtime'];
198
+        $data['storage_mtime'] = (int)$data['storage_mtime'];
199
+        $data['encryptedVersion'] = (int)$data['encrypted'];
200
+        $data['encrypted'] = (bool)$data['encrypted'];
201
+        $data['storage_id'] = $data['storage'];
202
+        $data['storage'] = (int)$data['storage'];
203
+        $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
204
+        $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
205
+        if ($data['storage_mtime'] == 0) {
206
+            $data['storage_mtime'] = $data['mtime'];
207
+        }
208
+        $data['permissions'] = (int)$data['permissions'];
209
+        if (isset($data['creation_time'])) {
210
+            $data['creation_time'] = (int)$data['creation_time'];
211
+        }
212
+        if (isset($data['upload_time'])) {
213
+            $data['upload_time'] = (int)$data['upload_time'];
214
+        }
215
+        return new CacheEntry($data);
216
+    }
217
+
218
+    /**
219
+     * get the metadata of all files stored in $folder
220
+     *
221
+     * @param string $folder
222
+     * @return ICacheEntry[]
223
+     */
224
+    public function getFolderContents($folder) {
225
+        $fileId = $this->getId($folder);
226
+        return $this->getFolderContentsById($fileId);
227
+    }
228
+
229
+    /**
230
+     * get the metadata of all files stored in $folder
231
+     *
232
+     * @param int $fileId the file id of the folder
233
+     * @return ICacheEntry[]
234
+     */
235
+    public function getFolderContentsById($fileId) {
236
+        if ($fileId > -1) {
237
+            $query = $this->getQueryBuilder();
238
+            $query->selectFileCache()
239
+                ->whereParent($fileId)
240
+                ->orderBy('name', 'ASC');
241
+
242
+            $result = $query->execute();
243
+            $files = $result->fetchAll();
244
+            $result->closeCursor();
245
+
246
+            return array_map(function (array $data) {
247
+                return self::cacheEntryFromData($data, $this->mimetypeLoader);
248
+            }, $files);
249
+        }
250
+        return [];
251
+    }
252
+
253
+    /**
254
+     * insert or update meta data for a file or folder
255
+     *
256
+     * @param string $file
257
+     * @param array $data
258
+     *
259
+     * @return int file id
260
+     * @throws \RuntimeException
261
+     */
262
+    public function put($file, array $data) {
263
+        if (($id = $this->getId($file)) > -1) {
264
+            $this->update($id, $data);
265
+            return $id;
266
+        } else {
267
+            return $this->insert($file, $data);
268
+        }
269
+    }
270
+
271
+    /**
272
+     * insert meta data for a new file or folder
273
+     *
274
+     * @param string $file
275
+     * @param array $data
276
+     *
277
+     * @return int file id
278
+     * @throws \RuntimeException
279
+     */
280
+    public function insert($file, array $data) {
281
+        // normalize file
282
+        $file = $this->normalize($file);
283
+
284
+        if (isset($this->partial[$file])) { //add any saved partial data
285
+            $data = array_merge($this->partial[$file], $data);
286
+            unset($this->partial[$file]);
287
+        }
288
+
289
+        $requiredFields = ['size', 'mtime', 'mimetype'];
290
+        foreach ($requiredFields as $field) {
291
+            if (!isset($data[$field])) { //data not complete save as partial and return
292
+                $this->partial[$file] = $data;
293
+                return -1;
294
+            }
295
+        }
296
+
297
+        $data['path'] = $file;
298
+        if (!isset($data['parent'])) {
299
+            $data['parent'] = $this->getParentId($file);
300
+        }
301
+        $data['name'] = basename($file);
302
+
303
+        [$values, $extensionValues] = $this->normalizeData($data);
304
+        $storageId = $this->getNumericStorageId();
305
+        $values['storage'] = $storageId;
306
+
307
+        try {
308
+            $builder = $this->connection->getQueryBuilder();
309
+            $builder->insert('filecache');
310
+
311
+            foreach ($values as $column => $value) {
312
+                $builder->setValue($column, $builder->createNamedParameter($value));
313
+            }
314
+
315
+            if ($builder->execute()) {
316
+                $fileId = $builder->getLastInsertId();
317
+
318
+                if (count($extensionValues)) {
319
+                    $query = $this->getQueryBuilder();
320
+                    $query->insert('filecache_extended');
321
+
322
+                    $query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
323
+                    foreach ($extensionValues as $column => $value) {
324
+                        $query->setValue($column, $query->createNamedParameter($value));
325
+                    }
326
+                    $query->execute();
327
+                }
328
+
329
+                $event = new CacheEntryInsertedEvent($this->storage, $file, $fileId, $storageId);
330
+                $this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
331
+                $this->eventDispatcher->dispatchTyped($event);
332
+                return $fileId;
333
+            }
334
+        } catch (UniqueConstraintViolationException $e) {
335
+            // entry exists already
336
+            if ($this->connection->inTransaction()) {
337
+                $this->connection->commit();
338
+                $this->connection->beginTransaction();
339
+            }
340
+        }
341
+
342
+        // The file was created in the mean time
343
+        if (($id = $this->getId($file)) > -1) {
344
+            $this->update($id, $data);
345
+            return $id;
346
+        } else {
347
+            throw new \RuntimeException('File entry could not be inserted but could also not be selected with getId() in order to perform an update. Please try again.');
348
+        }
349
+    }
350
+
351
+    /**
352
+     * update the metadata of an existing file or folder in the cache
353
+     *
354
+     * @param int $id the fileid of the existing file or folder
355
+     * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
356
+     */
357
+    public function update($id, array $data) {
358
+        if (isset($data['path'])) {
359
+            // normalize path
360
+            $data['path'] = $this->normalize($data['path']);
361
+        }
362
+
363
+        if (isset($data['name'])) {
364
+            // normalize path
365
+            $data['name'] = $this->normalize($data['name']);
366
+        }
367
+
368
+        [$values, $extensionValues] = $this->normalizeData($data);
369
+
370
+        if (count($values)) {
371
+            $query = $this->getQueryBuilder();
372
+
373
+            $query->update('filecache')
374
+                ->whereFileId($id)
375
+                ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
376
+                    return $query->expr()->orX(
377
+                        $query->expr()->neq($key, $query->createNamedParameter($value)),
378
+                        $query->expr()->isNull($key)
379
+                    );
380
+                }, array_keys($values), array_values($values))));
381
+
382
+            foreach ($values as $key => $value) {
383
+                $query->set($key, $query->createNamedParameter($value));
384
+            }
385
+
386
+            $query->execute();
387
+        }
388
+
389
+        if (count($extensionValues)) {
390
+            try {
391
+                $query = $this->getQueryBuilder();
392
+                $query->insert('filecache_extended');
393
+
394
+                $query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
395
+                foreach ($extensionValues as $column => $value) {
396
+                    $query->setValue($column, $query->createNamedParameter($value));
397
+                }
398
+
399
+                $query->execute();
400
+            } catch (UniqueConstraintViolationException $e) {
401
+                $query = $this->getQueryBuilder();
402
+                $query->update('filecache_extended')
403
+                    ->whereFileId($id)
404
+                    ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
405
+                        return $query->expr()->orX(
406
+                            $query->expr()->neq($key, $query->createNamedParameter($value)),
407
+                            $query->expr()->isNull($key)
408
+                        );
409
+                    }, array_keys($extensionValues), array_values($extensionValues))));
410
+
411
+                foreach ($extensionValues as $key => $value) {
412
+                    $query->set($key, $query->createNamedParameter($value));
413
+                }
414
+
415
+                $query->execute();
416
+            }
417
+        }
418
+
419
+        $path = $this->getPathById($id);
420
+        // path can still be null if the file doesn't exist
421
+        if ($path !== null) {
422
+            $event = new CacheEntryUpdatedEvent($this->storage, $path, $id, $this->getNumericStorageId());
423
+            $this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
424
+            $this->eventDispatcher->dispatchTyped($event);
425
+        }
426
+    }
427
+
428
+    /**
429
+     * extract query parts and params array from data array
430
+     *
431
+     * @param array $data
432
+     * @return array
433
+     */
434
+    protected function normalizeData(array $data): array {
435
+        $fields = [
436
+            'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
437
+            'etag', 'permissions', 'checksum', 'storage', 'unencrypted_size'];
438
+        $extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
439
+
440
+        $doNotCopyStorageMTime = false;
441
+        if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
442
+            // this horrific magic tells it to not copy storage_mtime to mtime
443
+            unset($data['mtime']);
444
+            $doNotCopyStorageMTime = true;
445
+        }
446
+
447
+        $params = [];
448
+        $extensionParams = [];
449
+        foreach ($data as $name => $value) {
450
+            if (array_search($name, $fields) !== false) {
451
+                if ($name === 'path') {
452
+                    $params['path_hash'] = md5($value);
453
+                } elseif ($name === 'mimetype') {
454
+                    $params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
455
+                    $value = $this->mimetypeLoader->getId($value);
456
+                } elseif ($name === 'storage_mtime') {
457
+                    if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
458
+                        $params['mtime'] = $value;
459
+                    }
460
+                } elseif ($name === 'encrypted') {
461
+                    if (isset($data['encryptedVersion'])) {
462
+                        $value = $data['encryptedVersion'];
463
+                    } else {
464
+                        // Boolean to integer conversion
465
+                        $value = $value ? 1 : 0;
466
+                    }
467
+                }
468
+                $params[$name] = $value;
469
+            }
470
+            if (array_search($name, $extensionFields) !== false) {
471
+                $extensionParams[$name] = $value;
472
+            }
473
+        }
474
+        return [$params, array_filter($extensionParams)];
475
+    }
476
+
477
+    /**
478
+     * get the file id for a file
479
+     *
480
+     * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
481
+     *
482
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
483
+     *
484
+     * @param string $file
485
+     * @return int
486
+     */
487
+    public function getId($file) {
488
+        // normalize file
489
+        $file = $this->normalize($file);
490
+
491
+        $query = $this->getQueryBuilder();
492
+        $query->select('fileid')
493
+            ->from('filecache')
494
+            ->whereStorageId($this->getNumericStorageId())
495
+            ->wherePath($file);
496
+
497
+        $result = $query->execute();
498
+        $id = $result->fetchOne();
499
+        $result->closeCursor();
500
+
501
+        return $id === false ? -1 : (int)$id;
502
+    }
503
+
504
+    /**
505
+     * get the id of the parent folder of a file
506
+     *
507
+     * @param string $file
508
+     * @return int
509
+     */
510
+    public function getParentId($file) {
511
+        if ($file === '') {
512
+            return -1;
513
+        } else {
514
+            $parent = $this->getParentPath($file);
515
+            return (int)$this->getId($parent);
516
+        }
517
+    }
518
+
519
+    private function getParentPath($path) {
520
+        $parent = dirname($path);
521
+        if ($parent === '.') {
522
+            $parent = '';
523
+        }
524
+        return $parent;
525
+    }
526
+
527
+    /**
528
+     * check if a file is available in the cache
529
+     *
530
+     * @param string $file
531
+     * @return bool
532
+     */
533
+    public function inCache($file) {
534
+        return $this->getId($file) != -1;
535
+    }
536
+
537
+    /**
538
+     * remove a file or folder from the cache
539
+     *
540
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
541
+     *
542
+     * @param string $file
543
+     */
544
+    public function remove($file) {
545
+        $entry = $this->get($file);
546
+
547
+        if ($entry instanceof ICacheEntry) {
548
+            $query = $this->getQueryBuilder();
549
+            $query->delete('filecache')
550
+                ->whereFileId($entry->getId());
551
+            $query->execute();
552
+
553
+            $query = $this->getQueryBuilder();
554
+            $query->delete('filecache_extended')
555
+                ->whereFileId($entry->getId());
556
+            $query->execute();
557
+
558
+            if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
559
+                $this->removeChildren($entry);
560
+            }
561
+
562
+            $this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $entry->getPath(), $entry->getId(), $this->getNumericStorageId()));
563
+        }
564
+    }
565
+
566
+    /**
567
+     * Remove all children of a folder
568
+     *
569
+     * @param ICacheEntry $entry the cache entry of the folder to remove the children of
570
+     * @throws \OC\DatabaseException
571
+     */
572
+    private function removeChildren(ICacheEntry $entry) {
573
+        $parentIds = [$entry->getId()];
574
+        $queue = [$entry->getId()];
575
+        $deletedIds = [];
576
+        $deletedPaths = [];
577
+
578
+        // we walk depth first through the file tree, removing all filecache_extended attributes while we walk
579
+        // and collecting all folder ids to later use to delete the filecache entries
580
+        while ($entryId = array_pop($queue)) {
581
+            $children = $this->getFolderContentsById($entryId);
582
+            $childIds = array_map(function (ICacheEntry $cacheEntry) {
583
+                return $cacheEntry->getId();
584
+            }, $children);
585
+            $childPaths = array_map(function (ICacheEntry $cacheEntry) {
586
+                return $cacheEntry->getPath();
587
+            }, $children);
588
+
589
+            $deletedIds = array_merge($deletedIds, $childIds);
590
+            $deletedPaths = array_merge($deletedPaths, $childPaths);
591
+
592
+            $query = $this->getQueryBuilder();
593
+            $query->delete('filecache_extended')
594
+                ->where($query->expr()->in('fileid', $query->createParameter('childIds')));
595
+
596
+            foreach (array_chunk($childIds, 1000) as $childIdChunk) {
597
+                $query->setParameter('childIds', $childIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
598
+                $query->execute();
599
+            }
600
+
601
+            /** @var ICacheEntry[] $childFolders */
602
+            $childFolders = array_filter($children, function ($child) {
603
+                return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
604
+            });
605
+            foreach ($childFolders as $folder) {
606
+                $parentIds[] = $folder->getId();
607
+                $queue[] = $folder->getId();
608
+            }
609
+        }
610
+
611
+        $query = $this->getQueryBuilder();
612
+        $query->delete('filecache')
613
+            ->whereParentInParameter('parentIds');
614
+
615
+        foreach (array_chunk($parentIds, 1000) as $parentIdChunk) {
616
+            $query->setParameter('parentIds', $parentIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
617
+            $query->execute();
618
+        }
619
+
620
+        foreach (array_combine($deletedIds, $deletedPaths) as $fileId => $filePath) {
621
+            $cacheEntryRemovedEvent = new CacheEntryRemovedEvent(
622
+                $this->storage,
623
+                $filePath,
624
+                $fileId,
625
+                $this->getNumericStorageId()
626
+            );
627
+            $this->eventDispatcher->dispatchTyped($cacheEntryRemovedEvent);
628
+        }
629
+    }
630
+
631
+    /**
632
+     * Move a file or folder in the cache
633
+     *
634
+     * @param string $source
635
+     * @param string $target
636
+     */
637
+    public function move($source, $target) {
638
+        $this->moveFromCache($this, $source, $target);
639
+    }
640
+
641
+    /**
642
+     * Get the storage id and path needed for a move
643
+     *
644
+     * @param string $path
645
+     * @return array [$storageId, $internalPath]
646
+     */
647
+    protected function getMoveInfo($path) {
648
+        return [$this->getNumericStorageId(), $path];
649
+    }
650
+
651
+    protected function hasEncryptionWrapper(): bool {
652
+        return $this->storage->instanceOfStorage(Encryption::class);
653
+    }
654
+
655
+    /**
656
+     * Move a file or folder in the cache
657
+     *
658
+     * @param ICache $sourceCache
659
+     * @param string $sourcePath
660
+     * @param string $targetPath
661
+     * @throws \OC\DatabaseException
662
+     * @throws \Exception if the given storages have an invalid id
663
+     */
664
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
665
+        if ($sourceCache instanceof Cache) {
666
+            // normalize source and target
667
+            $sourcePath = $this->normalize($sourcePath);
668
+            $targetPath = $this->normalize($targetPath);
669
+
670
+            $sourceData = $sourceCache->get($sourcePath);
671
+            if ($sourceData === false) {
672
+                throw new \Exception('Invalid source storage path: ' . $sourcePath);
673
+            }
674
+
675
+            $sourceId = $sourceData['fileid'];
676
+            $newParentId = $this->getParentId($targetPath);
677
+
678
+            [$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
679
+            [$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
680
+
681
+            if (is_null($sourceStorageId) || $sourceStorageId === false) {
682
+                throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
683
+            }
684
+            if (is_null($targetStorageId) || $targetStorageId === false) {
685
+                throw new \Exception('Invalid target storage id: ' . $targetStorageId);
686
+            }
687
+
688
+            $this->connection->beginTransaction();
689
+            if ($sourceData['mimetype'] === 'httpd/unix-directory') {
690
+                //update all child entries
691
+                $sourceLength = mb_strlen($sourcePath);
692
+                $query = $this->connection->getQueryBuilder();
693
+
694
+                $fun = $query->func();
695
+                $newPathFunction = $fun->concat(
696
+                    $query->createNamedParameter($targetPath),
697
+                    $fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
698
+                );
699
+                $query->update('filecache')
700
+                    ->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
701
+                    ->set('path_hash', $fun->md5($newPathFunction))
702
+                    ->set('path', $newPathFunction)
703
+                    ->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
704
+                    ->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
705
+
706
+                // when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
707
+                if ($sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
708
+                    $query->set('encrypted', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT));
709
+                }
710
+
711
+                try {
712
+                    $query->execute();
713
+                } catch (\OC\DatabaseException $e) {
714
+                    $this->connection->rollBack();
715
+                    throw $e;
716
+                }
717
+            }
718
+
719
+            $query = $this->getQueryBuilder();
720
+            $query->update('filecache')
721
+                ->set('storage', $query->createNamedParameter($targetStorageId))
722
+                ->set('path', $query->createNamedParameter($targetPath))
723
+                ->set('path_hash', $query->createNamedParameter(md5($targetPath)))
724
+                ->set('name', $query->createNamedParameter(basename($targetPath)))
725
+                ->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
726
+                ->whereFileId($sourceId);
727
+
728
+            // when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
729
+            if ($sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
730
+                $query->set('encrypted', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT));
731
+            }
732
+
733
+            $query->execute();
734
+
735
+            $this->connection->commit();
736
+
737
+            if ($sourceCache->getNumericStorageId() !== $this->getNumericStorageId()) {
738
+                $this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $sourcePath, $sourceId, $sourceCache->getNumericStorageId()));
739
+                $event = new CacheEntryInsertedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
740
+                $this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
741
+                $this->eventDispatcher->dispatchTyped($event);
742
+            } else {
743
+                $event = new CacheEntryUpdatedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
744
+                $this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
745
+                $this->eventDispatcher->dispatchTyped($event);
746
+            }
747
+        } else {
748
+            $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
749
+        }
750
+    }
751
+
752
+    /**
753
+     * remove all entries for files that are stored on the storage from the cache
754
+     */
755
+    public function clear() {
756
+        $query = $this->getQueryBuilder();
757
+        $query->delete('filecache')
758
+            ->whereStorageId($this->getNumericStorageId());
759
+        $query->execute();
760
+
761
+        $query = $this->connection->getQueryBuilder();
762
+        $query->delete('storages')
763
+            ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
764
+        $query->execute();
765
+    }
766
+
767
+    /**
768
+     * Get the scan status of a file
769
+     *
770
+     * - Cache::NOT_FOUND: File is not in the cache
771
+     * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
772
+     * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
773
+     * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
774
+     *
775
+     * @param string $file
776
+     *
777
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
778
+     */
779
+    public function getStatus($file) {
780
+        // normalize file
781
+        $file = $this->normalize($file);
782
+
783
+        $query = $this->getQueryBuilder();
784
+        $query->select('size')
785
+            ->from('filecache')
786
+            ->whereStorageId($this->getNumericStorageId())
787
+            ->wherePath($file);
788
+
789
+        $result = $query->execute();
790
+        $size = $result->fetchOne();
791
+        $result->closeCursor();
792
+
793
+        if ($size !== false) {
794
+            if ((int)$size === -1) {
795
+                return self::SHALLOW;
796
+            } else {
797
+                return self::COMPLETE;
798
+            }
799
+        } else {
800
+            if (isset($this->partial[$file])) {
801
+                return self::PARTIAL;
802
+            } else {
803
+                return self::NOT_FOUND;
804
+            }
805
+        }
806
+    }
807
+
808
+    /**
809
+     * search for files matching $pattern
810
+     *
811
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
812
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
813
+     */
814
+    public function search($pattern) {
815
+        $operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', $pattern);
816
+        return $this->searchQuery(new SearchQuery($operator, 0, 0, [], null));
817
+    }
818
+
819
+    /**
820
+     * search for files by mimetype
821
+     *
822
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
823
+     *        where it will search for all mimetypes in the group ('image/*')
824
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
825
+     */
826
+    public function searchByMime($mimetype) {
827
+        if (!str_contains($mimetype, '/')) {
828
+            $operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%');
829
+        } else {
830
+            $operator = new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype);
831
+        }
832
+        return $this->searchQuery(new SearchQuery($operator, 0, 0, [], null));
833
+    }
834
+
835
+    public function searchQuery(ISearchQuery $searchQuery) {
836
+        return current($this->querySearchHelper->searchInCaches($searchQuery, [$this]));
837
+    }
838
+
839
+    /**
840
+     * Re-calculate the folder size and the size of all parent folders
841
+     *
842
+     * @param string|boolean $path
843
+     * @param array $data (optional) meta data of the folder
844
+     */
845
+    public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
846
+        $this->calculateFolderSize($path, $data);
847
+        if ($path !== '') {
848
+            $parent = dirname($path);
849
+            if ($parent === '.' || $parent === '/') {
850
+                $parent = '';
851
+            }
852
+            if ($isBackgroundScan) {
853
+                $parentData = $this->get($parent);
854
+                if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
855
+                    $this->correctFolderSize($parent, $parentData, $isBackgroundScan);
856
+                }
857
+            } else {
858
+                $this->correctFolderSize($parent);
859
+            }
860
+        }
861
+    }
862
+
863
+    /**
864
+     * get the incomplete count that shares parent $folder
865
+     *
866
+     * @param int $fileId the file id of the folder
867
+     * @return int
868
+     */
869
+    public function getIncompleteChildrenCount($fileId) {
870
+        if ($fileId > -1) {
871
+            $query = $this->getQueryBuilder();
872
+            $query->select($query->func()->count())
873
+                ->from('filecache')
874
+                ->whereParent($fileId)
875
+                ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
876
+
877
+            $result = $query->execute();
878
+            $size = (int)$result->fetchOne();
879
+            $result->closeCursor();
880
+
881
+            return $size;
882
+        }
883
+        return -1;
884
+    }
885
+
886
+    /**
887
+     * calculate the size of a folder and set it in the cache
888
+     *
889
+     * @param string $path
890
+     * @param array|null|ICacheEntry $entry (optional) meta data of the folder
891
+     * @return int|float
892
+     */
893
+    public function calculateFolderSize($path, $entry = null) {
894
+        return $this->calculateFolderSizeInner($path, $entry);
895
+    }
896
+
897
+
898
+    /**
899
+     * inner function because we can't add new params to the public function without breaking any child classes
900
+     *
901
+     * @param string $path
902
+     * @param array|null|ICacheEntry $entry (optional) meta data of the folder
903
+     * @param bool $ignoreUnknown don't mark the folder size as unknown if any of it's children are unknown
904
+     * @return int|float
905
+     */
906
+    protected function calculateFolderSizeInner(string $path, $entry = null, bool $ignoreUnknown = false) {
907
+        $totalSize = 0;
908
+        if (is_null($entry) || !isset($entry['fileid'])) {
909
+            $entry = $this->get($path);
910
+        }
911
+        if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
912
+            $id = $entry['fileid'];
913
+
914
+            $query = $this->getQueryBuilder();
915
+            $query->select('size', 'unencrypted_size')
916
+                ->from('filecache')
917
+                ->whereParent($id);
918
+            if ($ignoreUnknown) {
919
+                $query->andWhere($query->expr()->gte('size', $query->createNamedParameter(0)));
920
+            }
921
+
922
+            $result = $query->execute();
923
+            $rows = $result->fetchAll();
924
+            $result->closeCursor();
925
+
926
+            if ($rows) {
927
+                $sizes = array_map(function (array $row) {
928
+                    return Util::numericToNumber($row['size']);
929
+                }, $rows);
930
+                $unencryptedOnlySizes = array_map(function (array $row) {
931
+                    return Util::numericToNumber($row['unencrypted_size']);
932
+                }, $rows);
933
+                $unencryptedSizes = array_map(function (array $row) {
934
+                    return Util::numericToNumber(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']);
935
+                }, $rows);
936
+
937
+                $sum = array_sum($sizes);
938
+                $min = min($sizes);
939
+
940
+                $unencryptedSum = array_sum($unencryptedSizes);
941
+                $unencryptedMin = min($unencryptedSizes);
942
+                $unencryptedMax = max($unencryptedOnlySizes);
943
+
944
+                $sum = 0 + $sum;
945
+                $min = 0 + $min;
946
+                if ($min === -1) {
947
+                    $totalSize = $min;
948
+                } else {
949
+                    $totalSize = $sum;
950
+                }
951
+                if ($unencryptedMin === -1 || $min === -1) {
952
+                    $unencryptedTotal = $unencryptedMin;
953
+                } else {
954
+                    $unencryptedTotal = $unencryptedSum;
955
+                }
956
+            } else {
957
+                $totalSize = 0;
958
+                $unencryptedTotal = 0;
959
+                $unencryptedMax = 0;
960
+            }
961
+
962
+            // only set unencrypted size for a folder if any child entries have it set, or the folder is empty
963
+            $shouldWriteUnEncryptedSize = $unencryptedMax > 0 || $totalSize === 0 || $entry['unencrypted_size'] > 0;
964
+            if ($entry['size'] !== $totalSize || ($entry['unencrypted_size'] !== $unencryptedTotal && $shouldWriteUnEncryptedSize)) {
965
+                if ($shouldWriteUnEncryptedSize) {
966
+                    // if all children have an unencrypted size of 0, just set the folder unencrypted size to 0 instead of summing the sizes
967
+                    if ($unencryptedMax === 0) {
968
+                        $unencryptedTotal = 0;
969
+                    }
970
+
971
+                    $this->update($id, [
972
+                        'size' => $totalSize,
973
+                        'unencrypted_size' => $unencryptedTotal,
974
+                    ]);
975
+                } else {
976
+                    $this->update($id, [
977
+                        'size' => $totalSize,
978
+                    ]);
979
+                }
980
+            }
981
+        }
982
+        return $totalSize;
983
+    }
984
+
985
+    /**
986
+     * get all file ids on the files on the storage
987
+     *
988
+     * @return int[]
989
+     */
990
+    public function getAll() {
991
+        $query = $this->getQueryBuilder();
992
+        $query->select('fileid')
993
+            ->from('filecache')
994
+            ->whereStorageId($this->getNumericStorageId());
995
+
996
+        $result = $query->execute();
997
+        $files = $result->fetchAll(\PDO::FETCH_COLUMN);
998
+        $result->closeCursor();
999
+
1000
+        return array_map(function ($id) {
1001
+            return (int)$id;
1002
+        }, $files);
1003
+    }
1004
+
1005
+    /**
1006
+     * find a folder in the cache which has not been fully scanned
1007
+     *
1008
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
1009
+     * use the one with the highest id gives the best result with the background scanner, since that is most
1010
+     * likely the folder where we stopped scanning previously
1011
+     *
1012
+     * @return string|false the path of the folder or false when no folder matched
1013
+     */
1014
+    public function getIncomplete() {
1015
+        $query = $this->getQueryBuilder();
1016
+        $query->select('path')
1017
+            ->from('filecache')
1018
+            ->whereStorageId($this->getNumericStorageId())
1019
+            ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
1020
+            ->orderBy('fileid', 'DESC')
1021
+            ->setMaxResults(1);
1022
+
1023
+        $result = $query->execute();
1024
+        $path = $result->fetchOne();
1025
+        $result->closeCursor();
1026
+
1027
+        if ($path === false) {
1028
+            return false;
1029
+        }
1030
+
1031
+        // Make sure Oracle does not continue with null for empty strings
1032
+        return (string)$path;
1033
+    }
1034
+
1035
+    /**
1036
+     * get the path of a file on this storage by it's file id
1037
+     *
1038
+     * @param int $id the file id of the file or folder to search
1039
+     * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
1040
+     */
1041
+    public function getPathById($id) {
1042
+        $query = $this->getQueryBuilder();
1043
+        $query->select('path')
1044
+            ->from('filecache')
1045
+            ->whereStorageId($this->getNumericStorageId())
1046
+            ->whereFileId($id);
1047
+
1048
+        $result = $query->execute();
1049
+        $path = $result->fetchOne();
1050
+        $result->closeCursor();
1051
+
1052
+        if ($path === false) {
1053
+            return null;
1054
+        }
1055
+
1056
+        return (string)$path;
1057
+    }
1058
+
1059
+    /**
1060
+     * get the storage id of the storage for a file and the internal path of the file
1061
+     * unlike getPathById this does not limit the search to files on this storage and
1062
+     * instead does a global search in the cache table
1063
+     *
1064
+     * @param int $id
1065
+     * @return array first element holding the storage id, second the path
1066
+     * @deprecated use getPathById() instead
1067
+     */
1068
+    public static function getById($id) {
1069
+        $query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
1070
+        $query->select('path', 'storage')
1071
+            ->from('filecache')
1072
+            ->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
1073
+
1074
+        $result = $query->execute();
1075
+        $row = $result->fetch();
1076
+        $result->closeCursor();
1077
+
1078
+        if ($row) {
1079
+            $numericId = $row['storage'];
1080
+            $path = $row['path'];
1081
+        } else {
1082
+            return null;
1083
+        }
1084
+
1085
+        if ($id = Storage::getStorageId($numericId)) {
1086
+            return [$id, $path];
1087
+        } else {
1088
+            return null;
1089
+        }
1090
+    }
1091
+
1092
+    /**
1093
+     * normalize the given path
1094
+     *
1095
+     * @param string $path
1096
+     * @return string
1097
+     */
1098
+    public function normalize($path) {
1099
+        return trim(\OC_Util::normalizeUnicode($path), '/');
1100
+    }
1101
+
1102
+    /**
1103
+     * Copy a file or folder in the cache
1104
+     *
1105
+     * @param ICache $sourceCache
1106
+     * @param ICacheEntry $sourceEntry
1107
+     * @param string $targetPath
1108
+     * @return int fileId of copied entry
1109
+     */
1110
+    public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
1111
+        if ($sourceEntry->getId() < 0) {
1112
+            throw new \RuntimeException("Invalid source cache entry on copyFromCache");
1113
+        }
1114
+        $data = $this->cacheEntryToArray($sourceEntry);
1115
+
1116
+        // when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
1117
+        if ($sourceCache instanceof Cache && $sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
1118
+            $data['encrypted'] = 0;
1119
+        }
1120
+
1121
+        $fileId = $this->put($targetPath, $data);
1122
+        if ($fileId <= 0) {
1123
+            throw new \RuntimeException("Failed to copy to " . $targetPath . " from cache with source data " . json_encode($data) . " ");
1124
+        }
1125
+        if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
1126
+            $folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId());
1127
+            foreach ($folderContent as $subEntry) {
1128
+                $subTargetPath = $targetPath . '/' . $subEntry->getName();
1129
+                $this->copyFromCache($sourceCache, $subEntry, $subTargetPath);
1130
+            }
1131
+        }
1132
+        return $fileId;
1133
+    }
1134
+
1135
+    private function cacheEntryToArray(ICacheEntry $entry): array {
1136
+        return [
1137
+            'size' => $entry->getSize(),
1138
+            'mtime' => $entry->getMTime(),
1139
+            'storage_mtime' => $entry->getStorageMTime(),
1140
+            'mimetype' => $entry->getMimeType(),
1141
+            'mimepart' => $entry->getMimePart(),
1142
+            'etag' => $entry->getEtag(),
1143
+            'permissions' => $entry->getPermissions(),
1144
+            'encrypted' => $entry->isEncrypted(),
1145
+            'creation_time' => $entry->getCreationTime(),
1146
+            'upload_time' => $entry->getUploadTime(),
1147
+            'metadata_etag' => $entry->getMetadataEtag(),
1148
+        ];
1149
+    }
1150
+
1151
+    public function getQueryFilterForStorage(): ISearchOperator {
1152
+        return new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', $this->getNumericStorageId());
1153
+    }
1154
+
1155
+    public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry {
1156
+        if ($rawEntry->getStorageId() === $this->getNumericStorageId()) {
1157
+            return $rawEntry;
1158
+        } else {
1159
+            return null;
1160
+        }
1161
+    }
1162 1162
 }
Please login to merge, or discard this patch.
Spacing   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -188,29 +188,29 @@  discard block
 block discarded – undo
188 188
 	 */
189 189
 	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
190 190
 		//fix types
191
-		$data['name'] = (string)$data['name'];
192
-		$data['path'] = (string)$data['path'];
193
-		$data['fileid'] = (int)$data['fileid'];
194
-		$data['parent'] = (int)$data['parent'];
191
+		$data['name'] = (string) $data['name'];
192
+		$data['path'] = (string) $data['path'];
193
+		$data['fileid'] = (int) $data['fileid'];
194
+		$data['parent'] = (int) $data['parent'];
195 195
 		$data['size'] = Util::numericToNumber($data['size']);
196 196
 		$data['unencrypted_size'] = Util::numericToNumber($data['unencrypted_size'] ?? 0);
197
-		$data['mtime'] = (int)$data['mtime'];
198
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
199
-		$data['encryptedVersion'] = (int)$data['encrypted'];
200
-		$data['encrypted'] = (bool)$data['encrypted'];
197
+		$data['mtime'] = (int) $data['mtime'];
198
+		$data['storage_mtime'] = (int) $data['storage_mtime'];
199
+		$data['encryptedVersion'] = (int) $data['encrypted'];
200
+		$data['encrypted'] = (bool) $data['encrypted'];
201 201
 		$data['storage_id'] = $data['storage'];
202
-		$data['storage'] = (int)$data['storage'];
202
+		$data['storage'] = (int) $data['storage'];
203 203
 		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
204 204
 		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
205 205
 		if ($data['storage_mtime'] == 0) {
206 206
 			$data['storage_mtime'] = $data['mtime'];
207 207
 		}
208
-		$data['permissions'] = (int)$data['permissions'];
208
+		$data['permissions'] = (int) $data['permissions'];
209 209
 		if (isset($data['creation_time'])) {
210
-			$data['creation_time'] = (int)$data['creation_time'];
210
+			$data['creation_time'] = (int) $data['creation_time'];
211 211
 		}
212 212
 		if (isset($data['upload_time'])) {
213
-			$data['upload_time'] = (int)$data['upload_time'];
213
+			$data['upload_time'] = (int) $data['upload_time'];
214 214
 		}
215 215
 		return new CacheEntry($data);
216 216
 	}
@@ -243,7 +243,7 @@  discard block
 block discarded – undo
243 243
 			$files = $result->fetchAll();
244 244
 			$result->closeCursor();
245 245
 
246
-			return array_map(function (array $data) {
246
+			return array_map(function(array $data) {
247 247
 				return self::cacheEntryFromData($data, $this->mimetypeLoader);
248 248
 			}, $files);
249 249
 		}
@@ -372,7 +372,7 @@  discard block
 block discarded – undo
372 372
 
373 373
 			$query->update('filecache')
374 374
 				->whereFileId($id)
375
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
375
+				->andWhere($query->expr()->orX(...array_map(function($key, $value) use ($query) {
376 376
 					return $query->expr()->orX(
377 377
 						$query->expr()->neq($key, $query->createNamedParameter($value)),
378 378
 						$query->expr()->isNull($key)
@@ -401,7 +401,7 @@  discard block
 block discarded – undo
401 401
 				$query = $this->getQueryBuilder();
402 402
 				$query->update('filecache_extended')
403 403
 					->whereFileId($id)
404
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
404
+					->andWhere($query->expr()->orX(...array_map(function($key, $value) use ($query) {
405 405
 						return $query->expr()->orX(
406 406
 							$query->expr()->neq($key, $query->createNamedParameter($value)),
407 407
 							$query->expr()->isNull($key)
@@ -498,7 +498,7 @@  discard block
 block discarded – undo
498 498
 		$id = $result->fetchOne();
499 499
 		$result->closeCursor();
500 500
 
501
-		return $id === false ? -1 : (int)$id;
501
+		return $id === false ? -1 : (int) $id;
502 502
 	}
503 503
 
504 504
 	/**
@@ -512,7 +512,7 @@  discard block
 block discarded – undo
512 512
 			return -1;
513 513
 		} else {
514 514
 			$parent = $this->getParentPath($file);
515
-			return (int)$this->getId($parent);
515
+			return (int) $this->getId($parent);
516 516
 		}
517 517
 	}
518 518
 
@@ -579,10 +579,10 @@  discard block
 block discarded – undo
579 579
 		// and collecting all folder ids to later use to delete the filecache entries
580 580
 		while ($entryId = array_pop($queue)) {
581 581
 			$children = $this->getFolderContentsById($entryId);
582
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
582
+			$childIds = array_map(function(ICacheEntry $cacheEntry) {
583 583
 				return $cacheEntry->getId();
584 584
 			}, $children);
585
-			$childPaths = array_map(function (ICacheEntry $cacheEntry) {
585
+			$childPaths = array_map(function(ICacheEntry $cacheEntry) {
586 586
 				return $cacheEntry->getPath();
587 587
 			}, $children);
588 588
 
@@ -599,7 +599,7 @@  discard block
 block discarded – undo
599 599
 			}
600 600
 
601 601
 			/** @var ICacheEntry[] $childFolders */
602
-			$childFolders = array_filter($children, function ($child) {
602
+			$childFolders = array_filter($children, function($child) {
603 603
 				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
604 604
 			});
605 605
 			foreach ($childFolders as $folder) {
@@ -669,7 +669,7 @@  discard block
 block discarded – undo
669 669
 
670 670
 			$sourceData = $sourceCache->get($sourcePath);
671 671
 			if ($sourceData === false) {
672
-				throw new \Exception('Invalid source storage path: ' . $sourcePath);
672
+				throw new \Exception('Invalid source storage path: '.$sourcePath);
673 673
 			}
674 674
 
675 675
 			$sourceId = $sourceData['fileid'];
@@ -679,10 +679,10 @@  discard block
 block discarded – undo
679 679
 			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
680 680
 
681 681
 			if (is_null($sourceStorageId) || $sourceStorageId === false) {
682
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
682
+				throw new \Exception('Invalid source storage id: '.$sourceStorageId);
683 683
 			}
684 684
 			if (is_null($targetStorageId) || $targetStorageId === false) {
685
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
685
+				throw new \Exception('Invalid target storage id: '.$targetStorageId);
686 686
 			}
687 687
 
688 688
 			$this->connection->beginTransaction();
@@ -701,7 +701,7 @@  discard block
 block discarded – undo
701 701
 					->set('path_hash', $fun->md5($newPathFunction))
702 702
 					->set('path', $newPathFunction)
703 703
 					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
704
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
704
+					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath).'/%')));
705 705
 
706 706
 				// when moving from an encrypted storage to a non-encrypted storage remove the `encrypted` mark
707 707
 				if ($sourceCache->hasEncryptionWrapper() && !$this->hasEncryptionWrapper()) {
@@ -791,7 +791,7 @@  discard block
 block discarded – undo
791 791
 		$result->closeCursor();
792 792
 
793 793
 		if ($size !== false) {
794
-			if ((int)$size === -1) {
794
+			if ((int) $size === -1) {
795 795
 				return self::SHALLOW;
796 796
 			} else {
797 797
 				return self::COMPLETE;
@@ -825,7 +825,7 @@  discard block
 block discarded – undo
825 825
 	 */
826 826
 	public function searchByMime($mimetype) {
827 827
 		if (!str_contains($mimetype, '/')) {
828
-			$operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%');
828
+			$operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype.'/%');
829 829
 		} else {
830 830
 			$operator = new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype);
831 831
 		}
@@ -875,7 +875,7 @@  discard block
 block discarded – undo
875 875
 				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
876 876
 
877 877
 			$result = $query->execute();
878
-			$size = (int)$result->fetchOne();
878
+			$size = (int) $result->fetchOne();
879 879
 			$result->closeCursor();
880 880
 
881 881
 			return $size;
@@ -924,13 +924,13 @@  discard block
 block discarded – undo
924 924
 			$result->closeCursor();
925 925
 
926 926
 			if ($rows) {
927
-				$sizes = array_map(function (array $row) {
927
+				$sizes = array_map(function(array $row) {
928 928
 					return Util::numericToNumber($row['size']);
929 929
 				}, $rows);
930
-				$unencryptedOnlySizes = array_map(function (array $row) {
930
+				$unencryptedOnlySizes = array_map(function(array $row) {
931 931
 					return Util::numericToNumber($row['unencrypted_size']);
932 932
 				}, $rows);
933
-				$unencryptedSizes = array_map(function (array $row) {
933
+				$unencryptedSizes = array_map(function(array $row) {
934 934
 					return Util::numericToNumber(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']);
935 935
 				}, $rows);
936 936
 
@@ -997,8 +997,8 @@  discard block
 block discarded – undo
997 997
 		$files = $result->fetchAll(\PDO::FETCH_COLUMN);
998 998
 		$result->closeCursor();
999 999
 
1000
-		return array_map(function ($id) {
1001
-			return (int)$id;
1000
+		return array_map(function($id) {
1001
+			return (int) $id;
1002 1002
 		}, $files);
1003 1003
 	}
1004 1004
 
@@ -1029,7 +1029,7 @@  discard block
 block discarded – undo
1029 1029
 		}
1030 1030
 
1031 1031
 		// Make sure Oracle does not continue with null for empty strings
1032
-		return (string)$path;
1032
+		return (string) $path;
1033 1033
 	}
1034 1034
 
1035 1035
 	/**
@@ -1053,7 +1053,7 @@  discard block
 block discarded – undo
1053 1053
 			return null;
1054 1054
 		}
1055 1055
 
1056
-		return (string)$path;
1056
+		return (string) $path;
1057 1057
 	}
1058 1058
 
1059 1059
 	/**
@@ -1120,12 +1120,12 @@  discard block
 block discarded – undo
1120 1120
 
1121 1121
 		$fileId = $this->put($targetPath, $data);
1122 1122
 		if ($fileId <= 0) {
1123
-			throw new \RuntimeException("Failed to copy to " . $targetPath . " from cache with source data " . json_encode($data) . " ");
1123
+			throw new \RuntimeException("Failed to copy to ".$targetPath." from cache with source data ".json_encode($data)." ");
1124 1124
 		}
1125 1125
 		if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
1126 1126
 			$folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId());
1127 1127
 			foreach ($folderContent as $subEntry) {
1128
-				$subTargetPath = $targetPath . '/' . $subEntry->getName();
1128
+				$subTargetPath = $targetPath.'/'.$subEntry->getName();
1129 1129
 				$this->copyFromCache($sourceCache, $subEntry, $subTargetPath);
1130 1130
 			}
1131 1131
 		}
Please login to merge, or discard this patch.