Passed
Push — master ( cdfad9...e39d65 )
by Joas
12:09 queued 10s
created
lib/private/Security/CredentialsManager.php 2 patches
Indentation   +100 added lines, -100 removed lines patch added patch discarded remove patch
@@ -35,104 +35,104 @@
 block discarded – undo
35 35
  * @package OC\Security
36 36
  */
37 37
 class CredentialsManager implements ICredentialsManager {
38
-	public const DB_TABLE = 'storages_credentials';
39
-
40
-	/** @var ICrypto */
41
-	protected $crypto;
42
-
43
-	/** @var IDBConnection */
44
-	protected $dbConnection;
45
-
46
-	/**
47
-	 * @param ICrypto $crypto
48
-	 * @param IDBConnection $dbConnection
49
-	 */
50
-	public function __construct(ICrypto $crypto, IDBConnection $dbConnection) {
51
-		$this->crypto = $crypto;
52
-		$this->dbConnection = $dbConnection;
53
-	}
54
-
55
-	/**
56
-	 * Store a set of credentials
57
-	 *
58
-	 * @param string $userId empty string for system-wide credentials
59
-	 * @param string $identifier
60
-	 * @param mixed $credentials
61
-	 */
62
-	public function store($userId, $identifier, $credentials) {
63
-		$value = $this->crypto->encrypt(json_encode($credentials));
64
-
65
-		$this->dbConnection->setValues(self::DB_TABLE, [
66
-			'user' => (string)$userId,
67
-			'identifier' => $identifier,
68
-		], [
69
-			'credentials' => $value,
70
-		]);
71
-	}
72
-
73
-	/**
74
-	 * Retrieve a set of credentials
75
-	 *
76
-	 * @param string $userId empty string for system-wide credentials
77
-	 * @param string $identifier
78
-	 * @return mixed
79
-	 */
80
-	public function retrieve($userId, $identifier) {
81
-		$qb = $this->dbConnection->getQueryBuilder();
82
-		$qb->select('credentials')
83
-			->from(self::DB_TABLE)
84
-			->where($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier)));
85
-
86
-		if ($userId === '') {
87
-			$qb->andWhere($qb->expr()->emptyString('user'));
88
-		} else {
89
-			$qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string)$userId)));
90
-		}
91
-
92
-		$qResult = $qb->execute();
93
-		$result = $qResult->fetch();
94
-		$qResult->closeCursor();
95
-
96
-		if (!$result) {
97
-			return null;
98
-		}
99
-		$value = $result['credentials'];
100
-
101
-		return json_decode($this->crypto->decrypt($value), true);
102
-	}
103
-
104
-	/**
105
-	 * Delete a set of credentials
106
-	 *
107
-	 * @param string $userId empty string for system-wide credentials
108
-	 * @param string $identifier
109
-	 * @return int rows removed
110
-	 */
111
-	public function delete($userId, $identifier) {
112
-		$qb = $this->dbConnection->getQueryBuilder();
113
-		$qb->delete(self::DB_TABLE)
114
-			->where($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier)));
115
-
116
-		if ($userId === '') {
117
-			$qb->andWhere($qb->expr()->emptyString('user'));
118
-		} else {
119
-			$qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string)$userId)));
120
-		}
121
-
122
-		return $qb->execute();
123
-	}
124
-
125
-	/**
126
-	 * Erase all credentials stored for a user
127
-	 *
128
-	 * @param string $userId
129
-	 * @return int rows removed
130
-	 */
131
-	public function erase($userId) {
132
-		$qb = $this->dbConnection->getQueryBuilder();
133
-		$qb->delete(self::DB_TABLE)
134
-			->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
135
-		;
136
-		return $qb->execute();
137
-	}
38
+    public const DB_TABLE = 'storages_credentials';
39
+
40
+    /** @var ICrypto */
41
+    protected $crypto;
42
+
43
+    /** @var IDBConnection */
44
+    protected $dbConnection;
45
+
46
+    /**
47
+     * @param ICrypto $crypto
48
+     * @param IDBConnection $dbConnection
49
+     */
50
+    public function __construct(ICrypto $crypto, IDBConnection $dbConnection) {
51
+        $this->crypto = $crypto;
52
+        $this->dbConnection = $dbConnection;
53
+    }
54
+
55
+    /**
56
+     * Store a set of credentials
57
+     *
58
+     * @param string $userId empty string for system-wide credentials
59
+     * @param string $identifier
60
+     * @param mixed $credentials
61
+     */
62
+    public function store($userId, $identifier, $credentials) {
63
+        $value = $this->crypto->encrypt(json_encode($credentials));
64
+
65
+        $this->dbConnection->setValues(self::DB_TABLE, [
66
+            'user' => (string)$userId,
67
+            'identifier' => $identifier,
68
+        ], [
69
+            'credentials' => $value,
70
+        ]);
71
+    }
72
+
73
+    /**
74
+     * Retrieve a set of credentials
75
+     *
76
+     * @param string $userId empty string for system-wide credentials
77
+     * @param string $identifier
78
+     * @return mixed
79
+     */
80
+    public function retrieve($userId, $identifier) {
81
+        $qb = $this->dbConnection->getQueryBuilder();
82
+        $qb->select('credentials')
83
+            ->from(self::DB_TABLE)
84
+            ->where($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier)));
85
+
86
+        if ($userId === '') {
87
+            $qb->andWhere($qb->expr()->emptyString('user'));
88
+        } else {
89
+            $qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string)$userId)));
90
+        }
91
+
92
+        $qResult = $qb->execute();
93
+        $result = $qResult->fetch();
94
+        $qResult->closeCursor();
95
+
96
+        if (!$result) {
97
+            return null;
98
+        }
99
+        $value = $result['credentials'];
100
+
101
+        return json_decode($this->crypto->decrypt($value), true);
102
+    }
103
+
104
+    /**
105
+     * Delete a set of credentials
106
+     *
107
+     * @param string $userId empty string for system-wide credentials
108
+     * @param string $identifier
109
+     * @return int rows removed
110
+     */
111
+    public function delete($userId, $identifier) {
112
+        $qb = $this->dbConnection->getQueryBuilder();
113
+        $qb->delete(self::DB_TABLE)
114
+            ->where($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier)));
115
+
116
+        if ($userId === '') {
117
+            $qb->andWhere($qb->expr()->emptyString('user'));
118
+        } else {
119
+            $qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string)$userId)));
120
+        }
121
+
122
+        return $qb->execute();
123
+    }
124
+
125
+    /**
126
+     * Erase all credentials stored for a user
127
+     *
128
+     * @param string $userId
129
+     * @return int rows removed
130
+     */
131
+    public function erase($userId) {
132
+        $qb = $this->dbConnection->getQueryBuilder();
133
+        $qb->delete(self::DB_TABLE)
134
+            ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
135
+        ;
136
+        return $qb->execute();
137
+    }
138 138
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -63,7 +63,7 @@  discard block
 block discarded – undo
63 63
 		$value = $this->crypto->encrypt(json_encode($credentials));
64 64
 
65 65
 		$this->dbConnection->setValues(self::DB_TABLE, [
66
-			'user' => (string)$userId,
66
+			'user' => (string) $userId,
67 67
 			'identifier' => $identifier,
68 68
 		], [
69 69
 			'credentials' => $value,
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 		if ($userId === '') {
87 87
 			$qb->andWhere($qb->expr()->emptyString('user'));
88 88
 		} else {
89
-			$qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string)$userId)));
89
+			$qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string) $userId)));
90 90
 		}
91 91
 
92 92
 		$qResult = $qb->execute();
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
 		if ($userId === '') {
117 117
 			$qb->andWhere($qb->expr()->emptyString('user'));
118 118
 		} else {
119
-			$qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string)$userId)));
119
+			$qb->andWhere($qb->expr()->eq('user', $qb->createNamedParameter((string) $userId)));
120 120
 		}
121 121
 
122 122
 		return $qb->execute();
Please login to merge, or discard this patch.
lib/private/Share20/DefaultShareProvider.php 2 patches
Indentation   +1401 added lines, -1401 removed lines patch added patch discarded remove patch
@@ -62,1441 +62,1441 @@
 block discarded – undo
62 62
  */
63 63
 class DefaultShareProvider implements IShareProvider {
64 64
 
65
-	// Special share type for user modified group shares
66
-	public const SHARE_TYPE_USERGROUP = 2;
67
-
68
-	/** @var IDBConnection */
69
-	private $dbConn;
70
-
71
-	/** @var IUserManager */
72
-	private $userManager;
73
-
74
-	/** @var IGroupManager */
75
-	private $groupManager;
76
-
77
-	/** @var IRootFolder */
78
-	private $rootFolder;
79
-
80
-	/** @var IMailer */
81
-	private $mailer;
82
-
83
-	/** @var Defaults */
84
-	private $defaults;
85
-
86
-	/** @var IFactory */
87
-	private $l10nFactory;
88
-
89
-	/** @var IURLGenerator */
90
-	private $urlGenerator;
91
-
92
-	/** @var IConfig */
93
-	private $config;
94
-
95
-	public function __construct(
96
-			IDBConnection $connection,
97
-			IUserManager $userManager,
98
-			IGroupManager $groupManager,
99
-			IRootFolder $rootFolder,
100
-			IMailer $mailer,
101
-			Defaults $defaults,
102
-			IFactory $l10nFactory,
103
-			IURLGenerator $urlGenerator,
104
-			IConfig $config) {
105
-		$this->dbConn = $connection;
106
-		$this->userManager = $userManager;
107
-		$this->groupManager = $groupManager;
108
-		$this->rootFolder = $rootFolder;
109
-		$this->mailer = $mailer;
110
-		$this->defaults = $defaults;
111
-		$this->l10nFactory = $l10nFactory;
112
-		$this->urlGenerator = $urlGenerator;
113
-		$this->config = $config;
114
-	}
115
-
116
-	/**
117
-	 * Return the identifier of this provider.
118
-	 *
119
-	 * @return string Containing only [a-zA-Z0-9]
120
-	 */
121
-	public function identifier() {
122
-		return 'ocinternal';
123
-	}
124
-
125
-	/**
126
-	 * Share a path
127
-	 *
128
-	 * @param \OCP\Share\IShare $share
129
-	 * @return \OCP\Share\IShare The share object
130
-	 * @throws ShareNotFound
131
-	 * @throws \Exception
132
-	 */
133
-	public function create(\OCP\Share\IShare $share) {
134
-		$qb = $this->dbConn->getQueryBuilder();
135
-
136
-		$qb->insert('share');
137
-		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
138
-
139
-		if ($share->getShareType() === IShare::TYPE_USER) {
140
-			//Set the UID of the user we share with
141
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
142
-			$qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
143
-
144
-			//If an expiration date is set store it
145
-			if ($share->getExpirationDate() !== null) {
146
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
147
-			}
148
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
149
-			//Set the GID of the group we share with
150
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
151
-
152
-			//If an expiration date is set store it
153
-			if ($share->getExpirationDate() !== null) {
154
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
155
-			}
156
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
157
-			//set label for public link
158
-			$qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
159
-			//Set the token of the share
160
-			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
161
-
162
-			//If a password is set store it
163
-			if ($share->getPassword() !== null) {
164
-				$qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
165
-			}
166
-
167
-			$qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
168
-
169
-			//If an expiration date is set store it
170
-			if ($share->getExpirationDate() !== null) {
171
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
172
-			}
173
-
174
-			if (method_exists($share, 'getParent')) {
175
-				$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
176
-			}
177
-		} else {
178
-			throw new \Exception('invalid share type!');
179
-		}
180
-
181
-		// Set what is shares
182
-		$qb->setValue('item_type', $qb->createParameter('itemType'));
183
-		if ($share->getNode() instanceof \OCP\Files\File) {
184
-			$qb->setParameter('itemType', 'file');
185
-		} else {
186
-			$qb->setParameter('itemType', 'folder');
187
-		}
188
-
189
-		// Set the file id
190
-		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
191
-		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
192
-
193
-		// set the permissions
194
-		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
195
-
196
-		// Set who created this share
197
-		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
198
-
199
-		// Set who is the owner of this file/folder (and this the owner of the share)
200
-		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
201
-
202
-		// Set the file target
203
-		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
204
-
205
-		// Set the time this share was created
206
-		$qb->setValue('stime', $qb->createNamedParameter(time()));
207
-
208
-		// insert the data and fetch the id of the share
209
-		$this->dbConn->beginTransaction();
210
-		$qb->execute();
211
-		$id = $this->dbConn->lastInsertId('*PREFIX*share');
212
-
213
-		// Now fetch the inserted share and create a complete share object
214
-		$qb = $this->dbConn->getQueryBuilder();
215
-		$qb->select('*')
216
-			->from('share')
217
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
218
-
219
-		$cursor = $qb->execute();
220
-		$data = $cursor->fetch();
221
-		$this->dbConn->commit();
222
-		$cursor->closeCursor();
223
-
224
-		if ($data === false) {
225
-			throw new ShareNotFound();
226
-		}
227
-
228
-		$mailSendValue = $share->getMailSend();
229
-		$data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
230
-
231
-		$share = $this->createShare($data);
232
-		return $share;
233
-	}
234
-
235
-	/**
236
-	 * Update a share
237
-	 *
238
-	 * @param \OCP\Share\IShare $share
239
-	 * @return \OCP\Share\IShare The share object
240
-	 * @throws ShareNotFound
241
-	 * @throws \OCP\Files\InvalidPathException
242
-	 * @throws \OCP\Files\NotFoundException
243
-	 */
244
-	public function update(\OCP\Share\IShare $share) {
245
-		$originalShare = $this->getShareById($share->getId());
246
-
247
-		if ($share->getShareType() === IShare::TYPE_USER) {
248
-			/*
65
+    // Special share type for user modified group shares
66
+    public const SHARE_TYPE_USERGROUP = 2;
67
+
68
+    /** @var IDBConnection */
69
+    private $dbConn;
70
+
71
+    /** @var IUserManager */
72
+    private $userManager;
73
+
74
+    /** @var IGroupManager */
75
+    private $groupManager;
76
+
77
+    /** @var IRootFolder */
78
+    private $rootFolder;
79
+
80
+    /** @var IMailer */
81
+    private $mailer;
82
+
83
+    /** @var Defaults */
84
+    private $defaults;
85
+
86
+    /** @var IFactory */
87
+    private $l10nFactory;
88
+
89
+    /** @var IURLGenerator */
90
+    private $urlGenerator;
91
+
92
+    /** @var IConfig */
93
+    private $config;
94
+
95
+    public function __construct(
96
+            IDBConnection $connection,
97
+            IUserManager $userManager,
98
+            IGroupManager $groupManager,
99
+            IRootFolder $rootFolder,
100
+            IMailer $mailer,
101
+            Defaults $defaults,
102
+            IFactory $l10nFactory,
103
+            IURLGenerator $urlGenerator,
104
+            IConfig $config) {
105
+        $this->dbConn = $connection;
106
+        $this->userManager = $userManager;
107
+        $this->groupManager = $groupManager;
108
+        $this->rootFolder = $rootFolder;
109
+        $this->mailer = $mailer;
110
+        $this->defaults = $defaults;
111
+        $this->l10nFactory = $l10nFactory;
112
+        $this->urlGenerator = $urlGenerator;
113
+        $this->config = $config;
114
+    }
115
+
116
+    /**
117
+     * Return the identifier of this provider.
118
+     *
119
+     * @return string Containing only [a-zA-Z0-9]
120
+     */
121
+    public function identifier() {
122
+        return 'ocinternal';
123
+    }
124
+
125
+    /**
126
+     * Share a path
127
+     *
128
+     * @param \OCP\Share\IShare $share
129
+     * @return \OCP\Share\IShare The share object
130
+     * @throws ShareNotFound
131
+     * @throws \Exception
132
+     */
133
+    public function create(\OCP\Share\IShare $share) {
134
+        $qb = $this->dbConn->getQueryBuilder();
135
+
136
+        $qb->insert('share');
137
+        $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
138
+
139
+        if ($share->getShareType() === IShare::TYPE_USER) {
140
+            //Set the UID of the user we share with
141
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
142
+            $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
143
+
144
+            //If an expiration date is set store it
145
+            if ($share->getExpirationDate() !== null) {
146
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
147
+            }
148
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
149
+            //Set the GID of the group we share with
150
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
151
+
152
+            //If an expiration date is set store it
153
+            if ($share->getExpirationDate() !== null) {
154
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
155
+            }
156
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
157
+            //set label for public link
158
+            $qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
159
+            //Set the token of the share
160
+            $qb->setValue('token', $qb->createNamedParameter($share->getToken()));
161
+
162
+            //If a password is set store it
163
+            if ($share->getPassword() !== null) {
164
+                $qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
165
+            }
166
+
167
+            $qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
168
+
169
+            //If an expiration date is set store it
170
+            if ($share->getExpirationDate() !== null) {
171
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
172
+            }
173
+
174
+            if (method_exists($share, 'getParent')) {
175
+                $qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
176
+            }
177
+        } else {
178
+            throw new \Exception('invalid share type!');
179
+        }
180
+
181
+        // Set what is shares
182
+        $qb->setValue('item_type', $qb->createParameter('itemType'));
183
+        if ($share->getNode() instanceof \OCP\Files\File) {
184
+            $qb->setParameter('itemType', 'file');
185
+        } else {
186
+            $qb->setParameter('itemType', 'folder');
187
+        }
188
+
189
+        // Set the file id
190
+        $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
191
+        $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
192
+
193
+        // set the permissions
194
+        $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
195
+
196
+        // Set who created this share
197
+        $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
198
+
199
+        // Set who is the owner of this file/folder (and this the owner of the share)
200
+        $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
201
+
202
+        // Set the file target
203
+        $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
204
+
205
+        // Set the time this share was created
206
+        $qb->setValue('stime', $qb->createNamedParameter(time()));
207
+
208
+        // insert the data and fetch the id of the share
209
+        $this->dbConn->beginTransaction();
210
+        $qb->execute();
211
+        $id = $this->dbConn->lastInsertId('*PREFIX*share');
212
+
213
+        // Now fetch the inserted share and create a complete share object
214
+        $qb = $this->dbConn->getQueryBuilder();
215
+        $qb->select('*')
216
+            ->from('share')
217
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
218
+
219
+        $cursor = $qb->execute();
220
+        $data = $cursor->fetch();
221
+        $this->dbConn->commit();
222
+        $cursor->closeCursor();
223
+
224
+        if ($data === false) {
225
+            throw new ShareNotFound();
226
+        }
227
+
228
+        $mailSendValue = $share->getMailSend();
229
+        $data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
230
+
231
+        $share = $this->createShare($data);
232
+        return $share;
233
+    }
234
+
235
+    /**
236
+     * Update a share
237
+     *
238
+     * @param \OCP\Share\IShare $share
239
+     * @return \OCP\Share\IShare The share object
240
+     * @throws ShareNotFound
241
+     * @throws \OCP\Files\InvalidPathException
242
+     * @throws \OCP\Files\NotFoundException
243
+     */
244
+    public function update(\OCP\Share\IShare $share) {
245
+        $originalShare = $this->getShareById($share->getId());
246
+
247
+        if ($share->getShareType() === IShare::TYPE_USER) {
248
+            /*
249 249
 			 * We allow updating the recipient on user shares.
250 250
 			 */
251
-			$qb = $this->dbConn->getQueryBuilder();
252
-			$qb->update('share')
253
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
254
-				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
255
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
256
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
257
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
258
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
259
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
260
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
261
-				->set('note', $qb->createNamedParameter($share->getNote()))
262
-				->set('accepted', $qb->createNamedParameter($share->getStatus()))
263
-				->execute();
264
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
265
-			$qb = $this->dbConn->getQueryBuilder();
266
-			$qb->update('share')
267
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
268
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
269
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
270
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
271
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
272
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
273
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
274
-				->set('note', $qb->createNamedParameter($share->getNote()))
275
-				->execute();
276
-
277
-			/*
251
+            $qb = $this->dbConn->getQueryBuilder();
252
+            $qb->update('share')
253
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
254
+                ->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
255
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
256
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
257
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
258
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
259
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
260
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
261
+                ->set('note', $qb->createNamedParameter($share->getNote()))
262
+                ->set('accepted', $qb->createNamedParameter($share->getStatus()))
263
+                ->execute();
264
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
265
+            $qb = $this->dbConn->getQueryBuilder();
266
+            $qb->update('share')
267
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
268
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
269
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
270
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
271
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
272
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
273
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
274
+                ->set('note', $qb->createNamedParameter($share->getNote()))
275
+                ->execute();
276
+
277
+            /*
278 278
 			 * Update all user defined group shares
279 279
 			 */
280
-			$qb = $this->dbConn->getQueryBuilder();
281
-			$qb->update('share')
282
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
283
-				->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
284
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
285
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
286
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
287
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
288
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
289
-				->set('note', $qb->createNamedParameter($share->getNote()))
290
-				->execute();
291
-
292
-			/*
280
+            $qb = $this->dbConn->getQueryBuilder();
281
+            $qb->update('share')
282
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
283
+                ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
284
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
285
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
286
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
287
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
288
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
289
+                ->set('note', $qb->createNamedParameter($share->getNote()))
290
+                ->execute();
291
+
292
+            /*
293 293
 			 * Now update the permissions for all children that have not set it to 0
294 294
 			 */
295
-			$qb = $this->dbConn->getQueryBuilder();
296
-			$qb->update('share')
297
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
298
-				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
299
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
300
-				->execute();
301
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
302
-			$qb = $this->dbConn->getQueryBuilder();
303
-			$qb->update('share')
304
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
305
-				->set('password', $qb->createNamedParameter($share->getPassword()))
306
-				->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
307
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
308
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
309
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
310
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
311
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
312
-				->set('token', $qb->createNamedParameter($share->getToken()))
313
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
314
-				->set('note', $qb->createNamedParameter($share->getNote()))
315
-				->set('label', $qb->createNamedParameter($share->getLabel()))
316
-				->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
317
-				->execute();
318
-		}
319
-
320
-		if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
321
-			$this->propagateNote($share);
322
-		}
323
-
324
-
325
-		return $share;
326
-	}
327
-
328
-	/**
329
-	 * Accept a share.
330
-	 *
331
-	 * @param IShare $share
332
-	 * @param string $recipient
333
-	 * @return IShare The share object
334
-	 * @since 9.0.0
335
-	 */
336
-	public function acceptShare(IShare $share, string $recipient): IShare {
337
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
338
-			$group = $this->groupManager->get($share->getSharedWith());
339
-			$user = $this->userManager->get($recipient);
340
-
341
-			if (is_null($group)) {
342
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
343
-			}
344
-
345
-			if (!$group->inGroup($user)) {
346
-				throw new ProviderException('Recipient not in receiving group');
347
-			}
348
-
349
-			// Try to fetch user specific share
350
-			$qb = $this->dbConn->getQueryBuilder();
351
-			$stmt = $qb->select('*')
352
-				->from('share')
353
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
354
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
355
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
356
-				->andWhere($qb->expr()->orX(
357
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
358
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
359
-				))
360
-				->execute();
361
-
362
-			$data = $stmt->fetch();
363
-			$stmt->closeCursor();
364
-
365
-			/*
295
+            $qb = $this->dbConn->getQueryBuilder();
296
+            $qb->update('share')
297
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
298
+                ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
299
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
300
+                ->execute();
301
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
302
+            $qb = $this->dbConn->getQueryBuilder();
303
+            $qb->update('share')
304
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
305
+                ->set('password', $qb->createNamedParameter($share->getPassword()))
306
+                ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
307
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
308
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
309
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
310
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
311
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
312
+                ->set('token', $qb->createNamedParameter($share->getToken()))
313
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
314
+                ->set('note', $qb->createNamedParameter($share->getNote()))
315
+                ->set('label', $qb->createNamedParameter($share->getLabel()))
316
+                ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
317
+                ->execute();
318
+        }
319
+
320
+        if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
321
+            $this->propagateNote($share);
322
+        }
323
+
324
+
325
+        return $share;
326
+    }
327
+
328
+    /**
329
+     * Accept a share.
330
+     *
331
+     * @param IShare $share
332
+     * @param string $recipient
333
+     * @return IShare The share object
334
+     * @since 9.0.0
335
+     */
336
+    public function acceptShare(IShare $share, string $recipient): IShare {
337
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
338
+            $group = $this->groupManager->get($share->getSharedWith());
339
+            $user = $this->userManager->get($recipient);
340
+
341
+            if (is_null($group)) {
342
+                throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
343
+            }
344
+
345
+            if (!$group->inGroup($user)) {
346
+                throw new ProviderException('Recipient not in receiving group');
347
+            }
348
+
349
+            // Try to fetch user specific share
350
+            $qb = $this->dbConn->getQueryBuilder();
351
+            $stmt = $qb->select('*')
352
+                ->from('share')
353
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
354
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
355
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
356
+                ->andWhere($qb->expr()->orX(
357
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
358
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
359
+                ))
360
+                ->execute();
361
+
362
+            $data = $stmt->fetch();
363
+            $stmt->closeCursor();
364
+
365
+            /*
366 366
 			 * Check if there already is a user specific group share.
367 367
 			 * If there is update it (if required).
368 368
 			 */
369
-			if ($data === false) {
370
-				$id = $this->createUserSpecificGroupShare($share, $recipient);
371
-			} else {
372
-				$id = $data['id'];
373
-			}
374
-		} elseif ($share->getShareType() === IShare::TYPE_USER) {
375
-			if ($share->getSharedWith() !== $recipient) {
376
-				throw new ProviderException('Recipient does not match');
377
-			}
378
-
379
-			$id = $share->getId();
380
-		} else {
381
-			throw new ProviderException('Invalid shareType');
382
-		}
383
-
384
-		$qb = $this->dbConn->getQueryBuilder();
385
-		$qb->update('share')
386
-			->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
387
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
388
-			->execute();
389
-
390
-		return $share;
391
-	}
392
-
393
-	/**
394
-	 * Get all children of this share
395
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
396
-	 *
397
-	 * @param \OCP\Share\IShare $parent
398
-	 * @return \OCP\Share\IShare[]
399
-	 */
400
-	public function getChildren(\OCP\Share\IShare $parent) {
401
-		$children = [];
402
-
403
-		$qb = $this->dbConn->getQueryBuilder();
404
-		$qb->select('*')
405
-			->from('share')
406
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
407
-			->andWhere(
408
-				$qb->expr()->in(
409
-					'share_type',
410
-					$qb->createNamedParameter([
411
-						IShare::TYPE_USER,
412
-						IShare::TYPE_GROUP,
413
-						IShare::TYPE_LINK,
414
-					], IQueryBuilder::PARAM_INT_ARRAY)
415
-				)
416
-			)
417
-			->andWhere($qb->expr()->orX(
418
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
419
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
420
-			))
421
-			->orderBy('id');
422
-
423
-		$cursor = $qb->execute();
424
-		while ($data = $cursor->fetch()) {
425
-			$children[] = $this->createShare($data);
426
-		}
427
-		$cursor->closeCursor();
428
-
429
-		return $children;
430
-	}
431
-
432
-	/**
433
-	 * Delete a share
434
-	 *
435
-	 * @param \OCP\Share\IShare $share
436
-	 */
437
-	public function delete(\OCP\Share\IShare $share) {
438
-		$qb = $this->dbConn->getQueryBuilder();
439
-		$qb->delete('share')
440
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
441
-
442
-		/*
369
+            if ($data === false) {
370
+                $id = $this->createUserSpecificGroupShare($share, $recipient);
371
+            } else {
372
+                $id = $data['id'];
373
+            }
374
+        } elseif ($share->getShareType() === IShare::TYPE_USER) {
375
+            if ($share->getSharedWith() !== $recipient) {
376
+                throw new ProviderException('Recipient does not match');
377
+            }
378
+
379
+            $id = $share->getId();
380
+        } else {
381
+            throw new ProviderException('Invalid shareType');
382
+        }
383
+
384
+        $qb = $this->dbConn->getQueryBuilder();
385
+        $qb->update('share')
386
+            ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
387
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
388
+            ->execute();
389
+
390
+        return $share;
391
+    }
392
+
393
+    /**
394
+     * Get all children of this share
395
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
396
+     *
397
+     * @param \OCP\Share\IShare $parent
398
+     * @return \OCP\Share\IShare[]
399
+     */
400
+    public function getChildren(\OCP\Share\IShare $parent) {
401
+        $children = [];
402
+
403
+        $qb = $this->dbConn->getQueryBuilder();
404
+        $qb->select('*')
405
+            ->from('share')
406
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
407
+            ->andWhere(
408
+                $qb->expr()->in(
409
+                    'share_type',
410
+                    $qb->createNamedParameter([
411
+                        IShare::TYPE_USER,
412
+                        IShare::TYPE_GROUP,
413
+                        IShare::TYPE_LINK,
414
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
415
+                )
416
+            )
417
+            ->andWhere($qb->expr()->orX(
418
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
419
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
420
+            ))
421
+            ->orderBy('id');
422
+
423
+        $cursor = $qb->execute();
424
+        while ($data = $cursor->fetch()) {
425
+            $children[] = $this->createShare($data);
426
+        }
427
+        $cursor->closeCursor();
428
+
429
+        return $children;
430
+    }
431
+
432
+    /**
433
+     * Delete a share
434
+     *
435
+     * @param \OCP\Share\IShare $share
436
+     */
437
+    public function delete(\OCP\Share\IShare $share) {
438
+        $qb = $this->dbConn->getQueryBuilder();
439
+        $qb->delete('share')
440
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
441
+
442
+        /*
443 443
 		 * If the share is a group share delete all possible
444 444
 		 * user defined groups shares.
445 445
 		 */
446
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
447
-			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
448
-		}
449
-
450
-		$qb->execute();
451
-	}
452
-
453
-	/**
454
-	 * Unshare a share from the recipient. If this is a group share
455
-	 * this means we need a special entry in the share db.
456
-	 *
457
-	 * @param IShare $share
458
-	 * @param string $recipient UserId of recipient
459
-	 * @throws BackendError
460
-	 * @throws ProviderException
461
-	 */
462
-	public function deleteFromSelf(IShare $share, $recipient) {
463
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
464
-			$group = $this->groupManager->get($share->getSharedWith());
465
-			$user = $this->userManager->get($recipient);
466
-
467
-			if (is_null($group)) {
468
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
469
-			}
470
-
471
-			if (!$group->inGroup($user)) {
472
-				throw new ProviderException('Recipient not in receiving group');
473
-			}
474
-
475
-			// Try to fetch user specific share
476
-			$qb = $this->dbConn->getQueryBuilder();
477
-			$stmt = $qb->select('*')
478
-				->from('share')
479
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
480
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
481
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
482
-				->andWhere($qb->expr()->orX(
483
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
484
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
485
-				))
486
-				->execute();
487
-
488
-			$data = $stmt->fetch();
489
-
490
-			/*
446
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
447
+            $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
448
+        }
449
+
450
+        $qb->execute();
451
+    }
452
+
453
+    /**
454
+     * Unshare a share from the recipient. If this is a group share
455
+     * this means we need a special entry in the share db.
456
+     *
457
+     * @param IShare $share
458
+     * @param string $recipient UserId of recipient
459
+     * @throws BackendError
460
+     * @throws ProviderException
461
+     */
462
+    public function deleteFromSelf(IShare $share, $recipient) {
463
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
464
+            $group = $this->groupManager->get($share->getSharedWith());
465
+            $user = $this->userManager->get($recipient);
466
+
467
+            if (is_null($group)) {
468
+                throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
469
+            }
470
+
471
+            if (!$group->inGroup($user)) {
472
+                throw new ProviderException('Recipient not in receiving group');
473
+            }
474
+
475
+            // Try to fetch user specific share
476
+            $qb = $this->dbConn->getQueryBuilder();
477
+            $stmt = $qb->select('*')
478
+                ->from('share')
479
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
480
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
481
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
482
+                ->andWhere($qb->expr()->orX(
483
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
484
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
485
+                ))
486
+                ->execute();
487
+
488
+            $data = $stmt->fetch();
489
+
490
+            /*
491 491
 			 * Check if there already is a user specific group share.
492 492
 			 * If there is update it (if required).
493 493
 			 */
494
-			if ($data === false) {
495
-				$id = $this->createUserSpecificGroupShare($share, $recipient);
496
-				$permissions = $share->getPermissions();
497
-			} else {
498
-				$permissions = $data['permissions'];
499
-				$id = $data['id'];
500
-			}
501
-
502
-			if ($permissions !== 0) {
503
-				// Update existing usergroup share
504
-				$qb = $this->dbConn->getQueryBuilder();
505
-				$qb->update('share')
506
-					->set('permissions', $qb->createNamedParameter(0))
507
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
508
-					->execute();
509
-			}
510
-		} elseif ($share->getShareType() === IShare::TYPE_USER) {
511
-			if ($share->getSharedWith() !== $recipient) {
512
-				throw new ProviderException('Recipient does not match');
513
-			}
514
-
515
-			// We can just delete user and link shares
516
-			$this->delete($share);
517
-		} else {
518
-			throw new ProviderException('Invalid shareType');
519
-		}
520
-	}
521
-
522
-	protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
523
-		$type = $share->getNodeType();
524
-
525
-		$qb = $this->dbConn->getQueryBuilder();
526
-		$qb->insert('share')
527
-			->values([
528
-				'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
529
-				'share_with' => $qb->createNamedParameter($recipient),
530
-				'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
531
-				'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
532
-				'parent' => $qb->createNamedParameter($share->getId()),
533
-				'item_type' => $qb->createNamedParameter($type),
534
-				'item_source' => $qb->createNamedParameter($share->getNodeId()),
535
-				'file_source' => $qb->createNamedParameter($share->getNodeId()),
536
-				'file_target' => $qb->createNamedParameter($share->getTarget()),
537
-				'permissions' => $qb->createNamedParameter($share->getPermissions()),
538
-				'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
539
-			])->execute();
540
-
541
-		return $qb->getLastInsertId();
542
-	}
543
-
544
-	/**
545
-	 * @inheritdoc
546
-	 *
547
-	 * For now this only works for group shares
548
-	 * If this gets implemented for normal shares we have to extend it
549
-	 */
550
-	public function restore(IShare $share, string $recipient): IShare {
551
-		$qb = $this->dbConn->getQueryBuilder();
552
-		$qb->select('permissions')
553
-			->from('share')
554
-			->where(
555
-				$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
556
-			);
557
-		$cursor = $qb->execute();
558
-		$data = $cursor->fetch();
559
-		$cursor->closeCursor();
560
-
561
-		$originalPermission = $data['permissions'];
562
-
563
-		$qb = $this->dbConn->getQueryBuilder();
564
-		$qb->update('share')
565
-			->set('permissions', $qb->createNamedParameter($originalPermission))
566
-			->where(
567
-				$qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
568
-			)->andWhere(
569
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
570
-			)->andWhere(
571
-				$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
572
-			);
573
-
574
-		$qb->execute();
575
-
576
-		return $this->getShareById($share->getId(), $recipient);
577
-	}
578
-
579
-	/**
580
-	 * @inheritdoc
581
-	 */
582
-	public function move(\OCP\Share\IShare $share, $recipient) {
583
-		if ($share->getShareType() === IShare::TYPE_USER) {
584
-			// Just update the target
585
-			$qb = $this->dbConn->getQueryBuilder();
586
-			$qb->update('share')
587
-				->set('file_target', $qb->createNamedParameter($share->getTarget()))
588
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
589
-				->execute();
590
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
591
-
592
-			// Check if there is a usergroup share
593
-			$qb = $this->dbConn->getQueryBuilder();
594
-			$stmt = $qb->select('id')
595
-				->from('share')
596
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
597
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
598
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
599
-				->andWhere($qb->expr()->orX(
600
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
601
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
602
-				))
603
-				->setMaxResults(1)
604
-				->execute();
605
-
606
-			$data = $stmt->fetch();
607
-			$stmt->closeCursor();
608
-
609
-			if ($data === false) {
610
-				// No usergroup share yet. Create one.
611
-				$qb = $this->dbConn->getQueryBuilder();
612
-				$qb->insert('share')
613
-					->values([
614
-						'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
615
-						'share_with' => $qb->createNamedParameter($recipient),
616
-						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
617
-						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
618
-						'parent' => $qb->createNamedParameter($share->getId()),
619
-						'item_type' => $qb->createNamedParameter($share->getNodeType()),
620
-						'item_source' => $qb->createNamedParameter($share->getNodeId()),
621
-						'file_source' => $qb->createNamedParameter($share->getNodeId()),
622
-						'file_target' => $qb->createNamedParameter($share->getTarget()),
623
-						'permissions' => $qb->createNamedParameter($share->getPermissions()),
624
-						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
625
-					])->execute();
626
-			} else {
627
-				// Already a usergroup share. Update it.
628
-				$qb = $this->dbConn->getQueryBuilder();
629
-				$qb->update('share')
630
-					->set('file_target', $qb->createNamedParameter($share->getTarget()))
631
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
632
-					->execute();
633
-			}
634
-		}
635
-
636
-		return $share;
637
-	}
638
-
639
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
640
-		$qb = $this->dbConn->getQueryBuilder();
641
-		$qb->select('*')
642
-			->from('share', 's')
643
-			->andWhere($qb->expr()->orX(
644
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
645
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
646
-			));
647
-
648
-		$qb->andWhere($qb->expr()->orX(
649
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
650
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
651
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
652
-		));
653
-
654
-		/**
655
-		 * Reshares for this user are shares where they are the owner.
656
-		 */
657
-		if ($reshares === false) {
658
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
659
-		} else {
660
-			$qb->andWhere(
661
-				$qb->expr()->orX(
662
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
663
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
664
-				)
665
-			);
666
-		}
667
-
668
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
669
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
670
-
671
-		$qb->orderBy('id');
672
-
673
-		$cursor = $qb->execute();
674
-		$shares = [];
675
-		while ($data = $cursor->fetch()) {
676
-			$shares[$data['fileid']][] = $this->createShare($data);
677
-		}
678
-		$cursor->closeCursor();
679
-
680
-		return $shares;
681
-	}
682
-
683
-	/**
684
-	 * @inheritdoc
685
-	 */
686
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
687
-		$qb = $this->dbConn->getQueryBuilder();
688
-		$qb->select('*')
689
-			->from('share')
690
-			->andWhere($qb->expr()->orX(
691
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
692
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
693
-			));
694
-
695
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
696
-
697
-		/**
698
-		 * Reshares for this user are shares where they are the owner.
699
-		 */
700
-		if ($reshares === false) {
701
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
702
-		} else {
703
-			if ($node === null) {
704
-				$qb->andWhere(
705
-					$qb->expr()->orX(
706
-						$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
707
-						$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
708
-					)
709
-				);
710
-			}
711
-		}
712
-
713
-		if ($node !== null) {
714
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
715
-		}
716
-
717
-		if ($limit !== -1) {
718
-			$qb->setMaxResults($limit);
719
-		}
720
-
721
-		$qb->setFirstResult($offset);
722
-		$qb->orderBy('id');
723
-
724
-		$cursor = $qb->execute();
725
-		$shares = [];
726
-		while ($data = $cursor->fetch()) {
727
-			$shares[] = $this->createShare($data);
728
-		}
729
-		$cursor->closeCursor();
730
-
731
-		return $shares;
732
-	}
733
-
734
-	/**
735
-	 * @inheritdoc
736
-	 */
737
-	public function getShareById($id, $recipientId = null) {
738
-		$qb = $this->dbConn->getQueryBuilder();
739
-
740
-		$qb->select('*')
741
-			->from('share')
742
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
743
-			->andWhere(
744
-				$qb->expr()->in(
745
-					'share_type',
746
-					$qb->createNamedParameter([
747
-						IShare::TYPE_USER,
748
-						IShare::TYPE_GROUP,
749
-						IShare::TYPE_LINK,
750
-					], IQueryBuilder::PARAM_INT_ARRAY)
751
-				)
752
-			)
753
-			->andWhere($qb->expr()->orX(
754
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
755
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
756
-			));
757
-
758
-		$cursor = $qb->execute();
759
-		$data = $cursor->fetch();
760
-		$cursor->closeCursor();
761
-
762
-		if ($data === false) {
763
-			throw new ShareNotFound();
764
-		}
765
-
766
-		try {
767
-			$share = $this->createShare($data);
768
-		} catch (InvalidShare $e) {
769
-			throw new ShareNotFound();
770
-		}
771
-
772
-		// If the recipient is set for a group share resolve to that user
773
-		if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
774
-			$share = $this->resolveGroupShares([$share], $recipientId)[0];
775
-		}
776
-
777
-		return $share;
778
-	}
779
-
780
-	/**
781
-	 * Get shares for a given path
782
-	 *
783
-	 * @param \OCP\Files\Node $path
784
-	 * @return \OCP\Share\IShare[]
785
-	 */
786
-	public function getSharesByPath(Node $path) {
787
-		$qb = $this->dbConn->getQueryBuilder();
788
-
789
-		$cursor = $qb->select('*')
790
-			->from('share')
791
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
792
-			->andWhere(
793
-				$qb->expr()->orX(
794
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
795
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
796
-				)
797
-			)
798
-			->andWhere($qb->expr()->orX(
799
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
800
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
801
-			))
802
-			->execute();
803
-
804
-		$shares = [];
805
-		while ($data = $cursor->fetch()) {
806
-			$shares[] = $this->createShare($data);
807
-		}
808
-		$cursor->closeCursor();
809
-
810
-		return $shares;
811
-	}
812
-
813
-	/**
814
-	 * Returns whether the given database result can be interpreted as
815
-	 * a share with accessible file (not trashed, not deleted)
816
-	 */
817
-	private function isAccessibleResult($data) {
818
-		// exclude shares leading to deleted file entries
819
-		if ($data['fileid'] === null || $data['path'] === null) {
820
-			return false;
821
-		}
822
-
823
-		// exclude shares leading to trashbin on home storages
824
-		$pathSections = explode('/', $data['path'], 2);
825
-		// FIXME: would not detect rare md5'd home storage case properly
826
-		if ($pathSections[0] !== 'files'
827
-				&& in_array(explode(':', $data['storage_string_id'], 2)[0], ['home', 'object'])) {
828
-			return false;
829
-		}
830
-		return true;
831
-	}
832
-
833
-	/**
834
-	 * @inheritdoc
835
-	 */
836
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
837
-		/** @var Share[] $shares */
838
-		$shares = [];
839
-
840
-		if ($shareType === IShare::TYPE_USER) {
841
-			//Get shares directly with this user
842
-			$qb = $this->dbConn->getQueryBuilder();
843
-			$qb->select('s.*',
844
-				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
845
-				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
846
-				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
847
-			)
848
-				->selectAlias('st.id', 'storage_string_id')
849
-				->from('share', 's')
850
-				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
851
-				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
852
-
853
-			// Order by id
854
-			$qb->orderBy('s.id');
855
-
856
-			// Set limit and offset
857
-			if ($limit !== -1) {
858
-				$qb->setMaxResults($limit);
859
-			}
860
-			$qb->setFirstResult($offset);
861
-
862
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
863
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
864
-				->andWhere($qb->expr()->orX(
865
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
866
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
867
-				));
868
-
869
-			// Filter by node if provided
870
-			if ($node !== null) {
871
-				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
872
-			}
873
-
874
-			$cursor = $qb->execute();
875
-
876
-			while ($data = $cursor->fetch()) {
877
-				if ($data['fileid'] && $data['path'] === null) {
878
-					$data['path'] = (string) $data['path'];
879
-					$data['name'] = (string) $data['name'];
880
-					$data['checksum'] = (string) $data['checksum'];
881
-				}
882
-				if ($this->isAccessibleResult($data)) {
883
-					$shares[] = $this->createShare($data);
884
-				}
885
-			}
886
-			$cursor->closeCursor();
887
-		} elseif ($shareType === IShare::TYPE_GROUP) {
888
-			$user = $this->userManager->get($userId);
889
-			$allGroups = ($user instanceof IUser) ? $this->groupManager->getUserGroupIds($user) : [];
890
-
891
-			/** @var Share[] $shares2 */
892
-			$shares2 = [];
893
-
894
-			$start = 0;
895
-			while (true) {
896
-				$groups = array_slice($allGroups, $start, 100);
897
-				$start += 100;
898
-
899
-				if ($groups === []) {
900
-					break;
901
-				}
902
-
903
-				$qb = $this->dbConn->getQueryBuilder();
904
-				$qb->select('s.*',
905
-					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
906
-					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
907
-					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
908
-				)
909
-					->selectAlias('st.id', 'storage_string_id')
910
-					->from('share', 's')
911
-					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
912
-					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
913
-					->orderBy('s.id')
914
-					->setFirstResult(0);
915
-
916
-				if ($limit !== -1) {
917
-					$qb->setMaxResults($limit - count($shares));
918
-				}
919
-
920
-				// Filter by node if provided
921
-				if ($node !== null) {
922
-					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
923
-				}
924
-
925
-
926
-				$groups = array_filter($groups);
927
-
928
-				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
929
-					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
930
-						$groups,
931
-						IQueryBuilder::PARAM_STR_ARRAY
932
-					)))
933
-					->andWhere($qb->expr()->orX(
934
-						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
935
-						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
936
-					));
937
-
938
-				$cursor = $qb->execute();
939
-				while ($data = $cursor->fetch()) {
940
-					if ($offset > 0) {
941
-						$offset--;
942
-						continue;
943
-					}
944
-
945
-					if ($this->isAccessibleResult($data)) {
946
-						$shares2[] = $this->createShare($data);
947
-					}
948
-				}
949
-				$cursor->closeCursor();
950
-			}
951
-
952
-			/*
494
+            if ($data === false) {
495
+                $id = $this->createUserSpecificGroupShare($share, $recipient);
496
+                $permissions = $share->getPermissions();
497
+            } else {
498
+                $permissions = $data['permissions'];
499
+                $id = $data['id'];
500
+            }
501
+
502
+            if ($permissions !== 0) {
503
+                // Update existing usergroup share
504
+                $qb = $this->dbConn->getQueryBuilder();
505
+                $qb->update('share')
506
+                    ->set('permissions', $qb->createNamedParameter(0))
507
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
508
+                    ->execute();
509
+            }
510
+        } elseif ($share->getShareType() === IShare::TYPE_USER) {
511
+            if ($share->getSharedWith() !== $recipient) {
512
+                throw new ProviderException('Recipient does not match');
513
+            }
514
+
515
+            // We can just delete user and link shares
516
+            $this->delete($share);
517
+        } else {
518
+            throw new ProviderException('Invalid shareType');
519
+        }
520
+    }
521
+
522
+    protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
523
+        $type = $share->getNodeType();
524
+
525
+        $qb = $this->dbConn->getQueryBuilder();
526
+        $qb->insert('share')
527
+            ->values([
528
+                'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
529
+                'share_with' => $qb->createNamedParameter($recipient),
530
+                'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
531
+                'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
532
+                'parent' => $qb->createNamedParameter($share->getId()),
533
+                'item_type' => $qb->createNamedParameter($type),
534
+                'item_source' => $qb->createNamedParameter($share->getNodeId()),
535
+                'file_source' => $qb->createNamedParameter($share->getNodeId()),
536
+                'file_target' => $qb->createNamedParameter($share->getTarget()),
537
+                'permissions' => $qb->createNamedParameter($share->getPermissions()),
538
+                'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
539
+            ])->execute();
540
+
541
+        return $qb->getLastInsertId();
542
+    }
543
+
544
+    /**
545
+     * @inheritdoc
546
+     *
547
+     * For now this only works for group shares
548
+     * If this gets implemented for normal shares we have to extend it
549
+     */
550
+    public function restore(IShare $share, string $recipient): IShare {
551
+        $qb = $this->dbConn->getQueryBuilder();
552
+        $qb->select('permissions')
553
+            ->from('share')
554
+            ->where(
555
+                $qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
556
+            );
557
+        $cursor = $qb->execute();
558
+        $data = $cursor->fetch();
559
+        $cursor->closeCursor();
560
+
561
+        $originalPermission = $data['permissions'];
562
+
563
+        $qb = $this->dbConn->getQueryBuilder();
564
+        $qb->update('share')
565
+            ->set('permissions', $qb->createNamedParameter($originalPermission))
566
+            ->where(
567
+                $qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
568
+            )->andWhere(
569
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
570
+            )->andWhere(
571
+                $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
572
+            );
573
+
574
+        $qb->execute();
575
+
576
+        return $this->getShareById($share->getId(), $recipient);
577
+    }
578
+
579
+    /**
580
+     * @inheritdoc
581
+     */
582
+    public function move(\OCP\Share\IShare $share, $recipient) {
583
+        if ($share->getShareType() === IShare::TYPE_USER) {
584
+            // Just update the target
585
+            $qb = $this->dbConn->getQueryBuilder();
586
+            $qb->update('share')
587
+                ->set('file_target', $qb->createNamedParameter($share->getTarget()))
588
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
589
+                ->execute();
590
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
591
+
592
+            // Check if there is a usergroup share
593
+            $qb = $this->dbConn->getQueryBuilder();
594
+            $stmt = $qb->select('id')
595
+                ->from('share')
596
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
597
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
598
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
599
+                ->andWhere($qb->expr()->orX(
600
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
601
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
602
+                ))
603
+                ->setMaxResults(1)
604
+                ->execute();
605
+
606
+            $data = $stmt->fetch();
607
+            $stmt->closeCursor();
608
+
609
+            if ($data === false) {
610
+                // No usergroup share yet. Create one.
611
+                $qb = $this->dbConn->getQueryBuilder();
612
+                $qb->insert('share')
613
+                    ->values([
614
+                        'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
615
+                        'share_with' => $qb->createNamedParameter($recipient),
616
+                        'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
617
+                        'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
618
+                        'parent' => $qb->createNamedParameter($share->getId()),
619
+                        'item_type' => $qb->createNamedParameter($share->getNodeType()),
620
+                        'item_source' => $qb->createNamedParameter($share->getNodeId()),
621
+                        'file_source' => $qb->createNamedParameter($share->getNodeId()),
622
+                        'file_target' => $qb->createNamedParameter($share->getTarget()),
623
+                        'permissions' => $qb->createNamedParameter($share->getPermissions()),
624
+                        'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
625
+                    ])->execute();
626
+            } else {
627
+                // Already a usergroup share. Update it.
628
+                $qb = $this->dbConn->getQueryBuilder();
629
+                $qb->update('share')
630
+                    ->set('file_target', $qb->createNamedParameter($share->getTarget()))
631
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
632
+                    ->execute();
633
+            }
634
+        }
635
+
636
+        return $share;
637
+    }
638
+
639
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
640
+        $qb = $this->dbConn->getQueryBuilder();
641
+        $qb->select('*')
642
+            ->from('share', 's')
643
+            ->andWhere($qb->expr()->orX(
644
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
645
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
646
+            ));
647
+
648
+        $qb->andWhere($qb->expr()->orX(
649
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
650
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
651
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
652
+        ));
653
+
654
+        /**
655
+         * Reshares for this user are shares where they are the owner.
656
+         */
657
+        if ($reshares === false) {
658
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
659
+        } else {
660
+            $qb->andWhere(
661
+                $qb->expr()->orX(
662
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
663
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
664
+                )
665
+            );
666
+        }
667
+
668
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
669
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
670
+
671
+        $qb->orderBy('id');
672
+
673
+        $cursor = $qb->execute();
674
+        $shares = [];
675
+        while ($data = $cursor->fetch()) {
676
+            $shares[$data['fileid']][] = $this->createShare($data);
677
+        }
678
+        $cursor->closeCursor();
679
+
680
+        return $shares;
681
+    }
682
+
683
+    /**
684
+     * @inheritdoc
685
+     */
686
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
687
+        $qb = $this->dbConn->getQueryBuilder();
688
+        $qb->select('*')
689
+            ->from('share')
690
+            ->andWhere($qb->expr()->orX(
691
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
692
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
693
+            ));
694
+
695
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
696
+
697
+        /**
698
+         * Reshares for this user are shares where they are the owner.
699
+         */
700
+        if ($reshares === false) {
701
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
702
+        } else {
703
+            if ($node === null) {
704
+                $qb->andWhere(
705
+                    $qb->expr()->orX(
706
+                        $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
707
+                        $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
708
+                    )
709
+                );
710
+            }
711
+        }
712
+
713
+        if ($node !== null) {
714
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
715
+        }
716
+
717
+        if ($limit !== -1) {
718
+            $qb->setMaxResults($limit);
719
+        }
720
+
721
+        $qb->setFirstResult($offset);
722
+        $qb->orderBy('id');
723
+
724
+        $cursor = $qb->execute();
725
+        $shares = [];
726
+        while ($data = $cursor->fetch()) {
727
+            $shares[] = $this->createShare($data);
728
+        }
729
+        $cursor->closeCursor();
730
+
731
+        return $shares;
732
+    }
733
+
734
+    /**
735
+     * @inheritdoc
736
+     */
737
+    public function getShareById($id, $recipientId = null) {
738
+        $qb = $this->dbConn->getQueryBuilder();
739
+
740
+        $qb->select('*')
741
+            ->from('share')
742
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
743
+            ->andWhere(
744
+                $qb->expr()->in(
745
+                    'share_type',
746
+                    $qb->createNamedParameter([
747
+                        IShare::TYPE_USER,
748
+                        IShare::TYPE_GROUP,
749
+                        IShare::TYPE_LINK,
750
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
751
+                )
752
+            )
753
+            ->andWhere($qb->expr()->orX(
754
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
755
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
756
+            ));
757
+
758
+        $cursor = $qb->execute();
759
+        $data = $cursor->fetch();
760
+        $cursor->closeCursor();
761
+
762
+        if ($data === false) {
763
+            throw new ShareNotFound();
764
+        }
765
+
766
+        try {
767
+            $share = $this->createShare($data);
768
+        } catch (InvalidShare $e) {
769
+            throw new ShareNotFound();
770
+        }
771
+
772
+        // If the recipient is set for a group share resolve to that user
773
+        if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
774
+            $share = $this->resolveGroupShares([$share], $recipientId)[0];
775
+        }
776
+
777
+        return $share;
778
+    }
779
+
780
+    /**
781
+     * Get shares for a given path
782
+     *
783
+     * @param \OCP\Files\Node $path
784
+     * @return \OCP\Share\IShare[]
785
+     */
786
+    public function getSharesByPath(Node $path) {
787
+        $qb = $this->dbConn->getQueryBuilder();
788
+
789
+        $cursor = $qb->select('*')
790
+            ->from('share')
791
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
792
+            ->andWhere(
793
+                $qb->expr()->orX(
794
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
795
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
796
+                )
797
+            )
798
+            ->andWhere($qb->expr()->orX(
799
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
800
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
801
+            ))
802
+            ->execute();
803
+
804
+        $shares = [];
805
+        while ($data = $cursor->fetch()) {
806
+            $shares[] = $this->createShare($data);
807
+        }
808
+        $cursor->closeCursor();
809
+
810
+        return $shares;
811
+    }
812
+
813
+    /**
814
+     * Returns whether the given database result can be interpreted as
815
+     * a share with accessible file (not trashed, not deleted)
816
+     */
817
+    private function isAccessibleResult($data) {
818
+        // exclude shares leading to deleted file entries
819
+        if ($data['fileid'] === null || $data['path'] === null) {
820
+            return false;
821
+        }
822
+
823
+        // exclude shares leading to trashbin on home storages
824
+        $pathSections = explode('/', $data['path'], 2);
825
+        // FIXME: would not detect rare md5'd home storage case properly
826
+        if ($pathSections[0] !== 'files'
827
+                && in_array(explode(':', $data['storage_string_id'], 2)[0], ['home', 'object'])) {
828
+            return false;
829
+        }
830
+        return true;
831
+    }
832
+
833
+    /**
834
+     * @inheritdoc
835
+     */
836
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
837
+        /** @var Share[] $shares */
838
+        $shares = [];
839
+
840
+        if ($shareType === IShare::TYPE_USER) {
841
+            //Get shares directly with this user
842
+            $qb = $this->dbConn->getQueryBuilder();
843
+            $qb->select('s.*',
844
+                'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
845
+                'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
846
+                'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
847
+            )
848
+                ->selectAlias('st.id', 'storage_string_id')
849
+                ->from('share', 's')
850
+                ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
851
+                ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
852
+
853
+            // Order by id
854
+            $qb->orderBy('s.id');
855
+
856
+            // Set limit and offset
857
+            if ($limit !== -1) {
858
+                $qb->setMaxResults($limit);
859
+            }
860
+            $qb->setFirstResult($offset);
861
+
862
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
863
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
864
+                ->andWhere($qb->expr()->orX(
865
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
866
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
867
+                ));
868
+
869
+            // Filter by node if provided
870
+            if ($node !== null) {
871
+                $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
872
+            }
873
+
874
+            $cursor = $qb->execute();
875
+
876
+            while ($data = $cursor->fetch()) {
877
+                if ($data['fileid'] && $data['path'] === null) {
878
+                    $data['path'] = (string) $data['path'];
879
+                    $data['name'] = (string) $data['name'];
880
+                    $data['checksum'] = (string) $data['checksum'];
881
+                }
882
+                if ($this->isAccessibleResult($data)) {
883
+                    $shares[] = $this->createShare($data);
884
+                }
885
+            }
886
+            $cursor->closeCursor();
887
+        } elseif ($shareType === IShare::TYPE_GROUP) {
888
+            $user = $this->userManager->get($userId);
889
+            $allGroups = ($user instanceof IUser) ? $this->groupManager->getUserGroupIds($user) : [];
890
+
891
+            /** @var Share[] $shares2 */
892
+            $shares2 = [];
893
+
894
+            $start = 0;
895
+            while (true) {
896
+                $groups = array_slice($allGroups, $start, 100);
897
+                $start += 100;
898
+
899
+                if ($groups === []) {
900
+                    break;
901
+                }
902
+
903
+                $qb = $this->dbConn->getQueryBuilder();
904
+                $qb->select('s.*',
905
+                    'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
906
+                    'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
907
+                    'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
908
+                )
909
+                    ->selectAlias('st.id', 'storage_string_id')
910
+                    ->from('share', 's')
911
+                    ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
912
+                    ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
913
+                    ->orderBy('s.id')
914
+                    ->setFirstResult(0);
915
+
916
+                if ($limit !== -1) {
917
+                    $qb->setMaxResults($limit - count($shares));
918
+                }
919
+
920
+                // Filter by node if provided
921
+                if ($node !== null) {
922
+                    $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
923
+                }
924
+
925
+
926
+                $groups = array_filter($groups);
927
+
928
+                $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
929
+                    ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
930
+                        $groups,
931
+                        IQueryBuilder::PARAM_STR_ARRAY
932
+                    )))
933
+                    ->andWhere($qb->expr()->orX(
934
+                        $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
935
+                        $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
936
+                    ));
937
+
938
+                $cursor = $qb->execute();
939
+                while ($data = $cursor->fetch()) {
940
+                    if ($offset > 0) {
941
+                        $offset--;
942
+                        continue;
943
+                    }
944
+
945
+                    if ($this->isAccessibleResult($data)) {
946
+                        $shares2[] = $this->createShare($data);
947
+                    }
948
+                }
949
+                $cursor->closeCursor();
950
+            }
951
+
952
+            /*
953 953
 			 * Resolve all group shares to user specific shares
954 954
 			 */
955
-			$shares = $this->resolveGroupShares($shares2, $userId);
956
-		} else {
957
-			throw new BackendError('Invalid backend');
958
-		}
959
-
960
-
961
-		return $shares;
962
-	}
963
-
964
-	/**
965
-	 * Get a share by token
966
-	 *
967
-	 * @param string $token
968
-	 * @return \OCP\Share\IShare
969
-	 * @throws ShareNotFound
970
-	 */
971
-	public function getShareByToken($token) {
972
-		$qb = $this->dbConn->getQueryBuilder();
973
-
974
-		$cursor = $qb->select('*')
975
-			->from('share')
976
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
977
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
978
-			->andWhere($qb->expr()->orX(
979
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
980
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
981
-			))
982
-			->execute();
983
-
984
-		$data = $cursor->fetch();
985
-
986
-		if ($data === false) {
987
-			throw new ShareNotFound();
988
-		}
989
-
990
-		try {
991
-			$share = $this->createShare($data);
992
-		} catch (InvalidShare $e) {
993
-			throw new ShareNotFound();
994
-		}
995
-
996
-		return $share;
997
-	}
998
-
999
-	/**
1000
-	 * Create a share object from an database row
1001
-	 *
1002
-	 * @param mixed[] $data
1003
-	 * @return \OCP\Share\IShare
1004
-	 * @throws InvalidShare
1005
-	 */
1006
-	private function createShare($data) {
1007
-		$share = new Share($this->rootFolder, $this->userManager);
1008
-		$share->setId((int)$data['id'])
1009
-			->setShareType((int)$data['share_type'])
1010
-			->setPermissions((int)$data['permissions'])
1011
-			->setTarget($data['file_target'])
1012
-			->setNote((string)$data['note'])
1013
-			->setMailSend((bool)$data['mail_send'])
1014
-			->setStatus((int)$data['accepted'])
1015
-			->setLabel($data['label']);
1016
-
1017
-		$shareTime = new \DateTime();
1018
-		$shareTime->setTimestamp((int)$data['stime']);
1019
-		$share->setShareTime($shareTime);
1020
-
1021
-		if ($share->getShareType() === IShare::TYPE_USER) {
1022
-			$share->setSharedWith($data['share_with']);
1023
-			$user = $this->userManager->get($data['share_with']);
1024
-			if ($user !== null) {
1025
-				$share->setSharedWithDisplayName($user->getDisplayName());
1026
-			}
1027
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1028
-			$share->setSharedWith($data['share_with']);
1029
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
1030
-			$share->setPassword($data['password']);
1031
-			$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1032
-			$share->setToken($data['token']);
1033
-		}
1034
-
1035
-		$share->setSharedBy($data['uid_initiator']);
1036
-		$share->setShareOwner($data['uid_owner']);
1037
-
1038
-		$share->setNodeId((int)$data['file_source']);
1039
-		$share->setNodeType($data['item_type']);
1040
-
1041
-		if ($data['expiration'] !== null) {
1042
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
1043
-			$share->setExpirationDate($expiration);
1044
-		}
1045
-
1046
-		if (isset($data['f_permissions'])) {
1047
-			$entryData = $data;
1048
-			$entryData['permissions'] = $entryData['f_permissions'];
1049
-			$entryData['parent'] = $entryData['f_parent'];
1050
-			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
1051
-				\OC::$server->getMimeTypeLoader()));
1052
-		}
1053
-
1054
-		$share->setProviderId($this->identifier());
1055
-		$share->setHideDownload((int)$data['hide_download'] === 1);
1056
-
1057
-		return $share;
1058
-	}
1059
-
1060
-	/**
1061
-	 * @param Share[] $shares
1062
-	 * @param $userId
1063
-	 * @return Share[] The updates shares if no update is found for a share return the original
1064
-	 */
1065
-	private function resolveGroupShares($shares, $userId) {
1066
-		$result = [];
1067
-
1068
-		$start = 0;
1069
-		while (true) {
1070
-			/** @var Share[] $shareSlice */
1071
-			$shareSlice = array_slice($shares, $start, 100);
1072
-			$start += 100;
1073
-
1074
-			if ($shareSlice === []) {
1075
-				break;
1076
-			}
1077
-
1078
-			/** @var int[] $ids */
1079
-			$ids = [];
1080
-			/** @var Share[] $shareMap */
1081
-			$shareMap = [];
1082
-
1083
-			foreach ($shareSlice as $share) {
1084
-				$ids[] = (int)$share->getId();
1085
-				$shareMap[$share->getId()] = $share;
1086
-			}
1087
-
1088
-			$qb = $this->dbConn->getQueryBuilder();
1089
-
1090
-			$query = $qb->select('*')
1091
-				->from('share')
1092
-				->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1093
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1094
-				->andWhere($qb->expr()->orX(
1095
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1096
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1097
-				));
1098
-
1099
-			$stmt = $query->execute();
1100
-
1101
-			while ($data = $stmt->fetch()) {
1102
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1103
-				$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1104
-				$shareMap[$data['parent']]->setTarget($data['file_target']);
1105
-				$shareMap[$data['parent']]->setParent($data['parent']);
1106
-			}
1107
-
1108
-			$stmt->closeCursor();
1109
-
1110
-			foreach ($shareMap as $share) {
1111
-				$result[] = $share;
1112
-			}
1113
-		}
1114
-
1115
-		return $result;
1116
-	}
1117
-
1118
-	/**
1119
-	 * A user is deleted from the system
1120
-	 * So clean up the relevant shares.
1121
-	 *
1122
-	 * @param string $uid
1123
-	 * @param int $shareType
1124
-	 */
1125
-	public function userDeleted($uid, $shareType) {
1126
-		$qb = $this->dbConn->getQueryBuilder();
1127
-
1128
-		$qb->delete('share');
1129
-
1130
-		if ($shareType === IShare::TYPE_USER) {
1131
-			/*
955
+            $shares = $this->resolveGroupShares($shares2, $userId);
956
+        } else {
957
+            throw new BackendError('Invalid backend');
958
+        }
959
+
960
+
961
+        return $shares;
962
+    }
963
+
964
+    /**
965
+     * Get a share by token
966
+     *
967
+     * @param string $token
968
+     * @return \OCP\Share\IShare
969
+     * @throws ShareNotFound
970
+     */
971
+    public function getShareByToken($token) {
972
+        $qb = $this->dbConn->getQueryBuilder();
973
+
974
+        $cursor = $qb->select('*')
975
+            ->from('share')
976
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
977
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
978
+            ->andWhere($qb->expr()->orX(
979
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
980
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
981
+            ))
982
+            ->execute();
983
+
984
+        $data = $cursor->fetch();
985
+
986
+        if ($data === false) {
987
+            throw new ShareNotFound();
988
+        }
989
+
990
+        try {
991
+            $share = $this->createShare($data);
992
+        } catch (InvalidShare $e) {
993
+            throw new ShareNotFound();
994
+        }
995
+
996
+        return $share;
997
+    }
998
+
999
+    /**
1000
+     * Create a share object from an database row
1001
+     *
1002
+     * @param mixed[] $data
1003
+     * @return \OCP\Share\IShare
1004
+     * @throws InvalidShare
1005
+     */
1006
+    private function createShare($data) {
1007
+        $share = new Share($this->rootFolder, $this->userManager);
1008
+        $share->setId((int)$data['id'])
1009
+            ->setShareType((int)$data['share_type'])
1010
+            ->setPermissions((int)$data['permissions'])
1011
+            ->setTarget($data['file_target'])
1012
+            ->setNote((string)$data['note'])
1013
+            ->setMailSend((bool)$data['mail_send'])
1014
+            ->setStatus((int)$data['accepted'])
1015
+            ->setLabel($data['label']);
1016
+
1017
+        $shareTime = new \DateTime();
1018
+        $shareTime->setTimestamp((int)$data['stime']);
1019
+        $share->setShareTime($shareTime);
1020
+
1021
+        if ($share->getShareType() === IShare::TYPE_USER) {
1022
+            $share->setSharedWith($data['share_with']);
1023
+            $user = $this->userManager->get($data['share_with']);
1024
+            if ($user !== null) {
1025
+                $share->setSharedWithDisplayName($user->getDisplayName());
1026
+            }
1027
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1028
+            $share->setSharedWith($data['share_with']);
1029
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
1030
+            $share->setPassword($data['password']);
1031
+            $share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1032
+            $share->setToken($data['token']);
1033
+        }
1034
+
1035
+        $share->setSharedBy($data['uid_initiator']);
1036
+        $share->setShareOwner($data['uid_owner']);
1037
+
1038
+        $share->setNodeId((int)$data['file_source']);
1039
+        $share->setNodeType($data['item_type']);
1040
+
1041
+        if ($data['expiration'] !== null) {
1042
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
1043
+            $share->setExpirationDate($expiration);
1044
+        }
1045
+
1046
+        if (isset($data['f_permissions'])) {
1047
+            $entryData = $data;
1048
+            $entryData['permissions'] = $entryData['f_permissions'];
1049
+            $entryData['parent'] = $entryData['f_parent'];
1050
+            $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
1051
+                \OC::$server->getMimeTypeLoader()));
1052
+        }
1053
+
1054
+        $share->setProviderId($this->identifier());
1055
+        $share->setHideDownload((int)$data['hide_download'] === 1);
1056
+
1057
+        return $share;
1058
+    }
1059
+
1060
+    /**
1061
+     * @param Share[] $shares
1062
+     * @param $userId
1063
+     * @return Share[] The updates shares if no update is found for a share return the original
1064
+     */
1065
+    private function resolveGroupShares($shares, $userId) {
1066
+        $result = [];
1067
+
1068
+        $start = 0;
1069
+        while (true) {
1070
+            /** @var Share[] $shareSlice */
1071
+            $shareSlice = array_slice($shares, $start, 100);
1072
+            $start += 100;
1073
+
1074
+            if ($shareSlice === []) {
1075
+                break;
1076
+            }
1077
+
1078
+            /** @var int[] $ids */
1079
+            $ids = [];
1080
+            /** @var Share[] $shareMap */
1081
+            $shareMap = [];
1082
+
1083
+            foreach ($shareSlice as $share) {
1084
+                $ids[] = (int)$share->getId();
1085
+                $shareMap[$share->getId()] = $share;
1086
+            }
1087
+
1088
+            $qb = $this->dbConn->getQueryBuilder();
1089
+
1090
+            $query = $qb->select('*')
1091
+                ->from('share')
1092
+                ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1093
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1094
+                ->andWhere($qb->expr()->orX(
1095
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1096
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1097
+                ));
1098
+
1099
+            $stmt = $query->execute();
1100
+
1101
+            while ($data = $stmt->fetch()) {
1102
+                $shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1103
+                $shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1104
+                $shareMap[$data['parent']]->setTarget($data['file_target']);
1105
+                $shareMap[$data['parent']]->setParent($data['parent']);
1106
+            }
1107
+
1108
+            $stmt->closeCursor();
1109
+
1110
+            foreach ($shareMap as $share) {
1111
+                $result[] = $share;
1112
+            }
1113
+        }
1114
+
1115
+        return $result;
1116
+    }
1117
+
1118
+    /**
1119
+     * A user is deleted from the system
1120
+     * So clean up the relevant shares.
1121
+     *
1122
+     * @param string $uid
1123
+     * @param int $shareType
1124
+     */
1125
+    public function userDeleted($uid, $shareType) {
1126
+        $qb = $this->dbConn->getQueryBuilder();
1127
+
1128
+        $qb->delete('share');
1129
+
1130
+        if ($shareType === IShare::TYPE_USER) {
1131
+            /*
1132 1132
 			 * Delete all user shares that are owned by this user
1133 1133
 			 * or that are received by this user
1134 1134
 			 */
1135 1135
 
1136
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
1136
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
1137 1137
 
1138
-			$qb->andWhere(
1139
-				$qb->expr()->orX(
1140
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1141
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1142
-				)
1143
-			);
1144
-		} elseif ($shareType === IShare::TYPE_GROUP) {
1145
-			/*
1138
+            $qb->andWhere(
1139
+                $qb->expr()->orX(
1140
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1141
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1142
+                )
1143
+            );
1144
+        } elseif ($shareType === IShare::TYPE_GROUP) {
1145
+            /*
1146 1146
 			 * Delete all group shares that are owned by this user
1147 1147
 			 * Or special user group shares that are received by this user
1148 1148
 			 */
1149
-			$qb->where(
1150
-				$qb->expr()->andX(
1151
-					$qb->expr()->orX(
1152
-						$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1153
-						$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
1154
-					),
1155
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1156
-				)
1157
-			);
1158
-
1159
-			$qb->orWhere(
1160
-				$qb->expr()->andX(
1161
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1162
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1163
-				)
1164
-			);
1165
-		} elseif ($shareType === IShare::TYPE_LINK) {
1166
-			/*
1149
+            $qb->where(
1150
+                $qb->expr()->andX(
1151
+                    $qb->expr()->orX(
1152
+                        $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1153
+                        $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
1154
+                    ),
1155
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1156
+                )
1157
+            );
1158
+
1159
+            $qb->orWhere(
1160
+                $qb->expr()->andX(
1161
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1162
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1163
+                )
1164
+            );
1165
+        } elseif ($shareType === IShare::TYPE_LINK) {
1166
+            /*
1167 1167
 			 * Delete all link shares owned by this user.
1168 1168
 			 * And all link shares initiated by this user (until #22327 is in)
1169 1169
 			 */
1170
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
1171
-
1172
-			$qb->andWhere(
1173
-				$qb->expr()->orX(
1174
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1175
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1176
-				)
1177
-			);
1178
-		} else {
1179
-			\OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
1180
-			return;
1181
-		}
1182
-
1183
-		$qb->execute();
1184
-	}
1185
-
1186
-	/**
1187
-	 * Delete all shares received by this group. As well as any custom group
1188
-	 * shares for group members.
1189
-	 *
1190
-	 * @param string $gid
1191
-	 */
1192
-	public function groupDeleted($gid) {
1193
-		/*
1170
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
1171
+
1172
+            $qb->andWhere(
1173
+                $qb->expr()->orX(
1174
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1175
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1176
+                )
1177
+            );
1178
+        } else {
1179
+            \OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
1180
+            return;
1181
+        }
1182
+
1183
+        $qb->execute();
1184
+    }
1185
+
1186
+    /**
1187
+     * Delete all shares received by this group. As well as any custom group
1188
+     * shares for group members.
1189
+     *
1190
+     * @param string $gid
1191
+     */
1192
+    public function groupDeleted($gid) {
1193
+        /*
1194 1194
 		 * First delete all custom group shares for group members
1195 1195
 		 */
1196
-		$qb = $this->dbConn->getQueryBuilder();
1197
-		$qb->select('id')
1198
-			->from('share')
1199
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1200
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1201
-
1202
-		$cursor = $qb->execute();
1203
-		$ids = [];
1204
-		while ($row = $cursor->fetch()) {
1205
-			$ids[] = (int)$row['id'];
1206
-		}
1207
-		$cursor->closeCursor();
1208
-
1209
-		if (!empty($ids)) {
1210
-			$chunks = array_chunk($ids, 100);
1211
-			foreach ($chunks as $chunk) {
1212
-				$qb->delete('share')
1213
-					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1214
-					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1215
-				$qb->execute();
1216
-			}
1217
-		}
1218
-
1219
-		/*
1196
+        $qb = $this->dbConn->getQueryBuilder();
1197
+        $qb->select('id')
1198
+            ->from('share')
1199
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1200
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1201
+
1202
+        $cursor = $qb->execute();
1203
+        $ids = [];
1204
+        while ($row = $cursor->fetch()) {
1205
+            $ids[] = (int)$row['id'];
1206
+        }
1207
+        $cursor->closeCursor();
1208
+
1209
+        if (!empty($ids)) {
1210
+            $chunks = array_chunk($ids, 100);
1211
+            foreach ($chunks as $chunk) {
1212
+                $qb->delete('share')
1213
+                    ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1214
+                    ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1215
+                $qb->execute();
1216
+            }
1217
+        }
1218
+
1219
+        /*
1220 1220
 		 * Now delete all the group shares
1221 1221
 		 */
1222
-		$qb = $this->dbConn->getQueryBuilder();
1223
-		$qb->delete('share')
1224
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1225
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1226
-		$qb->execute();
1227
-	}
1228
-
1229
-	/**
1230
-	 * Delete custom group shares to this group for this user
1231
-	 *
1232
-	 * @param string $uid
1233
-	 * @param string $gid
1234
-	 */
1235
-	public function userDeletedFromGroup($uid, $gid) {
1236
-		/*
1222
+        $qb = $this->dbConn->getQueryBuilder();
1223
+        $qb->delete('share')
1224
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1225
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1226
+        $qb->execute();
1227
+    }
1228
+
1229
+    /**
1230
+     * Delete custom group shares to this group for this user
1231
+     *
1232
+     * @param string $uid
1233
+     * @param string $gid
1234
+     */
1235
+    public function userDeletedFromGroup($uid, $gid) {
1236
+        /*
1237 1237
 		 * Get all group shares
1238 1238
 		 */
1239
-		$qb = $this->dbConn->getQueryBuilder();
1240
-		$qb->select('id')
1241
-			->from('share')
1242
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1243
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1244
-
1245
-		$cursor = $qb->execute();
1246
-		$ids = [];
1247
-		while ($row = $cursor->fetch()) {
1248
-			$ids[] = (int)$row['id'];
1249
-		}
1250
-		$cursor->closeCursor();
1251
-
1252
-		if (!empty($ids)) {
1253
-			$chunks = array_chunk($ids, 100);
1254
-			foreach ($chunks as $chunk) {
1255
-				/*
1239
+        $qb = $this->dbConn->getQueryBuilder();
1240
+        $qb->select('id')
1241
+            ->from('share')
1242
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1243
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1244
+
1245
+        $cursor = $qb->execute();
1246
+        $ids = [];
1247
+        while ($row = $cursor->fetch()) {
1248
+            $ids[] = (int)$row['id'];
1249
+        }
1250
+        $cursor->closeCursor();
1251
+
1252
+        if (!empty($ids)) {
1253
+            $chunks = array_chunk($ids, 100);
1254
+            foreach ($chunks as $chunk) {
1255
+                /*
1256 1256
 				 * Delete all special shares wit this users for the found group shares
1257 1257
 				 */
1258
-				$qb->delete('share')
1259
-					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1260
-					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1261
-					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1262
-				$qb->execute();
1263
-			}
1264
-		}
1265
-	}
1266
-
1267
-	/**
1268
-	 * @inheritdoc
1269
-	 */
1270
-	public function getAccessList($nodes, $currentAccess) {
1271
-		$ids = [];
1272
-		foreach ($nodes as $node) {
1273
-			$ids[] = $node->getId();
1274
-		}
1275
-
1276
-		$qb = $this->dbConn->getQueryBuilder();
1277
-
1278
-		$or = $qb->expr()->orX(
1279
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
1280
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1281
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
1282
-		);
1283
-
1284
-		if ($currentAccess) {
1285
-			$or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)));
1286
-		}
1287
-
1288
-		$qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1289
-			->from('share')
1290
-			->where(
1291
-				$or
1292
-			)
1293
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1294
-			->andWhere($qb->expr()->orX(
1295
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1296
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1297
-			));
1298
-		$cursor = $qb->execute();
1299
-
1300
-		$users = [];
1301
-		$link = false;
1302
-		while ($row = $cursor->fetch()) {
1303
-			$type = (int)$row['share_type'];
1304
-			if ($type === IShare::TYPE_USER) {
1305
-				$uid = $row['share_with'];
1306
-				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1307
-				$users[$uid][$row['id']] = $row;
1308
-			} elseif ($type === IShare::TYPE_GROUP) {
1309
-				$gid = $row['share_with'];
1310
-				$group = $this->groupManager->get($gid);
1311
-
1312
-				if ($group === null) {
1313
-					continue;
1314
-				}
1315
-
1316
-				$userList = $group->getUsers();
1317
-				foreach ($userList as $user) {
1318
-					$uid = $user->getUID();
1319
-					$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1320
-					$users[$uid][$row['id']] = $row;
1321
-				}
1322
-			} elseif ($type === IShare::TYPE_LINK) {
1323
-				$link = true;
1324
-			} elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
1325
-				$uid = $row['share_with'];
1326
-				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1327
-				$users[$uid][$row['id']] = $row;
1328
-			}
1329
-		}
1330
-		$cursor->closeCursor();
1331
-
1332
-		if ($currentAccess === true) {
1333
-			$users = array_map([$this, 'filterSharesOfUser'], $users);
1334
-			$users = array_filter($users);
1335
-		} else {
1336
-			$users = array_keys($users);
1337
-		}
1338
-
1339
-		return ['users' => $users, 'public' => $link];
1340
-	}
1341
-
1342
-	/**
1343
-	 * For each user the path with the fewest slashes is returned
1344
-	 * @param array $shares
1345
-	 * @return array
1346
-	 */
1347
-	protected function filterSharesOfUser(array $shares) {
1348
-		// Group shares when the user has a share exception
1349
-		foreach ($shares as $id => $share) {
1350
-			$type = (int) $share['share_type'];
1351
-			$permissions = (int) $share['permissions'];
1352
-
1353
-			if ($type === IShare::TYPE_USERGROUP) {
1354
-				unset($shares[$share['parent']]);
1355
-
1356
-				if ($permissions === 0) {
1357
-					unset($shares[$id]);
1358
-				}
1359
-			}
1360
-		}
1361
-
1362
-		$best = [];
1363
-		$bestDepth = 0;
1364
-		foreach ($shares as $id => $share) {
1365
-			$depth = substr_count($share['file_target'], '/');
1366
-			if (empty($best) || $depth < $bestDepth) {
1367
-				$bestDepth = $depth;
1368
-				$best = [
1369
-					'node_id' => $share['file_source'],
1370
-					'node_path' => $share['file_target'],
1371
-				];
1372
-			}
1373
-		}
1374
-
1375
-		return $best;
1376
-	}
1377
-
1378
-	/**
1379
-	 * propagate notes to the recipients
1380
-	 *
1381
-	 * @param IShare $share
1382
-	 * @throws \OCP\Files\NotFoundException
1383
-	 */
1384
-	private function propagateNote(IShare $share) {
1385
-		if ($share->getShareType() === IShare::TYPE_USER) {
1386
-			$user = $this->userManager->get($share->getSharedWith());
1387
-			$this->sendNote([$user], $share);
1388
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1389
-			$group = $this->groupManager->get($share->getSharedWith());
1390
-			$groupMembers = $group->getUsers();
1391
-			$this->sendNote($groupMembers, $share);
1392
-		}
1393
-	}
1394
-
1395
-	/**
1396
-	 * send note by mail
1397
-	 *
1398
-	 * @param array $recipients
1399
-	 * @param IShare $share
1400
-	 * @throws \OCP\Files\NotFoundException
1401
-	 */
1402
-	private function sendNote(array $recipients, IShare $share) {
1403
-		$toListByLanguage = [];
1404
-
1405
-		foreach ($recipients as $recipient) {
1406
-			/** @var IUser $recipient */
1407
-			$email = $recipient->getEMailAddress();
1408
-			if ($email) {
1409
-				$language = $this->l10nFactory->getUserLanguage($recipient);
1410
-				if (!isset($toListByLanguage[$language])) {
1411
-					$toListByLanguage[$language] = [];
1412
-				}
1413
-				$toListByLanguage[$language][$email] = $recipient->getDisplayName();
1414
-			}
1415
-		}
1416
-
1417
-		if (empty($toListByLanguage)) {
1418
-			return;
1419
-		}
1420
-
1421
-		foreach ($toListByLanguage as $l10n => $toList) {
1422
-			$filename = $share->getNode()->getName();
1423
-			$initiator = $share->getSharedBy();
1424
-			$note = $share->getNote();
1425
-
1426
-			$l = $this->l10nFactory->get('lib', $l10n);
1427
-
1428
-			$initiatorUser = $this->userManager->get($initiator);
1429
-			$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1430
-			$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1431
-			$plainHeading = $l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
1432
-			$htmlHeading = $l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
1433
-			$message = $this->mailer->createMessage();
1434
-
1435
-			$emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1436
-
1437
-			$emailTemplate->setSubject($l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
1438
-			$emailTemplate->addHeader();
1439
-			$emailTemplate->addHeading($htmlHeading, $plainHeading);
1440
-			$emailTemplate->addBodyText(htmlspecialchars($note), $note);
1441
-
1442
-			$link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1443
-			$emailTemplate->addBodyButton(
1444
-				$l->t('Open »%s«', [$filename]),
1445
-				$link
1446
-			);
1447
-
1448
-
1449
-			// The "From" contains the sharers name
1450
-			$instanceName = $this->defaults->getName();
1451
-			$senderName = $l->t(
1452
-				'%1$s via %2$s',
1453
-				[
1454
-					$initiatorDisplayName,
1455
-					$instanceName
1456
-				]
1457
-			);
1458
-			$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1459
-			if ($initiatorEmailAddress !== null) {
1460
-				$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1461
-				$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1462
-			} else {
1463
-				$emailTemplate->addFooter();
1464
-			}
1465
-
1466
-			if (count($toList) === 1) {
1467
-				$message->setTo($toList);
1468
-			} else {
1469
-				$message->setTo([]);
1470
-				$message->setBcc($toList);
1471
-			}
1472
-			$message->useTemplate($emailTemplate);
1473
-			$this->mailer->send($message);
1474
-		}
1475
-	}
1476
-
1477
-	public function getAllShares(): iterable {
1478
-		$qb = $this->dbConn->getQueryBuilder();
1479
-
1480
-		$qb->select('*')
1481
-			->from('share')
1482
-			->where(
1483
-				$qb->expr()->orX(
1484
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)),
1485
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)),
1486
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK))
1487
-				)
1488
-			);
1489
-
1490
-		$cursor = $qb->execute();
1491
-		while ($data = $cursor->fetch()) {
1492
-			try {
1493
-				$share = $this->createShare($data);
1494
-			} catch (InvalidShare $e) {
1495
-				continue;
1496
-			}
1497
-
1498
-			yield $share;
1499
-		}
1500
-		$cursor->closeCursor();
1501
-	}
1258
+                $qb->delete('share')
1259
+                    ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1260
+                    ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1261
+                    ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1262
+                $qb->execute();
1263
+            }
1264
+        }
1265
+    }
1266
+
1267
+    /**
1268
+     * @inheritdoc
1269
+     */
1270
+    public function getAccessList($nodes, $currentAccess) {
1271
+        $ids = [];
1272
+        foreach ($nodes as $node) {
1273
+            $ids[] = $node->getId();
1274
+        }
1275
+
1276
+        $qb = $this->dbConn->getQueryBuilder();
1277
+
1278
+        $or = $qb->expr()->orX(
1279
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
1280
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1281
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
1282
+        );
1283
+
1284
+        if ($currentAccess) {
1285
+            $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)));
1286
+        }
1287
+
1288
+        $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1289
+            ->from('share')
1290
+            ->where(
1291
+                $or
1292
+            )
1293
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1294
+            ->andWhere($qb->expr()->orX(
1295
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1296
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1297
+            ));
1298
+        $cursor = $qb->execute();
1299
+
1300
+        $users = [];
1301
+        $link = false;
1302
+        while ($row = $cursor->fetch()) {
1303
+            $type = (int)$row['share_type'];
1304
+            if ($type === IShare::TYPE_USER) {
1305
+                $uid = $row['share_with'];
1306
+                $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1307
+                $users[$uid][$row['id']] = $row;
1308
+            } elseif ($type === IShare::TYPE_GROUP) {
1309
+                $gid = $row['share_with'];
1310
+                $group = $this->groupManager->get($gid);
1311
+
1312
+                if ($group === null) {
1313
+                    continue;
1314
+                }
1315
+
1316
+                $userList = $group->getUsers();
1317
+                foreach ($userList as $user) {
1318
+                    $uid = $user->getUID();
1319
+                    $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1320
+                    $users[$uid][$row['id']] = $row;
1321
+                }
1322
+            } elseif ($type === IShare::TYPE_LINK) {
1323
+                $link = true;
1324
+            } elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
1325
+                $uid = $row['share_with'];
1326
+                $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1327
+                $users[$uid][$row['id']] = $row;
1328
+            }
1329
+        }
1330
+        $cursor->closeCursor();
1331
+
1332
+        if ($currentAccess === true) {
1333
+            $users = array_map([$this, 'filterSharesOfUser'], $users);
1334
+            $users = array_filter($users);
1335
+        } else {
1336
+            $users = array_keys($users);
1337
+        }
1338
+
1339
+        return ['users' => $users, 'public' => $link];
1340
+    }
1341
+
1342
+    /**
1343
+     * For each user the path with the fewest slashes is returned
1344
+     * @param array $shares
1345
+     * @return array
1346
+     */
1347
+    protected function filterSharesOfUser(array $shares) {
1348
+        // Group shares when the user has a share exception
1349
+        foreach ($shares as $id => $share) {
1350
+            $type = (int) $share['share_type'];
1351
+            $permissions = (int) $share['permissions'];
1352
+
1353
+            if ($type === IShare::TYPE_USERGROUP) {
1354
+                unset($shares[$share['parent']]);
1355
+
1356
+                if ($permissions === 0) {
1357
+                    unset($shares[$id]);
1358
+                }
1359
+            }
1360
+        }
1361
+
1362
+        $best = [];
1363
+        $bestDepth = 0;
1364
+        foreach ($shares as $id => $share) {
1365
+            $depth = substr_count($share['file_target'], '/');
1366
+            if (empty($best) || $depth < $bestDepth) {
1367
+                $bestDepth = $depth;
1368
+                $best = [
1369
+                    'node_id' => $share['file_source'],
1370
+                    'node_path' => $share['file_target'],
1371
+                ];
1372
+            }
1373
+        }
1374
+
1375
+        return $best;
1376
+    }
1377
+
1378
+    /**
1379
+     * propagate notes to the recipients
1380
+     *
1381
+     * @param IShare $share
1382
+     * @throws \OCP\Files\NotFoundException
1383
+     */
1384
+    private function propagateNote(IShare $share) {
1385
+        if ($share->getShareType() === IShare::TYPE_USER) {
1386
+            $user = $this->userManager->get($share->getSharedWith());
1387
+            $this->sendNote([$user], $share);
1388
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1389
+            $group = $this->groupManager->get($share->getSharedWith());
1390
+            $groupMembers = $group->getUsers();
1391
+            $this->sendNote($groupMembers, $share);
1392
+        }
1393
+    }
1394
+
1395
+    /**
1396
+     * send note by mail
1397
+     *
1398
+     * @param array $recipients
1399
+     * @param IShare $share
1400
+     * @throws \OCP\Files\NotFoundException
1401
+     */
1402
+    private function sendNote(array $recipients, IShare $share) {
1403
+        $toListByLanguage = [];
1404
+
1405
+        foreach ($recipients as $recipient) {
1406
+            /** @var IUser $recipient */
1407
+            $email = $recipient->getEMailAddress();
1408
+            if ($email) {
1409
+                $language = $this->l10nFactory->getUserLanguage($recipient);
1410
+                if (!isset($toListByLanguage[$language])) {
1411
+                    $toListByLanguage[$language] = [];
1412
+                }
1413
+                $toListByLanguage[$language][$email] = $recipient->getDisplayName();
1414
+            }
1415
+        }
1416
+
1417
+        if (empty($toListByLanguage)) {
1418
+            return;
1419
+        }
1420
+
1421
+        foreach ($toListByLanguage as $l10n => $toList) {
1422
+            $filename = $share->getNode()->getName();
1423
+            $initiator = $share->getSharedBy();
1424
+            $note = $share->getNote();
1425
+
1426
+            $l = $this->l10nFactory->get('lib', $l10n);
1427
+
1428
+            $initiatorUser = $this->userManager->get($initiator);
1429
+            $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1430
+            $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1431
+            $plainHeading = $l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
1432
+            $htmlHeading = $l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
1433
+            $message = $this->mailer->createMessage();
1434
+
1435
+            $emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1436
+
1437
+            $emailTemplate->setSubject($l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
1438
+            $emailTemplate->addHeader();
1439
+            $emailTemplate->addHeading($htmlHeading, $plainHeading);
1440
+            $emailTemplate->addBodyText(htmlspecialchars($note), $note);
1441
+
1442
+            $link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1443
+            $emailTemplate->addBodyButton(
1444
+                $l->t('Open »%s«', [$filename]),
1445
+                $link
1446
+            );
1447
+
1448
+
1449
+            // The "From" contains the sharers name
1450
+            $instanceName = $this->defaults->getName();
1451
+            $senderName = $l->t(
1452
+                '%1$s via %2$s',
1453
+                [
1454
+                    $initiatorDisplayName,
1455
+                    $instanceName
1456
+                ]
1457
+            );
1458
+            $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1459
+            if ($initiatorEmailAddress !== null) {
1460
+                $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1461
+                $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1462
+            } else {
1463
+                $emailTemplate->addFooter();
1464
+            }
1465
+
1466
+            if (count($toList) === 1) {
1467
+                $message->setTo($toList);
1468
+            } else {
1469
+                $message->setTo([]);
1470
+                $message->setBcc($toList);
1471
+            }
1472
+            $message->useTemplate($emailTemplate);
1473
+            $this->mailer->send($message);
1474
+        }
1475
+    }
1476
+
1477
+    public function getAllShares(): iterable {
1478
+        $qb = $this->dbConn->getQueryBuilder();
1479
+
1480
+        $qb->select('*')
1481
+            ->from('share')
1482
+            ->where(
1483
+                $qb->expr()->orX(
1484
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)),
1485
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)),
1486
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK))
1487
+                )
1488
+            );
1489
+
1490
+        $cursor = $qb->execute();
1491
+        while ($data = $cursor->fetch()) {
1492
+            try {
1493
+                $share = $this->createShare($data);
1494
+            } catch (InvalidShare $e) {
1495
+                continue;
1496
+            }
1497
+
1498
+            yield $share;
1499
+        }
1500
+        $cursor->closeCursor();
1501
+    }
1502 1502
 }
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -339,7 +339,7 @@  discard block
 block discarded – undo
339 339
 			$user = $this->userManager->get($recipient);
340 340
 
341 341
 			if (is_null($group)) {
342
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
342
+				throw new ProviderException('Group "'.$share->getSharedWith().'" does not exist');
343 343
 			}
344 344
 
345 345
 			if (!$group->inGroup($user)) {
@@ -465,7 +465,7 @@  discard block
 block discarded – undo
465 465
 			$user = $this->userManager->get($recipient);
466 466
 
467 467
 			if (is_null($group)) {
468
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
468
+				throw new ProviderException('Group "'.$share->getSharedWith().'" does not exist');
469 469
 			}
470 470
 
471 471
 			if (!$group->inGroup($user)) {
@@ -665,7 +665,7 @@  discard block
 block discarded – undo
665 665
 			);
666 666
 		}
667 667
 
668
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
668
+		$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
669 669
 		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
670 670
 
671 671
 		$qb->orderBy('id');
@@ -1005,17 +1005,17 @@  discard block
 block discarded – undo
1005 1005
 	 */
1006 1006
 	private function createShare($data) {
1007 1007
 		$share = new Share($this->rootFolder, $this->userManager);
1008
-		$share->setId((int)$data['id'])
1009
-			->setShareType((int)$data['share_type'])
1010
-			->setPermissions((int)$data['permissions'])
1008
+		$share->setId((int) $data['id'])
1009
+			->setShareType((int) $data['share_type'])
1010
+			->setPermissions((int) $data['permissions'])
1011 1011
 			->setTarget($data['file_target'])
1012
-			->setNote((string)$data['note'])
1013
-			->setMailSend((bool)$data['mail_send'])
1014
-			->setStatus((int)$data['accepted'])
1012
+			->setNote((string) $data['note'])
1013
+			->setMailSend((bool) $data['mail_send'])
1014
+			->setStatus((int) $data['accepted'])
1015 1015
 			->setLabel($data['label']);
1016 1016
 
1017 1017
 		$shareTime = new \DateTime();
1018
-		$shareTime->setTimestamp((int)$data['stime']);
1018
+		$shareTime->setTimestamp((int) $data['stime']);
1019 1019
 		$share->setShareTime($shareTime);
1020 1020
 
1021 1021
 		if ($share->getShareType() === IShare::TYPE_USER) {
@@ -1028,14 +1028,14 @@  discard block
 block discarded – undo
1028 1028
 			$share->setSharedWith($data['share_with']);
1029 1029
 		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
1030 1030
 			$share->setPassword($data['password']);
1031
-			$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1031
+			$share->setSendPasswordByTalk((bool) $data['password_by_talk']);
1032 1032
 			$share->setToken($data['token']);
1033 1033
 		}
1034 1034
 
1035 1035
 		$share->setSharedBy($data['uid_initiator']);
1036 1036
 		$share->setShareOwner($data['uid_owner']);
1037 1037
 
1038
-		$share->setNodeId((int)$data['file_source']);
1038
+		$share->setNodeId((int) $data['file_source']);
1039 1039
 		$share->setNodeType($data['item_type']);
1040 1040
 
1041 1041
 		if ($data['expiration'] !== null) {
@@ -1052,7 +1052,7 @@  discard block
 block discarded – undo
1052 1052
 		}
1053 1053
 
1054 1054
 		$share->setProviderId($this->identifier());
1055
-		$share->setHideDownload((int)$data['hide_download'] === 1);
1055
+		$share->setHideDownload((int) $data['hide_download'] === 1);
1056 1056
 
1057 1057
 		return $share;
1058 1058
 	}
@@ -1081,7 +1081,7 @@  discard block
 block discarded – undo
1081 1081
 			$shareMap = [];
1082 1082
 
1083 1083
 			foreach ($shareSlice as $share) {
1084
-				$ids[] = (int)$share->getId();
1084
+				$ids[] = (int) $share->getId();
1085 1085
 				$shareMap[$share->getId()] = $share;
1086 1086
 			}
1087 1087
 
@@ -1099,8 +1099,8 @@  discard block
 block discarded – undo
1099 1099
 			$stmt = $query->execute();
1100 1100
 
1101 1101
 			while ($data = $stmt->fetch()) {
1102
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1103
-				$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1102
+				$shareMap[$data['parent']]->setPermissions((int) $data['permissions']);
1103
+				$shareMap[$data['parent']]->setStatus((int) $data['accepted']);
1104 1104
 				$shareMap[$data['parent']]->setTarget($data['file_target']);
1105 1105
 				$shareMap[$data['parent']]->setParent($data['parent']);
1106 1106
 			}
@@ -1176,7 +1176,7 @@  discard block
 block discarded – undo
1176 1176
 				)
1177 1177
 			);
1178 1178
 		} else {
1179
-			\OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
1179
+			\OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: '.$shareType));
1180 1180
 			return;
1181 1181
 		}
1182 1182
 
@@ -1202,7 +1202,7 @@  discard block
 block discarded – undo
1202 1202
 		$cursor = $qb->execute();
1203 1203
 		$ids = [];
1204 1204
 		while ($row = $cursor->fetch()) {
1205
-			$ids[] = (int)$row['id'];
1205
+			$ids[] = (int) $row['id'];
1206 1206
 		}
1207 1207
 		$cursor->closeCursor();
1208 1208
 
@@ -1245,7 +1245,7 @@  discard block
 block discarded – undo
1245 1245
 		$cursor = $qb->execute();
1246 1246
 		$ids = [];
1247 1247
 		while ($row = $cursor->fetch()) {
1248
-			$ids[] = (int)$row['id'];
1248
+			$ids[] = (int) $row['id'];
1249 1249
 		}
1250 1250
 		$cursor->closeCursor();
1251 1251
 
@@ -1300,7 +1300,7 @@  discard block
 block discarded – undo
1300 1300
 		$users = [];
1301 1301
 		$link = false;
1302 1302
 		while ($row = $cursor->fetch()) {
1303
-			$type = (int)$row['share_type'];
1303
+			$type = (int) $row['share_type'];
1304 1304
 			if ($type === IShare::TYPE_USER) {
1305 1305
 				$uid = $row['share_with'];
1306 1306
 				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
@@ -1458,7 +1458,7 @@  discard block
 block discarded – undo
1458 1458
 			$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1459 1459
 			if ($initiatorEmailAddress !== null) {
1460 1460
 				$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1461
-				$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1461
+				$emailTemplate->addFooter($instanceName.' - '.$this->defaults->getSlogan());
1462 1462
 			} else {
1463 1463
 				$emailTemplate->addFooter();
1464 1464
 			}
Please login to merge, or discard this patch.
lib/private/Files/Cache/Cache.php 1 patch
Indentation   +985 added lines, -985 removed lines patch added patch discarded remove patch
@@ -62,989 +62,989 @@
 block discarded – undo
62 62
  * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater
63 63
  */
64 64
 class Cache implements ICache {
65
-	use MoveFromCacheTrait {
66
-		MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
67
-	}
68
-
69
-	/**
70
-	 * @var array partial data for the cache
71
-	 */
72
-	protected $partial = [];
73
-
74
-	/**
75
-	 * @var string
76
-	 */
77
-	protected $storageId;
78
-
79
-	private $storage;
80
-
81
-	/**
82
-	 * @var Storage $storageCache
83
-	 */
84
-	protected $storageCache;
85
-
86
-	/** @var IMimeTypeLoader */
87
-	protected $mimetypeLoader;
88
-
89
-	/**
90
-	 * @var IDBConnection
91
-	 */
92
-	protected $connection;
93
-
94
-	protected $eventDispatcher;
95
-
96
-	/** @var QuerySearchHelper */
97
-	protected $querySearchHelper;
98
-
99
-	/**
100
-	 * @param IStorage $storage
101
-	 */
102
-	public function __construct(IStorage $storage) {
103
-		$this->storageId = $storage->getId();
104
-		$this->storage = $storage;
105
-		if (strlen($this->storageId) > 64) {
106
-			$this->storageId = md5($this->storageId);
107
-		}
108
-
109
-		$this->storageCache = new Storage($storage);
110
-		$this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
111
-		$this->connection = \OC::$server->getDatabaseConnection();
112
-		$this->eventDispatcher = \OC::$server->getEventDispatcher();
113
-		$this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
114
-	}
115
-
116
-	private function getQueryBuilder() {
117
-		return new CacheQueryBuilder(
118
-			$this->connection,
119
-			\OC::$server->getSystemConfig(),
120
-			\OC::$server->getLogger(),
121
-			$this
122
-		);
123
-	}
124
-
125
-	/**
126
-	 * Get the numeric storage id for this cache's storage
127
-	 *
128
-	 * @return int
129
-	 */
130
-	public function getNumericStorageId() {
131
-		return $this->storageCache->getNumericId();
132
-	}
133
-
134
-	/**
135
-	 * get the stored metadata of a file or folder
136
-	 *
137
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
138
-	 * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
139
-	 */
140
-	public function get($file) {
141
-		$query = $this->getQueryBuilder();
142
-		$query->selectFileCache();
143
-
144
-		if (is_string($file) or $file == '') {
145
-			// normalize file
146
-			$file = $this->normalize($file);
147
-
148
-			$query->whereStorageId()
149
-				->wherePath($file);
150
-		} else { //file id
151
-			$query->whereFileId($file);
152
-		}
153
-
154
-		$result = $query->execute();
155
-		$data = $result->fetch();
156
-		$result->closeCursor();
157
-
158
-		//merge partial data
159
-		if (!$data and is_string($file) and isset($this->partial[$file])) {
160
-			return $this->partial[$file];
161
-		} elseif (!$data) {
162
-			return $data;
163
-		} else {
164
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
165
-		}
166
-	}
167
-
168
-	/**
169
-	 * Create a CacheEntry from database row
170
-	 *
171
-	 * @param array $data
172
-	 * @param IMimeTypeLoader $mimetypeLoader
173
-	 * @return CacheEntry
174
-	 */
175
-	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
176
-		//fix types
177
-		$data['fileid'] = (int)$data['fileid'];
178
-		$data['parent'] = (int)$data['parent'];
179
-		$data['size'] = 0 + $data['size'];
180
-		$data['mtime'] = (int)$data['mtime'];
181
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
182
-		$data['encryptedVersion'] = (int)$data['encrypted'];
183
-		$data['encrypted'] = (bool)$data['encrypted'];
184
-		$data['storage_id'] = $data['storage'];
185
-		$data['storage'] = (int)$data['storage'];
186
-		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
187
-		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
188
-		if ($data['storage_mtime'] == 0) {
189
-			$data['storage_mtime'] = $data['mtime'];
190
-		}
191
-		$data['permissions'] = (int)$data['permissions'];
192
-		if (isset($data['creation_time'])) {
193
-			$data['creation_time'] = (int) $data['creation_time'];
194
-		}
195
-		if (isset($data['upload_time'])) {
196
-			$data['upload_time'] = (int) $data['upload_time'];
197
-		}
198
-		return new CacheEntry($data);
199
-	}
200
-
201
-	/**
202
-	 * get the metadata of all files stored in $folder
203
-	 *
204
-	 * @param string $folder
205
-	 * @return ICacheEntry[]
206
-	 */
207
-	public function getFolderContents($folder) {
208
-		$fileId = $this->getId($folder);
209
-		return $this->getFolderContentsById($fileId);
210
-	}
211
-
212
-	/**
213
-	 * get the metadata of all files stored in $folder
214
-	 *
215
-	 * @param int $fileId the file id of the folder
216
-	 * @return ICacheEntry[]
217
-	 */
218
-	public function getFolderContentsById($fileId) {
219
-		if ($fileId > -1) {
220
-			$query = $this->getQueryBuilder();
221
-			$query->selectFileCache()
222
-				->whereParent($fileId)
223
-				->orderBy('name', 'ASC');
224
-
225
-			$result = $query->execute();
226
-			$files = $result->fetchAll();
227
-			$result->closeCursor();
228
-
229
-			return array_map(function (array $data) {
230
-				return self::cacheEntryFromData($data, $this->mimetypeLoader);
231
-			}, $files);
232
-		}
233
-		return [];
234
-	}
235
-
236
-	/**
237
-	 * insert or update meta data for a file or folder
238
-	 *
239
-	 * @param string $file
240
-	 * @param array $data
241
-	 *
242
-	 * @return int file id
243
-	 * @throws \RuntimeException
244
-	 */
245
-	public function put($file, array $data) {
246
-		if (($id = $this->getId($file)) > -1) {
247
-			$this->update($id, $data);
248
-			return $id;
249
-		} else {
250
-			return $this->insert($file, $data);
251
-		}
252
-	}
253
-
254
-	/**
255
-	 * insert meta data for a new file or folder
256
-	 *
257
-	 * @param string $file
258
-	 * @param array $data
259
-	 *
260
-	 * @return int file id
261
-	 * @throws \RuntimeException
262
-	 */
263
-	public function insert($file, array $data) {
264
-		// normalize file
265
-		$file = $this->normalize($file);
266
-
267
-		if (isset($this->partial[$file])) { //add any saved partial data
268
-			$data = array_merge($this->partial[$file], $data);
269
-			unset($this->partial[$file]);
270
-		}
271
-
272
-		$requiredFields = ['size', 'mtime', 'mimetype'];
273
-		foreach ($requiredFields as $field) {
274
-			if (!isset($data[$field])) { //data not complete save as partial and return
275
-				$this->partial[$file] = $data;
276
-				return -1;
277
-			}
278
-		}
279
-
280
-		$data['path'] = $file;
281
-		if (!isset($data['parent'])) {
282
-			$data['parent'] = $this->getParentId($file);
283
-		}
284
-		$data['name'] = basename($file);
285
-
286
-		[$values, $extensionValues] = $this->normalizeData($data);
287
-		$values['storage'] = $this->getNumericStorageId();
288
-
289
-		try {
290
-			$builder = $this->connection->getQueryBuilder();
291
-			$builder->insert('filecache');
292
-
293
-			foreach ($values as $column => $value) {
294
-				$builder->setValue($column, $builder->createNamedParameter($value));
295
-			}
296
-
297
-			if ($builder->execute()) {
298
-				$fileId = $builder->getLastInsertId();
299
-
300
-				if (count($extensionValues)) {
301
-					$query = $this->getQueryBuilder();
302
-					$query->insert('filecache_extended');
303
-
304
-					$query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
305
-					foreach ($extensionValues as $column => $value) {
306
-						$query->setValue($column, $query->createNamedParameter($value));
307
-					}
308
-					$query->execute();
309
-				}
310
-
311
-				$this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId));
312
-				return $fileId;
313
-			}
314
-		} catch (UniqueConstraintViolationException $e) {
315
-			// entry exists already
316
-			if ($this->connection->inTransaction()) {
317
-				$this->connection->commit();
318
-				$this->connection->beginTransaction();
319
-			}
320
-		}
321
-
322
-		// The file was created in the mean time
323
-		if (($id = $this->getId($file)) > -1) {
324
-			$this->update($id, $data);
325
-			return $id;
326
-		} else {
327
-			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.');
328
-		}
329
-	}
330
-
331
-	/**
332
-	 * update the metadata of an existing file or folder in the cache
333
-	 *
334
-	 * @param int $id the fileid of the existing file or folder
335
-	 * @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
336
-	 */
337
-	public function update($id, array $data) {
338
-		if (isset($data['path'])) {
339
-			// normalize path
340
-			$data['path'] = $this->normalize($data['path']);
341
-		}
342
-
343
-		if (isset($data['name'])) {
344
-			// normalize path
345
-			$data['name'] = $this->normalize($data['name']);
346
-		}
347
-
348
-		[$values, $extensionValues] = $this->normalizeData($data);
349
-
350
-		if (count($values)) {
351
-			$query = $this->getQueryBuilder();
352
-
353
-			$query->update('filecache')
354
-				->whereFileId($id)
355
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
356
-					return $query->expr()->orX(
357
-						$query->expr()->neq($key, $query->createNamedParameter($value)),
358
-						$query->expr()->isNull($key)
359
-					);
360
-				}, array_keys($values), array_values($values))));
361
-
362
-			foreach ($values as $key => $value) {
363
-				$query->set($key, $query->createNamedParameter($value));
364
-			}
365
-
366
-			$query->execute();
367
-		}
368
-
369
-		if (count($extensionValues)) {
370
-			try {
371
-				$query = $this->getQueryBuilder();
372
-				$query->insert('filecache_extended');
373
-
374
-				$query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
375
-				foreach ($extensionValues as $column => $value) {
376
-					$query->setValue($column, $query->createNamedParameter($value));
377
-				}
378
-
379
-				$query->execute();
380
-			} catch (UniqueConstraintViolationException $e) {
381
-				$query = $this->getQueryBuilder();
382
-				$query->update('filecache_extended')
383
-					->whereFileId($id)
384
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
385
-						return $query->expr()->orX(
386
-							$query->expr()->neq($key, $query->createNamedParameter($value)),
387
-							$query->expr()->isNull($key)
388
-						);
389
-					}, array_keys($extensionValues), array_values($extensionValues))));
390
-
391
-				foreach ($extensionValues as $key => $value) {
392
-					$query->set($key, $query->createNamedParameter($value));
393
-				}
394
-
395
-				$query->execute();
396
-			}
397
-		}
398
-
399
-		$path = $this->getPathById($id);
400
-		// path can still be null if the file doesn't exist
401
-		if ($path !== null) {
402
-			$this->eventDispatcher->dispatch(CacheUpdateEvent::class, new CacheUpdateEvent($this->storage, $path, $id));
403
-		}
404
-	}
405
-
406
-	/**
407
-	 * extract query parts and params array from data array
408
-	 *
409
-	 * @param array $data
410
-	 * @return array
411
-	 */
412
-	protected function normalizeData(array $data): array {
413
-		$fields = [
414
-			'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
415
-			'etag', 'permissions', 'checksum', 'storage'];
416
-		$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
417
-
418
-		$doNotCopyStorageMTime = false;
419
-		if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
420
-			// this horrific magic tells it to not copy storage_mtime to mtime
421
-			unset($data['mtime']);
422
-			$doNotCopyStorageMTime = true;
423
-		}
424
-
425
-		$params = [];
426
-		$extensionParams = [];
427
-		foreach ($data as $name => $value) {
428
-			if (array_search($name, $fields) !== false) {
429
-				if ($name === 'path') {
430
-					$params['path_hash'] = md5($value);
431
-				} elseif ($name === 'mimetype') {
432
-					$params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
433
-					$value = $this->mimetypeLoader->getId($value);
434
-				} elseif ($name === 'storage_mtime') {
435
-					if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
436
-						$params['mtime'] = $value;
437
-					}
438
-				} elseif ($name === 'encrypted') {
439
-					if (isset($data['encryptedVersion'])) {
440
-						$value = $data['encryptedVersion'];
441
-					} else {
442
-						// Boolean to integer conversion
443
-						$value = $value ? 1 : 0;
444
-					}
445
-				}
446
-				$params[$name] = $value;
447
-			}
448
-			if (array_search($name, $extensionFields) !== false) {
449
-				$extensionParams[$name] = $value;
450
-			}
451
-		}
452
-		return [$params, array_filter($extensionParams)];
453
-	}
454
-
455
-	/**
456
-	 * get the file id for a file
457
-	 *
458
-	 * 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
459
-	 *
460
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
461
-	 *
462
-	 * @param string $file
463
-	 * @return int
464
-	 */
465
-	public function getId($file) {
466
-		// normalize file
467
-		$file = $this->normalize($file);
468
-
469
-		$query = $this->getQueryBuilder();
470
-		$query->select('fileid')
471
-			->from('filecache')
472
-			->whereStorageId()
473
-			->wherePath($file);
474
-
475
-		$result = $query->execute();
476
-		$id = $result->fetchColumn();
477
-		$result->closeCursor();
478
-
479
-		return $id === false ? -1 : (int)$id;
480
-	}
481
-
482
-	/**
483
-	 * get the id of the parent folder of a file
484
-	 *
485
-	 * @param string $file
486
-	 * @return int
487
-	 */
488
-	public function getParentId($file) {
489
-		if ($file === '') {
490
-			return -1;
491
-		} else {
492
-			$parent = $this->getParentPath($file);
493
-			return (int)$this->getId($parent);
494
-		}
495
-	}
496
-
497
-	private function getParentPath($path) {
498
-		$parent = dirname($path);
499
-		if ($parent === '.') {
500
-			$parent = '';
501
-		}
502
-		return $parent;
503
-	}
504
-
505
-	/**
506
-	 * check if a file is available in the cache
507
-	 *
508
-	 * @param string $file
509
-	 * @return bool
510
-	 */
511
-	public function inCache($file) {
512
-		return $this->getId($file) != -1;
513
-	}
514
-
515
-	/**
516
-	 * remove a file or folder from the cache
517
-	 *
518
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
519
-	 *
520
-	 * @param string $file
521
-	 */
522
-	public function remove($file) {
523
-		$entry = $this->get($file);
524
-
525
-		if ($entry) {
526
-			$query = $this->getQueryBuilder();
527
-			$query->delete('filecache')
528
-				->whereFileId($entry->getId());
529
-			$query->execute();
530
-
531
-			$query = $this->getQueryBuilder();
532
-			$query->delete('filecache_extended')
533
-				->whereFileId($entry->getId());
534
-			$query->execute();
535
-
536
-			if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
537
-				$this->removeChildren($entry);
538
-			}
539
-		}
540
-	}
541
-
542
-	/**
543
-	 * Get all sub folders of a folder
544
-	 *
545
-	 * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for
546
-	 * @return ICacheEntry[] the cache entries for the subfolders
547
-	 */
548
-	private function getSubFolders(ICacheEntry $entry) {
549
-		$children = $this->getFolderContentsById($entry->getId());
550
-		return array_filter($children, function ($child) {
551
-			return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
552
-		});
553
-	}
554
-
555
-	/**
556
-	 * Recursively remove all children of a folder
557
-	 *
558
-	 * @param ICacheEntry $entry the cache entry of the folder to remove the children of
559
-	 * @throws \OC\DatabaseException
560
-	 */
561
-	private function removeChildren(ICacheEntry $entry) {
562
-		$parentIds = [$entry->getId()];
563
-		$queue = [$entry->getId()];
564
-
565
-		// we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
566
-		// and collecting all folder ids to later use to delete the filecache entries
567
-		while ($entryId = array_pop($queue)) {
568
-			$children = $this->getFolderContentsById($entryId);
569
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
570
-				return $cacheEntry->getId();
571
-			}, $children);
572
-
573
-			$query = $this->getQueryBuilder();
574
-			$query->delete('filecache_extended')
575
-				->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY)));
576
-			$query->execute();
577
-
578
-			/** @var ICacheEntry[] $childFolders */
579
-			$childFolders = array_filter($children, function ($child) {
580
-				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
581
-			});
582
-			foreach ($childFolders as $folder) {
583
-				$parentIds[] = $folder->getId();
584
-				$queue[] = $folder->getId();
585
-			}
586
-		}
587
-
588
-		$query = $this->getQueryBuilder();
589
-		$query->delete('filecache')
590
-			->whereParentIn($parentIds);
591
-		$query->execute();
592
-	}
593
-
594
-	/**
595
-	 * Move a file or folder in the cache
596
-	 *
597
-	 * @param string $source
598
-	 * @param string $target
599
-	 */
600
-	public function move($source, $target) {
601
-		$this->moveFromCache($this, $source, $target);
602
-	}
603
-
604
-	/**
605
-	 * Get the storage id and path needed for a move
606
-	 *
607
-	 * @param string $path
608
-	 * @return array [$storageId, $internalPath]
609
-	 */
610
-	protected function getMoveInfo($path) {
611
-		return [$this->getNumericStorageId(), $path];
612
-	}
613
-
614
-	/**
615
-	 * Move a file or folder in the cache
616
-	 *
617
-	 * @param \OCP\Files\Cache\ICache $sourceCache
618
-	 * @param string $sourcePath
619
-	 * @param string $targetPath
620
-	 * @throws \OC\DatabaseException
621
-	 * @throws \Exception if the given storages have an invalid id
622
-	 */
623
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
624
-		if ($sourceCache instanceof Cache) {
625
-			// normalize source and target
626
-			$sourcePath = $this->normalize($sourcePath);
627
-			$targetPath = $this->normalize($targetPath);
628
-
629
-			$sourceData = $sourceCache->get($sourcePath);
630
-			$sourceId = $sourceData['fileid'];
631
-			$newParentId = $this->getParentId($targetPath);
632
-
633
-			[$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
634
-			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
635
-
636
-			if (is_null($sourceStorageId) || $sourceStorageId === false) {
637
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
638
-			}
639
-			if (is_null($targetStorageId) || $targetStorageId === false) {
640
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
641
-			}
642
-
643
-			$this->connection->beginTransaction();
644
-			if ($sourceData['mimetype'] === 'httpd/unix-directory') {
645
-				//update all child entries
646
-				$sourceLength = mb_strlen($sourcePath);
647
-				$query = $this->connection->getQueryBuilder();
648
-
649
-				$fun = $query->func();
650
-				$newPathFunction = $fun->concat(
651
-					$query->createNamedParameter($targetPath),
652
-					$fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
653
-				);
654
-				$query->update('filecache')
655
-					->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
656
-					->set('path_hash', $fun->md5($newPathFunction))
657
-					->set('path', $newPathFunction)
658
-					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
659
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
660
-
661
-				try {
662
-					$query->execute();
663
-				} catch (\OC\DatabaseException $e) {
664
-					$this->connection->rollBack();
665
-					throw $e;
666
-				}
667
-			}
668
-
669
-			$query = $this->getQueryBuilder();
670
-			$query->update('filecache')
671
-				->set('storage', $query->createNamedParameter($targetStorageId))
672
-				->set('path', $query->createNamedParameter($targetPath))
673
-				->set('path_hash', $query->createNamedParameter(md5($targetPath)))
674
-				->set('name', $query->createNamedParameter(basename($targetPath)))
675
-				->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
676
-				->whereFileId($sourceId);
677
-			$query->execute();
678
-
679
-			$this->connection->commit();
680
-		} else {
681
-			$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
682
-		}
683
-	}
684
-
685
-	/**
686
-	 * remove all entries for files that are stored on the storage from the cache
687
-	 */
688
-	public function clear() {
689
-		$query = $this->getQueryBuilder();
690
-		$query->delete('filecache')
691
-			->whereStorageId();
692
-		$query->execute();
693
-
694
-		$query = $this->connection->getQueryBuilder();
695
-		$query->delete('storages')
696
-			->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
697
-		$query->execute();
698
-	}
699
-
700
-	/**
701
-	 * Get the scan status of a file
702
-	 *
703
-	 * - Cache::NOT_FOUND: File is not in the cache
704
-	 * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
705
-	 * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
706
-	 * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
707
-	 *
708
-	 * @param string $file
709
-	 *
710
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
711
-	 */
712
-	public function getStatus($file) {
713
-		// normalize file
714
-		$file = $this->normalize($file);
715
-
716
-		$query = $this->getQueryBuilder();
717
-		$query->select('size')
718
-			->from('filecache')
719
-			->whereStorageId()
720
-			->wherePath($file);
721
-
722
-		$result = $query->execute();
723
-		$size = $result->fetchColumn();
724
-		$result->closeCursor();
725
-
726
-		if ($size !== false) {
727
-			if ((int)$size === -1) {
728
-				return self::SHALLOW;
729
-			} else {
730
-				return self::COMPLETE;
731
-			}
732
-		} else {
733
-			if (isset($this->partial[$file])) {
734
-				return self::PARTIAL;
735
-			} else {
736
-				return self::NOT_FOUND;
737
-			}
738
-		}
739
-	}
740
-
741
-	/**
742
-	 * search for files matching $pattern
743
-	 *
744
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
745
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
746
-	 */
747
-	public function search($pattern) {
748
-		// normalize pattern
749
-		$pattern = $this->normalize($pattern);
750
-
751
-		if ($pattern === '%%') {
752
-			return [];
753
-		}
754
-
755
-		$query = $this->getQueryBuilder();
756
-		$query->selectFileCache()
757
-			->whereStorageId()
758
-			->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
759
-
760
-		$result = $query->execute();
761
-		$files = $result->fetchAll();
762
-		$result->closeCursor();
763
-
764
-		return array_map(function (array $data) {
765
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
766
-		}, $files);
767
-	}
768
-
769
-	/**
770
-	 * @param Statement $result
771
-	 * @return CacheEntry[]
772
-	 */
773
-	private function searchResultToCacheEntries(Statement $result) {
774
-		$files = $result->fetchAll();
775
-
776
-		return array_map(function (array $data) {
777
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
778
-		}, $files);
779
-	}
780
-
781
-	/**
782
-	 * search for files by mimetype
783
-	 *
784
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
785
-	 *        where it will search for all mimetypes in the group ('image/*')
786
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
787
-	 */
788
-	public function searchByMime($mimetype) {
789
-		$mimeId = $this->mimetypeLoader->getId($mimetype);
790
-
791
-		$query = $this->getQueryBuilder();
792
-		$query->selectFileCache()
793
-			->whereStorageId();
794
-
795
-		if (strpos($mimetype, '/')) {
796
-			$query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
797
-		} else {
798
-			$query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
799
-		}
800
-
801
-		$result = $query->execute();
802
-		$files = $result->fetchAll();
803
-		$result->closeCursor();
804
-
805
-		return array_map(function (array $data) {
806
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
807
-		}, $files);
808
-	}
809
-
810
-	public function searchQuery(ISearchQuery $searchQuery) {
811
-		$builder = $this->getQueryBuilder();
812
-
813
-		$query = $builder->selectFileCache('file');
814
-
815
-		$query->whereStorageId();
816
-
817
-		if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
818
-			$query
819
-				->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
820
-				->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
821
-					$builder->expr()->eq('tagmap.type', 'tag.type'),
822
-					$builder->expr()->eq('tagmap.categoryid', 'tag.id')
823
-				))
824
-				->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
825
-				->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
826
-		}
827
-
828
-		$searchExpr = $this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
829
-		if ($searchExpr) {
830
-			$query->andWhere($searchExpr);
831
-		}
832
-
833
-		if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
834
-			$query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
835
-		}
836
-
837
-		$this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
838
-
839
-		if ($searchQuery->getLimit()) {
840
-			$query->setMaxResults($searchQuery->getLimit());
841
-		}
842
-		if ($searchQuery->getOffset()) {
843
-			$query->setFirstResult($searchQuery->getOffset());
844
-		}
845
-
846
-		$result = $query->execute();
847
-		return $this->searchResultToCacheEntries($result);
848
-	}
849
-
850
-	/**
851
-	 * Re-calculate the folder size and the size of all parent folders
852
-	 *
853
-	 * @param string|boolean $path
854
-	 * @param array $data (optional) meta data of the folder
855
-	 */
856
-	public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
857
-		$this->calculateFolderSize($path, $data);
858
-		if ($path !== '') {
859
-			$parent = dirname($path);
860
-			if ($parent === '.' or $parent === '/') {
861
-				$parent = '';
862
-			}
863
-			if ($isBackgroundScan) {
864
-				$parentData = $this->get($parent);
865
-				if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
866
-					$this->correctFolderSize($parent, $parentData, $isBackgroundScan);
867
-				}
868
-			} else {
869
-				$this->correctFolderSize($parent);
870
-			}
871
-		}
872
-	}
873
-
874
-	/**
875
-	 * get the incomplete count that shares parent $folder
876
-	 *
877
-	 * @param int $fileId the file id of the folder
878
-	 * @return int
879
-	 */
880
-	public function getIncompleteChildrenCount($fileId) {
881
-		if ($fileId > -1) {
882
-			$query = $this->getQueryBuilder();
883
-			$query->select($query->func()->count())
884
-				->from('filecache')
885
-				->whereParent($fileId)
886
-				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
887
-
888
-			$result = $query->execute();
889
-			$size = (int)$result->fetchColumn();
890
-			$result->closeCursor();
891
-
892
-			return $size;
893
-		}
894
-		return -1;
895
-	}
896
-
897
-	/**
898
-	 * calculate the size of a folder and set it in the cache
899
-	 *
900
-	 * @param string $path
901
-	 * @param array $entry (optional) meta data of the folder
902
-	 * @return int
903
-	 */
904
-	public function calculateFolderSize($path, $entry = null) {
905
-		$totalSize = 0;
906
-		if (is_null($entry) or !isset($entry['fileid'])) {
907
-			$entry = $this->get($path);
908
-		}
909
-		if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
910
-			$id = $entry['fileid'];
911
-
912
-			$query = $this->getQueryBuilder();
913
-			$query->selectAlias($query->func()->sum('size'), 'f1')
914
-				->selectAlias($query->func()->min('size'), 'f2')
915
-				->from('filecache')
916
-				->whereStorageId()
917
-				->whereParent($id);
918
-
919
-			$result = $query->execute();
920
-			$row = $result->fetch();
921
-			$result->closeCursor();
922
-
923
-			if ($row) {
924
-				[$sum, $min] = array_values($row);
925
-				$sum = 0 + $sum;
926
-				$min = 0 + $min;
927
-				if ($min === -1) {
928
-					$totalSize = $min;
929
-				} else {
930
-					$totalSize = $sum;
931
-				}
932
-				if ($entry['size'] !== $totalSize) {
933
-					$this->update($id, ['size' => $totalSize]);
934
-				}
935
-			}
936
-		}
937
-		return $totalSize;
938
-	}
939
-
940
-	/**
941
-	 * get all file ids on the files on the storage
942
-	 *
943
-	 * @return int[]
944
-	 */
945
-	public function getAll() {
946
-		$query = $this->getQueryBuilder();
947
-		$query->select('fileid')
948
-			->from('filecache')
949
-			->whereStorageId();
950
-
951
-		$result = $query->execute();
952
-		$files = $result->fetchAll(\PDO::FETCH_COLUMN);
953
-		$result->closeCursor();
954
-
955
-		return array_map(function ($id) {
956
-			return (int)$id;
957
-		}, $files);
958
-	}
959
-
960
-	/**
961
-	 * find a folder in the cache which has not been fully scanned
962
-	 *
963
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
964
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
965
-	 * likely the folder where we stopped scanning previously
966
-	 *
967
-	 * @return string|bool the path of the folder or false when no folder matched
968
-	 */
969
-	public function getIncomplete() {
970
-		$query = $this->getQueryBuilder();
971
-		$query->select('path')
972
-			->from('filecache')
973
-			->whereStorageId()
974
-			->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
975
-			->orderBy('fileid', 'DESC');
976
-
977
-		$result = $query->execute();
978
-		$path = $result->fetchColumn();
979
-		$result->closeCursor();
980
-
981
-		return $path;
982
-	}
983
-
984
-	/**
985
-	 * get the path of a file on this storage by it's file id
986
-	 *
987
-	 * @param int $id the file id of the file or folder to search
988
-	 * @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
989
-	 */
990
-	public function getPathById($id) {
991
-		$query = $this->getQueryBuilder();
992
-		$query->select('path')
993
-			->from('filecache')
994
-			->whereStorageId()
995
-			->whereFileId($id);
996
-
997
-		$result = $query->execute();
998
-		$path = $result->fetchColumn();
999
-		$result->closeCursor();
1000
-
1001
-		if ($path === false) {
1002
-			return null;
1003
-		}
1004
-
1005
-		return (string) $path;
1006
-	}
1007
-
1008
-	/**
1009
-	 * get the storage id of the storage for a file and the internal path of the file
1010
-	 * unlike getPathById this does not limit the search to files on this storage and
1011
-	 * instead does a global search in the cache table
1012
-	 *
1013
-	 * @param int $id
1014
-	 * @return array first element holding the storage id, second the path
1015
-	 * @deprecated use getPathById() instead
1016
-	 */
1017
-	public static function getById($id) {
1018
-		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
1019
-		$query->select('path', 'storage')
1020
-			->from('filecache')
1021
-			->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
1022
-
1023
-		$result = $query->execute();
1024
-		$row = $result->fetch();
1025
-		$result->closeCursor();
1026
-
1027
-		if ($row) {
1028
-			$numericId = $row['storage'];
1029
-			$path = $row['path'];
1030
-		} else {
1031
-			return null;
1032
-		}
1033
-
1034
-		if ($id = Storage::getStorageId($numericId)) {
1035
-			return [$id, $path];
1036
-		} else {
1037
-			return null;
1038
-		}
1039
-	}
1040
-
1041
-	/**
1042
-	 * normalize the given path
1043
-	 *
1044
-	 * @param string $path
1045
-	 * @return string
1046
-	 */
1047
-	public function normalize($path) {
1048
-		return trim(\OC_Util::normalizeUnicode($path), '/');
1049
-	}
65
+    use MoveFromCacheTrait {
66
+        MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
67
+    }
68
+
69
+    /**
70
+     * @var array partial data for the cache
71
+     */
72
+    protected $partial = [];
73
+
74
+    /**
75
+     * @var string
76
+     */
77
+    protected $storageId;
78
+
79
+    private $storage;
80
+
81
+    /**
82
+     * @var Storage $storageCache
83
+     */
84
+    protected $storageCache;
85
+
86
+    /** @var IMimeTypeLoader */
87
+    protected $mimetypeLoader;
88
+
89
+    /**
90
+     * @var IDBConnection
91
+     */
92
+    protected $connection;
93
+
94
+    protected $eventDispatcher;
95
+
96
+    /** @var QuerySearchHelper */
97
+    protected $querySearchHelper;
98
+
99
+    /**
100
+     * @param IStorage $storage
101
+     */
102
+    public function __construct(IStorage $storage) {
103
+        $this->storageId = $storage->getId();
104
+        $this->storage = $storage;
105
+        if (strlen($this->storageId) > 64) {
106
+            $this->storageId = md5($this->storageId);
107
+        }
108
+
109
+        $this->storageCache = new Storage($storage);
110
+        $this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
111
+        $this->connection = \OC::$server->getDatabaseConnection();
112
+        $this->eventDispatcher = \OC::$server->getEventDispatcher();
113
+        $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
114
+    }
115
+
116
+    private function getQueryBuilder() {
117
+        return new CacheQueryBuilder(
118
+            $this->connection,
119
+            \OC::$server->getSystemConfig(),
120
+            \OC::$server->getLogger(),
121
+            $this
122
+        );
123
+    }
124
+
125
+    /**
126
+     * Get the numeric storage id for this cache's storage
127
+     *
128
+     * @return int
129
+     */
130
+    public function getNumericStorageId() {
131
+        return $this->storageCache->getNumericId();
132
+    }
133
+
134
+    /**
135
+     * get the stored metadata of a file or folder
136
+     *
137
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
138
+     * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
139
+     */
140
+    public function get($file) {
141
+        $query = $this->getQueryBuilder();
142
+        $query->selectFileCache();
143
+
144
+        if (is_string($file) or $file == '') {
145
+            // normalize file
146
+            $file = $this->normalize($file);
147
+
148
+            $query->whereStorageId()
149
+                ->wherePath($file);
150
+        } else { //file id
151
+            $query->whereFileId($file);
152
+        }
153
+
154
+        $result = $query->execute();
155
+        $data = $result->fetch();
156
+        $result->closeCursor();
157
+
158
+        //merge partial data
159
+        if (!$data and is_string($file) and isset($this->partial[$file])) {
160
+            return $this->partial[$file];
161
+        } elseif (!$data) {
162
+            return $data;
163
+        } else {
164
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
165
+        }
166
+    }
167
+
168
+    /**
169
+     * Create a CacheEntry from database row
170
+     *
171
+     * @param array $data
172
+     * @param IMimeTypeLoader $mimetypeLoader
173
+     * @return CacheEntry
174
+     */
175
+    public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
176
+        //fix types
177
+        $data['fileid'] = (int)$data['fileid'];
178
+        $data['parent'] = (int)$data['parent'];
179
+        $data['size'] = 0 + $data['size'];
180
+        $data['mtime'] = (int)$data['mtime'];
181
+        $data['storage_mtime'] = (int)$data['storage_mtime'];
182
+        $data['encryptedVersion'] = (int)$data['encrypted'];
183
+        $data['encrypted'] = (bool)$data['encrypted'];
184
+        $data['storage_id'] = $data['storage'];
185
+        $data['storage'] = (int)$data['storage'];
186
+        $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
187
+        $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
188
+        if ($data['storage_mtime'] == 0) {
189
+            $data['storage_mtime'] = $data['mtime'];
190
+        }
191
+        $data['permissions'] = (int)$data['permissions'];
192
+        if (isset($data['creation_time'])) {
193
+            $data['creation_time'] = (int) $data['creation_time'];
194
+        }
195
+        if (isset($data['upload_time'])) {
196
+            $data['upload_time'] = (int) $data['upload_time'];
197
+        }
198
+        return new CacheEntry($data);
199
+    }
200
+
201
+    /**
202
+     * get the metadata of all files stored in $folder
203
+     *
204
+     * @param string $folder
205
+     * @return ICacheEntry[]
206
+     */
207
+    public function getFolderContents($folder) {
208
+        $fileId = $this->getId($folder);
209
+        return $this->getFolderContentsById($fileId);
210
+    }
211
+
212
+    /**
213
+     * get the metadata of all files stored in $folder
214
+     *
215
+     * @param int $fileId the file id of the folder
216
+     * @return ICacheEntry[]
217
+     */
218
+    public function getFolderContentsById($fileId) {
219
+        if ($fileId > -1) {
220
+            $query = $this->getQueryBuilder();
221
+            $query->selectFileCache()
222
+                ->whereParent($fileId)
223
+                ->orderBy('name', 'ASC');
224
+
225
+            $result = $query->execute();
226
+            $files = $result->fetchAll();
227
+            $result->closeCursor();
228
+
229
+            return array_map(function (array $data) {
230
+                return self::cacheEntryFromData($data, $this->mimetypeLoader);
231
+            }, $files);
232
+        }
233
+        return [];
234
+    }
235
+
236
+    /**
237
+     * insert or update meta data for a file or folder
238
+     *
239
+     * @param string $file
240
+     * @param array $data
241
+     *
242
+     * @return int file id
243
+     * @throws \RuntimeException
244
+     */
245
+    public function put($file, array $data) {
246
+        if (($id = $this->getId($file)) > -1) {
247
+            $this->update($id, $data);
248
+            return $id;
249
+        } else {
250
+            return $this->insert($file, $data);
251
+        }
252
+    }
253
+
254
+    /**
255
+     * insert meta data for a new file or folder
256
+     *
257
+     * @param string $file
258
+     * @param array $data
259
+     *
260
+     * @return int file id
261
+     * @throws \RuntimeException
262
+     */
263
+    public function insert($file, array $data) {
264
+        // normalize file
265
+        $file = $this->normalize($file);
266
+
267
+        if (isset($this->partial[$file])) { //add any saved partial data
268
+            $data = array_merge($this->partial[$file], $data);
269
+            unset($this->partial[$file]);
270
+        }
271
+
272
+        $requiredFields = ['size', 'mtime', 'mimetype'];
273
+        foreach ($requiredFields as $field) {
274
+            if (!isset($data[$field])) { //data not complete save as partial and return
275
+                $this->partial[$file] = $data;
276
+                return -1;
277
+            }
278
+        }
279
+
280
+        $data['path'] = $file;
281
+        if (!isset($data['parent'])) {
282
+            $data['parent'] = $this->getParentId($file);
283
+        }
284
+        $data['name'] = basename($file);
285
+
286
+        [$values, $extensionValues] = $this->normalizeData($data);
287
+        $values['storage'] = $this->getNumericStorageId();
288
+
289
+        try {
290
+            $builder = $this->connection->getQueryBuilder();
291
+            $builder->insert('filecache');
292
+
293
+            foreach ($values as $column => $value) {
294
+                $builder->setValue($column, $builder->createNamedParameter($value));
295
+            }
296
+
297
+            if ($builder->execute()) {
298
+                $fileId = $builder->getLastInsertId();
299
+
300
+                if (count($extensionValues)) {
301
+                    $query = $this->getQueryBuilder();
302
+                    $query->insert('filecache_extended');
303
+
304
+                    $query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
305
+                    foreach ($extensionValues as $column => $value) {
306
+                        $query->setValue($column, $query->createNamedParameter($value));
307
+                    }
308
+                    $query->execute();
309
+                }
310
+
311
+                $this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId));
312
+                return $fileId;
313
+            }
314
+        } catch (UniqueConstraintViolationException $e) {
315
+            // entry exists already
316
+            if ($this->connection->inTransaction()) {
317
+                $this->connection->commit();
318
+                $this->connection->beginTransaction();
319
+            }
320
+        }
321
+
322
+        // The file was created in the mean time
323
+        if (($id = $this->getId($file)) > -1) {
324
+            $this->update($id, $data);
325
+            return $id;
326
+        } else {
327
+            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.');
328
+        }
329
+    }
330
+
331
+    /**
332
+     * update the metadata of an existing file or folder in the cache
333
+     *
334
+     * @param int $id the fileid of the existing file or folder
335
+     * @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
336
+     */
337
+    public function update($id, array $data) {
338
+        if (isset($data['path'])) {
339
+            // normalize path
340
+            $data['path'] = $this->normalize($data['path']);
341
+        }
342
+
343
+        if (isset($data['name'])) {
344
+            // normalize path
345
+            $data['name'] = $this->normalize($data['name']);
346
+        }
347
+
348
+        [$values, $extensionValues] = $this->normalizeData($data);
349
+
350
+        if (count($values)) {
351
+            $query = $this->getQueryBuilder();
352
+
353
+            $query->update('filecache')
354
+                ->whereFileId($id)
355
+                ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
356
+                    return $query->expr()->orX(
357
+                        $query->expr()->neq($key, $query->createNamedParameter($value)),
358
+                        $query->expr()->isNull($key)
359
+                    );
360
+                }, array_keys($values), array_values($values))));
361
+
362
+            foreach ($values as $key => $value) {
363
+                $query->set($key, $query->createNamedParameter($value));
364
+            }
365
+
366
+            $query->execute();
367
+        }
368
+
369
+        if (count($extensionValues)) {
370
+            try {
371
+                $query = $this->getQueryBuilder();
372
+                $query->insert('filecache_extended');
373
+
374
+                $query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
375
+                foreach ($extensionValues as $column => $value) {
376
+                    $query->setValue($column, $query->createNamedParameter($value));
377
+                }
378
+
379
+                $query->execute();
380
+            } catch (UniqueConstraintViolationException $e) {
381
+                $query = $this->getQueryBuilder();
382
+                $query->update('filecache_extended')
383
+                    ->whereFileId($id)
384
+                    ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
385
+                        return $query->expr()->orX(
386
+                            $query->expr()->neq($key, $query->createNamedParameter($value)),
387
+                            $query->expr()->isNull($key)
388
+                        );
389
+                    }, array_keys($extensionValues), array_values($extensionValues))));
390
+
391
+                foreach ($extensionValues as $key => $value) {
392
+                    $query->set($key, $query->createNamedParameter($value));
393
+                }
394
+
395
+                $query->execute();
396
+            }
397
+        }
398
+
399
+        $path = $this->getPathById($id);
400
+        // path can still be null if the file doesn't exist
401
+        if ($path !== null) {
402
+            $this->eventDispatcher->dispatch(CacheUpdateEvent::class, new CacheUpdateEvent($this->storage, $path, $id));
403
+        }
404
+    }
405
+
406
+    /**
407
+     * extract query parts and params array from data array
408
+     *
409
+     * @param array $data
410
+     * @return array
411
+     */
412
+    protected function normalizeData(array $data): array {
413
+        $fields = [
414
+            'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
415
+            'etag', 'permissions', 'checksum', 'storage'];
416
+        $extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
417
+
418
+        $doNotCopyStorageMTime = false;
419
+        if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
420
+            // this horrific magic tells it to not copy storage_mtime to mtime
421
+            unset($data['mtime']);
422
+            $doNotCopyStorageMTime = true;
423
+        }
424
+
425
+        $params = [];
426
+        $extensionParams = [];
427
+        foreach ($data as $name => $value) {
428
+            if (array_search($name, $fields) !== false) {
429
+                if ($name === 'path') {
430
+                    $params['path_hash'] = md5($value);
431
+                } elseif ($name === 'mimetype') {
432
+                    $params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
433
+                    $value = $this->mimetypeLoader->getId($value);
434
+                } elseif ($name === 'storage_mtime') {
435
+                    if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
436
+                        $params['mtime'] = $value;
437
+                    }
438
+                } elseif ($name === 'encrypted') {
439
+                    if (isset($data['encryptedVersion'])) {
440
+                        $value = $data['encryptedVersion'];
441
+                    } else {
442
+                        // Boolean to integer conversion
443
+                        $value = $value ? 1 : 0;
444
+                    }
445
+                }
446
+                $params[$name] = $value;
447
+            }
448
+            if (array_search($name, $extensionFields) !== false) {
449
+                $extensionParams[$name] = $value;
450
+            }
451
+        }
452
+        return [$params, array_filter($extensionParams)];
453
+    }
454
+
455
+    /**
456
+     * get the file id for a file
457
+     *
458
+     * 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
459
+     *
460
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
461
+     *
462
+     * @param string $file
463
+     * @return int
464
+     */
465
+    public function getId($file) {
466
+        // normalize file
467
+        $file = $this->normalize($file);
468
+
469
+        $query = $this->getQueryBuilder();
470
+        $query->select('fileid')
471
+            ->from('filecache')
472
+            ->whereStorageId()
473
+            ->wherePath($file);
474
+
475
+        $result = $query->execute();
476
+        $id = $result->fetchColumn();
477
+        $result->closeCursor();
478
+
479
+        return $id === false ? -1 : (int)$id;
480
+    }
481
+
482
+    /**
483
+     * get the id of the parent folder of a file
484
+     *
485
+     * @param string $file
486
+     * @return int
487
+     */
488
+    public function getParentId($file) {
489
+        if ($file === '') {
490
+            return -1;
491
+        } else {
492
+            $parent = $this->getParentPath($file);
493
+            return (int)$this->getId($parent);
494
+        }
495
+    }
496
+
497
+    private function getParentPath($path) {
498
+        $parent = dirname($path);
499
+        if ($parent === '.') {
500
+            $parent = '';
501
+        }
502
+        return $parent;
503
+    }
504
+
505
+    /**
506
+     * check if a file is available in the cache
507
+     *
508
+     * @param string $file
509
+     * @return bool
510
+     */
511
+    public function inCache($file) {
512
+        return $this->getId($file) != -1;
513
+    }
514
+
515
+    /**
516
+     * remove a file or folder from the cache
517
+     *
518
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
519
+     *
520
+     * @param string $file
521
+     */
522
+    public function remove($file) {
523
+        $entry = $this->get($file);
524
+
525
+        if ($entry) {
526
+            $query = $this->getQueryBuilder();
527
+            $query->delete('filecache')
528
+                ->whereFileId($entry->getId());
529
+            $query->execute();
530
+
531
+            $query = $this->getQueryBuilder();
532
+            $query->delete('filecache_extended')
533
+                ->whereFileId($entry->getId());
534
+            $query->execute();
535
+
536
+            if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
537
+                $this->removeChildren($entry);
538
+            }
539
+        }
540
+    }
541
+
542
+    /**
543
+     * Get all sub folders of a folder
544
+     *
545
+     * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for
546
+     * @return ICacheEntry[] the cache entries for the subfolders
547
+     */
548
+    private function getSubFolders(ICacheEntry $entry) {
549
+        $children = $this->getFolderContentsById($entry->getId());
550
+        return array_filter($children, function ($child) {
551
+            return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
552
+        });
553
+    }
554
+
555
+    /**
556
+     * Recursively remove all children of a folder
557
+     *
558
+     * @param ICacheEntry $entry the cache entry of the folder to remove the children of
559
+     * @throws \OC\DatabaseException
560
+     */
561
+    private function removeChildren(ICacheEntry $entry) {
562
+        $parentIds = [$entry->getId()];
563
+        $queue = [$entry->getId()];
564
+
565
+        // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
566
+        // and collecting all folder ids to later use to delete the filecache entries
567
+        while ($entryId = array_pop($queue)) {
568
+            $children = $this->getFolderContentsById($entryId);
569
+            $childIds = array_map(function (ICacheEntry $cacheEntry) {
570
+                return $cacheEntry->getId();
571
+            }, $children);
572
+
573
+            $query = $this->getQueryBuilder();
574
+            $query->delete('filecache_extended')
575
+                ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY)));
576
+            $query->execute();
577
+
578
+            /** @var ICacheEntry[] $childFolders */
579
+            $childFolders = array_filter($children, function ($child) {
580
+                return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
581
+            });
582
+            foreach ($childFolders as $folder) {
583
+                $parentIds[] = $folder->getId();
584
+                $queue[] = $folder->getId();
585
+            }
586
+        }
587
+
588
+        $query = $this->getQueryBuilder();
589
+        $query->delete('filecache')
590
+            ->whereParentIn($parentIds);
591
+        $query->execute();
592
+    }
593
+
594
+    /**
595
+     * Move a file or folder in the cache
596
+     *
597
+     * @param string $source
598
+     * @param string $target
599
+     */
600
+    public function move($source, $target) {
601
+        $this->moveFromCache($this, $source, $target);
602
+    }
603
+
604
+    /**
605
+     * Get the storage id and path needed for a move
606
+     *
607
+     * @param string $path
608
+     * @return array [$storageId, $internalPath]
609
+     */
610
+    protected function getMoveInfo($path) {
611
+        return [$this->getNumericStorageId(), $path];
612
+    }
613
+
614
+    /**
615
+     * Move a file or folder in the cache
616
+     *
617
+     * @param \OCP\Files\Cache\ICache $sourceCache
618
+     * @param string $sourcePath
619
+     * @param string $targetPath
620
+     * @throws \OC\DatabaseException
621
+     * @throws \Exception if the given storages have an invalid id
622
+     */
623
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
624
+        if ($sourceCache instanceof Cache) {
625
+            // normalize source and target
626
+            $sourcePath = $this->normalize($sourcePath);
627
+            $targetPath = $this->normalize($targetPath);
628
+
629
+            $sourceData = $sourceCache->get($sourcePath);
630
+            $sourceId = $sourceData['fileid'];
631
+            $newParentId = $this->getParentId($targetPath);
632
+
633
+            [$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
634
+            [$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
635
+
636
+            if (is_null($sourceStorageId) || $sourceStorageId === false) {
637
+                throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
638
+            }
639
+            if (is_null($targetStorageId) || $targetStorageId === false) {
640
+                throw new \Exception('Invalid target storage id: ' . $targetStorageId);
641
+            }
642
+
643
+            $this->connection->beginTransaction();
644
+            if ($sourceData['mimetype'] === 'httpd/unix-directory') {
645
+                //update all child entries
646
+                $sourceLength = mb_strlen($sourcePath);
647
+                $query = $this->connection->getQueryBuilder();
648
+
649
+                $fun = $query->func();
650
+                $newPathFunction = $fun->concat(
651
+                    $query->createNamedParameter($targetPath),
652
+                    $fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
653
+                );
654
+                $query->update('filecache')
655
+                    ->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
656
+                    ->set('path_hash', $fun->md5($newPathFunction))
657
+                    ->set('path', $newPathFunction)
658
+                    ->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
659
+                    ->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
660
+
661
+                try {
662
+                    $query->execute();
663
+                } catch (\OC\DatabaseException $e) {
664
+                    $this->connection->rollBack();
665
+                    throw $e;
666
+                }
667
+            }
668
+
669
+            $query = $this->getQueryBuilder();
670
+            $query->update('filecache')
671
+                ->set('storage', $query->createNamedParameter($targetStorageId))
672
+                ->set('path', $query->createNamedParameter($targetPath))
673
+                ->set('path_hash', $query->createNamedParameter(md5($targetPath)))
674
+                ->set('name', $query->createNamedParameter(basename($targetPath)))
675
+                ->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
676
+                ->whereFileId($sourceId);
677
+            $query->execute();
678
+
679
+            $this->connection->commit();
680
+        } else {
681
+            $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
682
+        }
683
+    }
684
+
685
+    /**
686
+     * remove all entries for files that are stored on the storage from the cache
687
+     */
688
+    public function clear() {
689
+        $query = $this->getQueryBuilder();
690
+        $query->delete('filecache')
691
+            ->whereStorageId();
692
+        $query->execute();
693
+
694
+        $query = $this->connection->getQueryBuilder();
695
+        $query->delete('storages')
696
+            ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
697
+        $query->execute();
698
+    }
699
+
700
+    /**
701
+     * Get the scan status of a file
702
+     *
703
+     * - Cache::NOT_FOUND: File is not in the cache
704
+     * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
705
+     * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
706
+     * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
707
+     *
708
+     * @param string $file
709
+     *
710
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
711
+     */
712
+    public function getStatus($file) {
713
+        // normalize file
714
+        $file = $this->normalize($file);
715
+
716
+        $query = $this->getQueryBuilder();
717
+        $query->select('size')
718
+            ->from('filecache')
719
+            ->whereStorageId()
720
+            ->wherePath($file);
721
+
722
+        $result = $query->execute();
723
+        $size = $result->fetchColumn();
724
+        $result->closeCursor();
725
+
726
+        if ($size !== false) {
727
+            if ((int)$size === -1) {
728
+                return self::SHALLOW;
729
+            } else {
730
+                return self::COMPLETE;
731
+            }
732
+        } else {
733
+            if (isset($this->partial[$file])) {
734
+                return self::PARTIAL;
735
+            } else {
736
+                return self::NOT_FOUND;
737
+            }
738
+        }
739
+    }
740
+
741
+    /**
742
+     * search for files matching $pattern
743
+     *
744
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
745
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
746
+     */
747
+    public function search($pattern) {
748
+        // normalize pattern
749
+        $pattern = $this->normalize($pattern);
750
+
751
+        if ($pattern === '%%') {
752
+            return [];
753
+        }
754
+
755
+        $query = $this->getQueryBuilder();
756
+        $query->selectFileCache()
757
+            ->whereStorageId()
758
+            ->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
759
+
760
+        $result = $query->execute();
761
+        $files = $result->fetchAll();
762
+        $result->closeCursor();
763
+
764
+        return array_map(function (array $data) {
765
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
766
+        }, $files);
767
+    }
768
+
769
+    /**
770
+     * @param Statement $result
771
+     * @return CacheEntry[]
772
+     */
773
+    private function searchResultToCacheEntries(Statement $result) {
774
+        $files = $result->fetchAll();
775
+
776
+        return array_map(function (array $data) {
777
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
778
+        }, $files);
779
+    }
780
+
781
+    /**
782
+     * search for files by mimetype
783
+     *
784
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
785
+     *        where it will search for all mimetypes in the group ('image/*')
786
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
787
+     */
788
+    public function searchByMime($mimetype) {
789
+        $mimeId = $this->mimetypeLoader->getId($mimetype);
790
+
791
+        $query = $this->getQueryBuilder();
792
+        $query->selectFileCache()
793
+            ->whereStorageId();
794
+
795
+        if (strpos($mimetype, '/')) {
796
+            $query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
797
+        } else {
798
+            $query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
799
+        }
800
+
801
+        $result = $query->execute();
802
+        $files = $result->fetchAll();
803
+        $result->closeCursor();
804
+
805
+        return array_map(function (array $data) {
806
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
807
+        }, $files);
808
+    }
809
+
810
+    public function searchQuery(ISearchQuery $searchQuery) {
811
+        $builder = $this->getQueryBuilder();
812
+
813
+        $query = $builder->selectFileCache('file');
814
+
815
+        $query->whereStorageId();
816
+
817
+        if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
818
+            $query
819
+                ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
820
+                ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
821
+                    $builder->expr()->eq('tagmap.type', 'tag.type'),
822
+                    $builder->expr()->eq('tagmap.categoryid', 'tag.id')
823
+                ))
824
+                ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
825
+                ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
826
+        }
827
+
828
+        $searchExpr = $this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
829
+        if ($searchExpr) {
830
+            $query->andWhere($searchExpr);
831
+        }
832
+
833
+        if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
834
+            $query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
835
+        }
836
+
837
+        $this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
838
+
839
+        if ($searchQuery->getLimit()) {
840
+            $query->setMaxResults($searchQuery->getLimit());
841
+        }
842
+        if ($searchQuery->getOffset()) {
843
+            $query->setFirstResult($searchQuery->getOffset());
844
+        }
845
+
846
+        $result = $query->execute();
847
+        return $this->searchResultToCacheEntries($result);
848
+    }
849
+
850
+    /**
851
+     * Re-calculate the folder size and the size of all parent folders
852
+     *
853
+     * @param string|boolean $path
854
+     * @param array $data (optional) meta data of the folder
855
+     */
856
+    public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
857
+        $this->calculateFolderSize($path, $data);
858
+        if ($path !== '') {
859
+            $parent = dirname($path);
860
+            if ($parent === '.' or $parent === '/') {
861
+                $parent = '';
862
+            }
863
+            if ($isBackgroundScan) {
864
+                $parentData = $this->get($parent);
865
+                if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
866
+                    $this->correctFolderSize($parent, $parentData, $isBackgroundScan);
867
+                }
868
+            } else {
869
+                $this->correctFolderSize($parent);
870
+            }
871
+        }
872
+    }
873
+
874
+    /**
875
+     * get the incomplete count that shares parent $folder
876
+     *
877
+     * @param int $fileId the file id of the folder
878
+     * @return int
879
+     */
880
+    public function getIncompleteChildrenCount($fileId) {
881
+        if ($fileId > -1) {
882
+            $query = $this->getQueryBuilder();
883
+            $query->select($query->func()->count())
884
+                ->from('filecache')
885
+                ->whereParent($fileId)
886
+                ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
887
+
888
+            $result = $query->execute();
889
+            $size = (int)$result->fetchColumn();
890
+            $result->closeCursor();
891
+
892
+            return $size;
893
+        }
894
+        return -1;
895
+    }
896
+
897
+    /**
898
+     * calculate the size of a folder and set it in the cache
899
+     *
900
+     * @param string $path
901
+     * @param array $entry (optional) meta data of the folder
902
+     * @return int
903
+     */
904
+    public function calculateFolderSize($path, $entry = null) {
905
+        $totalSize = 0;
906
+        if (is_null($entry) or !isset($entry['fileid'])) {
907
+            $entry = $this->get($path);
908
+        }
909
+        if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
910
+            $id = $entry['fileid'];
911
+
912
+            $query = $this->getQueryBuilder();
913
+            $query->selectAlias($query->func()->sum('size'), 'f1')
914
+                ->selectAlias($query->func()->min('size'), 'f2')
915
+                ->from('filecache')
916
+                ->whereStorageId()
917
+                ->whereParent($id);
918
+
919
+            $result = $query->execute();
920
+            $row = $result->fetch();
921
+            $result->closeCursor();
922
+
923
+            if ($row) {
924
+                [$sum, $min] = array_values($row);
925
+                $sum = 0 + $sum;
926
+                $min = 0 + $min;
927
+                if ($min === -1) {
928
+                    $totalSize = $min;
929
+                } else {
930
+                    $totalSize = $sum;
931
+                }
932
+                if ($entry['size'] !== $totalSize) {
933
+                    $this->update($id, ['size' => $totalSize]);
934
+                }
935
+            }
936
+        }
937
+        return $totalSize;
938
+    }
939
+
940
+    /**
941
+     * get all file ids on the files on the storage
942
+     *
943
+     * @return int[]
944
+     */
945
+    public function getAll() {
946
+        $query = $this->getQueryBuilder();
947
+        $query->select('fileid')
948
+            ->from('filecache')
949
+            ->whereStorageId();
950
+
951
+        $result = $query->execute();
952
+        $files = $result->fetchAll(\PDO::FETCH_COLUMN);
953
+        $result->closeCursor();
954
+
955
+        return array_map(function ($id) {
956
+            return (int)$id;
957
+        }, $files);
958
+    }
959
+
960
+    /**
961
+     * find a folder in the cache which has not been fully scanned
962
+     *
963
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
964
+     * use the one with the highest id gives the best result with the background scanner, since that is most
965
+     * likely the folder where we stopped scanning previously
966
+     *
967
+     * @return string|bool the path of the folder or false when no folder matched
968
+     */
969
+    public function getIncomplete() {
970
+        $query = $this->getQueryBuilder();
971
+        $query->select('path')
972
+            ->from('filecache')
973
+            ->whereStorageId()
974
+            ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
975
+            ->orderBy('fileid', 'DESC');
976
+
977
+        $result = $query->execute();
978
+        $path = $result->fetchColumn();
979
+        $result->closeCursor();
980
+
981
+        return $path;
982
+    }
983
+
984
+    /**
985
+     * get the path of a file on this storage by it's file id
986
+     *
987
+     * @param int $id the file id of the file or folder to search
988
+     * @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
989
+     */
990
+    public function getPathById($id) {
991
+        $query = $this->getQueryBuilder();
992
+        $query->select('path')
993
+            ->from('filecache')
994
+            ->whereStorageId()
995
+            ->whereFileId($id);
996
+
997
+        $result = $query->execute();
998
+        $path = $result->fetchColumn();
999
+        $result->closeCursor();
1000
+
1001
+        if ($path === false) {
1002
+            return null;
1003
+        }
1004
+
1005
+        return (string) $path;
1006
+    }
1007
+
1008
+    /**
1009
+     * get the storage id of the storage for a file and the internal path of the file
1010
+     * unlike getPathById this does not limit the search to files on this storage and
1011
+     * instead does a global search in the cache table
1012
+     *
1013
+     * @param int $id
1014
+     * @return array first element holding the storage id, second the path
1015
+     * @deprecated use getPathById() instead
1016
+     */
1017
+    public static function getById($id) {
1018
+        $query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
1019
+        $query->select('path', 'storage')
1020
+            ->from('filecache')
1021
+            ->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
1022
+
1023
+        $result = $query->execute();
1024
+        $row = $result->fetch();
1025
+        $result->closeCursor();
1026
+
1027
+        if ($row) {
1028
+            $numericId = $row['storage'];
1029
+            $path = $row['path'];
1030
+        } else {
1031
+            return null;
1032
+        }
1033
+
1034
+        if ($id = Storage::getStorageId($numericId)) {
1035
+            return [$id, $path];
1036
+        } else {
1037
+            return null;
1038
+        }
1039
+    }
1040
+
1041
+    /**
1042
+     * normalize the given path
1043
+     *
1044
+     * @param string $path
1045
+     * @return string
1046
+     */
1047
+    public function normalize($path) {
1048
+        return trim(\OC_Util::normalizeUnicode($path), '/');
1049
+    }
1050 1050
 }
Please login to merge, or discard this patch.
lib/private/DB/MissingPrimaryKeyInformation.php 1 patch
Indentation   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -28,15 +28,15 @@
 block discarded – undo
28 28
 namespace OC\DB;
29 29
 
30 30
 class MissingPrimaryKeyInformation {
31
-	private $listOfMissingPrimaryKeys = [];
31
+    private $listOfMissingPrimaryKeys = [];
32 32
 
33
-	public function addHintForMissingSubject(string $tableName) {
34
-		$this->listOfMissingPrimaryKeys[] = [
35
-			'tableName' => $tableName,
36
-		];
37
-	}
33
+    public function addHintForMissingSubject(string $tableName) {
34
+        $this->listOfMissingPrimaryKeys[] = [
35
+            'tableName' => $tableName,
36
+        ];
37
+    }
38 38
 
39
-	public function getListOfMissingPrimaryKeys(): array {
40
-		return $this->listOfMissingPrimaryKeys;
41
-	}
39
+    public function getListOfMissingPrimaryKeys(): array {
40
+        return $this->listOfMissingPrimaryKeys;
41
+    }
42 42
 }
Please login to merge, or discard this patch.
lib/private/DB/Connection.php 1 patch
Indentation   +440 added lines, -440 removed lines patch added patch discarded remove patch
@@ -50,444 +50,444 @@
 block discarded – undo
50 50
 use OCP\PreConditionNotMetException;
51 51
 
52 52
 class Connection extends ReconnectWrapper implements IDBConnection {
53
-	/** @var string */
54
-	protected $tablePrefix;
55
-
56
-	/** @var \OC\DB\Adapter $adapter */
57
-	protected $adapter;
58
-
59
-	/** @var SystemConfig */
60
-	private $systemConfig;
61
-
62
-	/** @var ILogger */
63
-	private $logger;
64
-
65
-	protected $lockedTable = null;
66
-
67
-	/** @var int */
68
-	protected $queriesBuilt = 0;
69
-
70
-	/** @var int */
71
-	protected $queriesExecuted = 0;
72
-
73
-	public function connect() {
74
-		try {
75
-			return parent::connect();
76
-		} catch (DBALException $e) {
77
-			// throw a new exception to prevent leaking info from the stacktrace
78
-			throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
79
-		}
80
-	}
81
-
82
-	public function getStats(): array {
83
-		return [
84
-			'built' => $this->queriesBuilt,
85
-			'executed' => $this->queriesExecuted,
86
-		];
87
-	}
88
-
89
-	/**
90
-	 * Returns a QueryBuilder for the connection.
91
-	 *
92
-	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
93
-	 */
94
-	public function getQueryBuilder() {
95
-		$this->queriesBuilt++;
96
-		return new QueryBuilder(
97
-			$this,
98
-			$this->systemConfig,
99
-			$this->logger
100
-		);
101
-	}
102
-
103
-	/**
104
-	 * Gets the QueryBuilder for the connection.
105
-	 *
106
-	 * @return \Doctrine\DBAL\Query\QueryBuilder
107
-	 * @deprecated please use $this->getQueryBuilder() instead
108
-	 */
109
-	public function createQueryBuilder() {
110
-		$backtrace = $this->getCallerBacktrace();
111
-		\OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
112
-		$this->queriesBuilt++;
113
-		return parent::createQueryBuilder();
114
-	}
115
-
116
-	/**
117
-	 * Gets the ExpressionBuilder for the connection.
118
-	 *
119
-	 * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
120
-	 * @deprecated please use $this->getQueryBuilder()->expr() instead
121
-	 */
122
-	public function getExpressionBuilder() {
123
-		$backtrace = $this->getCallerBacktrace();
124
-		\OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
125
-		$this->queriesBuilt++;
126
-		return parent::getExpressionBuilder();
127
-	}
128
-
129
-	/**
130
-	 * Get the file and line that called the method where `getCallerBacktrace()` was used
131
-	 *
132
-	 * @return string
133
-	 */
134
-	protected function getCallerBacktrace() {
135
-		$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
136
-
137
-		// 0 is the method where we use `getCallerBacktrace`
138
-		// 1 is the target method which uses the method we want to log
139
-		if (isset($traces[1])) {
140
-			return $traces[1]['file'] . ':' . $traces[1]['line'];
141
-		}
142
-
143
-		return '';
144
-	}
145
-
146
-	/**
147
-	 * @return string
148
-	 */
149
-	public function getPrefix() {
150
-		return $this->tablePrefix;
151
-	}
152
-
153
-	/**
154
-	 * Initializes a new instance of the Connection class.
155
-	 *
156
-	 * @param array $params  The connection parameters.
157
-	 * @param \Doctrine\DBAL\Driver $driver
158
-	 * @param \Doctrine\DBAL\Configuration $config
159
-	 * @param \Doctrine\Common\EventManager $eventManager
160
-	 * @throws \Exception
161
-	 */
162
-	public function __construct(array $params, Driver $driver, Configuration $config = null,
163
-		EventManager $eventManager = null) {
164
-		if (!isset($params['adapter'])) {
165
-			throw new \Exception('adapter not set');
166
-		}
167
-		if (!isset($params['tablePrefix'])) {
168
-			throw new \Exception('tablePrefix not set');
169
-		}
170
-		parent::__construct($params, $driver, $config, $eventManager);
171
-		$this->adapter = new $params['adapter']($this);
172
-		$this->tablePrefix = $params['tablePrefix'];
173
-
174
-		$this->systemConfig = \OC::$server->getSystemConfig();
175
-		$this->logger = \OC::$server->getLogger();
176
-	}
177
-
178
-	/**
179
-	 * Prepares an SQL statement.
180
-	 *
181
-	 * @param string $statement The SQL statement to prepare.
182
-	 * @param int $limit
183
-	 * @param int $offset
184
-	 * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
185
-	 */
186
-	public function prepare($statement, $limit = null, $offset = null) {
187
-		if ($limit === -1) {
188
-			$limit = null;
189
-		}
190
-		if (!is_null($limit)) {
191
-			$platform = $this->getDatabasePlatform();
192
-			$statement = $platform->modifyLimitQuery($statement, $limit, $offset);
193
-		}
194
-		$statement = $this->replaceTablePrefix($statement);
195
-		$statement = $this->adapter->fixupStatement($statement);
196
-
197
-		return parent::prepare($statement);
198
-	}
199
-
200
-	/**
201
-	 * Executes an, optionally parametrized, SQL query.
202
-	 *
203
-	 * If the query is parametrized, a prepared statement is used.
204
-	 * If an SQLLogger is configured, the execution is logged.
205
-	 *
206
-	 * @param string                                      $sql  The SQL query to execute.
207
-	 * @param array                                       $params The parameters to bind to the query, if any.
208
-	 * @param array                                       $types  The types the previous parameters are in.
209
-	 * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
210
-	 *
211
-	 * @return \Doctrine\DBAL\Driver\Statement The executed statement.
212
-	 *
213
-	 * @throws \Doctrine\DBAL\DBALException
214
-	 */
215
-	public function executeQuery($sql, array $params = [], $types = [], QueryCacheProfile $qcp = null) {
216
-		$sql = $this->replaceTablePrefix($sql);
217
-		$sql = $this->adapter->fixupStatement($sql);
218
-		$this->queriesExecuted++;
219
-		return parent::executeQuery($sql, $params, $types, $qcp);
220
-	}
221
-
222
-	public function executeUpdate($sql, array $params = [], array $types = []) {
223
-		$sql = $this->replaceTablePrefix($sql);
224
-		$sql = $this->adapter->fixupStatement($sql);
225
-		$this->queriesExecuted++;
226
-		return parent::executeUpdate($sql, $params, $types);
227
-	}
228
-
229
-	/**
230
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
231
-	 * and returns the number of affected rows.
232
-	 *
233
-	 * This method supports PDO binding types as well as DBAL mapping types.
234
-	 *
235
-	 * @param string $sql  The SQL query.
236
-	 * @param array  $params The query parameters.
237
-	 * @param array  $types  The parameter types.
238
-	 *
239
-	 * @return integer The number of affected rows.
240
-	 *
241
-	 * @throws \Doctrine\DBAL\DBALException
242
-	 */
243
-	public function executeStatement($sql, array $params = [], array $types = []) {
244
-		$sql = $this->replaceTablePrefix($sql);
245
-		$sql = $this->adapter->fixupStatement($sql);
246
-		$this->queriesExecuted++;
247
-		return parent::executeStatement($sql, $params, $types);
248
-	}
249
-
250
-	/**
251
-	 * Returns the ID of the last inserted row, or the last value from a sequence object,
252
-	 * depending on the underlying driver.
253
-	 *
254
-	 * Note: This method may not return a meaningful or consistent result across different drivers,
255
-	 * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
256
-	 * columns or sequences.
257
-	 *
258
-	 * @param string $seqName Name of the sequence object from which the ID should be returned.
259
-	 * @return string A string representation of the last inserted ID.
260
-	 */
261
-	public function lastInsertId($seqName = null) {
262
-		if ($seqName) {
263
-			$seqName = $this->replaceTablePrefix($seqName);
264
-		}
265
-		return $this->adapter->lastInsertId($seqName);
266
-	}
267
-
268
-	// internal use
269
-	public function realLastInsertId($seqName = null) {
270
-		return parent::lastInsertId($seqName);
271
-	}
272
-
273
-	/**
274
-	 * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
275
-	 * it is needed that there is also a unique constraint on the values. Then this method will
276
-	 * catch the exception and return 0.
277
-	 *
278
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
279
-	 * @param array $input data that should be inserted into the table  (column name => value)
280
-	 * @param array|null $compare List of values that should be checked for "if not exists"
281
-	 *				If this is null or an empty array, all keys of $input will be compared
282
-	 *				Please note: text fields (clob) must not be used in the compare array
283
-	 * @return int number of inserted rows
284
-	 * @throws \Doctrine\DBAL\DBALException
285
-	 * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
286
-	 */
287
-	public function insertIfNotExist($table, $input, array $compare = null) {
288
-		return $this->adapter->insertIfNotExist($table, $input, $compare);
289
-	}
290
-
291
-	public function insertIgnoreConflict(string $table, array $values) : int {
292
-		return $this->adapter->insertIgnoreConflict($table, $values);
293
-	}
294
-
295
-	private function getType($value) {
296
-		if (is_bool($value)) {
297
-			return IQueryBuilder::PARAM_BOOL;
298
-		} elseif (is_int($value)) {
299
-			return IQueryBuilder::PARAM_INT;
300
-		} else {
301
-			return IQueryBuilder::PARAM_STR;
302
-		}
303
-	}
304
-
305
-	/**
306
-	 * Insert or update a row value
307
-	 *
308
-	 * @param string $table
309
-	 * @param array $keys (column name => value)
310
-	 * @param array $values (column name => value)
311
-	 * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
312
-	 * @return int number of new rows
313
-	 * @throws \Doctrine\DBAL\DBALException
314
-	 * @throws PreConditionNotMetException
315
-	 */
316
-	public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
317
-		try {
318
-			$insertQb = $this->getQueryBuilder();
319
-			$insertQb->insert($table)
320
-				->values(
321
-					array_map(function ($value) use ($insertQb) {
322
-						return $insertQb->createNamedParameter($value, $this->getType($value));
323
-					}, array_merge($keys, $values))
324
-				);
325
-			return $insertQb->execute();
326
-		} catch (NotNullConstraintViolationException $e) {
327
-			throw $e;
328
-		} catch (ConstraintViolationException $e) {
329
-			// value already exists, try update
330
-			$updateQb = $this->getQueryBuilder();
331
-			$updateQb->update($table);
332
-			foreach ($values as $name => $value) {
333
-				$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
334
-			}
335
-			$where = $updateQb->expr()->andX();
336
-			$whereValues = array_merge($keys, $updatePreconditionValues);
337
-			foreach ($whereValues as $name => $value) {
338
-				if ($value === '') {
339
-					$where->add($updateQb->expr()->emptyString(
340
-						$name
341
-					));
342
-				} else {
343
-					$where->add($updateQb->expr()->eq(
344
-						$name,
345
-						$updateQb->createNamedParameter($value, $this->getType($value)),
346
-						$this->getType($value)
347
-					));
348
-				}
349
-			}
350
-			$updateQb->where($where);
351
-			$affected = $updateQb->execute();
352
-
353
-			if ($affected === 0 && !empty($updatePreconditionValues)) {
354
-				throw new PreConditionNotMetException();
355
-			}
356
-
357
-			return 0;
358
-		}
359
-	}
360
-
361
-	/**
362
-	 * Create an exclusive read+write lock on a table
363
-	 *
364
-	 * @param string $tableName
365
-	 * @throws \BadMethodCallException When trying to acquire a second lock
366
-	 * @since 9.1.0
367
-	 */
368
-	public function lockTable($tableName) {
369
-		if ($this->lockedTable !== null) {
370
-			throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
371
-		}
372
-
373
-		$tableName = $this->tablePrefix . $tableName;
374
-		$this->lockedTable = $tableName;
375
-		$this->adapter->lockTable($tableName);
376
-	}
377
-
378
-	/**
379
-	 * Release a previous acquired lock again
380
-	 *
381
-	 * @since 9.1.0
382
-	 */
383
-	public function unlockTable() {
384
-		$this->adapter->unlockTable();
385
-		$this->lockedTable = null;
386
-	}
387
-
388
-	/**
389
-	 * returns the error code and message as a string for logging
390
-	 * works with DoctrineException
391
-	 * @return string
392
-	 */
393
-	public function getError() {
394
-		$msg = $this->errorCode() . ': ';
395
-		$errorInfo = $this->errorInfo();
396
-		if (is_array($errorInfo)) {
397
-			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
398
-			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
399
-			$msg .= 'Driver Message = '.$errorInfo[2];
400
-		}
401
-		return $msg;
402
-	}
403
-
404
-	/**
405
-	 * Drop a table from the database if it exists
406
-	 *
407
-	 * @param string $table table name without the prefix
408
-	 */
409
-	public function dropTable($table) {
410
-		$table = $this->tablePrefix . trim($table);
411
-		$schema = $this->getSchemaManager();
412
-		if ($schema->tablesExist([$table])) {
413
-			$schema->dropTable($table);
414
-		}
415
-	}
416
-
417
-	/**
418
-	 * Check if a table exists
419
-	 *
420
-	 * @param string $table table name without the prefix
421
-	 * @return bool
422
-	 */
423
-	public function tableExists($table) {
424
-		$table = $this->tablePrefix . trim($table);
425
-		$schema = $this->getSchemaManager();
426
-		return $schema->tablesExist([$table]);
427
-	}
428
-
429
-	// internal use
430
-	/**
431
-	 * @param string $statement
432
-	 * @return string
433
-	 */
434
-	protected function replaceTablePrefix($statement) {
435
-		return str_replace('*PREFIX*', $this->tablePrefix, $statement);
436
-	}
437
-
438
-	/**
439
-	 * Check if a transaction is active
440
-	 *
441
-	 * @return bool
442
-	 * @since 8.2.0
443
-	 */
444
-	public function inTransaction() {
445
-		return $this->getTransactionNestingLevel() > 0;
446
-	}
447
-
448
-	/**
449
-	 * Escape a parameter to be used in a LIKE query
450
-	 *
451
-	 * @param string $param
452
-	 * @return string
453
-	 */
454
-	public function escapeLikeParameter($param) {
455
-		return addcslashes($param, '\\_%');
456
-	}
457
-
458
-	/**
459
-	 * Check whether or not the current database support 4byte wide unicode
460
-	 *
461
-	 * @return bool
462
-	 * @since 11.0.0
463
-	 */
464
-	public function supports4ByteText() {
465
-		if (!$this->getDatabasePlatform() instanceof MySqlPlatform) {
466
-			return true;
467
-		}
468
-		return $this->getParams()['charset'] === 'utf8mb4';
469
-	}
470
-
471
-
472
-	/**
473
-	 * Create the schema of the connected database
474
-	 *
475
-	 * @return Schema
476
-	 */
477
-	public function createSchema() {
478
-		$schemaManager = new MDB2SchemaManager($this);
479
-		$migrator = $schemaManager->getMigrator();
480
-		return $migrator->createSchema();
481
-	}
482
-
483
-	/**
484
-	 * Migrate the database to the given schema
485
-	 *
486
-	 * @param Schema $toSchema
487
-	 */
488
-	public function migrateToSchema(Schema $toSchema) {
489
-		$schemaManager = new MDB2SchemaManager($this);
490
-		$migrator = $schemaManager->getMigrator();
491
-		$migrator->migrate($toSchema);
492
-	}
53
+    /** @var string */
54
+    protected $tablePrefix;
55
+
56
+    /** @var \OC\DB\Adapter $adapter */
57
+    protected $adapter;
58
+
59
+    /** @var SystemConfig */
60
+    private $systemConfig;
61
+
62
+    /** @var ILogger */
63
+    private $logger;
64
+
65
+    protected $lockedTable = null;
66
+
67
+    /** @var int */
68
+    protected $queriesBuilt = 0;
69
+
70
+    /** @var int */
71
+    protected $queriesExecuted = 0;
72
+
73
+    public function connect() {
74
+        try {
75
+            return parent::connect();
76
+        } catch (DBALException $e) {
77
+            // throw a new exception to prevent leaking info from the stacktrace
78
+            throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
79
+        }
80
+    }
81
+
82
+    public function getStats(): array {
83
+        return [
84
+            'built' => $this->queriesBuilt,
85
+            'executed' => $this->queriesExecuted,
86
+        ];
87
+    }
88
+
89
+    /**
90
+     * Returns a QueryBuilder for the connection.
91
+     *
92
+     * @return \OCP\DB\QueryBuilder\IQueryBuilder
93
+     */
94
+    public function getQueryBuilder() {
95
+        $this->queriesBuilt++;
96
+        return new QueryBuilder(
97
+            $this,
98
+            $this->systemConfig,
99
+            $this->logger
100
+        );
101
+    }
102
+
103
+    /**
104
+     * Gets the QueryBuilder for the connection.
105
+     *
106
+     * @return \Doctrine\DBAL\Query\QueryBuilder
107
+     * @deprecated please use $this->getQueryBuilder() instead
108
+     */
109
+    public function createQueryBuilder() {
110
+        $backtrace = $this->getCallerBacktrace();
111
+        \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
112
+        $this->queriesBuilt++;
113
+        return parent::createQueryBuilder();
114
+    }
115
+
116
+    /**
117
+     * Gets the ExpressionBuilder for the connection.
118
+     *
119
+     * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
120
+     * @deprecated please use $this->getQueryBuilder()->expr() instead
121
+     */
122
+    public function getExpressionBuilder() {
123
+        $backtrace = $this->getCallerBacktrace();
124
+        \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
125
+        $this->queriesBuilt++;
126
+        return parent::getExpressionBuilder();
127
+    }
128
+
129
+    /**
130
+     * Get the file and line that called the method where `getCallerBacktrace()` was used
131
+     *
132
+     * @return string
133
+     */
134
+    protected function getCallerBacktrace() {
135
+        $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
136
+
137
+        // 0 is the method where we use `getCallerBacktrace`
138
+        // 1 is the target method which uses the method we want to log
139
+        if (isset($traces[1])) {
140
+            return $traces[1]['file'] . ':' . $traces[1]['line'];
141
+        }
142
+
143
+        return '';
144
+    }
145
+
146
+    /**
147
+     * @return string
148
+     */
149
+    public function getPrefix() {
150
+        return $this->tablePrefix;
151
+    }
152
+
153
+    /**
154
+     * Initializes a new instance of the Connection class.
155
+     *
156
+     * @param array $params  The connection parameters.
157
+     * @param \Doctrine\DBAL\Driver $driver
158
+     * @param \Doctrine\DBAL\Configuration $config
159
+     * @param \Doctrine\Common\EventManager $eventManager
160
+     * @throws \Exception
161
+     */
162
+    public function __construct(array $params, Driver $driver, Configuration $config = null,
163
+        EventManager $eventManager = null) {
164
+        if (!isset($params['adapter'])) {
165
+            throw new \Exception('adapter not set');
166
+        }
167
+        if (!isset($params['tablePrefix'])) {
168
+            throw new \Exception('tablePrefix not set');
169
+        }
170
+        parent::__construct($params, $driver, $config, $eventManager);
171
+        $this->adapter = new $params['adapter']($this);
172
+        $this->tablePrefix = $params['tablePrefix'];
173
+
174
+        $this->systemConfig = \OC::$server->getSystemConfig();
175
+        $this->logger = \OC::$server->getLogger();
176
+    }
177
+
178
+    /**
179
+     * Prepares an SQL statement.
180
+     *
181
+     * @param string $statement The SQL statement to prepare.
182
+     * @param int $limit
183
+     * @param int $offset
184
+     * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
185
+     */
186
+    public function prepare($statement, $limit = null, $offset = null) {
187
+        if ($limit === -1) {
188
+            $limit = null;
189
+        }
190
+        if (!is_null($limit)) {
191
+            $platform = $this->getDatabasePlatform();
192
+            $statement = $platform->modifyLimitQuery($statement, $limit, $offset);
193
+        }
194
+        $statement = $this->replaceTablePrefix($statement);
195
+        $statement = $this->adapter->fixupStatement($statement);
196
+
197
+        return parent::prepare($statement);
198
+    }
199
+
200
+    /**
201
+     * Executes an, optionally parametrized, SQL query.
202
+     *
203
+     * If the query is parametrized, a prepared statement is used.
204
+     * If an SQLLogger is configured, the execution is logged.
205
+     *
206
+     * @param string                                      $sql  The SQL query to execute.
207
+     * @param array                                       $params The parameters to bind to the query, if any.
208
+     * @param array                                       $types  The types the previous parameters are in.
209
+     * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
210
+     *
211
+     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
212
+     *
213
+     * @throws \Doctrine\DBAL\DBALException
214
+     */
215
+    public function executeQuery($sql, array $params = [], $types = [], QueryCacheProfile $qcp = null) {
216
+        $sql = $this->replaceTablePrefix($sql);
217
+        $sql = $this->adapter->fixupStatement($sql);
218
+        $this->queriesExecuted++;
219
+        return parent::executeQuery($sql, $params, $types, $qcp);
220
+    }
221
+
222
+    public function executeUpdate($sql, array $params = [], array $types = []) {
223
+        $sql = $this->replaceTablePrefix($sql);
224
+        $sql = $this->adapter->fixupStatement($sql);
225
+        $this->queriesExecuted++;
226
+        return parent::executeUpdate($sql, $params, $types);
227
+    }
228
+
229
+    /**
230
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
231
+     * and returns the number of affected rows.
232
+     *
233
+     * This method supports PDO binding types as well as DBAL mapping types.
234
+     *
235
+     * @param string $sql  The SQL query.
236
+     * @param array  $params The query parameters.
237
+     * @param array  $types  The parameter types.
238
+     *
239
+     * @return integer The number of affected rows.
240
+     *
241
+     * @throws \Doctrine\DBAL\DBALException
242
+     */
243
+    public function executeStatement($sql, array $params = [], array $types = []) {
244
+        $sql = $this->replaceTablePrefix($sql);
245
+        $sql = $this->adapter->fixupStatement($sql);
246
+        $this->queriesExecuted++;
247
+        return parent::executeStatement($sql, $params, $types);
248
+    }
249
+
250
+    /**
251
+     * Returns the ID of the last inserted row, or the last value from a sequence object,
252
+     * depending on the underlying driver.
253
+     *
254
+     * Note: This method may not return a meaningful or consistent result across different drivers,
255
+     * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
256
+     * columns or sequences.
257
+     *
258
+     * @param string $seqName Name of the sequence object from which the ID should be returned.
259
+     * @return string A string representation of the last inserted ID.
260
+     */
261
+    public function lastInsertId($seqName = null) {
262
+        if ($seqName) {
263
+            $seqName = $this->replaceTablePrefix($seqName);
264
+        }
265
+        return $this->adapter->lastInsertId($seqName);
266
+    }
267
+
268
+    // internal use
269
+    public function realLastInsertId($seqName = null) {
270
+        return parent::lastInsertId($seqName);
271
+    }
272
+
273
+    /**
274
+     * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
275
+     * it is needed that there is also a unique constraint on the values. Then this method will
276
+     * catch the exception and return 0.
277
+     *
278
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
279
+     * @param array $input data that should be inserted into the table  (column name => value)
280
+     * @param array|null $compare List of values that should be checked for "if not exists"
281
+     *				If this is null or an empty array, all keys of $input will be compared
282
+     *				Please note: text fields (clob) must not be used in the compare array
283
+     * @return int number of inserted rows
284
+     * @throws \Doctrine\DBAL\DBALException
285
+     * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
286
+     */
287
+    public function insertIfNotExist($table, $input, array $compare = null) {
288
+        return $this->adapter->insertIfNotExist($table, $input, $compare);
289
+    }
290
+
291
+    public function insertIgnoreConflict(string $table, array $values) : int {
292
+        return $this->adapter->insertIgnoreConflict($table, $values);
293
+    }
294
+
295
+    private function getType($value) {
296
+        if (is_bool($value)) {
297
+            return IQueryBuilder::PARAM_BOOL;
298
+        } elseif (is_int($value)) {
299
+            return IQueryBuilder::PARAM_INT;
300
+        } else {
301
+            return IQueryBuilder::PARAM_STR;
302
+        }
303
+    }
304
+
305
+    /**
306
+     * Insert or update a row value
307
+     *
308
+     * @param string $table
309
+     * @param array $keys (column name => value)
310
+     * @param array $values (column name => value)
311
+     * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
312
+     * @return int number of new rows
313
+     * @throws \Doctrine\DBAL\DBALException
314
+     * @throws PreConditionNotMetException
315
+     */
316
+    public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
317
+        try {
318
+            $insertQb = $this->getQueryBuilder();
319
+            $insertQb->insert($table)
320
+                ->values(
321
+                    array_map(function ($value) use ($insertQb) {
322
+                        return $insertQb->createNamedParameter($value, $this->getType($value));
323
+                    }, array_merge($keys, $values))
324
+                );
325
+            return $insertQb->execute();
326
+        } catch (NotNullConstraintViolationException $e) {
327
+            throw $e;
328
+        } catch (ConstraintViolationException $e) {
329
+            // value already exists, try update
330
+            $updateQb = $this->getQueryBuilder();
331
+            $updateQb->update($table);
332
+            foreach ($values as $name => $value) {
333
+                $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
334
+            }
335
+            $where = $updateQb->expr()->andX();
336
+            $whereValues = array_merge($keys, $updatePreconditionValues);
337
+            foreach ($whereValues as $name => $value) {
338
+                if ($value === '') {
339
+                    $where->add($updateQb->expr()->emptyString(
340
+                        $name
341
+                    ));
342
+                } else {
343
+                    $where->add($updateQb->expr()->eq(
344
+                        $name,
345
+                        $updateQb->createNamedParameter($value, $this->getType($value)),
346
+                        $this->getType($value)
347
+                    ));
348
+                }
349
+            }
350
+            $updateQb->where($where);
351
+            $affected = $updateQb->execute();
352
+
353
+            if ($affected === 0 && !empty($updatePreconditionValues)) {
354
+                throw new PreConditionNotMetException();
355
+            }
356
+
357
+            return 0;
358
+        }
359
+    }
360
+
361
+    /**
362
+     * Create an exclusive read+write lock on a table
363
+     *
364
+     * @param string $tableName
365
+     * @throws \BadMethodCallException When trying to acquire a second lock
366
+     * @since 9.1.0
367
+     */
368
+    public function lockTable($tableName) {
369
+        if ($this->lockedTable !== null) {
370
+            throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
371
+        }
372
+
373
+        $tableName = $this->tablePrefix . $tableName;
374
+        $this->lockedTable = $tableName;
375
+        $this->adapter->lockTable($tableName);
376
+    }
377
+
378
+    /**
379
+     * Release a previous acquired lock again
380
+     *
381
+     * @since 9.1.0
382
+     */
383
+    public function unlockTable() {
384
+        $this->adapter->unlockTable();
385
+        $this->lockedTable = null;
386
+    }
387
+
388
+    /**
389
+     * returns the error code and message as a string for logging
390
+     * works with DoctrineException
391
+     * @return string
392
+     */
393
+    public function getError() {
394
+        $msg = $this->errorCode() . ': ';
395
+        $errorInfo = $this->errorInfo();
396
+        if (is_array($errorInfo)) {
397
+            $msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
398
+            $msg .= 'Driver Code = '.$errorInfo[1] . ', ';
399
+            $msg .= 'Driver Message = '.$errorInfo[2];
400
+        }
401
+        return $msg;
402
+    }
403
+
404
+    /**
405
+     * Drop a table from the database if it exists
406
+     *
407
+     * @param string $table table name without the prefix
408
+     */
409
+    public function dropTable($table) {
410
+        $table = $this->tablePrefix . trim($table);
411
+        $schema = $this->getSchemaManager();
412
+        if ($schema->tablesExist([$table])) {
413
+            $schema->dropTable($table);
414
+        }
415
+    }
416
+
417
+    /**
418
+     * Check if a table exists
419
+     *
420
+     * @param string $table table name without the prefix
421
+     * @return bool
422
+     */
423
+    public function tableExists($table) {
424
+        $table = $this->tablePrefix . trim($table);
425
+        $schema = $this->getSchemaManager();
426
+        return $schema->tablesExist([$table]);
427
+    }
428
+
429
+    // internal use
430
+    /**
431
+     * @param string $statement
432
+     * @return string
433
+     */
434
+    protected function replaceTablePrefix($statement) {
435
+        return str_replace('*PREFIX*', $this->tablePrefix, $statement);
436
+    }
437
+
438
+    /**
439
+     * Check if a transaction is active
440
+     *
441
+     * @return bool
442
+     * @since 8.2.0
443
+     */
444
+    public function inTransaction() {
445
+        return $this->getTransactionNestingLevel() > 0;
446
+    }
447
+
448
+    /**
449
+     * Escape a parameter to be used in a LIKE query
450
+     *
451
+     * @param string $param
452
+     * @return string
453
+     */
454
+    public function escapeLikeParameter($param) {
455
+        return addcslashes($param, '\\_%');
456
+    }
457
+
458
+    /**
459
+     * Check whether or not the current database support 4byte wide unicode
460
+     *
461
+     * @return bool
462
+     * @since 11.0.0
463
+     */
464
+    public function supports4ByteText() {
465
+        if (!$this->getDatabasePlatform() instanceof MySqlPlatform) {
466
+            return true;
467
+        }
468
+        return $this->getParams()['charset'] === 'utf8mb4';
469
+    }
470
+
471
+
472
+    /**
473
+     * Create the schema of the connected database
474
+     *
475
+     * @return Schema
476
+     */
477
+    public function createSchema() {
478
+        $schemaManager = new MDB2SchemaManager($this);
479
+        $migrator = $schemaManager->getMigrator();
480
+        return $migrator->createSchema();
481
+    }
482
+
483
+    /**
484
+     * Migrate the database to the given schema
485
+     *
486
+     * @param Schema $toSchema
487
+     */
488
+    public function migrateToSchema(Schema $toSchema) {
489
+        $schemaManager = new MDB2SchemaManager($this);
490
+        $migrator = $schemaManager->getMigrator();
491
+        $migrator->migrate($toSchema);
492
+    }
493 493
 }
Please login to merge, or discard this patch.
lib/public/IDBConnection.php 2 patches
Indentation   +272 added lines, -272 removed lines patch added patch discarded remove patch
@@ -48,276 +48,276 @@
 block discarded – undo
48 48
  * @since 6.0.0
49 49
  */
50 50
 interface IDBConnection {
51
-	public const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES';
52
-	public const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES';
53
-	public const ADD_MISSING_PRIMARY_KEYS_EVENT = self::class . '::ADD_MISSING_PRIMARY_KEYS';
54
-	public const CHECK_MISSING_PRIMARY_KEYS_EVENT = self::class . '::CHECK_MISSING_PRIMARY_KEYS';
55
-	public const ADD_MISSING_COLUMNS_EVENT = self::class . '::ADD_MISSING_COLUMNS';
56
-	public const CHECK_MISSING_COLUMNS_EVENT = self::class . '::CHECK_MISSING_COLUMNS';
57
-
58
-	/**
59
-	 * Gets the QueryBuilder for the connection.
60
-	 *
61
-	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
62
-	 * @since 8.2.0
63
-	 */
64
-	public function getQueryBuilder();
65
-
66
-	/**
67
-	 * Used to abstract the ownCloud database access away
68
-	 * @param string $sql the sql query with ? placeholder for params
69
-	 * @param int $limit the maximum number of rows
70
-	 * @param int $offset from which row we want to start
71
-	 * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
72
-	 * @since 6.0.0
73
-	 */
74
-	public function prepare($sql, $limit = null, $offset = null);
75
-
76
-	/**
77
-	 * Executes an, optionally parameterized, SQL query.
78
-	 *
79
-	 * If the query is parameterized, a prepared statement is used.
80
-	 * If an SQLLogger is configured, the execution is logged.
81
-	 *
82
-	 * @param string $sql The SQL query to execute.
83
-	 * @param string[] $params The parameters to bind to the query, if any.
84
-	 * @param array $types The types the previous parameters are in.
85
-	 * @return \Doctrine\DBAL\Driver\Statement The executed statement.
86
-	 * @since 8.0.0
87
-	 */
88
-	public function executeQuery($sql, array $params = [], $types = []);
89
-
90
-	/**
91
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
92
-	 * and returns the number of affected rows.
93
-	 *
94
-	 * This method supports PDO binding types as well as DBAL mapping types.
95
-	 *
96
-	 * @param string $sql The SQL query.
97
-	 * @param array $params The query parameters.
98
-	 * @param array $types The parameter types.
99
-	 * @return integer The number of affected rows.
100
-	 * @since 8.0.0
101
-	 *
102
-	 * @deprecated 21.0.0 use executeStatement
103
-	 */
104
-	public function executeUpdate($sql, array $params = [], array $types = []);
105
-
106
-	/**
107
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
108
-	 * and returns the number of affected rows.
109
-	 *
110
-	 * This method supports PDO binding types as well as DBAL mapping types.
111
-	 *
112
-	 * @param string $sql The SQL query.
113
-	 * @param array $params The query parameters.
114
-	 * @param array $types The parameter types.
115
-	 * @return integer The number of affected rows.
116
-	 * @since 21.0.0
117
-	 */
118
-	public function executeStatement($sql, array $params = [], array $types = []);
119
-
120
-	/**
121
-	 * Used to get the id of the just inserted element
122
-	 * @param string $table the name of the table where we inserted the item
123
-	 * @return int the id of the inserted element
124
-	 * @since 6.0.0
125
-	 */
126
-	public function lastInsertId($table = null);
127
-
128
-	/**
129
-	 * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
130
-	 * it is needed that there is also a unique constraint on the values. Then this method will
131
-	 * catch the exception and return 0.
132
-	 *
133
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
134
-	 * @param array $input data that should be inserted into the table  (column name => value)
135
-	 * @param array|null $compare List of values that should be checked for "if not exists"
136
-	 *				If this is null or an empty array, all keys of $input will be compared
137
-	 *				Please note: text fields (clob) must not be used in the compare array
138
-	 * @return int number of inserted rows
139
-	 * @throws \Doctrine\DBAL\DBALException
140
-	 * @since 6.0.0 - parameter $compare was added in 8.1.0, return type changed from boolean in 8.1.0
141
-	 * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
142
-	 */
143
-	public function insertIfNotExist($table, $input, array $compare = null);
144
-
145
-
146
-	/**
147
-	 *
148
-	 * Insert a row if the row does not exist. Eventual conflicts during insert will be ignored.
149
-	 *
150
-	 * Implementation is not fully finished and should not be used!
151
-	 *
152
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
153
-	 * @param array $values data that should be inserted into the table  (column name => value)
154
-	 * @return int number of inserted rows
155
-	 * @since 16.0.0
156
-	 */
157
-	public function insertIgnoreConflict(string $table,array $values) : int;
158
-
159
-	/**
160
-	 * Insert or update a row value
161
-	 *
162
-	 * @param string $table
163
-	 * @param array $keys (column name => value)
164
-	 * @param array $values (column name => value)
165
-	 * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
166
-	 * @return int number of new rows
167
-	 * @throws \Doctrine\DBAL\DBALException
168
-	 * @throws PreconditionNotMetException
169
-	 * @since 9.0.0
170
-	 */
171
-	public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []);
172
-
173
-	/**
174
-	 * Create an exclusive read+write lock on a table
175
-	 *
176
-	 * Important Note: Due to the nature how locks work on different DBs, it is
177
-	 * only possible to lock one table at a time. You should also NOT start a
178
-	 * transaction while holding a lock.
179
-	 *
180
-	 * @param string $tableName
181
-	 * @since 9.1.0
182
-	 */
183
-	public function lockTable($tableName);
184
-
185
-	/**
186
-	 * Release a previous acquired lock again
187
-	 *
188
-	 * @since 9.1.0
189
-	 */
190
-	public function unlockTable();
191
-
192
-	/**
193
-	 * Start a transaction
194
-	 * @since 6.0.0
195
-	 */
196
-	public function beginTransaction();
197
-
198
-	/**
199
-	 * Check if a transaction is active
200
-	 *
201
-	 * @return bool
202
-	 * @since 8.2.0
203
-	 */
204
-	public function inTransaction();
205
-
206
-	/**
207
-	 * Commit the database changes done during a transaction that is in progress
208
-	 * @since 6.0.0
209
-	 */
210
-	public function commit();
211
-
212
-	/**
213
-	 * Rollback the database changes done during a transaction that is in progress
214
-	 * @since 6.0.0
215
-	 */
216
-	public function rollBack();
217
-
218
-	/**
219
-	 * Gets the error code and message as a string for logging
220
-	 * @return string
221
-	 * @since 6.0.0
222
-	 */
223
-	public function getError();
224
-
225
-	/**
226
-	 * Fetch the SQLSTATE associated with the last database operation.
227
-	 *
228
-	 * @return integer The last error code.
229
-	 * @since 8.0.0
230
-	 */
231
-	public function errorCode();
232
-
233
-	/**
234
-	 * Fetch extended error information associated with the last database operation.
235
-	 *
236
-	 * @return array The last error information.
237
-	 * @since 8.0.0
238
-	 */
239
-	public function errorInfo();
240
-
241
-	/**
242
-	 * Establishes the connection with the database.
243
-	 *
244
-	 * @return bool
245
-	 * @since 8.0.0
246
-	 */
247
-	public function connect();
248
-
249
-	/**
250
-	 * Close the database connection
251
-	 * @since 8.0.0
252
-	 */
253
-	public function close();
254
-
255
-	/**
256
-	 * Quotes a given input parameter.
257
-	 *
258
-	 * @param mixed $input Parameter to be quoted.
259
-	 * @param int $type Type of the parameter.
260
-	 * @return string The quoted parameter.
261
-	 * @since 8.0.0
262
-	 */
263
-	public function quote($input, $type = IQueryBuilder::PARAM_STR);
264
-
265
-	/**
266
-	 * Gets the DatabasePlatform instance that provides all the metadata about
267
-	 * the platform this driver connects to.
268
-	 *
269
-	 * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform.
270
-	 * @since 8.0.0
271
-	 */
272
-	public function getDatabasePlatform();
273
-
274
-	/**
275
-	 * Drop a table from the database if it exists
276
-	 *
277
-	 * @param string $table table name without the prefix
278
-	 * @since 8.0.0
279
-	 */
280
-	public function dropTable($table);
281
-
282
-	/**
283
-	 * Check if a table exists
284
-	 *
285
-	 * @param string $table table name without the prefix
286
-	 * @return bool
287
-	 * @since 8.0.0
288
-	 */
289
-	public function tableExists($table);
290
-
291
-	/**
292
-	 * Escape a parameter to be used in a LIKE query
293
-	 *
294
-	 * @param string $param
295
-	 * @return string
296
-	 * @since 9.0.0
297
-	 */
298
-	public function escapeLikeParameter($param);
299
-
300
-	/**
301
-	 * Check whether or not the current database support 4byte wide unicode
302
-	 *
303
-	 * @return bool
304
-	 * @since 11.0.0
305
-	 */
306
-	public function supports4ByteText();
307
-
308
-	/**
309
-	 * Create the schema of the connected database
310
-	 *
311
-	 * @return Schema
312
-	 * @since 13.0.0
313
-	 */
314
-	public function createSchema();
315
-
316
-	/**
317
-	 * Migrate the database to the given schema
318
-	 *
319
-	 * @param Schema $toSchema
320
-	 * @since 13.0.0
321
-	 */
322
-	public function migrateToSchema(Schema $toSchema);
51
+    public const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES';
52
+    public const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES';
53
+    public const ADD_MISSING_PRIMARY_KEYS_EVENT = self::class . '::ADD_MISSING_PRIMARY_KEYS';
54
+    public const CHECK_MISSING_PRIMARY_KEYS_EVENT = self::class . '::CHECK_MISSING_PRIMARY_KEYS';
55
+    public const ADD_MISSING_COLUMNS_EVENT = self::class . '::ADD_MISSING_COLUMNS';
56
+    public const CHECK_MISSING_COLUMNS_EVENT = self::class . '::CHECK_MISSING_COLUMNS';
57
+
58
+    /**
59
+     * Gets the QueryBuilder for the connection.
60
+     *
61
+     * @return \OCP\DB\QueryBuilder\IQueryBuilder
62
+     * @since 8.2.0
63
+     */
64
+    public function getQueryBuilder();
65
+
66
+    /**
67
+     * Used to abstract the ownCloud database access away
68
+     * @param string $sql the sql query with ? placeholder for params
69
+     * @param int $limit the maximum number of rows
70
+     * @param int $offset from which row we want to start
71
+     * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
72
+     * @since 6.0.0
73
+     */
74
+    public function prepare($sql, $limit = null, $offset = null);
75
+
76
+    /**
77
+     * Executes an, optionally parameterized, SQL query.
78
+     *
79
+     * If the query is parameterized, a prepared statement is used.
80
+     * If an SQLLogger is configured, the execution is logged.
81
+     *
82
+     * @param string $sql The SQL query to execute.
83
+     * @param string[] $params The parameters to bind to the query, if any.
84
+     * @param array $types The types the previous parameters are in.
85
+     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
86
+     * @since 8.0.0
87
+     */
88
+    public function executeQuery($sql, array $params = [], $types = []);
89
+
90
+    /**
91
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
92
+     * and returns the number of affected rows.
93
+     *
94
+     * This method supports PDO binding types as well as DBAL mapping types.
95
+     *
96
+     * @param string $sql The SQL query.
97
+     * @param array $params The query parameters.
98
+     * @param array $types The parameter types.
99
+     * @return integer The number of affected rows.
100
+     * @since 8.0.0
101
+     *
102
+     * @deprecated 21.0.0 use executeStatement
103
+     */
104
+    public function executeUpdate($sql, array $params = [], array $types = []);
105
+
106
+    /**
107
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
108
+     * and returns the number of affected rows.
109
+     *
110
+     * This method supports PDO binding types as well as DBAL mapping types.
111
+     *
112
+     * @param string $sql The SQL query.
113
+     * @param array $params The query parameters.
114
+     * @param array $types The parameter types.
115
+     * @return integer The number of affected rows.
116
+     * @since 21.0.0
117
+     */
118
+    public function executeStatement($sql, array $params = [], array $types = []);
119
+
120
+    /**
121
+     * Used to get the id of the just inserted element
122
+     * @param string $table the name of the table where we inserted the item
123
+     * @return int the id of the inserted element
124
+     * @since 6.0.0
125
+     */
126
+    public function lastInsertId($table = null);
127
+
128
+    /**
129
+     * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
130
+     * it is needed that there is also a unique constraint on the values. Then this method will
131
+     * catch the exception and return 0.
132
+     *
133
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
134
+     * @param array $input data that should be inserted into the table  (column name => value)
135
+     * @param array|null $compare List of values that should be checked for "if not exists"
136
+     *				If this is null or an empty array, all keys of $input will be compared
137
+     *				Please note: text fields (clob) must not be used in the compare array
138
+     * @return int number of inserted rows
139
+     * @throws \Doctrine\DBAL\DBALException
140
+     * @since 6.0.0 - parameter $compare was added in 8.1.0, return type changed from boolean in 8.1.0
141
+     * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
142
+     */
143
+    public function insertIfNotExist($table, $input, array $compare = null);
144
+
145
+
146
+    /**
147
+     *
148
+     * Insert a row if the row does not exist. Eventual conflicts during insert will be ignored.
149
+     *
150
+     * Implementation is not fully finished and should not be used!
151
+     *
152
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
153
+     * @param array $values data that should be inserted into the table  (column name => value)
154
+     * @return int number of inserted rows
155
+     * @since 16.0.0
156
+     */
157
+    public function insertIgnoreConflict(string $table,array $values) : int;
158
+
159
+    /**
160
+     * Insert or update a row value
161
+     *
162
+     * @param string $table
163
+     * @param array $keys (column name => value)
164
+     * @param array $values (column name => value)
165
+     * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
166
+     * @return int number of new rows
167
+     * @throws \Doctrine\DBAL\DBALException
168
+     * @throws PreconditionNotMetException
169
+     * @since 9.0.0
170
+     */
171
+    public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []);
172
+
173
+    /**
174
+     * Create an exclusive read+write lock on a table
175
+     *
176
+     * Important Note: Due to the nature how locks work on different DBs, it is
177
+     * only possible to lock one table at a time. You should also NOT start a
178
+     * transaction while holding a lock.
179
+     *
180
+     * @param string $tableName
181
+     * @since 9.1.0
182
+     */
183
+    public function lockTable($tableName);
184
+
185
+    /**
186
+     * Release a previous acquired lock again
187
+     *
188
+     * @since 9.1.0
189
+     */
190
+    public function unlockTable();
191
+
192
+    /**
193
+     * Start a transaction
194
+     * @since 6.0.0
195
+     */
196
+    public function beginTransaction();
197
+
198
+    /**
199
+     * Check if a transaction is active
200
+     *
201
+     * @return bool
202
+     * @since 8.2.0
203
+     */
204
+    public function inTransaction();
205
+
206
+    /**
207
+     * Commit the database changes done during a transaction that is in progress
208
+     * @since 6.0.0
209
+     */
210
+    public function commit();
211
+
212
+    /**
213
+     * Rollback the database changes done during a transaction that is in progress
214
+     * @since 6.0.0
215
+     */
216
+    public function rollBack();
217
+
218
+    /**
219
+     * Gets the error code and message as a string for logging
220
+     * @return string
221
+     * @since 6.0.0
222
+     */
223
+    public function getError();
224
+
225
+    /**
226
+     * Fetch the SQLSTATE associated with the last database operation.
227
+     *
228
+     * @return integer The last error code.
229
+     * @since 8.0.0
230
+     */
231
+    public function errorCode();
232
+
233
+    /**
234
+     * Fetch extended error information associated with the last database operation.
235
+     *
236
+     * @return array The last error information.
237
+     * @since 8.0.0
238
+     */
239
+    public function errorInfo();
240
+
241
+    /**
242
+     * Establishes the connection with the database.
243
+     *
244
+     * @return bool
245
+     * @since 8.0.0
246
+     */
247
+    public function connect();
248
+
249
+    /**
250
+     * Close the database connection
251
+     * @since 8.0.0
252
+     */
253
+    public function close();
254
+
255
+    /**
256
+     * Quotes a given input parameter.
257
+     *
258
+     * @param mixed $input Parameter to be quoted.
259
+     * @param int $type Type of the parameter.
260
+     * @return string The quoted parameter.
261
+     * @since 8.0.0
262
+     */
263
+    public function quote($input, $type = IQueryBuilder::PARAM_STR);
264
+
265
+    /**
266
+     * Gets the DatabasePlatform instance that provides all the metadata about
267
+     * the platform this driver connects to.
268
+     *
269
+     * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform.
270
+     * @since 8.0.0
271
+     */
272
+    public function getDatabasePlatform();
273
+
274
+    /**
275
+     * Drop a table from the database if it exists
276
+     *
277
+     * @param string $table table name without the prefix
278
+     * @since 8.0.0
279
+     */
280
+    public function dropTable($table);
281
+
282
+    /**
283
+     * Check if a table exists
284
+     *
285
+     * @param string $table table name without the prefix
286
+     * @return bool
287
+     * @since 8.0.0
288
+     */
289
+    public function tableExists($table);
290
+
291
+    /**
292
+     * Escape a parameter to be used in a LIKE query
293
+     *
294
+     * @param string $param
295
+     * @return string
296
+     * @since 9.0.0
297
+     */
298
+    public function escapeLikeParameter($param);
299
+
300
+    /**
301
+     * Check whether or not the current database support 4byte wide unicode
302
+     *
303
+     * @return bool
304
+     * @since 11.0.0
305
+     */
306
+    public function supports4ByteText();
307
+
308
+    /**
309
+     * Create the schema of the connected database
310
+     *
311
+     * @return Schema
312
+     * @since 13.0.0
313
+     */
314
+    public function createSchema();
315
+
316
+    /**
317
+     * Migrate the database to the given schema
318
+     *
319
+     * @param Schema $toSchema
320
+     * @since 13.0.0
321
+     */
322
+    public function migrateToSchema(Schema $toSchema);
323 323
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -48,12 +48,12 @@  discard block
 block discarded – undo
48 48
  * @since 6.0.0
49 49
  */
50 50
 interface IDBConnection {
51
-	public const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES';
52
-	public const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES';
53
-	public const ADD_MISSING_PRIMARY_KEYS_EVENT = self::class . '::ADD_MISSING_PRIMARY_KEYS';
54
-	public const CHECK_MISSING_PRIMARY_KEYS_EVENT = self::class . '::CHECK_MISSING_PRIMARY_KEYS';
55
-	public const ADD_MISSING_COLUMNS_EVENT = self::class . '::ADD_MISSING_COLUMNS';
56
-	public const CHECK_MISSING_COLUMNS_EVENT = self::class . '::CHECK_MISSING_COLUMNS';
51
+	public const ADD_MISSING_INDEXES_EVENT = self::class.'::ADD_MISSING_INDEXES';
52
+	public const CHECK_MISSING_INDEXES_EVENT = self::class.'::CHECK_MISSING_INDEXES';
53
+	public const ADD_MISSING_PRIMARY_KEYS_EVENT = self::class.'::ADD_MISSING_PRIMARY_KEYS';
54
+	public const CHECK_MISSING_PRIMARY_KEYS_EVENT = self::class.'::CHECK_MISSING_PRIMARY_KEYS';
55
+	public const ADD_MISSING_COLUMNS_EVENT = self::class.'::ADD_MISSING_COLUMNS';
56
+	public const CHECK_MISSING_COLUMNS_EVENT = self::class.'::CHECK_MISSING_COLUMNS';
57 57
 
58 58
 	/**
59 59
 	 * Gets the QueryBuilder for the connection.
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 	 * @return int number of inserted rows
155 155
 	 * @since 16.0.0
156 156
 	 */
157
-	public function insertIgnoreConflict(string $table,array $values) : int;
157
+	public function insertIgnoreConflict(string $table, array $values) : int;
158 158
 
159 159
 	/**
160 160
 	 * Insert or update a row value
Please login to merge, or discard this patch.