Completed
Pull Request — master (#3787)
by Robin
14:18
created
apps/user_ldap/lib/Access.php 2 patches
Indentation   +1768 added lines, -1768 removed lines patch added patch discarded remove patch
@@ -52,1527 +52,1527 @@  discard block
 block discarded – undo
52 52
  * @package OCA\User_LDAP
53 53
  */
54 54
 class Access extends LDAPUtility implements IUserTools {
55
-	/**
56
-	 * @var \OCA\User_LDAP\Connection
57
-	 */
58
-	public $connection;
59
-	/** @var Manager */
60
-	public $userManager;
61
-	//never ever check this var directly, always use getPagedSearchResultState
62
-	protected $pagedSearchedSuccessful;
63
-
64
-	/**
65
-	 * @var string[] $cookies an array of returned Paged Result cookies
66
-	 */
67
-	protected $cookies = array();
68
-
69
-	/**
70
-	 * @var string $lastCookie the last cookie returned from a Paged Results
71
-	 * operation, defaults to an empty string
72
-	 */
73
-	protected $lastCookie = '';
74
-
75
-	/**
76
-	 * @var AbstractMapping $userMapper
77
-	 */
78
-	protected $userMapper;
79
-
80
-	/**
81
-	* @var AbstractMapping $userMapper
82
-	*/
83
-	protected $groupMapper;
55
+    /**
56
+     * @var \OCA\User_LDAP\Connection
57
+     */
58
+    public $connection;
59
+    /** @var Manager */
60
+    public $userManager;
61
+    //never ever check this var directly, always use getPagedSearchResultState
62
+    protected $pagedSearchedSuccessful;
63
+
64
+    /**
65
+     * @var string[] $cookies an array of returned Paged Result cookies
66
+     */
67
+    protected $cookies = array();
68
+
69
+    /**
70
+     * @var string $lastCookie the last cookie returned from a Paged Results
71
+     * operation, defaults to an empty string
72
+     */
73
+    protected $lastCookie = '';
74
+
75
+    /**
76
+     * @var AbstractMapping $userMapper
77
+     */
78
+    protected $userMapper;
79
+
80
+    /**
81
+     * @var AbstractMapping $userMapper
82
+     */
83
+    protected $groupMapper;
84 84
 	
85
-	/**
86
-	 * @var \OCA\User_LDAP\Helper
87
-	 */
88
-	private $helper;
89
-
90
-	public function __construct(Connection $connection, ILDAPWrapper $ldap,
91
-		Manager $userManager, Helper $helper) {
92
-		parent::__construct($ldap);
93
-		$this->connection = $connection;
94
-		$this->userManager = $userManager;
95
-		$this->userManager->setLdapAccess($this);
96
-		$this->helper = $helper;
97
-	}
98
-
99
-	/**
100
-	 * sets the User Mapper
101
-	 * @param AbstractMapping $mapper
102
-	 */
103
-	public function setUserMapper(AbstractMapping $mapper) {
104
-		$this->userMapper = $mapper;
105
-	}
106
-
107
-	/**
108
-	 * returns the User Mapper
109
-	 * @throws \Exception
110
-	 * @return AbstractMapping
111
-	 */
112
-	public function getUserMapper() {
113
-		if(is_null($this->userMapper)) {
114
-			throw new \Exception('UserMapper was not assigned to this Access instance.');
115
-		}
116
-		return $this->userMapper;
117
-	}
118
-
119
-	/**
120
-	 * sets the Group Mapper
121
-	 * @param AbstractMapping $mapper
122
-	 */
123
-	public function setGroupMapper(AbstractMapping $mapper) {
124
-		$this->groupMapper = $mapper;
125
-	}
126
-
127
-	/**
128
-	 * returns the Group Mapper
129
-	 * @throws \Exception
130
-	 * @return AbstractMapping
131
-	 */
132
-	public function getGroupMapper() {
133
-		if(is_null($this->groupMapper)) {
134
-			throw new \Exception('GroupMapper was not assigned to this Access instance.');
135
-		}
136
-		return $this->groupMapper;
137
-	}
138
-
139
-	/**
140
-	 * @return bool
141
-	 */
142
-	private function checkConnection() {
143
-		return ($this->connection instanceof Connection);
144
-	}
145
-
146
-	/**
147
-	 * returns the Connection instance
148
-	 * @return \OCA\User_LDAP\Connection
149
-	 */
150
-	public function getConnection() {
151
-		return $this->connection;
152
-	}
153
-
154
-	/**
155
-	 * reads a given attribute for an LDAP record identified by a DN
156
-	 * @param string $dn the record in question
157
-	 * @param string $attr the attribute that shall be retrieved
158
-	 *        if empty, just check the record's existence
159
-	 * @param string $filter
160
-	 * @return array|false an array of values on success or an empty
161
-	 *          array if $attr is empty, false otherwise
162
-	 */
163
-	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
164
-		if(!$this->checkConnection()) {
165
-			\OCP\Util::writeLog('user_ldap',
166
-				'No LDAP Connector assigned, access impossible for readAttribute.',
167
-				\OCP\Util::WARN);
168
-			return false;
169
-		}
170
-		$cr = $this->connection->getConnectionResource();
171
-		if(!$this->ldap->isResource($cr)) {
172
-			//LDAP not available
173
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
174
-			return false;
175
-		}
176
-		//Cancel possibly running Paged Results operation, otherwise we run in
177
-		//LDAP protocol errors
178
-		$this->abandonPagedSearch();
179
-		// openLDAP requires that we init a new Paged Search. Not needed by AD,
180
-		// but does not hurt either.
181
-		$pagingSize = intval($this->connection->ldapPagingSize);
182
-		// 0 won't result in replies, small numbers may leave out groups
183
-		// (cf. #12306), 500 is default for paging and should work everywhere.
184
-		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
185
-		$attr = mb_strtolower($attr, 'UTF-8');
186
-		// the actual read attribute later may contain parameters on a ranged
187
-		// request, e.g. member;range=99-199. Depends on server reply.
188
-		$attrToRead = $attr;
189
-
190
-		$values = [];
191
-		$isRangeRequest = false;
192
-		do {
193
-			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
194
-			if(is_bool($result)) {
195
-				// when an exists request was run and it was successful, an empty
196
-				// array must be returned
197
-				return $result ? [] : false;
198
-			}
199
-
200
-			if (!$isRangeRequest) {
201
-				$values = $this->extractAttributeValuesFromResult($result, $attr);
202
-				if (!empty($values)) {
203
-					return $values;
204
-				}
205
-			}
206
-
207
-			$isRangeRequest = false;
208
-			$result = $this->extractRangeData($result, $attr);
209
-			if (!empty($result)) {
210
-				$normalizedResult = $this->extractAttributeValuesFromResult(
211
-					[ $attr => $result['values'] ],
212
-					$attr
213
-				);
214
-				$values = array_merge($values, $normalizedResult);
215
-
216
-				if($result['rangeHigh'] === '*') {
217
-					// when server replies with * as high range value, there are
218
-					// no more results left
219
-					return $values;
220
-				} else {
221
-					$low  = $result['rangeHigh'] + 1;
222
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
223
-					$isRangeRequest = true;
224
-				}
225
-			}
226
-		} while($isRangeRequest);
227
-
228
-		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
229
-		return false;
230
-	}
231
-
232
-	/**
233
-	 * Runs an read operation against LDAP
234
-	 *
235
-	 * @param resource $cr the LDAP connection
236
-	 * @param string $dn
237
-	 * @param string $attribute
238
-	 * @param string $filter
239
-	 * @param int $maxResults
240
-	 * @return array|bool false if there was any error, true if an exists check
241
-	 *                    was performed and the requested DN found, array with the
242
-	 *                    returned data on a successful usual operation
243
-	 */
244
-	public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
245
-		$this->initPagedSearch($filter, array($dn), array($attribute), $maxResults, 0);
246
-		$dn = $this->helper->DNasBaseParameter($dn);
247
-		$rr = @$this->ldap->read($cr, $dn, $filter, array($attribute));
248
-		if (!$this->ldap->isResource($rr)) {
249
-			if ($attribute !== '') {
250
-				//do not throw this message on userExists check, irritates
251
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG);
252
-			}
253
-			//in case an error occurs , e.g. object does not exist
254
-			return false;
255
-		}
256
-		if ($attribute === '' && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
257
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG);
258
-			return true;
259
-		}
260
-		$er = $this->ldap->firstEntry($cr, $rr);
261
-		if (!$this->ldap->isResource($er)) {
262
-			//did not match the filter, return false
263
-			return false;
264
-		}
265
-		//LDAP attributes are not case sensitive
266
-		$result = \OCP\Util::mb_array_change_key_case(
267
-			$this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
268
-
269
-		return $result;
270
-	}
271
-
272
-	/**
273
-	 * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
274
-	 * data if present.
275
-	 *
276
-	 * @param array $result from ILDAPWrapper::getAttributes()
277
-	 * @param string $attribute the attribute name that was read
278
-	 * @return string[]
279
-	 */
280
-	public function extractAttributeValuesFromResult($result, $attribute) {
281
-		$values = [];
282
-		if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
283
-			$lowercaseAttribute = strtolower($attribute);
284
-			for($i=0;$i<$result[$attribute]['count'];$i++) {
285
-				if($this->resemblesDN($attribute)) {
286
-					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
287
-				} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
288
-					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
289
-				} else {
290
-					$values[] = $result[$attribute][$i];
291
-				}
292
-			}
293
-		}
294
-		return $values;
295
-	}
296
-
297
-	/**
298
-	 * Attempts to find ranged data in a getAttribute results and extracts the
299
-	 * returned values as well as information on the range and full attribute
300
-	 * name for further processing.
301
-	 *
302
-	 * @param array $result from ILDAPWrapper::getAttributes()
303
-	 * @param string $attribute the attribute name that was read. Without ";range=…"
304
-	 * @return array If a range was detected with keys 'values', 'attributeName',
305
-	 *               'attributeFull' and 'rangeHigh', otherwise empty.
306
-	 */
307
-	public function extractRangeData($result, $attribute) {
308
-		$keys = array_keys($result);
309
-		foreach($keys as $key) {
310
-			if($key !== $attribute && strpos($key, $attribute) === 0) {
311
-				$queryData = explode(';', $key);
312
-				if(strpos($queryData[1], 'range=') === 0) {
313
-					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
314
-					$data = [
315
-						'values' => $result[$key],
316
-						'attributeName' => $queryData[0],
317
-						'attributeFull' => $key,
318
-						'rangeHigh' => $high,
319
-					];
320
-					return $data;
321
-				}
322
-			}
323
-		}
324
-		return [];
325
-	}
85
+    /**
86
+     * @var \OCA\User_LDAP\Helper
87
+     */
88
+    private $helper;
89
+
90
+    public function __construct(Connection $connection, ILDAPWrapper $ldap,
91
+        Manager $userManager, Helper $helper) {
92
+        parent::__construct($ldap);
93
+        $this->connection = $connection;
94
+        $this->userManager = $userManager;
95
+        $this->userManager->setLdapAccess($this);
96
+        $this->helper = $helper;
97
+    }
98
+
99
+    /**
100
+     * sets the User Mapper
101
+     * @param AbstractMapping $mapper
102
+     */
103
+    public function setUserMapper(AbstractMapping $mapper) {
104
+        $this->userMapper = $mapper;
105
+    }
106
+
107
+    /**
108
+     * returns the User Mapper
109
+     * @throws \Exception
110
+     * @return AbstractMapping
111
+     */
112
+    public function getUserMapper() {
113
+        if(is_null($this->userMapper)) {
114
+            throw new \Exception('UserMapper was not assigned to this Access instance.');
115
+        }
116
+        return $this->userMapper;
117
+    }
118
+
119
+    /**
120
+     * sets the Group Mapper
121
+     * @param AbstractMapping $mapper
122
+     */
123
+    public function setGroupMapper(AbstractMapping $mapper) {
124
+        $this->groupMapper = $mapper;
125
+    }
126
+
127
+    /**
128
+     * returns the Group Mapper
129
+     * @throws \Exception
130
+     * @return AbstractMapping
131
+     */
132
+    public function getGroupMapper() {
133
+        if(is_null($this->groupMapper)) {
134
+            throw new \Exception('GroupMapper was not assigned to this Access instance.');
135
+        }
136
+        return $this->groupMapper;
137
+    }
138
+
139
+    /**
140
+     * @return bool
141
+     */
142
+    private function checkConnection() {
143
+        return ($this->connection instanceof Connection);
144
+    }
145
+
146
+    /**
147
+     * returns the Connection instance
148
+     * @return \OCA\User_LDAP\Connection
149
+     */
150
+    public function getConnection() {
151
+        return $this->connection;
152
+    }
153
+
154
+    /**
155
+     * reads a given attribute for an LDAP record identified by a DN
156
+     * @param string $dn the record in question
157
+     * @param string $attr the attribute that shall be retrieved
158
+     *        if empty, just check the record's existence
159
+     * @param string $filter
160
+     * @return array|false an array of values on success or an empty
161
+     *          array if $attr is empty, false otherwise
162
+     */
163
+    public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
164
+        if(!$this->checkConnection()) {
165
+            \OCP\Util::writeLog('user_ldap',
166
+                'No LDAP Connector assigned, access impossible for readAttribute.',
167
+                \OCP\Util::WARN);
168
+            return false;
169
+        }
170
+        $cr = $this->connection->getConnectionResource();
171
+        if(!$this->ldap->isResource($cr)) {
172
+            //LDAP not available
173
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
174
+            return false;
175
+        }
176
+        //Cancel possibly running Paged Results operation, otherwise we run in
177
+        //LDAP protocol errors
178
+        $this->abandonPagedSearch();
179
+        // openLDAP requires that we init a new Paged Search. Not needed by AD,
180
+        // but does not hurt either.
181
+        $pagingSize = intval($this->connection->ldapPagingSize);
182
+        // 0 won't result in replies, small numbers may leave out groups
183
+        // (cf. #12306), 500 is default for paging and should work everywhere.
184
+        $maxResults = $pagingSize > 20 ? $pagingSize : 500;
185
+        $attr = mb_strtolower($attr, 'UTF-8');
186
+        // the actual read attribute later may contain parameters on a ranged
187
+        // request, e.g. member;range=99-199. Depends on server reply.
188
+        $attrToRead = $attr;
189
+
190
+        $values = [];
191
+        $isRangeRequest = false;
192
+        do {
193
+            $result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
194
+            if(is_bool($result)) {
195
+                // when an exists request was run and it was successful, an empty
196
+                // array must be returned
197
+                return $result ? [] : false;
198
+            }
199
+
200
+            if (!$isRangeRequest) {
201
+                $values = $this->extractAttributeValuesFromResult($result, $attr);
202
+                if (!empty($values)) {
203
+                    return $values;
204
+                }
205
+            }
206
+
207
+            $isRangeRequest = false;
208
+            $result = $this->extractRangeData($result, $attr);
209
+            if (!empty($result)) {
210
+                $normalizedResult = $this->extractAttributeValuesFromResult(
211
+                    [ $attr => $result['values'] ],
212
+                    $attr
213
+                );
214
+                $values = array_merge($values, $normalizedResult);
215
+
216
+                if($result['rangeHigh'] === '*') {
217
+                    // when server replies with * as high range value, there are
218
+                    // no more results left
219
+                    return $values;
220
+                } else {
221
+                    $low  = $result['rangeHigh'] + 1;
222
+                    $attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
223
+                    $isRangeRequest = true;
224
+                }
225
+            }
226
+        } while($isRangeRequest);
227
+
228
+        \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
229
+        return false;
230
+    }
231
+
232
+    /**
233
+     * Runs an read operation against LDAP
234
+     *
235
+     * @param resource $cr the LDAP connection
236
+     * @param string $dn
237
+     * @param string $attribute
238
+     * @param string $filter
239
+     * @param int $maxResults
240
+     * @return array|bool false if there was any error, true if an exists check
241
+     *                    was performed and the requested DN found, array with the
242
+     *                    returned data on a successful usual operation
243
+     */
244
+    public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
245
+        $this->initPagedSearch($filter, array($dn), array($attribute), $maxResults, 0);
246
+        $dn = $this->helper->DNasBaseParameter($dn);
247
+        $rr = @$this->ldap->read($cr, $dn, $filter, array($attribute));
248
+        if (!$this->ldap->isResource($rr)) {
249
+            if ($attribute !== '') {
250
+                //do not throw this message on userExists check, irritates
251
+                \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG);
252
+            }
253
+            //in case an error occurs , e.g. object does not exist
254
+            return false;
255
+        }
256
+        if ($attribute === '' && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
257
+            \OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG);
258
+            return true;
259
+        }
260
+        $er = $this->ldap->firstEntry($cr, $rr);
261
+        if (!$this->ldap->isResource($er)) {
262
+            //did not match the filter, return false
263
+            return false;
264
+        }
265
+        //LDAP attributes are not case sensitive
266
+        $result = \OCP\Util::mb_array_change_key_case(
267
+            $this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
268
+
269
+        return $result;
270
+    }
271
+
272
+    /**
273
+     * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
274
+     * data if present.
275
+     *
276
+     * @param array $result from ILDAPWrapper::getAttributes()
277
+     * @param string $attribute the attribute name that was read
278
+     * @return string[]
279
+     */
280
+    public function extractAttributeValuesFromResult($result, $attribute) {
281
+        $values = [];
282
+        if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
283
+            $lowercaseAttribute = strtolower($attribute);
284
+            for($i=0;$i<$result[$attribute]['count'];$i++) {
285
+                if($this->resemblesDN($attribute)) {
286
+                    $values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
287
+                } elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
288
+                    $values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
289
+                } else {
290
+                    $values[] = $result[$attribute][$i];
291
+                }
292
+            }
293
+        }
294
+        return $values;
295
+    }
296
+
297
+    /**
298
+     * Attempts to find ranged data in a getAttribute results and extracts the
299
+     * returned values as well as information on the range and full attribute
300
+     * name for further processing.
301
+     *
302
+     * @param array $result from ILDAPWrapper::getAttributes()
303
+     * @param string $attribute the attribute name that was read. Without ";range=…"
304
+     * @return array If a range was detected with keys 'values', 'attributeName',
305
+     *               'attributeFull' and 'rangeHigh', otherwise empty.
306
+     */
307
+    public function extractRangeData($result, $attribute) {
308
+        $keys = array_keys($result);
309
+        foreach($keys as $key) {
310
+            if($key !== $attribute && strpos($key, $attribute) === 0) {
311
+                $queryData = explode(';', $key);
312
+                if(strpos($queryData[1], 'range=') === 0) {
313
+                    $high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
314
+                    $data = [
315
+                        'values' => $result[$key],
316
+                        'attributeName' => $queryData[0],
317
+                        'attributeFull' => $key,
318
+                        'rangeHigh' => $high,
319
+                    ];
320
+                    return $data;
321
+                }
322
+            }
323
+        }
324
+        return [];
325
+    }
326 326
 	
327
-	/**
328
-	 * Set password for an LDAP user identified by a DN
329
-	 *
330
-	 * @param string $userDN the user in question
331
-	 * @param string $password the new password
332
-	 * @return bool
333
-	 * @throws HintException
334
-	 * @throws \Exception
335
-	 */
336
-	public function setPassword($userDN, $password) {
337
-		if(intval($this->connection->turnOnPasswordChange) !== 1) {
338
-			throw new \Exception('LDAP password changes are disabled.');
339
-		}
340
-		$cr = $this->connection->getConnectionResource();
341
-		if(!$this->ldap->isResource($cr)) {
342
-			//LDAP not available
343
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
344
-			return false;
345
-		}
327
+    /**
328
+     * Set password for an LDAP user identified by a DN
329
+     *
330
+     * @param string $userDN the user in question
331
+     * @param string $password the new password
332
+     * @return bool
333
+     * @throws HintException
334
+     * @throws \Exception
335
+     */
336
+    public function setPassword($userDN, $password) {
337
+        if(intval($this->connection->turnOnPasswordChange) !== 1) {
338
+            throw new \Exception('LDAP password changes are disabled.');
339
+        }
340
+        $cr = $this->connection->getConnectionResource();
341
+        if(!$this->ldap->isResource($cr)) {
342
+            //LDAP not available
343
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
344
+            return false;
345
+        }
346 346
 		
347
-		try {
348
-			return $this->ldap->modReplace($cr, $userDN, $password);
349
-		} catch(ConstraintViolationException $e) {
350
-			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
351
-		}
352
-	}
353
-
354
-	/**
355
-	 * checks whether the given attributes value is probably a DN
356
-	 * @param string $attr the attribute in question
357
-	 * @return boolean if so true, otherwise false
358
-	 */
359
-	private function resemblesDN($attr) {
360
-		$resemblingAttributes = array(
361
-			'dn',
362
-			'uniquemember',
363
-			'member',
364
-			// memberOf is an "operational" attribute, without a definition in any RFC
365
-			'memberof'
366
-		);
367
-		return in_array($attr, $resemblingAttributes);
368
-	}
369
-
370
-	/**
371
-	 * checks whether the given string is probably a DN
372
-	 * @param string $string
373
-	 * @return boolean
374
-	 */
375
-	public function stringResemblesDN($string) {
376
-		$r = $this->ldap->explodeDN($string, 0);
377
-		// if exploding a DN succeeds and does not end up in
378
-		// an empty array except for $r[count] being 0.
379
-		return (is_array($r) && count($r) > 1);
380
-	}
381
-
382
-	/**
383
-	 * returns a DN-string that is cleaned from not domain parts, e.g.
384
-	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
385
-	 * becomes dc=foobar,dc=server,dc=org
386
-	 * @param string $dn
387
-	 * @return string
388
-	 */
389
-	public function getDomainDNFromDN($dn) {
390
-		$allParts = $this->ldap->explodeDN($dn, 0);
391
-		if($allParts === false) {
392
-			//not a valid DN
393
-			return '';
394
-		}
395
-		$domainParts = array();
396
-		$dcFound = false;
397
-		foreach($allParts as $part) {
398
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
399
-				$dcFound = true;
400
-			}
401
-			if($dcFound) {
402
-				$domainParts[] = $part;
403
-			}
404
-		}
405
-		$domainDN = implode(',', $domainParts);
406
-		return $domainDN;
407
-	}
408
-
409
-	/**
410
-	 * returns the LDAP DN for the given internal ownCloud name of the group
411
-	 * @param string $name the ownCloud name in question
412
-	 * @return string|false LDAP DN on success, otherwise false
413
-	 */
414
-	public function groupname2dn($name) {
415
-		return $this->groupMapper->getDNByName($name);
416
-	}
417
-
418
-	/**
419
-	 * returns the LDAP DN for the given internal ownCloud name of the user
420
-	 * @param string $name the ownCloud name in question
421
-	 * @return string|false with the LDAP DN on success, otherwise false
422
-	 */
423
-	public function username2dn($name) {
424
-		$fdn = $this->userMapper->getDNByName($name);
425
-
426
-		//Check whether the DN belongs to the Base, to avoid issues on multi-
427
-		//server setups
428
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
429
-			return $fdn;
430
-		}
431
-
432
-		return false;
433
-	}
434
-
435
-	/**
436
-	 * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
437
-	 * @param string $fdn the dn of the group object
438
-	 * @param string $ldapName optional, the display name of the object
439
-	 * @return string|false with the name to use in ownCloud, false on DN outside of search DN
440
-	 */
441
-	public function dn2groupname($fdn, $ldapName = null) {
442
-		//To avoid bypassing the base DN settings under certain circumstances
443
-		//with the group support, check whether the provided DN matches one of
444
-		//the given Bases
445
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
446
-			return false;
447
-		}
448
-
449
-		return $this->dn2ocname($fdn, $ldapName, false);
450
-	}
451
-
452
-	/**
453
-	 * accepts an array of group DNs and tests whether they match the user
454
-	 * filter by doing read operations against the group entries. Returns an
455
-	 * array of DNs that match the filter.
456
-	 *
457
-	 * @param string[] $groupDNs
458
-	 * @return string[]
459
-	 */
460
-	public function groupsMatchFilter($groupDNs) {
461
-		$validGroupDNs = [];
462
-		foreach($groupDNs as $dn) {
463
-			$cacheKey = 'groupsMatchFilter-'.$dn;
464
-			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
465
-			if(!is_null($groupMatchFilter)) {
466
-				if($groupMatchFilter) {
467
-					$validGroupDNs[] = $dn;
468
-				}
469
-				continue;
470
-			}
471
-
472
-			// Check the base DN first. If this is not met already, we don't
473
-			// need to ask the server at all.
474
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
475
-				$this->connection->writeToCache($cacheKey, false);
476
-				continue;
477
-			}
478
-
479
-			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
480
-			if(is_array($result)) {
481
-				$this->connection->writeToCache($cacheKey, true);
482
-				$validGroupDNs[] = $dn;
483
-			} else {
484
-				$this->connection->writeToCache($cacheKey, false);
485
-			}
486
-
487
-		}
488
-		return $validGroupDNs;
489
-	}
490
-
491
-	/**
492
-	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
493
-	 * @param string $dn the dn of the user object
494
-	 * @param string $ldapName optional, the display name of the object
495
-	 * @return string|false with with the name to use in ownCloud
496
-	 */
497
-	public function dn2username($fdn, $ldapName = null) {
498
-		//To avoid bypassing the base DN settings under certain circumstances
499
-		//with the group support, check whether the provided DN matches one of
500
-		//the given Bases
501
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
502
-			return false;
503
-		}
504
-
505
-		return $this->dn2ocname($fdn, $ldapName, true);
506
-	}
507
-
508
-	/**
509
-	 * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
510
-	 * @param string $dn the dn of the user object
511
-	 * @param string $ldapName optional, the display name of the object
512
-	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
513
-	 * @return string|false with with the name to use in ownCloud
514
-	 */
515
-	public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
516
-		if($isUser) {
517
-			$mapper = $this->getUserMapper();
518
-			$nameAttribute = $this->connection->ldapUserDisplayName;
519
-		} else {
520
-			$mapper = $this->getGroupMapper();
521
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
522
-		}
523
-
524
-		//let's try to retrieve the ownCloud name from the mappings table
525
-		$ocName = $mapper->getNameByDN($fdn);
526
-		if(is_string($ocName)) {
527
-			return $ocName;
528
-		}
529
-
530
-		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
531
-		$uuid = $this->getUUID($fdn, $isUser);
532
-		if(is_string($uuid)) {
533
-			$ocName = $mapper->getNameByUUID($uuid);
534
-			if(is_string($ocName)) {
535
-				$mapper->setDNbyUUID($fdn, $uuid);
536
-				return $ocName;
537
-			}
538
-		} else {
539
-			//If the UUID can't be detected something is foul.
540
-			\OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
541
-			return false;
542
-		}
543
-
544
-		if(is_null($ldapName)) {
545
-			$ldapName = $this->readAttribute($fdn, $nameAttribute);
546
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
547
-				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
548
-				return false;
549
-			}
550
-			$ldapName = $ldapName[0];
551
-		}
552
-
553
-		if($isUser) {
554
-			$usernameAttribute = strval($this->connection->ldapExpertUsernameAttr);
555
-			if ($usernameAttribute !== '') {
556
-				$username = $this->readAttribute($fdn, $usernameAttribute);
557
-				$username = $username[0];
558
-			} else {
559
-				$username = $uuid;
560
-			}
561
-			$intName = $this->sanitizeUsername($username);
562
-		} else {
563
-			$intName = $ldapName;
564
-		}
565
-
566
-		//a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
567
-		//disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
568
-		//NOTE: mind, disabling cache affects only this instance! Using it
569
-		// outside of core user management will still cache the user as non-existing.
570
-		$originalTTL = $this->connection->ldapCacheTTL;
571
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
572
-		if(($isUser && !\OCP\User::userExists($intName))
573
-			|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) {
574
-			if($mapper->map($fdn, $intName, $uuid)) {
575
-				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
576
-				return $intName;
577
-			}
578
-		}
579
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
580
-
581
-		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
582
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
583
-			return $altName;
584
-		}
585
-
586
-		//if everything else did not help..
587
-		\OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
588
-		return false;
589
-	}
590
-
591
-	/**
592
-	 * gives back the user names as they are used ownClod internally
593
-	 * @param array $ldapUsers as returned by fetchList()
594
-	 * @return array an array with the user names to use in ownCloud
595
-	 *
596
-	 * gives back the user names as they are used ownClod internally
597
-	 */
598
-	public function ownCloudUserNames($ldapUsers) {
599
-		return $this->ldap2ownCloudNames($ldapUsers, true);
600
-	}
601
-
602
-	/**
603
-	 * gives back the group names as they are used ownClod internally
604
-	 * @param array $ldapGroups as returned by fetchList()
605
-	 * @return array an array with the group names to use in ownCloud
606
-	 *
607
-	 * gives back the group names as they are used ownClod internally
608
-	 */
609
-	public function ownCloudGroupNames($ldapGroups) {
610
-		return $this->ldap2ownCloudNames($ldapGroups, false);
611
-	}
612
-
613
-	/**
614
-	 * @param array $ldapObjects as returned by fetchList()
615
-	 * @param bool $isUsers
616
-	 * @return array
617
-	 */
618
-	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
619
-		if($isUsers) {
620
-			$nameAttribute = $this->connection->ldapUserDisplayName;
621
-			$sndAttribute  = $this->connection->ldapUserDisplayName2;
622
-		} else {
623
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
624
-		}
625
-		$ownCloudNames = array();
626
-
627
-		foreach($ldapObjects as $ldapObject) {
628
-			$nameByLDAP = null;
629
-			if(    isset($ldapObject[$nameAttribute])
630
-				&& is_array($ldapObject[$nameAttribute])
631
-				&& isset($ldapObject[$nameAttribute][0])
632
-			) {
633
-				// might be set, but not necessarily. if so, we use it.
634
-				$nameByLDAP = $ldapObject[$nameAttribute][0];
635
-			}
636
-
637
-			$ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
638
-			if($ocName) {
639
-				$ownCloudNames[] = $ocName;
640
-				if($isUsers) {
641
-					//cache the user names so it does not need to be retrieved
642
-					//again later (e.g. sharing dialogue).
643
-					if(is_null($nameByLDAP)) {
644
-						continue;
645
-					}
646
-					$sndName = isset($ldapObject[$sndAttribute][0])
647
-						? $ldapObject[$sndAttribute][0] : '';
648
-					$this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
649
-				}
650
-			}
651
-		}
652
-		return $ownCloudNames;
653
-	}
654
-
655
-	/**
656
-	 * caches the user display name
657
-	 * @param string $ocName the internal ownCloud username
658
-	 * @param string|false $home the home directory path
659
-	 */
660
-	public function cacheUserHome($ocName, $home) {
661
-		$cacheKey = 'getHome'.$ocName;
662
-		$this->connection->writeToCache($cacheKey, $home);
663
-	}
664
-
665
-	/**
666
-	 * caches a user as existing
667
-	 * @param string $ocName the internal ownCloud username
668
-	 */
669
-	public function cacheUserExists($ocName) {
670
-		$this->connection->writeToCache('userExists'.$ocName, true);
671
-	}
672
-
673
-	/**
674
-	 * caches the user display name
675
-	 * @param string $ocName the internal ownCloud username
676
-	 * @param string $displayName the display name
677
-	 * @param string $displayName2 the second display name
678
-	 */
679
-	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
680
-		$user = $this->userManager->get($ocName);
681
-		if($user === null) {
682
-			return;
683
-		}
684
-		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
685
-		$cacheKeyTrunk = 'getDisplayName';
686
-		$this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
687
-	}
688
-
689
-	/**
690
-	 * creates a unique name for internal ownCloud use for users. Don't call it directly.
691
-	 * @param string $name the display name of the object
692
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful
693
-	 *
694
-	 * Instead of using this method directly, call
695
-	 * createAltInternalOwnCloudName($name, true)
696
-	 */
697
-	private function _createAltInternalOwnCloudNameForUsers($name) {
698
-		$attempts = 0;
699
-		//while loop is just a precaution. If a name is not generated within
700
-		//20 attempts, something else is very wrong. Avoids infinite loop.
701
-		while($attempts < 20){
702
-			$altName = $name . '_' . rand(1000,9999);
703
-			if(!\OCP\User::userExists($altName)) {
704
-				return $altName;
705
-			}
706
-			$attempts++;
707
-		}
708
-		return false;
709
-	}
710
-
711
-	/**
712
-	 * creates a unique name for internal ownCloud use for groups. Don't call it directly.
713
-	 * @param string $name the display name of the object
714
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful.
715
-	 *
716
-	 * Instead of using this method directly, call
717
-	 * createAltInternalOwnCloudName($name, false)
718
-	 *
719
-	 * Group names are also used as display names, so we do a sequential
720
-	 * numbering, e.g. Developers_42 when there are 41 other groups called
721
-	 * "Developers"
722
-	 */
723
-	private function _createAltInternalOwnCloudNameForGroups($name) {
724
-		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
725
-		if(!($usedNames) || count($usedNames) === 0) {
726
-			$lastNo = 1; //will become name_2
727
-		} else {
728
-			natsort($usedNames);
729
-			$lastName = array_pop($usedNames);
730
-			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
731
-		}
732
-		$altName = $name.'_'.strval($lastNo+1);
733
-		unset($usedNames);
734
-
735
-		$attempts = 1;
736
-		while($attempts < 21){
737
-			// Check to be really sure it is unique
738
-			// while loop is just a precaution. If a name is not generated within
739
-			// 20 attempts, something else is very wrong. Avoids infinite loop.
740
-			if(!\OC::$server->getGroupManager()->groupExists($altName)) {
741
-				return $altName;
742
-			}
743
-			$altName = $name . '_' . ($lastNo + $attempts);
744
-			$attempts++;
745
-		}
746
-		return false;
747
-	}
748
-
749
-	/**
750
-	 * creates a unique name for internal ownCloud use.
751
-	 * @param string $name the display name of the object
752
-	 * @param boolean $isUser whether name should be created for a user (true) or a group (false)
753
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful
754
-	 */
755
-	private function createAltInternalOwnCloudName($name, $isUser) {
756
-		$originalTTL = $this->connection->ldapCacheTTL;
757
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
758
-		if($isUser) {
759
-			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
760
-		} else {
761
-			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
762
-		}
763
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
764
-
765
-		return $altName;
766
-	}
767
-
768
-	/**
769
-	 * fetches a list of users according to a provided loginName and utilizing
770
-	 * the login filter.
771
-	 *
772
-	 * @param string $loginName
773
-	 * @param array $attributes optional, list of attributes to read
774
-	 * @return array
775
-	 */
776
-	public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
777
-		$loginName = $this->escapeFilterPart($loginName);
778
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
779
-		$users = $this->fetchListOfUsers($filter, $attributes);
780
-		return $users;
781
-	}
782
-
783
-	/**
784
-	 * counts the number of users according to a provided loginName and
785
-	 * utilizing the login filter.
786
-	 *
787
-	 * @param string $loginName
788
-	 * @return array
789
-	 */
790
-	public function countUsersByLoginName($loginName) {
791
-		$loginName = $this->escapeFilterPart($loginName);
792
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
793
-		$users = $this->countUsers($filter);
794
-		return $users;
795
-	}
796
-
797
-	/**
798
-	 * @param string $filter
799
-	 * @param string|string[] $attr
800
-	 * @param int $limit
801
-	 * @param int $offset
802
-	 * @return array
803
-	 */
804
-	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
805
-		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
806
-		$this->batchApplyUserAttributes($ldapRecords);
807
-		return $this->fetchList($ldapRecords, (count($attr) > 1));
808
-	}
809
-
810
-	/**
811
-	 * provided with an array of LDAP user records the method will fetch the
812
-	 * user object and requests it to process the freshly fetched attributes and
813
-	 * and their values
814
-	 * @param array $ldapRecords
815
-	 */
816
-	public function batchApplyUserAttributes(array $ldapRecords){
817
-		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
818
-		foreach($ldapRecords as $userRecord) {
819
-			if(!isset($userRecord[$displayNameAttribute])) {
820
-				// displayName is obligatory
821
-				continue;
822
-			}
823
-			$ocName  = $this->dn2ocname($userRecord['dn'][0]);
824
-			if($ocName === false) {
825
-				continue;
826
-			}
827
-			$this->cacheUserExists($ocName);
828
-			$user = $this->userManager->get($ocName);
829
-			if($user instanceof OfflineUser) {
830
-				$user->unmark();
831
-				$user = $this->userManager->get($ocName);
832
-			}
833
-			if ($user !== null) {
834
-				$user->processAttributes($userRecord);
835
-			} else {
836
-				\OC::$server->getLogger()->debug(
837
-					"The ldap user manager returned null for $ocName",
838
-					['app'=>'user_ldap']
839
-				);
840
-			}
841
-		}
842
-	}
843
-
844
-	/**
845
-	 * @param string $filter
846
-	 * @param string|string[] $attr
847
-	 * @param int $limit
848
-	 * @param int $offset
849
-	 * @return array
850
-	 */
851
-	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
852
-		return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
853
-	}
854
-
855
-	/**
856
-	 * @param array $list
857
-	 * @param bool $manyAttributes
858
-	 * @return array
859
-	 */
860
-	private function fetchList($list, $manyAttributes) {
861
-		if(is_array($list)) {
862
-			if($manyAttributes) {
863
-				return $list;
864
-			} else {
865
-				$list = array_reduce($list, function($carry, $item) {
866
-					$attribute = array_keys($item)[0];
867
-					$carry[] = $item[$attribute][0];
868
-					return $carry;
869
-				}, array());
870
-				return array_unique($list, SORT_LOCALE_STRING);
871
-			}
872
-		}
873
-
874
-		//error cause actually, maybe throw an exception in future.
875
-		return array();
876
-	}
877
-
878
-	/**
879
-	 * executes an LDAP search, optimized for Users
880
-	 * @param string $filter the LDAP filter for the search
881
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
882
-	 * @param integer $limit
883
-	 * @param integer $offset
884
-	 * @return array with the search result
885
-	 *
886
-	 * Executes an LDAP search
887
-	 */
888
-	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
889
-		return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
890
-	}
891
-
892
-	/**
893
-	 * @param string $filter
894
-	 * @param string|string[] $attr
895
-	 * @param int $limit
896
-	 * @param int $offset
897
-	 * @return false|int
898
-	 */
899
-	public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
900
-		return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
901
-	}
902
-
903
-	/**
904
-	 * executes an LDAP search, optimized for Groups
905
-	 * @param string $filter the LDAP filter for the search
906
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
907
-	 * @param integer $limit
908
-	 * @param integer $offset
909
-	 * @return array with the search result
910
-	 *
911
-	 * Executes an LDAP search
912
-	 */
913
-	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
914
-		return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
915
-	}
916
-
917
-	/**
918
-	 * returns the number of available groups
919
-	 * @param string $filter the LDAP search filter
920
-	 * @param string[] $attr optional
921
-	 * @param int|null $limit
922
-	 * @param int|null $offset
923
-	 * @return int|bool
924
-	 */
925
-	public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
926
-		return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
927
-	}
928
-
929
-	/**
930
-	 * returns the number of available objects on the base DN
931
-	 *
932
-	 * @param int|null $limit
933
-	 * @param int|null $offset
934
-	 * @return int|bool
935
-	 */
936
-	public function countObjects($limit = null, $offset = null) {
937
-		return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
938
-	}
939
-
940
-	/**
941
-	 * retrieved. Results will according to the order in the array.
942
-	 * @param int $limit optional, maximum results to be counted
943
-	 * @param int $offset optional, a starting point
944
-	 * @return array|false array with the search result as first value and pagedSearchOK as
945
-	 * second | false if not successful
946
-	 */
947
-	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
948
-		if(!is_null($attr) && !is_array($attr)) {
949
-			$attr = array(mb_strtolower($attr, 'UTF-8'));
950
-		}
951
-
952
-		// See if we have a resource, in case not cancel with message
953
-		$cr = $this->connection->getConnectionResource();
954
-		if(!$this->ldap->isResource($cr)) {
955
-			// Seems like we didn't find any resource.
956
-			// Return an empty array just like before.
957
-			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
958
-			return false;
959
-		}
960
-
961
-		//check whether paged search should be attempted
962
-		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
963
-
964
-		$linkResources = array_pad(array(), count($base), $cr);
965
-		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
966
-		$error = $this->ldap->errno($cr);
967
-		if(!is_array($sr) || $error !== 0) {
968
-			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
969
-			return false;
970
-		}
971
-
972
-		return array($sr, $pagedSearchOK);
973
-	}
974
-
975
-	/**
976
-	 * processes an LDAP paged search operation
977
-	 * @param array $sr the array containing the LDAP search resources
978
-	 * @param string $filter the LDAP filter for the search
979
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
980
-	 * @param int $iFoundItems number of results in the search operation
981
-	 * @param int $limit maximum results to be counted
982
-	 * @param int $offset a starting point
983
-	 * @param bool $pagedSearchOK whether a paged search has been executed
984
-	 * @param bool $skipHandling required for paged search when cookies to
985
-	 * prior results need to be gained
986
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
987
-	 */
988
-	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
989
-		$cookie = null;
990
-		if($pagedSearchOK) {
991
-			$cr = $this->connection->getConnectionResource();
992
-			foreach($sr as $key => $res) {
993
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
994
-					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
995
-				}
996
-			}
997
-
998
-			//browsing through prior pages to get the cookie for the new one
999
-			if($skipHandling) {
1000
-				return false;
1001
-			}
1002
-			// if count is bigger, then the server does not support
1003
-			// paged search. Instead, he did a normal search. We set a
1004
-			// flag here, so the callee knows how to deal with it.
1005
-			if($iFoundItems <= $limit) {
1006
-				$this->pagedSearchedSuccessful = true;
1007
-			}
1008
-		} else {
1009
-			if(!is_null($limit)) {
1010
-				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
1011
-			}
1012
-		}
1013
-		/* ++ Fixing RHDS searches with pages with zero results ++
347
+        try {
348
+            return $this->ldap->modReplace($cr, $userDN, $password);
349
+        } catch(ConstraintViolationException $e) {
350
+            throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
351
+        }
352
+    }
353
+
354
+    /**
355
+     * checks whether the given attributes value is probably a DN
356
+     * @param string $attr the attribute in question
357
+     * @return boolean if so true, otherwise false
358
+     */
359
+    private function resemblesDN($attr) {
360
+        $resemblingAttributes = array(
361
+            'dn',
362
+            'uniquemember',
363
+            'member',
364
+            // memberOf is an "operational" attribute, without a definition in any RFC
365
+            'memberof'
366
+        );
367
+        return in_array($attr, $resemblingAttributes);
368
+    }
369
+
370
+    /**
371
+     * checks whether the given string is probably a DN
372
+     * @param string $string
373
+     * @return boolean
374
+     */
375
+    public function stringResemblesDN($string) {
376
+        $r = $this->ldap->explodeDN($string, 0);
377
+        // if exploding a DN succeeds and does not end up in
378
+        // an empty array except for $r[count] being 0.
379
+        return (is_array($r) && count($r) > 1);
380
+    }
381
+
382
+    /**
383
+     * returns a DN-string that is cleaned from not domain parts, e.g.
384
+     * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
385
+     * becomes dc=foobar,dc=server,dc=org
386
+     * @param string $dn
387
+     * @return string
388
+     */
389
+    public function getDomainDNFromDN($dn) {
390
+        $allParts = $this->ldap->explodeDN($dn, 0);
391
+        if($allParts === false) {
392
+            //not a valid DN
393
+            return '';
394
+        }
395
+        $domainParts = array();
396
+        $dcFound = false;
397
+        foreach($allParts as $part) {
398
+            if(!$dcFound && strpos($part, 'dc=') === 0) {
399
+                $dcFound = true;
400
+            }
401
+            if($dcFound) {
402
+                $domainParts[] = $part;
403
+            }
404
+        }
405
+        $domainDN = implode(',', $domainParts);
406
+        return $domainDN;
407
+    }
408
+
409
+    /**
410
+     * returns the LDAP DN for the given internal ownCloud name of the group
411
+     * @param string $name the ownCloud name in question
412
+     * @return string|false LDAP DN on success, otherwise false
413
+     */
414
+    public function groupname2dn($name) {
415
+        return $this->groupMapper->getDNByName($name);
416
+    }
417
+
418
+    /**
419
+     * returns the LDAP DN for the given internal ownCloud name of the user
420
+     * @param string $name the ownCloud name in question
421
+     * @return string|false with the LDAP DN on success, otherwise false
422
+     */
423
+    public function username2dn($name) {
424
+        $fdn = $this->userMapper->getDNByName($name);
425
+
426
+        //Check whether the DN belongs to the Base, to avoid issues on multi-
427
+        //server setups
428
+        if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
429
+            return $fdn;
430
+        }
431
+
432
+        return false;
433
+    }
434
+
435
+    /**
436
+     * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
437
+     * @param string $fdn the dn of the group object
438
+     * @param string $ldapName optional, the display name of the object
439
+     * @return string|false with the name to use in ownCloud, false on DN outside of search DN
440
+     */
441
+    public function dn2groupname($fdn, $ldapName = null) {
442
+        //To avoid bypassing the base DN settings under certain circumstances
443
+        //with the group support, check whether the provided DN matches one of
444
+        //the given Bases
445
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
446
+            return false;
447
+        }
448
+
449
+        return $this->dn2ocname($fdn, $ldapName, false);
450
+    }
451
+
452
+    /**
453
+     * accepts an array of group DNs and tests whether they match the user
454
+     * filter by doing read operations against the group entries. Returns an
455
+     * array of DNs that match the filter.
456
+     *
457
+     * @param string[] $groupDNs
458
+     * @return string[]
459
+     */
460
+    public function groupsMatchFilter($groupDNs) {
461
+        $validGroupDNs = [];
462
+        foreach($groupDNs as $dn) {
463
+            $cacheKey = 'groupsMatchFilter-'.$dn;
464
+            $groupMatchFilter = $this->connection->getFromCache($cacheKey);
465
+            if(!is_null($groupMatchFilter)) {
466
+                if($groupMatchFilter) {
467
+                    $validGroupDNs[] = $dn;
468
+                }
469
+                continue;
470
+            }
471
+
472
+            // Check the base DN first. If this is not met already, we don't
473
+            // need to ask the server at all.
474
+            if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
475
+                $this->connection->writeToCache($cacheKey, false);
476
+                continue;
477
+            }
478
+
479
+            $result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
480
+            if(is_array($result)) {
481
+                $this->connection->writeToCache($cacheKey, true);
482
+                $validGroupDNs[] = $dn;
483
+            } else {
484
+                $this->connection->writeToCache($cacheKey, false);
485
+            }
486
+
487
+        }
488
+        return $validGroupDNs;
489
+    }
490
+
491
+    /**
492
+     * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
493
+     * @param string $dn the dn of the user object
494
+     * @param string $ldapName optional, the display name of the object
495
+     * @return string|false with with the name to use in ownCloud
496
+     */
497
+    public function dn2username($fdn, $ldapName = null) {
498
+        //To avoid bypassing the base DN settings under certain circumstances
499
+        //with the group support, check whether the provided DN matches one of
500
+        //the given Bases
501
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
502
+            return false;
503
+        }
504
+
505
+        return $this->dn2ocname($fdn, $ldapName, true);
506
+    }
507
+
508
+    /**
509
+     * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
510
+     * @param string $dn the dn of the user object
511
+     * @param string $ldapName optional, the display name of the object
512
+     * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
513
+     * @return string|false with with the name to use in ownCloud
514
+     */
515
+    public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
516
+        if($isUser) {
517
+            $mapper = $this->getUserMapper();
518
+            $nameAttribute = $this->connection->ldapUserDisplayName;
519
+        } else {
520
+            $mapper = $this->getGroupMapper();
521
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
522
+        }
523
+
524
+        //let's try to retrieve the ownCloud name from the mappings table
525
+        $ocName = $mapper->getNameByDN($fdn);
526
+        if(is_string($ocName)) {
527
+            return $ocName;
528
+        }
529
+
530
+        //second try: get the UUID and check if it is known. Then, update the DN and return the name.
531
+        $uuid = $this->getUUID($fdn, $isUser);
532
+        if(is_string($uuid)) {
533
+            $ocName = $mapper->getNameByUUID($uuid);
534
+            if(is_string($ocName)) {
535
+                $mapper->setDNbyUUID($fdn, $uuid);
536
+                return $ocName;
537
+            }
538
+        } else {
539
+            //If the UUID can't be detected something is foul.
540
+            \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
541
+            return false;
542
+        }
543
+
544
+        if(is_null($ldapName)) {
545
+            $ldapName = $this->readAttribute($fdn, $nameAttribute);
546
+            if(!isset($ldapName[0]) && empty($ldapName[0])) {
547
+                \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
548
+                return false;
549
+            }
550
+            $ldapName = $ldapName[0];
551
+        }
552
+
553
+        if($isUser) {
554
+            $usernameAttribute = strval($this->connection->ldapExpertUsernameAttr);
555
+            if ($usernameAttribute !== '') {
556
+                $username = $this->readAttribute($fdn, $usernameAttribute);
557
+                $username = $username[0];
558
+            } else {
559
+                $username = $uuid;
560
+            }
561
+            $intName = $this->sanitizeUsername($username);
562
+        } else {
563
+            $intName = $ldapName;
564
+        }
565
+
566
+        //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
567
+        //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
568
+        //NOTE: mind, disabling cache affects only this instance! Using it
569
+        // outside of core user management will still cache the user as non-existing.
570
+        $originalTTL = $this->connection->ldapCacheTTL;
571
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
572
+        if(($isUser && !\OCP\User::userExists($intName))
573
+            || (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) {
574
+            if($mapper->map($fdn, $intName, $uuid)) {
575
+                $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
576
+                return $intName;
577
+            }
578
+        }
579
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
580
+
581
+        $altName = $this->createAltInternalOwnCloudName($intName, $isUser);
582
+        if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
583
+            return $altName;
584
+        }
585
+
586
+        //if everything else did not help..
587
+        \OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
588
+        return false;
589
+    }
590
+
591
+    /**
592
+     * gives back the user names as they are used ownClod internally
593
+     * @param array $ldapUsers as returned by fetchList()
594
+     * @return array an array with the user names to use in ownCloud
595
+     *
596
+     * gives back the user names as they are used ownClod internally
597
+     */
598
+    public function ownCloudUserNames($ldapUsers) {
599
+        return $this->ldap2ownCloudNames($ldapUsers, true);
600
+    }
601
+
602
+    /**
603
+     * gives back the group names as they are used ownClod internally
604
+     * @param array $ldapGroups as returned by fetchList()
605
+     * @return array an array with the group names to use in ownCloud
606
+     *
607
+     * gives back the group names as they are used ownClod internally
608
+     */
609
+    public function ownCloudGroupNames($ldapGroups) {
610
+        return $this->ldap2ownCloudNames($ldapGroups, false);
611
+    }
612
+
613
+    /**
614
+     * @param array $ldapObjects as returned by fetchList()
615
+     * @param bool $isUsers
616
+     * @return array
617
+     */
618
+    private function ldap2ownCloudNames($ldapObjects, $isUsers) {
619
+        if($isUsers) {
620
+            $nameAttribute = $this->connection->ldapUserDisplayName;
621
+            $sndAttribute  = $this->connection->ldapUserDisplayName2;
622
+        } else {
623
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
624
+        }
625
+        $ownCloudNames = array();
626
+
627
+        foreach($ldapObjects as $ldapObject) {
628
+            $nameByLDAP = null;
629
+            if(    isset($ldapObject[$nameAttribute])
630
+                && is_array($ldapObject[$nameAttribute])
631
+                && isset($ldapObject[$nameAttribute][0])
632
+            ) {
633
+                // might be set, but not necessarily. if so, we use it.
634
+                $nameByLDAP = $ldapObject[$nameAttribute][0];
635
+            }
636
+
637
+            $ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
638
+            if($ocName) {
639
+                $ownCloudNames[] = $ocName;
640
+                if($isUsers) {
641
+                    //cache the user names so it does not need to be retrieved
642
+                    //again later (e.g. sharing dialogue).
643
+                    if(is_null($nameByLDAP)) {
644
+                        continue;
645
+                    }
646
+                    $sndName = isset($ldapObject[$sndAttribute][0])
647
+                        ? $ldapObject[$sndAttribute][0] : '';
648
+                    $this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
649
+                }
650
+            }
651
+        }
652
+        return $ownCloudNames;
653
+    }
654
+
655
+    /**
656
+     * caches the user display name
657
+     * @param string $ocName the internal ownCloud username
658
+     * @param string|false $home the home directory path
659
+     */
660
+    public function cacheUserHome($ocName, $home) {
661
+        $cacheKey = 'getHome'.$ocName;
662
+        $this->connection->writeToCache($cacheKey, $home);
663
+    }
664
+
665
+    /**
666
+     * caches a user as existing
667
+     * @param string $ocName the internal ownCloud username
668
+     */
669
+    public function cacheUserExists($ocName) {
670
+        $this->connection->writeToCache('userExists'.$ocName, true);
671
+    }
672
+
673
+    /**
674
+     * caches the user display name
675
+     * @param string $ocName the internal ownCloud username
676
+     * @param string $displayName the display name
677
+     * @param string $displayName2 the second display name
678
+     */
679
+    public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
680
+        $user = $this->userManager->get($ocName);
681
+        if($user === null) {
682
+            return;
683
+        }
684
+        $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
685
+        $cacheKeyTrunk = 'getDisplayName';
686
+        $this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
687
+    }
688
+
689
+    /**
690
+     * creates a unique name for internal ownCloud use for users. Don't call it directly.
691
+     * @param string $name the display name of the object
692
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful
693
+     *
694
+     * Instead of using this method directly, call
695
+     * createAltInternalOwnCloudName($name, true)
696
+     */
697
+    private function _createAltInternalOwnCloudNameForUsers($name) {
698
+        $attempts = 0;
699
+        //while loop is just a precaution. If a name is not generated within
700
+        //20 attempts, something else is very wrong. Avoids infinite loop.
701
+        while($attempts < 20){
702
+            $altName = $name . '_' . rand(1000,9999);
703
+            if(!\OCP\User::userExists($altName)) {
704
+                return $altName;
705
+            }
706
+            $attempts++;
707
+        }
708
+        return false;
709
+    }
710
+
711
+    /**
712
+     * creates a unique name for internal ownCloud use for groups. Don't call it directly.
713
+     * @param string $name the display name of the object
714
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful.
715
+     *
716
+     * Instead of using this method directly, call
717
+     * createAltInternalOwnCloudName($name, false)
718
+     *
719
+     * Group names are also used as display names, so we do a sequential
720
+     * numbering, e.g. Developers_42 when there are 41 other groups called
721
+     * "Developers"
722
+     */
723
+    private function _createAltInternalOwnCloudNameForGroups($name) {
724
+        $usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
725
+        if(!($usedNames) || count($usedNames) === 0) {
726
+            $lastNo = 1; //will become name_2
727
+        } else {
728
+            natsort($usedNames);
729
+            $lastName = array_pop($usedNames);
730
+            $lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
731
+        }
732
+        $altName = $name.'_'.strval($lastNo+1);
733
+        unset($usedNames);
734
+
735
+        $attempts = 1;
736
+        while($attempts < 21){
737
+            // Check to be really sure it is unique
738
+            // while loop is just a precaution. If a name is not generated within
739
+            // 20 attempts, something else is very wrong. Avoids infinite loop.
740
+            if(!\OC::$server->getGroupManager()->groupExists($altName)) {
741
+                return $altName;
742
+            }
743
+            $altName = $name . '_' . ($lastNo + $attempts);
744
+            $attempts++;
745
+        }
746
+        return false;
747
+    }
748
+
749
+    /**
750
+     * creates a unique name for internal ownCloud use.
751
+     * @param string $name the display name of the object
752
+     * @param boolean $isUser whether name should be created for a user (true) or a group (false)
753
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful
754
+     */
755
+    private function createAltInternalOwnCloudName($name, $isUser) {
756
+        $originalTTL = $this->connection->ldapCacheTTL;
757
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
758
+        if($isUser) {
759
+            $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
760
+        } else {
761
+            $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
762
+        }
763
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
764
+
765
+        return $altName;
766
+    }
767
+
768
+    /**
769
+     * fetches a list of users according to a provided loginName and utilizing
770
+     * the login filter.
771
+     *
772
+     * @param string $loginName
773
+     * @param array $attributes optional, list of attributes to read
774
+     * @return array
775
+     */
776
+    public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
777
+        $loginName = $this->escapeFilterPart($loginName);
778
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
779
+        $users = $this->fetchListOfUsers($filter, $attributes);
780
+        return $users;
781
+    }
782
+
783
+    /**
784
+     * counts the number of users according to a provided loginName and
785
+     * utilizing the login filter.
786
+     *
787
+     * @param string $loginName
788
+     * @return array
789
+     */
790
+    public function countUsersByLoginName($loginName) {
791
+        $loginName = $this->escapeFilterPart($loginName);
792
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
793
+        $users = $this->countUsers($filter);
794
+        return $users;
795
+    }
796
+
797
+    /**
798
+     * @param string $filter
799
+     * @param string|string[] $attr
800
+     * @param int $limit
801
+     * @param int $offset
802
+     * @return array
803
+     */
804
+    public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
805
+        $ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
806
+        $this->batchApplyUserAttributes($ldapRecords);
807
+        return $this->fetchList($ldapRecords, (count($attr) > 1));
808
+    }
809
+
810
+    /**
811
+     * provided with an array of LDAP user records the method will fetch the
812
+     * user object and requests it to process the freshly fetched attributes and
813
+     * and their values
814
+     * @param array $ldapRecords
815
+     */
816
+    public function batchApplyUserAttributes(array $ldapRecords){
817
+        $displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
818
+        foreach($ldapRecords as $userRecord) {
819
+            if(!isset($userRecord[$displayNameAttribute])) {
820
+                // displayName is obligatory
821
+                continue;
822
+            }
823
+            $ocName  = $this->dn2ocname($userRecord['dn'][0]);
824
+            if($ocName === false) {
825
+                continue;
826
+            }
827
+            $this->cacheUserExists($ocName);
828
+            $user = $this->userManager->get($ocName);
829
+            if($user instanceof OfflineUser) {
830
+                $user->unmark();
831
+                $user = $this->userManager->get($ocName);
832
+            }
833
+            if ($user !== null) {
834
+                $user->processAttributes($userRecord);
835
+            } else {
836
+                \OC::$server->getLogger()->debug(
837
+                    "The ldap user manager returned null for $ocName",
838
+                    ['app'=>'user_ldap']
839
+                );
840
+            }
841
+        }
842
+    }
843
+
844
+    /**
845
+     * @param string $filter
846
+     * @param string|string[] $attr
847
+     * @param int $limit
848
+     * @param int $offset
849
+     * @return array
850
+     */
851
+    public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
852
+        return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
853
+    }
854
+
855
+    /**
856
+     * @param array $list
857
+     * @param bool $manyAttributes
858
+     * @return array
859
+     */
860
+    private function fetchList($list, $manyAttributes) {
861
+        if(is_array($list)) {
862
+            if($manyAttributes) {
863
+                return $list;
864
+            } else {
865
+                $list = array_reduce($list, function($carry, $item) {
866
+                    $attribute = array_keys($item)[0];
867
+                    $carry[] = $item[$attribute][0];
868
+                    return $carry;
869
+                }, array());
870
+                return array_unique($list, SORT_LOCALE_STRING);
871
+            }
872
+        }
873
+
874
+        //error cause actually, maybe throw an exception in future.
875
+        return array();
876
+    }
877
+
878
+    /**
879
+     * executes an LDAP search, optimized for Users
880
+     * @param string $filter the LDAP filter for the search
881
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
882
+     * @param integer $limit
883
+     * @param integer $offset
884
+     * @return array with the search result
885
+     *
886
+     * Executes an LDAP search
887
+     */
888
+    public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
889
+        return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
890
+    }
891
+
892
+    /**
893
+     * @param string $filter
894
+     * @param string|string[] $attr
895
+     * @param int $limit
896
+     * @param int $offset
897
+     * @return false|int
898
+     */
899
+    public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
900
+        return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
901
+    }
902
+
903
+    /**
904
+     * executes an LDAP search, optimized for Groups
905
+     * @param string $filter the LDAP filter for the search
906
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
907
+     * @param integer $limit
908
+     * @param integer $offset
909
+     * @return array with the search result
910
+     *
911
+     * Executes an LDAP search
912
+     */
913
+    public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
914
+        return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
915
+    }
916
+
917
+    /**
918
+     * returns the number of available groups
919
+     * @param string $filter the LDAP search filter
920
+     * @param string[] $attr optional
921
+     * @param int|null $limit
922
+     * @param int|null $offset
923
+     * @return int|bool
924
+     */
925
+    public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
926
+        return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
927
+    }
928
+
929
+    /**
930
+     * returns the number of available objects on the base DN
931
+     *
932
+     * @param int|null $limit
933
+     * @param int|null $offset
934
+     * @return int|bool
935
+     */
936
+    public function countObjects($limit = null, $offset = null) {
937
+        return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
938
+    }
939
+
940
+    /**
941
+     * retrieved. Results will according to the order in the array.
942
+     * @param int $limit optional, maximum results to be counted
943
+     * @param int $offset optional, a starting point
944
+     * @return array|false array with the search result as first value and pagedSearchOK as
945
+     * second | false if not successful
946
+     */
947
+    private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
948
+        if(!is_null($attr) && !is_array($attr)) {
949
+            $attr = array(mb_strtolower($attr, 'UTF-8'));
950
+        }
951
+
952
+        // See if we have a resource, in case not cancel with message
953
+        $cr = $this->connection->getConnectionResource();
954
+        if(!$this->ldap->isResource($cr)) {
955
+            // Seems like we didn't find any resource.
956
+            // Return an empty array just like before.
957
+            \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
958
+            return false;
959
+        }
960
+
961
+        //check whether paged search should be attempted
962
+        $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
963
+
964
+        $linkResources = array_pad(array(), count($base), $cr);
965
+        $sr = $this->ldap->search($linkResources, $base, $filter, $attr);
966
+        $error = $this->ldap->errno($cr);
967
+        if(!is_array($sr) || $error !== 0) {
968
+            \OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
969
+            return false;
970
+        }
971
+
972
+        return array($sr, $pagedSearchOK);
973
+    }
974
+
975
+    /**
976
+     * processes an LDAP paged search operation
977
+     * @param array $sr the array containing the LDAP search resources
978
+     * @param string $filter the LDAP filter for the search
979
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
980
+     * @param int $iFoundItems number of results in the search operation
981
+     * @param int $limit maximum results to be counted
982
+     * @param int $offset a starting point
983
+     * @param bool $pagedSearchOK whether a paged search has been executed
984
+     * @param bool $skipHandling required for paged search when cookies to
985
+     * prior results need to be gained
986
+     * @return bool cookie validity, true if we have more pages, false otherwise.
987
+     */
988
+    private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
989
+        $cookie = null;
990
+        if($pagedSearchOK) {
991
+            $cr = $this->connection->getConnectionResource();
992
+            foreach($sr as $key => $res) {
993
+                if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
994
+                    $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
995
+                }
996
+            }
997
+
998
+            //browsing through prior pages to get the cookie for the new one
999
+            if($skipHandling) {
1000
+                return false;
1001
+            }
1002
+            // if count is bigger, then the server does not support
1003
+            // paged search. Instead, he did a normal search. We set a
1004
+            // flag here, so the callee knows how to deal with it.
1005
+            if($iFoundItems <= $limit) {
1006
+                $this->pagedSearchedSuccessful = true;
1007
+            }
1008
+        } else {
1009
+            if(!is_null($limit)) {
1010
+                \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
1011
+            }
1012
+        }
1013
+        /* ++ Fixing RHDS searches with pages with zero results ++
1014 1014
 		 * Return cookie status. If we don't have more pages, with RHDS
1015 1015
 		 * cookie is null, with openldap cookie is an empty string and
1016 1016
 		 * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0
1017 1017
 		 */
1018
-		return !empty($cookie) || $cookie === '0';
1019
-	}
1020
-
1021
-	/**
1022
-	 * executes an LDAP search, but counts the results only
1023
-	 * @param string $filter the LDAP filter for the search
1024
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1025
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1026
-	 * retrieved. Results will according to the order in the array.
1027
-	 * @param int $limit optional, maximum results to be counted
1028
-	 * @param int $offset optional, a starting point
1029
-	 * @param bool $skipHandling indicates whether the pages search operation is
1030
-	 * completed
1031
-	 * @return int|false Integer or false if the search could not be initialized
1032
-	 *
1033
-	 */
1034
-	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1035
-		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
1036
-
1037
-		$limitPerPage = intval($this->connection->ldapPagingSize);
1038
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1039
-			$limitPerPage = $limit;
1040
-		}
1041
-
1042
-		$counter = 0;
1043
-		$count = null;
1044
-		$this->connection->getConnectionResource();
1045
-
1046
-		do {
1047
-			$search = $this->executeSearch($filter, $base, $attr,
1048
-										   $limitPerPage, $offset);
1049
-			if($search === false) {
1050
-				return $counter > 0 ? $counter : false;
1051
-			}
1052
-			list($sr, $pagedSearchOK) = $search;
1053
-
1054
-			/* ++ Fixing RHDS searches with pages with zero results ++
1018
+        return !empty($cookie) || $cookie === '0';
1019
+    }
1020
+
1021
+    /**
1022
+     * executes an LDAP search, but counts the results only
1023
+     * @param string $filter the LDAP filter for the search
1024
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1025
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1026
+     * retrieved. Results will according to the order in the array.
1027
+     * @param int $limit optional, maximum results to be counted
1028
+     * @param int $offset optional, a starting point
1029
+     * @param bool $skipHandling indicates whether the pages search operation is
1030
+     * completed
1031
+     * @return int|false Integer or false if the search could not be initialized
1032
+     *
1033
+     */
1034
+    private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1035
+        \OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
1036
+
1037
+        $limitPerPage = intval($this->connection->ldapPagingSize);
1038
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1039
+            $limitPerPage = $limit;
1040
+        }
1041
+
1042
+        $counter = 0;
1043
+        $count = null;
1044
+        $this->connection->getConnectionResource();
1045
+
1046
+        do {
1047
+            $search = $this->executeSearch($filter, $base, $attr,
1048
+                                            $limitPerPage, $offset);
1049
+            if($search === false) {
1050
+                return $counter > 0 ? $counter : false;
1051
+            }
1052
+            list($sr, $pagedSearchOK) = $search;
1053
+
1054
+            /* ++ Fixing RHDS searches with pages with zero results ++
1055 1055
 			 * countEntriesInSearchResults() method signature changed
1056 1056
 			 * by removing $limit and &$hasHitLimit parameters
1057 1057
 			 */
1058
-			$count = $this->countEntriesInSearchResults($sr);
1059
-			$counter += $count;
1058
+            $count = $this->countEntriesInSearchResults($sr);
1059
+            $counter += $count;
1060 1060
 
1061
-			$hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
1062
-										$offset, $pagedSearchOK, $skipHandling);
1063
-			$offset += $limitPerPage;
1064
-			/* ++ Fixing RHDS searches with pages with zero results ++
1061
+            $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
1062
+                                        $offset, $pagedSearchOK, $skipHandling);
1063
+            $offset += $limitPerPage;
1064
+            /* ++ Fixing RHDS searches with pages with zero results ++
1065 1065
 			 * Continue now depends on $hasMorePages value
1066 1066
 			 */
1067
-			$continue = $pagedSearchOK && $hasMorePages;
1068
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1069
-
1070
-		return $counter;
1071
-	}
1072
-
1073
-	/**
1074
-	 * @param array $searchResults
1075
-	 * @return int
1076
-	 */
1077
-	private function countEntriesInSearchResults($searchResults) {
1078
-		$cr = $this->connection->getConnectionResource();
1079
-		$counter = 0;
1080
-
1081
-		foreach($searchResults as $res) {
1082
-			$count = intval($this->ldap->countEntries($cr, $res));
1083
-			$counter += $count;
1084
-		}
1085
-
1086
-		return $counter;
1087
-	}
1088
-
1089
-	/**
1090
-	 * Executes an LDAP search
1091
-	 * @param string $filter the LDAP filter for the search
1092
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1093
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1094
-	 * @param int $limit
1095
-	 * @param int $offset
1096
-	 * @param bool $skipHandling
1097
-	 * @return array with the search result
1098
-	 */
1099
-	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1100
-		if($limit <= 0) {
1101
-			//otherwise search will fail
1102
-			$limit = null;
1103
-		}
1104
-
1105
-		/* ++ Fixing RHDS searches with pages with zero results ++
1067
+            $continue = $pagedSearchOK && $hasMorePages;
1068
+        } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1069
+
1070
+        return $counter;
1071
+    }
1072
+
1073
+    /**
1074
+     * @param array $searchResults
1075
+     * @return int
1076
+     */
1077
+    private function countEntriesInSearchResults($searchResults) {
1078
+        $cr = $this->connection->getConnectionResource();
1079
+        $counter = 0;
1080
+
1081
+        foreach($searchResults as $res) {
1082
+            $count = intval($this->ldap->countEntries($cr, $res));
1083
+            $counter += $count;
1084
+        }
1085
+
1086
+        return $counter;
1087
+    }
1088
+
1089
+    /**
1090
+     * Executes an LDAP search
1091
+     * @param string $filter the LDAP filter for the search
1092
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1093
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1094
+     * @param int $limit
1095
+     * @param int $offset
1096
+     * @param bool $skipHandling
1097
+     * @return array with the search result
1098
+     */
1099
+    private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1100
+        if($limit <= 0) {
1101
+            //otherwise search will fail
1102
+            $limit = null;
1103
+        }
1104
+
1105
+        /* ++ Fixing RHDS searches with pages with zero results ++
1106 1106
 		 * As we can have pages with zero results and/or pages with less
1107 1107
 		 * than $limit results but with a still valid server 'cookie',
1108 1108
 		 * loops through until we get $continue equals true and
1109 1109
 		 * $findings['count'] < $limit
1110 1110
 		 */
1111
-		$findings = array();
1112
-		$savedoffset = $offset;
1113
-		do {
1114
-			$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
1115
-			if($search === false) {
1116
-				return array();
1117
-			}
1118
-			list($sr, $pagedSearchOK) = $search;
1119
-			$cr = $this->connection->getConnectionResource();
1120
-
1121
-			if($skipHandling) {
1122
-				//i.e. result do not need to be fetched, we just need the cookie
1123
-				//thus pass 1 or any other value as $iFoundItems because it is not
1124
-				//used
1125
-				$this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
1126
-								$offset, $pagedSearchOK,
1127
-								$skipHandling);
1128
-				return array();
1129
-			}
1130
-
1131
-			foreach($sr as $res) {
1132
-				$findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1133
-			}
1134
-
1135
-			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
1136
-								$limit, $offset, $pagedSearchOK,
1137
-										$skipHandling);
1138
-			$offset += $limit;
1139
-		} while ($continue && $pagedSearchOK && $findings['count'] < $limit);
1140
-		// reseting offset
1141
-		$offset = $savedoffset;
1142
-
1143
-		// if we're here, probably no connection resource is returned.
1144
-		// to make ownCloud behave nicely, we simply give back an empty array.
1145
-		if(is_null($findings)) {
1146
-			return array();
1147
-		}
1148
-
1149
-		if(!is_null($attr)) {
1150
-			$selection = array();
1151
-			$i = 0;
1152
-			foreach($findings as $item) {
1153
-				if(!is_array($item)) {
1154
-					continue;
1155
-				}
1156
-				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1157
-				foreach($attr as $key) {
1158
-					$key = mb_strtolower($key, 'UTF-8');
1159
-					if(isset($item[$key])) {
1160
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1161
-							unset($item[$key]['count']);
1162
-						}
1163
-						if($key !== 'dn') {
1164
-							$selection[$i][$key] = $this->resemblesDN($key) ?
1165
-								$this->helper->sanitizeDN($item[$key])
1166
-								: $item[$key];
1167
-						} else {
1168
-							$selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1169
-						}
1170
-					}
1171
-
1172
-				}
1173
-				$i++;
1174
-			}
1175
-			$findings = $selection;
1176
-		}
1177
-		//we slice the findings, when
1178
-		//a) paged search unsuccessful, though attempted
1179
-		//b) no paged search, but limit set
1180
-		if((!$this->getPagedSearchResultState()
1181
-			&& $pagedSearchOK)
1182
-			|| (
1183
-				!$pagedSearchOK
1184
-				&& !is_null($limit)
1185
-			)
1186
-		) {
1187
-			$findings = array_slice($findings, intval($offset), $limit);
1188
-		}
1189
-		return $findings;
1190
-	}
1191
-
1192
-	/**
1193
-	 * @param string $name
1194
-	 * @return bool|mixed|string
1195
-	 */
1196
-	public function sanitizeUsername($name) {
1197
-		if($this->connection->ldapIgnoreNamingRules) {
1198
-			return $name;
1199
-		}
1200
-
1201
-		// Transliteration
1202
-		// latin characters to ASCII
1203
-		$name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1204
-
1205
-		// Replacements
1206
-		$name = str_replace(' ', '_', $name);
1207
-
1208
-		// Every remaining disallowed characters will be removed
1209
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1210
-
1211
-		return $name;
1212
-	}
1213
-
1214
-	/**
1215
-	* escapes (user provided) parts for LDAP filter
1216
-	* @param string $input, the provided value
1217
-	* @param bool $allowAsterisk whether in * at the beginning should be preserved
1218
-	* @return string the escaped string
1219
-	*/
1220
-	public function escapeFilterPart($input, $allowAsterisk = false) {
1221
-		$asterisk = '';
1222
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1223
-			$asterisk = '*';
1224
-			$input = mb_substr($input, 1, null, 'UTF-8');
1225
-		}
1226
-		$search  = array('*', '\\', '(', ')');
1227
-		$replace = array('\\*', '\\\\', '\\(', '\\)');
1228
-		return $asterisk . str_replace($search, $replace, $input);
1229
-	}
1230
-
1231
-	/**
1232
-	 * combines the input filters with AND
1233
-	 * @param string[] $filters the filters to connect
1234
-	 * @return string the combined filter
1235
-	 */
1236
-	public function combineFilterWithAnd($filters) {
1237
-		return $this->combineFilter($filters, '&');
1238
-	}
1239
-
1240
-	/**
1241
-	 * combines the input filters with OR
1242
-	 * @param string[] $filters the filters to connect
1243
-	 * @return string the combined filter
1244
-	 * Combines Filter arguments with OR
1245
-	 */
1246
-	public function combineFilterWithOr($filters) {
1247
-		return $this->combineFilter($filters, '|');
1248
-	}
1249
-
1250
-	/**
1251
-	 * combines the input filters with given operator
1252
-	 * @param string[] $filters the filters to connect
1253
-	 * @param string $operator either & or |
1254
-	 * @return string the combined filter
1255
-	 */
1256
-	private function combineFilter($filters, $operator) {
1257
-		$combinedFilter = '('.$operator;
1258
-		foreach($filters as $filter) {
1259
-			if ($filter !== '' && $filter[0] !== '(') {
1260
-				$filter = '('.$filter.')';
1261
-			}
1262
-			$combinedFilter.=$filter;
1263
-		}
1264
-		$combinedFilter.=')';
1265
-		return $combinedFilter;
1266
-	}
1267
-
1268
-	/**
1269
-	 * creates a filter part for to perform search for users
1270
-	 * @param string $search the search term
1271
-	 * @return string the final filter part to use in LDAP searches
1272
-	 */
1273
-	public function getFilterPartForUserSearch($search) {
1274
-		return $this->getFilterPartForSearch($search,
1275
-			$this->connection->ldapAttributesForUserSearch,
1276
-			$this->connection->ldapUserDisplayName);
1277
-	}
1278
-
1279
-	/**
1280
-	 * creates a filter part for to perform search for groups
1281
-	 * @param string $search the search term
1282
-	 * @return string the final filter part to use in LDAP searches
1283
-	 */
1284
-	public function getFilterPartForGroupSearch($search) {
1285
-		return $this->getFilterPartForSearch($search,
1286
-			$this->connection->ldapAttributesForGroupSearch,
1287
-			$this->connection->ldapGroupDisplayName);
1288
-	}
1289
-
1290
-	/**
1291
-	 * creates a filter part for searches by splitting up the given search
1292
-	 * string into single words
1293
-	 * @param string $search the search term
1294
-	 * @param string[] $searchAttributes needs to have at least two attributes,
1295
-	 * otherwise it does not make sense :)
1296
-	 * @return string the final filter part to use in LDAP searches
1297
-	 * @throws \Exception
1298
-	 */
1299
-	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1300
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1301
-			throw new \Exception('searchAttributes must be an array with at least two string');
1302
-		}
1303
-		$searchWords = explode(' ', trim($search));
1304
-		$wordFilters = array();
1305
-		foreach($searchWords as $word) {
1306
-			$word = $this->prepareSearchTerm($word);
1307
-			//every word needs to appear at least once
1308
-			$wordMatchOneAttrFilters = array();
1309
-			foreach($searchAttributes as $attr) {
1310
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1311
-			}
1312
-			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1313
-		}
1314
-		return $this->combineFilterWithAnd($wordFilters);
1315
-	}
1316
-
1317
-	/**
1318
-	 * creates a filter part for searches
1319
-	 * @param string $search the search term
1320
-	 * @param string[]|null $searchAttributes
1321
-	 * @param string $fallbackAttribute a fallback attribute in case the user
1322
-	 * did not define search attributes. Typically the display name attribute.
1323
-	 * @return string the final filter part to use in LDAP searches
1324
-	 */
1325
-	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1326
-		$filter = array();
1327
-		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1328
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1329
-			try {
1330
-				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1331
-			} catch(\Exception $e) {
1332
-				\OCP\Util::writeLog(
1333
-					'user_ldap',
1334
-					'Creating advanced filter for search failed, falling back to simple method.',
1335
-					\OCP\Util::INFO
1336
-				);
1337
-			}
1338
-		}
1339
-
1340
-		$search = $this->prepareSearchTerm($search);
1341
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1342
-			if ($fallbackAttribute === '') {
1343
-				return '';
1344
-			}
1345
-			$filter[] = $fallbackAttribute . '=' . $search;
1346
-		} else {
1347
-			foreach($searchAttributes as $attribute) {
1348
-				$filter[] = $attribute . '=' . $search;
1349
-			}
1350
-		}
1351
-		if(count($filter) === 1) {
1352
-			return '('.$filter[0].')';
1353
-		}
1354
-		return $this->combineFilterWithOr($filter);
1355
-	}
1356
-
1357
-	/**
1358
-	 * returns the search term depending on whether we are allowed
1359
-	 * list users found by ldap with the current input appended by
1360
-	 * a *
1361
-	 * @return string
1362
-	 */
1363
-	private function prepareSearchTerm($term) {
1364
-		$config = \OC::$server->getConfig();
1365
-
1366
-		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1367
-
1368
-		$result = $term;
1369
-		if ($term === '') {
1370
-			$result = '*';
1371
-		} else if ($allowEnum !== 'no') {
1372
-			$result = $term . '*';
1373
-		}
1374
-		return $result;
1375
-	}
1376
-
1377
-	/**
1378
-	 * returns the filter used for counting users
1379
-	 * @return string
1380
-	 */
1381
-	public function getFilterForUserCount() {
1382
-		$filter = $this->combineFilterWithAnd(array(
1383
-			$this->connection->ldapUserFilter,
1384
-			$this->connection->ldapUserDisplayName . '=*'
1385
-		));
1386
-
1387
-		return $filter;
1388
-	}
1389
-
1390
-	/**
1391
-	 * @param string $name
1392
-	 * @param string $password
1393
-	 * @return bool
1394
-	 */
1395
-	public function areCredentialsValid($name, $password) {
1396
-		$name = $this->helper->DNasBaseParameter($name);
1397
-		$testConnection = clone $this->connection;
1398
-		$credentials = array(
1399
-			'ldapAgentName' => $name,
1400
-			'ldapAgentPassword' => $password
1401
-		);
1402
-		if(!$testConnection->setConfiguration($credentials)) {
1403
-			return false;
1404
-		}
1405
-		return $testConnection->bind();
1406
-	}
1407
-
1408
-	/**
1409
-	 * reverse lookup of a DN given a known UUID
1410
-	 *
1411
-	 * @param string $uuid
1412
-	 * @return string
1413
-	 * @throws \Exception
1414
-	 */
1415
-	public function getUserDnByUuid($uuid) {
1416
-		$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1417
-		$filter       = $this->connection->ldapUserFilter;
1418
-		$base         = $this->connection->ldapBaseUsers;
1419
-
1420
-		if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1421
-			// Sacrebleu! The UUID attribute is unknown :( We need first an
1422
-			// existing DN to be able to reliably detect it.
1423
-			$result = $this->search($filter, $base, ['dn'], 1);
1424
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1425
-				throw new \Exception('Cannot determine UUID attribute');
1426
-			}
1427
-			$dn = $result[0]['dn'][0];
1428
-			if(!$this->detectUuidAttribute($dn, true)) {
1429
-				throw new \Exception('Cannot determine UUID attribute');
1430
-			}
1431
-		} else {
1432
-			// The UUID attribute is either known or an override is given.
1433
-			// By calling this method we ensure that $this->connection->$uuidAttr
1434
-			// is definitely set
1435
-			if(!$this->detectUuidAttribute('', true)) {
1436
-				throw new \Exception('Cannot determine UUID attribute');
1437
-			}
1438
-		}
1439
-
1440
-		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1441
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1442
-			$uuid = $this->formatGuid2ForFilterUser($uuid);
1443
-		}
1444
-
1445
-		$filter = $uuidAttr . '=' . $uuid;
1446
-		$result = $this->searchUsers($filter, ['dn'], 2);
1447
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1448
-			// we put the count into account to make sure that this is
1449
-			// really unique
1450
-			return $result[0]['dn'][0];
1451
-		}
1452
-
1453
-		throw new \Exception('Cannot determine UUID attribute');
1454
-	}
1455
-
1456
-	/**
1457
-	 * auto-detects the directory's UUID attribute
1458
-	 * @param string $dn a known DN used to check against
1459
-	 * @param bool $isUser
1460
-	 * @param bool $force the detection should be run, even if it is not set to auto
1461
-	 * @return bool true on success, false otherwise
1462
-	 */
1463
-	private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1464
-		if($isUser) {
1465
-			$uuidAttr     = 'ldapUuidUserAttribute';
1466
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1467
-		} else {
1468
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1469
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1470
-		}
1471
-
1472
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1473
-			return true;
1474
-		}
1475
-
1476
-		if (is_string($uuidOverride) && trim($uuidOverride) !== '' && !$force) {
1477
-			$this->connection->$uuidAttr = $uuidOverride;
1478
-			return true;
1479
-		}
1480
-
1481
-		// for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1482
-		$testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1483
-
1484
-		foreach($testAttributes as $attribute) {
1485
-			$value = $this->readAttribute($dn, $attribute);
1486
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1487
-				\OCP\Util::writeLog('user_ldap',
1488
-									'Setting '.$attribute.' as '.$uuidAttr,
1489
-									\OCP\Util::DEBUG);
1490
-				$this->connection->$uuidAttr = $attribute;
1491
-				return true;
1492
-			}
1493
-		}
1494
-		\OCP\Util::writeLog('user_ldap',
1495
-							'Could not autodetect the UUID attribute',
1496
-							\OCP\Util::ERROR);
1497
-
1498
-		return false;
1499
-	}
1500
-
1501
-	/**
1502
-	 * @param string $dn
1503
-	 * @param bool $isUser
1504
-	 * @return string|bool
1505
-	 */
1506
-	public function getUUID($dn, $isUser = true) {
1507
-		if($isUser) {
1508
-			$uuidAttr     = 'ldapUuidUserAttribute';
1509
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1510
-		} else {
1511
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1512
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1513
-		}
1514
-
1515
-		$uuid = false;
1516
-		if($this->detectUuidAttribute($dn, $isUser)) {
1517
-			$uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1518
-			if( !is_array($uuid)
1519
-				&& $uuidOverride !== ''
1520
-				&& $this->detectUuidAttribute($dn, $isUser, true)) {
1521
-					$uuid = $this->readAttribute($dn,
1522
-												 $this->connection->$uuidAttr);
1523
-			}
1524
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1525
-				$uuid = $uuid[0];
1526
-			}
1527
-		}
1528
-
1529
-		return $uuid;
1530
-	}
1531
-
1532
-	/**
1533
-	 * converts a binary ObjectGUID into a string representation
1534
-	 * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1535
-	 * @return string
1536
-	 * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1537
-	 */
1538
-	private function convertObjectGUID2Str($oguid) {
1539
-		$hex_guid = bin2hex($oguid);
1540
-		$hex_guid_to_guid_str = '';
1541
-		for($k = 1; $k <= 4; ++$k) {
1542
-			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1543
-		}
1544
-		$hex_guid_to_guid_str .= '-';
1545
-		for($k = 1; $k <= 2; ++$k) {
1546
-			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1547
-		}
1548
-		$hex_guid_to_guid_str .= '-';
1549
-		for($k = 1; $k <= 2; ++$k) {
1550
-			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1551
-		}
1552
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1553
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1554
-
1555
-		return strtoupper($hex_guid_to_guid_str);
1556
-	}
1557
-
1558
-	/**
1559
-	 * the first three blocks of the string-converted GUID happen to be in
1560
-	 * reverse order. In order to use it in a filter, this needs to be
1561
-	 * corrected. Furthermore the dashes need to be replaced and \\ preprended
1562
-	 * to every two hax figures.
1563
-	 *
1564
-	 * If an invalid string is passed, it will be returned without change.
1565
-	 *
1566
-	 * @param string $guid
1567
-	 * @return string
1568
-	 */
1569
-	public function formatGuid2ForFilterUser($guid) {
1570
-		if(!is_string($guid)) {
1571
-			throw new \InvalidArgumentException('String expected');
1572
-		}
1573
-		$blocks = explode('-', $guid);
1574
-		if(count($blocks) !== 5) {
1575
-			/*
1111
+        $findings = array();
1112
+        $savedoffset = $offset;
1113
+        do {
1114
+            $search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
1115
+            if($search === false) {
1116
+                return array();
1117
+            }
1118
+            list($sr, $pagedSearchOK) = $search;
1119
+            $cr = $this->connection->getConnectionResource();
1120
+
1121
+            if($skipHandling) {
1122
+                //i.e. result do not need to be fetched, we just need the cookie
1123
+                //thus pass 1 or any other value as $iFoundItems because it is not
1124
+                //used
1125
+                $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
1126
+                                $offset, $pagedSearchOK,
1127
+                                $skipHandling);
1128
+                return array();
1129
+            }
1130
+
1131
+            foreach($sr as $res) {
1132
+                $findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1133
+            }
1134
+
1135
+            $continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
1136
+                                $limit, $offset, $pagedSearchOK,
1137
+                                        $skipHandling);
1138
+            $offset += $limit;
1139
+        } while ($continue && $pagedSearchOK && $findings['count'] < $limit);
1140
+        // reseting offset
1141
+        $offset = $savedoffset;
1142
+
1143
+        // if we're here, probably no connection resource is returned.
1144
+        // to make ownCloud behave nicely, we simply give back an empty array.
1145
+        if(is_null($findings)) {
1146
+            return array();
1147
+        }
1148
+
1149
+        if(!is_null($attr)) {
1150
+            $selection = array();
1151
+            $i = 0;
1152
+            foreach($findings as $item) {
1153
+                if(!is_array($item)) {
1154
+                    continue;
1155
+                }
1156
+                $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1157
+                foreach($attr as $key) {
1158
+                    $key = mb_strtolower($key, 'UTF-8');
1159
+                    if(isset($item[$key])) {
1160
+                        if(is_array($item[$key]) && isset($item[$key]['count'])) {
1161
+                            unset($item[$key]['count']);
1162
+                        }
1163
+                        if($key !== 'dn') {
1164
+                            $selection[$i][$key] = $this->resemblesDN($key) ?
1165
+                                $this->helper->sanitizeDN($item[$key])
1166
+                                : $item[$key];
1167
+                        } else {
1168
+                            $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1169
+                        }
1170
+                    }
1171
+
1172
+                }
1173
+                $i++;
1174
+            }
1175
+            $findings = $selection;
1176
+        }
1177
+        //we slice the findings, when
1178
+        //a) paged search unsuccessful, though attempted
1179
+        //b) no paged search, but limit set
1180
+        if((!$this->getPagedSearchResultState()
1181
+            && $pagedSearchOK)
1182
+            || (
1183
+                !$pagedSearchOK
1184
+                && !is_null($limit)
1185
+            )
1186
+        ) {
1187
+            $findings = array_slice($findings, intval($offset), $limit);
1188
+        }
1189
+        return $findings;
1190
+    }
1191
+
1192
+    /**
1193
+     * @param string $name
1194
+     * @return bool|mixed|string
1195
+     */
1196
+    public function sanitizeUsername($name) {
1197
+        if($this->connection->ldapIgnoreNamingRules) {
1198
+            return $name;
1199
+        }
1200
+
1201
+        // Transliteration
1202
+        // latin characters to ASCII
1203
+        $name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1204
+
1205
+        // Replacements
1206
+        $name = str_replace(' ', '_', $name);
1207
+
1208
+        // Every remaining disallowed characters will be removed
1209
+        $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1210
+
1211
+        return $name;
1212
+    }
1213
+
1214
+    /**
1215
+     * escapes (user provided) parts for LDAP filter
1216
+     * @param string $input, the provided value
1217
+     * @param bool $allowAsterisk whether in * at the beginning should be preserved
1218
+     * @return string the escaped string
1219
+     */
1220
+    public function escapeFilterPart($input, $allowAsterisk = false) {
1221
+        $asterisk = '';
1222
+        if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1223
+            $asterisk = '*';
1224
+            $input = mb_substr($input, 1, null, 'UTF-8');
1225
+        }
1226
+        $search  = array('*', '\\', '(', ')');
1227
+        $replace = array('\\*', '\\\\', '\\(', '\\)');
1228
+        return $asterisk . str_replace($search, $replace, $input);
1229
+    }
1230
+
1231
+    /**
1232
+     * combines the input filters with AND
1233
+     * @param string[] $filters the filters to connect
1234
+     * @return string the combined filter
1235
+     */
1236
+    public function combineFilterWithAnd($filters) {
1237
+        return $this->combineFilter($filters, '&');
1238
+    }
1239
+
1240
+    /**
1241
+     * combines the input filters with OR
1242
+     * @param string[] $filters the filters to connect
1243
+     * @return string the combined filter
1244
+     * Combines Filter arguments with OR
1245
+     */
1246
+    public function combineFilterWithOr($filters) {
1247
+        return $this->combineFilter($filters, '|');
1248
+    }
1249
+
1250
+    /**
1251
+     * combines the input filters with given operator
1252
+     * @param string[] $filters the filters to connect
1253
+     * @param string $operator either & or |
1254
+     * @return string the combined filter
1255
+     */
1256
+    private function combineFilter($filters, $operator) {
1257
+        $combinedFilter = '('.$operator;
1258
+        foreach($filters as $filter) {
1259
+            if ($filter !== '' && $filter[0] !== '(') {
1260
+                $filter = '('.$filter.')';
1261
+            }
1262
+            $combinedFilter.=$filter;
1263
+        }
1264
+        $combinedFilter.=')';
1265
+        return $combinedFilter;
1266
+    }
1267
+
1268
+    /**
1269
+     * creates a filter part for to perform search for users
1270
+     * @param string $search the search term
1271
+     * @return string the final filter part to use in LDAP searches
1272
+     */
1273
+    public function getFilterPartForUserSearch($search) {
1274
+        return $this->getFilterPartForSearch($search,
1275
+            $this->connection->ldapAttributesForUserSearch,
1276
+            $this->connection->ldapUserDisplayName);
1277
+    }
1278
+
1279
+    /**
1280
+     * creates a filter part for to perform search for groups
1281
+     * @param string $search the search term
1282
+     * @return string the final filter part to use in LDAP searches
1283
+     */
1284
+    public function getFilterPartForGroupSearch($search) {
1285
+        return $this->getFilterPartForSearch($search,
1286
+            $this->connection->ldapAttributesForGroupSearch,
1287
+            $this->connection->ldapGroupDisplayName);
1288
+    }
1289
+
1290
+    /**
1291
+     * creates a filter part for searches by splitting up the given search
1292
+     * string into single words
1293
+     * @param string $search the search term
1294
+     * @param string[] $searchAttributes needs to have at least two attributes,
1295
+     * otherwise it does not make sense :)
1296
+     * @return string the final filter part to use in LDAP searches
1297
+     * @throws \Exception
1298
+     */
1299
+    private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1300
+        if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1301
+            throw new \Exception('searchAttributes must be an array with at least two string');
1302
+        }
1303
+        $searchWords = explode(' ', trim($search));
1304
+        $wordFilters = array();
1305
+        foreach($searchWords as $word) {
1306
+            $word = $this->prepareSearchTerm($word);
1307
+            //every word needs to appear at least once
1308
+            $wordMatchOneAttrFilters = array();
1309
+            foreach($searchAttributes as $attr) {
1310
+                $wordMatchOneAttrFilters[] = $attr . '=' . $word;
1311
+            }
1312
+            $wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1313
+        }
1314
+        return $this->combineFilterWithAnd($wordFilters);
1315
+    }
1316
+
1317
+    /**
1318
+     * creates a filter part for searches
1319
+     * @param string $search the search term
1320
+     * @param string[]|null $searchAttributes
1321
+     * @param string $fallbackAttribute a fallback attribute in case the user
1322
+     * did not define search attributes. Typically the display name attribute.
1323
+     * @return string the final filter part to use in LDAP searches
1324
+     */
1325
+    private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1326
+        $filter = array();
1327
+        $haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1328
+        if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1329
+            try {
1330
+                return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1331
+            } catch(\Exception $e) {
1332
+                \OCP\Util::writeLog(
1333
+                    'user_ldap',
1334
+                    'Creating advanced filter for search failed, falling back to simple method.',
1335
+                    \OCP\Util::INFO
1336
+                );
1337
+            }
1338
+        }
1339
+
1340
+        $search = $this->prepareSearchTerm($search);
1341
+        if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1342
+            if ($fallbackAttribute === '') {
1343
+                return '';
1344
+            }
1345
+            $filter[] = $fallbackAttribute . '=' . $search;
1346
+        } else {
1347
+            foreach($searchAttributes as $attribute) {
1348
+                $filter[] = $attribute . '=' . $search;
1349
+            }
1350
+        }
1351
+        if(count($filter) === 1) {
1352
+            return '('.$filter[0].')';
1353
+        }
1354
+        return $this->combineFilterWithOr($filter);
1355
+    }
1356
+
1357
+    /**
1358
+     * returns the search term depending on whether we are allowed
1359
+     * list users found by ldap with the current input appended by
1360
+     * a *
1361
+     * @return string
1362
+     */
1363
+    private function prepareSearchTerm($term) {
1364
+        $config = \OC::$server->getConfig();
1365
+
1366
+        $allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1367
+
1368
+        $result = $term;
1369
+        if ($term === '') {
1370
+            $result = '*';
1371
+        } else if ($allowEnum !== 'no') {
1372
+            $result = $term . '*';
1373
+        }
1374
+        return $result;
1375
+    }
1376
+
1377
+    /**
1378
+     * returns the filter used for counting users
1379
+     * @return string
1380
+     */
1381
+    public function getFilterForUserCount() {
1382
+        $filter = $this->combineFilterWithAnd(array(
1383
+            $this->connection->ldapUserFilter,
1384
+            $this->connection->ldapUserDisplayName . '=*'
1385
+        ));
1386
+
1387
+        return $filter;
1388
+    }
1389
+
1390
+    /**
1391
+     * @param string $name
1392
+     * @param string $password
1393
+     * @return bool
1394
+     */
1395
+    public function areCredentialsValid($name, $password) {
1396
+        $name = $this->helper->DNasBaseParameter($name);
1397
+        $testConnection = clone $this->connection;
1398
+        $credentials = array(
1399
+            'ldapAgentName' => $name,
1400
+            'ldapAgentPassword' => $password
1401
+        );
1402
+        if(!$testConnection->setConfiguration($credentials)) {
1403
+            return false;
1404
+        }
1405
+        return $testConnection->bind();
1406
+    }
1407
+
1408
+    /**
1409
+     * reverse lookup of a DN given a known UUID
1410
+     *
1411
+     * @param string $uuid
1412
+     * @return string
1413
+     * @throws \Exception
1414
+     */
1415
+    public function getUserDnByUuid($uuid) {
1416
+        $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1417
+        $filter       = $this->connection->ldapUserFilter;
1418
+        $base         = $this->connection->ldapBaseUsers;
1419
+
1420
+        if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1421
+            // Sacrebleu! The UUID attribute is unknown :( We need first an
1422
+            // existing DN to be able to reliably detect it.
1423
+            $result = $this->search($filter, $base, ['dn'], 1);
1424
+            if(!isset($result[0]) || !isset($result[0]['dn'])) {
1425
+                throw new \Exception('Cannot determine UUID attribute');
1426
+            }
1427
+            $dn = $result[0]['dn'][0];
1428
+            if(!$this->detectUuidAttribute($dn, true)) {
1429
+                throw new \Exception('Cannot determine UUID attribute');
1430
+            }
1431
+        } else {
1432
+            // The UUID attribute is either known or an override is given.
1433
+            // By calling this method we ensure that $this->connection->$uuidAttr
1434
+            // is definitely set
1435
+            if(!$this->detectUuidAttribute('', true)) {
1436
+                throw new \Exception('Cannot determine UUID attribute');
1437
+            }
1438
+        }
1439
+
1440
+        $uuidAttr = $this->connection->ldapUuidUserAttribute;
1441
+        if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1442
+            $uuid = $this->formatGuid2ForFilterUser($uuid);
1443
+        }
1444
+
1445
+        $filter = $uuidAttr . '=' . $uuid;
1446
+        $result = $this->searchUsers($filter, ['dn'], 2);
1447
+        if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1448
+            // we put the count into account to make sure that this is
1449
+            // really unique
1450
+            return $result[0]['dn'][0];
1451
+        }
1452
+
1453
+        throw new \Exception('Cannot determine UUID attribute');
1454
+    }
1455
+
1456
+    /**
1457
+     * auto-detects the directory's UUID attribute
1458
+     * @param string $dn a known DN used to check against
1459
+     * @param bool $isUser
1460
+     * @param bool $force the detection should be run, even if it is not set to auto
1461
+     * @return bool true on success, false otherwise
1462
+     */
1463
+    private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1464
+        if($isUser) {
1465
+            $uuidAttr     = 'ldapUuidUserAttribute';
1466
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1467
+        } else {
1468
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1469
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1470
+        }
1471
+
1472
+        if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1473
+            return true;
1474
+        }
1475
+
1476
+        if (is_string($uuidOverride) && trim($uuidOverride) !== '' && !$force) {
1477
+            $this->connection->$uuidAttr = $uuidOverride;
1478
+            return true;
1479
+        }
1480
+
1481
+        // for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1482
+        $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1483
+
1484
+        foreach($testAttributes as $attribute) {
1485
+            $value = $this->readAttribute($dn, $attribute);
1486
+            if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1487
+                \OCP\Util::writeLog('user_ldap',
1488
+                                    'Setting '.$attribute.' as '.$uuidAttr,
1489
+                                    \OCP\Util::DEBUG);
1490
+                $this->connection->$uuidAttr = $attribute;
1491
+                return true;
1492
+            }
1493
+        }
1494
+        \OCP\Util::writeLog('user_ldap',
1495
+                            'Could not autodetect the UUID attribute',
1496
+                            \OCP\Util::ERROR);
1497
+
1498
+        return false;
1499
+    }
1500
+
1501
+    /**
1502
+     * @param string $dn
1503
+     * @param bool $isUser
1504
+     * @return string|bool
1505
+     */
1506
+    public function getUUID($dn, $isUser = true) {
1507
+        if($isUser) {
1508
+            $uuidAttr     = 'ldapUuidUserAttribute';
1509
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1510
+        } else {
1511
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1512
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1513
+        }
1514
+
1515
+        $uuid = false;
1516
+        if($this->detectUuidAttribute($dn, $isUser)) {
1517
+            $uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1518
+            if( !is_array($uuid)
1519
+                && $uuidOverride !== ''
1520
+                && $this->detectUuidAttribute($dn, $isUser, true)) {
1521
+                    $uuid = $this->readAttribute($dn,
1522
+                                                    $this->connection->$uuidAttr);
1523
+            }
1524
+            if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1525
+                $uuid = $uuid[0];
1526
+            }
1527
+        }
1528
+
1529
+        return $uuid;
1530
+    }
1531
+
1532
+    /**
1533
+     * converts a binary ObjectGUID into a string representation
1534
+     * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1535
+     * @return string
1536
+     * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1537
+     */
1538
+    private function convertObjectGUID2Str($oguid) {
1539
+        $hex_guid = bin2hex($oguid);
1540
+        $hex_guid_to_guid_str = '';
1541
+        for($k = 1; $k <= 4; ++$k) {
1542
+            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1543
+        }
1544
+        $hex_guid_to_guid_str .= '-';
1545
+        for($k = 1; $k <= 2; ++$k) {
1546
+            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1547
+        }
1548
+        $hex_guid_to_guid_str .= '-';
1549
+        for($k = 1; $k <= 2; ++$k) {
1550
+            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1551
+        }
1552
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1553
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1554
+
1555
+        return strtoupper($hex_guid_to_guid_str);
1556
+    }
1557
+
1558
+    /**
1559
+     * the first three blocks of the string-converted GUID happen to be in
1560
+     * reverse order. In order to use it in a filter, this needs to be
1561
+     * corrected. Furthermore the dashes need to be replaced and \\ preprended
1562
+     * to every two hax figures.
1563
+     *
1564
+     * If an invalid string is passed, it will be returned without change.
1565
+     *
1566
+     * @param string $guid
1567
+     * @return string
1568
+     */
1569
+    public function formatGuid2ForFilterUser($guid) {
1570
+        if(!is_string($guid)) {
1571
+            throw new \InvalidArgumentException('String expected');
1572
+        }
1573
+        $blocks = explode('-', $guid);
1574
+        if(count($blocks) !== 5) {
1575
+            /*
1576 1576
 			 * Why not throw an Exception instead? This method is a utility
1577 1577
 			 * called only when trying to figure out whether a "missing" known
1578 1578
 			 * LDAP user was or was not renamed on the LDAP server. And this
@@ -1583,275 +1583,275 @@  discard block
 block discarded – undo
1583 1583
 			 * an exception here would kill the experience for a valid, acting
1584 1584
 			 * user. Instead we write a log message.
1585 1585
 			 */
1586
-			\OC::$server->getLogger()->info(
1587
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1588
-				'({uuid}) probably does not match UUID configuration.',
1589
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1590
-			);
1591
-			return $guid;
1592
-		}
1593
-		for($i=0; $i < 3; $i++) {
1594
-			$pairs = str_split($blocks[$i], 2);
1595
-			$pairs = array_reverse($pairs);
1596
-			$blocks[$i] = implode('', $pairs);
1597
-		}
1598
-		for($i=0; $i < 5; $i++) {
1599
-			$pairs = str_split($blocks[$i], 2);
1600
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1601
-		}
1602
-		return implode('', $blocks);
1603
-	}
1604
-
1605
-	/**
1606
-	 * gets a SID of the domain of the given dn
1607
-	 * @param string $dn
1608
-	 * @return string|bool
1609
-	 */
1610
-	public function getSID($dn) {
1611
-		$domainDN = $this->getDomainDNFromDN($dn);
1612
-		$cacheKey = 'getSID-'.$domainDN;
1613
-		$sid = $this->connection->getFromCache($cacheKey);
1614
-		if(!is_null($sid)) {
1615
-			return $sid;
1616
-		}
1617
-
1618
-		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1619
-		if(!is_array($objectSid) || empty($objectSid)) {
1620
-			$this->connection->writeToCache($cacheKey, false);
1621
-			return false;
1622
-		}
1623
-		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
1624
-		$this->connection->writeToCache($cacheKey, $domainObjectSid);
1625
-
1626
-		return $domainObjectSid;
1627
-	}
1628
-
1629
-	/**
1630
-	 * converts a binary SID into a string representation
1631
-	 * @param string $sid
1632
-	 * @return string
1633
-	 */
1634
-	public function convertSID2Str($sid) {
1635
-		// The format of a SID binary string is as follows:
1636
-		// 1 byte for the revision level
1637
-		// 1 byte for the number n of variable sub-ids
1638
-		// 6 bytes for identifier authority value
1639
-		// n*4 bytes for n sub-ids
1640
-		//
1641
-		// Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1642
-		//  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1643
-		$revision = ord($sid[0]);
1644
-		$numberSubID = ord($sid[1]);
1645
-
1646
-		$subIdStart = 8; // 1 + 1 + 6
1647
-		$subIdLength = 4;
1648
-		if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1649
-			// Incorrect number of bytes present.
1650
-			return '';
1651
-		}
1652
-
1653
-		// 6 bytes = 48 bits can be represented using floats without loss of
1654
-		// precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1655
-		$iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1656
-
1657
-		$subIDs = array();
1658
-		for ($i = 0; $i < $numberSubID; $i++) {
1659
-			$subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1660
-			$subIDs[] = sprintf('%u', $subID[1]);
1661
-		}
1662
-
1663
-		// Result for example above: S-1-5-21-249921958-728525901-1594176202
1664
-		return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1665
-	}
1666
-
1667
-	/**
1668
-	 * checks if the given DN is part of the given base DN(s)
1669
-	 * @param string $dn the DN
1670
-	 * @param string[] $bases array containing the allowed base DN or DNs
1671
-	 * @return bool
1672
-	 */
1673
-	public function isDNPartOfBase($dn, $bases) {
1674
-		$belongsToBase = false;
1675
-		$bases = $this->helper->sanitizeDN($bases);
1676
-
1677
-		foreach($bases as $base) {
1678
-			$belongsToBase = true;
1679
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1680
-				$belongsToBase = false;
1681
-			}
1682
-			if($belongsToBase) {
1683
-				break;
1684
-			}
1685
-		}
1686
-		return $belongsToBase;
1687
-	}
1688
-
1689
-	/**
1690
-	 * resets a running Paged Search operation
1691
-	 */
1692
-	private function abandonPagedSearch() {
1693
-		if($this->connection->hasPagedResultSupport) {
1694
-			$cr = $this->connection->getConnectionResource();
1695
-			$this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1696
-			$this->getPagedSearchResultState();
1697
-			$this->lastCookie = '';
1698
-			$this->cookies = array();
1699
-		}
1700
-	}
1701
-
1702
-	/**
1703
-	 * get a cookie for the next LDAP paged search
1704
-	 * @param string $base a string with the base DN for the search
1705
-	 * @param string $filter the search filter to identify the correct search
1706
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1707
-	 * @param int $offset the offset for the new search to identify the correct search really good
1708
-	 * @return string containing the key or empty if none is cached
1709
-	 */
1710
-	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1711
-		if($offset === 0) {
1712
-			return '';
1713
-		}
1714
-		$offset -= $limit;
1715
-		//we work with cache here
1716
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1717
-		$cookie = '';
1718
-		if(isset($this->cookies[$cacheKey])) {
1719
-			$cookie = $this->cookies[$cacheKey];
1720
-			if(is_null($cookie)) {
1721
-				$cookie = '';
1722
-			}
1723
-		}
1724
-		return $cookie;
1725
-	}
1726
-
1727
-	/**
1728
-	 * checks whether an LDAP paged search operation has more pages that can be
1729
-	 * retrieved, typically when offset and limit are provided.
1730
-	 *
1731
-	 * Be very careful to use it: the last cookie value, which is inspected, can
1732
-	 * be reset by other operations. Best, call it immediately after a search(),
1733
-	 * searchUsers() or searchGroups() call. count-methods are probably safe as
1734
-	 * well. Don't rely on it with any fetchList-method.
1735
-	 * @return bool
1736
-	 */
1737
-	public function hasMoreResults() {
1738
-		if(!$this->connection->hasPagedResultSupport) {
1739
-			return false;
1740
-		}
1741
-
1742
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1743
-			// as in RFC 2696, when all results are returned, the cookie will
1744
-			// be empty.
1745
-			return false;
1746
-		}
1747
-
1748
-		return true;
1749
-	}
1750
-
1751
-	/**
1752
-	 * set a cookie for LDAP paged search run
1753
-	 * @param string $base a string with the base DN for the search
1754
-	 * @param string $filter the search filter to identify the correct search
1755
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1756
-	 * @param int $offset the offset for the run search to identify the correct search really good
1757
-	 * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1758
-	 * @return void
1759
-	 */
1760
-	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1761
-		// allow '0' for 389ds
1762
-		if(!empty($cookie) || $cookie === '0') {
1763
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1764
-			$this->cookies[$cacheKey] = $cookie;
1765
-			$this->lastCookie = $cookie;
1766
-		}
1767
-	}
1768
-
1769
-	/**
1770
-	 * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1771
-	 * @return boolean|null true on success, null or false otherwise
1772
-	 */
1773
-	public function getPagedSearchResultState() {
1774
-		$result = $this->pagedSearchedSuccessful;
1775
-		$this->pagedSearchedSuccessful = null;
1776
-		return $result;
1777
-	}
1778
-
1779
-	/**
1780
-	 * Prepares a paged search, if possible
1781
-	 * @param string $filter the LDAP filter for the search
1782
-	 * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1783
-	 * @param string[] $attr optional, when a certain attribute shall be filtered outside
1784
-	 * @param int $limit
1785
-	 * @param int $offset
1786
-	 * @return bool|true
1787
-	 */
1788
-	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1789
-		$pagedSearchOK = false;
1790
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1791
-			$offset = intval($offset); //can be null
1792
-			\OCP\Util::writeLog('user_ldap',
1793
-				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1794
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1795
-				\OCP\Util::DEBUG);
1796
-			//get the cookie from the search for the previous search, required by LDAP
1797
-			foreach($bases as $base) {
1798
-
1799
-				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1800
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1801
-					// no cookie known, although the offset is not 0. Maybe cache run out. We need
1802
-					// to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1803
-					// searching was designed by MSFT?)
1804
-					// 		Lukas: No, but thanks to reading that source I finally know!
1805
-					// '0' is valid, because 389ds
1806
-					$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
1807
-					//a bit recursive, $offset of 0 is the exit
1808
-					\OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
1809
-					$this->search($filter, array($base), $attr, $limit, $reOffset, true);
1810
-					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1811
-					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1812
-					//TODO: remember this, probably does not change in the next request...
1813
-					if(empty($cookie) && $cookie !== '0') {
1814
-						// '0' is valid, because 389ds
1815
-						$cookie = null;
1816
-					}
1817
-				}
1818
-				if(!is_null($cookie)) {
1819
-					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1820
-					$this->abandonPagedSearch();
1821
-					$pagedSearchOK = $this->ldap->controlPagedResult(
1822
-						$this->connection->getConnectionResource(), $limit,
1823
-						false, $cookie);
1824
-					if(!$pagedSearchOK) {
1825
-						return false;
1826
-					}
1827
-					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1828
-				} else {
1829
-					\OCP\Util::writeLog('user_ldap',
1830
-						'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1831
-						\OCP\Util::INFO);
1832
-				}
1833
-
1834
-			}
1835
-		/* ++ Fixing RHDS searches with pages with zero results ++
1586
+            \OC::$server->getLogger()->info(
1587
+                'Passed string does not resemble a valid GUID. Known UUID ' .
1588
+                '({uuid}) probably does not match UUID configuration.',
1589
+                [ 'app' => 'user_ldap', 'uuid' => $guid ]
1590
+            );
1591
+            return $guid;
1592
+        }
1593
+        for($i=0; $i < 3; $i++) {
1594
+            $pairs = str_split($blocks[$i], 2);
1595
+            $pairs = array_reverse($pairs);
1596
+            $blocks[$i] = implode('', $pairs);
1597
+        }
1598
+        for($i=0; $i < 5; $i++) {
1599
+            $pairs = str_split($blocks[$i], 2);
1600
+            $blocks[$i] = '\\' . implode('\\', $pairs);
1601
+        }
1602
+        return implode('', $blocks);
1603
+    }
1604
+
1605
+    /**
1606
+     * gets a SID of the domain of the given dn
1607
+     * @param string $dn
1608
+     * @return string|bool
1609
+     */
1610
+    public function getSID($dn) {
1611
+        $domainDN = $this->getDomainDNFromDN($dn);
1612
+        $cacheKey = 'getSID-'.$domainDN;
1613
+        $sid = $this->connection->getFromCache($cacheKey);
1614
+        if(!is_null($sid)) {
1615
+            return $sid;
1616
+        }
1617
+
1618
+        $objectSid = $this->readAttribute($domainDN, 'objectsid');
1619
+        if(!is_array($objectSid) || empty($objectSid)) {
1620
+            $this->connection->writeToCache($cacheKey, false);
1621
+            return false;
1622
+        }
1623
+        $domainObjectSid = $this->convertSID2Str($objectSid[0]);
1624
+        $this->connection->writeToCache($cacheKey, $domainObjectSid);
1625
+
1626
+        return $domainObjectSid;
1627
+    }
1628
+
1629
+    /**
1630
+     * converts a binary SID into a string representation
1631
+     * @param string $sid
1632
+     * @return string
1633
+     */
1634
+    public function convertSID2Str($sid) {
1635
+        // The format of a SID binary string is as follows:
1636
+        // 1 byte for the revision level
1637
+        // 1 byte for the number n of variable sub-ids
1638
+        // 6 bytes for identifier authority value
1639
+        // n*4 bytes for n sub-ids
1640
+        //
1641
+        // Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1642
+        //  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1643
+        $revision = ord($sid[0]);
1644
+        $numberSubID = ord($sid[1]);
1645
+
1646
+        $subIdStart = 8; // 1 + 1 + 6
1647
+        $subIdLength = 4;
1648
+        if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1649
+            // Incorrect number of bytes present.
1650
+            return '';
1651
+        }
1652
+
1653
+        // 6 bytes = 48 bits can be represented using floats without loss of
1654
+        // precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1655
+        $iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1656
+
1657
+        $subIDs = array();
1658
+        for ($i = 0; $i < $numberSubID; $i++) {
1659
+            $subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1660
+            $subIDs[] = sprintf('%u', $subID[1]);
1661
+        }
1662
+
1663
+        // Result for example above: S-1-5-21-249921958-728525901-1594176202
1664
+        return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1665
+    }
1666
+
1667
+    /**
1668
+     * checks if the given DN is part of the given base DN(s)
1669
+     * @param string $dn the DN
1670
+     * @param string[] $bases array containing the allowed base DN or DNs
1671
+     * @return bool
1672
+     */
1673
+    public function isDNPartOfBase($dn, $bases) {
1674
+        $belongsToBase = false;
1675
+        $bases = $this->helper->sanitizeDN($bases);
1676
+
1677
+        foreach($bases as $base) {
1678
+            $belongsToBase = true;
1679
+            if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1680
+                $belongsToBase = false;
1681
+            }
1682
+            if($belongsToBase) {
1683
+                break;
1684
+            }
1685
+        }
1686
+        return $belongsToBase;
1687
+    }
1688
+
1689
+    /**
1690
+     * resets a running Paged Search operation
1691
+     */
1692
+    private function abandonPagedSearch() {
1693
+        if($this->connection->hasPagedResultSupport) {
1694
+            $cr = $this->connection->getConnectionResource();
1695
+            $this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1696
+            $this->getPagedSearchResultState();
1697
+            $this->lastCookie = '';
1698
+            $this->cookies = array();
1699
+        }
1700
+    }
1701
+
1702
+    /**
1703
+     * get a cookie for the next LDAP paged search
1704
+     * @param string $base a string with the base DN for the search
1705
+     * @param string $filter the search filter to identify the correct search
1706
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1707
+     * @param int $offset the offset for the new search to identify the correct search really good
1708
+     * @return string containing the key or empty if none is cached
1709
+     */
1710
+    private function getPagedResultCookie($base, $filter, $limit, $offset) {
1711
+        if($offset === 0) {
1712
+            return '';
1713
+        }
1714
+        $offset -= $limit;
1715
+        //we work with cache here
1716
+        $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1717
+        $cookie = '';
1718
+        if(isset($this->cookies[$cacheKey])) {
1719
+            $cookie = $this->cookies[$cacheKey];
1720
+            if(is_null($cookie)) {
1721
+                $cookie = '';
1722
+            }
1723
+        }
1724
+        return $cookie;
1725
+    }
1726
+
1727
+    /**
1728
+     * checks whether an LDAP paged search operation has more pages that can be
1729
+     * retrieved, typically when offset and limit are provided.
1730
+     *
1731
+     * Be very careful to use it: the last cookie value, which is inspected, can
1732
+     * be reset by other operations. Best, call it immediately after a search(),
1733
+     * searchUsers() or searchGroups() call. count-methods are probably safe as
1734
+     * well. Don't rely on it with any fetchList-method.
1735
+     * @return bool
1736
+     */
1737
+    public function hasMoreResults() {
1738
+        if(!$this->connection->hasPagedResultSupport) {
1739
+            return false;
1740
+        }
1741
+
1742
+        if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1743
+            // as in RFC 2696, when all results are returned, the cookie will
1744
+            // be empty.
1745
+            return false;
1746
+        }
1747
+
1748
+        return true;
1749
+    }
1750
+
1751
+    /**
1752
+     * set a cookie for LDAP paged search run
1753
+     * @param string $base a string with the base DN for the search
1754
+     * @param string $filter the search filter to identify the correct search
1755
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1756
+     * @param int $offset the offset for the run search to identify the correct search really good
1757
+     * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1758
+     * @return void
1759
+     */
1760
+    private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1761
+        // allow '0' for 389ds
1762
+        if(!empty($cookie) || $cookie === '0') {
1763
+            $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1764
+            $this->cookies[$cacheKey] = $cookie;
1765
+            $this->lastCookie = $cookie;
1766
+        }
1767
+    }
1768
+
1769
+    /**
1770
+     * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1771
+     * @return boolean|null true on success, null or false otherwise
1772
+     */
1773
+    public function getPagedSearchResultState() {
1774
+        $result = $this->pagedSearchedSuccessful;
1775
+        $this->pagedSearchedSuccessful = null;
1776
+        return $result;
1777
+    }
1778
+
1779
+    /**
1780
+     * Prepares a paged search, if possible
1781
+     * @param string $filter the LDAP filter for the search
1782
+     * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1783
+     * @param string[] $attr optional, when a certain attribute shall be filtered outside
1784
+     * @param int $limit
1785
+     * @param int $offset
1786
+     * @return bool|true
1787
+     */
1788
+    private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1789
+        $pagedSearchOK = false;
1790
+        if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1791
+            $offset = intval($offset); //can be null
1792
+            \OCP\Util::writeLog('user_ldap',
1793
+                'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1794
+                .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1795
+                \OCP\Util::DEBUG);
1796
+            //get the cookie from the search for the previous search, required by LDAP
1797
+            foreach($bases as $base) {
1798
+
1799
+                $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1800
+                if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1801
+                    // no cookie known, although the offset is not 0. Maybe cache run out. We need
1802
+                    // to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1803
+                    // searching was designed by MSFT?)
1804
+                    // 		Lukas: No, but thanks to reading that source I finally know!
1805
+                    // '0' is valid, because 389ds
1806
+                    $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
1807
+                    //a bit recursive, $offset of 0 is the exit
1808
+                    \OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
1809
+                    $this->search($filter, array($base), $attr, $limit, $reOffset, true);
1810
+                    $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1811
+                    //still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1812
+                    //TODO: remember this, probably does not change in the next request...
1813
+                    if(empty($cookie) && $cookie !== '0') {
1814
+                        // '0' is valid, because 389ds
1815
+                        $cookie = null;
1816
+                    }
1817
+                }
1818
+                if(!is_null($cookie)) {
1819
+                    //since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1820
+                    $this->abandonPagedSearch();
1821
+                    $pagedSearchOK = $this->ldap->controlPagedResult(
1822
+                        $this->connection->getConnectionResource(), $limit,
1823
+                        false, $cookie);
1824
+                    if(!$pagedSearchOK) {
1825
+                        return false;
1826
+                    }
1827
+                    \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1828
+                } else {
1829
+                    \OCP\Util::writeLog('user_ldap',
1830
+                        'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1831
+                        \OCP\Util::INFO);
1832
+                }
1833
+
1834
+            }
1835
+        /* ++ Fixing RHDS searches with pages with zero results ++
1836 1836
 		 * We coudn't get paged searches working with our RHDS for login ($limit = 0),
1837 1837
 		 * due to pages with zero results.
1838 1838
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1839 1839
 		 * if we don't have a previous paged search.
1840 1840
 		 */
1841
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1842
-			// a search without limit was requested. However, if we do use
1843
-			// Paged Search once, we always must do it. This requires us to
1844
-			// initialize it with the configured page size.
1845
-			$this->abandonPagedSearch();
1846
-			// in case someone set it to 0 … use 500, otherwise no results will
1847
-			// be returned.
1848
-			$pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1849
-			$pagedSearchOK = $this->ldap->controlPagedResult(
1850
-				$this->connection->getConnectionResource(), $pageSize, false, ''
1851
-			);
1852
-		}
1853
-
1854
-		return $pagedSearchOK;
1855
-	}
1841
+        } else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1842
+            // a search without limit was requested. However, if we do use
1843
+            // Paged Search once, we always must do it. This requires us to
1844
+            // initialize it with the configured page size.
1845
+            $this->abandonPagedSearch();
1846
+            // in case someone set it to 0 … use 500, otherwise no results will
1847
+            // be returned.
1848
+            $pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1849
+            $pagedSearchOK = $this->ldap->controlPagedResult(
1850
+                $this->connection->getConnectionResource(), $pageSize, false, ''
1851
+            );
1852
+        }
1853
+
1854
+        return $pagedSearchOK;
1855
+    }
1856 1856
 
1857 1857
 }
Please login to merge, or discard this patch.
Spacing   +163 added lines, -163 removed lines patch added patch discarded remove patch
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 	 * @return AbstractMapping
111 111
 	 */
112 112
 	public function getUserMapper() {
113
-		if(is_null($this->userMapper)) {
113
+		if (is_null($this->userMapper)) {
114 114
 			throw new \Exception('UserMapper was not assigned to this Access instance.');
115 115
 		}
116 116
 		return $this->userMapper;
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
 	 * @return AbstractMapping
131 131
 	 */
132 132
 	public function getGroupMapper() {
133
-		if(is_null($this->groupMapper)) {
133
+		if (is_null($this->groupMapper)) {
134 134
 			throw new \Exception('GroupMapper was not assigned to this Access instance.');
135 135
 		}
136 136
 		return $this->groupMapper;
@@ -161,14 +161,14 @@  discard block
 block discarded – undo
161 161
 	 *          array if $attr is empty, false otherwise
162 162
 	 */
163 163
 	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
164
-		if(!$this->checkConnection()) {
164
+		if (!$this->checkConnection()) {
165 165
 			\OCP\Util::writeLog('user_ldap',
166 166
 				'No LDAP Connector assigned, access impossible for readAttribute.',
167 167
 				\OCP\Util::WARN);
168 168
 			return false;
169 169
 		}
170 170
 		$cr = $this->connection->getConnectionResource();
171
-		if(!$this->ldap->isResource($cr)) {
171
+		if (!$this->ldap->isResource($cr)) {
172 172
 			//LDAP not available
173 173
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
174 174
 			return false;
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
 		$isRangeRequest = false;
192 192
 		do {
193 193
 			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
194
-			if(is_bool($result)) {
194
+			if (is_bool($result)) {
195 195
 				// when an exists request was run and it was successful, an empty
196 196
 				// array must be returned
197 197
 				return $result ? [] : false;
@@ -208,22 +208,22 @@  discard block
 block discarded – undo
208 208
 			$result = $this->extractRangeData($result, $attr);
209 209
 			if (!empty($result)) {
210 210
 				$normalizedResult = $this->extractAttributeValuesFromResult(
211
-					[ $attr => $result['values'] ],
211
+					[$attr => $result['values']],
212 212
 					$attr
213 213
 				);
214 214
 				$values = array_merge($values, $normalizedResult);
215 215
 
216
-				if($result['rangeHigh'] === '*') {
216
+				if ($result['rangeHigh'] === '*') {
217 217
 					// when server replies with * as high range value, there are
218 218
 					// no more results left
219 219
 					return $values;
220 220
 				} else {
221
-					$low  = $result['rangeHigh'] + 1;
222
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
221
+					$low = $result['rangeHigh'] + 1;
222
+					$attrToRead = $result['attributeName'].';range='.$low.'-*';
223 223
 					$isRangeRequest = true;
224 224
 				}
225 225
 			}
226
-		} while($isRangeRequest);
226
+		} while ($isRangeRequest);
227 227
 
228 228
 		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
229 229
 		return false;
@@ -248,13 +248,13 @@  discard block
 block discarded – undo
248 248
 		if (!$this->ldap->isResource($rr)) {
249 249
 			if ($attribute !== '') {
250 250
 				//do not throw this message on userExists check, irritates
251
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG);
251
+				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
252 252
 			}
253 253
 			//in case an error occurs , e.g. object does not exist
254 254
 			return false;
255 255
 		}
256 256
 		if ($attribute === '' && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
257
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG);
257
+			\OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
258 258
 			return true;
259 259
 		}
260 260
 		$er = $this->ldap->firstEntry($cr, $rr);
@@ -279,12 +279,12 @@  discard block
 block discarded – undo
279 279
 	 */
280 280
 	public function extractAttributeValuesFromResult($result, $attribute) {
281 281
 		$values = [];
282
-		if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
282
+		if (isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
283 283
 			$lowercaseAttribute = strtolower($attribute);
284
-			for($i=0;$i<$result[$attribute]['count'];$i++) {
285
-				if($this->resemblesDN($attribute)) {
284
+			for ($i = 0; $i < $result[$attribute]['count']; $i++) {
285
+				if ($this->resemblesDN($attribute)) {
286 286
 					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
287
-				} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
287
+				} elseif ($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
288 288
 					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
289 289
 				} else {
290 290
 					$values[] = $result[$attribute][$i];
@@ -306,10 +306,10 @@  discard block
 block discarded – undo
306 306
 	 */
307 307
 	public function extractRangeData($result, $attribute) {
308 308
 		$keys = array_keys($result);
309
-		foreach($keys as $key) {
310
-			if($key !== $attribute && strpos($key, $attribute) === 0) {
309
+		foreach ($keys as $key) {
310
+			if ($key !== $attribute && strpos($key, $attribute) === 0) {
311 311
 				$queryData = explode(';', $key);
312
-				if(strpos($queryData[1], 'range=') === 0) {
312
+				if (strpos($queryData[1], 'range=') === 0) {
313 313
 					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
314 314
 					$data = [
315 315
 						'values' => $result[$key],
@@ -334,11 +334,11 @@  discard block
 block discarded – undo
334 334
 	 * @throws \Exception
335 335
 	 */
336 336
 	public function setPassword($userDN, $password) {
337
-		if(intval($this->connection->turnOnPasswordChange) !== 1) {
337
+		if (intval($this->connection->turnOnPasswordChange) !== 1) {
338 338
 			throw new \Exception('LDAP password changes are disabled.');
339 339
 		}
340 340
 		$cr = $this->connection->getConnectionResource();
341
-		if(!$this->ldap->isResource($cr)) {
341
+		if (!$this->ldap->isResource($cr)) {
342 342
 			//LDAP not available
343 343
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
344 344
 			return false;
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 		
347 347
 		try {
348 348
 			return $this->ldap->modReplace($cr, $userDN, $password);
349
-		} catch(ConstraintViolationException $e) {
349
+		} catch (ConstraintViolationException $e) {
350 350
 			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
351 351
 		}
352 352
 	}
@@ -388,17 +388,17 @@  discard block
 block discarded – undo
388 388
 	 */
389 389
 	public function getDomainDNFromDN($dn) {
390 390
 		$allParts = $this->ldap->explodeDN($dn, 0);
391
-		if($allParts === false) {
391
+		if ($allParts === false) {
392 392
 			//not a valid DN
393 393
 			return '';
394 394
 		}
395 395
 		$domainParts = array();
396 396
 		$dcFound = false;
397
-		foreach($allParts as $part) {
398
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
397
+		foreach ($allParts as $part) {
398
+			if (!$dcFound && strpos($part, 'dc=') === 0) {
399 399
 				$dcFound = true;
400 400
 			}
401
-			if($dcFound) {
401
+			if ($dcFound) {
402 402
 				$domainParts[] = $part;
403 403
 			}
404 404
 		}
@@ -425,7 +425,7 @@  discard block
 block discarded – undo
425 425
 
426 426
 		//Check whether the DN belongs to the Base, to avoid issues on multi-
427 427
 		//server setups
428
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
428
+		if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
429 429
 			return $fdn;
430 430
 		}
431 431
 
@@ -442,7 +442,7 @@  discard block
 block discarded – undo
442 442
 		//To avoid bypassing the base DN settings under certain circumstances
443 443
 		//with the group support, check whether the provided DN matches one of
444 444
 		//the given Bases
445
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
445
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
446 446
 			return false;
447 447
 		}
448 448
 
@@ -459,11 +459,11 @@  discard block
 block discarded – undo
459 459
 	 */
460 460
 	public function groupsMatchFilter($groupDNs) {
461 461
 		$validGroupDNs = [];
462
-		foreach($groupDNs as $dn) {
462
+		foreach ($groupDNs as $dn) {
463 463
 			$cacheKey = 'groupsMatchFilter-'.$dn;
464 464
 			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
465
-			if(!is_null($groupMatchFilter)) {
466
-				if($groupMatchFilter) {
465
+			if (!is_null($groupMatchFilter)) {
466
+				if ($groupMatchFilter) {
467 467
 					$validGroupDNs[] = $dn;
468 468
 				}
469 469
 				continue;
@@ -471,13 +471,13 @@  discard block
 block discarded – undo
471 471
 
472 472
 			// Check the base DN first. If this is not met already, we don't
473 473
 			// need to ask the server at all.
474
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
474
+			if (!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
475 475
 				$this->connection->writeToCache($cacheKey, false);
476 476
 				continue;
477 477
 			}
478 478
 
479 479
 			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
480
-			if(is_array($result)) {
480
+			if (is_array($result)) {
481 481
 				$this->connection->writeToCache($cacheKey, true);
482 482
 				$validGroupDNs[] = $dn;
483 483
 			} else {
@@ -498,7 +498,7 @@  discard block
 block discarded – undo
498 498
 		//To avoid bypassing the base DN settings under certain circumstances
499 499
 		//with the group support, check whether the provided DN matches one of
500 500
 		//the given Bases
501
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
501
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
502 502
 			return false;
503 503
 		}
504 504
 
@@ -513,7 +513,7 @@  discard block
 block discarded – undo
513 513
 	 * @return string|false with with the name to use in ownCloud
514 514
 	 */
515 515
 	public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
516
-		if($isUser) {
516
+		if ($isUser) {
517 517
 			$mapper = $this->getUserMapper();
518 518
 			$nameAttribute = $this->connection->ldapUserDisplayName;
519 519
 		} else {
@@ -523,15 +523,15 @@  discard block
 block discarded – undo
523 523
 
524 524
 		//let's try to retrieve the ownCloud name from the mappings table
525 525
 		$ocName = $mapper->getNameByDN($fdn);
526
-		if(is_string($ocName)) {
526
+		if (is_string($ocName)) {
527 527
 			return $ocName;
528 528
 		}
529 529
 
530 530
 		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
531 531
 		$uuid = $this->getUUID($fdn, $isUser);
532
-		if(is_string($uuid)) {
532
+		if (is_string($uuid)) {
533 533
 			$ocName = $mapper->getNameByUUID($uuid);
534
-			if(is_string($ocName)) {
534
+			if (is_string($ocName)) {
535 535
 				$mapper->setDNbyUUID($fdn, $uuid);
536 536
 				return $ocName;
537 537
 			}
@@ -541,16 +541,16 @@  discard block
 block discarded – undo
541 541
 			return false;
542 542
 		}
543 543
 
544
-		if(is_null($ldapName)) {
544
+		if (is_null($ldapName)) {
545 545
 			$ldapName = $this->readAttribute($fdn, $nameAttribute);
546
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
546
+			if (!isset($ldapName[0]) && empty($ldapName[0])) {
547 547
 				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
548 548
 				return false;
549 549
 			}
550 550
 			$ldapName = $ldapName[0];
551 551
 		}
552 552
 
553
-		if($isUser) {
553
+		if ($isUser) {
554 554
 			$usernameAttribute = strval($this->connection->ldapExpertUsernameAttr);
555 555
 			if ($usernameAttribute !== '') {
556 556
 				$username = $this->readAttribute($fdn, $usernameAttribute);
@@ -569,9 +569,9 @@  discard block
 block discarded – undo
569 569
 		// outside of core user management will still cache the user as non-existing.
570 570
 		$originalTTL = $this->connection->ldapCacheTTL;
571 571
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
572
-		if(($isUser && !\OCP\User::userExists($intName))
572
+		if (($isUser && !\OCP\User::userExists($intName))
573 573
 			|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) {
574
-			if($mapper->map($fdn, $intName, $uuid)) {
574
+			if ($mapper->map($fdn, $intName, $uuid)) {
575 575
 				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
576 576
 				return $intName;
577 577
 			}
@@ -579,7 +579,7 @@  discard block
 block discarded – undo
579 579
 		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
580 580
 
581 581
 		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
582
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
582
+		if (is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
583 583
 			return $altName;
584 584
 		}
585 585
 
@@ -616,7 +616,7 @@  discard block
 block discarded – undo
616 616
 	 * @return array
617 617
 	 */
618 618
 	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
619
-		if($isUsers) {
619
+		if ($isUsers) {
620 620
 			$nameAttribute = $this->connection->ldapUserDisplayName;
621 621
 			$sndAttribute  = $this->connection->ldapUserDisplayName2;
622 622
 		} else {
@@ -624,9 +624,9 @@  discard block
 block discarded – undo
624 624
 		}
625 625
 		$ownCloudNames = array();
626 626
 
627
-		foreach($ldapObjects as $ldapObject) {
627
+		foreach ($ldapObjects as $ldapObject) {
628 628
 			$nameByLDAP = null;
629
-			if(    isset($ldapObject[$nameAttribute])
629
+			if (isset($ldapObject[$nameAttribute])
630 630
 				&& is_array($ldapObject[$nameAttribute])
631 631
 				&& isset($ldapObject[$nameAttribute][0])
632 632
 			) {
@@ -635,12 +635,12 @@  discard block
 block discarded – undo
635 635
 			}
636 636
 
637 637
 			$ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
638
-			if($ocName) {
638
+			if ($ocName) {
639 639
 				$ownCloudNames[] = $ocName;
640
-				if($isUsers) {
640
+				if ($isUsers) {
641 641
 					//cache the user names so it does not need to be retrieved
642 642
 					//again later (e.g. sharing dialogue).
643
-					if(is_null($nameByLDAP)) {
643
+					if (is_null($nameByLDAP)) {
644 644
 						continue;
645 645
 					}
646 646
 					$sndName = isset($ldapObject[$sndAttribute][0])
@@ -678,7 +678,7 @@  discard block
 block discarded – undo
678 678
 	 */
679 679
 	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
680 680
 		$user = $this->userManager->get($ocName);
681
-		if($user === null) {
681
+		if ($user === null) {
682 682
 			return;
683 683
 		}
684 684
 		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
@@ -698,9 +698,9 @@  discard block
 block discarded – undo
698 698
 		$attempts = 0;
699 699
 		//while loop is just a precaution. If a name is not generated within
700 700
 		//20 attempts, something else is very wrong. Avoids infinite loop.
701
-		while($attempts < 20){
702
-			$altName = $name . '_' . rand(1000,9999);
703
-			if(!\OCP\User::userExists($altName)) {
701
+		while ($attempts < 20) {
702
+			$altName = $name.'_'.rand(1000, 9999);
703
+			if (!\OCP\User::userExists($altName)) {
704 704
 				return $altName;
705 705
 			}
706 706
 			$attempts++;
@@ -722,25 +722,25 @@  discard block
 block discarded – undo
722 722
 	 */
723 723
 	private function _createAltInternalOwnCloudNameForGroups($name) {
724 724
 		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
725
-		if(!($usedNames) || count($usedNames) === 0) {
725
+		if (!($usedNames) || count($usedNames) === 0) {
726 726
 			$lastNo = 1; //will become name_2
727 727
 		} else {
728 728
 			natsort($usedNames);
729 729
 			$lastName = array_pop($usedNames);
730 730
 			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
731 731
 		}
732
-		$altName = $name.'_'.strval($lastNo+1);
732
+		$altName = $name.'_'.strval($lastNo + 1);
733 733
 		unset($usedNames);
734 734
 
735 735
 		$attempts = 1;
736
-		while($attempts < 21){
736
+		while ($attempts < 21) {
737 737
 			// Check to be really sure it is unique
738 738
 			// while loop is just a precaution. If a name is not generated within
739 739
 			// 20 attempts, something else is very wrong. Avoids infinite loop.
740
-			if(!\OC::$server->getGroupManager()->groupExists($altName)) {
740
+			if (!\OC::$server->getGroupManager()->groupExists($altName)) {
741 741
 				return $altName;
742 742
 			}
743
-			$altName = $name . '_' . ($lastNo + $attempts);
743
+			$altName = $name.'_'.($lastNo + $attempts);
744 744
 			$attempts++;
745 745
 		}
746 746
 		return false;
@@ -755,7 +755,7 @@  discard block
 block discarded – undo
755 755
 	private function createAltInternalOwnCloudName($name, $isUser) {
756 756
 		$originalTTL = $this->connection->ldapCacheTTL;
757 757
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
758
-		if($isUser) {
758
+		if ($isUser) {
759 759
 			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
760 760
 		} else {
761 761
 			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
@@ -813,20 +813,20 @@  discard block
 block discarded – undo
813 813
 	 * and their values
814 814
 	 * @param array $ldapRecords
815 815
 	 */
816
-	public function batchApplyUserAttributes(array $ldapRecords){
816
+	public function batchApplyUserAttributes(array $ldapRecords) {
817 817
 		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
818
-		foreach($ldapRecords as $userRecord) {
819
-			if(!isset($userRecord[$displayNameAttribute])) {
818
+		foreach ($ldapRecords as $userRecord) {
819
+			if (!isset($userRecord[$displayNameAttribute])) {
820 820
 				// displayName is obligatory
821 821
 				continue;
822 822
 			}
823
-			$ocName  = $this->dn2ocname($userRecord['dn'][0]);
824
-			if($ocName === false) {
823
+			$ocName = $this->dn2ocname($userRecord['dn'][0]);
824
+			if ($ocName === false) {
825 825
 				continue;
826 826
 			}
827 827
 			$this->cacheUserExists($ocName);
828 828
 			$user = $this->userManager->get($ocName);
829
-			if($user instanceof OfflineUser) {
829
+			if ($user instanceof OfflineUser) {
830 830
 				$user->unmark();
831 831
 				$user = $this->userManager->get($ocName);
832 832
 			}
@@ -858,8 +858,8 @@  discard block
 block discarded – undo
858 858
 	 * @return array
859 859
 	 */
860 860
 	private function fetchList($list, $manyAttributes) {
861
-		if(is_array($list)) {
862
-			if($manyAttributes) {
861
+		if (is_array($list)) {
862
+			if ($manyAttributes) {
863 863
 				return $list;
864 864
 			} else {
865 865
 				$list = array_reduce($list, function($carry, $item) {
@@ -945,13 +945,13 @@  discard block
 block discarded – undo
945 945
 	 * second | false if not successful
946 946
 	 */
947 947
 	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
948
-		if(!is_null($attr) && !is_array($attr)) {
948
+		if (!is_null($attr) && !is_array($attr)) {
949 949
 			$attr = array(mb_strtolower($attr, 'UTF-8'));
950 950
 		}
951 951
 
952 952
 		// See if we have a resource, in case not cancel with message
953 953
 		$cr = $this->connection->getConnectionResource();
954
-		if(!$this->ldap->isResource($cr)) {
954
+		if (!$this->ldap->isResource($cr)) {
955 955
 			// Seems like we didn't find any resource.
956 956
 			// Return an empty array just like before.
957 957
 			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
@@ -964,7 +964,7 @@  discard block
 block discarded – undo
964 964
 		$linkResources = array_pad(array(), count($base), $cr);
965 965
 		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
966 966
 		$error = $this->ldap->errno($cr);
967
-		if(!is_array($sr) || $error !== 0) {
967
+		if (!is_array($sr) || $error !== 0) {
968 968
 			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
969 969
 			return false;
970 970
 		}
@@ -987,26 +987,26 @@  discard block
 block discarded – undo
987 987
 	 */
988 988
 	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
989 989
 		$cookie = null;
990
-		if($pagedSearchOK) {
990
+		if ($pagedSearchOK) {
991 991
 			$cr = $this->connection->getConnectionResource();
992
-			foreach($sr as $key => $res) {
993
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
992
+			foreach ($sr as $key => $res) {
993
+				if ($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
994 994
 					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
995 995
 				}
996 996
 			}
997 997
 
998 998
 			//browsing through prior pages to get the cookie for the new one
999
-			if($skipHandling) {
999
+			if ($skipHandling) {
1000 1000
 				return false;
1001 1001
 			}
1002 1002
 			// if count is bigger, then the server does not support
1003 1003
 			// paged search. Instead, he did a normal search. We set a
1004 1004
 			// flag here, so the callee knows how to deal with it.
1005
-			if($iFoundItems <= $limit) {
1005
+			if ($iFoundItems <= $limit) {
1006 1006
 				$this->pagedSearchedSuccessful = true;
1007 1007
 			}
1008 1008
 		} else {
1009
-			if(!is_null($limit)) {
1009
+			if (!is_null($limit)) {
1010 1010
 				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
1011 1011
 			}
1012 1012
 		}
@@ -1035,7 +1035,7 @@  discard block
 block discarded – undo
1035 1035
 		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
1036 1036
 
1037 1037
 		$limitPerPage = intval($this->connection->ldapPagingSize);
1038
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1038
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1039 1039
 			$limitPerPage = $limit;
1040 1040
 		}
1041 1041
 
@@ -1046,7 +1046,7 @@  discard block
 block discarded – undo
1046 1046
 		do {
1047 1047
 			$search = $this->executeSearch($filter, $base, $attr,
1048 1048
 										   $limitPerPage, $offset);
1049
-			if($search === false) {
1049
+			if ($search === false) {
1050 1050
 				return $counter > 0 ? $counter : false;
1051 1051
 			}
1052 1052
 			list($sr, $pagedSearchOK) = $search;
@@ -1065,7 +1065,7 @@  discard block
 block discarded – undo
1065 1065
 			 * Continue now depends on $hasMorePages value
1066 1066
 			 */
1067 1067
 			$continue = $pagedSearchOK && $hasMorePages;
1068
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1068
+		} while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1069 1069
 
1070 1070
 		return $counter;
1071 1071
 	}
@@ -1078,7 +1078,7 @@  discard block
 block discarded – undo
1078 1078
 		$cr = $this->connection->getConnectionResource();
1079 1079
 		$counter = 0;
1080 1080
 
1081
-		foreach($searchResults as $res) {
1081
+		foreach ($searchResults as $res) {
1082 1082
 			$count = intval($this->ldap->countEntries($cr, $res));
1083 1083
 			$counter += $count;
1084 1084
 		}
@@ -1097,7 +1097,7 @@  discard block
 block discarded – undo
1097 1097
 	 * @return array with the search result
1098 1098
 	 */
1099 1099
 	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1100
-		if($limit <= 0) {
1100
+		if ($limit <= 0) {
1101 1101
 			//otherwise search will fail
1102 1102
 			$limit = null;
1103 1103
 		}
@@ -1112,13 +1112,13 @@  discard block
 block discarded – undo
1112 1112
 		$savedoffset = $offset;
1113 1113
 		do {
1114 1114
 			$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
1115
-			if($search === false) {
1115
+			if ($search === false) {
1116 1116
 				return array();
1117 1117
 			}
1118 1118
 			list($sr, $pagedSearchOK) = $search;
1119 1119
 			$cr = $this->connection->getConnectionResource();
1120 1120
 
1121
-			if($skipHandling) {
1121
+			if ($skipHandling) {
1122 1122
 				//i.e. result do not need to be fetched, we just need the cookie
1123 1123
 				//thus pass 1 or any other value as $iFoundItems because it is not
1124 1124
 				//used
@@ -1128,8 +1128,8 @@  discard block
 block discarded – undo
1128 1128
 				return array();
1129 1129
 			}
1130 1130
 
1131
-			foreach($sr as $res) {
1132
-				$findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1131
+			foreach ($sr as $res) {
1132
+				$findings = array_merge($findings, $this->ldap->getEntries($cr, $res));
1133 1133
 			}
1134 1134
 
1135 1135
 			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
@@ -1142,25 +1142,25 @@  discard block
 block discarded – undo
1142 1142
 
1143 1143
 		// if we're here, probably no connection resource is returned.
1144 1144
 		// to make ownCloud behave nicely, we simply give back an empty array.
1145
-		if(is_null($findings)) {
1145
+		if (is_null($findings)) {
1146 1146
 			return array();
1147 1147
 		}
1148 1148
 
1149
-		if(!is_null($attr)) {
1149
+		if (!is_null($attr)) {
1150 1150
 			$selection = array();
1151 1151
 			$i = 0;
1152
-			foreach($findings as $item) {
1153
-				if(!is_array($item)) {
1152
+			foreach ($findings as $item) {
1153
+				if (!is_array($item)) {
1154 1154
 					continue;
1155 1155
 				}
1156 1156
 				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1157
-				foreach($attr as $key) {
1157
+				foreach ($attr as $key) {
1158 1158
 					$key = mb_strtolower($key, 'UTF-8');
1159
-					if(isset($item[$key])) {
1160
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1159
+					if (isset($item[$key])) {
1160
+						if (is_array($item[$key]) && isset($item[$key]['count'])) {
1161 1161
 							unset($item[$key]['count']);
1162 1162
 						}
1163
-						if($key !== 'dn') {
1163
+						if ($key !== 'dn') {
1164 1164
 							$selection[$i][$key] = $this->resemblesDN($key) ?
1165 1165
 								$this->helper->sanitizeDN($item[$key])
1166 1166
 								: $item[$key];
@@ -1177,7 +1177,7 @@  discard block
 block discarded – undo
1177 1177
 		//we slice the findings, when
1178 1178
 		//a) paged search unsuccessful, though attempted
1179 1179
 		//b) no paged search, but limit set
1180
-		if((!$this->getPagedSearchResultState()
1180
+		if ((!$this->getPagedSearchResultState()
1181 1181
 			&& $pagedSearchOK)
1182 1182
 			|| (
1183 1183
 				!$pagedSearchOK
@@ -1194,7 +1194,7 @@  discard block
 block discarded – undo
1194 1194
 	 * @return bool|mixed|string
1195 1195
 	 */
1196 1196
 	public function sanitizeUsername($name) {
1197
-		if($this->connection->ldapIgnoreNamingRules) {
1197
+		if ($this->connection->ldapIgnoreNamingRules) {
1198 1198
 			return $name;
1199 1199
 		}
1200 1200
 
@@ -1219,13 +1219,13 @@  discard block
 block discarded – undo
1219 1219
 	*/
1220 1220
 	public function escapeFilterPart($input, $allowAsterisk = false) {
1221 1221
 		$asterisk = '';
1222
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1222
+		if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1223 1223
 			$asterisk = '*';
1224 1224
 			$input = mb_substr($input, 1, null, 'UTF-8');
1225 1225
 		}
1226 1226
 		$search  = array('*', '\\', '(', ')');
1227 1227
 		$replace = array('\\*', '\\\\', '\\(', '\\)');
1228
-		return $asterisk . str_replace($search, $replace, $input);
1228
+		return $asterisk.str_replace($search, $replace, $input);
1229 1229
 	}
1230 1230
 
1231 1231
 	/**
@@ -1255,13 +1255,13 @@  discard block
 block discarded – undo
1255 1255
 	 */
1256 1256
 	private function combineFilter($filters, $operator) {
1257 1257
 		$combinedFilter = '('.$operator;
1258
-		foreach($filters as $filter) {
1258
+		foreach ($filters as $filter) {
1259 1259
 			if ($filter !== '' && $filter[0] !== '(') {
1260 1260
 				$filter = '('.$filter.')';
1261 1261
 			}
1262
-			$combinedFilter.=$filter;
1262
+			$combinedFilter .= $filter;
1263 1263
 		}
1264
-		$combinedFilter.=')';
1264
+		$combinedFilter .= ')';
1265 1265
 		return $combinedFilter;
1266 1266
 	}
1267 1267
 
@@ -1297,17 +1297,17 @@  discard block
 block discarded – undo
1297 1297
 	 * @throws \Exception
1298 1298
 	 */
1299 1299
 	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1300
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1300
+		if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1301 1301
 			throw new \Exception('searchAttributes must be an array with at least two string');
1302 1302
 		}
1303 1303
 		$searchWords = explode(' ', trim($search));
1304 1304
 		$wordFilters = array();
1305
-		foreach($searchWords as $word) {
1305
+		foreach ($searchWords as $word) {
1306 1306
 			$word = $this->prepareSearchTerm($word);
1307 1307
 			//every word needs to appear at least once
1308 1308
 			$wordMatchOneAttrFilters = array();
1309
-			foreach($searchAttributes as $attr) {
1310
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1309
+			foreach ($searchAttributes as $attr) {
1310
+				$wordMatchOneAttrFilters[] = $attr.'='.$word;
1311 1311
 			}
1312 1312
 			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1313 1313
 		}
@@ -1325,10 +1325,10 @@  discard block
 block discarded – undo
1325 1325
 	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1326 1326
 		$filter = array();
1327 1327
 		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1328
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1328
+		if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1329 1329
 			try {
1330 1330
 				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1331
-			} catch(\Exception $e) {
1331
+			} catch (\Exception $e) {
1332 1332
 				\OCP\Util::writeLog(
1333 1333
 					'user_ldap',
1334 1334
 					'Creating advanced filter for search failed, falling back to simple method.',
@@ -1338,17 +1338,17 @@  discard block
 block discarded – undo
1338 1338
 		}
1339 1339
 
1340 1340
 		$search = $this->prepareSearchTerm($search);
1341
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1341
+		if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1342 1342
 			if ($fallbackAttribute === '') {
1343 1343
 				return '';
1344 1344
 			}
1345
-			$filter[] = $fallbackAttribute . '=' . $search;
1345
+			$filter[] = $fallbackAttribute.'='.$search;
1346 1346
 		} else {
1347
-			foreach($searchAttributes as $attribute) {
1348
-				$filter[] = $attribute . '=' . $search;
1347
+			foreach ($searchAttributes as $attribute) {
1348
+				$filter[] = $attribute.'='.$search;
1349 1349
 			}
1350 1350
 		}
1351
-		if(count($filter) === 1) {
1351
+		if (count($filter) === 1) {
1352 1352
 			return '('.$filter[0].')';
1353 1353
 		}
1354 1354
 		return $this->combineFilterWithOr($filter);
@@ -1369,7 +1369,7 @@  discard block
 block discarded – undo
1369 1369
 		if ($term === '') {
1370 1370
 			$result = '*';
1371 1371
 		} else if ($allowEnum !== 'no') {
1372
-			$result = $term . '*';
1372
+			$result = $term.'*';
1373 1373
 		}
1374 1374
 		return $result;
1375 1375
 	}
@@ -1381,7 +1381,7 @@  discard block
 block discarded – undo
1381 1381
 	public function getFilterForUserCount() {
1382 1382
 		$filter = $this->combineFilterWithAnd(array(
1383 1383
 			$this->connection->ldapUserFilter,
1384
-			$this->connection->ldapUserDisplayName . '=*'
1384
+			$this->connection->ldapUserDisplayName.'=*'
1385 1385
 		));
1386 1386
 
1387 1387
 		return $filter;
@@ -1399,7 +1399,7 @@  discard block
 block discarded – undo
1399 1399
 			'ldapAgentName' => $name,
1400 1400
 			'ldapAgentPassword' => $password
1401 1401
 		);
1402
-		if(!$testConnection->setConfiguration($credentials)) {
1402
+		if (!$testConnection->setConfiguration($credentials)) {
1403 1403
 			return false;
1404 1404
 		}
1405 1405
 		return $testConnection->bind();
@@ -1421,30 +1421,30 @@  discard block
 block discarded – undo
1421 1421
 			// Sacrebleu! The UUID attribute is unknown :( We need first an
1422 1422
 			// existing DN to be able to reliably detect it.
1423 1423
 			$result = $this->search($filter, $base, ['dn'], 1);
1424
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1424
+			if (!isset($result[0]) || !isset($result[0]['dn'])) {
1425 1425
 				throw new \Exception('Cannot determine UUID attribute');
1426 1426
 			}
1427 1427
 			$dn = $result[0]['dn'][0];
1428
-			if(!$this->detectUuidAttribute($dn, true)) {
1428
+			if (!$this->detectUuidAttribute($dn, true)) {
1429 1429
 				throw new \Exception('Cannot determine UUID attribute');
1430 1430
 			}
1431 1431
 		} else {
1432 1432
 			// The UUID attribute is either known or an override is given.
1433 1433
 			// By calling this method we ensure that $this->connection->$uuidAttr
1434 1434
 			// is definitely set
1435
-			if(!$this->detectUuidAttribute('', true)) {
1435
+			if (!$this->detectUuidAttribute('', true)) {
1436 1436
 				throw new \Exception('Cannot determine UUID attribute');
1437 1437
 			}
1438 1438
 		}
1439 1439
 
1440 1440
 		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1441
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1441
+		if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1442 1442
 			$uuid = $this->formatGuid2ForFilterUser($uuid);
1443 1443
 		}
1444 1444
 
1445
-		$filter = $uuidAttr . '=' . $uuid;
1445
+		$filter = $uuidAttr.'='.$uuid;
1446 1446
 		$result = $this->searchUsers($filter, ['dn'], 2);
1447
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1447
+		if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1448 1448
 			// we put the count into account to make sure that this is
1449 1449
 			// really unique
1450 1450
 			return $result[0]['dn'][0];
@@ -1461,7 +1461,7 @@  discard block
 block discarded – undo
1461 1461
 	 * @return bool true on success, false otherwise
1462 1462
 	 */
1463 1463
 	private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1464
-		if($isUser) {
1464
+		if ($isUser) {
1465 1465
 			$uuidAttr     = 'ldapUuidUserAttribute';
1466 1466
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1467 1467
 		} else {
@@ -1469,7 +1469,7 @@  discard block
 block discarded – undo
1469 1469
 			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1470 1470
 		}
1471 1471
 
1472
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1472
+		if (($this->connection->$uuidAttr !== 'auto') && !$force) {
1473 1473
 			return true;
1474 1474
 		}
1475 1475
 
@@ -1481,9 +1481,9 @@  discard block
 block discarded – undo
1481 1481
 		// for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1482 1482
 		$testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1483 1483
 
1484
-		foreach($testAttributes as $attribute) {
1484
+		foreach ($testAttributes as $attribute) {
1485 1485
 			$value = $this->readAttribute($dn, $attribute);
1486
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1486
+			if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1487 1487
 				\OCP\Util::writeLog('user_ldap',
1488 1488
 									'Setting '.$attribute.' as '.$uuidAttr,
1489 1489
 									\OCP\Util::DEBUG);
@@ -1504,7 +1504,7 @@  discard block
 block discarded – undo
1504 1504
 	 * @return string|bool
1505 1505
 	 */
1506 1506
 	public function getUUID($dn, $isUser = true) {
1507
-		if($isUser) {
1507
+		if ($isUser) {
1508 1508
 			$uuidAttr     = 'ldapUuidUserAttribute';
1509 1509
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1510 1510
 		} else {
@@ -1513,15 +1513,15 @@  discard block
 block discarded – undo
1513 1513
 		}
1514 1514
 
1515 1515
 		$uuid = false;
1516
-		if($this->detectUuidAttribute($dn, $isUser)) {
1516
+		if ($this->detectUuidAttribute($dn, $isUser)) {
1517 1517
 			$uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1518
-			if( !is_array($uuid)
1518
+			if (!is_array($uuid)
1519 1519
 				&& $uuidOverride !== ''
1520 1520
 				&& $this->detectUuidAttribute($dn, $isUser, true)) {
1521 1521
 					$uuid = $this->readAttribute($dn,
1522 1522
 												 $this->connection->$uuidAttr);
1523 1523
 			}
1524
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1524
+			if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1525 1525
 				$uuid = $uuid[0];
1526 1526
 			}
1527 1527
 		}
@@ -1538,19 +1538,19 @@  discard block
 block discarded – undo
1538 1538
 	private function convertObjectGUID2Str($oguid) {
1539 1539
 		$hex_guid = bin2hex($oguid);
1540 1540
 		$hex_guid_to_guid_str = '';
1541
-		for($k = 1; $k <= 4; ++$k) {
1541
+		for ($k = 1; $k <= 4; ++$k) {
1542 1542
 			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1543 1543
 		}
1544 1544
 		$hex_guid_to_guid_str .= '-';
1545
-		for($k = 1; $k <= 2; ++$k) {
1545
+		for ($k = 1; $k <= 2; ++$k) {
1546 1546
 			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1547 1547
 		}
1548 1548
 		$hex_guid_to_guid_str .= '-';
1549
-		for($k = 1; $k <= 2; ++$k) {
1549
+		for ($k = 1; $k <= 2; ++$k) {
1550 1550
 			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1551 1551
 		}
1552
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1553
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1552
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 16, 4);
1553
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 20);
1554 1554
 
1555 1555
 		return strtoupper($hex_guid_to_guid_str);
1556 1556
 	}
@@ -1567,11 +1567,11 @@  discard block
 block discarded – undo
1567 1567
 	 * @return string
1568 1568
 	 */
1569 1569
 	public function formatGuid2ForFilterUser($guid) {
1570
-		if(!is_string($guid)) {
1570
+		if (!is_string($guid)) {
1571 1571
 			throw new \InvalidArgumentException('String expected');
1572 1572
 		}
1573 1573
 		$blocks = explode('-', $guid);
1574
-		if(count($blocks) !== 5) {
1574
+		if (count($blocks) !== 5) {
1575 1575
 			/*
1576 1576
 			 * Why not throw an Exception instead? This method is a utility
1577 1577
 			 * called only when trying to figure out whether a "missing" known
@@ -1584,20 +1584,20 @@  discard block
 block discarded – undo
1584 1584
 			 * user. Instead we write a log message.
1585 1585
 			 */
1586 1586
 			\OC::$server->getLogger()->info(
1587
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1587
+				'Passed string does not resemble a valid GUID. Known UUID '.
1588 1588
 				'({uuid}) probably does not match UUID configuration.',
1589
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1589
+				['app' => 'user_ldap', 'uuid' => $guid]
1590 1590
 			);
1591 1591
 			return $guid;
1592 1592
 		}
1593
-		for($i=0; $i < 3; $i++) {
1593
+		for ($i = 0; $i < 3; $i++) {
1594 1594
 			$pairs = str_split($blocks[$i], 2);
1595 1595
 			$pairs = array_reverse($pairs);
1596 1596
 			$blocks[$i] = implode('', $pairs);
1597 1597
 		}
1598
-		for($i=0; $i < 5; $i++) {
1598
+		for ($i = 0; $i < 5; $i++) {
1599 1599
 			$pairs = str_split($blocks[$i], 2);
1600
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1600
+			$blocks[$i] = '\\'.implode('\\', $pairs);
1601 1601
 		}
1602 1602
 		return implode('', $blocks);
1603 1603
 	}
@@ -1611,12 +1611,12 @@  discard block
 block discarded – undo
1611 1611
 		$domainDN = $this->getDomainDNFromDN($dn);
1612 1612
 		$cacheKey = 'getSID-'.$domainDN;
1613 1613
 		$sid = $this->connection->getFromCache($cacheKey);
1614
-		if(!is_null($sid)) {
1614
+		if (!is_null($sid)) {
1615 1615
 			return $sid;
1616 1616
 		}
1617 1617
 
1618 1618
 		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1619
-		if(!is_array($objectSid) || empty($objectSid)) {
1619
+		if (!is_array($objectSid) || empty($objectSid)) {
1620 1620
 			$this->connection->writeToCache($cacheKey, false);
1621 1621
 			return false;
1622 1622
 		}
@@ -1674,12 +1674,12 @@  discard block
 block discarded – undo
1674 1674
 		$belongsToBase = false;
1675 1675
 		$bases = $this->helper->sanitizeDN($bases);
1676 1676
 
1677
-		foreach($bases as $base) {
1677
+		foreach ($bases as $base) {
1678 1678
 			$belongsToBase = true;
1679
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1679
+			if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1680 1680
 				$belongsToBase = false;
1681 1681
 			}
1682
-			if($belongsToBase) {
1682
+			if ($belongsToBase) {
1683 1683
 				break;
1684 1684
 			}
1685 1685
 		}
@@ -1690,7 +1690,7 @@  discard block
 block discarded – undo
1690 1690
 	 * resets a running Paged Search operation
1691 1691
 	 */
1692 1692
 	private function abandonPagedSearch() {
1693
-		if($this->connection->hasPagedResultSupport) {
1693
+		if ($this->connection->hasPagedResultSupport) {
1694 1694
 			$cr = $this->connection->getConnectionResource();
1695 1695
 			$this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1696 1696
 			$this->getPagedSearchResultState();
@@ -1708,16 +1708,16 @@  discard block
 block discarded – undo
1708 1708
 	 * @return string containing the key or empty if none is cached
1709 1709
 	 */
1710 1710
 	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1711
-		if($offset === 0) {
1711
+		if ($offset === 0) {
1712 1712
 			return '';
1713 1713
 		}
1714 1714
 		$offset -= $limit;
1715 1715
 		//we work with cache here
1716
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1716
+		$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1717 1717
 		$cookie = '';
1718
-		if(isset($this->cookies[$cacheKey])) {
1718
+		if (isset($this->cookies[$cacheKey])) {
1719 1719
 			$cookie = $this->cookies[$cacheKey];
1720
-			if(is_null($cookie)) {
1720
+			if (is_null($cookie)) {
1721 1721
 				$cookie = '';
1722 1722
 			}
1723 1723
 		}
@@ -1735,11 +1735,11 @@  discard block
 block discarded – undo
1735 1735
 	 * @return bool
1736 1736
 	 */
1737 1737
 	public function hasMoreResults() {
1738
-		if(!$this->connection->hasPagedResultSupport) {
1738
+		if (!$this->connection->hasPagedResultSupport) {
1739 1739
 			return false;
1740 1740
 		}
1741 1741
 
1742
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1742
+		if (empty($this->lastCookie) && $this->lastCookie !== '0') {
1743 1743
 			// as in RFC 2696, when all results are returned, the cookie will
1744 1744
 			// be empty.
1745 1745
 			return false;
@@ -1759,8 +1759,8 @@  discard block
 block discarded – undo
1759 1759
 	 */
1760 1760
 	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1761 1761
 		// allow '0' for 389ds
1762
-		if(!empty($cookie) || $cookie === '0') {
1763
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1762
+		if (!empty($cookie) || $cookie === '0') {
1763
+			$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1764 1764
 			$this->cookies[$cacheKey] = $cookie;
1765 1765
 			$this->lastCookie = $cookie;
1766 1766
 		}
@@ -1787,17 +1787,17 @@  discard block
 block discarded – undo
1787 1787
 	 */
1788 1788
 	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1789 1789
 		$pagedSearchOK = false;
1790
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1790
+		if ($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1791 1791
 			$offset = intval($offset); //can be null
1792 1792
 			\OCP\Util::writeLog('user_ldap',
1793 1793
 				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1794
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1794
+				.' attr '.print_r($attr, true).' limit '.$limit.' offset '.$offset,
1795 1795
 				\OCP\Util::DEBUG);
1796 1796
 			//get the cookie from the search for the previous search, required by LDAP
1797
-			foreach($bases as $base) {
1797
+			foreach ($bases as $base) {
1798 1798
 
1799 1799
 				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1800
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1800
+				if (empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1801 1801
 					// no cookie known, although the offset is not 0. Maybe cache run out. We need
1802 1802
 					// to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1803 1803
 					// searching was designed by MSFT?)
@@ -1810,18 +1810,18 @@  discard block
 block discarded – undo
1810 1810
 					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1811 1811
 					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1812 1812
 					//TODO: remember this, probably does not change in the next request...
1813
-					if(empty($cookie) && $cookie !== '0') {
1813
+					if (empty($cookie) && $cookie !== '0') {
1814 1814
 						// '0' is valid, because 389ds
1815 1815
 						$cookie = null;
1816 1816
 					}
1817 1817
 				}
1818
-				if(!is_null($cookie)) {
1818
+				if (!is_null($cookie)) {
1819 1819
 					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1820 1820
 					$this->abandonPagedSearch();
1821 1821
 					$pagedSearchOK = $this->ldap->controlPagedResult(
1822 1822
 						$this->connection->getConnectionResource(), $limit,
1823 1823
 						false, $cookie);
1824
-					if(!$pagedSearchOK) {
1824
+					if (!$pagedSearchOK) {
1825 1825
 						return false;
1826 1826
 					}
1827 1827
 					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
@@ -1838,7 +1838,7 @@  discard block
 block discarded – undo
1838 1838
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1839 1839
 		 * if we don't have a previous paged search.
1840 1840
 		 */
1841
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1841
+		} else if ($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1842 1842
 			// a search without limit was requested. However, if we do use
1843 1843
 			// Paged Search once, we always must do it. This requires us to
1844 1844
 			// initialize it with the configured page size.
Please login to merge, or discard this patch.
apps/user_ldap/appinfo/app.php 2 patches
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -32,44 +32,44 @@
 block discarded – undo
32 32
 $ldapWrapper = new OCA\User_LDAP\LDAP();
33 33
 $ocConfig = \OC::$server->getConfig();
34 34
 if(count($configPrefixes) === 1) {
35
-	$dbc = \OC::$server->getDatabaseConnection();
36
-	$userManager = new OCA\User_LDAP\User\Manager($ocConfig,
37
-		new OCA\User_LDAP\FilesystemHelper(),
38
-		new OCA\User_LDAP\LogWrapper(),
39
-		\OC::$server->getAvatarManager(),
40
-		new \OCP\Image(),
41
-		$dbc,
42
-		\OC::$server->getUserManager()
43
-	);
44
-	$connector = new OCA\User_LDAP\Connection($ldapWrapper, $configPrefixes[0]);
45
-	$ldapAccess = new OCA\User_LDAP\Access($connector, $ldapWrapper, $userManager, $helper);
35
+    $dbc = \OC::$server->getDatabaseConnection();
36
+    $userManager = new OCA\User_LDAP\User\Manager($ocConfig,
37
+        new OCA\User_LDAP\FilesystemHelper(),
38
+        new OCA\User_LDAP\LogWrapper(),
39
+        \OC::$server->getAvatarManager(),
40
+        new \OCP\Image(),
41
+        $dbc,
42
+        \OC::$server->getUserManager()
43
+    );
44
+    $connector = new OCA\User_LDAP\Connection($ldapWrapper, $configPrefixes[0]);
45
+    $ldapAccess = new OCA\User_LDAP\Access($connector, $ldapWrapper, $userManager, $helper);
46 46
 
47
-	$ldapAccess->setUserMapper(new OCA\User_LDAP\Mapping\UserMapping($dbc));
48
-	$ldapAccess->setGroupMapper(new OCA\User_LDAP\Mapping\GroupMapping($dbc));
49
-	$userBackend  = new OCA\User_LDAP\User_LDAP($ldapAccess, $ocConfig);
50
-	$groupBackend = new \OCA\User_LDAP\Group_LDAP($ldapAccess);
47
+    $ldapAccess->setUserMapper(new OCA\User_LDAP\Mapping\UserMapping($dbc));
48
+    $ldapAccess->setGroupMapper(new OCA\User_LDAP\Mapping\GroupMapping($dbc));
49
+    $userBackend  = new OCA\User_LDAP\User_LDAP($ldapAccess, $ocConfig);
50
+    $groupBackend = new \OCA\User_LDAP\Group_LDAP($ldapAccess);
51 51
 } else if(count($configPrefixes) > 1) {
52
-	$userBackend  = new OCA\User_LDAP\User_Proxy(
53
-		$configPrefixes, $ldapWrapper, $ocConfig
54
-	);
55
-	$groupBackend  = new OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
52
+    $userBackend  = new OCA\User_LDAP\User_Proxy(
53
+        $configPrefixes, $ldapWrapper, $ocConfig
54
+    );
55
+    $groupBackend  = new OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
56 56
 }
57 57
 
58 58
 if(count($configPrefixes) > 0) {
59
-	// register user backend
60
-	OC_User::useBackend($userBackend);
61
-	\OC::$server->getGroupManager()->addBackend($groupBackend);
59
+    // register user backend
60
+    OC_User::useBackend($userBackend);
61
+    \OC::$server->getGroupManager()->addBackend($groupBackend);
62 62
 }
63 63
 
64 64
 \OCP\Util::connectHook(
65
-	'\OCA\Files_Sharing\API\Server2Server',
66
-	'preLoginNameUsedAsUserName',
67
-	'\OCA\User_LDAP\Helper',
68
-	'loginName2UserName'
65
+    '\OCA\Files_Sharing\API\Server2Server',
66
+    'preLoginNameUsedAsUserName',
67
+    '\OCA\User_LDAP\Helper',
68
+    'loginName2UserName'
69 69
 );
70 70
 
71 71
 if(OCP\App::isEnabled('user_webdavauth')) {
72
-	OCP\Util::writeLog('user_ldap',
73
-		'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour',
74
-		OCP\Util::WARN);
72
+    OCP\Util::writeLog('user_ldap',
73
+        'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour',
74
+        OCP\Util::WARN);
75 75
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -31,7 +31,7 @@  discard block
 block discarded – undo
31 31
 $configPrefixes = $helper->getServerConfigurationPrefixes(true);
32 32
 $ldapWrapper = new OCA\User_LDAP\LDAP();
33 33
 $ocConfig = \OC::$server->getConfig();
34
-if(count($configPrefixes) === 1) {
34
+if (count($configPrefixes) === 1) {
35 35
 	$dbc = \OC::$server->getDatabaseConnection();
36 36
 	$userManager = new OCA\User_LDAP\User\Manager($ocConfig,
37 37
 		new OCA\User_LDAP\FilesystemHelper(),
@@ -48,14 +48,14 @@  discard block
 block discarded – undo
48 48
 	$ldapAccess->setGroupMapper(new OCA\User_LDAP\Mapping\GroupMapping($dbc));
49 49
 	$userBackend  = new OCA\User_LDAP\User_LDAP($ldapAccess, $ocConfig);
50 50
 	$groupBackend = new \OCA\User_LDAP\Group_LDAP($ldapAccess);
51
-} else if(count($configPrefixes) > 1) {
51
+} else if (count($configPrefixes) > 1) {
52 52
 	$userBackend  = new OCA\User_LDAP\User_Proxy(
53 53
 		$configPrefixes, $ldapWrapper, $ocConfig
54 54
 	);
55
-	$groupBackend  = new OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
55
+	$groupBackend = new OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
56 56
 }
57 57
 
58
-if(count($configPrefixes) > 0) {
58
+if (count($configPrefixes) > 0) {
59 59
 	// register user backend
60 60
 	OC_User::useBackend($userBackend);
61 61
 	\OC::$server->getGroupManager()->addBackend($groupBackend);
@@ -68,7 +68,7 @@  discard block
 block discarded – undo
68 68
 	'loginName2UserName'
69 69
 );
70 70
 
71
-if(OCP\App::isEnabled('user_webdavauth')) {
71
+if (OCP\App::isEnabled('user_webdavauth')) {
72 72
 	OCP\Util::writeLog('user_ldap',
73 73
 		'user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour',
74 74
 		OCP\Util::WARN);
Please login to merge, or discard this patch.
lib/private/Encryption/Util.php 1 patch
Indentation   +365 added lines, -365 removed lines patch added patch discarded remove patch
@@ -36,370 +36,370 @@
 block discarded – undo
36 36
 
37 37
 class Util {
38 38
 
39
-	const HEADER_START = 'HBEGIN';
40
-	const HEADER_END = 'HEND';
41
-	const HEADER_PADDING_CHAR = '-';
42
-
43
-	const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
44
-
45
-	/**
46
-	 * block size will always be 8192 for a PHP stream
47
-	 * @see https://bugs.php.net/bug.php?id=21641
48
-	 * @var integer
49
-	 */
50
-	protected $headerSize = 8192;
51
-
52
-	/**
53
-	 * block size will always be 8192 for a PHP stream
54
-	 * @see https://bugs.php.net/bug.php?id=21641
55
-	 * @var integer
56
-	 */
57
-	protected $blockSize = 8192;
58
-
59
-	/** @var View */
60
-	protected $rootView;
61
-
62
-	/** @var array */
63
-	protected $ocHeaderKeys;
64
-
65
-	/** @var \OC\User\Manager */
66
-	protected $userManager;
67
-
68
-	/** @var IConfig */
69
-	protected $config;
70
-
71
-	/** @var array paths excluded from encryption */
72
-	protected $excludedPaths;
73
-
74
-	/** @var \OC\Group\Manager $manager */
75
-	protected $groupManager;
76
-
77
-	/**
78
-	 *
79
-	 * @param View $rootView
80
-	 * @param \OC\User\Manager $userManager
81
-	 * @param \OC\Group\Manager $groupManager
82
-	 * @param IConfig $config
83
-	 */
84
-	public function __construct(
85
-		View $rootView,
86
-		\OC\User\Manager $userManager,
87
-		\OC\Group\Manager $groupManager,
88
-		IConfig $config) {
89
-
90
-		$this->ocHeaderKeys = [
91
-			self::HEADER_ENCRYPTION_MODULE_KEY
92
-		];
93
-
94
-		$this->rootView = $rootView;
95
-		$this->userManager = $userManager;
96
-		$this->groupManager = $groupManager;
97
-		$this->config = $config;
98
-
99
-		$this->excludedPaths[] = 'files_encryption';
100
-		$this->excludedPaths[] = 'appdata_' . $config->getSystemValue('instanceid', null);
101
-	}
102
-
103
-	/**
104
-	 * read encryption module ID from header
105
-	 *
106
-	 * @param array $header
107
-	 * @return string
108
-	 * @throws ModuleDoesNotExistsException
109
-	 */
110
-	public function getEncryptionModuleId(array $header = null) {
111
-		$id = '';
112
-		$encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
113
-
114
-		if (isset($header[$encryptionModuleKey])) {
115
-			$id = $header[$encryptionModuleKey];
116
-		} elseif (isset($header['cipher'])) {
117
-			if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
118
-				// fall back to default encryption if the user migrated from
119
-				// ownCloud <= 8.0 with the old encryption
120
-				$id = \OCA\Encryption\Crypto\Encryption::ID;
121
-			} else {
122
-				throw new ModuleDoesNotExistsException('Default encryption module missing');
123
-			}
124
-		}
125
-
126
-		return $id;
127
-	}
128
-
129
-	/**
130
-	 * create header for encrypted file
131
-	 *
132
-	 * @param array $headerData
133
-	 * @param IEncryptionModule $encryptionModule
134
-	 * @return string
135
-	 * @throws EncryptionHeaderToLargeException if header has to many arguments
136
-	 * @throws EncryptionHeaderKeyExistsException if header key is already in use
137
-	 */
138
-	public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
139
-		$header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
140
-		foreach ($headerData as $key => $value) {
141
-			if (in_array($key, $this->ocHeaderKeys)) {
142
-				throw new EncryptionHeaderKeyExistsException($key);
143
-			}
144
-			$header .= $key . ':' . $value . ':';
145
-		}
146
-		$header .= self::HEADER_END;
147
-
148
-		if (strlen($header) > $this->getHeaderSize()) {
149
-			throw new EncryptionHeaderToLargeException();
150
-		}
151
-
152
-		$paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
153
-
154
-		return $paddedHeader;
155
-	}
156
-
157
-	/**
158
-	 * go recursively through a dir and collect all files and sub files.
159
-	 *
160
-	 * @param string $dir relative to the users files folder
161
-	 * @return array with list of files relative to the users files folder
162
-	 */
163
-	public function getAllFiles($dir) {
164
-		$result = array();
165
-		$dirList = array($dir);
166
-
167
-		while ($dirList) {
168
-			$dir = array_pop($dirList);
169
-			$content = $this->rootView->getDirectoryContent($dir);
170
-
171
-			foreach ($content as $c) {
172
-				if ($c->getType() === 'dir') {
173
-					$dirList[] = $c->getPath();
174
-				} else {
175
-					$result[] =  $c->getPath();
176
-				}
177
-			}
178
-
179
-		}
180
-
181
-		return $result;
182
-	}
183
-
184
-	/**
185
-	 * check if it is a file uploaded by the user stored in data/user/files
186
-	 * or a metadata file
187
-	 *
188
-	 * @param string $path relative to the data/ folder
189
-	 * @return boolean
190
-	 */
191
-	public function isFile($path) {
192
-		$parts = explode('/', Filesystem::normalizePath($path), 4);
193
-		if (isset($parts[2]) && $parts[2] === 'files') {
194
-			return true;
195
-		}
196
-		return false;
197
-	}
198
-
199
-	/**
200
-	 * return size of encryption header
201
-	 *
202
-	 * @return integer
203
-	 */
204
-	public function getHeaderSize() {
205
-		return $this->headerSize;
206
-	}
207
-
208
-	/**
209
-	 * return size of block read by a PHP stream
210
-	 *
211
-	 * @return integer
212
-	 */
213
-	public function getBlockSize() {
214
-		return $this->blockSize;
215
-	}
216
-
217
-	/**
218
-	 * get the owner and the path for the file relative to the owners files folder
219
-	 *
220
-	 * @param string $path
221
-	 * @return array
222
-	 * @throws \BadMethodCallException
223
-	 */
224
-	public function getUidAndFilename($path) {
225
-
226
-		$parts = explode('/', $path);
227
-		$uid = '';
228
-		if (count($parts) > 2) {
229
-			$uid = $parts[1];
230
-		}
231
-		if (!$this->userManager->userExists($uid)) {
232
-			throw new \BadMethodCallException(
233
-				'path needs to be relative to the system wide data folder and point to a user specific file'
234
-			);
235
-		}
236
-
237
-		$ownerPath = implode('/', array_slice($parts, 2));
238
-
239
-		return array($uid, Filesystem::normalizePath($ownerPath));
240
-
241
-	}
242
-
243
-	/**
244
-	 * Remove .path extension from a file path
245
-	 * @param string $path Path that may identify a .part file
246
-	 * @return string File path without .part extension
247
-	 * @note this is needed for reusing keys
248
-	 */
249
-	public function stripPartialFileExtension($path) {
250
-		$extension = pathinfo($path, PATHINFO_EXTENSION);
251
-
252
-		if ( $extension === 'part') {
253
-
254
-			$newLength = strlen($path) - 5; // 5 = strlen(".part")
255
-			$fPath = substr($path, 0, $newLength);
256
-
257
-			// if path also contains a transaction id, we remove it too
258
-			$extension = pathinfo($fPath, PATHINFO_EXTENSION);
259
-			if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
260
-				$newLength = strlen($fPath) - strlen($extension) -1;
261
-				$fPath = substr($fPath, 0, $newLength);
262
-			}
263
-			return $fPath;
264
-
265
-		} else {
266
-			return $path;
267
-		}
268
-	}
269
-
270
-	public function getUserWithAccessToMountPoint($users, $groups) {
271
-		$result = array();
272
-		if (in_array('all', $users)) {
273
-			$result = \OCP\User::getUsers();
274
-		} else {
275
-			$result = array_merge($result, $users);
276
-
277
-			$groupManager = \OC::$server->getGroupManager();
278
-			foreach ($groups as $group) {
279
-				$groupObject = $groupManager->get($group);
280
-				if ($groupObject) {
281
-					$foundUsers = $groupObject->searchUsers('', -1, 0);
282
-					$userIds = [];
283
-					foreach ($foundUsers as $user) {
284
-						$userIds[] = $user->getUID();
285
-					}
286
-					$result = array_merge($result, $userIds);
287
-				}
288
-			}
289
-		}
290
-
291
-		return $result;
292
-	}
293
-
294
-	/**
295
-	 * check if the file is stored on a system wide mount point
296
-	 * @param string $path relative to /data/user with leading '/'
297
-	 * @param string $uid
298
-	 * @return boolean
299
-	 */
300
-	public function isSystemWideMountPoint($path, $uid) {
301
-		if (\OCP\App::isEnabled("files_external")) {
302
-			$mounts = \OC_Mount_Config::getSystemMountPoints();
303
-			foreach ($mounts as $mount) {
304
-				if (strpos($path, '/files/' . $mount['mountpoint']) === 0) {
305
-					if ($this->isMountPointApplicableToUser($mount, $uid)) {
306
-						return true;
307
-					}
308
-				}
309
-			}
310
-		}
311
-		return false;
312
-	}
313
-
314
-	/**
315
-	 * check if mount point is applicable to user
316
-	 *
317
-	 * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
318
-	 * @param string $uid
319
-	 * @return boolean
320
-	 */
321
-	private function isMountPointApplicableToUser($mount, $uid) {
322
-		$acceptedUids = array('all', $uid);
323
-		// check if mount point is applicable for the user
324
-		$intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
325
-		if (!empty($intersection)) {
326
-			return true;
327
-		}
328
-		// check if mount point is applicable for group where the user is a member
329
-		foreach ($mount['applicable']['groups'] as $gid) {
330
-			if ($this->groupManager->isInGroup($uid, $gid)) {
331
-				return true;
332
-			}
333
-		}
334
-		return false;
335
-	}
336
-
337
-	/**
338
-	 * check if it is a path which is excluded by ownCloud from encryption
339
-	 *
340
-	 * @param string $path
341
-	 * @return boolean
342
-	 */
343
-	public function isExcluded($path) {
344
-		$normalizedPath = Filesystem::normalizePath($path);
345
-		$root = explode('/', $normalizedPath, 4);
346
-		if (count($root) > 1) {
347
-
348
-			// detect alternative key storage root
349
-			$rootDir = $this->getKeyStorageRoot();
350
-			if ($rootDir !== '' &&
351
-				0 === strpos(
352
-					Filesystem::normalizePath($path),
353
-					Filesystem::normalizePath($rootDir)
354
-				)
355
-			) {
356
-				return true;
357
-			}
358
-
359
-
360
-			//detect system wide folders
361
-			if (in_array($root[1], $this->excludedPaths)) {
362
-				return true;
363
-			}
364
-
365
-			// detect user specific folders
366
-			if ($this->userManager->userExists($root[1])
367
-				&& in_array($root[2], $this->excludedPaths)) {
368
-
369
-				return true;
370
-			}
371
-		}
372
-		return false;
373
-	}
374
-
375
-	/**
376
-	 * check if recovery key is enabled for user
377
-	 *
378
-	 * @param string $uid
379
-	 * @return boolean
380
-	 */
381
-	public function recoveryEnabled($uid) {
382
-		$enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
383
-
384
-		return ($enabled === '1') ? true : false;
385
-	}
386
-
387
-	/**
388
-	 * set new key storage root
389
-	 *
390
-	 * @param string $root new key store root relative to the data folder
391
-	 */
392
-	public function setKeyStorageRoot($root) {
393
-		$this->config->setAppValue('core', 'encryption_key_storage_root', $root);
394
-	}
395
-
396
-	/**
397
-	 * get key storage root
398
-	 *
399
-	 * @return string key storage root
400
-	 */
401
-	public function getKeyStorageRoot() {
402
-		return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
403
-	}
39
+    const HEADER_START = 'HBEGIN';
40
+    const HEADER_END = 'HEND';
41
+    const HEADER_PADDING_CHAR = '-';
42
+
43
+    const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
44
+
45
+    /**
46
+     * block size will always be 8192 for a PHP stream
47
+     * @see https://bugs.php.net/bug.php?id=21641
48
+     * @var integer
49
+     */
50
+    protected $headerSize = 8192;
51
+
52
+    /**
53
+     * block size will always be 8192 for a PHP stream
54
+     * @see https://bugs.php.net/bug.php?id=21641
55
+     * @var integer
56
+     */
57
+    protected $blockSize = 8192;
58
+
59
+    /** @var View */
60
+    protected $rootView;
61
+
62
+    /** @var array */
63
+    protected $ocHeaderKeys;
64
+
65
+    /** @var \OC\User\Manager */
66
+    protected $userManager;
67
+
68
+    /** @var IConfig */
69
+    protected $config;
70
+
71
+    /** @var array paths excluded from encryption */
72
+    protected $excludedPaths;
73
+
74
+    /** @var \OC\Group\Manager $manager */
75
+    protected $groupManager;
76
+
77
+    /**
78
+     *
79
+     * @param View $rootView
80
+     * @param \OC\User\Manager $userManager
81
+     * @param \OC\Group\Manager $groupManager
82
+     * @param IConfig $config
83
+     */
84
+    public function __construct(
85
+        View $rootView,
86
+        \OC\User\Manager $userManager,
87
+        \OC\Group\Manager $groupManager,
88
+        IConfig $config) {
89
+
90
+        $this->ocHeaderKeys = [
91
+            self::HEADER_ENCRYPTION_MODULE_KEY
92
+        ];
93
+
94
+        $this->rootView = $rootView;
95
+        $this->userManager = $userManager;
96
+        $this->groupManager = $groupManager;
97
+        $this->config = $config;
98
+
99
+        $this->excludedPaths[] = 'files_encryption';
100
+        $this->excludedPaths[] = 'appdata_' . $config->getSystemValue('instanceid', null);
101
+    }
102
+
103
+    /**
104
+     * read encryption module ID from header
105
+     *
106
+     * @param array $header
107
+     * @return string
108
+     * @throws ModuleDoesNotExistsException
109
+     */
110
+    public function getEncryptionModuleId(array $header = null) {
111
+        $id = '';
112
+        $encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
113
+
114
+        if (isset($header[$encryptionModuleKey])) {
115
+            $id = $header[$encryptionModuleKey];
116
+        } elseif (isset($header['cipher'])) {
117
+            if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
118
+                // fall back to default encryption if the user migrated from
119
+                // ownCloud <= 8.0 with the old encryption
120
+                $id = \OCA\Encryption\Crypto\Encryption::ID;
121
+            } else {
122
+                throw new ModuleDoesNotExistsException('Default encryption module missing');
123
+            }
124
+        }
125
+
126
+        return $id;
127
+    }
128
+
129
+    /**
130
+     * create header for encrypted file
131
+     *
132
+     * @param array $headerData
133
+     * @param IEncryptionModule $encryptionModule
134
+     * @return string
135
+     * @throws EncryptionHeaderToLargeException if header has to many arguments
136
+     * @throws EncryptionHeaderKeyExistsException if header key is already in use
137
+     */
138
+    public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
139
+        $header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
140
+        foreach ($headerData as $key => $value) {
141
+            if (in_array($key, $this->ocHeaderKeys)) {
142
+                throw new EncryptionHeaderKeyExistsException($key);
143
+            }
144
+            $header .= $key . ':' . $value . ':';
145
+        }
146
+        $header .= self::HEADER_END;
147
+
148
+        if (strlen($header) > $this->getHeaderSize()) {
149
+            throw new EncryptionHeaderToLargeException();
150
+        }
151
+
152
+        $paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
153
+
154
+        return $paddedHeader;
155
+    }
156
+
157
+    /**
158
+     * go recursively through a dir and collect all files and sub files.
159
+     *
160
+     * @param string $dir relative to the users files folder
161
+     * @return array with list of files relative to the users files folder
162
+     */
163
+    public function getAllFiles($dir) {
164
+        $result = array();
165
+        $dirList = array($dir);
166
+
167
+        while ($dirList) {
168
+            $dir = array_pop($dirList);
169
+            $content = $this->rootView->getDirectoryContent($dir);
170
+
171
+            foreach ($content as $c) {
172
+                if ($c->getType() === 'dir') {
173
+                    $dirList[] = $c->getPath();
174
+                } else {
175
+                    $result[] =  $c->getPath();
176
+                }
177
+            }
178
+
179
+        }
180
+
181
+        return $result;
182
+    }
183
+
184
+    /**
185
+     * check if it is a file uploaded by the user stored in data/user/files
186
+     * or a metadata file
187
+     *
188
+     * @param string $path relative to the data/ folder
189
+     * @return boolean
190
+     */
191
+    public function isFile($path) {
192
+        $parts = explode('/', Filesystem::normalizePath($path), 4);
193
+        if (isset($parts[2]) && $parts[2] === 'files') {
194
+            return true;
195
+        }
196
+        return false;
197
+    }
198
+
199
+    /**
200
+     * return size of encryption header
201
+     *
202
+     * @return integer
203
+     */
204
+    public function getHeaderSize() {
205
+        return $this->headerSize;
206
+    }
207
+
208
+    /**
209
+     * return size of block read by a PHP stream
210
+     *
211
+     * @return integer
212
+     */
213
+    public function getBlockSize() {
214
+        return $this->blockSize;
215
+    }
216
+
217
+    /**
218
+     * get the owner and the path for the file relative to the owners files folder
219
+     *
220
+     * @param string $path
221
+     * @return array
222
+     * @throws \BadMethodCallException
223
+     */
224
+    public function getUidAndFilename($path) {
225
+
226
+        $parts = explode('/', $path);
227
+        $uid = '';
228
+        if (count($parts) > 2) {
229
+            $uid = $parts[1];
230
+        }
231
+        if (!$this->userManager->userExists($uid)) {
232
+            throw new \BadMethodCallException(
233
+                'path needs to be relative to the system wide data folder and point to a user specific file'
234
+            );
235
+        }
236
+
237
+        $ownerPath = implode('/', array_slice($parts, 2));
238
+
239
+        return array($uid, Filesystem::normalizePath($ownerPath));
240
+
241
+    }
242
+
243
+    /**
244
+     * Remove .path extension from a file path
245
+     * @param string $path Path that may identify a .part file
246
+     * @return string File path without .part extension
247
+     * @note this is needed for reusing keys
248
+     */
249
+    public function stripPartialFileExtension($path) {
250
+        $extension = pathinfo($path, PATHINFO_EXTENSION);
251
+
252
+        if ( $extension === 'part') {
253
+
254
+            $newLength = strlen($path) - 5; // 5 = strlen(".part")
255
+            $fPath = substr($path, 0, $newLength);
256
+
257
+            // if path also contains a transaction id, we remove it too
258
+            $extension = pathinfo($fPath, PATHINFO_EXTENSION);
259
+            if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
260
+                $newLength = strlen($fPath) - strlen($extension) -1;
261
+                $fPath = substr($fPath, 0, $newLength);
262
+            }
263
+            return $fPath;
264
+
265
+        } else {
266
+            return $path;
267
+        }
268
+    }
269
+
270
+    public function getUserWithAccessToMountPoint($users, $groups) {
271
+        $result = array();
272
+        if (in_array('all', $users)) {
273
+            $result = \OCP\User::getUsers();
274
+        } else {
275
+            $result = array_merge($result, $users);
276
+
277
+            $groupManager = \OC::$server->getGroupManager();
278
+            foreach ($groups as $group) {
279
+                $groupObject = $groupManager->get($group);
280
+                if ($groupObject) {
281
+                    $foundUsers = $groupObject->searchUsers('', -1, 0);
282
+                    $userIds = [];
283
+                    foreach ($foundUsers as $user) {
284
+                        $userIds[] = $user->getUID();
285
+                    }
286
+                    $result = array_merge($result, $userIds);
287
+                }
288
+            }
289
+        }
290
+
291
+        return $result;
292
+    }
293
+
294
+    /**
295
+     * check if the file is stored on a system wide mount point
296
+     * @param string $path relative to /data/user with leading '/'
297
+     * @param string $uid
298
+     * @return boolean
299
+     */
300
+    public function isSystemWideMountPoint($path, $uid) {
301
+        if (\OCP\App::isEnabled("files_external")) {
302
+            $mounts = \OC_Mount_Config::getSystemMountPoints();
303
+            foreach ($mounts as $mount) {
304
+                if (strpos($path, '/files/' . $mount['mountpoint']) === 0) {
305
+                    if ($this->isMountPointApplicableToUser($mount, $uid)) {
306
+                        return true;
307
+                    }
308
+                }
309
+            }
310
+        }
311
+        return false;
312
+    }
313
+
314
+    /**
315
+     * check if mount point is applicable to user
316
+     *
317
+     * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
318
+     * @param string $uid
319
+     * @return boolean
320
+     */
321
+    private function isMountPointApplicableToUser($mount, $uid) {
322
+        $acceptedUids = array('all', $uid);
323
+        // check if mount point is applicable for the user
324
+        $intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
325
+        if (!empty($intersection)) {
326
+            return true;
327
+        }
328
+        // check if mount point is applicable for group where the user is a member
329
+        foreach ($mount['applicable']['groups'] as $gid) {
330
+            if ($this->groupManager->isInGroup($uid, $gid)) {
331
+                return true;
332
+            }
333
+        }
334
+        return false;
335
+    }
336
+
337
+    /**
338
+     * check if it is a path which is excluded by ownCloud from encryption
339
+     *
340
+     * @param string $path
341
+     * @return boolean
342
+     */
343
+    public function isExcluded($path) {
344
+        $normalizedPath = Filesystem::normalizePath($path);
345
+        $root = explode('/', $normalizedPath, 4);
346
+        if (count($root) > 1) {
347
+
348
+            // detect alternative key storage root
349
+            $rootDir = $this->getKeyStorageRoot();
350
+            if ($rootDir !== '' &&
351
+                0 === strpos(
352
+                    Filesystem::normalizePath($path),
353
+                    Filesystem::normalizePath($rootDir)
354
+                )
355
+            ) {
356
+                return true;
357
+            }
358
+
359
+
360
+            //detect system wide folders
361
+            if (in_array($root[1], $this->excludedPaths)) {
362
+                return true;
363
+            }
364
+
365
+            // detect user specific folders
366
+            if ($this->userManager->userExists($root[1])
367
+                && in_array($root[2], $this->excludedPaths)) {
368
+
369
+                return true;
370
+            }
371
+        }
372
+        return false;
373
+    }
374
+
375
+    /**
376
+     * check if recovery key is enabled for user
377
+     *
378
+     * @param string $uid
379
+     * @return boolean
380
+     */
381
+    public function recoveryEnabled($uid) {
382
+        $enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
383
+
384
+        return ($enabled === '1') ? true : false;
385
+    }
386
+
387
+    /**
388
+     * set new key storage root
389
+     *
390
+     * @param string $root new key store root relative to the data folder
391
+     */
392
+    public function setKeyStorageRoot($root) {
393
+        $this->config->setAppValue('core', 'encryption_key_storage_root', $root);
394
+    }
395
+
396
+    /**
397
+     * get key storage root
398
+     *
399
+     * @return string key storage root
400
+     */
401
+    public function getKeyStorageRoot() {
402
+        return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
403
+    }
404 404
 
405 405
 }
Please login to merge, or discard this patch.
lib/private/legacy/user.php 1 patch
Indentation   +559 added lines, -560 removed lines patch added patch discarded remove patch
@@ -57,564 +57,563 @@
 block discarded – undo
57 57
  */
58 58
 class OC_User {
59 59
 
60
-	/**
61
-	 * @return \OC\User\Session
62
-	 */
63
-	public static function getUserSession() {
64
-		return OC::$server->getUserSession();
65
-	}
66
-
67
-	private static $_usedBackends = array();
68
-
69
-	private static $_setupedBackends = array();
70
-
71
-	// bool, stores if a user want to access a resource anonymously, e.g if they open a public link
72
-	private static $incognitoMode = false;
73
-
74
-	/**
75
-	 * Adds the backend to the list of used backends
76
-	 *
77
-	 * @param string|\OCP\UserInterface $backend default: database The backend to use for user management
78
-	 * @return bool
79
-	 *
80
-	 * Set the User Authentication Module
81
-	 */
82
-	public static function useBackend($backend = 'database') {
83
-		if ($backend instanceof \OCP\UserInterface) {
84
-			self::$_usedBackends[get_class($backend)] = $backend;
85
-			\OC::$server->getUserManager()->registerBackend($backend);
86
-		} else {
87
-			// You'll never know what happens
88
-			if (null === $backend OR !is_string($backend)) {
89
-				$backend = 'database';
90
-			}
91
-
92
-			// Load backend
93
-			switch ($backend) {
94
-				case 'database':
95
-				case 'mysql':
96
-				case 'sqlite':
97
-					\OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', \OCP\Util::DEBUG);
98
-					self::$_usedBackends[$backend] = new \OC\User\Database();
99
-					\OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
100
-					break;
101
-				case 'dummy':
102
-					self::$_usedBackends[$backend] = new \Test\Util\User\Dummy();
103
-					\OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
104
-					break;
105
-				default:
106
-					\OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', \OCP\Util::DEBUG);
107
-					$className = 'OC_USER_' . strtoupper($backend);
108
-					self::$_usedBackends[$backend] = new $className();
109
-					\OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
110
-					break;
111
-			}
112
-		}
113
-		return true;
114
-	}
115
-
116
-	/**
117
-	 * remove all used backends
118
-	 */
119
-	public static function clearBackends() {
120
-		self::$_usedBackends = array();
121
-		\OC::$server->getUserManager()->clearBackends();
122
-	}
123
-
124
-	/**
125
-	 * setup the configured backends in config.php
126
-	 */
127
-	public static function setupBackends() {
128
-		OC_App::loadApps(['prelogin']);
129
-		$backends = \OC::$server->getSystemConfig()->getValue('user_backends', []);
130
-		if (isset($backends['default']) && !$backends['default']) {
131
-			// clear default backends
132
-			self::clearBackends();
133
-		}
134
-		foreach ($backends as $i => $config) {
135
-			if (!is_array($config)) {
136
-				continue;
137
-			}
138
-			$class = $config['class'];
139
-			$arguments = $config['arguments'];
140
-			if (class_exists($class)) {
141
-				if (array_search($i, self::$_setupedBackends) === false) {
142
-					// make a reflection object
143
-					$reflectionObj = new ReflectionClass($class);
144
-
145
-					// use Reflection to create a new instance, using the $args
146
-					$backend = $reflectionObj->newInstanceArgs($arguments);
147
-					self::useBackend($backend);
148
-					self::$_setupedBackends[] = $i;
149
-				} else {
150
-					\OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', \OCP\Util::DEBUG);
151
-				}
152
-			} else {
153
-				\OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', \OCP\Util::ERROR);
154
-			}
155
-		}
156
-	}
157
-
158
-	/**
159
-
160
-	 * Try to login a user using the magic cookie (remember login)
161
-	 *
162
-	 * @deprecated use \OCP\IUserSession::loginWithCookie()
163
-	 * @param string $uid The username of the user to log in
164
-	 * @param string $token
165
-	 * @param string $oldSessionId
166
-	 * @return bool
167
-	 */
168
-	public static function loginWithCookie($uid, $token, $oldSessionId) {
169
-		return self::getUserSession()->loginWithCookie($uid, $token, $oldSessionId);
170
-	}
171
-
172
-	/**
173
-	 * Try to login a user, assuming authentication
174
-	 * has already happened (e.g. via Single Sign On).
175
-	 *
176
-	 * Log in a user and regenerate a new session.
177
-	 *
178
-	 * @param \OCP\Authentication\IApacheBackend $backend
179
-	 * @return bool
180
-	 */
181
-	public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) {
182
-
183
-		$uid = $backend->getCurrentUserId();
184
-		$run = true;
185
-		OC_Hook::emit("OC_User", "pre_login", array("run" => &$run, "uid" => $uid));
186
-
187
-		if ($uid) {
188
-			if (self::getUser() !== $uid) {
189
-				self::setUserId($uid);
190
-				$setUidAsDisplayName = true;
191
-				if($backend instanceof \OCP\UserInterface
192
-					&& $backend->implementsActions(OC_User_Backend::GET_DISPLAYNAME)) {
193
-
194
-					$backendDisplayName = $backend->getDisplayName($uid);
195
-					if(is_string($backendDisplayName) && trim($backendDisplayName) !== '') {
196
-						$setUidAsDisplayName = false;
197
-					}
198
-				}
199
-				if($setUidAsDisplayName) {
200
-					self::setDisplayName($uid);
201
-				}
202
-				self::getUserSession()->setLoginName($uid);
203
-				$request = OC::$server->getRequest();
204
-				self::getUserSession()->createSessionToken($request, $uid, $uid);
205
-				// setup the filesystem
206
-				OC_Util::setupFS($uid);
207
-				// first call the post_login hooks, the login-process needs to be
208
-				// completed before we can safely create the users folder.
209
-				// For example encryption needs to initialize the users keys first
210
-				// before we can create the user folder with the skeleton files
211
-				OC_Hook::emit("OC_User", "post_login", array("uid" => $uid, 'password' => ''));
212
-				//trigger creation of user home and /files folder
213
-				\OC::$server->getUserFolder($uid);
214
-			}
215
-			return true;
216
-		}
217
-		return false;
218
-	}
219
-
220
-	/**
221
-	 * Verify with Apache whether user is authenticated.
222
-	 *
223
-	 * @return boolean|null
224
-	 *          true: authenticated
225
-	 *          false: not authenticated
226
-	 *          null: not handled / no backend available
227
-	 */
228
-	public static function handleApacheAuth() {
229
-		$backend = self::findFirstActiveUsedBackend();
230
-		if ($backend) {
231
-			OC_App::loadApps();
232
-
233
-			//setup extra user backends
234
-			self::setupBackends();
235
-			self::unsetMagicInCookie();
236
-
237
-			return self::loginWithApache($backend);
238
-		}
239
-
240
-		return null;
241
-	}
242
-
243
-
244
-	/**
245
-	 * Sets user id for session and triggers emit
246
-	 *
247
-	 * @param string $uid
248
-	 */
249
-	public static function setUserId($uid) {
250
-		$userSession = \OC::$server->getUserSession();
251
-		$userManager = \OC::$server->getUserManager();
252
-		if ($user = $userManager->get($uid)) {
253
-			$userSession->setUser($user);
254
-		} else {
255
-			\OC::$server->getSession()->set('user_id', $uid);
256
-		}
257
-	}
258
-
259
-	/**
260
-	 * Sets user display name for session
261
-	 *
262
-	 * @param string $uid
263
-	 * @param string $displayName
264
-	 * @return bool Whether the display name could get set
265
-	 */
266
-	public static function setDisplayName($uid, $displayName = null) {
267
-		if (is_null($displayName)) {
268
-			$displayName = $uid;
269
-		}
270
-		$user = \OC::$server->getUserManager()->get($uid);
271
-		if ($user) {
272
-			return $user->setDisplayName($displayName);
273
-		} else {
274
-			return false;
275
-		}
276
-	}
277
-
278
-	/**
279
-	 * Check if the user is logged in, considers also the HTTP basic credentials
280
-	 *
281
-	 * @deprecated use \OC::$server->getUserSession()->isLoggedIn()
282
-	 * @return bool
283
-	 */
284
-	public static function isLoggedIn() {
285
-		return \OC::$server->getUserSession()->isLoggedIn();
286
-	}
287
-
288
-	/**
289
-	 * set incognito mode, e.g. if a user wants to open a public link
290
-	 *
291
-	 * @param bool $status
292
-	 */
293
-	public static function setIncognitoMode($status) {
294
-		self::$incognitoMode = $status;
295
-	}
296
-
297
-	/**
298
-	 * get incognito mode status
299
-	 *
300
-	 * @return bool
301
-	 */
302
-	public static function isIncognitoMode() {
303
-		return self::$incognitoMode;
304
-	}
305
-
306
-	/**
307
-	 * Supplies an attribute to the logout hyperlink. The default behaviour
308
-	 * is to return an href with '?logout=true' appended. However, it can
309
-	 * supply any attribute(s) which are valid for <a>.
310
-	 *
311
-	 * @return string with one or more HTML attributes.
312
-	 */
313
-	public static function getLogoutAttribute() {
314
-		$backend = self::findFirstActiveUsedBackend();
315
-		if ($backend) {
316
-			return $backend->getLogoutAttribute();
317
-		}
318
-
319
-		$logoutUrl = \OC::$server->getURLGenerator()->linkToRouteAbsolute(
320
-			'core.login.logout',
321
-			[
322
-				'requesttoken' => \OCP\Util::callRegister(),
323
-			]
324
-		);
325
-
326
-		return 'href="'.$logoutUrl.'"';
327
-	}
328
-
329
-	/**
330
-	 * Check if the user is an admin user
331
-	 *
332
-	 * @param string $uid uid of the admin
333
-	 * @return bool
334
-	 */
335
-	public static function isAdminUser($uid) {
336
-		$group = \OC::$server->getGroupManager()->get('admin');
337
-		$user = \OC::$server->getUserManager()->get($uid);
338
-		if ($group && $user && $group->inGroup($user) && self::$incognitoMode === false) {
339
-			return true;
340
-		}
341
-		return false;
342
-	}
343
-
344
-
345
-	/**
346
-	 * get the user id of the user currently logged in.
347
-	 *
348
-	 * @return string|bool uid or false
349
-	 */
350
-	public static function getUser() {
351
-		$uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null;
352
-		if (!is_null($uid) && self::$incognitoMode === false) {
353
-			return $uid;
354
-		} else {
355
-			return false;
356
-		}
357
-	}
358
-
359
-	/**
360
-	 * get the display name of the user currently logged in.
361
-	 *
362
-	 * @param string $uid
363
-	 * @return string uid or false
364
-	 */
365
-	public static function getDisplayName($uid = null) {
366
-		if ($uid) {
367
-			$user = \OC::$server->getUserManager()->get($uid);
368
-			if ($user) {
369
-				return $user->getDisplayName();
370
-			} else {
371
-				return $uid;
372
-			}
373
-		} else {
374
-			$user = self::getUserSession()->getUser();
375
-			if ($user) {
376
-				return $user->getDisplayName();
377
-			} else {
378
-				return false;
379
-			}
380
-		}
381
-	}
382
-
383
-	/**
384
-	 * Autogenerate a password
385
-	 *
386
-	 * @return string
387
-	 *
388
-	 * generates a password
389
-	 */
390
-	public static function generatePassword() {
391
-		return \OC::$server->getSecureRandom()->generate(30);
392
-	}
393
-
394
-	/**
395
-	 * Set password
396
-	 *
397
-	 * @param string $uid The username
398
-	 * @param string $password The new password
399
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
400
-	 * @return bool
401
-	 *
402
-	 * Change the password of a user
403
-	 */
404
-	public static function setPassword($uid, $password, $recoveryPassword = null) {
405
-		$user = \OC::$server->getUserManager()->get($uid);
406
-		if ($user) {
407
-			return $user->setPassword($password, $recoveryPassword);
408
-		} else {
409
-			return false;
410
-		}
411
-	}
412
-
413
-	/**
414
-	 * Check whether user can change his avatar
415
-	 *
416
-	 * @param string $uid The username
417
-	 * @return bool
418
-	 *
419
-	 * Check whether a specified user can change his avatar
420
-	 */
421
-	public static function canUserChangeAvatar($uid) {
422
-		$user = \OC::$server->getUserManager()->get($uid);
423
-		if ($user) {
424
-			return $user->canChangeAvatar();
425
-		} else {
426
-			return false;
427
-		}
428
-	}
429
-
430
-	/**
431
-	 * Check whether user can change his password
432
-	 *
433
-	 * @param string $uid The username
434
-	 * @return bool
435
-	 *
436
-	 * Check whether a specified user can change his password
437
-	 */
438
-	public static function canUserChangePassword($uid) {
439
-		$user = \OC::$server->getUserManager()->get($uid);
440
-		if ($user) {
441
-			return $user->canChangePassword();
442
-		} else {
443
-			return false;
444
-		}
445
-	}
446
-
447
-	/**
448
-	 * Check whether user can change his display name
449
-	 *
450
-	 * @param string $uid The username
451
-	 * @return bool
452
-	 *
453
-	 * Check whether a specified user can change his display name
454
-	 */
455
-	public static function canUserChangeDisplayName($uid) {
456
-		$user = \OC::$server->getUserManager()->get($uid);
457
-		if ($user) {
458
-			return $user->canChangeDisplayName();
459
-		} else {
460
-			return false;
461
-		}
462
-	}
463
-
464
-	/**
465
-	 * Check if the password is correct
466
-	 *
467
-	 * @param string $uid The username
468
-	 * @param string $password The password
469
-	 * @return string|false user id a string on success, false otherwise
470
-	 *
471
-	 * Check if the password is correct without logging in the user
472
-	 * returns the user id or false
473
-	 */
474
-	public static function checkPassword($uid, $password) {
475
-		$manager = \OC::$server->getUserManager();
476
-		$username = $manager->checkPassword($uid, $password);
477
-		if ($username !== false) {
478
-			return $username->getUID();
479
-		}
480
-		return false;
481
-	}
482
-
483
-	/**
484
-	 * @param string $uid The username
485
-	 * @return string
486
-	 *
487
-	 * returns the path to the users home directory
488
-	 * @deprecated Use \OC::$server->getUserManager->getHome()
489
-	 */
490
-	public static function getHome($uid) {
491
-		$user = \OC::$server->getUserManager()->get($uid);
492
-		if ($user) {
493
-			return $user->getHome();
494
-		} else {
495
-			return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid;
496
-		}
497
-	}
498
-
499
-	/**
500
-	 * Get a list of all users
501
-	 *
502
-	 * @return array an array of all uids
503
-	 *
504
-	 * Get a list of all users.
505
-	 * @param string $search
506
-	 * @param integer $limit
507
-	 * @param integer $offset
508
-	 */
509
-	public static function getUsers($search = '', $limit = null, $offset = null) {
510
-		$users = \OC::$server->getUserManager()->search($search, $limit, $offset);
511
-		$uids = array();
512
-		foreach ($users as $user) {
513
-			$uids[] = $user->getUID();
514
-		}
515
-		return $uids;
516
-	}
517
-
518
-	/**
519
-	 * Get a list of all users display name
520
-	 *
521
-	 * @param string $search
522
-	 * @param int $limit
523
-	 * @param int $offset
524
-	 * @return array associative array with all display names (value) and corresponding uids (key)
525
-	 *
526
-	 * Get a list of all display names and user ids.
527
-	 * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead.
528
-	 */
529
-	public static function getDisplayNames($search = '', $limit = null, $offset = null) {
530
-		$displayNames = array();
531
-		$users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset);
532
-		foreach ($users as $user) {
533
-			$displayNames[$user->getUID()] = $user->getDisplayName();
534
-		}
535
-		return $displayNames;
536
-	}
537
-
538
-	/**
539
-	 * check if a user exists
540
-	 *
541
-	 * @param string $uid the username
542
-	 * @return boolean
543
-	 */
544
-	public static function userExists($uid) {
545
-		return \OC::$server->getUserManager()->userExists($uid);
546
-	}
547
-
548
-	/**
549
-	 * disables a user
550
-	 *
551
-	 * @param string $uid the user to disable
552
-	 */
553
-	public static function disableUser($uid) {
554
-		$user = \OC::$server->getUserManager()->get($uid);
555
-		if ($user) {
556
-			$user->setEnabled(false);
557
-		}
558
-	}
559
-
560
-	/**
561
-	 * enable a user
562
-	 *
563
-	 * @param string $uid
564
-	 */
565
-	public static function enableUser($uid) {
566
-		$user = \OC::$server->getUserManager()->get($uid);
567
-		if ($user) {
568
-			$user->setEnabled(true);
569
-		}
570
-	}
571
-
572
-	/**
573
-	 * checks if a user is enabled
574
-	 *
575
-	 * @param string $uid
576
-	 * @return bool
577
-	 */
578
-	public static function isEnabled($uid) {
579
-		$user = \OC::$server->getUserManager()->get($uid);
580
-		if ($user) {
581
-			return $user->isEnabled();
582
-		} else {
583
-			return false;
584
-		}
585
-	}
586
-
587
-	/**
588
-	 * Set cookie value to use in next page load
589
-	 *
590
-	 * @param string $username username to be set
591
-	 * @param string $token
592
-	 */
593
-	public static function setMagicInCookie($username, $token) {
594
-		self::getUserSession()->setMagicInCookie($username, $token);
595
-	}
596
-
597
-	/**
598
-	 * Remove cookie for "remember username"
599
-	 */
600
-	public static function unsetMagicInCookie() {
601
-		self::getUserSession()->unsetMagicInCookie();
602
-	}
603
-
604
-	/**
605
-	 * Returns the first active backend from self::$_usedBackends.
606
-	 *
607
-	 * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend
608
-	 */
609
-	private static function findFirstActiveUsedBackend() {
610
-		foreach (self::$_usedBackends as $backend) {
611
-			if ($backend instanceof OCP\Authentication\IApacheBackend) {
612
-				if ($backend->isSessionActive()) {
613
-					return $backend;
614
-				}
615
-			}
616
-		}
617
-
618
-		return null;
619
-	}
60
+    /**
61
+     * @return \OC\User\Session
62
+     */
63
+    public static function getUserSession() {
64
+        return OC::$server->getUserSession();
65
+    }
66
+
67
+    private static $_usedBackends = array();
68
+
69
+    private static $_setupedBackends = array();
70
+
71
+    // bool, stores if a user want to access a resource anonymously, e.g if they open a public link
72
+    private static $incognitoMode = false;
73
+
74
+    /**
75
+     * Adds the backend to the list of used backends
76
+     *
77
+     * @param string|\OCP\UserInterface $backend default: database The backend to use for user management
78
+     * @return bool
79
+     *
80
+     * Set the User Authentication Module
81
+     */
82
+    public static function useBackend($backend = 'database') {
83
+        if ($backend instanceof \OCP\UserInterface) {
84
+            self::$_usedBackends[get_class($backend)] = $backend;
85
+            \OC::$server->getUserManager()->registerBackend($backend);
86
+        } else {
87
+            // You'll never know what happens
88
+            if (null === $backend OR !is_string($backend)) {
89
+                $backend = 'database';
90
+            }
91
+
92
+            // Load backend
93
+            switch ($backend) {
94
+                case 'database':
95
+                case 'mysql':
96
+                case 'sqlite':
97
+                    \OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', \OCP\Util::DEBUG);
98
+                    self::$_usedBackends[$backend] = new \OC\User\Database();
99
+                    \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
100
+                    break;
101
+                case 'dummy':
102
+                    self::$_usedBackends[$backend] = new \Test\Util\User\Dummy();
103
+                    \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
104
+                    break;
105
+                default:
106
+                    \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', \OCP\Util::DEBUG);
107
+                    $className = 'OC_USER_' . strtoupper($backend);
108
+                    self::$_usedBackends[$backend] = new $className();
109
+                    \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]);
110
+                    break;
111
+            }
112
+        }
113
+        return true;
114
+    }
115
+
116
+    /**
117
+     * remove all used backends
118
+     */
119
+    public static function clearBackends() {
120
+        self::$_usedBackends = array();
121
+        \OC::$server->getUserManager()->clearBackends();
122
+    }
123
+
124
+    /**
125
+     * setup the configured backends in config.php
126
+     */
127
+    public static function setupBackends() {
128
+        OC_App::loadApps(['prelogin']);
129
+        $backends = \OC::$server->getSystemConfig()->getValue('user_backends', []);
130
+        if (isset($backends['default']) && !$backends['default']) {
131
+            // clear default backends
132
+            self::clearBackends();
133
+        }
134
+        foreach ($backends as $i => $config) {
135
+            if (!is_array($config)) {
136
+                continue;
137
+            }
138
+            $class = $config['class'];
139
+            $arguments = $config['arguments'];
140
+            if (class_exists($class)) {
141
+                if (array_search($i, self::$_setupedBackends) === false) {
142
+                    // make a reflection object
143
+                    $reflectionObj = new ReflectionClass($class);
144
+
145
+                    // use Reflection to create a new instance, using the $args
146
+                    $backend = $reflectionObj->newInstanceArgs($arguments);
147
+                    self::useBackend($backend);
148
+                    self::$_setupedBackends[] = $i;
149
+                } else {
150
+                    \OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', \OCP\Util::DEBUG);
151
+                }
152
+            } else {
153
+                \OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', \OCP\Util::ERROR);
154
+            }
155
+        }
156
+    }
157
+
158
+    /**
159
+     * Try to login a user using the magic cookie (remember login)
160
+     *
161
+     * @deprecated use \OCP\IUserSession::loginWithCookie()
162
+     * @param string $uid The username of the user to log in
163
+     * @param string $token
164
+     * @param string $oldSessionId
165
+     * @return bool
166
+     */
167
+    public static function loginWithCookie($uid, $token, $oldSessionId) {
168
+        return self::getUserSession()->loginWithCookie($uid, $token, $oldSessionId);
169
+    }
170
+
171
+    /**
172
+     * Try to login a user, assuming authentication
173
+     * has already happened (e.g. via Single Sign On).
174
+     *
175
+     * Log in a user and regenerate a new session.
176
+     *
177
+     * @param \OCP\Authentication\IApacheBackend $backend
178
+     * @return bool
179
+     */
180
+    public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) {
181
+
182
+        $uid = $backend->getCurrentUserId();
183
+        $run = true;
184
+        OC_Hook::emit("OC_User", "pre_login", array("run" => &$run, "uid" => $uid));
185
+
186
+        if ($uid) {
187
+            if (self::getUser() !== $uid) {
188
+                self::setUserId($uid);
189
+                $setUidAsDisplayName = true;
190
+                if($backend instanceof \OCP\UserInterface
191
+                    && $backend->implementsActions(OC_User_Backend::GET_DISPLAYNAME)) {
192
+
193
+                    $backendDisplayName = $backend->getDisplayName($uid);
194
+                    if(is_string($backendDisplayName) && trim($backendDisplayName) !== '') {
195
+                        $setUidAsDisplayName = false;
196
+                    }
197
+                }
198
+                if($setUidAsDisplayName) {
199
+                    self::setDisplayName($uid);
200
+                }
201
+                self::getUserSession()->setLoginName($uid);
202
+                $request = OC::$server->getRequest();
203
+                self::getUserSession()->createSessionToken($request, $uid, $uid);
204
+                // setup the filesystem
205
+                OC_Util::setupFS($uid);
206
+                // first call the post_login hooks, the login-process needs to be
207
+                // completed before we can safely create the users folder.
208
+                // For example encryption needs to initialize the users keys first
209
+                // before we can create the user folder with the skeleton files
210
+                OC_Hook::emit("OC_User", "post_login", array("uid" => $uid, 'password' => ''));
211
+                //trigger creation of user home and /files folder
212
+                \OC::$server->getUserFolder($uid);
213
+            }
214
+            return true;
215
+        }
216
+        return false;
217
+    }
218
+
219
+    /**
220
+     * Verify with Apache whether user is authenticated.
221
+     *
222
+     * @return boolean|null
223
+     *          true: authenticated
224
+     *          false: not authenticated
225
+     *          null: not handled / no backend available
226
+     */
227
+    public static function handleApacheAuth() {
228
+        $backend = self::findFirstActiveUsedBackend();
229
+        if ($backend) {
230
+            OC_App::loadApps();
231
+
232
+            //setup extra user backends
233
+            self::setupBackends();
234
+            self::unsetMagicInCookie();
235
+
236
+            return self::loginWithApache($backend);
237
+        }
238
+
239
+        return null;
240
+    }
241
+
242
+
243
+    /**
244
+     * Sets user id for session and triggers emit
245
+     *
246
+     * @param string $uid
247
+     */
248
+    public static function setUserId($uid) {
249
+        $userSession = \OC::$server->getUserSession();
250
+        $userManager = \OC::$server->getUserManager();
251
+        if ($user = $userManager->get($uid)) {
252
+            $userSession->setUser($user);
253
+        } else {
254
+            \OC::$server->getSession()->set('user_id', $uid);
255
+        }
256
+    }
257
+
258
+    /**
259
+     * Sets user display name for session
260
+     *
261
+     * @param string $uid
262
+     * @param string $displayName
263
+     * @return bool Whether the display name could get set
264
+     */
265
+    public static function setDisplayName($uid, $displayName = null) {
266
+        if (is_null($displayName)) {
267
+            $displayName = $uid;
268
+        }
269
+        $user = \OC::$server->getUserManager()->get($uid);
270
+        if ($user) {
271
+            return $user->setDisplayName($displayName);
272
+        } else {
273
+            return false;
274
+        }
275
+    }
276
+
277
+    /**
278
+     * Check if the user is logged in, considers also the HTTP basic credentials
279
+     *
280
+     * @deprecated use \OC::$server->getUserSession()->isLoggedIn()
281
+     * @return bool
282
+     */
283
+    public static function isLoggedIn() {
284
+        return \OC::$server->getUserSession()->isLoggedIn();
285
+    }
286
+
287
+    /**
288
+     * set incognito mode, e.g. if a user wants to open a public link
289
+     *
290
+     * @param bool $status
291
+     */
292
+    public static function setIncognitoMode($status) {
293
+        self::$incognitoMode = $status;
294
+    }
295
+
296
+    /**
297
+     * get incognito mode status
298
+     *
299
+     * @return bool
300
+     */
301
+    public static function isIncognitoMode() {
302
+        return self::$incognitoMode;
303
+    }
304
+
305
+    /**
306
+     * Supplies an attribute to the logout hyperlink. The default behaviour
307
+     * is to return an href with '?logout=true' appended. However, it can
308
+     * supply any attribute(s) which are valid for <a>.
309
+     *
310
+     * @return string with one or more HTML attributes.
311
+     */
312
+    public static function getLogoutAttribute() {
313
+        $backend = self::findFirstActiveUsedBackend();
314
+        if ($backend) {
315
+            return $backend->getLogoutAttribute();
316
+        }
317
+
318
+        $logoutUrl = \OC::$server->getURLGenerator()->linkToRouteAbsolute(
319
+            'core.login.logout',
320
+            [
321
+                'requesttoken' => \OCP\Util::callRegister(),
322
+            ]
323
+        );
324
+
325
+        return 'href="'.$logoutUrl.'"';
326
+    }
327
+
328
+    /**
329
+     * Check if the user is an admin user
330
+     *
331
+     * @param string $uid uid of the admin
332
+     * @return bool
333
+     */
334
+    public static function isAdminUser($uid) {
335
+        $group = \OC::$server->getGroupManager()->get('admin');
336
+        $user = \OC::$server->getUserManager()->get($uid);
337
+        if ($group && $user && $group->inGroup($user) && self::$incognitoMode === false) {
338
+            return true;
339
+        }
340
+        return false;
341
+    }
342
+
343
+
344
+    /**
345
+     * get the user id of the user currently logged in.
346
+     *
347
+     * @return string|bool uid or false
348
+     */
349
+    public static function getUser() {
350
+        $uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null;
351
+        if (!is_null($uid) && self::$incognitoMode === false) {
352
+            return $uid;
353
+        } else {
354
+            return false;
355
+        }
356
+    }
357
+
358
+    /**
359
+     * get the display name of the user currently logged in.
360
+     *
361
+     * @param string $uid
362
+     * @return string uid or false
363
+     */
364
+    public static function getDisplayName($uid = null) {
365
+        if ($uid) {
366
+            $user = \OC::$server->getUserManager()->get($uid);
367
+            if ($user) {
368
+                return $user->getDisplayName();
369
+            } else {
370
+                return $uid;
371
+            }
372
+        } else {
373
+            $user = self::getUserSession()->getUser();
374
+            if ($user) {
375
+                return $user->getDisplayName();
376
+            } else {
377
+                return false;
378
+            }
379
+        }
380
+    }
381
+
382
+    /**
383
+     * Autogenerate a password
384
+     *
385
+     * @return string
386
+     *
387
+     * generates a password
388
+     */
389
+    public static function generatePassword() {
390
+        return \OC::$server->getSecureRandom()->generate(30);
391
+    }
392
+
393
+    /**
394
+     * Set password
395
+     *
396
+     * @param string $uid The username
397
+     * @param string $password The new password
398
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
399
+     * @return bool
400
+     *
401
+     * Change the password of a user
402
+     */
403
+    public static function setPassword($uid, $password, $recoveryPassword = null) {
404
+        $user = \OC::$server->getUserManager()->get($uid);
405
+        if ($user) {
406
+            return $user->setPassword($password, $recoveryPassword);
407
+        } else {
408
+            return false;
409
+        }
410
+    }
411
+
412
+    /**
413
+     * Check whether user can change his avatar
414
+     *
415
+     * @param string $uid The username
416
+     * @return bool
417
+     *
418
+     * Check whether a specified user can change his avatar
419
+     */
420
+    public static function canUserChangeAvatar($uid) {
421
+        $user = \OC::$server->getUserManager()->get($uid);
422
+        if ($user) {
423
+            return $user->canChangeAvatar();
424
+        } else {
425
+            return false;
426
+        }
427
+    }
428
+
429
+    /**
430
+     * Check whether user can change his password
431
+     *
432
+     * @param string $uid The username
433
+     * @return bool
434
+     *
435
+     * Check whether a specified user can change his password
436
+     */
437
+    public static function canUserChangePassword($uid) {
438
+        $user = \OC::$server->getUserManager()->get($uid);
439
+        if ($user) {
440
+            return $user->canChangePassword();
441
+        } else {
442
+            return false;
443
+        }
444
+    }
445
+
446
+    /**
447
+     * Check whether user can change his display name
448
+     *
449
+     * @param string $uid The username
450
+     * @return bool
451
+     *
452
+     * Check whether a specified user can change his display name
453
+     */
454
+    public static function canUserChangeDisplayName($uid) {
455
+        $user = \OC::$server->getUserManager()->get($uid);
456
+        if ($user) {
457
+            return $user->canChangeDisplayName();
458
+        } else {
459
+            return false;
460
+        }
461
+    }
462
+
463
+    /**
464
+     * Check if the password is correct
465
+     *
466
+     * @param string $uid The username
467
+     * @param string $password The password
468
+     * @return string|false user id a string on success, false otherwise
469
+     *
470
+     * Check if the password is correct without logging in the user
471
+     * returns the user id or false
472
+     */
473
+    public static function checkPassword($uid, $password) {
474
+        $manager = \OC::$server->getUserManager();
475
+        $username = $manager->checkPassword($uid, $password);
476
+        if ($username !== false) {
477
+            return $username->getUID();
478
+        }
479
+        return false;
480
+    }
481
+
482
+    /**
483
+     * @param string $uid The username
484
+     * @return string
485
+     *
486
+     * returns the path to the users home directory
487
+     * @deprecated Use \OC::$server->getUserManager->getHome()
488
+     */
489
+    public static function getHome($uid) {
490
+        $user = \OC::$server->getUserManager()->get($uid);
491
+        if ($user) {
492
+            return $user->getHome();
493
+        } else {
494
+            return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid;
495
+        }
496
+    }
497
+
498
+    /**
499
+     * Get a list of all users
500
+     *
501
+     * @return array an array of all uids
502
+     *
503
+     * Get a list of all users.
504
+     * @param string $search
505
+     * @param integer $limit
506
+     * @param integer $offset
507
+     */
508
+    public static function getUsers($search = '', $limit = null, $offset = null) {
509
+        $users = \OC::$server->getUserManager()->search($search, $limit, $offset);
510
+        $uids = array();
511
+        foreach ($users as $user) {
512
+            $uids[] = $user->getUID();
513
+        }
514
+        return $uids;
515
+    }
516
+
517
+    /**
518
+     * Get a list of all users display name
519
+     *
520
+     * @param string $search
521
+     * @param int $limit
522
+     * @param int $offset
523
+     * @return array associative array with all display names (value) and corresponding uids (key)
524
+     *
525
+     * Get a list of all display names and user ids.
526
+     * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead.
527
+     */
528
+    public static function getDisplayNames($search = '', $limit = null, $offset = null) {
529
+        $displayNames = array();
530
+        $users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset);
531
+        foreach ($users as $user) {
532
+            $displayNames[$user->getUID()] = $user->getDisplayName();
533
+        }
534
+        return $displayNames;
535
+    }
536
+
537
+    /**
538
+     * check if a user exists
539
+     *
540
+     * @param string $uid the username
541
+     * @return boolean
542
+     */
543
+    public static function userExists($uid) {
544
+        return \OC::$server->getUserManager()->userExists($uid);
545
+    }
546
+
547
+    /**
548
+     * disables a user
549
+     *
550
+     * @param string $uid the user to disable
551
+     */
552
+    public static function disableUser($uid) {
553
+        $user = \OC::$server->getUserManager()->get($uid);
554
+        if ($user) {
555
+            $user->setEnabled(false);
556
+        }
557
+    }
558
+
559
+    /**
560
+     * enable a user
561
+     *
562
+     * @param string $uid
563
+     */
564
+    public static function enableUser($uid) {
565
+        $user = \OC::$server->getUserManager()->get($uid);
566
+        if ($user) {
567
+            $user->setEnabled(true);
568
+        }
569
+    }
570
+
571
+    /**
572
+     * checks if a user is enabled
573
+     *
574
+     * @param string $uid
575
+     * @return bool
576
+     */
577
+    public static function isEnabled($uid) {
578
+        $user = \OC::$server->getUserManager()->get($uid);
579
+        if ($user) {
580
+            return $user->isEnabled();
581
+        } else {
582
+            return false;
583
+        }
584
+    }
585
+
586
+    /**
587
+     * Set cookie value to use in next page load
588
+     *
589
+     * @param string $username username to be set
590
+     * @param string $token
591
+     */
592
+    public static function setMagicInCookie($username, $token) {
593
+        self::getUserSession()->setMagicInCookie($username, $token);
594
+    }
595
+
596
+    /**
597
+     * Remove cookie for "remember username"
598
+     */
599
+    public static function unsetMagicInCookie() {
600
+        self::getUserSession()->unsetMagicInCookie();
601
+    }
602
+
603
+    /**
604
+     * Returns the first active backend from self::$_usedBackends.
605
+     *
606
+     * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend
607
+     */
608
+    private static function findFirstActiveUsedBackend() {
609
+        foreach (self::$_usedBackends as $backend) {
610
+            if ($backend instanceof OCP\Authentication\IApacheBackend) {
611
+                if ($backend->isSessionActive()) {
612
+                    return $backend;
613
+                }
614
+            }
615
+        }
616
+
617
+        return null;
618
+    }
620 619
 }
Please login to merge, or discard this patch.
lib/private/Share/Share.php 2 patches
Indentation   +2837 added lines, -2837 removed lines patch added patch discarded remove patch
@@ -60,2874 +60,2874 @@
 block discarded – undo
60 60
  */
61 61
 class Share extends Constants {
62 62
 
63
-	/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
64
-	 * Construct permissions for share() and setPermissions with Or (|) e.g.
65
-	 * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
66
-	 *
67
-	 * Check if permission is granted with And (&) e.g. Check if delete is
68
-	 * granted: if ($permissions & PERMISSION_DELETE)
69
-	 *
70
-	 * Remove permissions with And (&) and Not (~) e.g. Remove the update
71
-	 * permission: $permissions &= ~PERMISSION_UPDATE
72
-	 *
73
-	 * Apps are required to handle permissions on their own, this class only
74
-	 * stores and manages the permissions of shares
75
-	 * @see lib/public/constants.php
76
-	 */
77
-
78
-	/**
79
-	 * Register a sharing backend class that implements OCP\Share_Backend for an item type
80
-	 * @param string $itemType Item type
81
-	 * @param string $class Backend class
82
-	 * @param string $collectionOf (optional) Depends on item type
83
-	 * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
84
-	 * @return boolean true if backend is registered or false if error
85
-	 */
86
-	public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
87
-		if (self::isEnabled()) {
88
-			if (!isset(self::$backendTypes[$itemType])) {
89
-				self::$backendTypes[$itemType] = array(
90
-					'class' => $class,
91
-					'collectionOf' => $collectionOf,
92
-					'supportedFileExtensions' => $supportedFileExtensions
93
-				);
94
-				if(count(self::$backendTypes) === 1) {
95
-					\OC_Util::addScript('core', 'shareconfigmodel');
96
-					\OC_Util::addScript('core', 'shareitemmodel');
97
-					\OC_Util::addScript('core', 'sharedialogresharerinfoview');
98
-					\OC_Util::addScript('core', 'sharedialoglinkshareview');
99
-					\OC_Util::addScript('core', 'sharedialogexpirationview');
100
-					\OC_Util::addScript('core', 'sharedialogshareelistview');
101
-					\OC_Util::addScript('core', 'sharedialogview');
102
-					\OC_Util::addScript('core', 'share');
103
-					\OC_Util::addStyle('core', 'share');
104
-				}
105
-				return true;
106
-			}
107
-			\OCP\Util::writeLog('OCP\Share',
108
-				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
109
-				.' is already registered for '.$itemType,
110
-				\OCP\Util::WARN);
111
-		}
112
-		return false;
113
-	}
114
-
115
-	/**
116
-	 * Check if the Share API is enabled
117
-	 * @return boolean true if enabled or false
118
-	 *
119
-	 * The Share API is enabled by default if not configured
120
-	 */
121
-	public static function isEnabled() {
122
-		if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
123
-			return true;
124
-		}
125
-		return false;
126
-	}
127
-
128
-	/**
129
-	 * Find which users can access a shared item
130
-	 * @param string $path to the file
131
-	 * @param string $ownerUser owner of the file
132
-	 * @param IUserManager $userManager
133
-	 * @param ILogger $logger
134
-	 * @param boolean $includeOwner include owner to the list of users with access to the file
135
-	 * @param boolean $returnUserPaths Return an array with the user => path map
136
-	 * @param boolean $recursive take all parent folders into account (default true)
137
-	 * @return array
138
-	 * @note $path needs to be relative to user data dir, e.g. 'file.txt'
139
-	 *       not '/admin/data/file.txt'
140
-	 * @throws \OC\User\NoUserException
141
-	 */
142
-	public static function getUsersSharingFile($path,
143
-											   $ownerUser,
144
-											   IUserManager $userManager,
145
-											   ILogger $logger,
146
-											   $includeOwner = false,
147
-											   $returnUserPaths = false,
148
-											   $recursive = true) {
149
-		$userObject = $userManager->get($ownerUser);
150
-
151
-		if (is_null($userObject)) {
152
-			$logger->error(
153
-				sprintf(
154
-					'Backends provided no user object for %s',
155
-					$ownerUser
156
-				),
157
-				[
158
-					'app' => 'files',
159
-				]
160
-			);
161
-			throw new \OC\User\NoUserException('Backends provided no user object');
162
-		}
163
-
164
-		$ownerUser = $userObject->getUID();
165
-
166
-		Filesystem::initMountPoints($ownerUser);
167
-		$shares = $sharePaths = $fileTargets = array();
168
-		$publicShare = false;
169
-		$remoteShare = false;
170
-		$source = -1;
171
-		$cache = $mountPath = false;
172
-
173
-		$view = new \OC\Files\View('/' . $ownerUser . '/files');
174
-		$meta = $view->getFileInfo($path);
175
-		if ($meta) {
176
-			$path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
177
-		} else {
178
-			// if the file doesn't exists yet we start with the parent folder
179
-			$meta = $view->getFileInfo(dirname($path));
180
-		}
181
-
182
-		if($meta !== false) {
183
-			$source = $meta['fileid'];
184
-			$cache = new \OC\Files\Cache\Cache($meta['storage']);
185
-
186
-			$mountPath = $meta->getMountPoint()->getMountPoint();
187
-			if ($mountPath !== false) {
188
-				$mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files'));
189
-			}
190
-		}
191
-
192
-		$paths = [];
193
-		while ($source !== -1) {
194
-			// Fetch all shares with another user
195
-			if (!$returnUserPaths) {
196
-				$query = \OC_DB::prepare(
197
-					'SELECT `share_with`, `file_source`, `file_target`
63
+    /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
64
+     * Construct permissions for share() and setPermissions with Or (|) e.g.
65
+     * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
66
+     *
67
+     * Check if permission is granted with And (&) e.g. Check if delete is
68
+     * granted: if ($permissions & PERMISSION_DELETE)
69
+     *
70
+     * Remove permissions with And (&) and Not (~) e.g. Remove the update
71
+     * permission: $permissions &= ~PERMISSION_UPDATE
72
+     *
73
+     * Apps are required to handle permissions on their own, this class only
74
+     * stores and manages the permissions of shares
75
+     * @see lib/public/constants.php
76
+     */
77
+
78
+    /**
79
+     * Register a sharing backend class that implements OCP\Share_Backend for an item type
80
+     * @param string $itemType Item type
81
+     * @param string $class Backend class
82
+     * @param string $collectionOf (optional) Depends on item type
83
+     * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
84
+     * @return boolean true if backend is registered or false if error
85
+     */
86
+    public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
87
+        if (self::isEnabled()) {
88
+            if (!isset(self::$backendTypes[$itemType])) {
89
+                self::$backendTypes[$itemType] = array(
90
+                    'class' => $class,
91
+                    'collectionOf' => $collectionOf,
92
+                    'supportedFileExtensions' => $supportedFileExtensions
93
+                );
94
+                if(count(self::$backendTypes) === 1) {
95
+                    \OC_Util::addScript('core', 'shareconfigmodel');
96
+                    \OC_Util::addScript('core', 'shareitemmodel');
97
+                    \OC_Util::addScript('core', 'sharedialogresharerinfoview');
98
+                    \OC_Util::addScript('core', 'sharedialoglinkshareview');
99
+                    \OC_Util::addScript('core', 'sharedialogexpirationview');
100
+                    \OC_Util::addScript('core', 'sharedialogshareelistview');
101
+                    \OC_Util::addScript('core', 'sharedialogview');
102
+                    \OC_Util::addScript('core', 'share');
103
+                    \OC_Util::addStyle('core', 'share');
104
+                }
105
+                return true;
106
+            }
107
+            \OCP\Util::writeLog('OCP\Share',
108
+                'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
109
+                .' is already registered for '.$itemType,
110
+                \OCP\Util::WARN);
111
+        }
112
+        return false;
113
+    }
114
+
115
+    /**
116
+     * Check if the Share API is enabled
117
+     * @return boolean true if enabled or false
118
+     *
119
+     * The Share API is enabled by default if not configured
120
+     */
121
+    public static function isEnabled() {
122
+        if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
123
+            return true;
124
+        }
125
+        return false;
126
+    }
127
+
128
+    /**
129
+     * Find which users can access a shared item
130
+     * @param string $path to the file
131
+     * @param string $ownerUser owner of the file
132
+     * @param IUserManager $userManager
133
+     * @param ILogger $logger
134
+     * @param boolean $includeOwner include owner to the list of users with access to the file
135
+     * @param boolean $returnUserPaths Return an array with the user => path map
136
+     * @param boolean $recursive take all parent folders into account (default true)
137
+     * @return array
138
+     * @note $path needs to be relative to user data dir, e.g. 'file.txt'
139
+     *       not '/admin/data/file.txt'
140
+     * @throws \OC\User\NoUserException
141
+     */
142
+    public static function getUsersSharingFile($path,
143
+                                                $ownerUser,
144
+                                                IUserManager $userManager,
145
+                                                ILogger $logger,
146
+                                                $includeOwner = false,
147
+                                                $returnUserPaths = false,
148
+                                                $recursive = true) {
149
+        $userObject = $userManager->get($ownerUser);
150
+
151
+        if (is_null($userObject)) {
152
+            $logger->error(
153
+                sprintf(
154
+                    'Backends provided no user object for %s',
155
+                    $ownerUser
156
+                ),
157
+                [
158
+                    'app' => 'files',
159
+                ]
160
+            );
161
+            throw new \OC\User\NoUserException('Backends provided no user object');
162
+        }
163
+
164
+        $ownerUser = $userObject->getUID();
165
+
166
+        Filesystem::initMountPoints($ownerUser);
167
+        $shares = $sharePaths = $fileTargets = array();
168
+        $publicShare = false;
169
+        $remoteShare = false;
170
+        $source = -1;
171
+        $cache = $mountPath = false;
172
+
173
+        $view = new \OC\Files\View('/' . $ownerUser . '/files');
174
+        $meta = $view->getFileInfo($path);
175
+        if ($meta) {
176
+            $path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
177
+        } else {
178
+            // if the file doesn't exists yet we start with the parent folder
179
+            $meta = $view->getFileInfo(dirname($path));
180
+        }
181
+
182
+        if($meta !== false) {
183
+            $source = $meta['fileid'];
184
+            $cache = new \OC\Files\Cache\Cache($meta['storage']);
185
+
186
+            $mountPath = $meta->getMountPoint()->getMountPoint();
187
+            if ($mountPath !== false) {
188
+                $mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files'));
189
+            }
190
+        }
191
+
192
+        $paths = [];
193
+        while ($source !== -1) {
194
+            // Fetch all shares with another user
195
+            if (!$returnUserPaths) {
196
+                $query = \OC_DB::prepare(
197
+                    'SELECT `share_with`, `file_source`, `file_target`
198 198
 					FROM
199 199
 					`*PREFIX*share`
200 200
 					WHERE
201 201
 					`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
202
-				);
203
-				$result = $query->execute(array($source, self::SHARE_TYPE_USER));
204
-			} else {
205
-				$query = \OC_DB::prepare(
206
-					'SELECT `share_with`, `file_source`, `file_target`
202
+                );
203
+                $result = $query->execute(array($source, self::SHARE_TYPE_USER));
204
+            } else {
205
+                $query = \OC_DB::prepare(
206
+                    'SELECT `share_with`, `file_source`, `file_target`
207 207
 				FROM
208 208
 				`*PREFIX*share`
209 209
 				WHERE
210 210
 				`item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')'
211
-				);
212
-				$result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
213
-			}
214
-
215
-			if (\OCP\DB::isError($result)) {
216
-				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
217
-			} else {
218
-				while ($row = $result->fetchRow()) {
219
-					$shares[] = $row['share_with'];
220
-					if ($returnUserPaths) {
221
-						$fileTargets[(int) $row['file_source']][$row['share_with']] = $row;
222
-					}
223
-				}
224
-			}
225
-
226
-			// We also need to take group shares into account
227
-			$query = \OC_DB::prepare(
228
-				'SELECT `share_with`, `file_source`, `file_target`
211
+                );
212
+                $result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
213
+            }
214
+
215
+            if (\OCP\DB::isError($result)) {
216
+                \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
217
+            } else {
218
+                while ($row = $result->fetchRow()) {
219
+                    $shares[] = $row['share_with'];
220
+                    if ($returnUserPaths) {
221
+                        $fileTargets[(int) $row['file_source']][$row['share_with']] = $row;
222
+                    }
223
+                }
224
+            }
225
+
226
+            // We also need to take group shares into account
227
+            $query = \OC_DB::prepare(
228
+                'SELECT `share_with`, `file_source`, `file_target`
229 229
 				FROM
230 230
 				`*PREFIX*share`
231 231
 				WHERE
232 232
 				`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
233
-			);
234
-
235
-			$result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
236
-
237
-			if (\OCP\DB::isError($result)) {
238
-				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
239
-			} else {
240
-				$groupManager = \OC::$server->getGroupManager();
241
-				while ($row = $result->fetchRow()) {
242
-
243
-					$usersInGroup = [];
244
-					$group = $groupManager->get($row['share_with']);
245
-					if ($group) {
246
-						$users = $group->searchUsers('', -1, 0);
247
-						$userIds = array();
248
-						foreach ($users as $user) {
249
-							$userIds[] = $user->getUID();
250
-						}
251
-						$usersInGroup = $userIds;
252
-					}
253
-					$shares = array_merge($shares, $usersInGroup);
254
-					if ($returnUserPaths) {
255
-						foreach ($usersInGroup as $user) {
256
-							if (!isset($fileTargets[(int) $row['file_source']][$user])) {
257
-								// When the user already has an entry for this file source
258
-								// the file is either shared directly with him as well, or
259
-								// he has an exception entry (because of naming conflict).
260
-								$fileTargets[(int) $row['file_source']][$user] = $row;
261
-							}
262
-						}
263
-					}
264
-				}
265
-			}
266
-
267
-			//check for public link shares
268
-			if (!$publicShare) {
269
-				$query = \OC_DB::prepare('
233
+            );
234
+
235
+            $result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
236
+
237
+            if (\OCP\DB::isError($result)) {
238
+                \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
239
+            } else {
240
+                $groupManager = \OC::$server->getGroupManager();
241
+                while ($row = $result->fetchRow()) {
242
+
243
+                    $usersInGroup = [];
244
+                    $group = $groupManager->get($row['share_with']);
245
+                    if ($group) {
246
+                        $users = $group->searchUsers('', -1, 0);
247
+                        $userIds = array();
248
+                        foreach ($users as $user) {
249
+                            $userIds[] = $user->getUID();
250
+                        }
251
+                        $usersInGroup = $userIds;
252
+                    }
253
+                    $shares = array_merge($shares, $usersInGroup);
254
+                    if ($returnUserPaths) {
255
+                        foreach ($usersInGroup as $user) {
256
+                            if (!isset($fileTargets[(int) $row['file_source']][$user])) {
257
+                                // When the user already has an entry for this file source
258
+                                // the file is either shared directly with him as well, or
259
+                                // he has an exception entry (because of naming conflict).
260
+                                $fileTargets[(int) $row['file_source']][$user] = $row;
261
+                            }
262
+                        }
263
+                    }
264
+                }
265
+            }
266
+
267
+            //check for public link shares
268
+            if (!$publicShare) {
269
+                $query = \OC_DB::prepare('
270 270
 					SELECT `share_with`
271 271
 					FROM `*PREFIX*share`
272 272
 					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
273
-				);
274
-
275
-				$result = $query->execute(array($source, self::SHARE_TYPE_LINK));
276
-
277
-				if (\OCP\DB::isError($result)) {
278
-					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
279
-				} else {
280
-					if ($result->fetchRow()) {
281
-						$publicShare = true;
282
-					}
283
-				}
284
-			}
285
-
286
-			//check for remote share
287
-			if (!$remoteShare) {
288
-				$query = \OC_DB::prepare('
273
+                );
274
+
275
+                $result = $query->execute(array($source, self::SHARE_TYPE_LINK));
276
+
277
+                if (\OCP\DB::isError($result)) {
278
+                    \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
279
+                } else {
280
+                    if ($result->fetchRow()) {
281
+                        $publicShare = true;
282
+                    }
283
+                }
284
+            }
285
+
286
+            //check for remote share
287
+            if (!$remoteShare) {
288
+                $query = \OC_DB::prepare('
289 289
 					SELECT `share_with`
290 290
 					FROM `*PREFIX*share`
291 291
 					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
292
-				);
293
-
294
-				$result = $query->execute(array($source, self::SHARE_TYPE_REMOTE));
295
-
296
-				if (\OCP\DB::isError($result)) {
297
-					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
298
-				} else {
299
-					if ($result->fetchRow()) {
300
-						$remoteShare = true;
301
-					}
302
-				}
303
-			}
304
-
305
-			// let's get the parent for the next round
306
-			$meta = $cache->get((int)$source);
307
-			if ($recursive === true && $meta !== false) {
308
-				$paths[$source] = $meta['path'];
309
-				$source = (int)$meta['parent'];
310
-			} else {
311
-				$source = -1;
312
-			}
313
-		}
314
-
315
-		// Include owner in list of users, if requested
316
-		if ($includeOwner) {
317
-			$shares[] = $ownerUser;
318
-		}
319
-
320
-		if ($returnUserPaths) {
321
-			$fileTargetIDs = array_keys($fileTargets);
322
-			$fileTargetIDs = array_unique($fileTargetIDs);
323
-
324
-			if (!empty($fileTargetIDs)) {
325
-				$query = \OC_DB::prepare(
326
-					'SELECT `fileid`, `path`
292
+                );
293
+
294
+                $result = $query->execute(array($source, self::SHARE_TYPE_REMOTE));
295
+
296
+                if (\OCP\DB::isError($result)) {
297
+                    \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
298
+                } else {
299
+                    if ($result->fetchRow()) {
300
+                        $remoteShare = true;
301
+                    }
302
+                }
303
+            }
304
+
305
+            // let's get the parent for the next round
306
+            $meta = $cache->get((int)$source);
307
+            if ($recursive === true && $meta !== false) {
308
+                $paths[$source] = $meta['path'];
309
+                $source = (int)$meta['parent'];
310
+            } else {
311
+                $source = -1;
312
+            }
313
+        }
314
+
315
+        // Include owner in list of users, if requested
316
+        if ($includeOwner) {
317
+            $shares[] = $ownerUser;
318
+        }
319
+
320
+        if ($returnUserPaths) {
321
+            $fileTargetIDs = array_keys($fileTargets);
322
+            $fileTargetIDs = array_unique($fileTargetIDs);
323
+
324
+            if (!empty($fileTargetIDs)) {
325
+                $query = \OC_DB::prepare(
326
+                    'SELECT `fileid`, `path`
327 327
 					FROM `*PREFIX*filecache`
328 328
 					WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')'
329
-				);
330
-				$result = $query->execute();
331
-
332
-				if (\OCP\DB::isError($result)) {
333
-					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
334
-				} else {
335
-					while ($row = $result->fetchRow()) {
336
-						foreach ($fileTargets[$row['fileid']] as $uid => $shareData) {
337
-							if ($mountPath !== false) {
338
-								$sharedPath = $shareData['file_target'];
339
-								$sharedPath .= substr($path, strlen($mountPath) + strlen($paths[$row['fileid']]));
340
-								$sharePaths[$uid] = $sharedPath;
341
-							} else {
342
-								$sharedPath = $shareData['file_target'];
343
-								$sharedPath .= substr($path, strlen($row['path']) -5);
344
-								$sharePaths[$uid] = $sharedPath;
345
-							}
346
-						}
347
-					}
348
-				}
349
-			}
350
-
351
-			if ($includeOwner) {
352
-				$sharePaths[$ownerUser] = $path;
353
-			} else {
354
-				unset($sharePaths[$ownerUser]);
355
-			}
356
-
357
-			return $sharePaths;
358
-		}
359
-
360
-		return array('users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare);
361
-	}
362
-
363
-	/**
364
-	 * Get the items of item type shared with the current user
365
-	 * @param string $itemType
366
-	 * @param int $format (optional) Format type must be defined by the backend
367
-	 * @param mixed $parameters (optional)
368
-	 * @param int $limit Number of items to return (optional) Returns all by default
369
-	 * @param boolean $includeCollections (optional)
370
-	 * @return mixed Return depends on format
371
-	 */
372
-	public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
373
-											  $parameters = null, $limit = -1, $includeCollections = false) {
374
-		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
375
-			$parameters, $limit, $includeCollections);
376
-	}
377
-
378
-	/**
379
-	 * Get the items of item type shared with a user
380
-	 * @param string $itemType
381
-	 * @param string $user id for which user we want the shares
382
-	 * @param int $format (optional) Format type must be defined by the backend
383
-	 * @param mixed $parameters (optional)
384
-	 * @param int $limit Number of items to return (optional) Returns all by default
385
-	 * @param boolean $includeCollections (optional)
386
-	 * @return mixed Return depends on format
387
-	 */
388
-	public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
389
-												  $parameters = null, $limit = -1, $includeCollections = false) {
390
-		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
391
-			$parameters, $limit, $includeCollections);
392
-	}
393
-
394
-	/**
395
-	 * Get the item of item type shared with the current user
396
-	 * @param string $itemType
397
-	 * @param string $itemTarget
398
-	 * @param int $format (optional) Format type must be defined by the backend
399
-	 * @param mixed $parameters (optional)
400
-	 * @param boolean $includeCollections (optional)
401
-	 * @return mixed Return depends on format
402
-	 */
403
-	public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
404
-											 $parameters = null, $includeCollections = false) {
405
-		return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
406
-			$parameters, 1, $includeCollections);
407
-	}
408
-
409
-	/**
410
-	 * Get the item of item type shared with a given user by source
411
-	 * @param string $itemType
412
-	 * @param string $itemSource
413
-	 * @param string $user User to whom the item was shared
414
-	 * @param string $owner Owner of the share
415
-	 * @param int $shareType only look for a specific share type
416
-	 * @return array Return list of items with file_target, permissions and expiration
417
-	 */
418
-	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
419
-		$shares = array();
420
-		$fileDependent = false;
421
-
422
-		$where = 'WHERE';
423
-		$fileDependentWhere = '';
424
-		if ($itemType === 'file' || $itemType === 'folder') {
425
-			$fileDependent = true;
426
-			$column = 'file_source';
427
-			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
428
-			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
429
-		} else {
430
-			$column = 'item_source';
431
-		}
432
-
433
-		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
434
-
435
-		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
436
-		$arguments = array($itemSource, $itemType);
437
-		// for link shares $user === null
438
-		if ($user !== null) {
439
-			$where .= ' AND `share_with` = ? ';
440
-			$arguments[] = $user;
441
-		}
442
-
443
-		if ($shareType !== null) {
444
-			$where .= ' AND `share_type` = ? ';
445
-			$arguments[] = $shareType;
446
-		}
447
-
448
-		if ($owner !== null) {
449
-			$where .= ' AND `uid_owner` = ? ';
450
-			$arguments[] = $owner;
451
-		}
452
-
453
-		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
454
-
455
-		$result = \OC_DB::executeAudited($query, $arguments);
456
-
457
-		while ($row = $result->fetchRow()) {
458
-			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
459
-				continue;
460
-			}
461
-			if ($fileDependent && (int)$row['file_parent'] === -1) {
462
-				// if it is a mount point we need to get the path from the mount manager
463
-				$mountManager = \OC\Files\Filesystem::getMountManager();
464
-				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
465
-				if (!empty($mountPoint)) {
466
-					$path = $mountPoint[0]->getMountPoint();
467
-					$path = trim($path, '/');
468
-					$path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
469
-					$row['path'] = $path;
470
-				} else {
471
-					\OC::$server->getLogger()->warning(
472
-						'Could not resolve mount point for ' . $row['storage_id'],
473
-						['app' => 'OCP\Share']
474
-					);
475
-				}
476
-			}
477
-			$shares[] = $row;
478
-		}
479
-
480
-		//if didn't found a result than let's look for a group share.
481
-		if(empty($shares) && $user !== null) {
482
-			$userObject = \OC::$server->getUserManager()->get($user);
483
-			$groups = [];
484
-			if ($userObject) {
485
-				$groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
486
-			}
487
-
488
-			if (!empty($groups)) {
489
-				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
490
-				$arguments = array($itemSource, $itemType, $groups);
491
-				$types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY);
492
-
493
-				if ($owner !== null) {
494
-					$where .= ' AND `uid_owner` = ?';
495
-					$arguments[] = $owner;
496
-					$types[] = null;
497
-				}
498
-
499
-				// TODO: inject connection, hopefully one day in the future when this
500
-				// class isn't static anymore...
501
-				$conn = \OC::$server->getDatabaseConnection();
502
-				$result = $conn->executeQuery(
503
-					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
504
-					$arguments,
505
-					$types
506
-				);
507
-
508
-				while ($row = $result->fetch()) {
509
-					$shares[] = $row;
510
-				}
511
-			}
512
-		}
513
-
514
-		return $shares;
515
-
516
-	}
517
-
518
-	/**
519
-	 * Get the item of item type shared with the current user by source
520
-	 * @param string $itemType
521
-	 * @param string $itemSource
522
-	 * @param int $format (optional) Format type must be defined by the backend
523
-	 * @param mixed $parameters
524
-	 * @param boolean $includeCollections
525
-	 * @param string $shareWith (optional) define against which user should be checked, default: current user
526
-	 * @return array
527
-	 */
528
-	public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
529
-													 $parameters = null, $includeCollections = false, $shareWith = null) {
530
-		$shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
531
-		return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
532
-			$parameters, 1, $includeCollections, true);
533
-	}
534
-
535
-	/**
536
-	 * Get the item of item type shared by a link
537
-	 * @param string $itemType
538
-	 * @param string $itemSource
539
-	 * @param string $uidOwner Owner of link
540
-	 * @return array
541
-	 */
542
-	public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
543
-		return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
544
-			null, 1);
545
-	}
546
-
547
-	/**
548
-	 * Based on the given token the share information will be returned - password protected shares will be verified
549
-	 * @param string $token
550
-	 * @param bool $checkPasswordProtection
551
-	 * @return array|boolean false will be returned in case the token is unknown or unauthorized
552
-	 */
553
-	public static function getShareByToken($token, $checkPasswordProtection = true) {
554
-		$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
555
-		$result = $query->execute(array($token));
556
-		if ($result === false) {
557
-			\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR);
558
-		}
559
-		$row = $result->fetchRow();
560
-		if ($row === false) {
561
-			return false;
562
-		}
563
-		if (is_array($row) and self::expireItem($row)) {
564
-			return false;
565
-		}
566
-
567
-		// password protected shares need to be authenticated
568
-		if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
569
-			return false;
570
-		}
571
-
572
-		return $row;
573
-	}
574
-
575
-	/**
576
-	 * resolves reshares down to the last real share
577
-	 * @param array $linkItem
578
-	 * @return array file owner
579
-	 */
580
-	public static function resolveReShare($linkItem)
581
-	{
582
-		if (isset($linkItem['parent'])) {
583
-			$parent = $linkItem['parent'];
584
-			while (isset($parent)) {
585
-				$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
586
-				$item = $query->execute(array($parent))->fetchRow();
587
-				if (isset($item['parent'])) {
588
-					$parent = $item['parent'];
589
-				} else {
590
-					return $item;
591
-				}
592
-			}
593
-		}
594
-		return $linkItem;
595
-	}
596
-
597
-
598
-	/**
599
-	 * Get the shared items of item type owned by the current user
600
-	 * @param string $itemType
601
-	 * @param int $format (optional) Format type must be defined by the backend
602
-	 * @param mixed $parameters
603
-	 * @param int $limit Number of items to return (optional) Returns all by default
604
-	 * @param boolean $includeCollections
605
-	 * @return mixed Return depends on format
606
-	 */
607
-	public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
608
-										  $limit = -1, $includeCollections = false) {
609
-		return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
610
-			$parameters, $limit, $includeCollections);
611
-	}
612
-
613
-	/**
614
-	 * Get the shared item of item type owned by the current user
615
-	 * @param string $itemType
616
-	 * @param string $itemSource
617
-	 * @param int $format (optional) Format type must be defined by the backend
618
-	 * @param mixed $parameters
619
-	 * @param boolean $includeCollections
620
-	 * @return mixed Return depends on format
621
-	 */
622
-	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
623
-										 $parameters = null, $includeCollections = false) {
624
-		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
625
-			$parameters, -1, $includeCollections);
626
-	}
627
-
628
-	/**
629
-	 * Get all users an item is shared with
630
-	 * @param string $itemType
631
-	 * @param string $itemSource
632
-	 * @param string $uidOwner
633
-	 * @param boolean $includeCollections
634
-	 * @param boolean $checkExpireDate
635
-	 * @return array Return array of users
636
-	 */
637
-	public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
638
-
639
-		$users = array();
640
-		$items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
641
-		if ($items) {
642
-			foreach ($items as $item) {
643
-				if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
644
-					$users[] = $item['share_with'];
645
-				} else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
646
-
647
-					$group = \OC::$server->getGroupManager()->get($item['share_with']);
648
-					$userIds = [];
649
-					if ($group) {
650
-						$users = $group->searchUsers('', -1, 0);
651
-						foreach ($users as $user) {
652
-							$userIds[] = $user->getUID();
653
-						}
654
-						return $userIds;
655
-					}
656
-
657
-					$users = array_merge($users, $userIds);
658
-				}
659
-			}
660
-		}
661
-		return $users;
662
-	}
663
-
664
-	/**
665
-	 * Share an item with a user, group, or via private link
666
-	 * @param string $itemType
667
-	 * @param string $itemSource
668
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
669
-	 * @param string $shareWith User or group the item is being shared with
670
-	 * @param int $permissions CRUDS
671
-	 * @param string $itemSourceName
672
-	 * @param \DateTime $expirationDate
673
-	 * @param bool $passwordChanged
674
-	 * @return boolean|string Returns true on success or false on failure, Returns token on success for links
675
-	 * @throws \OC\HintException when the share type is remote and the shareWith is invalid
676
-	 * @throws \Exception
677
-	 */
678
-	public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
679
-
680
-		$backend = self::getBackend($itemType);
681
-		$l = \OC::$server->getL10N('lib');
682
-
683
-		if ($backend->isShareTypeAllowed($shareType) === false) {
684
-			$message = 'Sharing %s failed, because the backend does not allow shares from type %i';
685
-			$message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType));
686
-			\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG);
687
-			throw new \Exception($message_t);
688
-		}
689
-
690
-		$uidOwner = \OC_User::getUser();
691
-		$shareWithinGroupOnly = self::shareWithGroupMembersOnly();
692
-
693
-		if (is_null($itemSourceName)) {
694
-			$itemSourceName = $itemSource;
695
-		}
696
-		$itemName = $itemSourceName;
697
-
698
-		// check if file can be shared
699
-		if ($itemType === 'file' or $itemType === 'folder') {
700
-			$path = \OC\Files\Filesystem::getPath($itemSource);
701
-			$itemName = $path;
702
-
703
-			// verify that the file exists before we try to share it
704
-			if (!$path) {
705
-				$message = 'Sharing %s failed, because the file does not exist';
706
-				$message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName));
707
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
708
-				throw new \Exception($message_t);
709
-			}
710
-			// verify that the user has share permission
711
-			if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) {
712
-				$message = 'You are not allowed to share %s';
713
-				$message_t = $l->t('You are not allowed to share %s', [$path]);
714
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
715
-				throw new \Exception($message_t);
716
-			}
717
-		}
718
-
719
-		//verify that we don't share a folder which already contains a share mount point
720
-		if ($itemType === 'folder') {
721
-			$path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
722
-			$mountManager = \OC\Files\Filesystem::getMountManager();
723
-			$mounts = $mountManager->findIn($path);
724
-			foreach ($mounts as $mount) {
725
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
726
-					$message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
727
-					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
728
-					throw new \Exception($message);
729
-				}
730
-
731
-			}
732
-		}
733
-
734
-		// single file shares should never have delete permissions
735
-		if ($itemType === 'file') {
736
-			$permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
737
-		}
738
-
739
-		//Validate expirationDate
740
-		if ($expirationDate !== null) {
741
-			try {
742
-				/*
329
+                );
330
+                $result = $query->execute();
331
+
332
+                if (\OCP\DB::isError($result)) {
333
+                    \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
334
+                } else {
335
+                    while ($row = $result->fetchRow()) {
336
+                        foreach ($fileTargets[$row['fileid']] as $uid => $shareData) {
337
+                            if ($mountPath !== false) {
338
+                                $sharedPath = $shareData['file_target'];
339
+                                $sharedPath .= substr($path, strlen($mountPath) + strlen($paths[$row['fileid']]));
340
+                                $sharePaths[$uid] = $sharedPath;
341
+                            } else {
342
+                                $sharedPath = $shareData['file_target'];
343
+                                $sharedPath .= substr($path, strlen($row['path']) -5);
344
+                                $sharePaths[$uid] = $sharedPath;
345
+                            }
346
+                        }
347
+                    }
348
+                }
349
+            }
350
+
351
+            if ($includeOwner) {
352
+                $sharePaths[$ownerUser] = $path;
353
+            } else {
354
+                unset($sharePaths[$ownerUser]);
355
+            }
356
+
357
+            return $sharePaths;
358
+        }
359
+
360
+        return array('users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare);
361
+    }
362
+
363
+    /**
364
+     * Get the items of item type shared with the current user
365
+     * @param string $itemType
366
+     * @param int $format (optional) Format type must be defined by the backend
367
+     * @param mixed $parameters (optional)
368
+     * @param int $limit Number of items to return (optional) Returns all by default
369
+     * @param boolean $includeCollections (optional)
370
+     * @return mixed Return depends on format
371
+     */
372
+    public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
373
+                                                $parameters = null, $limit = -1, $includeCollections = false) {
374
+        return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
375
+            $parameters, $limit, $includeCollections);
376
+    }
377
+
378
+    /**
379
+     * Get the items of item type shared with a user
380
+     * @param string $itemType
381
+     * @param string $user id for which user we want the shares
382
+     * @param int $format (optional) Format type must be defined by the backend
383
+     * @param mixed $parameters (optional)
384
+     * @param int $limit Number of items to return (optional) Returns all by default
385
+     * @param boolean $includeCollections (optional)
386
+     * @return mixed Return depends on format
387
+     */
388
+    public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
389
+                                                    $parameters = null, $limit = -1, $includeCollections = false) {
390
+        return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
391
+            $parameters, $limit, $includeCollections);
392
+    }
393
+
394
+    /**
395
+     * Get the item of item type shared with the current user
396
+     * @param string $itemType
397
+     * @param string $itemTarget
398
+     * @param int $format (optional) Format type must be defined by the backend
399
+     * @param mixed $parameters (optional)
400
+     * @param boolean $includeCollections (optional)
401
+     * @return mixed Return depends on format
402
+     */
403
+    public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
404
+                                                $parameters = null, $includeCollections = false) {
405
+        return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
406
+            $parameters, 1, $includeCollections);
407
+    }
408
+
409
+    /**
410
+     * Get the item of item type shared with a given user by source
411
+     * @param string $itemType
412
+     * @param string $itemSource
413
+     * @param string $user User to whom the item was shared
414
+     * @param string $owner Owner of the share
415
+     * @param int $shareType only look for a specific share type
416
+     * @return array Return list of items with file_target, permissions and expiration
417
+     */
418
+    public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
419
+        $shares = array();
420
+        $fileDependent = false;
421
+
422
+        $where = 'WHERE';
423
+        $fileDependentWhere = '';
424
+        if ($itemType === 'file' || $itemType === 'folder') {
425
+            $fileDependent = true;
426
+            $column = 'file_source';
427
+            $fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
428
+            $fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
429
+        } else {
430
+            $column = 'item_source';
431
+        }
432
+
433
+        $select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
434
+
435
+        $where .= ' `' . $column . '` = ? AND `item_type` = ? ';
436
+        $arguments = array($itemSource, $itemType);
437
+        // for link shares $user === null
438
+        if ($user !== null) {
439
+            $where .= ' AND `share_with` = ? ';
440
+            $arguments[] = $user;
441
+        }
442
+
443
+        if ($shareType !== null) {
444
+            $where .= ' AND `share_type` = ? ';
445
+            $arguments[] = $shareType;
446
+        }
447
+
448
+        if ($owner !== null) {
449
+            $where .= ' AND `uid_owner` = ? ';
450
+            $arguments[] = $owner;
451
+        }
452
+
453
+        $query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
454
+
455
+        $result = \OC_DB::executeAudited($query, $arguments);
456
+
457
+        while ($row = $result->fetchRow()) {
458
+            if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
459
+                continue;
460
+            }
461
+            if ($fileDependent && (int)$row['file_parent'] === -1) {
462
+                // if it is a mount point we need to get the path from the mount manager
463
+                $mountManager = \OC\Files\Filesystem::getMountManager();
464
+                $mountPoint = $mountManager->findByStorageId($row['storage_id']);
465
+                if (!empty($mountPoint)) {
466
+                    $path = $mountPoint[0]->getMountPoint();
467
+                    $path = trim($path, '/');
468
+                    $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
469
+                    $row['path'] = $path;
470
+                } else {
471
+                    \OC::$server->getLogger()->warning(
472
+                        'Could not resolve mount point for ' . $row['storage_id'],
473
+                        ['app' => 'OCP\Share']
474
+                    );
475
+                }
476
+            }
477
+            $shares[] = $row;
478
+        }
479
+
480
+        //if didn't found a result than let's look for a group share.
481
+        if(empty($shares) && $user !== null) {
482
+            $userObject = \OC::$server->getUserManager()->get($user);
483
+            $groups = [];
484
+            if ($userObject) {
485
+                $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
486
+            }
487
+
488
+            if (!empty($groups)) {
489
+                $where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
490
+                $arguments = array($itemSource, $itemType, $groups);
491
+                $types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY);
492
+
493
+                if ($owner !== null) {
494
+                    $where .= ' AND `uid_owner` = ?';
495
+                    $arguments[] = $owner;
496
+                    $types[] = null;
497
+                }
498
+
499
+                // TODO: inject connection, hopefully one day in the future when this
500
+                // class isn't static anymore...
501
+                $conn = \OC::$server->getDatabaseConnection();
502
+                $result = $conn->executeQuery(
503
+                    'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
504
+                    $arguments,
505
+                    $types
506
+                );
507
+
508
+                while ($row = $result->fetch()) {
509
+                    $shares[] = $row;
510
+                }
511
+            }
512
+        }
513
+
514
+        return $shares;
515
+
516
+    }
517
+
518
+    /**
519
+     * Get the item of item type shared with the current user by source
520
+     * @param string $itemType
521
+     * @param string $itemSource
522
+     * @param int $format (optional) Format type must be defined by the backend
523
+     * @param mixed $parameters
524
+     * @param boolean $includeCollections
525
+     * @param string $shareWith (optional) define against which user should be checked, default: current user
526
+     * @return array
527
+     */
528
+    public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
529
+                                                        $parameters = null, $includeCollections = false, $shareWith = null) {
530
+        $shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
531
+        return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
532
+            $parameters, 1, $includeCollections, true);
533
+    }
534
+
535
+    /**
536
+     * Get the item of item type shared by a link
537
+     * @param string $itemType
538
+     * @param string $itemSource
539
+     * @param string $uidOwner Owner of link
540
+     * @return array
541
+     */
542
+    public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
543
+        return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
544
+            null, 1);
545
+    }
546
+
547
+    /**
548
+     * Based on the given token the share information will be returned - password protected shares will be verified
549
+     * @param string $token
550
+     * @param bool $checkPasswordProtection
551
+     * @return array|boolean false will be returned in case the token is unknown or unauthorized
552
+     */
553
+    public static function getShareByToken($token, $checkPasswordProtection = true) {
554
+        $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
555
+        $result = $query->execute(array($token));
556
+        if ($result === false) {
557
+            \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR);
558
+        }
559
+        $row = $result->fetchRow();
560
+        if ($row === false) {
561
+            return false;
562
+        }
563
+        if (is_array($row) and self::expireItem($row)) {
564
+            return false;
565
+        }
566
+
567
+        // password protected shares need to be authenticated
568
+        if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
569
+            return false;
570
+        }
571
+
572
+        return $row;
573
+    }
574
+
575
+    /**
576
+     * resolves reshares down to the last real share
577
+     * @param array $linkItem
578
+     * @return array file owner
579
+     */
580
+    public static function resolveReShare($linkItem)
581
+    {
582
+        if (isset($linkItem['parent'])) {
583
+            $parent = $linkItem['parent'];
584
+            while (isset($parent)) {
585
+                $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
586
+                $item = $query->execute(array($parent))->fetchRow();
587
+                if (isset($item['parent'])) {
588
+                    $parent = $item['parent'];
589
+                } else {
590
+                    return $item;
591
+                }
592
+            }
593
+        }
594
+        return $linkItem;
595
+    }
596
+
597
+
598
+    /**
599
+     * Get the shared items of item type owned by the current user
600
+     * @param string $itemType
601
+     * @param int $format (optional) Format type must be defined by the backend
602
+     * @param mixed $parameters
603
+     * @param int $limit Number of items to return (optional) Returns all by default
604
+     * @param boolean $includeCollections
605
+     * @return mixed Return depends on format
606
+     */
607
+    public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
608
+                                            $limit = -1, $includeCollections = false) {
609
+        return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
610
+            $parameters, $limit, $includeCollections);
611
+    }
612
+
613
+    /**
614
+     * Get the shared item of item type owned by the current user
615
+     * @param string $itemType
616
+     * @param string $itemSource
617
+     * @param int $format (optional) Format type must be defined by the backend
618
+     * @param mixed $parameters
619
+     * @param boolean $includeCollections
620
+     * @return mixed Return depends on format
621
+     */
622
+    public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
623
+                                            $parameters = null, $includeCollections = false) {
624
+        return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
625
+            $parameters, -1, $includeCollections);
626
+    }
627
+
628
+    /**
629
+     * Get all users an item is shared with
630
+     * @param string $itemType
631
+     * @param string $itemSource
632
+     * @param string $uidOwner
633
+     * @param boolean $includeCollections
634
+     * @param boolean $checkExpireDate
635
+     * @return array Return array of users
636
+     */
637
+    public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
638
+
639
+        $users = array();
640
+        $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
641
+        if ($items) {
642
+            foreach ($items as $item) {
643
+                if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
644
+                    $users[] = $item['share_with'];
645
+                } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
646
+
647
+                    $group = \OC::$server->getGroupManager()->get($item['share_with']);
648
+                    $userIds = [];
649
+                    if ($group) {
650
+                        $users = $group->searchUsers('', -1, 0);
651
+                        foreach ($users as $user) {
652
+                            $userIds[] = $user->getUID();
653
+                        }
654
+                        return $userIds;
655
+                    }
656
+
657
+                    $users = array_merge($users, $userIds);
658
+                }
659
+            }
660
+        }
661
+        return $users;
662
+    }
663
+
664
+    /**
665
+     * Share an item with a user, group, or via private link
666
+     * @param string $itemType
667
+     * @param string $itemSource
668
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
669
+     * @param string $shareWith User or group the item is being shared with
670
+     * @param int $permissions CRUDS
671
+     * @param string $itemSourceName
672
+     * @param \DateTime $expirationDate
673
+     * @param bool $passwordChanged
674
+     * @return boolean|string Returns true on success or false on failure, Returns token on success for links
675
+     * @throws \OC\HintException when the share type is remote and the shareWith is invalid
676
+     * @throws \Exception
677
+     */
678
+    public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
679
+
680
+        $backend = self::getBackend($itemType);
681
+        $l = \OC::$server->getL10N('lib');
682
+
683
+        if ($backend->isShareTypeAllowed($shareType) === false) {
684
+            $message = 'Sharing %s failed, because the backend does not allow shares from type %i';
685
+            $message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType));
686
+            \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG);
687
+            throw new \Exception($message_t);
688
+        }
689
+
690
+        $uidOwner = \OC_User::getUser();
691
+        $shareWithinGroupOnly = self::shareWithGroupMembersOnly();
692
+
693
+        if (is_null($itemSourceName)) {
694
+            $itemSourceName = $itemSource;
695
+        }
696
+        $itemName = $itemSourceName;
697
+
698
+        // check if file can be shared
699
+        if ($itemType === 'file' or $itemType === 'folder') {
700
+            $path = \OC\Files\Filesystem::getPath($itemSource);
701
+            $itemName = $path;
702
+
703
+            // verify that the file exists before we try to share it
704
+            if (!$path) {
705
+                $message = 'Sharing %s failed, because the file does not exist';
706
+                $message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName));
707
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
708
+                throw new \Exception($message_t);
709
+            }
710
+            // verify that the user has share permission
711
+            if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) {
712
+                $message = 'You are not allowed to share %s';
713
+                $message_t = $l->t('You are not allowed to share %s', [$path]);
714
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
715
+                throw new \Exception($message_t);
716
+            }
717
+        }
718
+
719
+        //verify that we don't share a folder which already contains a share mount point
720
+        if ($itemType === 'folder') {
721
+            $path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
722
+            $mountManager = \OC\Files\Filesystem::getMountManager();
723
+            $mounts = $mountManager->findIn($path);
724
+            foreach ($mounts as $mount) {
725
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
726
+                    $message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
727
+                    \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
728
+                    throw new \Exception($message);
729
+                }
730
+
731
+            }
732
+        }
733
+
734
+        // single file shares should never have delete permissions
735
+        if ($itemType === 'file') {
736
+            $permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
737
+        }
738
+
739
+        //Validate expirationDate
740
+        if ($expirationDate !== null) {
741
+            try {
742
+                /*
743 743
 				 * Reuse the validateExpireDate.
744 744
 				 * We have to pass time() since the second arg is the time
745 745
 				 * the file was shared, since it is not shared yet we just use
746 746
 				 * the current time.
747 747
 				 */
748
-				$expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource);
749
-			} catch (\Exception $e) {
750
-				throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404);
751
-			}
752
-		}
753
-
754
-		// Verify share type and sharing conditions are met
755
-		if ($shareType === self::SHARE_TYPE_USER) {
756
-			if ($shareWith == $uidOwner) {
757
-				$message = 'Sharing %s failed, because you can not share with yourself';
758
-				$message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
759
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
760
-				throw new \Exception($message_t);
761
-			}
762
-			if (!\OC_User::userExists($shareWith)) {
763
-				$message = 'Sharing %s failed, because the user %s does not exist';
764
-				$message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith));
765
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
766
-				throw new \Exception($message_t);
767
-			}
768
-			if ($shareWithinGroupOnly) {
769
-				$userManager = \OC::$server->getUserManager();
770
-				$groupManager = \OC::$server->getGroupManager();
771
-				$userOwner = $userManager->get($uidOwner);
772
-				$userShareWith = $userManager->get($shareWith);
773
-				$groupsOwner = [];
774
-				$groupsShareWith = [];
775
-				if ($userOwner) {
776
-					$groupsOwner = $groupManager->getUserGroupIds($userOwner);
777
-				}
778
-				if ($userShareWith) {
779
-					$groupsShareWith = $groupManager->getUserGroupIds($userShareWith);
780
-				}
781
-				$inGroup = array_intersect($groupsOwner, $groupsShareWith);
782
-				if (empty($inGroup)) {
783
-					$message = 'Sharing %s failed, because the user '
784
-						.'%s is not a member of any groups that %s is a member of';
785
-					$message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', array($itemName, $shareWith, $uidOwner));
786
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG);
787
-					throw new \Exception($message_t);
788
-				}
789
-			}
790
-			// Check if the item source is already shared with the user, either from the same owner or a different user
791
-			if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
792
-				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
793
-				// Only allow the same share to occur again if it is the same
794
-				// owner and is not a user share, this use case is for increasing
795
-				// permissions for a specific user
796
-				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
797
-					$message = 'Sharing %s failed, because this item is already shared with %s';
798
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
799
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
800
-					throw new \Exception($message_t);
801
-				}
802
-			}
803
-			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
804
-				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
805
-				// Only allow the same share to occur again if it is the same
806
-				// owner and is not a user share, this use case is for increasing
807
-				// permissions for a specific user
808
-				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
809
-					$message = 'Sharing %s failed, because this item is already shared with user %s';
810
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', array($itemSourceName, $shareWith));
811
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
812
-					throw new \Exception($message_t);
813
-				}
814
-			}
815
-		} else if ($shareType === self::SHARE_TYPE_GROUP) {
816
-			if (!\OC::$server->getGroupManager()->groupExists($shareWith)) {
817
-				$message = 'Sharing %s failed, because the group %s does not exist';
818
-				$message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith));
819
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
820
-				throw new \Exception($message_t);
821
-			}
822
-			if ($shareWithinGroupOnly && !\OC_Group::inGroup($uidOwner, $shareWith)) {
823
-				$group = \OC::$server->getGroupManager()->get($shareWith);
824
-				$user = \OC::$server->getUserManager()->get($uidOwner);
825
-				if (!$group || !$user || !$group->inGroup($user)) {
826
-					$message = 'Sharing %s failed, because '
827
-						. '%s is not a member of the group %s';
828
-					$message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith));
829
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG);
830
-					throw new \Exception($message_t);
831
-				}
832
-			}
833
-			// Check if the item source is already shared with the group, either from the same owner or a different user
834
-			// The check for each user in the group is done inside the put() function
835
-			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
836
-				null, self::FORMAT_NONE, null, 1, true, true)) {
837
-
838
-				if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
839
-					$message = 'Sharing %s failed, because this item is already shared with %s';
840
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
841
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
842
-					throw new \Exception($message_t);
843
-				}
844
-			}
845
-			// Convert share with into an array with the keys group and users
846
-			$group = $shareWith;
847
-			$shareWith = array();
848
-			$shareWith['group'] = $group;
849
-
850
-
851
-			$groupObject = \OC::$server->getGroupManager()->get($group);
852
-			$userIds = [];
853
-			if ($groupObject) {
854
-				$users = $groupObject->searchUsers('', -1, 0);
855
-				foreach ($users as $user) {
856
-					$userIds[] = $user->getUID();
857
-				}
858
-			}
859
-
860
-			$shareWith['users'] = array_diff($userIds, array($uidOwner));
861
-		} else if ($shareType === self::SHARE_TYPE_LINK) {
862
-			$updateExistingShare = false;
863
-			if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
864
-
865
-				// IF the password is changed via the old ajax endpoint verify it before deleting the old share
866
-				if ($passwordChanged === true) {
867
-					self::verifyPassword($shareWith);
868
-				}
869
-
870
-				// when updating a link share
871
-				// FIXME Don't delete link if we update it
872
-				if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
873
-					$uidOwner, self::FORMAT_NONE, null, 1)) {
874
-					// remember old token
875
-					$oldToken = $checkExists['token'];
876
-					$oldPermissions = $checkExists['permissions'];
877
-					//delete the old share
878
-					Helper::delete($checkExists['id']);
879
-					$updateExistingShare = true;
880
-				}
881
-
882
-				if ($passwordChanged === null) {
883
-					// Generate hash of password - same method as user passwords
884
-					if (is_string($shareWith) && $shareWith !== '') {
885
-						self::verifyPassword($shareWith);
886
-						$shareWith = \OC::$server->getHasher()->hash($shareWith);
887
-					} else {
888
-						// reuse the already set password, but only if we change permissions
889
-						// otherwise the user disabled the password protection
890
-						if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
891
-							$shareWith = $checkExists['share_with'];
892
-						}
893
-					}
894
-				} else {
895
-					if ($passwordChanged === true) {
896
-						if (is_string($shareWith) && $shareWith !== '') {
897
-							self::verifyPassword($shareWith);
898
-							$shareWith = \OC::$server->getHasher()->hash($shareWith);
899
-						}
900
-					} else if ($updateExistingShare) {
901
-						$shareWith = $checkExists['share_with'];
902
-					}
903
-				}
904
-
905
-				if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) {
906
-					$message = 'You need to provide a password to create a public link, only protected links are allowed';
907
-					$message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed');
908
-					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
909
-					throw new \Exception($message_t);
910
-				}
911
-
912
-				if ($updateExistingShare === false &&
913
-					self::isDefaultExpireDateEnabled() &&
914
-					empty($expirationDate)) {
915
-					$expirationDate = Helper::calcExpireDate();
916
-				}
917
-
918
-				// Generate token
919
-				if (isset($oldToken)) {
920
-					$token = $oldToken;
921
-				} else {
922
-					$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH,
923
-						\OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_UPPER.
924
-						\OCP\Security\ISecureRandom::CHAR_DIGITS
925
-					);
926
-				}
927
-				$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
928
-					null, $token, $itemSourceName, $expirationDate);
929
-				if ($result) {
930
-					return $token;
931
-				} else {
932
-					return false;
933
-				}
934
-			}
935
-			$message = 'Sharing %s failed, because sharing with links is not allowed';
936
-			$message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', array($itemSourceName));
937
-			\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
938
-			throw new \Exception($message_t);
939
-		} else if ($shareType === self::SHARE_TYPE_REMOTE) {
940
-
941
-			/*
748
+                $expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource);
749
+            } catch (\Exception $e) {
750
+                throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404);
751
+            }
752
+        }
753
+
754
+        // Verify share type and sharing conditions are met
755
+        if ($shareType === self::SHARE_TYPE_USER) {
756
+            if ($shareWith == $uidOwner) {
757
+                $message = 'Sharing %s failed, because you can not share with yourself';
758
+                $message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
759
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
760
+                throw new \Exception($message_t);
761
+            }
762
+            if (!\OC_User::userExists($shareWith)) {
763
+                $message = 'Sharing %s failed, because the user %s does not exist';
764
+                $message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith));
765
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
766
+                throw new \Exception($message_t);
767
+            }
768
+            if ($shareWithinGroupOnly) {
769
+                $userManager = \OC::$server->getUserManager();
770
+                $groupManager = \OC::$server->getGroupManager();
771
+                $userOwner = $userManager->get($uidOwner);
772
+                $userShareWith = $userManager->get($shareWith);
773
+                $groupsOwner = [];
774
+                $groupsShareWith = [];
775
+                if ($userOwner) {
776
+                    $groupsOwner = $groupManager->getUserGroupIds($userOwner);
777
+                }
778
+                if ($userShareWith) {
779
+                    $groupsShareWith = $groupManager->getUserGroupIds($userShareWith);
780
+                }
781
+                $inGroup = array_intersect($groupsOwner, $groupsShareWith);
782
+                if (empty($inGroup)) {
783
+                    $message = 'Sharing %s failed, because the user '
784
+                        .'%s is not a member of any groups that %s is a member of';
785
+                    $message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', array($itemName, $shareWith, $uidOwner));
786
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG);
787
+                    throw new \Exception($message_t);
788
+                }
789
+            }
790
+            // Check if the item source is already shared with the user, either from the same owner or a different user
791
+            if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
792
+                $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
793
+                // Only allow the same share to occur again if it is the same
794
+                // owner and is not a user share, this use case is for increasing
795
+                // permissions for a specific user
796
+                if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
797
+                    $message = 'Sharing %s failed, because this item is already shared with %s';
798
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
799
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
800
+                    throw new \Exception($message_t);
801
+                }
802
+            }
803
+            if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
804
+                $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
805
+                // Only allow the same share to occur again if it is the same
806
+                // owner and is not a user share, this use case is for increasing
807
+                // permissions for a specific user
808
+                if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
809
+                    $message = 'Sharing %s failed, because this item is already shared with user %s';
810
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', array($itemSourceName, $shareWith));
811
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
812
+                    throw new \Exception($message_t);
813
+                }
814
+            }
815
+        } else if ($shareType === self::SHARE_TYPE_GROUP) {
816
+            if (!\OC::$server->getGroupManager()->groupExists($shareWith)) {
817
+                $message = 'Sharing %s failed, because the group %s does not exist';
818
+                $message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith));
819
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
820
+                throw new \Exception($message_t);
821
+            }
822
+            if ($shareWithinGroupOnly && !\OC_Group::inGroup($uidOwner, $shareWith)) {
823
+                $group = \OC::$server->getGroupManager()->get($shareWith);
824
+                $user = \OC::$server->getUserManager()->get($uidOwner);
825
+                if (!$group || !$user || !$group->inGroup($user)) {
826
+                    $message = 'Sharing %s failed, because '
827
+                        . '%s is not a member of the group %s';
828
+                    $message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith));
829
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG);
830
+                    throw new \Exception($message_t);
831
+                }
832
+            }
833
+            // Check if the item source is already shared with the group, either from the same owner or a different user
834
+            // The check for each user in the group is done inside the put() function
835
+            if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
836
+                null, self::FORMAT_NONE, null, 1, true, true)) {
837
+
838
+                if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
839
+                    $message = 'Sharing %s failed, because this item is already shared with %s';
840
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
841
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
842
+                    throw new \Exception($message_t);
843
+                }
844
+            }
845
+            // Convert share with into an array with the keys group and users
846
+            $group = $shareWith;
847
+            $shareWith = array();
848
+            $shareWith['group'] = $group;
849
+
850
+
851
+            $groupObject = \OC::$server->getGroupManager()->get($group);
852
+            $userIds = [];
853
+            if ($groupObject) {
854
+                $users = $groupObject->searchUsers('', -1, 0);
855
+                foreach ($users as $user) {
856
+                    $userIds[] = $user->getUID();
857
+                }
858
+            }
859
+
860
+            $shareWith['users'] = array_diff($userIds, array($uidOwner));
861
+        } else if ($shareType === self::SHARE_TYPE_LINK) {
862
+            $updateExistingShare = false;
863
+            if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
864
+
865
+                // IF the password is changed via the old ajax endpoint verify it before deleting the old share
866
+                if ($passwordChanged === true) {
867
+                    self::verifyPassword($shareWith);
868
+                }
869
+
870
+                // when updating a link share
871
+                // FIXME Don't delete link if we update it
872
+                if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
873
+                    $uidOwner, self::FORMAT_NONE, null, 1)) {
874
+                    // remember old token
875
+                    $oldToken = $checkExists['token'];
876
+                    $oldPermissions = $checkExists['permissions'];
877
+                    //delete the old share
878
+                    Helper::delete($checkExists['id']);
879
+                    $updateExistingShare = true;
880
+                }
881
+
882
+                if ($passwordChanged === null) {
883
+                    // Generate hash of password - same method as user passwords
884
+                    if (is_string($shareWith) && $shareWith !== '') {
885
+                        self::verifyPassword($shareWith);
886
+                        $shareWith = \OC::$server->getHasher()->hash($shareWith);
887
+                    } else {
888
+                        // reuse the already set password, but only if we change permissions
889
+                        // otherwise the user disabled the password protection
890
+                        if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
891
+                            $shareWith = $checkExists['share_with'];
892
+                        }
893
+                    }
894
+                } else {
895
+                    if ($passwordChanged === true) {
896
+                        if (is_string($shareWith) && $shareWith !== '') {
897
+                            self::verifyPassword($shareWith);
898
+                            $shareWith = \OC::$server->getHasher()->hash($shareWith);
899
+                        }
900
+                    } else if ($updateExistingShare) {
901
+                        $shareWith = $checkExists['share_with'];
902
+                    }
903
+                }
904
+
905
+                if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) {
906
+                    $message = 'You need to provide a password to create a public link, only protected links are allowed';
907
+                    $message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed');
908
+                    \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
909
+                    throw new \Exception($message_t);
910
+                }
911
+
912
+                if ($updateExistingShare === false &&
913
+                    self::isDefaultExpireDateEnabled() &&
914
+                    empty($expirationDate)) {
915
+                    $expirationDate = Helper::calcExpireDate();
916
+                }
917
+
918
+                // Generate token
919
+                if (isset($oldToken)) {
920
+                    $token = $oldToken;
921
+                } else {
922
+                    $token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH,
923
+                        \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_UPPER.
924
+                        \OCP\Security\ISecureRandom::CHAR_DIGITS
925
+                    );
926
+                }
927
+                $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
928
+                    null, $token, $itemSourceName, $expirationDate);
929
+                if ($result) {
930
+                    return $token;
931
+                } else {
932
+                    return false;
933
+                }
934
+            }
935
+            $message = 'Sharing %s failed, because sharing with links is not allowed';
936
+            $message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', array($itemSourceName));
937
+            \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
938
+            throw new \Exception($message_t);
939
+        } else if ($shareType === self::SHARE_TYPE_REMOTE) {
940
+
941
+            /*
942 942
 			 * Check if file is not already shared with the remote user
943 943
 			 */
944
-			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE,
945
-				$shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) {
946
-					$message = 'Sharing %s failed, because this item is already shared with %s';
947
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
948
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
949
-					throw new \Exception($message_t);
950
-			}
951
-
952
-			// don't allow federated shares if source and target server are the same
953
-			list($user, $remote) = Helper::splitUserRemote($shareWith);
954
-			$currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
955
-			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
956
-			if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) {
957
-				$message = 'Not allowed to create a federated share with the same user.';
958
-				$message_t = $l->t('Not allowed to create a federated share with the same user');
959
-				\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
960
-				throw new \Exception($message_t);
961
-			}
962
-
963
-			$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
964
-				\OCP\Security\ISecureRandom::CHAR_DIGITS);
965
-
966
-			$shareWith = $user . '@' . $remote;
967
-			$shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
968
-
969
-			$send = false;
970
-			if ($shareId) {
971
-				$send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner);
972
-			}
973
-
974
-			if ($send === false) {
975
-				$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
976
-				self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser);
977
-				$message_t = $l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', array($itemSourceName, $shareWith));
978
-				throw new \Exception($message_t);
979
-			}
980
-
981
-			return $send;
982
-		} else {
983
-			// Future share types need to include their own conditions
984
-			$message = 'Share type %s is not valid for %s';
985
-			$message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource));
986
-			\OCP\Util::writeLog('OCP\Share', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG);
987
-			throw new \Exception($message_t);
988
-		}
989
-
990
-		// Put the item into the database
991
-		$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate);
992
-
993
-		return $result ? true : false;
994
-	}
995
-
996
-	/**
997
-	 * Unshare an item from a user, group, or delete a private link
998
-	 * @param string $itemType
999
-	 * @param string $itemSource
1000
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1001
-	 * @param string $shareWith User or group the item is being shared with
1002
-	 * @param string $owner owner of the share, if null the current user is used
1003
-	 * @return boolean true on success or false on failure
1004
-	 */
1005
-	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
1006
-
1007
-		// check if it is a valid itemType
1008
-		self::getBackend($itemType);
1009
-
1010
-		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
1011
-
1012
-		$toDelete = array();
1013
-		$newParent = null;
1014
-		$currentUser = $owner ? $owner : \OC_User::getUser();
1015
-		foreach ($items as $item) {
1016
-			// delete the item with the expected share_type and owner
1017
-			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
1018
-				$toDelete = $item;
1019
-				// if there is more then one result we don't have to delete the children
1020
-				// but update their parent. For group shares the new parent should always be
1021
-				// the original group share and not the db entry with the unique name
1022
-			} else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
1023
-				$newParent = $item['parent'];
1024
-			} else {
1025
-				$newParent = $item['id'];
1026
-			}
1027
-		}
1028
-
1029
-		if (!empty($toDelete)) {
1030
-			self::unshareItem($toDelete, $newParent);
1031
-			return true;
1032
-		}
1033
-		return false;
1034
-	}
1035
-
1036
-	/**
1037
-	 * Unshare an item from all users, groups, and remove all links
1038
-	 * @param string $itemType
1039
-	 * @param string $itemSource
1040
-	 * @return boolean true on success or false on failure
1041
-	 */
1042
-	public static function unshareAll($itemType, $itemSource) {
1043
-		// Get all of the owners of shares of this item.
1044
-		$query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
1045
-		$result = $query->execute(array($itemType, $itemSource));
1046
-		$shares = array();
1047
-		// Add each owner's shares to the array of all shares for this item.
1048
-		while ($row = $result->fetchRow()) {
1049
-			$shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
1050
-		}
1051
-		if (!empty($shares)) {
1052
-			// Pass all the vars we have for now, they may be useful
1053
-			$hookParams = array(
1054
-				'itemType' => $itemType,
1055
-				'itemSource' => $itemSource,
1056
-				'shares' => $shares,
1057
-			);
1058
-			\OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
1059
-			foreach ($shares as $share) {
1060
-				self::unshareItem($share);
1061
-			}
1062
-			\OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
1063
-			return true;
1064
-		}
1065
-		return false;
1066
-	}
1067
-
1068
-	/**
1069
-	 * Unshare an item shared with the current user
1070
-	 * @param string $itemType
1071
-	 * @param string $itemOrigin Item target or source
1072
-	 * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional)
1073
-	 * @return boolean true on success or false on failure
1074
-	 *
1075
-	 * Unsharing from self is not allowed for items inside collections
1076
-	 */
1077
-	public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) {
1078
-		$originType = ($originIsSource) ? 'source' : 'target';
1079
-		$uid = \OCP\User::getUser();
1080
-
1081
-		if ($itemType === 'file' || $itemType === 'folder') {
1082
-			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
1083
-		} else {
1084
-			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
1085
-		}
1086
-
1087
-		$query = \OCP\DB::prepare($statement);
1088
-		$result = $query->execute(array($itemType, $itemOrigin));
1089
-
1090
-		$shares = $result->fetchAll();
1091
-
1092
-		$listOfUnsharedItems = array();
1093
-
1094
-		$itemUnshared = false;
1095
-		foreach ($shares as $share) {
1096
-			if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1097
-				$share['share_with'] === $uid) {
1098
-				$deletedShares = Helper::delete($share['id']);
1099
-				$shareTmp = array(
1100
-					'id' => $share['id'],
1101
-					'shareWith' => $share['share_with'],
1102
-					'itemTarget' => $share['item_target'],
1103
-					'itemType' => $share['item_type'],
1104
-					'shareType' => (int)$share['share_type'],
1105
-				);
1106
-				if (isset($share['file_target'])) {
1107
-					$shareTmp['fileTarget'] = $share['file_target'];
1108
-				}
1109
-				$listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp));
1110
-				$itemUnshared = true;
1111
-				break;
1112
-			} elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1113
-				$group = \OC::$server->getGroupManager()->get($share['share_with']);
1114
-				$user = \OC::$server->getUserManager()->get($uid);
1115
-				if ($group && $user && $group->inGroup($user)) {
1116
-					$groupShare = $share;
1117
-				}
1118
-			} elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
1119
-				$share['share_with'] === $uid) {
1120
-				$uniqueGroupShare = $share;
1121
-			}
1122
-		}
1123
-
1124
-		if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) {
1125
-			$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
1126
-				.' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
1127
-				.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
1128
-				.' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
1129
-			$query->execute(array($groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'],
1130
-				$groupShare['id'], self::$shareTypeGroupUserUnique,
1131
-				\OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'],
1132
-				$groupShare['file_target']));
1133
-			$shareTmp = array(
1134
-				'id' => $groupShare['id'],
1135
-				'shareWith' => $groupShare['share_with'],
1136
-				'itemTarget' => $groupShare['item_target'],
1137
-				'itemType' => $groupShare['item_type'],
1138
-				'shareType' => (int)$groupShare['share_type'],
1139
-			);
1140
-			if (isset($groupShare['file_target'])) {
1141
-				$shareTmp['fileTarget'] = $groupShare['file_target'];
1142
-			}
1143
-			$listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1144
-			$itemUnshared = true;
1145
-		} elseif (!$itemUnshared && isset($uniqueGroupShare)) {
1146
-			$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
1147
-			$query->execute(array(0, $uniqueGroupShare['id']));
1148
-			$shareTmp = array(
1149
-				'id' => $uniqueGroupShare['id'],
1150
-				'shareWith' => $uniqueGroupShare['share_with'],
1151
-				'itemTarget' => $uniqueGroupShare['item_target'],
1152
-				'itemType' => $uniqueGroupShare['item_type'],
1153
-				'shareType' => (int)$uniqueGroupShare['share_type'],
1154
-			);
1155
-			if (isset($uniqueGroupShare['file_target'])) {
1156
-				$shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
1157
-			}
1158
-			$listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1159
-			$itemUnshared = true;
1160
-		}
1161
-
1162
-		if ($itemUnshared) {
1163
-			\OC_Hook::emit('OCP\Share', 'post_unshareFromSelf',
1164
-				array('unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType));
1165
-		}
1166
-
1167
-		return $itemUnshared;
1168
-	}
1169
-
1170
-	/**
1171
-	 * sent status if users got informed by mail about share
1172
-	 * @param string $itemType
1173
-	 * @param string $itemSource
1174
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1175
-	 * @param string $recipient with whom was the file shared
1176
-	 * @param boolean $status
1177
-	 */
1178
-	public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
1179
-		$status = $status ? 1 : 0;
1180
-
1181
-		$query = \OC_DB::prepare(
1182
-			'UPDATE `*PREFIX*share`
944
+            if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE,
945
+                $shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) {
946
+                    $message = 'Sharing %s failed, because this item is already shared with %s';
947
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
948
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
949
+                    throw new \Exception($message_t);
950
+            }
951
+
952
+            // don't allow federated shares if source and target server are the same
953
+            list($user, $remote) = Helper::splitUserRemote($shareWith);
954
+            $currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
955
+            $currentUser = \OC::$server->getUserSession()->getUser()->getUID();
956
+            if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) {
957
+                $message = 'Not allowed to create a federated share with the same user.';
958
+                $message_t = $l->t('Not allowed to create a federated share with the same user');
959
+                \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
960
+                throw new \Exception($message_t);
961
+            }
962
+
963
+            $token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
964
+                \OCP\Security\ISecureRandom::CHAR_DIGITS);
965
+
966
+            $shareWith = $user . '@' . $remote;
967
+            $shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
968
+
969
+            $send = false;
970
+            if ($shareId) {
971
+                $send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner);
972
+            }
973
+
974
+            if ($send === false) {
975
+                $currentUser = \OC::$server->getUserSession()->getUser()->getUID();
976
+                self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser);
977
+                $message_t = $l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', array($itemSourceName, $shareWith));
978
+                throw new \Exception($message_t);
979
+            }
980
+
981
+            return $send;
982
+        } else {
983
+            // Future share types need to include their own conditions
984
+            $message = 'Share type %s is not valid for %s';
985
+            $message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource));
986
+            \OCP\Util::writeLog('OCP\Share', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG);
987
+            throw new \Exception($message_t);
988
+        }
989
+
990
+        // Put the item into the database
991
+        $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate);
992
+
993
+        return $result ? true : false;
994
+    }
995
+
996
+    /**
997
+     * Unshare an item from a user, group, or delete a private link
998
+     * @param string $itemType
999
+     * @param string $itemSource
1000
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1001
+     * @param string $shareWith User or group the item is being shared with
1002
+     * @param string $owner owner of the share, if null the current user is used
1003
+     * @return boolean true on success or false on failure
1004
+     */
1005
+    public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
1006
+
1007
+        // check if it is a valid itemType
1008
+        self::getBackend($itemType);
1009
+
1010
+        $items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
1011
+
1012
+        $toDelete = array();
1013
+        $newParent = null;
1014
+        $currentUser = $owner ? $owner : \OC_User::getUser();
1015
+        foreach ($items as $item) {
1016
+            // delete the item with the expected share_type and owner
1017
+            if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
1018
+                $toDelete = $item;
1019
+                // if there is more then one result we don't have to delete the children
1020
+                // but update their parent. For group shares the new parent should always be
1021
+                // the original group share and not the db entry with the unique name
1022
+            } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
1023
+                $newParent = $item['parent'];
1024
+            } else {
1025
+                $newParent = $item['id'];
1026
+            }
1027
+        }
1028
+
1029
+        if (!empty($toDelete)) {
1030
+            self::unshareItem($toDelete, $newParent);
1031
+            return true;
1032
+        }
1033
+        return false;
1034
+    }
1035
+
1036
+    /**
1037
+     * Unshare an item from all users, groups, and remove all links
1038
+     * @param string $itemType
1039
+     * @param string $itemSource
1040
+     * @return boolean true on success or false on failure
1041
+     */
1042
+    public static function unshareAll($itemType, $itemSource) {
1043
+        // Get all of the owners of shares of this item.
1044
+        $query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
1045
+        $result = $query->execute(array($itemType, $itemSource));
1046
+        $shares = array();
1047
+        // Add each owner's shares to the array of all shares for this item.
1048
+        while ($row = $result->fetchRow()) {
1049
+            $shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
1050
+        }
1051
+        if (!empty($shares)) {
1052
+            // Pass all the vars we have for now, they may be useful
1053
+            $hookParams = array(
1054
+                'itemType' => $itemType,
1055
+                'itemSource' => $itemSource,
1056
+                'shares' => $shares,
1057
+            );
1058
+            \OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
1059
+            foreach ($shares as $share) {
1060
+                self::unshareItem($share);
1061
+            }
1062
+            \OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
1063
+            return true;
1064
+        }
1065
+        return false;
1066
+    }
1067
+
1068
+    /**
1069
+     * Unshare an item shared with the current user
1070
+     * @param string $itemType
1071
+     * @param string $itemOrigin Item target or source
1072
+     * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional)
1073
+     * @return boolean true on success or false on failure
1074
+     *
1075
+     * Unsharing from self is not allowed for items inside collections
1076
+     */
1077
+    public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) {
1078
+        $originType = ($originIsSource) ? 'source' : 'target';
1079
+        $uid = \OCP\User::getUser();
1080
+
1081
+        if ($itemType === 'file' || $itemType === 'folder') {
1082
+            $statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
1083
+        } else {
1084
+            $statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
1085
+        }
1086
+
1087
+        $query = \OCP\DB::prepare($statement);
1088
+        $result = $query->execute(array($itemType, $itemOrigin));
1089
+
1090
+        $shares = $result->fetchAll();
1091
+
1092
+        $listOfUnsharedItems = array();
1093
+
1094
+        $itemUnshared = false;
1095
+        foreach ($shares as $share) {
1096
+            if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1097
+                $share['share_with'] === $uid) {
1098
+                $deletedShares = Helper::delete($share['id']);
1099
+                $shareTmp = array(
1100
+                    'id' => $share['id'],
1101
+                    'shareWith' => $share['share_with'],
1102
+                    'itemTarget' => $share['item_target'],
1103
+                    'itemType' => $share['item_type'],
1104
+                    'shareType' => (int)$share['share_type'],
1105
+                );
1106
+                if (isset($share['file_target'])) {
1107
+                    $shareTmp['fileTarget'] = $share['file_target'];
1108
+                }
1109
+                $listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp));
1110
+                $itemUnshared = true;
1111
+                break;
1112
+            } elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1113
+                $group = \OC::$server->getGroupManager()->get($share['share_with']);
1114
+                $user = \OC::$server->getUserManager()->get($uid);
1115
+                if ($group && $user && $group->inGroup($user)) {
1116
+                    $groupShare = $share;
1117
+                }
1118
+            } elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
1119
+                $share['share_with'] === $uid) {
1120
+                $uniqueGroupShare = $share;
1121
+            }
1122
+        }
1123
+
1124
+        if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) {
1125
+            $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
1126
+                .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
1127
+                .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
1128
+                .' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
1129
+            $query->execute(array($groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'],
1130
+                $groupShare['id'], self::$shareTypeGroupUserUnique,
1131
+                \OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'],
1132
+                $groupShare['file_target']));
1133
+            $shareTmp = array(
1134
+                'id' => $groupShare['id'],
1135
+                'shareWith' => $groupShare['share_with'],
1136
+                'itemTarget' => $groupShare['item_target'],
1137
+                'itemType' => $groupShare['item_type'],
1138
+                'shareType' => (int)$groupShare['share_type'],
1139
+            );
1140
+            if (isset($groupShare['file_target'])) {
1141
+                $shareTmp['fileTarget'] = $groupShare['file_target'];
1142
+            }
1143
+            $listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1144
+            $itemUnshared = true;
1145
+        } elseif (!$itemUnshared && isset($uniqueGroupShare)) {
1146
+            $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
1147
+            $query->execute(array(0, $uniqueGroupShare['id']));
1148
+            $shareTmp = array(
1149
+                'id' => $uniqueGroupShare['id'],
1150
+                'shareWith' => $uniqueGroupShare['share_with'],
1151
+                'itemTarget' => $uniqueGroupShare['item_target'],
1152
+                'itemType' => $uniqueGroupShare['item_type'],
1153
+                'shareType' => (int)$uniqueGroupShare['share_type'],
1154
+            );
1155
+            if (isset($uniqueGroupShare['file_target'])) {
1156
+                $shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
1157
+            }
1158
+            $listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1159
+            $itemUnshared = true;
1160
+        }
1161
+
1162
+        if ($itemUnshared) {
1163
+            \OC_Hook::emit('OCP\Share', 'post_unshareFromSelf',
1164
+                array('unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType));
1165
+        }
1166
+
1167
+        return $itemUnshared;
1168
+    }
1169
+
1170
+    /**
1171
+     * sent status if users got informed by mail about share
1172
+     * @param string $itemType
1173
+     * @param string $itemSource
1174
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1175
+     * @param string $recipient with whom was the file shared
1176
+     * @param boolean $status
1177
+     */
1178
+    public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
1179
+        $status = $status ? 1 : 0;
1180
+
1181
+        $query = \OC_DB::prepare(
1182
+            'UPDATE `*PREFIX*share`
1183 1183
 					SET `mail_send` = ?
1184 1184
 					WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ? AND `share_with` = ?');
1185 1185
 
1186
-		$result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient));
1187
-
1188
-		if($result === false) {
1189
-			\OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR);
1190
-		}
1191
-	}
1192
-
1193
-	/**
1194
-	 * Set the permissions of an item for a specific user or group
1195
-	 * @param string $itemType
1196
-	 * @param string $itemSource
1197
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1198
-	 * @param string $shareWith User or group the item is being shared with
1199
-	 * @param int $permissions CRUDS permissions
1200
-	 * @return boolean true on success or false on failure
1201
-	 * @throws \Exception when trying to grant more permissions then the user has himself
1202
-	 */
1203
-	public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
1204
-		$l = \OC::$server->getL10N('lib');
1205
-		$connection = \OC::$server->getDatabaseConnection();
1206
-
1207
-		$intArrayToLiteralArray = function($intArray, $eb) {
1208
-			return array_map(function($int) use ($eb) {
1209
-				return $eb->literal((int)$int, 'integer');
1210
-			}, $intArray);
1211
-		};
1212
-		$sanitizeItem = function($item) {
1213
-			$item['id'] = (int)$item['id'];
1214
-			$item['premissions'] = (int)$item['permissions'];
1215
-			return $item;
1216
-		};
1217
-
1218
-		if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith,
1219
-			\OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
1220
-			// Check if this item is a reshare and verify that the permissions
1221
-			// granted don't exceed the parent shared item
1222
-			if (isset($rootItem['parent'])) {
1223
-				$qb = $connection->getQueryBuilder();
1224
-				$qb->select('permissions')
1225
-					->from('share')
1226
-					->where($qb->expr()->eq('id', $qb->createParameter('id')))
1227
-					->setParameter(':id', $rootItem['parent']);
1228
-				$dbresult = $qb->execute();
1229
-
1230
-				$result = $dbresult->fetch();
1231
-				$dbresult->closeCursor();
1232
-				if (~(int)$result['permissions'] & $permissions) {
1233
-					$message = 'Setting permissions for %s failed,'
1234
-						.' because the permissions exceed permissions granted to %s';
1235
-					$message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
1236
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG);
1237
-					throw new \Exception($message_t);
1238
-				}
1239
-			}
1240
-			$qb = $connection->getQueryBuilder();
1241
-			$qb->update('share')
1242
-				->set('permissions', $qb->createParameter('permissions'))
1243
-				->where($qb->expr()->eq('id', $qb->createParameter('id')))
1244
-				->setParameter(':id', $rootItem['id'])
1245
-				->setParameter(':permissions', $permissions);
1246
-			$qb->execute();
1247
-			if ($itemType === 'file' || $itemType === 'folder') {
1248
-				\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
1249
-					'itemType' => $itemType,
1250
-					'itemSource' => $itemSource,
1251
-					'shareType' => $shareType,
1252
-					'shareWith' => $shareWith,
1253
-					'uidOwner' => \OC_User::getUser(),
1254
-					'permissions' => $permissions,
1255
-					'path' => $rootItem['path'],
1256
-					'share' => $rootItem
1257
-				));
1258
-			}
1259
-
1260
-			// Share id's to update with the new permissions
1261
-			$ids = [];
1262
-			$items = [];
1263
-
1264
-			// Check if permissions were removed
1265
-			if ((int)$rootItem['permissions'] & ~$permissions) {
1266
-				// If share permission is removed all reshares must be deleted
1267
-				if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1268
-					// delete all shares, keep parent and group children
1269
-					Helper::delete($rootItem['id'], true, null, null, true);
1270
-				}
1271
-
1272
-				// Remove permission from all children
1273
-				$parents = [$rootItem['id']];
1274
-				while (!empty($parents)) {
1275
-					$parents = $intArrayToLiteralArray($parents, $qb->expr());
1276
-					$qb = $connection->getQueryBuilder();
1277
-					$qb->select('id', 'permissions', 'item_type')
1278
-						->from('share')
1279
-						->where($qb->expr()->in('parent', $parents));
1280
-					$result = $qb->execute();
1281
-					// Reset parents array, only go through loop again if
1282
-					// items are found that need permissions removed
1283
-					$parents = [];
1284
-					while ($item = $result->fetch()) {
1285
-						$item = $sanitizeItem($item);
1286
-
1287
-						$items[] = $item;
1288
-						// Check if permissions need to be removed
1289
-						if ($item['permissions'] & ~$permissions) {
1290
-							// Add to list of items that need permissions removed
1291
-							$ids[] = $item['id'];
1292
-							$parents[] = $item['id'];
1293
-						}
1294
-					}
1295
-					$result->closeCursor();
1296
-				}
1297
-
1298
-				// Remove the permissions for all reshares of this item
1299
-				if (!empty($ids)) {
1300
-					$ids = "'".implode("','", $ids)."'";
1301
-					// TODO this should be done with Doctrine platform objects
1302
-					if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') {
1303
-						$andOp = 'BITAND(`permissions`, ?)';
1304
-					} else {
1305
-						$andOp = '`permissions` & ?';
1306
-					}
1307
-					$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
1308
-						.' WHERE `id` IN ('.$ids.')');
1309
-					$query->execute(array($permissions));
1310
-				}
1311
-
1312
-			}
1313
-
1314
-			/*
1186
+        $result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient));
1187
+
1188
+        if($result === false) {
1189
+            \OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR);
1190
+        }
1191
+    }
1192
+
1193
+    /**
1194
+     * Set the permissions of an item for a specific user or group
1195
+     * @param string $itemType
1196
+     * @param string $itemSource
1197
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1198
+     * @param string $shareWith User or group the item is being shared with
1199
+     * @param int $permissions CRUDS permissions
1200
+     * @return boolean true on success or false on failure
1201
+     * @throws \Exception when trying to grant more permissions then the user has himself
1202
+     */
1203
+    public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
1204
+        $l = \OC::$server->getL10N('lib');
1205
+        $connection = \OC::$server->getDatabaseConnection();
1206
+
1207
+        $intArrayToLiteralArray = function($intArray, $eb) {
1208
+            return array_map(function($int) use ($eb) {
1209
+                return $eb->literal((int)$int, 'integer');
1210
+            }, $intArray);
1211
+        };
1212
+        $sanitizeItem = function($item) {
1213
+            $item['id'] = (int)$item['id'];
1214
+            $item['premissions'] = (int)$item['permissions'];
1215
+            return $item;
1216
+        };
1217
+
1218
+        if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith,
1219
+            \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
1220
+            // Check if this item is a reshare and verify that the permissions
1221
+            // granted don't exceed the parent shared item
1222
+            if (isset($rootItem['parent'])) {
1223
+                $qb = $connection->getQueryBuilder();
1224
+                $qb->select('permissions')
1225
+                    ->from('share')
1226
+                    ->where($qb->expr()->eq('id', $qb->createParameter('id')))
1227
+                    ->setParameter(':id', $rootItem['parent']);
1228
+                $dbresult = $qb->execute();
1229
+
1230
+                $result = $dbresult->fetch();
1231
+                $dbresult->closeCursor();
1232
+                if (~(int)$result['permissions'] & $permissions) {
1233
+                    $message = 'Setting permissions for %s failed,'
1234
+                        .' because the permissions exceed permissions granted to %s';
1235
+                    $message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
1236
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG);
1237
+                    throw new \Exception($message_t);
1238
+                }
1239
+            }
1240
+            $qb = $connection->getQueryBuilder();
1241
+            $qb->update('share')
1242
+                ->set('permissions', $qb->createParameter('permissions'))
1243
+                ->where($qb->expr()->eq('id', $qb->createParameter('id')))
1244
+                ->setParameter(':id', $rootItem['id'])
1245
+                ->setParameter(':permissions', $permissions);
1246
+            $qb->execute();
1247
+            if ($itemType === 'file' || $itemType === 'folder') {
1248
+                \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
1249
+                    'itemType' => $itemType,
1250
+                    'itemSource' => $itemSource,
1251
+                    'shareType' => $shareType,
1252
+                    'shareWith' => $shareWith,
1253
+                    'uidOwner' => \OC_User::getUser(),
1254
+                    'permissions' => $permissions,
1255
+                    'path' => $rootItem['path'],
1256
+                    'share' => $rootItem
1257
+                ));
1258
+            }
1259
+
1260
+            // Share id's to update with the new permissions
1261
+            $ids = [];
1262
+            $items = [];
1263
+
1264
+            // Check if permissions were removed
1265
+            if ((int)$rootItem['permissions'] & ~$permissions) {
1266
+                // If share permission is removed all reshares must be deleted
1267
+                if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1268
+                    // delete all shares, keep parent and group children
1269
+                    Helper::delete($rootItem['id'], true, null, null, true);
1270
+                }
1271
+
1272
+                // Remove permission from all children
1273
+                $parents = [$rootItem['id']];
1274
+                while (!empty($parents)) {
1275
+                    $parents = $intArrayToLiteralArray($parents, $qb->expr());
1276
+                    $qb = $connection->getQueryBuilder();
1277
+                    $qb->select('id', 'permissions', 'item_type')
1278
+                        ->from('share')
1279
+                        ->where($qb->expr()->in('parent', $parents));
1280
+                    $result = $qb->execute();
1281
+                    // Reset parents array, only go through loop again if
1282
+                    // items are found that need permissions removed
1283
+                    $parents = [];
1284
+                    while ($item = $result->fetch()) {
1285
+                        $item = $sanitizeItem($item);
1286
+
1287
+                        $items[] = $item;
1288
+                        // Check if permissions need to be removed
1289
+                        if ($item['permissions'] & ~$permissions) {
1290
+                            // Add to list of items that need permissions removed
1291
+                            $ids[] = $item['id'];
1292
+                            $parents[] = $item['id'];
1293
+                        }
1294
+                    }
1295
+                    $result->closeCursor();
1296
+                }
1297
+
1298
+                // Remove the permissions for all reshares of this item
1299
+                if (!empty($ids)) {
1300
+                    $ids = "'".implode("','", $ids)."'";
1301
+                    // TODO this should be done with Doctrine platform objects
1302
+                    if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') {
1303
+                        $andOp = 'BITAND(`permissions`, ?)';
1304
+                    } else {
1305
+                        $andOp = '`permissions` & ?';
1306
+                    }
1307
+                    $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
1308
+                        .' WHERE `id` IN ('.$ids.')');
1309
+                    $query->execute(array($permissions));
1310
+                }
1311
+
1312
+            }
1313
+
1314
+            /*
1315 1315
 			 * Permissions were added
1316 1316
 			 * Update all USERGROUP shares. (So group shares where the user moved their mountpoint).
1317 1317
 			 */
1318
-			if ($permissions & ~(int)$rootItem['permissions']) {
1319
-				$qb = $connection->getQueryBuilder();
1320
-				$qb->select('id', 'permissions', 'item_type')
1321
-					->from('share')
1322
-					->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
1323
-					->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
1324
-					->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
1325
-					->setParameter(':parent', (int)$rootItem['id'])
1326
-					->setParameter(':share_type', 2)
1327
-					->setParameter(':shareDeleted', 0);
1328
-				$result = $qb->execute();
1329
-
1330
-				$ids = [];
1331
-				while ($item = $result->fetch()) {
1332
-					$item = $sanitizeItem($item);
1333
-					$items[] = $item;
1334
-					$ids[] = $item['id'];
1335
-				}
1336
-				$result->closeCursor();
1337
-
1338
-				// Add permssions for all USERGROUP shares of this item
1339
-				if (!empty($ids)) {
1340
-					$ids = $intArrayToLiteralArray($ids, $qb->expr());
1341
-
1342
-					$qb = $connection->getQueryBuilder();
1343
-					$qb->update('share')
1344
-						->set('permissions', $qb->createParameter('permissions'))
1345
-						->where($qb->expr()->in('id', $ids))
1346
-						->setParameter(':permissions', $permissions);
1347
-					$qb->execute();
1348
-				}
1349
-			}
1350
-
1351
-			foreach ($items as $item) {
1352
-				\OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]);
1353
-			}
1354
-
1355
-			return true;
1356
-		}
1357
-		$message = 'Setting permissions for %s failed, because the item was not found';
1358
-		$message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource));
1359
-
1360
-		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
1361
-		throw new \Exception($message_t);
1362
-	}
1363
-
1364
-	/**
1365
-	 * validate expiration date if it meets all constraints
1366
-	 *
1367
-	 * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY"
1368
-	 * @param string $shareTime timestamp when the file was shared
1369
-	 * @param string $itemType
1370
-	 * @param string $itemSource
1371
-	 * @return \DateTime validated date
1372
-	 * @throws \Exception when the expire date is in the past or further in the future then the enforced date
1373
-	 */
1374
-	private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
1375
-		$l = \OC::$server->getL10N('lib');
1376
-		$date = new \DateTime($expireDate);
1377
-		$today = new \DateTime('now');
1378
-
1379
-		// if the user doesn't provide a share time we need to get it from the database
1380
-		// fall-back mode to keep API stable, because the $shareTime parameter was added later
1381
-		$defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
1382
-		if ($defaultExpireDateEnforced && $shareTime === null) {
1383
-			$items = self::getItemShared($itemType, $itemSource);
1384
-			$firstItem = reset($items);
1385
-			$shareTime = (int)$firstItem['stime'];
1386
-		}
1387
-
1388
-		if ($defaultExpireDateEnforced) {
1389
-			// initialize max date with share time
1390
-			$maxDate = new \DateTime();
1391
-			$maxDate->setTimestamp($shareTime);
1392
-			$maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1393
-			$maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1394
-			if ($date > $maxDate) {
1395
-				$warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1396
-				$warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
1397
-				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1398
-				throw new \Exception($warning_t);
1399
-			}
1400
-		}
1401
-
1402
-		if ($date < $today) {
1403
-			$message = 'Cannot set expiration date. Expiration date is in the past';
1404
-			$message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
1405
-			\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN);
1406
-			throw new \Exception($message_t);
1407
-		}
1408
-
1409
-		return $date;
1410
-	}
1411
-
1412
-	/**
1413
-	 * Set expiration date for a share
1414
-	 * @param string $itemType
1415
-	 * @param string $itemSource
1416
-	 * @param string $date expiration date
1417
-	 * @param int $shareTime timestamp from when the file was shared
1418
-	 * @return boolean
1419
-	 * @throws \Exception when the expire date is not set, in the past or further in the future then the enforced date
1420
-	 */
1421
-	public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) {
1422
-		$user = \OC_User::getUser();
1423
-		$l = \OC::$server->getL10N('lib');
1424
-
1425
-		if ($date == '') {
1426
-			if (\OCP\Util::isDefaultExpireDateEnforced()) {
1427
-				$warning = 'Cannot clear expiration date. Shares are required to have an expiration date.';
1428
-				$warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.');
1429
-				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1430
-				throw new \Exception($warning_t);
1431
-			} else {
1432
-				$date = null;
1433
-			}
1434
-		} else {
1435
-			$date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource);
1436
-		}
1437
-		$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ?  AND `uid_owner` = ? AND `share_type` = ?');
1438
-		$query->bindValue(1, $date, 'datetime');
1439
-		$query->bindValue(2, $itemType);
1440
-		$query->bindValue(3, $itemSource);
1441
-		$query->bindValue(4, $user);
1442
-		$query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK);
1443
-
1444
-		$query->execute();
1445
-
1446
-		\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array(
1447
-			'itemType' => $itemType,
1448
-			'itemSource' => $itemSource,
1449
-			'date' => $date,
1450
-			'uidOwner' => $user
1451
-		));
1452
-
1453
-		return true;
1454
-	}
1455
-
1456
-	/**
1457
-	 * Retrieve the owner of a connection
1458
-	 *
1459
-	 * @param IDBConnection $connection
1460
-	 * @param int $shareId
1461
-	 * @throws \Exception
1462
-	 * @return string uid of share owner
1463
-	 */
1464
-	private static function getShareOwner(IDBConnection $connection, $shareId) {
1465
-		$qb = $connection->getQueryBuilder();
1466
-
1467
-		$qb->select('uid_owner')
1468
-			->from('share')
1469
-			->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1470
-			->setParameter(':shareId', $shareId);
1471
-		$result = $qb->execute();
1472
-		$result = $result->fetch();
1473
-
1474
-		if (empty($result)) {
1475
-			throw new \Exception('Share not found');
1476
-		}
1477
-
1478
-		return $result['uid_owner'];
1479
-	}
1480
-
1481
-	/**
1482
-	 * Set password for a public link share
1483
-	 *
1484
-	 * @param IUserSession $userSession
1485
-	 * @param IDBConnection $connection
1486
-	 * @param IConfig $config
1487
-	 * @param int $shareId
1488
-	 * @param string $password
1489
-	 * @throws \Exception
1490
-	 * @return boolean
1491
-	 */
1492
-	public static function setPassword(IUserSession $userSession,
1493
-	                                   IDBConnection $connection,
1494
-	                                   IConfig $config,
1495
-	                                   $shareId, $password) {
1496
-		$user = $userSession->getUser();
1497
-		if (is_null($user)) {
1498
-			throw new \Exception("User not logged in");
1499
-		}
1500
-
1501
-		$uid = self::getShareOwner($connection, $shareId);
1502
-
1503
-		if ($uid !== $user->getUID()) {
1504
-			throw new \Exception('Cannot update share of a different user');
1505
-		}
1506
-
1507
-		if ($password === '') {
1508
-			$password = null;
1509
-		}
1510
-
1511
-		//If passwords are enforced the password can't be null
1512
-		if (self::enforcePassword($config) && is_null($password)) {
1513
-			throw new \Exception('Cannot remove password');
1514
-		}
1515
-
1516
-		self::verifyPassword($password);
1517
-
1518
-		$qb = $connection->getQueryBuilder();
1519
-		$qb->update('share')
1520
-			->set('share_with', $qb->createParameter('pass'))
1521
-			->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1522
-			->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
1523
-			->setParameter(':shareId', $shareId);
1524
-
1525
-		$qb->execute();
1526
-
1527
-		return true;
1528
-	}
1529
-
1530
-	/**
1531
-	 * Checks whether a share has expired, calls unshareItem() if yes.
1532
-	 * @param array $item Share data (usually database row)
1533
-	 * @return boolean True if item was expired, false otherwise.
1534
-	 */
1535
-	protected static function expireItem(array $item) {
1536
-
1537
-		$result = false;
1538
-
1539
-		// only use default expiration date for link shares
1540
-		if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
1541
-
1542
-			// calculate expiration date
1543
-			if (!empty($item['expiration'])) {
1544
-				$userDefinedExpire = new \DateTime($item['expiration']);
1545
-				$expires = $userDefinedExpire->getTimestamp();
1546
-			} else {
1547
-				$expires = null;
1548
-			}
1549
-
1550
-
1551
-			// get default expiration settings
1552
-			$defaultSettings = Helper::getDefaultExpireSetting();
1553
-			$expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
1554
-
1555
-
1556
-			if (is_int($expires)) {
1557
-				$now = time();
1558
-				if ($now > $expires) {
1559
-					self::unshareItem($item);
1560
-					$result = true;
1561
-				}
1562
-			}
1563
-		}
1564
-		return $result;
1565
-	}
1566
-
1567
-	/**
1568
-	 * Unshares a share given a share data array
1569
-	 * @param array $item Share data (usually database row)
1570
-	 * @param int $newParent parent ID
1571
-	 * @return null
1572
-	 */
1573
-	protected static function unshareItem(array $item, $newParent = null) {
1574
-
1575
-		$shareType = (int)$item['share_type'];
1576
-		$shareWith = null;
1577
-		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
1578
-			$shareWith = $item['share_with'];
1579
-		}
1580
-
1581
-		// Pass all the vars we have for now, they may be useful
1582
-		$hookParams = array(
1583
-			'id'            => $item['id'],
1584
-			'itemType'      => $item['item_type'],
1585
-			'itemSource'    => $item['item_source'],
1586
-			'shareType'     => $shareType,
1587
-			'shareWith'     => $shareWith,
1588
-			'itemParent'    => $item['parent'],
1589
-			'uidOwner'      => $item['uid_owner'],
1590
-		);
1591
-		if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1592
-			$hookParams['fileSource'] = $item['file_source'];
1593
-			$hookParams['fileTarget'] = $item['file_target'];
1594
-		}
1595
-
1596
-		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
1597
-		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
1598
-		$deletedShares[] = $hookParams;
1599
-		$hookParams['deletedShares'] = $deletedShares;
1600
-		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1601
-		if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1602
-			list(, $remote) = Helper::splitUserRemote($item['share_with']);
1603
-			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
1604
-		}
1605
-	}
1606
-
1607
-	/**
1608
-	 * Get the backend class for the specified item type
1609
-	 * @param string $itemType
1610
-	 * @throws \Exception
1611
-	 * @return \OCP\Share_Backend
1612
-	 */
1613
-	public static function getBackend($itemType) {
1614
-		$l = \OC::$server->getL10N('lib');
1615
-		if (isset(self::$backends[$itemType])) {
1616
-			return self::$backends[$itemType];
1617
-		} else if (isset(self::$backendTypes[$itemType]['class'])) {
1618
-			$class = self::$backendTypes[$itemType]['class'];
1619
-			if (class_exists($class)) {
1620
-				self::$backends[$itemType] = new $class;
1621
-				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
1622
-					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
1623
-					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class));
1624
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1625
-					throw new \Exception($message_t);
1626
-				}
1627
-				return self::$backends[$itemType];
1628
-			} else {
1629
-				$message = 'Sharing backend %s not found';
1630
-				$message_t = $l->t('Sharing backend %s not found', array($class));
1631
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1632
-				throw new \Exception($message_t);
1633
-			}
1634
-		}
1635
-		$message = 'Sharing backend for %s not found';
1636
-		$message_t = $l->t('Sharing backend for %s not found', array($itemType));
1637
-		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), \OCP\Util::ERROR);
1638
-		throw new \Exception($message_t);
1639
-	}
1640
-
1641
-	/**
1642
-	 * Check if resharing is allowed
1643
-	 * @return boolean true if allowed or false
1644
-	 *
1645
-	 * Resharing is allowed by default if not configured
1646
-	 */
1647
-	public static function isResharingAllowed() {
1648
-		if (!isset(self::$isResharingAllowed)) {
1649
-			if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
1650
-				self::$isResharingAllowed = true;
1651
-			} else {
1652
-				self::$isResharingAllowed = false;
1653
-			}
1654
-		}
1655
-		return self::$isResharingAllowed;
1656
-	}
1657
-
1658
-	/**
1659
-	 * Get a list of collection item types for the specified item type
1660
-	 * @param string $itemType
1661
-	 * @return array
1662
-	 */
1663
-	private static function getCollectionItemTypes($itemType) {
1664
-		$collectionTypes = array($itemType);
1665
-		foreach (self::$backendTypes as $type => $backend) {
1666
-			if (in_array($backend['collectionOf'], $collectionTypes)) {
1667
-				$collectionTypes[] = $type;
1668
-			}
1669
-		}
1670
-		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
1671
-		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
1672
-			unset($collectionTypes[0]);
1673
-		}
1674
-		// Return array if collections were found or the item type is a
1675
-		// collection itself - collections can be inside collections
1676
-		if (count($collectionTypes) > 0) {
1677
-			return $collectionTypes;
1678
-		}
1679
-		return false;
1680
-	}
1681
-
1682
-	/**
1683
-	 * Get the owners of items shared with a user.
1684
-	 *
1685
-	 * @param string $user The user the items are shared with.
1686
-	 * @param string $type The type of the items shared with the user.
1687
-	 * @param boolean $includeCollections Include collection item types (optional)
1688
-	 * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
1689
-	 * @return array
1690
-	 */
1691
-	public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
1692
-		// First, we find out if $type is part of a collection (and if that collection is part of
1693
-		// another one and so on).
1694
-		$collectionTypes = array();
1695
-		if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
1696
-			$collectionTypes[] = $type;
1697
-		}
1698
-
1699
-		// Of these collection types, along with our original $type, we make a
1700
-		// list of the ones for which a sharing backend has been registered.
1701
-		// FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
1702
-		// with its $includeCollections parameter set to true. Unfortunately, this fails currently.
1703
-		$allMaybeSharedItems = array();
1704
-		foreach ($collectionTypes as $collectionType) {
1705
-			if (isset(self::$backends[$collectionType])) {
1706
-				$allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
1707
-					$collectionType,
1708
-					$user,
1709
-					self::FORMAT_NONE
1710
-				);
1711
-			}
1712
-		}
1713
-
1714
-		$owners = array();
1715
-		if ($includeOwner) {
1716
-			$owners[] = $user;
1717
-		}
1718
-
1719
-		// We take a look at all shared items of the given $type (or of the collections it is part of)
1720
-		// and find out their owners. Then, we gather the tags for the original $type from all owners,
1721
-		// and return them as elements of a list that look like "Tag (owner)".
1722
-		foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
1723
-			foreach ($maybeSharedItems as $sharedItem) {
1724
-				if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
1725
-					$owners[] = $sharedItem['uid_owner'];
1726
-				}
1727
-			}
1728
-		}
1729
-
1730
-		return $owners;
1731
-	}
1732
-
1733
-	/**
1734
-	 * Get shared items from the database
1735
-	 * @param string $itemType
1736
-	 * @param string $item Item source or target (optional)
1737
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
1738
-	 * @param string $shareWith User or group the item is being shared with
1739
-	 * @param string $uidOwner User that is the owner of shared items (optional)
1740
-	 * @param int $format Format to convert items to with formatItems() (optional)
1741
-	 * @param mixed $parameters to pass to formatItems() (optional)
1742
-	 * @param int $limit Number of items to return, -1 to return all matches (optional)
1743
-	 * @param boolean $includeCollections Include collection item types (optional)
1744
-	 * @param boolean $itemShareWithBySource (optional)
1745
-	 * @param boolean $checkExpireDate
1746
-	 * @return array
1747
-	 *
1748
-	 * See public functions getItem(s)... for parameter usage
1749
-	 *
1750
-	 */
1751
-	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
1752
-									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
1753
-									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
1754
-		if (!self::isEnabled()) {
1755
-			return array();
1756
-		}
1757
-		$backend = self::getBackend($itemType);
1758
-		$collectionTypes = false;
1759
-		// Get filesystem root to add it to the file target and remove from the
1760
-		// file source, match file_source with the file cache
1761
-		if ($itemType == 'file' || $itemType == 'folder') {
1762
-			if(!is_null($uidOwner)) {
1763
-				$root = \OC\Files\Filesystem::getRoot();
1764
-			} else {
1765
-				$root = '';
1766
-			}
1767
-			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
1768
-			if (!isset($item)) {
1769
-				$where .= ' AND `file_target` IS NOT NULL ';
1770
-			}
1771
-			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
1772
-			$fileDependent = true;
1773
-			$queryArgs = array();
1774
-		} else {
1775
-			$fileDependent = false;
1776
-			$root = '';
1777
-			$collectionTypes = self::getCollectionItemTypes($itemType);
1778
-			if ($includeCollections && !isset($item) && $collectionTypes) {
1779
-				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
1780
-				if (!in_array($itemType, $collectionTypes)) {
1781
-					$itemTypes = array_merge(array($itemType), $collectionTypes);
1782
-				} else {
1783
-					$itemTypes = $collectionTypes;
1784
-				}
1785
-				$placeholders = join(',', array_fill(0, count($itemTypes), '?'));
1786
-				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
1787
-				$queryArgs = $itemTypes;
1788
-			} else {
1789
-				$where = ' WHERE `item_type` = ?';
1790
-				$queryArgs = array($itemType);
1791
-			}
1792
-		}
1793
-		if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1794
-			$where .= ' AND `share_type` != ?';
1795
-			$queryArgs[] = self::SHARE_TYPE_LINK;
1796
-		}
1797
-		if (isset($shareType)) {
1798
-			// Include all user and group items
1799
-			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
1800
-				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
1801
-				$queryArgs[] = self::SHARE_TYPE_USER;
1802
-				$queryArgs[] = self::$shareTypeGroupUserUnique;
1803
-				$queryArgs[] = $shareWith;
1804
-
1805
-				$user = \OC::$server->getUserManager()->get($shareWith);
1806
-				$groups = [];
1807
-				if ($user) {
1808
-					$groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
1809
-				}
1810
-				if (!empty($groups)) {
1811
-					$placeholders = join(',', array_fill(0, count($groups), '?'));
1812
-					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
1813
-					$queryArgs[] = self::SHARE_TYPE_GROUP;
1814
-					$queryArgs = array_merge($queryArgs, $groups);
1815
-				}
1816
-				$where .= ')';
1817
-				// Don't include own group shares
1818
-				$where .= ' AND `uid_owner` != ?';
1819
-				$queryArgs[] = $shareWith;
1820
-			} else {
1821
-				$where .= ' AND `share_type` = ?';
1822
-				$queryArgs[] = $shareType;
1823
-				if (isset($shareWith)) {
1824
-					$where .= ' AND `share_with` = ?';
1825
-					$queryArgs[] = $shareWith;
1826
-				}
1827
-			}
1828
-		}
1829
-		if (isset($uidOwner)) {
1830
-			$where .= ' AND `uid_owner` = ?';
1831
-			$queryArgs[] = $uidOwner;
1832
-			if (!isset($shareType)) {
1833
-				// Prevent unique user targets for group shares from being selected
1834
-				$where .= ' AND `share_type` != ?';
1835
-				$queryArgs[] = self::$shareTypeGroupUserUnique;
1836
-			}
1837
-			if ($fileDependent) {
1838
-				$column = 'file_source';
1839
-			} else {
1840
-				$column = 'item_source';
1841
-			}
1842
-		} else {
1843
-			if ($fileDependent) {
1844
-				$column = 'file_target';
1845
-			} else {
1846
-				$column = 'item_target';
1847
-			}
1848
-		}
1849
-		if (isset($item)) {
1850
-			$collectionTypes = self::getCollectionItemTypes($itemType);
1851
-			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1852
-				$where .= ' AND (';
1853
-			} else {
1854
-				$where .= ' AND';
1855
-			}
1856
-			// If looking for own shared items, check item_source else check item_target
1857
-			if (isset($uidOwner) || $itemShareWithBySource) {
1858
-				// If item type is a file, file source needs to be checked in case the item was converted
1859
-				if ($fileDependent) {
1860
-					$where .= ' `file_source` = ?';
1861
-					$column = 'file_source';
1862
-				} else {
1863
-					$where .= ' `item_source` = ?';
1864
-					$column = 'item_source';
1865
-				}
1866
-			} else {
1867
-				if ($fileDependent) {
1868
-					$where .= ' `file_target` = ?';
1869
-					$item = \OC\Files\Filesystem::normalizePath($item);
1870
-				} else {
1871
-					$where .= ' `item_target` = ?';
1872
-				}
1873
-			}
1874
-			$queryArgs[] = $item;
1875
-			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1876
-				$placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
1877
-				$where .= ' OR `item_type` IN ('.$placeholders.'))';
1878
-				$queryArgs = array_merge($queryArgs, $collectionTypes);
1879
-			}
1880
-		}
1881
-
1882
-		if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
1883
-			// Make sure the unique user target is returned if it exists,
1884
-			// unique targets should follow the group share in the database
1885
-			// If the limit is not 1, the filtering can be done later
1886
-			$where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
1887
-		} else {
1888
-			$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
1889
-		}
1890
-
1891
-		if ($limit != -1 && !$includeCollections) {
1892
-			// The limit must be at least 3, because filtering needs to be done
1893
-			if ($limit < 3) {
1894
-				$queryLimit = 3;
1895
-			} else {
1896
-				$queryLimit = $limit;
1897
-			}
1898
-		} else {
1899
-			$queryLimit = null;
1900
-		}
1901
-		$select = self::createSelectStatement($format, $fileDependent, $uidOwner);
1902
-		$root = strlen($root);
1903
-		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
1904
-		$result = $query->execute($queryArgs);
1905
-		if ($result === false) {
1906
-			\OCP\Util::writeLog('OCP\Share',
1907
-				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
1908
-				\OCP\Util::ERROR);
1909
-		}
1910
-		$items = array();
1911
-		$targets = array();
1912
-		$switchedItems = array();
1913
-		$mounts = array();
1914
-		while ($row = $result->fetchRow()) {
1915
-			self::transformDBResults($row);
1916
-			// Filter out duplicate group shares for users with unique targets
1917
-			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
1918
-				continue;
1919
-			}
1920
-			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
1921
-				$row['share_type'] = self::SHARE_TYPE_GROUP;
1922
-				$row['unique_name'] = true; // remember that we use a unique name for this user
1923
-				$row['share_with'] = $items[$row['parent']]['share_with'];
1924
-				// if the group share was unshared from the user we keep the permission, otherwise
1925
-				// we take the permission from the parent because this is always the up-to-date
1926
-				// permission for the group share
1927
-				if ($row['permissions'] > 0) {
1928
-					$row['permissions'] = $items[$row['parent']]['permissions'];
1929
-				}
1930
-				// Remove the parent group share
1931
-				unset($items[$row['parent']]);
1932
-				if ($row['permissions'] == 0) {
1933
-					continue;
1934
-				}
1935
-			} else if (!isset($uidOwner)) {
1936
-				// Check if the same target already exists
1937
-				if (isset($targets[$row['id']])) {
1938
-					// Check if the same owner shared with the user twice
1939
-					// through a group and user share - this is allowed
1940
-					$id = $targets[$row['id']];
1941
-					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
1942
-						// Switch to group share type to ensure resharing conditions aren't bypassed
1943
-						if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
1944
-							$items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
1945
-							$items[$id]['share_with'] = $row['share_with'];
1946
-						}
1947
-						// Switch ids if sharing permission is granted on only
1948
-						// one share to ensure correct parent is used if resharing
1949
-						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1950
-							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1951
-							$items[$row['id']] = $items[$id];
1952
-							$switchedItems[$id] = $row['id'];
1953
-							unset($items[$id]);
1954
-							$id = $row['id'];
1955
-						}
1956
-						$items[$id]['permissions'] |= (int)$row['permissions'];
1957
-
1958
-					}
1959
-					continue;
1960
-				} elseif (!empty($row['parent'])) {
1961
-					$targets[$row['parent']] = $row['id'];
1962
-				}
1963
-			}
1964
-			// Remove root from file source paths if retrieving own shared items
1965
-			if (isset($uidOwner) && isset($row['path'])) {
1966
-				if (isset($row['parent'])) {
1967
-					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1968
-					$parentResult = $query->execute(array($row['parent']));
1969
-					if ($result === false) {
1970
-						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
1971
-							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
1972
-							\OCP\Util::ERROR);
1973
-					} else {
1974
-						$parentRow = $parentResult->fetchRow();
1975
-						$tmpPath = $parentRow['file_target'];
1976
-						// find the right position where the row path continues from the target path
1977
-						$pos = strrpos($row['path'], $parentRow['file_target']);
1978
-						$subPath = substr($row['path'], $pos);
1979
-						$splitPath = explode('/', $subPath);
1980
-						foreach (array_slice($splitPath, 2) as $pathPart) {
1981
-							$tmpPath = $tmpPath . '/' . $pathPart;
1982
-						}
1983
-						$row['path'] = $tmpPath;
1984
-					}
1985
-				} else {
1986
-					if (!isset($mounts[$row['storage']])) {
1987
-						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
1988
-						if (is_array($mountPoints) && !empty($mountPoints)) {
1989
-							$mounts[$row['storage']] = current($mountPoints);
1990
-						}
1991
-					}
1992
-					if (!empty($mounts[$row['storage']])) {
1993
-						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
1994
-						$relPath = substr($path, $root); // path relative to data/user
1995
-						$row['path'] = rtrim($relPath, '/');
1996
-					}
1997
-				}
1998
-			}
1999
-
2000
-			if($checkExpireDate) {
2001
-				if (self::expireItem($row)) {
2002
-					continue;
2003
-				}
2004
-			}
2005
-			// Check if resharing is allowed, if not remove share permission
2006
-			if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
2007
-				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
2008
-			}
2009
-			// Add display names to result
2010
-			$row['share_with_displayname'] = $row['share_with'];
2011
-			if ( isset($row['share_with']) && $row['share_with'] != '' &&
2012
-				$row['share_type'] === self::SHARE_TYPE_USER) {
2013
-				$row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
2014
-			} else if(isset($row['share_with']) && $row['share_with'] != '' &&
2015
-				$row['share_type'] === self::SHARE_TYPE_REMOTE) {
2016
-				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
2017
-				foreach ($addressBookEntries as $entry) {
2018
-					foreach ($entry['CLOUD'] as $cloudID) {
2019
-						if ($cloudID === $row['share_with']) {
2020
-							$row['share_with_displayname'] = $entry['FN'];
2021
-						}
2022
-					}
2023
-				}
2024
-			}
2025
-			if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
2026
-				$row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
2027
-			}
2028
-
2029
-			if ($row['permissions'] > 0) {
2030
-				$items[$row['id']] = $row;
2031
-			}
2032
-
2033
-		}
2034
-
2035
-		// group items if we are looking for items shared with the current user
2036
-		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
2037
-			$items = self::groupItems($items, $itemType);
2038
-		}
2039
-
2040
-		if (!empty($items)) {
2041
-			$collectionItems = array();
2042
-			foreach ($items as &$row) {
2043
-				// Return only the item instead of a 2-dimensional array
2044
-				if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
2045
-					if ($format == self::FORMAT_NONE) {
2046
-						return $row;
2047
-					} else {
2048
-						break;
2049
-					}
2050
-				}
2051
-				// Check if this is a collection of the requested item type
2052
-				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
2053
-					if (($collectionBackend = self::getBackend($row['item_type']))
2054
-						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
2055
-						// Collections can be inside collections, check if the item is a collection
2056
-						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
2057
-							$collectionItems[] = $row;
2058
-						} else {
2059
-							$collection = array();
2060
-							$collection['item_type'] = $row['item_type'];
2061
-							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2062
-								$collection['path'] = basename($row['path']);
2063
-							}
2064
-							$row['collection'] = $collection;
2065
-							// Fetch all of the children sources
2066
-							$children = $collectionBackend->getChildren($row[$column]);
2067
-							foreach ($children as $child) {
2068
-								$childItem = $row;
2069
-								$childItem['item_type'] = $itemType;
2070
-								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
2071
-									$childItem['item_source'] = $child['source'];
2072
-									$childItem['item_target'] = $child['target'];
2073
-								}
2074
-								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2075
-									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2076
-										$childItem['file_source'] = $child['source'];
2077
-									} else { // TODO is this really needed if we already know that we use the file backend?
2078
-										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
2079
-										$childItem['file_source'] = $meta['fileid'];
2080
-									}
2081
-									$childItem['file_target'] =
2082
-										\OC\Files\Filesystem::normalizePath($child['file_path']);
2083
-								}
2084
-								if (isset($item)) {
2085
-									if ($childItem[$column] == $item) {
2086
-										// Return only the item instead of a 2-dimensional array
2087
-										if ($limit == 1) {
2088
-											if ($format == self::FORMAT_NONE) {
2089
-												return $childItem;
2090
-											} else {
2091
-												// Unset the items array and break out of both loops
2092
-												$items = array();
2093
-												$items[] = $childItem;
2094
-												break 2;
2095
-											}
2096
-										} else {
2097
-											$collectionItems[] = $childItem;
2098
-										}
2099
-									}
2100
-								} else {
2101
-									$collectionItems[] = $childItem;
2102
-								}
2103
-							}
2104
-						}
2105
-					}
2106
-					// Remove collection item
2107
-					$toRemove = $row['id'];
2108
-					if (array_key_exists($toRemove, $switchedItems)) {
2109
-						$toRemove = $switchedItems[$toRemove];
2110
-					}
2111
-					unset($items[$toRemove]);
2112
-				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
2113
-					// FIXME: Thats a dirty hack to improve file sharing performance,
2114
-					// see github issue #10588 for more details
2115
-					// Need to find a solution which works for all back-ends
2116
-					$collectionBackend = self::getBackend($row['item_type']);
2117
-					$sharedParents = $collectionBackend->getParents($row['item_source']);
2118
-					foreach ($sharedParents as $parent) {
2119
-						$collectionItems[] = $parent;
2120
-					}
2121
-				}
2122
-			}
2123
-			if (!empty($collectionItems)) {
2124
-				$collectionItems = array_unique($collectionItems, SORT_REGULAR);
2125
-				$items = array_merge($items, $collectionItems);
2126
-			}
2127
-
2128
-			// filter out invalid items, these can appear when subshare entries exist
2129
-			// for a group in which the requested user isn't a member any more
2130
-			$items = array_filter($items, function($item) {
2131
-				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
2132
-			});
2133
-
2134
-			return self::formatResult($items, $column, $backend, $format, $parameters);
2135
-		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
2136
-			// FIXME: Thats a dirty hack to improve file sharing performance,
2137
-			// see github issue #10588 for more details
2138
-			// Need to find a solution which works for all back-ends
2139
-			$collectionItems = array();
2140
-			$collectionBackend = self::getBackend('folder');
2141
-			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
2142
-			foreach ($sharedParents as $parent) {
2143
-				$collectionItems[] = $parent;
2144
-			}
2145
-			if ($limit === 1) {
2146
-				return reset($collectionItems);
2147
-			}
2148
-			return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
2149
-		}
2150
-
2151
-		return array();
2152
-	}
2153
-
2154
-	/**
2155
-	 * group items with link to the same source
2156
-	 *
2157
-	 * @param array $items
2158
-	 * @param string $itemType
2159
-	 * @return array of grouped items
2160
-	 */
2161
-	protected static function groupItems($items, $itemType) {
2162
-
2163
-		$fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false;
2164
-
2165
-		$result = array();
2166
-
2167
-		foreach ($items as $item) {
2168
-			$grouped = false;
2169
-			foreach ($result as $key => $r) {
2170
-				// for file/folder shares we need to compare file_source, otherwise we compare item_source
2171
-				// only group shares if they already point to the same target, otherwise the file where shared
2172
-				// before grouping of shares was added. In this case we don't group them toi avoid confusions
2173
-				if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2174
-					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
2175
-					// add the first item to the list of grouped shares
2176
-					if (!isset($result[$key]['grouped'])) {
2177
-						$result[$key]['grouped'][] = $result[$key];
2178
-					}
2179
-					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
2180
-					$result[$key]['grouped'][] = $item;
2181
-					$grouped = true;
2182
-					break;
2183
-				}
2184
-			}
2185
-
2186
-			if (!$grouped) {
2187
-				$result[] = $item;
2188
-			}
2189
-
2190
-		}
2191
-
2192
-		return $result;
2193
-	}
2194
-
2195
-	/**
2196
-	 * Put shared item into the database
2197
-	 * @param string $itemType Item type
2198
-	 * @param string $itemSource Item source
2199
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
2200
-	 * @param string $shareWith User or group the item is being shared with
2201
-	 * @param string $uidOwner User that is the owner of shared item
2202
-	 * @param int $permissions CRUDS permissions
2203
-	 * @param boolean|array $parentFolder Parent folder target (optional)
2204
-	 * @param string $token (optional)
2205
-	 * @param string $itemSourceName name of the source item (optional)
2206
-	 * @param \DateTime $expirationDate (optional)
2207
-	 * @throws \Exception
2208
-	 * @return mixed id of the new share or false
2209
-	 */
2210
-	private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2211
-								$permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
2212
-
2213
-		$queriesToExecute = array();
2214
-		$suggestedItemTarget = null;
2215
-		$groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
2216
-		$groupItemTarget = $itemTarget = $fileSource = $parent = 0;
2217
-
2218
-		$result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
2219
-		if(!empty($result)) {
2220
-			$parent = $result['parent'];
2221
-			$itemSource = $result['itemSource'];
2222
-			$fileSource = $result['fileSource'];
2223
-			$suggestedItemTarget = $result['suggestedItemTarget'];
2224
-			$suggestedFileTarget = $result['suggestedFileTarget'];
2225
-			$filePath = $result['filePath'];
2226
-		}
2227
-
2228
-		$isGroupShare = false;
2229
-		if ($shareType == self::SHARE_TYPE_GROUP) {
2230
-			$isGroupShare = true;
2231
-			if (isset($shareWith['users'])) {
2232
-				$users = $shareWith['users'];
2233
-			} else {
2234
-				$group = \OC::$server->getGroupManager()->get($shareWith['group']);
2235
-				if ($group) {
2236
-					$users = $group->searchUsers('', -1, 0);
2237
-					$userIds = [];
2238
-					foreach ($users as $user) {
2239
-						$userIds[] = $user->getUID();
2240
-					}
2241
-					$users = $userIds;
2242
-				} else {
2243
-					$users = [];
2244
-				}
2245
-			}
2246
-			// remove current user from list
2247
-			if (in_array(\OCP\User::getUser(), $users)) {
2248
-				unset($users[array_search(\OCP\User::getUser(), $users)]);
2249
-			}
2250
-			$groupItemTarget = Helper::generateTarget($itemType, $itemSource,
2251
-				$shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
2252
-			$groupFileTarget = Helper::generateTarget($itemType, $itemSource,
2253
-				$shareType, $shareWith['group'], $uidOwner, $filePath);
2254
-
2255
-			// add group share to table and remember the id as parent
2256
-			$queriesToExecute['groupShare'] = array(
2257
-				'itemType'			=> $itemType,
2258
-				'itemSource'		=> $itemSource,
2259
-				'itemTarget'		=> $groupItemTarget,
2260
-				'shareType'			=> $shareType,
2261
-				'shareWith'			=> $shareWith['group'],
2262
-				'uidOwner'			=> $uidOwner,
2263
-				'permissions'		=> $permissions,
2264
-				'shareTime'			=> time(),
2265
-				'fileSource'		=> $fileSource,
2266
-				'fileTarget'		=> $groupFileTarget,
2267
-				'token'				=> $token,
2268
-				'parent'			=> $parent,
2269
-				'expiration'		=> $expirationDate,
2270
-			);
2271
-
2272
-		} else {
2273
-			$users = array($shareWith);
2274
-			$itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2275
-				$suggestedItemTarget);
2276
-		}
2277
-
2278
-		$run = true;
2279
-		$error = '';
2280
-		$preHookData = array(
2281
-			'itemType' => $itemType,
2282
-			'itemSource' => $itemSource,
2283
-			'shareType' => $shareType,
2284
-			'uidOwner' => $uidOwner,
2285
-			'permissions' => $permissions,
2286
-			'fileSource' => $fileSource,
2287
-			'expiration' => $expirationDate,
2288
-			'token' => $token,
2289
-			'run' => &$run,
2290
-			'error' => &$error
2291
-		);
2292
-
2293
-		$preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2294
-		$preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2295
-
2296
-		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
2297
-
2298
-		if ($run === false) {
2299
-			throw new \Exception($error);
2300
-		}
2301
-
2302
-		foreach ($users as $user) {
2303
-			$sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
2304
-			$sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
2305
-
2306
-			$userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType;
2307
-
2308
-			if ($sourceExists && $sourceExists['item_source'] === $itemSource) {
2309
-				$fileTarget = $sourceExists['file_target'];
2310
-				$itemTarget = $sourceExists['item_target'];
2311
-
2312
-				// for group shares we don't need a additional entry if the target is the same
2313
-				if($isGroupShare && $groupItemTarget === $itemTarget) {
2314
-					continue;
2315
-				}
2316
-
2317
-			} elseif(!$sourceExists && !$isGroupShare)  {
2318
-
2319
-				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
2320
-					$uidOwner, $suggestedItemTarget, $parent);
2321
-				if (isset($fileSource)) {
2322
-					if ($parentFolder) {
2323
-						if ($parentFolder === true) {
2324
-							$fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
2325
-								$uidOwner, $suggestedFileTarget, $parent);
2326
-							if ($fileTarget != $groupFileTarget) {
2327
-								$parentFolders[$user]['folder'] = $fileTarget;
2328
-							}
2329
-						} else if (isset($parentFolder[$user])) {
2330
-							$fileTarget = $parentFolder[$user]['folder'].$itemSource;
2331
-							$parent = $parentFolder[$user]['id'];
2332
-						}
2333
-					} else {
2334
-						$fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
2335
-							$user, $uidOwner, $suggestedFileTarget, $parent);
2336
-					}
2337
-				} else {
2338
-					$fileTarget = null;
2339
-				}
2340
-
2341
-			} else {
2342
-
2343
-				// group share which doesn't exists until now, check if we need a unique target for this user
2344
-
2345
-				$itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
2346
-					$uidOwner, $suggestedItemTarget, $parent);
2347
-
2348
-				// do we also need a file target
2349
-				if (isset($fileSource)) {
2350
-					$fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
2351
-						$uidOwner, $suggestedFileTarget, $parent);
2352
-				} else {
2353
-					$fileTarget = null;
2354
-				}
2355
-
2356
-				if (($itemTarget === $groupItemTarget) &&
2357
-					(!isset($fileSource) || $fileTarget === $groupFileTarget)) {
2358
-					continue;
2359
-				}
2360
-			}
2361
-
2362
-			$queriesToExecute[] = array(
2363
-				'itemType'			=> $itemType,
2364
-				'itemSource'		=> $itemSource,
2365
-				'itemTarget'		=> $itemTarget,
2366
-				'shareType'			=> $userShareType,
2367
-				'shareWith'			=> $user,
2368
-				'uidOwner'			=> $uidOwner,
2369
-				'permissions'		=> $permissions,
2370
-				'shareTime'			=> time(),
2371
-				'fileSource'		=> $fileSource,
2372
-				'fileTarget'		=> $fileTarget,
2373
-				'token'				=> $token,
2374
-				'parent'			=> $parent,
2375
-				'expiration'		=> $expirationDate,
2376
-			);
2377
-
2378
-		}
2379
-
2380
-		$id = false;
2381
-		if ($isGroupShare) {
2382
-			$id = self::insertShare($queriesToExecute['groupShare']);
2383
-			// Save this id, any extra rows for this group share will need to reference it
2384
-			$parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2385
-			unset($queriesToExecute['groupShare']);
2386
-		}
2387
-
2388
-		foreach ($queriesToExecute as $shareQuery) {
2389
-			$shareQuery['parent'] = $parent;
2390
-			$id = self::insertShare($shareQuery);
2391
-		}
2392
-
2393
-		$postHookData = array(
2394
-			'itemType' => $itemType,
2395
-			'itemSource' => $itemSource,
2396
-			'parent' => $parent,
2397
-			'shareType' => $shareType,
2398
-			'uidOwner' => $uidOwner,
2399
-			'permissions' => $permissions,
2400
-			'fileSource' => $fileSource,
2401
-			'id' => $parent,
2402
-			'token' => $token,
2403
-			'expirationDate' => $expirationDate,
2404
-		);
2405
-
2406
-		$postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2407
-		$postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2408
-		$postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget;
2409
-
2410
-		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
2411
-
2412
-
2413
-		return $id ? $id : false;
2414
-	}
2415
-
2416
-	/**
2417
-	 * @param string $itemType
2418
-	 * @param string $itemSource
2419
-	 * @param int $shareType
2420
-	 * @param string $shareWith
2421
-	 * @param string $uidOwner
2422
-	 * @param int $permissions
2423
-	 * @param string|null $itemSourceName
2424
-	 * @param null|\DateTime $expirationDate
2425
-	 */
2426
-	private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
2427
-		$backend = self::getBackend($itemType);
2428
-
2429
-		$l = \OC::$server->getL10N('lib');
2430
-		$result = array();
2431
-
2432
-		$column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
2433
-
2434
-		$checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
2435
-		if ($checkReshare) {
2436
-			// Check if attempting to share back to owner
2437
-			if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
2438
-				$message = 'Sharing %s failed, because the user %s is the original sharer';
2439
-				$message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
2440
-
2441
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
2442
-				throw new \Exception($message_t);
2443
-			}
2444
-		}
2445
-
2446
-		if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
2447
-			// Check if share permissions is granted
2448
-			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2449
-				if (~(int)$checkReshare['permissions'] & $permissions) {
2450
-					$message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2451
-					$message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
2452
-
2453
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG);
2454
-					throw new \Exception($message_t);
2455
-				} else {
2456
-					// TODO Don't check if inside folder
2457
-					$result['parent'] = $checkReshare['id'];
2458
-
2459
-					$result['expirationDate'] = $expirationDate;
2460
-					// $checkReshare['expiration'] could be null and then is always less than any value
2461
-					if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2462
-						$result['expirationDate'] = $checkReshare['expiration'];
2463
-					}
2464
-
2465
-					// only suggest the same name as new target if it is a reshare of the
2466
-					// same file/folder and not the reshare of a child
2467
-					if ($checkReshare[$column] === $itemSource) {
2468
-						$result['filePath'] = $checkReshare['file_target'];
2469
-						$result['itemSource'] = $checkReshare['item_source'];
2470
-						$result['fileSource'] = $checkReshare['file_source'];
2471
-						$result['suggestedItemTarget'] = $checkReshare['item_target'];
2472
-						$result['suggestedFileTarget'] = $checkReshare['file_target'];
2473
-					} else {
2474
-						$result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
2475
-						$result['suggestedItemTarget'] = null;
2476
-						$result['suggestedFileTarget'] = null;
2477
-						$result['itemSource'] = $itemSource;
2478
-						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
2479
-					}
2480
-				}
2481
-			} else {
2482
-				$message = 'Sharing %s failed, because resharing is not allowed';
2483
-				$message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName));
2484
-
2485
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
2486
-				throw new \Exception($message_t);
2487
-			}
2488
-		} else {
2489
-			$result['parent'] = null;
2490
-			$result['suggestedItemTarget'] = null;
2491
-			$result['suggestedFileTarget'] = null;
2492
-			$result['itemSource'] = $itemSource;
2493
-			$result['expirationDate'] = $expirationDate;
2494
-			if (!$backend->isValidSource($itemSource, $uidOwner)) {
2495
-				$message = 'Sharing %s failed, because the sharing backend for '
2496
-					.'%s could not find its source';
2497
-				$message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType));
2498
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG);
2499
-				throw new \Exception($message_t);
2500
-			}
2501
-			if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2502
-				$result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
2503
-				if ($itemType == 'file' || $itemType == 'folder') {
2504
-					$result['fileSource'] = $itemSource;
2505
-				} else {
2506
-					$meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
2507
-					$result['fileSource'] = $meta['fileid'];
2508
-				}
2509
-				if ($result['fileSource'] == -1) {
2510
-					$message = 'Sharing %s failed, because the file could not be found in the file cache';
2511
-					$message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource));
2512
-
2513
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
2514
-					throw new \Exception($message_t);
2515
-				}
2516
-			} else {
2517
-				$result['filePath'] = null;
2518
-				$result['fileSource'] = null;
2519
-			}
2520
-		}
2521
-
2522
-		return $result;
2523
-	}
2524
-
2525
-	/**
2526
-	 *
2527
-	 * @param array $shareData
2528
-	 * @return mixed false in case of a failure or the id of the new share
2529
-	 */
2530
-	private static function insertShare(array $shareData) {
2531
-
2532
-		$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
2533
-			.' `item_type`, `item_source`, `item_target`, `share_type`,'
2534
-			.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
2535
-			.' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
2536
-		$query->bindValue(1, $shareData['itemType']);
2537
-		$query->bindValue(2, $shareData['itemSource']);
2538
-		$query->bindValue(3, $shareData['itemTarget']);
2539
-		$query->bindValue(4, $shareData['shareType']);
2540
-		$query->bindValue(5, $shareData['shareWith']);
2541
-		$query->bindValue(6, $shareData['uidOwner']);
2542
-		$query->bindValue(7, $shareData['permissions']);
2543
-		$query->bindValue(8, $shareData['shareTime']);
2544
-		$query->bindValue(9, $shareData['fileSource']);
2545
-		$query->bindValue(10, $shareData['fileTarget']);
2546
-		$query->bindValue(11, $shareData['token']);
2547
-		$query->bindValue(12, $shareData['parent']);
2548
-		$query->bindValue(13, $shareData['expiration'], 'datetime');
2549
-		$result = $query->execute();
2550
-
2551
-		$id = false;
2552
-		if ($result) {
2553
-			$id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2554
-		}
2555
-
2556
-		return $id;
2557
-
2558
-	}
2559
-
2560
-	/**
2561
-	 * Delete all shares with type SHARE_TYPE_LINK
2562
-	 */
2563
-	public static function removeAllLinkShares() {
2564
-		// Delete any link shares
2565
-		$query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
2566
-		$result = $query->execute(array(self::SHARE_TYPE_LINK));
2567
-		while ($item = $result->fetchRow()) {
2568
-			Helper::delete($item['id']);
2569
-		}
2570
-	}
2571
-
2572
-	/**
2573
-	 * In case a password protected link is not yet authenticated this function will return false
2574
-	 *
2575
-	 * @param array $linkItem
2576
-	 * @return boolean
2577
-	 */
2578
-	public static function checkPasswordProtectedShare(array $linkItem) {
2579
-		if (!isset($linkItem['share_with'])) {
2580
-			return true;
2581
-		}
2582
-		if (!isset($linkItem['share_type'])) {
2583
-			return true;
2584
-		}
2585
-		if (!isset($linkItem['id'])) {
2586
-			return true;
2587
-		}
2588
-
2589
-		if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
2590
-			return true;
2591
-		}
2592
-
2593
-		if ( \OC::$server->getSession()->exists('public_link_authenticated')
2594
-			&& \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) {
2595
-			return true;
2596
-		}
2597
-
2598
-		return false;
2599
-	}
2600
-
2601
-	/**
2602
-	 * construct select statement
2603
-	 * @param int $format
2604
-	 * @param boolean $fileDependent ist it a file/folder share or a generla share
2605
-	 * @param string $uidOwner
2606
-	 * @return string select statement
2607
-	 */
2608
-	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
2609
-		$select = '*';
2610
-		if ($format == self::FORMAT_STATUSES) {
2611
-			if ($fileDependent) {
2612
-				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
2613
-					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
2614
-					. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2615
-					. '`uid_initiator`';
2616
-			} else {
2617
-				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
2618
-			}
2619
-		} else {
2620
-			if (isset($uidOwner)) {
2621
-				if ($fileDependent) {
2622
-					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
2623
-						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
2624
-						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
2625
-						. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2626
-				} else {
2627
-					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
2628
-						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
2629
-				}
2630
-			} else {
2631
-				if ($fileDependent) {
2632
-					if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
2633
-						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
2634
-							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
2635
-							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2636
-							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
2637
-					} else {
2638
-						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
2639
-							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
2640
-							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
2641
-						    . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
2642
-							. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2643
-					}
2644
-				}
2645
-			}
2646
-		}
2647
-		return $select;
2648
-	}
2649
-
2650
-
2651
-	/**
2652
-	 * transform db results
2653
-	 * @param array $row result
2654
-	 */
2655
-	private static function transformDBResults(&$row) {
2656
-		if (isset($row['id'])) {
2657
-			$row['id'] = (int) $row['id'];
2658
-		}
2659
-		if (isset($row['share_type'])) {
2660
-			$row['share_type'] = (int) $row['share_type'];
2661
-		}
2662
-		if (isset($row['parent'])) {
2663
-			$row['parent'] = (int) $row['parent'];
2664
-		}
2665
-		if (isset($row['file_parent'])) {
2666
-			$row['file_parent'] = (int) $row['file_parent'];
2667
-		}
2668
-		if (isset($row['file_source'])) {
2669
-			$row['file_source'] = (int) $row['file_source'];
2670
-		}
2671
-		if (isset($row['permissions'])) {
2672
-			$row['permissions'] = (int) $row['permissions'];
2673
-		}
2674
-		if (isset($row['storage'])) {
2675
-			$row['storage'] = (int) $row['storage'];
2676
-		}
2677
-		if (isset($row['stime'])) {
2678
-			$row['stime'] = (int) $row['stime'];
2679
-		}
2680
-		if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) {
2681
-			// discard expiration date for non-link shares, which might have been
2682
-			// set by ancient bugs
2683
-			$row['expiration'] = null;
2684
-		}
2685
-	}
2686
-
2687
-	/**
2688
-	 * format result
2689
-	 * @param array $items result
2690
-	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
2691
-	 * @param \OCP\Share_Backend $backend sharing backend
2692
-	 * @param int $format
2693
-	 * @param array $parameters additional format parameters
2694
-	 * @return array format result
2695
-	 */
2696
-	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2697
-		if ($format === self::FORMAT_NONE) {
2698
-			return $items;
2699
-		} else if ($format === self::FORMAT_STATUSES) {
2700
-			$statuses = array();
2701
-			foreach ($items as $item) {
2702
-				if ($item['share_type'] === self::SHARE_TYPE_LINK) {
2703
-					if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
2704
-						continue;
2705
-					}
2706
-					$statuses[$item[$column]]['link'] = true;
2707
-				} else if (!isset($statuses[$item[$column]])) {
2708
-					$statuses[$item[$column]]['link'] = false;
2709
-				}
2710
-				if (!empty($item['file_target'])) {
2711
-					$statuses[$item[$column]]['path'] = $item['path'];
2712
-				}
2713
-			}
2714
-			return $statuses;
2715
-		} else {
2716
-			return $backend->formatItems($items, $format, $parameters);
2717
-		}
2718
-	}
2719
-
2720
-	/**
2721
-	 * remove protocol from URL
2722
-	 *
2723
-	 * @param string $url
2724
-	 * @return string
2725
-	 */
2726
-	public static function removeProtocolFromUrl($url) {
2727
-		if (strpos($url, 'https://') === 0) {
2728
-			return substr($url, strlen('https://'));
2729
-		} else if (strpos($url, 'http://') === 0) {
2730
-			return substr($url, strlen('http://'));
2731
-		}
2732
-
2733
-		return $url;
2734
-	}
2735
-
2736
-	/**
2737
-	 * try http post first with https and then with http as a fallback
2738
-	 *
2739
-	 * @param string $remoteDomain
2740
-	 * @param string $urlSuffix
2741
-	 * @param array $fields post parameters
2742
-	 * @return array
2743
-	 */
2744
-	private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
2745
-		$protocol = 'https://';
2746
-		$result = [
2747
-			'success' => false,
2748
-			'result' => '',
2749
-		];
2750
-		$try = 0;
2751
-		$discoveryManager = new DiscoveryManager(
2752
-			\OC::$server->getMemCacheFactory(),
2753
-			\OC::$server->getHTTPClientService()
2754
-		);
2755
-		while ($result['success'] === false && $try < 2) {
2756
-			$endpoint = $discoveryManager->getShareEndpoint($protocol . $remoteDomain);
2757
-			$result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields);
2758
-			$try++;
2759
-			$protocol = 'http://';
2760
-		}
2761
-
2762
-		return $result;
2763
-	}
2764
-
2765
-	/**
2766
-	 * send server-to-server share to remote server
2767
-	 *
2768
-	 * @param string $token
2769
-	 * @param string $shareWith
2770
-	 * @param string $name
2771
-	 * @param int $remote_id
2772
-	 * @param string $owner
2773
-	 * @return bool
2774
-	 */
2775
-	private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
2776
-
2777
-		list($user, $remote) = Helper::splitUserRemote($shareWith);
2778
-
2779
-		if ($user && $remote) {
2780
-			$url = $remote;
2781
-
2782
-			$local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
2783
-
2784
-			$fields = array(
2785
-				'shareWith' => $user,
2786
-				'token' => $token,
2787
-				'name' => $name,
2788
-				'remoteId' => $remote_id,
2789
-				'owner' => $owner,
2790
-				'remote' => $local,
2791
-			);
2792
-
2793
-			$url = self::removeProtocolFromUrl($url);
2794
-			$result = self::tryHttpPostToShareEndpoint($url, '', $fields);
2795
-			$status = json_decode($result['result'], true);
2796
-
2797
-			if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) {
2798
-				\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
2799
-				return true;
2800
-			}
2801
-
2802
-		}
2803
-
2804
-		return false;
2805
-	}
2806
-
2807
-	/**
2808
-	 * send server-to-server unshare to remote server
2809
-	 *
2810
-	 * @param string $remote url
2811
-	 * @param int $id share id
2812
-	 * @param string $token
2813
-	 * @return bool
2814
-	 */
2815
-	private static function sendRemoteUnshare($remote, $id, $token) {
2816
-		$url = rtrim($remote, '/');
2817
-		$fields = array('token' => $token, 'format' => 'json');
2818
-		$url = self::removeProtocolFromUrl($url);
2819
-		$result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
2820
-		$status = json_decode($result['result'], true);
2821
-
2822
-		return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
2823
-	}
2824
-
2825
-	/**
2826
-	 * check if user can only share with group members
2827
-	 * @return bool
2828
-	 */
2829
-	public static function shareWithGroupMembersOnly() {
2830
-		$value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_group_members', 'no');
2831
-		return ($value === 'yes') ? true : false;
2832
-	}
2833
-
2834
-	/**
2835
-	 * @return bool
2836
-	 */
2837
-	public static function isDefaultExpireDateEnabled() {
2838
-		$defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
2839
-		return ($defaultExpireDateEnabled === "yes") ? true : false;
2840
-	}
2841
-
2842
-	/**
2843
-	 * @return bool
2844
-	 */
2845
-	public static function enforceDefaultExpireDate() {
2846
-		$enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
2847
-		return ($enforceDefaultExpireDate === "yes") ? true : false;
2848
-	}
2849
-
2850
-	/**
2851
-	 * @return int
2852
-	 */
2853
-	public static function getExpireInterval() {
2854
-		return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2855
-	}
2856
-
2857
-	/**
2858
-	 * Checks whether the given path is reachable for the given owner
2859
-	 *
2860
-	 * @param string $path path relative to files
2861
-	 * @param string $ownerStorageId storage id of the owner
2862
-	 *
2863
-	 * @return boolean true if file is reachable, false otherwise
2864
-	 */
2865
-	private static function isFileReachable($path, $ownerStorageId) {
2866
-		// if outside the home storage, file is always considered reachable
2867
-		if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
2868
-			substr($ownerStorageId, 0, 13) === 'object::user:'
2869
-		)) {
2870
-			return true;
2871
-		}
2872
-
2873
-		// if inside the home storage, the file has to be under "/files/"
2874
-		$path = ltrim($path, '/');
2875
-		if (substr($path, 0, 6) === 'files/') {
2876
-			return true;
2877
-		}
2878
-
2879
-		return false;
2880
-	}
2881
-
2882
-	/**
2883
-	 * @param IConfig $config
2884
-	 * @return bool 
2885
-	 */
2886
-	public static function enforcePassword(IConfig $config) {
2887
-		$enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
2888
-		return ($enforcePassword === "yes") ? true : false;
2889
-	}
2890
-
2891
-	/**
2892
-	 * Get all share entries, including non-unique group items
2893
-	 *
2894
-	 * @param string $owner
2895
-	 * @return array
2896
-	 */
2897
-	public static function getAllSharesForOwner($owner) {
2898
-		$query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?';
2899
-		$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]);
2900
-		return $result->fetchAll();
2901
-	}
2902
-
2903
-	/**
2904
-	 * Get all share entries, including non-unique group items for a file
2905
-	 *
2906
-	 * @param int $id
2907
-	 * @return array
2908
-	 */
2909
-	public static function getAllSharesForFileId($id) {
2910
-		$query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?';
2911
-		$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
2912
-		return $result->fetchAll();
2913
-	}
2914
-
2915
-	/**
2916
-	 * @param string $password
2917
-	 * @throws \Exception
2918
-	 */
2919
-	private static function verifyPassword($password) {
2920
-
2921
-		$accepted = true;
2922
-		$message = '';
2923
-		\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
2924
-			'password' => $password,
2925
-			'accepted' => &$accepted,
2926
-			'message' => &$message
2927
-		]);
2928
-
2929
-		if (!$accepted) {
2930
-			throw new \Exception($message);
2931
-		}
2932
-	}
1318
+            if ($permissions & ~(int)$rootItem['permissions']) {
1319
+                $qb = $connection->getQueryBuilder();
1320
+                $qb->select('id', 'permissions', 'item_type')
1321
+                    ->from('share')
1322
+                    ->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
1323
+                    ->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
1324
+                    ->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
1325
+                    ->setParameter(':parent', (int)$rootItem['id'])
1326
+                    ->setParameter(':share_type', 2)
1327
+                    ->setParameter(':shareDeleted', 0);
1328
+                $result = $qb->execute();
1329
+
1330
+                $ids = [];
1331
+                while ($item = $result->fetch()) {
1332
+                    $item = $sanitizeItem($item);
1333
+                    $items[] = $item;
1334
+                    $ids[] = $item['id'];
1335
+                }
1336
+                $result->closeCursor();
1337
+
1338
+                // Add permssions for all USERGROUP shares of this item
1339
+                if (!empty($ids)) {
1340
+                    $ids = $intArrayToLiteralArray($ids, $qb->expr());
1341
+
1342
+                    $qb = $connection->getQueryBuilder();
1343
+                    $qb->update('share')
1344
+                        ->set('permissions', $qb->createParameter('permissions'))
1345
+                        ->where($qb->expr()->in('id', $ids))
1346
+                        ->setParameter(':permissions', $permissions);
1347
+                    $qb->execute();
1348
+                }
1349
+            }
1350
+
1351
+            foreach ($items as $item) {
1352
+                \OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]);
1353
+            }
1354
+
1355
+            return true;
1356
+        }
1357
+        $message = 'Setting permissions for %s failed, because the item was not found';
1358
+        $message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource));
1359
+
1360
+        \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
1361
+        throw new \Exception($message_t);
1362
+    }
1363
+
1364
+    /**
1365
+     * validate expiration date if it meets all constraints
1366
+     *
1367
+     * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY"
1368
+     * @param string $shareTime timestamp when the file was shared
1369
+     * @param string $itemType
1370
+     * @param string $itemSource
1371
+     * @return \DateTime validated date
1372
+     * @throws \Exception when the expire date is in the past or further in the future then the enforced date
1373
+     */
1374
+    private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
1375
+        $l = \OC::$server->getL10N('lib');
1376
+        $date = new \DateTime($expireDate);
1377
+        $today = new \DateTime('now');
1378
+
1379
+        // if the user doesn't provide a share time we need to get it from the database
1380
+        // fall-back mode to keep API stable, because the $shareTime parameter was added later
1381
+        $defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
1382
+        if ($defaultExpireDateEnforced && $shareTime === null) {
1383
+            $items = self::getItemShared($itemType, $itemSource);
1384
+            $firstItem = reset($items);
1385
+            $shareTime = (int)$firstItem['stime'];
1386
+        }
1387
+
1388
+        if ($defaultExpireDateEnforced) {
1389
+            // initialize max date with share time
1390
+            $maxDate = new \DateTime();
1391
+            $maxDate->setTimestamp($shareTime);
1392
+            $maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1393
+            $maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1394
+            if ($date > $maxDate) {
1395
+                $warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1396
+                $warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
1397
+                \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1398
+                throw new \Exception($warning_t);
1399
+            }
1400
+        }
1401
+
1402
+        if ($date < $today) {
1403
+            $message = 'Cannot set expiration date. Expiration date is in the past';
1404
+            $message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
1405
+            \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN);
1406
+            throw new \Exception($message_t);
1407
+        }
1408
+
1409
+        return $date;
1410
+    }
1411
+
1412
+    /**
1413
+     * Set expiration date for a share
1414
+     * @param string $itemType
1415
+     * @param string $itemSource
1416
+     * @param string $date expiration date
1417
+     * @param int $shareTime timestamp from when the file was shared
1418
+     * @return boolean
1419
+     * @throws \Exception when the expire date is not set, in the past or further in the future then the enforced date
1420
+     */
1421
+    public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) {
1422
+        $user = \OC_User::getUser();
1423
+        $l = \OC::$server->getL10N('lib');
1424
+
1425
+        if ($date == '') {
1426
+            if (\OCP\Util::isDefaultExpireDateEnforced()) {
1427
+                $warning = 'Cannot clear expiration date. Shares are required to have an expiration date.';
1428
+                $warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.');
1429
+                \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1430
+                throw new \Exception($warning_t);
1431
+            } else {
1432
+                $date = null;
1433
+            }
1434
+        } else {
1435
+            $date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource);
1436
+        }
1437
+        $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ?  AND `uid_owner` = ? AND `share_type` = ?');
1438
+        $query->bindValue(1, $date, 'datetime');
1439
+        $query->bindValue(2, $itemType);
1440
+        $query->bindValue(3, $itemSource);
1441
+        $query->bindValue(4, $user);
1442
+        $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK);
1443
+
1444
+        $query->execute();
1445
+
1446
+        \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array(
1447
+            'itemType' => $itemType,
1448
+            'itemSource' => $itemSource,
1449
+            'date' => $date,
1450
+            'uidOwner' => $user
1451
+        ));
1452
+
1453
+        return true;
1454
+    }
1455
+
1456
+    /**
1457
+     * Retrieve the owner of a connection
1458
+     *
1459
+     * @param IDBConnection $connection
1460
+     * @param int $shareId
1461
+     * @throws \Exception
1462
+     * @return string uid of share owner
1463
+     */
1464
+    private static function getShareOwner(IDBConnection $connection, $shareId) {
1465
+        $qb = $connection->getQueryBuilder();
1466
+
1467
+        $qb->select('uid_owner')
1468
+            ->from('share')
1469
+            ->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1470
+            ->setParameter(':shareId', $shareId);
1471
+        $result = $qb->execute();
1472
+        $result = $result->fetch();
1473
+
1474
+        if (empty($result)) {
1475
+            throw new \Exception('Share not found');
1476
+        }
1477
+
1478
+        return $result['uid_owner'];
1479
+    }
1480
+
1481
+    /**
1482
+     * Set password for a public link share
1483
+     *
1484
+     * @param IUserSession $userSession
1485
+     * @param IDBConnection $connection
1486
+     * @param IConfig $config
1487
+     * @param int $shareId
1488
+     * @param string $password
1489
+     * @throws \Exception
1490
+     * @return boolean
1491
+     */
1492
+    public static function setPassword(IUserSession $userSession,
1493
+                                        IDBConnection $connection,
1494
+                                        IConfig $config,
1495
+                                        $shareId, $password) {
1496
+        $user = $userSession->getUser();
1497
+        if (is_null($user)) {
1498
+            throw new \Exception("User not logged in");
1499
+        }
1500
+
1501
+        $uid = self::getShareOwner($connection, $shareId);
1502
+
1503
+        if ($uid !== $user->getUID()) {
1504
+            throw new \Exception('Cannot update share of a different user');
1505
+        }
1506
+
1507
+        if ($password === '') {
1508
+            $password = null;
1509
+        }
1510
+
1511
+        //If passwords are enforced the password can't be null
1512
+        if (self::enforcePassword($config) && is_null($password)) {
1513
+            throw new \Exception('Cannot remove password');
1514
+        }
1515
+
1516
+        self::verifyPassword($password);
1517
+
1518
+        $qb = $connection->getQueryBuilder();
1519
+        $qb->update('share')
1520
+            ->set('share_with', $qb->createParameter('pass'))
1521
+            ->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1522
+            ->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
1523
+            ->setParameter(':shareId', $shareId);
1524
+
1525
+        $qb->execute();
1526
+
1527
+        return true;
1528
+    }
1529
+
1530
+    /**
1531
+     * Checks whether a share has expired, calls unshareItem() if yes.
1532
+     * @param array $item Share data (usually database row)
1533
+     * @return boolean True if item was expired, false otherwise.
1534
+     */
1535
+    protected static function expireItem(array $item) {
1536
+
1537
+        $result = false;
1538
+
1539
+        // only use default expiration date for link shares
1540
+        if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
1541
+
1542
+            // calculate expiration date
1543
+            if (!empty($item['expiration'])) {
1544
+                $userDefinedExpire = new \DateTime($item['expiration']);
1545
+                $expires = $userDefinedExpire->getTimestamp();
1546
+            } else {
1547
+                $expires = null;
1548
+            }
1549
+
1550
+
1551
+            // get default expiration settings
1552
+            $defaultSettings = Helper::getDefaultExpireSetting();
1553
+            $expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
1554
+
1555
+
1556
+            if (is_int($expires)) {
1557
+                $now = time();
1558
+                if ($now > $expires) {
1559
+                    self::unshareItem($item);
1560
+                    $result = true;
1561
+                }
1562
+            }
1563
+        }
1564
+        return $result;
1565
+    }
1566
+
1567
+    /**
1568
+     * Unshares a share given a share data array
1569
+     * @param array $item Share data (usually database row)
1570
+     * @param int $newParent parent ID
1571
+     * @return null
1572
+     */
1573
+    protected static function unshareItem(array $item, $newParent = null) {
1574
+
1575
+        $shareType = (int)$item['share_type'];
1576
+        $shareWith = null;
1577
+        if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
1578
+            $shareWith = $item['share_with'];
1579
+        }
1580
+
1581
+        // Pass all the vars we have for now, they may be useful
1582
+        $hookParams = array(
1583
+            'id'            => $item['id'],
1584
+            'itemType'      => $item['item_type'],
1585
+            'itemSource'    => $item['item_source'],
1586
+            'shareType'     => $shareType,
1587
+            'shareWith'     => $shareWith,
1588
+            'itemParent'    => $item['parent'],
1589
+            'uidOwner'      => $item['uid_owner'],
1590
+        );
1591
+        if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1592
+            $hookParams['fileSource'] = $item['file_source'];
1593
+            $hookParams['fileTarget'] = $item['file_target'];
1594
+        }
1595
+
1596
+        \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
1597
+        $deletedShares = Helper::delete($item['id'], false, null, $newParent);
1598
+        $deletedShares[] = $hookParams;
1599
+        $hookParams['deletedShares'] = $deletedShares;
1600
+        \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1601
+        if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1602
+            list(, $remote) = Helper::splitUserRemote($item['share_with']);
1603
+            self::sendRemoteUnshare($remote, $item['id'], $item['token']);
1604
+        }
1605
+    }
1606
+
1607
+    /**
1608
+     * Get the backend class for the specified item type
1609
+     * @param string $itemType
1610
+     * @throws \Exception
1611
+     * @return \OCP\Share_Backend
1612
+     */
1613
+    public static function getBackend($itemType) {
1614
+        $l = \OC::$server->getL10N('lib');
1615
+        if (isset(self::$backends[$itemType])) {
1616
+            return self::$backends[$itemType];
1617
+        } else if (isset(self::$backendTypes[$itemType]['class'])) {
1618
+            $class = self::$backendTypes[$itemType]['class'];
1619
+            if (class_exists($class)) {
1620
+                self::$backends[$itemType] = new $class;
1621
+                if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
1622
+                    $message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
1623
+                    $message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class));
1624
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1625
+                    throw new \Exception($message_t);
1626
+                }
1627
+                return self::$backends[$itemType];
1628
+            } else {
1629
+                $message = 'Sharing backend %s not found';
1630
+                $message_t = $l->t('Sharing backend %s not found', array($class));
1631
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1632
+                throw new \Exception($message_t);
1633
+            }
1634
+        }
1635
+        $message = 'Sharing backend for %s not found';
1636
+        $message_t = $l->t('Sharing backend for %s not found', array($itemType));
1637
+        \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), \OCP\Util::ERROR);
1638
+        throw new \Exception($message_t);
1639
+    }
1640
+
1641
+    /**
1642
+     * Check if resharing is allowed
1643
+     * @return boolean true if allowed or false
1644
+     *
1645
+     * Resharing is allowed by default if not configured
1646
+     */
1647
+    public static function isResharingAllowed() {
1648
+        if (!isset(self::$isResharingAllowed)) {
1649
+            if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
1650
+                self::$isResharingAllowed = true;
1651
+            } else {
1652
+                self::$isResharingAllowed = false;
1653
+            }
1654
+        }
1655
+        return self::$isResharingAllowed;
1656
+    }
1657
+
1658
+    /**
1659
+     * Get a list of collection item types for the specified item type
1660
+     * @param string $itemType
1661
+     * @return array
1662
+     */
1663
+    private static function getCollectionItemTypes($itemType) {
1664
+        $collectionTypes = array($itemType);
1665
+        foreach (self::$backendTypes as $type => $backend) {
1666
+            if (in_array($backend['collectionOf'], $collectionTypes)) {
1667
+                $collectionTypes[] = $type;
1668
+            }
1669
+        }
1670
+        // TODO Add option for collections to be collection of themselves, only 'folder' does it now...
1671
+        if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
1672
+            unset($collectionTypes[0]);
1673
+        }
1674
+        // Return array if collections were found or the item type is a
1675
+        // collection itself - collections can be inside collections
1676
+        if (count($collectionTypes) > 0) {
1677
+            return $collectionTypes;
1678
+        }
1679
+        return false;
1680
+    }
1681
+
1682
+    /**
1683
+     * Get the owners of items shared with a user.
1684
+     *
1685
+     * @param string $user The user the items are shared with.
1686
+     * @param string $type The type of the items shared with the user.
1687
+     * @param boolean $includeCollections Include collection item types (optional)
1688
+     * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
1689
+     * @return array
1690
+     */
1691
+    public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
1692
+        // First, we find out if $type is part of a collection (and if that collection is part of
1693
+        // another one and so on).
1694
+        $collectionTypes = array();
1695
+        if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
1696
+            $collectionTypes[] = $type;
1697
+        }
1698
+
1699
+        // Of these collection types, along with our original $type, we make a
1700
+        // list of the ones for which a sharing backend has been registered.
1701
+        // FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
1702
+        // with its $includeCollections parameter set to true. Unfortunately, this fails currently.
1703
+        $allMaybeSharedItems = array();
1704
+        foreach ($collectionTypes as $collectionType) {
1705
+            if (isset(self::$backends[$collectionType])) {
1706
+                $allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
1707
+                    $collectionType,
1708
+                    $user,
1709
+                    self::FORMAT_NONE
1710
+                );
1711
+            }
1712
+        }
1713
+
1714
+        $owners = array();
1715
+        if ($includeOwner) {
1716
+            $owners[] = $user;
1717
+        }
1718
+
1719
+        // We take a look at all shared items of the given $type (or of the collections it is part of)
1720
+        // and find out their owners. Then, we gather the tags for the original $type from all owners,
1721
+        // and return them as elements of a list that look like "Tag (owner)".
1722
+        foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
1723
+            foreach ($maybeSharedItems as $sharedItem) {
1724
+                if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
1725
+                    $owners[] = $sharedItem['uid_owner'];
1726
+                }
1727
+            }
1728
+        }
1729
+
1730
+        return $owners;
1731
+    }
1732
+
1733
+    /**
1734
+     * Get shared items from the database
1735
+     * @param string $itemType
1736
+     * @param string $item Item source or target (optional)
1737
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
1738
+     * @param string $shareWith User or group the item is being shared with
1739
+     * @param string $uidOwner User that is the owner of shared items (optional)
1740
+     * @param int $format Format to convert items to with formatItems() (optional)
1741
+     * @param mixed $parameters to pass to formatItems() (optional)
1742
+     * @param int $limit Number of items to return, -1 to return all matches (optional)
1743
+     * @param boolean $includeCollections Include collection item types (optional)
1744
+     * @param boolean $itemShareWithBySource (optional)
1745
+     * @param boolean $checkExpireDate
1746
+     * @return array
1747
+     *
1748
+     * See public functions getItem(s)... for parameter usage
1749
+     *
1750
+     */
1751
+    public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
1752
+                                    $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
1753
+                                    $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
1754
+        if (!self::isEnabled()) {
1755
+            return array();
1756
+        }
1757
+        $backend = self::getBackend($itemType);
1758
+        $collectionTypes = false;
1759
+        // Get filesystem root to add it to the file target and remove from the
1760
+        // file source, match file_source with the file cache
1761
+        if ($itemType == 'file' || $itemType == 'folder') {
1762
+            if(!is_null($uidOwner)) {
1763
+                $root = \OC\Files\Filesystem::getRoot();
1764
+            } else {
1765
+                $root = '';
1766
+            }
1767
+            $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
1768
+            if (!isset($item)) {
1769
+                $where .= ' AND `file_target` IS NOT NULL ';
1770
+            }
1771
+            $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
1772
+            $fileDependent = true;
1773
+            $queryArgs = array();
1774
+        } else {
1775
+            $fileDependent = false;
1776
+            $root = '';
1777
+            $collectionTypes = self::getCollectionItemTypes($itemType);
1778
+            if ($includeCollections && !isset($item) && $collectionTypes) {
1779
+                // If includeCollections is true, find collections of this item type, e.g. a music album contains songs
1780
+                if (!in_array($itemType, $collectionTypes)) {
1781
+                    $itemTypes = array_merge(array($itemType), $collectionTypes);
1782
+                } else {
1783
+                    $itemTypes = $collectionTypes;
1784
+                }
1785
+                $placeholders = join(',', array_fill(0, count($itemTypes), '?'));
1786
+                $where = ' WHERE `item_type` IN ('.$placeholders.'))';
1787
+                $queryArgs = $itemTypes;
1788
+            } else {
1789
+                $where = ' WHERE `item_type` = ?';
1790
+                $queryArgs = array($itemType);
1791
+            }
1792
+        }
1793
+        if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1794
+            $where .= ' AND `share_type` != ?';
1795
+            $queryArgs[] = self::SHARE_TYPE_LINK;
1796
+        }
1797
+        if (isset($shareType)) {
1798
+            // Include all user and group items
1799
+            if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
1800
+                $where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
1801
+                $queryArgs[] = self::SHARE_TYPE_USER;
1802
+                $queryArgs[] = self::$shareTypeGroupUserUnique;
1803
+                $queryArgs[] = $shareWith;
1804
+
1805
+                $user = \OC::$server->getUserManager()->get($shareWith);
1806
+                $groups = [];
1807
+                if ($user) {
1808
+                    $groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
1809
+                }
1810
+                if (!empty($groups)) {
1811
+                    $placeholders = join(',', array_fill(0, count($groups), '?'));
1812
+                    $where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
1813
+                    $queryArgs[] = self::SHARE_TYPE_GROUP;
1814
+                    $queryArgs = array_merge($queryArgs, $groups);
1815
+                }
1816
+                $where .= ')';
1817
+                // Don't include own group shares
1818
+                $where .= ' AND `uid_owner` != ?';
1819
+                $queryArgs[] = $shareWith;
1820
+            } else {
1821
+                $where .= ' AND `share_type` = ?';
1822
+                $queryArgs[] = $shareType;
1823
+                if (isset($shareWith)) {
1824
+                    $where .= ' AND `share_with` = ?';
1825
+                    $queryArgs[] = $shareWith;
1826
+                }
1827
+            }
1828
+        }
1829
+        if (isset($uidOwner)) {
1830
+            $where .= ' AND `uid_owner` = ?';
1831
+            $queryArgs[] = $uidOwner;
1832
+            if (!isset($shareType)) {
1833
+                // Prevent unique user targets for group shares from being selected
1834
+                $where .= ' AND `share_type` != ?';
1835
+                $queryArgs[] = self::$shareTypeGroupUserUnique;
1836
+            }
1837
+            if ($fileDependent) {
1838
+                $column = 'file_source';
1839
+            } else {
1840
+                $column = 'item_source';
1841
+            }
1842
+        } else {
1843
+            if ($fileDependent) {
1844
+                $column = 'file_target';
1845
+            } else {
1846
+                $column = 'item_target';
1847
+            }
1848
+        }
1849
+        if (isset($item)) {
1850
+            $collectionTypes = self::getCollectionItemTypes($itemType);
1851
+            if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1852
+                $where .= ' AND (';
1853
+            } else {
1854
+                $where .= ' AND';
1855
+            }
1856
+            // If looking for own shared items, check item_source else check item_target
1857
+            if (isset($uidOwner) || $itemShareWithBySource) {
1858
+                // If item type is a file, file source needs to be checked in case the item was converted
1859
+                if ($fileDependent) {
1860
+                    $where .= ' `file_source` = ?';
1861
+                    $column = 'file_source';
1862
+                } else {
1863
+                    $where .= ' `item_source` = ?';
1864
+                    $column = 'item_source';
1865
+                }
1866
+            } else {
1867
+                if ($fileDependent) {
1868
+                    $where .= ' `file_target` = ?';
1869
+                    $item = \OC\Files\Filesystem::normalizePath($item);
1870
+                } else {
1871
+                    $where .= ' `item_target` = ?';
1872
+                }
1873
+            }
1874
+            $queryArgs[] = $item;
1875
+            if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1876
+                $placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
1877
+                $where .= ' OR `item_type` IN ('.$placeholders.'))';
1878
+                $queryArgs = array_merge($queryArgs, $collectionTypes);
1879
+            }
1880
+        }
1881
+
1882
+        if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
1883
+            // Make sure the unique user target is returned if it exists,
1884
+            // unique targets should follow the group share in the database
1885
+            // If the limit is not 1, the filtering can be done later
1886
+            $where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
1887
+        } else {
1888
+            $where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
1889
+        }
1890
+
1891
+        if ($limit != -1 && !$includeCollections) {
1892
+            // The limit must be at least 3, because filtering needs to be done
1893
+            if ($limit < 3) {
1894
+                $queryLimit = 3;
1895
+            } else {
1896
+                $queryLimit = $limit;
1897
+            }
1898
+        } else {
1899
+            $queryLimit = null;
1900
+        }
1901
+        $select = self::createSelectStatement($format, $fileDependent, $uidOwner);
1902
+        $root = strlen($root);
1903
+        $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
1904
+        $result = $query->execute($queryArgs);
1905
+        if ($result === false) {
1906
+            \OCP\Util::writeLog('OCP\Share',
1907
+                \OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
1908
+                \OCP\Util::ERROR);
1909
+        }
1910
+        $items = array();
1911
+        $targets = array();
1912
+        $switchedItems = array();
1913
+        $mounts = array();
1914
+        while ($row = $result->fetchRow()) {
1915
+            self::transformDBResults($row);
1916
+            // Filter out duplicate group shares for users with unique targets
1917
+            if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
1918
+                continue;
1919
+            }
1920
+            if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
1921
+                $row['share_type'] = self::SHARE_TYPE_GROUP;
1922
+                $row['unique_name'] = true; // remember that we use a unique name for this user
1923
+                $row['share_with'] = $items[$row['parent']]['share_with'];
1924
+                // if the group share was unshared from the user we keep the permission, otherwise
1925
+                // we take the permission from the parent because this is always the up-to-date
1926
+                // permission for the group share
1927
+                if ($row['permissions'] > 0) {
1928
+                    $row['permissions'] = $items[$row['parent']]['permissions'];
1929
+                }
1930
+                // Remove the parent group share
1931
+                unset($items[$row['parent']]);
1932
+                if ($row['permissions'] == 0) {
1933
+                    continue;
1934
+                }
1935
+            } else if (!isset($uidOwner)) {
1936
+                // Check if the same target already exists
1937
+                if (isset($targets[$row['id']])) {
1938
+                    // Check if the same owner shared with the user twice
1939
+                    // through a group and user share - this is allowed
1940
+                    $id = $targets[$row['id']];
1941
+                    if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
1942
+                        // Switch to group share type to ensure resharing conditions aren't bypassed
1943
+                        if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
1944
+                            $items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
1945
+                            $items[$id]['share_with'] = $row['share_with'];
1946
+                        }
1947
+                        // Switch ids if sharing permission is granted on only
1948
+                        // one share to ensure correct parent is used if resharing
1949
+                        if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1950
+                            && (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1951
+                            $items[$row['id']] = $items[$id];
1952
+                            $switchedItems[$id] = $row['id'];
1953
+                            unset($items[$id]);
1954
+                            $id = $row['id'];
1955
+                        }
1956
+                        $items[$id]['permissions'] |= (int)$row['permissions'];
1957
+
1958
+                    }
1959
+                    continue;
1960
+                } elseif (!empty($row['parent'])) {
1961
+                    $targets[$row['parent']] = $row['id'];
1962
+                }
1963
+            }
1964
+            // Remove root from file source paths if retrieving own shared items
1965
+            if (isset($uidOwner) && isset($row['path'])) {
1966
+                if (isset($row['parent'])) {
1967
+                    $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1968
+                    $parentResult = $query->execute(array($row['parent']));
1969
+                    if ($result === false) {
1970
+                        \OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
1971
+                            \OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
1972
+                            \OCP\Util::ERROR);
1973
+                    } else {
1974
+                        $parentRow = $parentResult->fetchRow();
1975
+                        $tmpPath = $parentRow['file_target'];
1976
+                        // find the right position where the row path continues from the target path
1977
+                        $pos = strrpos($row['path'], $parentRow['file_target']);
1978
+                        $subPath = substr($row['path'], $pos);
1979
+                        $splitPath = explode('/', $subPath);
1980
+                        foreach (array_slice($splitPath, 2) as $pathPart) {
1981
+                            $tmpPath = $tmpPath . '/' . $pathPart;
1982
+                        }
1983
+                        $row['path'] = $tmpPath;
1984
+                    }
1985
+                } else {
1986
+                    if (!isset($mounts[$row['storage']])) {
1987
+                        $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
1988
+                        if (is_array($mountPoints) && !empty($mountPoints)) {
1989
+                            $mounts[$row['storage']] = current($mountPoints);
1990
+                        }
1991
+                    }
1992
+                    if (!empty($mounts[$row['storage']])) {
1993
+                        $path = $mounts[$row['storage']]->getMountPoint().$row['path'];
1994
+                        $relPath = substr($path, $root); // path relative to data/user
1995
+                        $row['path'] = rtrim($relPath, '/');
1996
+                    }
1997
+                }
1998
+            }
1999
+
2000
+            if($checkExpireDate) {
2001
+                if (self::expireItem($row)) {
2002
+                    continue;
2003
+                }
2004
+            }
2005
+            // Check if resharing is allowed, if not remove share permission
2006
+            if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
2007
+                $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
2008
+            }
2009
+            // Add display names to result
2010
+            $row['share_with_displayname'] = $row['share_with'];
2011
+            if ( isset($row['share_with']) && $row['share_with'] != '' &&
2012
+                $row['share_type'] === self::SHARE_TYPE_USER) {
2013
+                $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
2014
+            } else if(isset($row['share_with']) && $row['share_with'] != '' &&
2015
+                $row['share_type'] === self::SHARE_TYPE_REMOTE) {
2016
+                $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
2017
+                foreach ($addressBookEntries as $entry) {
2018
+                    foreach ($entry['CLOUD'] as $cloudID) {
2019
+                        if ($cloudID === $row['share_with']) {
2020
+                            $row['share_with_displayname'] = $entry['FN'];
2021
+                        }
2022
+                    }
2023
+                }
2024
+            }
2025
+            if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
2026
+                $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
2027
+            }
2028
+
2029
+            if ($row['permissions'] > 0) {
2030
+                $items[$row['id']] = $row;
2031
+            }
2032
+
2033
+        }
2034
+
2035
+        // group items if we are looking for items shared with the current user
2036
+        if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
2037
+            $items = self::groupItems($items, $itemType);
2038
+        }
2039
+
2040
+        if (!empty($items)) {
2041
+            $collectionItems = array();
2042
+            foreach ($items as &$row) {
2043
+                // Return only the item instead of a 2-dimensional array
2044
+                if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
2045
+                    if ($format == self::FORMAT_NONE) {
2046
+                        return $row;
2047
+                    } else {
2048
+                        break;
2049
+                    }
2050
+                }
2051
+                // Check if this is a collection of the requested item type
2052
+                if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
2053
+                    if (($collectionBackend = self::getBackend($row['item_type']))
2054
+                        && $collectionBackend instanceof \OCP\Share_Backend_Collection) {
2055
+                        // Collections can be inside collections, check if the item is a collection
2056
+                        if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
2057
+                            $collectionItems[] = $row;
2058
+                        } else {
2059
+                            $collection = array();
2060
+                            $collection['item_type'] = $row['item_type'];
2061
+                            if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2062
+                                $collection['path'] = basename($row['path']);
2063
+                            }
2064
+                            $row['collection'] = $collection;
2065
+                            // Fetch all of the children sources
2066
+                            $children = $collectionBackend->getChildren($row[$column]);
2067
+                            foreach ($children as $child) {
2068
+                                $childItem = $row;
2069
+                                $childItem['item_type'] = $itemType;
2070
+                                if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
2071
+                                    $childItem['item_source'] = $child['source'];
2072
+                                    $childItem['item_target'] = $child['target'];
2073
+                                }
2074
+                                if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2075
+                                    if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2076
+                                        $childItem['file_source'] = $child['source'];
2077
+                                    } else { // TODO is this really needed if we already know that we use the file backend?
2078
+                                        $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
2079
+                                        $childItem['file_source'] = $meta['fileid'];
2080
+                                    }
2081
+                                    $childItem['file_target'] =
2082
+                                        \OC\Files\Filesystem::normalizePath($child['file_path']);
2083
+                                }
2084
+                                if (isset($item)) {
2085
+                                    if ($childItem[$column] == $item) {
2086
+                                        // Return only the item instead of a 2-dimensional array
2087
+                                        if ($limit == 1) {
2088
+                                            if ($format == self::FORMAT_NONE) {
2089
+                                                return $childItem;
2090
+                                            } else {
2091
+                                                // Unset the items array and break out of both loops
2092
+                                                $items = array();
2093
+                                                $items[] = $childItem;
2094
+                                                break 2;
2095
+                                            }
2096
+                                        } else {
2097
+                                            $collectionItems[] = $childItem;
2098
+                                        }
2099
+                                    }
2100
+                                } else {
2101
+                                    $collectionItems[] = $childItem;
2102
+                                }
2103
+                            }
2104
+                        }
2105
+                    }
2106
+                    // Remove collection item
2107
+                    $toRemove = $row['id'];
2108
+                    if (array_key_exists($toRemove, $switchedItems)) {
2109
+                        $toRemove = $switchedItems[$toRemove];
2110
+                    }
2111
+                    unset($items[$toRemove]);
2112
+                } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
2113
+                    // FIXME: Thats a dirty hack to improve file sharing performance,
2114
+                    // see github issue #10588 for more details
2115
+                    // Need to find a solution which works for all back-ends
2116
+                    $collectionBackend = self::getBackend($row['item_type']);
2117
+                    $sharedParents = $collectionBackend->getParents($row['item_source']);
2118
+                    foreach ($sharedParents as $parent) {
2119
+                        $collectionItems[] = $parent;
2120
+                    }
2121
+                }
2122
+            }
2123
+            if (!empty($collectionItems)) {
2124
+                $collectionItems = array_unique($collectionItems, SORT_REGULAR);
2125
+                $items = array_merge($items, $collectionItems);
2126
+            }
2127
+
2128
+            // filter out invalid items, these can appear when subshare entries exist
2129
+            // for a group in which the requested user isn't a member any more
2130
+            $items = array_filter($items, function($item) {
2131
+                return $item['share_type'] !== self::$shareTypeGroupUserUnique;
2132
+            });
2133
+
2134
+            return self::formatResult($items, $column, $backend, $format, $parameters);
2135
+        } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
2136
+            // FIXME: Thats a dirty hack to improve file sharing performance,
2137
+            // see github issue #10588 for more details
2138
+            // Need to find a solution which works for all back-ends
2139
+            $collectionItems = array();
2140
+            $collectionBackend = self::getBackend('folder');
2141
+            $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
2142
+            foreach ($sharedParents as $parent) {
2143
+                $collectionItems[] = $parent;
2144
+            }
2145
+            if ($limit === 1) {
2146
+                return reset($collectionItems);
2147
+            }
2148
+            return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
2149
+        }
2150
+
2151
+        return array();
2152
+    }
2153
+
2154
+    /**
2155
+     * group items with link to the same source
2156
+     *
2157
+     * @param array $items
2158
+     * @param string $itemType
2159
+     * @return array of grouped items
2160
+     */
2161
+    protected static function groupItems($items, $itemType) {
2162
+
2163
+        $fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false;
2164
+
2165
+        $result = array();
2166
+
2167
+        foreach ($items as $item) {
2168
+            $grouped = false;
2169
+            foreach ($result as $key => $r) {
2170
+                // for file/folder shares we need to compare file_source, otherwise we compare item_source
2171
+                // only group shares if they already point to the same target, otherwise the file where shared
2172
+                // before grouping of shares was added. In this case we don't group them toi avoid confusions
2173
+                if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2174
+                    (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
2175
+                    // add the first item to the list of grouped shares
2176
+                    if (!isset($result[$key]['grouped'])) {
2177
+                        $result[$key]['grouped'][] = $result[$key];
2178
+                    }
2179
+                    $result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
2180
+                    $result[$key]['grouped'][] = $item;
2181
+                    $grouped = true;
2182
+                    break;
2183
+                }
2184
+            }
2185
+
2186
+            if (!$grouped) {
2187
+                $result[] = $item;
2188
+            }
2189
+
2190
+        }
2191
+
2192
+        return $result;
2193
+    }
2194
+
2195
+    /**
2196
+     * Put shared item into the database
2197
+     * @param string $itemType Item type
2198
+     * @param string $itemSource Item source
2199
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
2200
+     * @param string $shareWith User or group the item is being shared with
2201
+     * @param string $uidOwner User that is the owner of shared item
2202
+     * @param int $permissions CRUDS permissions
2203
+     * @param boolean|array $parentFolder Parent folder target (optional)
2204
+     * @param string $token (optional)
2205
+     * @param string $itemSourceName name of the source item (optional)
2206
+     * @param \DateTime $expirationDate (optional)
2207
+     * @throws \Exception
2208
+     * @return mixed id of the new share or false
2209
+     */
2210
+    private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2211
+                                $permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
2212
+
2213
+        $queriesToExecute = array();
2214
+        $suggestedItemTarget = null;
2215
+        $groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
2216
+        $groupItemTarget = $itemTarget = $fileSource = $parent = 0;
2217
+
2218
+        $result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
2219
+        if(!empty($result)) {
2220
+            $parent = $result['parent'];
2221
+            $itemSource = $result['itemSource'];
2222
+            $fileSource = $result['fileSource'];
2223
+            $suggestedItemTarget = $result['suggestedItemTarget'];
2224
+            $suggestedFileTarget = $result['suggestedFileTarget'];
2225
+            $filePath = $result['filePath'];
2226
+        }
2227
+
2228
+        $isGroupShare = false;
2229
+        if ($shareType == self::SHARE_TYPE_GROUP) {
2230
+            $isGroupShare = true;
2231
+            if (isset($shareWith['users'])) {
2232
+                $users = $shareWith['users'];
2233
+            } else {
2234
+                $group = \OC::$server->getGroupManager()->get($shareWith['group']);
2235
+                if ($group) {
2236
+                    $users = $group->searchUsers('', -1, 0);
2237
+                    $userIds = [];
2238
+                    foreach ($users as $user) {
2239
+                        $userIds[] = $user->getUID();
2240
+                    }
2241
+                    $users = $userIds;
2242
+                } else {
2243
+                    $users = [];
2244
+                }
2245
+            }
2246
+            // remove current user from list
2247
+            if (in_array(\OCP\User::getUser(), $users)) {
2248
+                unset($users[array_search(\OCP\User::getUser(), $users)]);
2249
+            }
2250
+            $groupItemTarget = Helper::generateTarget($itemType, $itemSource,
2251
+                $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
2252
+            $groupFileTarget = Helper::generateTarget($itemType, $itemSource,
2253
+                $shareType, $shareWith['group'], $uidOwner, $filePath);
2254
+
2255
+            // add group share to table and remember the id as parent
2256
+            $queriesToExecute['groupShare'] = array(
2257
+                'itemType'			=> $itemType,
2258
+                'itemSource'		=> $itemSource,
2259
+                'itemTarget'		=> $groupItemTarget,
2260
+                'shareType'			=> $shareType,
2261
+                'shareWith'			=> $shareWith['group'],
2262
+                'uidOwner'			=> $uidOwner,
2263
+                'permissions'		=> $permissions,
2264
+                'shareTime'			=> time(),
2265
+                'fileSource'		=> $fileSource,
2266
+                'fileTarget'		=> $groupFileTarget,
2267
+                'token'				=> $token,
2268
+                'parent'			=> $parent,
2269
+                'expiration'		=> $expirationDate,
2270
+            );
2271
+
2272
+        } else {
2273
+            $users = array($shareWith);
2274
+            $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2275
+                $suggestedItemTarget);
2276
+        }
2277
+
2278
+        $run = true;
2279
+        $error = '';
2280
+        $preHookData = array(
2281
+            'itemType' => $itemType,
2282
+            'itemSource' => $itemSource,
2283
+            'shareType' => $shareType,
2284
+            'uidOwner' => $uidOwner,
2285
+            'permissions' => $permissions,
2286
+            'fileSource' => $fileSource,
2287
+            'expiration' => $expirationDate,
2288
+            'token' => $token,
2289
+            'run' => &$run,
2290
+            'error' => &$error
2291
+        );
2292
+
2293
+        $preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2294
+        $preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2295
+
2296
+        \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
2297
+
2298
+        if ($run === false) {
2299
+            throw new \Exception($error);
2300
+        }
2301
+
2302
+        foreach ($users as $user) {
2303
+            $sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
2304
+            $sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
2305
+
2306
+            $userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType;
2307
+
2308
+            if ($sourceExists && $sourceExists['item_source'] === $itemSource) {
2309
+                $fileTarget = $sourceExists['file_target'];
2310
+                $itemTarget = $sourceExists['item_target'];
2311
+
2312
+                // for group shares we don't need a additional entry if the target is the same
2313
+                if($isGroupShare && $groupItemTarget === $itemTarget) {
2314
+                    continue;
2315
+                }
2316
+
2317
+            } elseif(!$sourceExists && !$isGroupShare)  {
2318
+
2319
+                $itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
2320
+                    $uidOwner, $suggestedItemTarget, $parent);
2321
+                if (isset($fileSource)) {
2322
+                    if ($parentFolder) {
2323
+                        if ($parentFolder === true) {
2324
+                            $fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
2325
+                                $uidOwner, $suggestedFileTarget, $parent);
2326
+                            if ($fileTarget != $groupFileTarget) {
2327
+                                $parentFolders[$user]['folder'] = $fileTarget;
2328
+                            }
2329
+                        } else if (isset($parentFolder[$user])) {
2330
+                            $fileTarget = $parentFolder[$user]['folder'].$itemSource;
2331
+                            $parent = $parentFolder[$user]['id'];
2332
+                        }
2333
+                    } else {
2334
+                        $fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
2335
+                            $user, $uidOwner, $suggestedFileTarget, $parent);
2336
+                    }
2337
+                } else {
2338
+                    $fileTarget = null;
2339
+                }
2340
+
2341
+            } else {
2342
+
2343
+                // group share which doesn't exists until now, check if we need a unique target for this user
2344
+
2345
+                $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
2346
+                    $uidOwner, $suggestedItemTarget, $parent);
2347
+
2348
+                // do we also need a file target
2349
+                if (isset($fileSource)) {
2350
+                    $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
2351
+                        $uidOwner, $suggestedFileTarget, $parent);
2352
+                } else {
2353
+                    $fileTarget = null;
2354
+                }
2355
+
2356
+                if (($itemTarget === $groupItemTarget) &&
2357
+                    (!isset($fileSource) || $fileTarget === $groupFileTarget)) {
2358
+                    continue;
2359
+                }
2360
+            }
2361
+
2362
+            $queriesToExecute[] = array(
2363
+                'itemType'			=> $itemType,
2364
+                'itemSource'		=> $itemSource,
2365
+                'itemTarget'		=> $itemTarget,
2366
+                'shareType'			=> $userShareType,
2367
+                'shareWith'			=> $user,
2368
+                'uidOwner'			=> $uidOwner,
2369
+                'permissions'		=> $permissions,
2370
+                'shareTime'			=> time(),
2371
+                'fileSource'		=> $fileSource,
2372
+                'fileTarget'		=> $fileTarget,
2373
+                'token'				=> $token,
2374
+                'parent'			=> $parent,
2375
+                'expiration'		=> $expirationDate,
2376
+            );
2377
+
2378
+        }
2379
+
2380
+        $id = false;
2381
+        if ($isGroupShare) {
2382
+            $id = self::insertShare($queriesToExecute['groupShare']);
2383
+            // Save this id, any extra rows for this group share will need to reference it
2384
+            $parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2385
+            unset($queriesToExecute['groupShare']);
2386
+        }
2387
+
2388
+        foreach ($queriesToExecute as $shareQuery) {
2389
+            $shareQuery['parent'] = $parent;
2390
+            $id = self::insertShare($shareQuery);
2391
+        }
2392
+
2393
+        $postHookData = array(
2394
+            'itemType' => $itemType,
2395
+            'itemSource' => $itemSource,
2396
+            'parent' => $parent,
2397
+            'shareType' => $shareType,
2398
+            'uidOwner' => $uidOwner,
2399
+            'permissions' => $permissions,
2400
+            'fileSource' => $fileSource,
2401
+            'id' => $parent,
2402
+            'token' => $token,
2403
+            'expirationDate' => $expirationDate,
2404
+        );
2405
+
2406
+        $postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2407
+        $postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2408
+        $postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget;
2409
+
2410
+        \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
2411
+
2412
+
2413
+        return $id ? $id : false;
2414
+    }
2415
+
2416
+    /**
2417
+     * @param string $itemType
2418
+     * @param string $itemSource
2419
+     * @param int $shareType
2420
+     * @param string $shareWith
2421
+     * @param string $uidOwner
2422
+     * @param int $permissions
2423
+     * @param string|null $itemSourceName
2424
+     * @param null|\DateTime $expirationDate
2425
+     */
2426
+    private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
2427
+        $backend = self::getBackend($itemType);
2428
+
2429
+        $l = \OC::$server->getL10N('lib');
2430
+        $result = array();
2431
+
2432
+        $column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
2433
+
2434
+        $checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
2435
+        if ($checkReshare) {
2436
+            // Check if attempting to share back to owner
2437
+            if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
2438
+                $message = 'Sharing %s failed, because the user %s is the original sharer';
2439
+                $message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
2440
+
2441
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
2442
+                throw new \Exception($message_t);
2443
+            }
2444
+        }
2445
+
2446
+        if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
2447
+            // Check if share permissions is granted
2448
+            if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2449
+                if (~(int)$checkReshare['permissions'] & $permissions) {
2450
+                    $message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2451
+                    $message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
2452
+
2453
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG);
2454
+                    throw new \Exception($message_t);
2455
+                } else {
2456
+                    // TODO Don't check if inside folder
2457
+                    $result['parent'] = $checkReshare['id'];
2458
+
2459
+                    $result['expirationDate'] = $expirationDate;
2460
+                    // $checkReshare['expiration'] could be null and then is always less than any value
2461
+                    if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2462
+                        $result['expirationDate'] = $checkReshare['expiration'];
2463
+                    }
2464
+
2465
+                    // only suggest the same name as new target if it is a reshare of the
2466
+                    // same file/folder and not the reshare of a child
2467
+                    if ($checkReshare[$column] === $itemSource) {
2468
+                        $result['filePath'] = $checkReshare['file_target'];
2469
+                        $result['itemSource'] = $checkReshare['item_source'];
2470
+                        $result['fileSource'] = $checkReshare['file_source'];
2471
+                        $result['suggestedItemTarget'] = $checkReshare['item_target'];
2472
+                        $result['suggestedFileTarget'] = $checkReshare['file_target'];
2473
+                    } else {
2474
+                        $result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
2475
+                        $result['suggestedItemTarget'] = null;
2476
+                        $result['suggestedFileTarget'] = null;
2477
+                        $result['itemSource'] = $itemSource;
2478
+                        $result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
2479
+                    }
2480
+                }
2481
+            } else {
2482
+                $message = 'Sharing %s failed, because resharing is not allowed';
2483
+                $message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName));
2484
+
2485
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
2486
+                throw new \Exception($message_t);
2487
+            }
2488
+        } else {
2489
+            $result['parent'] = null;
2490
+            $result['suggestedItemTarget'] = null;
2491
+            $result['suggestedFileTarget'] = null;
2492
+            $result['itemSource'] = $itemSource;
2493
+            $result['expirationDate'] = $expirationDate;
2494
+            if (!$backend->isValidSource($itemSource, $uidOwner)) {
2495
+                $message = 'Sharing %s failed, because the sharing backend for '
2496
+                    .'%s could not find its source';
2497
+                $message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType));
2498
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG);
2499
+                throw new \Exception($message_t);
2500
+            }
2501
+            if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2502
+                $result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
2503
+                if ($itemType == 'file' || $itemType == 'folder') {
2504
+                    $result['fileSource'] = $itemSource;
2505
+                } else {
2506
+                    $meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
2507
+                    $result['fileSource'] = $meta['fileid'];
2508
+                }
2509
+                if ($result['fileSource'] == -1) {
2510
+                    $message = 'Sharing %s failed, because the file could not be found in the file cache';
2511
+                    $message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource));
2512
+
2513
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
2514
+                    throw new \Exception($message_t);
2515
+                }
2516
+            } else {
2517
+                $result['filePath'] = null;
2518
+                $result['fileSource'] = null;
2519
+            }
2520
+        }
2521
+
2522
+        return $result;
2523
+    }
2524
+
2525
+    /**
2526
+     *
2527
+     * @param array $shareData
2528
+     * @return mixed false in case of a failure or the id of the new share
2529
+     */
2530
+    private static function insertShare(array $shareData) {
2531
+
2532
+        $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
2533
+            .' `item_type`, `item_source`, `item_target`, `share_type`,'
2534
+            .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
2535
+            .' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
2536
+        $query->bindValue(1, $shareData['itemType']);
2537
+        $query->bindValue(2, $shareData['itemSource']);
2538
+        $query->bindValue(3, $shareData['itemTarget']);
2539
+        $query->bindValue(4, $shareData['shareType']);
2540
+        $query->bindValue(5, $shareData['shareWith']);
2541
+        $query->bindValue(6, $shareData['uidOwner']);
2542
+        $query->bindValue(7, $shareData['permissions']);
2543
+        $query->bindValue(8, $shareData['shareTime']);
2544
+        $query->bindValue(9, $shareData['fileSource']);
2545
+        $query->bindValue(10, $shareData['fileTarget']);
2546
+        $query->bindValue(11, $shareData['token']);
2547
+        $query->bindValue(12, $shareData['parent']);
2548
+        $query->bindValue(13, $shareData['expiration'], 'datetime');
2549
+        $result = $query->execute();
2550
+
2551
+        $id = false;
2552
+        if ($result) {
2553
+            $id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2554
+        }
2555
+
2556
+        return $id;
2557
+
2558
+    }
2559
+
2560
+    /**
2561
+     * Delete all shares with type SHARE_TYPE_LINK
2562
+     */
2563
+    public static function removeAllLinkShares() {
2564
+        // Delete any link shares
2565
+        $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
2566
+        $result = $query->execute(array(self::SHARE_TYPE_LINK));
2567
+        while ($item = $result->fetchRow()) {
2568
+            Helper::delete($item['id']);
2569
+        }
2570
+    }
2571
+
2572
+    /**
2573
+     * In case a password protected link is not yet authenticated this function will return false
2574
+     *
2575
+     * @param array $linkItem
2576
+     * @return boolean
2577
+     */
2578
+    public static function checkPasswordProtectedShare(array $linkItem) {
2579
+        if (!isset($linkItem['share_with'])) {
2580
+            return true;
2581
+        }
2582
+        if (!isset($linkItem['share_type'])) {
2583
+            return true;
2584
+        }
2585
+        if (!isset($linkItem['id'])) {
2586
+            return true;
2587
+        }
2588
+
2589
+        if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
2590
+            return true;
2591
+        }
2592
+
2593
+        if ( \OC::$server->getSession()->exists('public_link_authenticated')
2594
+            && \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) {
2595
+            return true;
2596
+        }
2597
+
2598
+        return false;
2599
+    }
2600
+
2601
+    /**
2602
+     * construct select statement
2603
+     * @param int $format
2604
+     * @param boolean $fileDependent ist it a file/folder share or a generla share
2605
+     * @param string $uidOwner
2606
+     * @return string select statement
2607
+     */
2608
+    private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
2609
+        $select = '*';
2610
+        if ($format == self::FORMAT_STATUSES) {
2611
+            if ($fileDependent) {
2612
+                $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
2613
+                    . '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
2614
+                    . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2615
+                    . '`uid_initiator`';
2616
+            } else {
2617
+                $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
2618
+            }
2619
+        } else {
2620
+            if (isset($uidOwner)) {
2621
+                if ($fileDependent) {
2622
+                    $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
2623
+                        . ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
2624
+                        . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
2625
+                        . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2626
+                } else {
2627
+                    $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
2628
+                        . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
2629
+                }
2630
+            } else {
2631
+                if ($fileDependent) {
2632
+                    if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
2633
+                        $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
2634
+                            . '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
2635
+                            . '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2636
+                            . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
2637
+                    } else {
2638
+                        $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
2639
+                            . '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
2640
+                            . '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
2641
+                            . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
2642
+                            . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2643
+                    }
2644
+                }
2645
+            }
2646
+        }
2647
+        return $select;
2648
+    }
2649
+
2650
+
2651
+    /**
2652
+     * transform db results
2653
+     * @param array $row result
2654
+     */
2655
+    private static function transformDBResults(&$row) {
2656
+        if (isset($row['id'])) {
2657
+            $row['id'] = (int) $row['id'];
2658
+        }
2659
+        if (isset($row['share_type'])) {
2660
+            $row['share_type'] = (int) $row['share_type'];
2661
+        }
2662
+        if (isset($row['parent'])) {
2663
+            $row['parent'] = (int) $row['parent'];
2664
+        }
2665
+        if (isset($row['file_parent'])) {
2666
+            $row['file_parent'] = (int) $row['file_parent'];
2667
+        }
2668
+        if (isset($row['file_source'])) {
2669
+            $row['file_source'] = (int) $row['file_source'];
2670
+        }
2671
+        if (isset($row['permissions'])) {
2672
+            $row['permissions'] = (int) $row['permissions'];
2673
+        }
2674
+        if (isset($row['storage'])) {
2675
+            $row['storage'] = (int) $row['storage'];
2676
+        }
2677
+        if (isset($row['stime'])) {
2678
+            $row['stime'] = (int) $row['stime'];
2679
+        }
2680
+        if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) {
2681
+            // discard expiration date for non-link shares, which might have been
2682
+            // set by ancient bugs
2683
+            $row['expiration'] = null;
2684
+        }
2685
+    }
2686
+
2687
+    /**
2688
+     * format result
2689
+     * @param array $items result
2690
+     * @param string $column is it a file share or a general share ('file_target' or 'item_target')
2691
+     * @param \OCP\Share_Backend $backend sharing backend
2692
+     * @param int $format
2693
+     * @param array $parameters additional format parameters
2694
+     * @return array format result
2695
+     */
2696
+    private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2697
+        if ($format === self::FORMAT_NONE) {
2698
+            return $items;
2699
+        } else if ($format === self::FORMAT_STATUSES) {
2700
+            $statuses = array();
2701
+            foreach ($items as $item) {
2702
+                if ($item['share_type'] === self::SHARE_TYPE_LINK) {
2703
+                    if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
2704
+                        continue;
2705
+                    }
2706
+                    $statuses[$item[$column]]['link'] = true;
2707
+                } else if (!isset($statuses[$item[$column]])) {
2708
+                    $statuses[$item[$column]]['link'] = false;
2709
+                }
2710
+                if (!empty($item['file_target'])) {
2711
+                    $statuses[$item[$column]]['path'] = $item['path'];
2712
+                }
2713
+            }
2714
+            return $statuses;
2715
+        } else {
2716
+            return $backend->formatItems($items, $format, $parameters);
2717
+        }
2718
+    }
2719
+
2720
+    /**
2721
+     * remove protocol from URL
2722
+     *
2723
+     * @param string $url
2724
+     * @return string
2725
+     */
2726
+    public static function removeProtocolFromUrl($url) {
2727
+        if (strpos($url, 'https://') === 0) {
2728
+            return substr($url, strlen('https://'));
2729
+        } else if (strpos($url, 'http://') === 0) {
2730
+            return substr($url, strlen('http://'));
2731
+        }
2732
+
2733
+        return $url;
2734
+    }
2735
+
2736
+    /**
2737
+     * try http post first with https and then with http as a fallback
2738
+     *
2739
+     * @param string $remoteDomain
2740
+     * @param string $urlSuffix
2741
+     * @param array $fields post parameters
2742
+     * @return array
2743
+     */
2744
+    private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
2745
+        $protocol = 'https://';
2746
+        $result = [
2747
+            'success' => false,
2748
+            'result' => '',
2749
+        ];
2750
+        $try = 0;
2751
+        $discoveryManager = new DiscoveryManager(
2752
+            \OC::$server->getMemCacheFactory(),
2753
+            \OC::$server->getHTTPClientService()
2754
+        );
2755
+        while ($result['success'] === false && $try < 2) {
2756
+            $endpoint = $discoveryManager->getShareEndpoint($protocol . $remoteDomain);
2757
+            $result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields);
2758
+            $try++;
2759
+            $protocol = 'http://';
2760
+        }
2761
+
2762
+        return $result;
2763
+    }
2764
+
2765
+    /**
2766
+     * send server-to-server share to remote server
2767
+     *
2768
+     * @param string $token
2769
+     * @param string $shareWith
2770
+     * @param string $name
2771
+     * @param int $remote_id
2772
+     * @param string $owner
2773
+     * @return bool
2774
+     */
2775
+    private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
2776
+
2777
+        list($user, $remote) = Helper::splitUserRemote($shareWith);
2778
+
2779
+        if ($user && $remote) {
2780
+            $url = $remote;
2781
+
2782
+            $local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
2783
+
2784
+            $fields = array(
2785
+                'shareWith' => $user,
2786
+                'token' => $token,
2787
+                'name' => $name,
2788
+                'remoteId' => $remote_id,
2789
+                'owner' => $owner,
2790
+                'remote' => $local,
2791
+            );
2792
+
2793
+            $url = self::removeProtocolFromUrl($url);
2794
+            $result = self::tryHttpPostToShareEndpoint($url, '', $fields);
2795
+            $status = json_decode($result['result'], true);
2796
+
2797
+            if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) {
2798
+                \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
2799
+                return true;
2800
+            }
2801
+
2802
+        }
2803
+
2804
+        return false;
2805
+    }
2806
+
2807
+    /**
2808
+     * send server-to-server unshare to remote server
2809
+     *
2810
+     * @param string $remote url
2811
+     * @param int $id share id
2812
+     * @param string $token
2813
+     * @return bool
2814
+     */
2815
+    private static function sendRemoteUnshare($remote, $id, $token) {
2816
+        $url = rtrim($remote, '/');
2817
+        $fields = array('token' => $token, 'format' => 'json');
2818
+        $url = self::removeProtocolFromUrl($url);
2819
+        $result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
2820
+        $status = json_decode($result['result'], true);
2821
+
2822
+        return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
2823
+    }
2824
+
2825
+    /**
2826
+     * check if user can only share with group members
2827
+     * @return bool
2828
+     */
2829
+    public static function shareWithGroupMembersOnly() {
2830
+        $value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_group_members', 'no');
2831
+        return ($value === 'yes') ? true : false;
2832
+    }
2833
+
2834
+    /**
2835
+     * @return bool
2836
+     */
2837
+    public static function isDefaultExpireDateEnabled() {
2838
+        $defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
2839
+        return ($defaultExpireDateEnabled === "yes") ? true : false;
2840
+    }
2841
+
2842
+    /**
2843
+     * @return bool
2844
+     */
2845
+    public static function enforceDefaultExpireDate() {
2846
+        $enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
2847
+        return ($enforceDefaultExpireDate === "yes") ? true : false;
2848
+    }
2849
+
2850
+    /**
2851
+     * @return int
2852
+     */
2853
+    public static function getExpireInterval() {
2854
+        return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2855
+    }
2856
+
2857
+    /**
2858
+     * Checks whether the given path is reachable for the given owner
2859
+     *
2860
+     * @param string $path path relative to files
2861
+     * @param string $ownerStorageId storage id of the owner
2862
+     *
2863
+     * @return boolean true if file is reachable, false otherwise
2864
+     */
2865
+    private static function isFileReachable($path, $ownerStorageId) {
2866
+        // if outside the home storage, file is always considered reachable
2867
+        if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
2868
+            substr($ownerStorageId, 0, 13) === 'object::user:'
2869
+        )) {
2870
+            return true;
2871
+        }
2872
+
2873
+        // if inside the home storage, the file has to be under "/files/"
2874
+        $path = ltrim($path, '/');
2875
+        if (substr($path, 0, 6) === 'files/') {
2876
+            return true;
2877
+        }
2878
+
2879
+        return false;
2880
+    }
2881
+
2882
+    /**
2883
+     * @param IConfig $config
2884
+     * @return bool 
2885
+     */
2886
+    public static function enforcePassword(IConfig $config) {
2887
+        $enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
2888
+        return ($enforcePassword === "yes") ? true : false;
2889
+    }
2890
+
2891
+    /**
2892
+     * Get all share entries, including non-unique group items
2893
+     *
2894
+     * @param string $owner
2895
+     * @return array
2896
+     */
2897
+    public static function getAllSharesForOwner($owner) {
2898
+        $query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?';
2899
+        $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]);
2900
+        return $result->fetchAll();
2901
+    }
2902
+
2903
+    /**
2904
+     * Get all share entries, including non-unique group items for a file
2905
+     *
2906
+     * @param int $id
2907
+     * @return array
2908
+     */
2909
+    public static function getAllSharesForFileId($id) {
2910
+        $query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?';
2911
+        $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
2912
+        return $result->fetchAll();
2913
+    }
2914
+
2915
+    /**
2916
+     * @param string $password
2917
+     * @throws \Exception
2918
+     */
2919
+    private static function verifyPassword($password) {
2920
+
2921
+        $accepted = true;
2922
+        $message = '';
2923
+        \OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
2924
+            'password' => $password,
2925
+            'accepted' => &$accepted,
2926
+            'message' => &$message
2927
+        ]);
2928
+
2929
+        if (!$accepted) {
2930
+            throw new \Exception($message);
2931
+        }
2932
+    }
2933 2933
 }
Please login to merge, or discard this patch.
Spacing   +76 added lines, -76 removed lines patch added patch discarded remove patch
@@ -91,7 +91,7 @@  discard block
 block discarded – undo
91 91
 					'collectionOf' => $collectionOf,
92 92
 					'supportedFileExtensions' => $supportedFileExtensions
93 93
 				);
94
-				if(count(self::$backendTypes) === 1) {
94
+				if (count(self::$backendTypes) === 1) {
95 95
 					\OC_Util::addScript('core', 'shareconfigmodel');
96 96
 					\OC_Util::addScript('core', 'shareitemmodel');
97 97
 					\OC_Util::addScript('core', 'sharedialogresharerinfoview');
@@ -170,22 +170,22 @@  discard block
 block discarded – undo
170 170
 		$source = -1;
171 171
 		$cache = $mountPath = false;
172 172
 
173
-		$view = new \OC\Files\View('/' . $ownerUser . '/files');
173
+		$view = new \OC\Files\View('/'.$ownerUser.'/files');
174 174
 		$meta = $view->getFileInfo($path);
175 175
 		if ($meta) {
176
-			$path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
176
+			$path = substr($meta->getPath(), strlen('/'.$ownerUser.'/files'));
177 177
 		} else {
178 178
 			// if the file doesn't exists yet we start with the parent folder
179 179
 			$meta = $view->getFileInfo(dirname($path));
180 180
 		}
181 181
 
182
-		if($meta !== false) {
182
+		if ($meta !== false) {
183 183
 			$source = $meta['fileid'];
184 184
 			$cache = new \OC\Files\Cache\Cache($meta['storage']);
185 185
 
186 186
 			$mountPath = $meta->getMountPoint()->getMountPoint();
187 187
 			if ($mountPath !== false) {
188
-				$mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files'));
188
+				$mountPath = substr($mountPath, strlen('/'.$ownerUser.'/files'));
189 189
 			}
190 190
 		}
191 191
 
@@ -303,10 +303,10 @@  discard block
 block discarded – undo
303 303
 			}
304 304
 
305 305
 			// let's get the parent for the next round
306
-			$meta = $cache->get((int)$source);
306
+			$meta = $cache->get((int) $source);
307 307
 			if ($recursive === true && $meta !== false) {
308 308
 				$paths[$source] = $meta['path'];
309
-				$source = (int)$meta['parent'];
309
+				$source = (int) $meta['parent'];
310 310
 			} else {
311 311
 				$source = -1;
312 312
 			}
@@ -325,7 +325,7 @@  discard block
 block discarded – undo
325 325
 				$query = \OC_DB::prepare(
326 326
 					'SELECT `fileid`, `path`
327 327
 					FROM `*PREFIX*filecache`
328
-					WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')'
328
+					WHERE `fileid` IN (' . implode(',', $fileTargetIDs).')'
329 329
 				);
330 330
 				$result = $query->execute();
331 331
 
@@ -340,7 +340,7 @@  discard block
 block discarded – undo
340 340
 								$sharePaths[$uid] = $sharedPath;
341 341
 							} else {
342 342
 								$sharedPath = $shareData['file_target'];
343
-								$sharedPath .= substr($path, strlen($row['path']) -5);
343
+								$sharedPath .= substr($path, strlen($row['path']) - 5);
344 344
 								$sharePaths[$uid] = $sharedPath;
345 345
 							}
346 346
 						}
@@ -432,7 +432,7 @@  discard block
 block discarded – undo
432 432
 
433 433
 		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
434 434
 
435
-		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
435
+		$where .= ' `'.$column.'` = ? AND `item_type` = ? ';
436 436
 		$arguments = array($itemSource, $itemType);
437 437
 		// for link shares $user === null
438 438
 		if ($user !== null) {
@@ -450,7 +450,7 @@  discard block
 block discarded – undo
450 450
 			$arguments[] = $owner;
451 451
 		}
452 452
 
453
-		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
453
+		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$fileDependentWhere.$where);
454 454
 
455 455
 		$result = \OC_DB::executeAudited($query, $arguments);
456 456
 
@@ -458,7 +458,7 @@  discard block
 block discarded – undo
458 458
 			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
459 459
 				continue;
460 460
 			}
461
-			if ($fileDependent && (int)$row['file_parent'] === -1) {
461
+			if ($fileDependent && (int) $row['file_parent'] === -1) {
462 462
 				// if it is a mount point we need to get the path from the mount manager
463 463
 				$mountManager = \OC\Files\Filesystem::getMountManager();
464 464
 				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
 					$row['path'] = $path;
470 470
 				} else {
471 471
 					\OC::$server->getLogger()->warning(
472
-						'Could not resolve mount point for ' . $row['storage_id'],
472
+						'Could not resolve mount point for '.$row['storage_id'],
473 473
 						['app' => 'OCP\Share']
474 474
 					);
475 475
 				}
@@ -478,7 +478,7 @@  discard block
 block discarded – undo
478 478
 		}
479 479
 
480 480
 		//if didn't found a result than let's look for a group share.
481
-		if(empty($shares) && $user !== null) {
481
+		if (empty($shares) && $user !== null) {
482 482
 			$userObject = \OC::$server->getUserManager()->get($user);
483 483
 			$groups = [];
484 484
 			if ($userObject) {
@@ -486,7 +486,7 @@  discard block
 block discarded – undo
486 486
 			}
487 487
 
488 488
 			if (!empty($groups)) {
489
-				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
489
+				$where = $fileDependentWhere.' WHERE `'.$column.'` = ? AND `item_type` = ? AND `share_with` in (?)';
490 490
 				$arguments = array($itemSource, $itemType, $groups);
491 491
 				$types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY);
492 492
 
@@ -500,7 +500,7 @@  discard block
 block discarded – undo
500 500
 				// class isn't static anymore...
501 501
 				$conn = \OC::$server->getDatabaseConnection();
502 502
 				$result = $conn->executeQuery(
503
-					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
503
+					'SELECT '.$select.' FROM `*PREFIX*share` '.$where,
504 504
 					$arguments,
505 505
 					$types
506 506
 				);
@@ -554,7 +554,7 @@  discard block
 block discarded – undo
554 554
 		$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
555 555
 		$result = $query->execute(array($token));
556 556
 		if ($result === false) {
557
-			\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR);
557
+			\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage().', token='.$token, \OCP\Util::ERROR);
558 558
 		}
559 559
 		$row = $result->fetchRow();
560 560
 		if ($row === false) {
@@ -640,9 +640,9 @@  discard block
 block discarded – undo
640 640
 		$items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
641 641
 		if ($items) {
642 642
 			foreach ($items as $item) {
643
-				if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
643
+				if ((int) $item['share_type'] === self::SHARE_TYPE_USER) {
644 644
 					$users[] = $item['share_with'];
645
-				} else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
645
+				} else if ((int) $item['share_type'] === self::SHARE_TYPE_GROUP) {
646 646
 
647 647
 					$group = \OC::$server->getGroupManager()->get($item['share_with']);
648 648
 					$userIds = [];
@@ -718,12 +718,12 @@  discard block
 block discarded – undo
718 718
 
719 719
 		//verify that we don't share a folder which already contains a share mount point
720 720
 		if ($itemType === 'folder') {
721
-			$path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
721
+			$path = '/'.$uidOwner.'/files'.\OC\Files\Filesystem::getPath($itemSource).'/';
722 722
 			$mountManager = \OC\Files\Filesystem::getMountManager();
723 723
 			$mounts = $mountManager->findIn($path);
724 724
 			foreach ($mounts as $mount) {
725 725
 				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
726
-					$message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
726
+					$message = 'Sharing "'.$itemSourceName.'" failed, because it contains files shared with you!';
727 727
 					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
728 728
 					throw new \Exception($message);
729 729
 				}
@@ -733,7 +733,7 @@  discard block
 block discarded – undo
733 733
 
734 734
 		// single file shares should never have delete permissions
735 735
 		if ($itemType === 'file') {
736
-			$permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
736
+			$permissions = (int) $permissions & ~\OCP\Constants::PERMISSION_DELETE;
737 737
 		}
738 738
 
739 739
 		//Validate expirationDate
@@ -887,7 +887,7 @@  discard block
 block discarded – undo
887 887
 					} else {
888 888
 						// reuse the already set password, but only if we change permissions
889 889
 						// otherwise the user disabled the password protection
890
-						if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
890
+						if ($checkExists && (int) $permissions !== (int) $oldPermissions) {
891 891
 							$shareWith = $checkExists['share_with'];
892 892
 						}
893 893
 					}
@@ -960,10 +960,10 @@  discard block
 block discarded – undo
960 960
 				throw new \Exception($message_t);
961 961
 			}
962 962
 
963
-			$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
963
+			$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_UPPER.
964 964
 				\OCP\Security\ISecureRandom::CHAR_DIGITS);
965 965
 
966
-			$shareWith = $user . '@' . $remote;
966
+			$shareWith = $user.'@'.$remote;
967 967
 			$shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
968 968
 
969 969
 			$send = false;
@@ -1014,12 +1014,12 @@  discard block
 block discarded – undo
1014 1014
 		$currentUser = $owner ? $owner : \OC_User::getUser();
1015 1015
 		foreach ($items as $item) {
1016 1016
 			// delete the item with the expected share_type and owner
1017
-			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
1017
+			if ((int) $item['share_type'] === (int) $shareType && $item['uid_owner'] === $currentUser) {
1018 1018
 				$toDelete = $item;
1019 1019
 				// if there is more then one result we don't have to delete the children
1020 1020
 				// but update their parent. For group shares the new parent should always be
1021 1021
 				// the original group share and not the db entry with the unique name
1022
-			} else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
1022
+			} else if ((int) $item['share_type'] === self::$shareTypeGroupUserUnique) {
1023 1023
 				$newParent = $item['parent'];
1024 1024
 			} else {
1025 1025
 				$newParent = $item['id'];
@@ -1041,7 +1041,7 @@  discard block
 block discarded – undo
1041 1041
 	 */
1042 1042
 	public static function unshareAll($itemType, $itemSource) {
1043 1043
 		// Get all of the owners of shares of this item.
1044
-		$query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
1044
+		$query = \OC_DB::prepare('SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?');
1045 1045
 		$result = $query->execute(array($itemType, $itemSource));
1046 1046
 		$shares = array();
1047 1047
 		// Add each owner's shares to the array of all shares for this item.
@@ -1079,9 +1079,9 @@  discard block
 block discarded – undo
1079 1079
 		$uid = \OCP\User::getUser();
1080 1080
 
1081 1081
 		if ($itemType === 'file' || $itemType === 'folder') {
1082
-			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
1082
+			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_'.$originType.'` = ?';
1083 1083
 		} else {
1084
-			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
1084
+			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_'.$originType.'` = ?';
1085 1085
 		}
1086 1086
 
1087 1087
 		$query = \OCP\DB::prepare($statement);
@@ -1093,7 +1093,7 @@  discard block
 block discarded – undo
1093 1093
 
1094 1094
 		$itemUnshared = false;
1095 1095
 		foreach ($shares as $share) {
1096
-			if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1096
+			if ((int) $share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1097 1097
 				$share['share_with'] === $uid) {
1098 1098
 				$deletedShares = Helper::delete($share['id']);
1099 1099
 				$shareTmp = array(
@@ -1101,7 +1101,7 @@  discard block
 block discarded – undo
1101 1101
 					'shareWith' => $share['share_with'],
1102 1102
 					'itemTarget' => $share['item_target'],
1103 1103
 					'itemType' => $share['item_type'],
1104
-					'shareType' => (int)$share['share_type'],
1104
+					'shareType' => (int) $share['share_type'],
1105 1105
 				);
1106 1106
 				if (isset($share['file_target'])) {
1107 1107
 					$shareTmp['fileTarget'] = $share['file_target'];
@@ -1109,13 +1109,13 @@  discard block
 block discarded – undo
1109 1109
 				$listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp));
1110 1110
 				$itemUnshared = true;
1111 1111
 				break;
1112
-			} elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1112
+			} elseif ((int) $share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1113 1113
 				$group = \OC::$server->getGroupManager()->get($share['share_with']);
1114 1114
 				$user = \OC::$server->getUserManager()->get($uid);
1115 1115
 				if ($group && $user && $group->inGroup($user)) {
1116 1116
 					$groupShare = $share;
1117 1117
 				}
1118
-			} elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
1118
+			} elseif ((int) $share['share_type'] === self::$shareTypeGroupUserUnique &&
1119 1119
 				$share['share_with'] === $uid) {
1120 1120
 				$uniqueGroupShare = $share;
1121 1121
 			}
@@ -1135,7 +1135,7 @@  discard block
 block discarded – undo
1135 1135
 				'shareWith' => $groupShare['share_with'],
1136 1136
 				'itemTarget' => $groupShare['item_target'],
1137 1137
 				'itemType' => $groupShare['item_type'],
1138
-				'shareType' => (int)$groupShare['share_type'],
1138
+				'shareType' => (int) $groupShare['share_type'],
1139 1139
 			);
1140 1140
 			if (isset($groupShare['file_target'])) {
1141 1141
 				$shareTmp['fileTarget'] = $groupShare['file_target'];
@@ -1150,7 +1150,7 @@  discard block
 block discarded – undo
1150 1150
 				'shareWith' => $uniqueGroupShare['share_with'],
1151 1151
 				'itemTarget' => $uniqueGroupShare['item_target'],
1152 1152
 				'itemType' => $uniqueGroupShare['item_type'],
1153
-				'shareType' => (int)$uniqueGroupShare['share_type'],
1153
+				'shareType' => (int) $uniqueGroupShare['share_type'],
1154 1154
 			);
1155 1155
 			if (isset($uniqueGroupShare['file_target'])) {
1156 1156
 				$shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
@@ -1185,7 +1185,7 @@  discard block
 block discarded – undo
1185 1185
 
1186 1186
 		$result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient));
1187 1187
 
1188
-		if($result === false) {
1188
+		if ($result === false) {
1189 1189
 			\OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR);
1190 1190
 		}
1191 1191
 	}
@@ -1206,12 +1206,12 @@  discard block
 block discarded – undo
1206 1206
 
1207 1207
 		$intArrayToLiteralArray = function($intArray, $eb) {
1208 1208
 			return array_map(function($int) use ($eb) {
1209
-				return $eb->literal((int)$int, 'integer');
1209
+				return $eb->literal((int) $int, 'integer');
1210 1210
 			}, $intArray);
1211 1211
 		};
1212 1212
 		$sanitizeItem = function($item) {
1213
-			$item['id'] = (int)$item['id'];
1214
-			$item['premissions'] = (int)$item['permissions'];
1213
+			$item['id'] = (int) $item['id'];
1214
+			$item['premissions'] = (int) $item['permissions'];
1215 1215
 			return $item;
1216 1216
 		};
1217 1217
 
@@ -1229,7 +1229,7 @@  discard block
 block discarded – undo
1229 1229
 
1230 1230
 				$result = $dbresult->fetch();
1231 1231
 				$dbresult->closeCursor();
1232
-				if (~(int)$result['permissions'] & $permissions) {
1232
+				if (~(int) $result['permissions'] & $permissions) {
1233 1233
 					$message = 'Setting permissions for %s failed,'
1234 1234
 						.' because the permissions exceed permissions granted to %s';
1235 1235
 					$message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
@@ -1262,7 +1262,7 @@  discard block
 block discarded – undo
1262 1262
 			$items = [];
1263 1263
 
1264 1264
 			// Check if permissions were removed
1265
-			if ((int)$rootItem['permissions'] & ~$permissions) {
1265
+			if ((int) $rootItem['permissions'] & ~$permissions) {
1266 1266
 				// If share permission is removed all reshares must be deleted
1267 1267
 				if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1268 1268
 					// delete all shares, keep parent and group children
@@ -1315,14 +1315,14 @@  discard block
 block discarded – undo
1315 1315
 			 * Permissions were added
1316 1316
 			 * Update all USERGROUP shares. (So group shares where the user moved their mountpoint).
1317 1317
 			 */
1318
-			if ($permissions & ~(int)$rootItem['permissions']) {
1318
+			if ($permissions & ~(int) $rootItem['permissions']) {
1319 1319
 				$qb = $connection->getQueryBuilder();
1320 1320
 				$qb->select('id', 'permissions', 'item_type')
1321 1321
 					->from('share')
1322 1322
 					->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
1323 1323
 					->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
1324 1324
 					->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
1325
-					->setParameter(':parent', (int)$rootItem['id'])
1325
+					->setParameter(':parent', (int) $rootItem['id'])
1326 1326
 					->setParameter(':share_type', 2)
1327 1327
 					->setParameter(':shareDeleted', 0);
1328 1328
 				$result = $qb->execute();
@@ -1382,7 +1382,7 @@  discard block
 block discarded – undo
1382 1382
 		if ($defaultExpireDateEnforced && $shareTime === null) {
1383 1383
 			$items = self::getItemShared($itemType, $itemSource);
1384 1384
 			$firstItem = reset($items);
1385
-			$shareTime = (int)$firstItem['stime'];
1385
+			$shareTime = (int) $firstItem['stime'];
1386 1386
 		}
1387 1387
 
1388 1388
 		if ($defaultExpireDateEnforced) {
@@ -1390,9 +1390,9 @@  discard block
 block discarded – undo
1390 1390
 			$maxDate = new \DateTime();
1391 1391
 			$maxDate->setTimestamp($shareTime);
1392 1392
 			$maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1393
-			$maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1393
+			$maxDate->add(new \DateInterval('P'.$maxDays.'D'));
1394 1394
 			if ($date > $maxDate) {
1395
-				$warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1395
+				$warning = 'Cannot set expiration date. Shares cannot expire later than '.$maxDays.' after they have been shared';
1396 1396
 				$warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
1397 1397
 				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1398 1398
 				throw new \Exception($warning_t);
@@ -1572,7 +1572,7 @@  discard block
 block discarded – undo
1572 1572
 	 */
1573 1573
 	protected static function unshareItem(array $item, $newParent = null) {
1574 1574
 
1575
-		$shareType = (int)$item['share_type'];
1575
+		$shareType = (int) $item['share_type'];
1576 1576
 		$shareWith = null;
1577 1577
 		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
1578 1578
 			$shareWith = $item['share_with'];
@@ -1588,7 +1588,7 @@  discard block
 block discarded – undo
1588 1588
 			'itemParent'    => $item['parent'],
1589 1589
 			'uidOwner'      => $item['uid_owner'],
1590 1590
 		);
1591
-		if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1591
+		if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1592 1592
 			$hookParams['fileSource'] = $item['file_source'];
1593 1593
 			$hookParams['fileTarget'] = $item['file_target'];
1594 1594
 		}
@@ -1598,7 +1598,7 @@  discard block
 block discarded – undo
1598 1598
 		$deletedShares[] = $hookParams;
1599 1599
 		$hookParams['deletedShares'] = $deletedShares;
1600 1600
 		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1601
-		if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1601
+		if ((int) $item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1602 1602
 			list(, $remote) = Helper::splitUserRemote($item['share_with']);
1603 1603
 			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
1604 1604
 		}
@@ -1759,7 +1759,7 @@  discard block
 block discarded – undo
1759 1759
 		// Get filesystem root to add it to the file target and remove from the
1760 1760
 		// file source, match file_source with the file cache
1761 1761
 		if ($itemType == 'file' || $itemType == 'folder') {
1762
-			if(!is_null($uidOwner)) {
1762
+			if (!is_null($uidOwner)) {
1763 1763
 				$root = \OC\Files\Filesystem::getRoot();
1764 1764
 			} else {
1765 1765
 				$root = '';
@@ -1904,7 +1904,7 @@  discard block
 block discarded – undo
1904 1904
 		$result = $query->execute($queryArgs);
1905 1905
 		if ($result === false) {
1906 1906
 			\OCP\Util::writeLog('OCP\Share',
1907
-				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
1907
+				\OC_DB::getErrorMessage().', select='.$select.' where=',
1908 1908
 				\OCP\Util::ERROR);
1909 1909
 		}
1910 1910
 		$items = array();
@@ -1946,14 +1946,14 @@  discard block
 block discarded – undo
1946 1946
 						}
1947 1947
 						// Switch ids if sharing permission is granted on only
1948 1948
 						// one share to ensure correct parent is used if resharing
1949
-						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1950
-							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1949
+						if (~(int) $items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1950
+							&& (int) $row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1951 1951
 							$items[$row['id']] = $items[$id];
1952 1952
 							$switchedItems[$id] = $row['id'];
1953 1953
 							unset($items[$id]);
1954 1954
 							$id = $row['id'];
1955 1955
 						}
1956
-						$items[$id]['permissions'] |= (int)$row['permissions'];
1956
+						$items[$id]['permissions'] |= (int) $row['permissions'];
1957 1957
 
1958 1958
 					}
1959 1959
 					continue;
@@ -1967,8 +1967,8 @@  discard block
 block discarded – undo
1967 1967
 					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1968 1968
 					$parentResult = $query->execute(array($row['parent']));
1969 1969
 					if ($result === false) {
1970
-						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
1971
-							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
1970
+						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: '.
1971
+							\OC_DB::getErrorMessage().', select='.$select.' where='.$where,
1972 1972
 							\OCP\Util::ERROR);
1973 1973
 					} else {
1974 1974
 						$parentRow = $parentResult->fetchRow();
@@ -1978,7 +1978,7 @@  discard block
 block discarded – undo
1978 1978
 						$subPath = substr($row['path'], $pos);
1979 1979
 						$splitPath = explode('/', $subPath);
1980 1980
 						foreach (array_slice($splitPath, 2) as $pathPart) {
1981
-							$tmpPath = $tmpPath . '/' . $pathPart;
1981
+							$tmpPath = $tmpPath.'/'.$pathPart;
1982 1982
 						}
1983 1983
 						$row['path'] = $tmpPath;
1984 1984
 					}
@@ -1997,7 +1997,7 @@  discard block
 block discarded – undo
1997 1997
 				}
1998 1998
 			}
1999 1999
 
2000
-			if($checkExpireDate) {
2000
+			if ($checkExpireDate) {
2001 2001
 				if (self::expireItem($row)) {
2002 2002
 					continue;
2003 2003
 				}
@@ -2008,10 +2008,10 @@  discard block
 block discarded – undo
2008 2008
 			}
2009 2009
 			// Add display names to result
2010 2010
 			$row['share_with_displayname'] = $row['share_with'];
2011
-			if ( isset($row['share_with']) && $row['share_with'] != '' &&
2011
+			if (isset($row['share_with']) && $row['share_with'] != '' &&
2012 2012
 				$row['share_type'] === self::SHARE_TYPE_USER) {
2013 2013
 				$row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
2014
-			} else if(isset($row['share_with']) && $row['share_with'] != '' &&
2014
+			} else if (isset($row['share_with']) && $row['share_with'] != '' &&
2015 2015
 				$row['share_type'] === self::SHARE_TYPE_REMOTE) {
2016 2016
 				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
2017 2017
 				foreach ($addressBookEntries as $entry) {
@@ -2022,7 +2022,7 @@  discard block
 block discarded – undo
2022 2022
 					}
2023 2023
 				}
2024 2024
 			}
2025
-			if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
2025
+			if (isset($row['uid_owner']) && $row['uid_owner'] != '') {
2026 2026
 				$row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
2027 2027
 			}
2028 2028
 
@@ -2170,7 +2170,7 @@  discard block
 block discarded – undo
2170 2170
 				// for file/folder shares we need to compare file_source, otherwise we compare item_source
2171 2171
 				// only group shares if they already point to the same target, otherwise the file where shared
2172 2172
 				// before grouping of shares was added. In this case we don't group them toi avoid confusions
2173
-				if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2173
+				if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2174 2174
 					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
2175 2175
 					// add the first item to the list of grouped shares
2176 2176
 					if (!isset($result[$key]['grouped'])) {
@@ -2216,7 +2216,7 @@  discard block
 block discarded – undo
2216 2216
 		$groupItemTarget = $itemTarget = $fileSource = $parent = 0;
2217 2217
 
2218 2218
 		$result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
2219
-		if(!empty($result)) {
2219
+		if (!empty($result)) {
2220 2220
 			$parent = $result['parent'];
2221 2221
 			$itemSource = $result['itemSource'];
2222 2222
 			$fileSource = $result['fileSource'];
@@ -2310,11 +2310,11 @@  discard block
 block discarded – undo
2310 2310
 				$itemTarget = $sourceExists['item_target'];
2311 2311
 
2312 2312
 				// for group shares we don't need a additional entry if the target is the same
2313
-				if($isGroupShare && $groupItemTarget === $itemTarget) {
2313
+				if ($isGroupShare && $groupItemTarget === $itemTarget) {
2314 2314
 					continue;
2315 2315
 				}
2316 2316
 
2317
-			} elseif(!$sourceExists && !$isGroupShare)  {
2317
+			} elseif (!$sourceExists && !$isGroupShare) {
2318 2318
 
2319 2319
 				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
2320 2320
 					$uidOwner, $suggestedItemTarget, $parent);
@@ -2445,8 +2445,8 @@  discard block
 block discarded – undo
2445 2445
 
2446 2446
 		if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
2447 2447
 			// Check if share permissions is granted
2448
-			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2449
-				if (~(int)$checkReshare['permissions'] & $permissions) {
2448
+			if (self::isResharingAllowed() && (int) $checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2449
+				if (~(int) $checkReshare['permissions'] & $permissions) {
2450 2450
 					$message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2451 2451
 					$message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
2452 2452
 
@@ -2458,7 +2458,7 @@  discard block
 block discarded – undo
2458 2458
 
2459 2459
 					$result['expirationDate'] = $expirationDate;
2460 2460
 					// $checkReshare['expiration'] could be null and then is always less than any value
2461
-					if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2461
+					if (isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2462 2462
 						$result['expirationDate'] = $checkReshare['expiration'];
2463 2463
 					}
2464 2464
 
@@ -2550,7 +2550,7 @@  discard block
 block discarded – undo
2550 2550
 
2551 2551
 		$id = false;
2552 2552
 		if ($result) {
2553
-			$id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2553
+			$id = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2554 2554
 		}
2555 2555
 
2556 2556
 		return $id;
@@ -2590,8 +2590,8 @@  discard block
 block discarded – undo
2590 2590
 			return true;
2591 2591
 		}
2592 2592
 
2593
-		if ( \OC::$server->getSession()->exists('public_link_authenticated')
2594
-			&& \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) {
2593
+		if (\OC::$server->getSession()->exists('public_link_authenticated')
2594
+			&& \OC::$server->getSession()->get('public_link_authenticated') === (string) $linkItem['id']) {
2595 2595
 			return true;
2596 2596
 		}
2597 2597
 
@@ -2693,7 +2693,7 @@  discard block
 block discarded – undo
2693 2693
 	 * @param array $parameters additional format parameters
2694 2694
 	 * @return array format result
2695 2695
 	 */
2696
-	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2696
+	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE, $parameters = null) {
2697 2697
 		if ($format === self::FORMAT_NONE) {
2698 2698
 			return $items;
2699 2699
 		} else if ($format === self::FORMAT_STATUSES) {
@@ -2753,8 +2753,8 @@  discard block
 block discarded – undo
2753 2753
 			\OC::$server->getHTTPClientService()
2754 2754
 		);
2755 2755
 		while ($result['success'] === false && $try < 2) {
2756
-			$endpoint = $discoveryManager->getShareEndpoint($protocol . $remoteDomain);
2757
-			$result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields);
2756
+			$endpoint = $discoveryManager->getShareEndpoint($protocol.$remoteDomain);
2757
+			$result = \OC::$server->getHTTPHelper()->post($protocol.$remoteDomain.$endpoint.$urlSuffix.'?format='.self::RESPONSE_FORMAT, $fields);
2758 2758
 			$try++;
2759 2759
 			$protocol = 'http://';
2760 2760
 		}
@@ -2851,7 +2851,7 @@  discard block
 block discarded – undo
2851 2851
 	 * @return int
2852 2852
 	 */
2853 2853
 	public static function getExpireInterval() {
2854
-		return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2854
+		return (int) \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2855 2855
 	}
2856 2856
 
2857 2857
 	/**
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Request.php 2 patches
Indentation   +806 added lines, -806 removed lines patch added patch discarded remove patch
@@ -55,811 +55,811 @@
 block discarded – undo
55 55
  */
56 56
 class Request implements \ArrayAccess, \Countable, IRequest {
57 57
 
58
-	const USER_AGENT_IE = '/(MSIE)|(Trident)/';
59
-	// Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
60
-	const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
61
-	// Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
62
-	const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
63
-	// Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
64
-	const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+$/';
65
-	// Safari User Agent from http://www.useragentstring.com/pages/Safari/
66
-	const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
67
-	// Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
68
-	const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
69
-	const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
70
-	const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/';
71
-
72
-	/**
73
-	 * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_IOS instead
74
-	 */
75
-	const USER_AGENT_OWNCLOUD_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/';
76
-	/**
77
-	 * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_ANDROID instead
78
-	 */
79
-	const USER_AGENT_OWNCLOUD_ANDROID = '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/';
80
-	/**
81
-	 * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_DESKTOP instead
82
-	 */
83
-	const USER_AGENT_OWNCLOUD_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/';
84
-
85
-	protected $inputStream;
86
-	protected $content;
87
-	protected $items = array();
88
-	protected $allowedKeys = array(
89
-		'get',
90
-		'post',
91
-		'files',
92
-		'server',
93
-		'env',
94
-		'cookies',
95
-		'urlParams',
96
-		'parameters',
97
-		'method',
98
-		'requesttoken',
99
-	);
100
-	/** @var ISecureRandom */
101
-	protected $secureRandom;
102
-	/** @var IConfig */
103
-	protected $config;
104
-	/** @var string */
105
-	protected $requestId = '';
106
-	/** @var ICrypto */
107
-	protected $crypto;
108
-	/** @var CsrfTokenManager|null */
109
-	protected $csrfTokenManager;
110
-
111
-	/** @var bool */
112
-	protected $contentDecoded = false;
113
-
114
-	/**
115
-	 * @param array $vars An associative array with the following optional values:
116
-	 *        - array 'urlParams' the parameters which were matched from the URL
117
-	 *        - array 'get' the $_GET array
118
-	 *        - array|string 'post' the $_POST array or JSON string
119
-	 *        - array 'files' the $_FILES array
120
-	 *        - array 'server' the $_SERVER array
121
-	 *        - array 'env' the $_ENV array
122
-	 *        - array 'cookies' the $_COOKIE array
123
-	 *        - string 'method' the request method (GET, POST etc)
124
-	 *        - string|false 'requesttoken' the requesttoken or false when not available
125
-	 * @param ISecureRandom $secureRandom
126
-	 * @param IConfig $config
127
-	 * @param CsrfTokenManager|null $csrfTokenManager
128
-	 * @param string $stream
129
-	 * @see http://www.php.net/manual/en/reserved.variables.php
130
-	 */
131
-	public function __construct(array $vars=array(),
132
-								ISecureRandom $secureRandom = null,
133
-								IConfig $config,
134
-								CsrfTokenManager $csrfTokenManager = null,
135
-								$stream = 'php://input') {
136
-		$this->inputStream = $stream;
137
-		$this->items['params'] = array();
138
-		$this->secureRandom = $secureRandom;
139
-		$this->config = $config;
140
-		$this->csrfTokenManager = $csrfTokenManager;
141
-
142
-		if(!array_key_exists('method', $vars)) {
143
-			$vars['method'] = 'GET';
144
-		}
145
-
146
-		foreach($this->allowedKeys as $name) {
147
-			$this->items[$name] = isset($vars[$name])
148
-				? $vars[$name]
149
-				: array();
150
-		}
151
-
152
-		$this->items['parameters'] = array_merge(
153
-			$this->items['get'],
154
-			$this->items['post'],
155
-			$this->items['urlParams'],
156
-			$this->items['params']
157
-		);
158
-
159
-	}
160
-	/**
161
-	 * @param array $parameters
162
-	 */
163
-	public function setUrlParameters(array $parameters) {
164
-		$this->items['urlParams'] = $parameters;
165
-		$this->items['parameters'] = array_merge(
166
-			$this->items['parameters'],
167
-			$this->items['urlParams']
168
-		);
169
-	}
170
-
171
-	/**
172
-	 * Countable method
173
-	 * @return int
174
-	 */
175
-	public function count() {
176
-		return count(array_keys($this->items['parameters']));
177
-	}
178
-
179
-	/**
180
-	* ArrayAccess methods
181
-	*
182
-	* Gives access to the combined GET, POST and urlParams arrays
183
-	*
184
-	* Examples:
185
-	*
186
-	* $var = $request['myvar'];
187
-	*
188
-	* or
189
-	*
190
-	* if(!isset($request['myvar']) {
191
-	* 	// Do something
192
-	* }
193
-	*
194
-	* $request['myvar'] = 'something'; // This throws an exception.
195
-	*
196
-	* @param string $offset The key to lookup
197
-	* @return boolean
198
-	*/
199
-	public function offsetExists($offset) {
200
-		return isset($this->items['parameters'][$offset]);
201
-	}
202
-
203
-	/**
204
-	* @see offsetExists
205
-	*/
206
-	public function offsetGet($offset) {
207
-		return isset($this->items['parameters'][$offset])
208
-			? $this->items['parameters'][$offset]
209
-			: null;
210
-	}
211
-
212
-	/**
213
-	* @see offsetExists
214
-	*/
215
-	public function offsetSet($offset, $value) {
216
-		throw new \RuntimeException('You cannot change the contents of the request object');
217
-	}
218
-
219
-	/**
220
-	* @see offsetExists
221
-	*/
222
-	public function offsetUnset($offset) {
223
-		throw new \RuntimeException('You cannot change the contents of the request object');
224
-	}
225
-
226
-	/**
227
-	 * Magic property accessors
228
-	 * @param string $name
229
-	 * @param mixed $value
230
-	 */
231
-	public function __set($name, $value) {
232
-		throw new \RuntimeException('You cannot change the contents of the request object');
233
-	}
234
-
235
-	/**
236
-	* Access request variables by method and name.
237
-	* Examples:
238
-	*
239
-	* $request->post['myvar']; // Only look for POST variables
240
-	* $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
241
-	* Looks in the combined GET, POST and urlParams array.
242
-	*
243
-	* If you access e.g. ->post but the current HTTP request method
244
-	* is GET a \LogicException will be thrown.
245
-	*
246
-	* @param string $name The key to look for.
247
-	* @throws \LogicException
248
-	* @return mixed|null
249
-	*/
250
-	public function __get($name) {
251
-		switch($name) {
252
-			case 'put':
253
-			case 'patch':
254
-			case 'get':
255
-			case 'post':
256
-				if($this->method !== strtoupper($name)) {
257
-					throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
258
-				}
259
-				return $this->getContent();
260
-			case 'files':
261
-			case 'server':
262
-			case 'env':
263
-			case 'cookies':
264
-			case 'urlParams':
265
-			case 'method':
266
-				return isset($this->items[$name])
267
-					? $this->items[$name]
268
-					: null;
269
-			case 'parameters':
270
-			case 'params':
271
-				return $this->getContent();
272
-			default;
273
-				return isset($this[$name])
274
-					? $this[$name]
275
-					: null;
276
-		}
277
-	}
278
-
279
-	/**
280
-	 * @param string $name
281
-	 * @return bool
282
-	 */
283
-	public function __isset($name) {
284
-		if (in_array($name, $this->allowedKeys, true)) {
285
-			return true;
286
-		}
287
-		return isset($this->items['parameters'][$name]);
288
-	}
289
-
290
-	/**
291
-	 * @param string $id
292
-	 */
293
-	public function __unset($id) {
294
-		throw new \RuntimeException('You cannot change the contents of the request object');
295
-	}
296
-
297
-	/**
298
-	 * Returns the value for a specific http header.
299
-	 *
300
-	 * This method returns null if the header did not exist.
301
-	 *
302
-	 * @param string $name
303
-	 * @return string
304
-	 */
305
-	public function getHeader($name) {
306
-
307
-		$name = strtoupper(str_replace(array('-'),array('_'),$name));
308
-		if (isset($this->server['HTTP_' . $name])) {
309
-			return $this->server['HTTP_' . $name];
310
-		}
311
-
312
-		// There's a few headers that seem to end up in the top-level
313
-		// server array.
314
-		switch($name) {
315
-			case 'CONTENT_TYPE' :
316
-			case 'CONTENT_LENGTH' :
317
-				if (isset($this->server[$name])) {
318
-					return $this->server[$name];
319
-				}
320
-				break;
321
-
322
-		}
323
-
324
-		return null;
325
-	}
326
-
327
-	/**
328
-	 * Lets you access post and get parameters by the index
329
-	 * In case of json requests the encoded json body is accessed
330
-	 *
331
-	 * @param string $key the key which you want to access in the URL Parameter
332
-	 *                     placeholder, $_POST or $_GET array.
333
-	 *                     The priority how they're returned is the following:
334
-	 *                     1. URL parameters
335
-	 *                     2. POST parameters
336
-	 *                     3. GET parameters
337
-	 * @param mixed $default If the key is not found, this value will be returned
338
-	 * @return mixed the content of the array
339
-	 */
340
-	public function getParam($key, $default = null) {
341
-		return isset($this->parameters[$key])
342
-			? $this->parameters[$key]
343
-			: $default;
344
-	}
345
-
346
-	/**
347
-	 * Returns all params that were received, be it from the request
348
-	 * (as GET or POST) or throuh the URL by the route
349
-	 * @return array the array with all parameters
350
-	 */
351
-	public function getParams() {
352
-		return $this->parameters;
353
-	}
354
-
355
-	/**
356
-	 * Returns the method of the request
357
-	 * @return string the method of the request (POST, GET, etc)
358
-	 */
359
-	public function getMethod() {
360
-		return $this->method;
361
-	}
362
-
363
-	/**
364
-	 * Shortcut for accessing an uploaded file through the $_FILES array
365
-	 * @param string $key the key that will be taken from the $_FILES array
366
-	 * @return array the file in the $_FILES element
367
-	 */
368
-	public function getUploadedFile($key) {
369
-		return isset($this->files[$key]) ? $this->files[$key] : null;
370
-	}
371
-
372
-	/**
373
-	 * Shortcut for getting env variables
374
-	 * @param string $key the key that will be taken from the $_ENV array
375
-	 * @return array the value in the $_ENV element
376
-	 */
377
-	public function getEnv($key) {
378
-		return isset($this->env[$key]) ? $this->env[$key] : null;
379
-	}
380
-
381
-	/**
382
-	 * Shortcut for getting cookie variables
383
-	 * @param string $key the key that will be taken from the $_COOKIE array
384
-	 * @return string the value in the $_COOKIE element
385
-	 */
386
-	public function getCookie($key) {
387
-		return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
388
-	}
389
-
390
-	/**
391
-	 * Returns the request body content.
392
-	 *
393
-	 * If the HTTP request method is PUT and the body
394
-	 * not application/x-www-form-urlencoded or application/json a stream
395
-	 * resource is returned, otherwise an array.
396
-	 *
397
-	 * @return array|string|resource The request body content or a resource to read the body stream.
398
-	 *
399
-	 * @throws \LogicException
400
-	 */
401
-	protected function getContent() {
402
-		// If the content can't be parsed into an array then return a stream resource.
403
-		if ($this->method === 'PUT'
404
-			&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
405
-			&& strpos($this->getHeader('Content-Type'), 'application/json') === false
406
-		) {
407
-			if ($this->content === false) {
408
-				throw new \LogicException(
409
-					'"put" can only be accessed once if not '
410
-					. 'application/x-www-form-urlencoded or application/json.'
411
-				);
412
-			}
413
-			$this->content = false;
414
-			return fopen($this->inputStream, 'rb');
415
-		} else {
416
-			$this->decodeContent();
417
-			return $this->items['parameters'];
418
-		}
419
-	}
420
-
421
-	/**
422
-	 * Attempt to decode the content and populate parameters
423
-	 */
424
-	protected function decodeContent() {
425
-		if ($this->contentDecoded) {
426
-			return;
427
-		}
428
-		$params = [];
429
-
430
-		// 'application/json' must be decoded manually.
431
-		if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
432
-			$params = json_decode(file_get_contents($this->inputStream), true);
433
-			if(count($params) > 0) {
434
-				$this->items['params'] = $params;
435
-				if($this->method === 'POST') {
436
-					$this->items['post'] = $params;
437
-				}
438
-			}
439
-
440
-		// Handle application/x-www-form-urlencoded for methods other than GET
441
-		// or post correctly
442
-		} elseif($this->method !== 'GET'
443
-				&& $this->method !== 'POST'
444
-				&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
445
-
446
-			parse_str(file_get_contents($this->inputStream), $params);
447
-			if(is_array($params)) {
448
-				$this->items['params'] = $params;
449
-			}
450
-		}
451
-
452
-		if (is_array($params)) {
453
-			$this->items['parameters'] = array_merge($this->items['parameters'], $params);
454
-		}
455
-		$this->contentDecoded = true;
456
-	}
457
-
458
-
459
-	/**
460
-	 * Checks if the CSRF check was correct
461
-	 * @return bool true if CSRF check passed
462
-	 */
463
-	public function passesCSRFCheck() {
464
-		if($this->csrfTokenManager === null) {
465
-			return false;
466
-		}
467
-
468
-		if(!$this->passesStrictCookieCheck()) {
469
-			return false;
470
-		}
471
-
472
-		if (isset($this->items['get']['requesttoken'])) {
473
-			$token = $this->items['get']['requesttoken'];
474
-		} elseif (isset($this->items['post']['requesttoken'])) {
475
-			$token = $this->items['post']['requesttoken'];
476
-		} elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
477
-			$token = $this->items['server']['HTTP_REQUESTTOKEN'];
478
-		} else {
479
-			//no token found.
480
-			return false;
481
-		}
482
-		$token = new CsrfToken($token);
483
-
484
-		return $this->csrfTokenManager->isTokenValid($token);
485
-	}
486
-
487
-	/**
488
-	 * Whether the cookie checks are required
489
-	 *
490
-	 * @return bool
491
-	 */
492
-	private function cookieCheckRequired() {
493
-		if ($this->getHeader('OCS-APIREQUEST')) {
494
-			return false;
495
-		}
496
-		if($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
497
-			return false;
498
-		}
499
-
500
-		return true;
501
-	}
502
-
503
-	/**
504
-	 * Wrapper around session_get_cookie_params
505
-	 *
506
-	 * @return array
507
-	 */
508
-	protected function getCookieParams() {
509
-		return session_get_cookie_params();
510
-	}
511
-
512
-	/**
513
-	 * Appends the __Host- prefix to the cookie if applicable
514
-	 *
515
-	 * @param string $name
516
-	 * @return string
517
-	 */
518
-	protected function getProtectedCookieName($name) {
519
-		$cookieParams = $this->getCookieParams();
520
-		$prefix = '';
521
-		if($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
522
-			$prefix = '__Host-';
523
-		}
524
-
525
-		return $prefix.$name;
526
-	}
527
-
528
-	/**
529
-	 * Checks if the strict cookie has been sent with the request if the request
530
-	 * is including any cookies.
531
-	 *
532
-	 * @return bool
533
-	 * @since 9.1.0
534
-	 */
535
-	public function passesStrictCookieCheck() {
536
-		if(!$this->cookieCheckRequired()) {
537
-			return true;
538
-		}
539
-
540
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
541
-		if($this->getCookie($cookieName) === 'true'
542
-			&& $this->passesLaxCookieCheck()) {
543
-			return true;
544
-		}
545
-		return false;
546
-	}
547
-
548
-	/**
549
-	 * Checks if the lax cookie has been sent with the request if the request
550
-	 * is including any cookies.
551
-	 *
552
-	 * @return bool
553
-	 * @since 9.1.0
554
-	 */
555
-	public function passesLaxCookieCheck() {
556
-		if(!$this->cookieCheckRequired()) {
557
-			return true;
558
-		}
559
-
560
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
561
-		if($this->getCookie($cookieName) === 'true') {
562
-			return true;
563
-		}
564
-		return false;
565
-	}
566
-
567
-
568
-	/**
569
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
570
-	 * If `mod_unique_id` is installed this value will be taken.
571
-	 * @return string
572
-	 */
573
-	public function getId() {
574
-		if(isset($this->server['UNIQUE_ID'])) {
575
-			return $this->server['UNIQUE_ID'];
576
-		}
577
-
578
-		if(empty($this->requestId)) {
579
-			$this->requestId = $this->secureRandom->generate(20);
580
-		}
581
-
582
-		return $this->requestId;
583
-	}
584
-
585
-	/**
586
-	 * Returns the remote address, if the connection came from a trusted proxy
587
-	 * and `forwarded_for_headers` has been configured then the IP address
588
-	 * specified in this header will be returned instead.
589
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
590
-	 * @return string IP address
591
-	 */
592
-	public function getRemoteAddress() {
593
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
594
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
595
-
596
-		if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
597
-			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
598
-				'HTTP_X_FORWARDED_FOR'
599
-				// only have one default, so we cannot ship an insecure product out of the box
600
-			]);
601
-
602
-			foreach($forwardedForHeaders as $header) {
603
-				if(isset($this->server[$header])) {
604
-					foreach(explode(',', $this->server[$header]) as $IP) {
605
-						$IP = trim($IP);
606
-						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
607
-							return $IP;
608
-						}
609
-					}
610
-				}
611
-			}
612
-		}
613
-
614
-		return $remoteAddress;
615
-	}
616
-
617
-	/**
618
-	 * Check overwrite condition
619
-	 * @param string $type
620
-	 * @return bool
621
-	 */
622
-	private function isOverwriteCondition($type = '') {
623
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
624
-		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
625
-		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
626
-		|| $type !== 'protocol';
627
-	}
628
-
629
-	/**
630
-	 * Returns the server protocol. It respects one or more reverse proxies servers
631
-	 * and load balancers
632
-	 * @return string Server protocol (http or https)
633
-	 */
634
-	public function getServerProtocol() {
635
-		if($this->config->getSystemValue('overwriteprotocol') !== ''
636
-			&& $this->isOverwriteCondition('protocol')) {
637
-			return $this->config->getSystemValue('overwriteprotocol');
638
-		}
639
-
640
-		if (isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
641
-			if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
642
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
643
-				$proto = strtolower(trim($parts[0]));
644
-			} else {
645
-				$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
646
-			}
647
-
648
-			// Verify that the protocol is always HTTP or HTTPS
649
-			// default to http if an invalid value is provided
650
-			return $proto === 'https' ? 'https' : 'http';
651
-		}
652
-
653
-		if (isset($this->server['HTTPS'])
654
-			&& $this->server['HTTPS'] !== null
655
-			&& $this->server['HTTPS'] !== 'off'
656
-			&& $this->server['HTTPS'] !== '') {
657
-			return 'https';
658
-		}
659
-
660
-		return 'http';
661
-	}
662
-
663
-	/**
664
-	 * Returns the used HTTP protocol.
665
-	 *
666
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
667
-	 */
668
-	public function getHttpProtocol() {
669
-		$claimedProtocol = strtoupper($this->server['SERVER_PROTOCOL']);
670
-
671
-		$validProtocols = [
672
-			'HTTP/1.0',
673
-			'HTTP/1.1',
674
-			'HTTP/2',
675
-		];
676
-
677
-		if(in_array($claimedProtocol, $validProtocols, true)) {
678
-			return $claimedProtocol;
679
-		}
680
-
681
-		return 'HTTP/1.1';
682
-	}
683
-
684
-	/**
685
-	 * Returns the request uri, even if the website uses one or more
686
-	 * reverse proxies
687
-	 * @return string
688
-	 */
689
-	public function getRequestUri() {
690
-		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
691
-		if($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
692
-			$uri = $this->getScriptName() . substr($uri, strlen($this->server['SCRIPT_NAME']));
693
-		}
694
-		return $uri;
695
-	}
696
-
697
-	/**
698
-	 * Get raw PathInfo from request (not urldecoded)
699
-	 * @throws \Exception
700
-	 * @return string Path info
701
-	 */
702
-	public function getRawPathInfo() {
703
-		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
704
-		// remove too many leading slashes - can be caused by reverse proxy configuration
705
-		if (strpos($requestUri, '/') === 0) {
706
-			$requestUri = '/' . ltrim($requestUri, '/');
707
-		}
708
-
709
-		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
710
-
711
-		// Remove the query string from REQUEST_URI
712
-		if ($pos = strpos($requestUri, '?')) {
713
-			$requestUri = substr($requestUri, 0, $pos);
714
-		}
715
-
716
-		$scriptName = $this->server['SCRIPT_NAME'];
717
-		$pathInfo = $requestUri;
718
-
719
-		// strip off the script name's dir and file name
720
-		// FIXME: Sabre does not really belong here
721
-		list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($scriptName);
722
-		if (!empty($path)) {
723
-			if($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
724
-				$pathInfo = substr($pathInfo, strlen($path));
725
-			} else {
726
-				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
727
-			}
728
-		}
729
-		if (strpos($pathInfo, '/'.$name) === 0) {
730
-			$pathInfo = substr($pathInfo, strlen($name) + 1);
731
-		}
732
-		if (strpos($pathInfo, $name) === 0) {
733
-			$pathInfo = substr($pathInfo, strlen($name));
734
-		}
735
-		if($pathInfo === false || $pathInfo === '/'){
736
-			return '';
737
-		} else {
738
-			return $pathInfo;
739
-		}
740
-	}
741
-
742
-	/**
743
-	 * Get PathInfo from request
744
-	 * @throws \Exception
745
-	 * @return string|false Path info or false when not found
746
-	 */
747
-	public function getPathInfo() {
748
-		$pathInfo = $this->getRawPathInfo();
749
-		// following is taken from \Sabre\HTTP\URLUtil::decodePathSegment
750
-		$pathInfo = rawurldecode($pathInfo);
751
-		$encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
752
-
753
-		switch($encoding) {
754
-			case 'ISO-8859-1' :
755
-				$pathInfo = utf8_encode($pathInfo);
756
-		}
757
-		// end copy
758
-
759
-		return $pathInfo;
760
-	}
761
-
762
-	/**
763
-	 * Returns the script name, even if the website uses one or more
764
-	 * reverse proxies
765
-	 * @return string the script name
766
-	 */
767
-	public function getScriptName() {
768
-		$name = $this->server['SCRIPT_NAME'];
769
-		$overwriteWebRoot =  $this->config->getSystemValue('overwritewebroot');
770
-		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
771
-			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
772
-			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -strlen('lib/private/appframework/http/')));
773
-			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), strlen($serverRoot)));
774
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
775
-		}
776
-		return $name;
777
-	}
778
-
779
-	/**
780
-	 * Checks whether the user agent matches a given regex
781
-	 * @param array $agent array of agent names
782
-	 * @return bool true if at least one of the given agent matches, false otherwise
783
-	 */
784
-	public function isUserAgent(array $agent) {
785
-		if (!isset($this->server['HTTP_USER_AGENT'])) {
786
-			return false;
787
-		}
788
-		foreach ($agent as $regex) {
789
-			if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
790
-				return true;
791
-			}
792
-		}
793
-		return false;
794
-	}
795
-
796
-	/**
797
-	 * Returns the unverified server host from the headers without checking
798
-	 * whether it is a trusted domain
799
-	 * @return string Server host
800
-	 */
801
-	public function getInsecureServerHost() {
802
-		$host = 'localhost';
803
-		if (isset($this->server['HTTP_X_FORWARDED_HOST'])) {
804
-			if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
805
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
806
-				$host = trim(current($parts));
807
-			} else {
808
-				$host = $this->server['HTTP_X_FORWARDED_HOST'];
809
-			}
810
-		} else {
811
-			if (isset($this->server['HTTP_HOST'])) {
812
-				$host = $this->server['HTTP_HOST'];
813
-			} else if (isset($this->server['SERVER_NAME'])) {
814
-				$host = $this->server['SERVER_NAME'];
815
-			}
816
-		}
817
-		return $host;
818
-	}
819
-
820
-
821
-	/**
822
-	 * Returns the server host from the headers, or the first configured
823
-	 * trusted domain if the host isn't in the trusted list
824
-	 * @return string Server host
825
-	 */
826
-	public function getServerHost() {
827
-		// overwritehost is always trusted
828
-		$host = $this->getOverwriteHost();
829
-		if ($host !== null) {
830
-			return $host;
831
-		}
832
-
833
-		// get the host from the headers
834
-		$host = $this->getInsecureServerHost();
835
-
836
-		// Verify that the host is a trusted domain if the trusted domains
837
-		// are defined
838
-		// If no trusted domain is provided the first trusted domain is returned
839
-		$trustedDomainHelper = new TrustedDomainHelper($this->config);
840
-		if ($trustedDomainHelper->isTrustedDomain($host)) {
841
-			return $host;
842
-		} else {
843
-			$trustedList = $this->config->getSystemValue('trusted_domains', []);
844
-			if(!empty($trustedList)) {
845
-				return $trustedList[0];
846
-			} else {
847
-				return '';
848
-			}
849
-		}
850
-	}
851
-
852
-	/**
853
-	 * Returns the overwritehost setting from the config if set and
854
-	 * if the overwrite condition is met
855
-	 * @return string|null overwritehost value or null if not defined or the defined condition
856
-	 * isn't met
857
-	 */
858
-	private function getOverwriteHost() {
859
-		if($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
860
-			return $this->config->getSystemValue('overwritehost');
861
-		}
862
-		return null;
863
-	}
58
+    const USER_AGENT_IE = '/(MSIE)|(Trident)/';
59
+    // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
60
+    const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
61
+    // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
62
+    const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
63
+    // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
64
+    const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+$/';
65
+    // Safari User Agent from http://www.useragentstring.com/pages/Safari/
66
+    const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
67
+    // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
68
+    const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
69
+    const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
70
+    const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/';
71
+
72
+    /**
73
+     * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_IOS instead
74
+     */
75
+    const USER_AGENT_OWNCLOUD_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/';
76
+    /**
77
+     * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_ANDROID instead
78
+     */
79
+    const USER_AGENT_OWNCLOUD_ANDROID = '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/';
80
+    /**
81
+     * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_DESKTOP instead
82
+     */
83
+    const USER_AGENT_OWNCLOUD_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/';
84
+
85
+    protected $inputStream;
86
+    protected $content;
87
+    protected $items = array();
88
+    protected $allowedKeys = array(
89
+        'get',
90
+        'post',
91
+        'files',
92
+        'server',
93
+        'env',
94
+        'cookies',
95
+        'urlParams',
96
+        'parameters',
97
+        'method',
98
+        'requesttoken',
99
+    );
100
+    /** @var ISecureRandom */
101
+    protected $secureRandom;
102
+    /** @var IConfig */
103
+    protected $config;
104
+    /** @var string */
105
+    protected $requestId = '';
106
+    /** @var ICrypto */
107
+    protected $crypto;
108
+    /** @var CsrfTokenManager|null */
109
+    protected $csrfTokenManager;
110
+
111
+    /** @var bool */
112
+    protected $contentDecoded = false;
113
+
114
+    /**
115
+     * @param array $vars An associative array with the following optional values:
116
+     *        - array 'urlParams' the parameters which were matched from the URL
117
+     *        - array 'get' the $_GET array
118
+     *        - array|string 'post' the $_POST array or JSON string
119
+     *        - array 'files' the $_FILES array
120
+     *        - array 'server' the $_SERVER array
121
+     *        - array 'env' the $_ENV array
122
+     *        - array 'cookies' the $_COOKIE array
123
+     *        - string 'method' the request method (GET, POST etc)
124
+     *        - string|false 'requesttoken' the requesttoken or false when not available
125
+     * @param ISecureRandom $secureRandom
126
+     * @param IConfig $config
127
+     * @param CsrfTokenManager|null $csrfTokenManager
128
+     * @param string $stream
129
+     * @see http://www.php.net/manual/en/reserved.variables.php
130
+     */
131
+    public function __construct(array $vars=array(),
132
+                                ISecureRandom $secureRandom = null,
133
+                                IConfig $config,
134
+                                CsrfTokenManager $csrfTokenManager = null,
135
+                                $stream = 'php://input') {
136
+        $this->inputStream = $stream;
137
+        $this->items['params'] = array();
138
+        $this->secureRandom = $secureRandom;
139
+        $this->config = $config;
140
+        $this->csrfTokenManager = $csrfTokenManager;
141
+
142
+        if(!array_key_exists('method', $vars)) {
143
+            $vars['method'] = 'GET';
144
+        }
145
+
146
+        foreach($this->allowedKeys as $name) {
147
+            $this->items[$name] = isset($vars[$name])
148
+                ? $vars[$name]
149
+                : array();
150
+        }
151
+
152
+        $this->items['parameters'] = array_merge(
153
+            $this->items['get'],
154
+            $this->items['post'],
155
+            $this->items['urlParams'],
156
+            $this->items['params']
157
+        );
158
+
159
+    }
160
+    /**
161
+     * @param array $parameters
162
+     */
163
+    public function setUrlParameters(array $parameters) {
164
+        $this->items['urlParams'] = $parameters;
165
+        $this->items['parameters'] = array_merge(
166
+            $this->items['parameters'],
167
+            $this->items['urlParams']
168
+        );
169
+    }
170
+
171
+    /**
172
+     * Countable method
173
+     * @return int
174
+     */
175
+    public function count() {
176
+        return count(array_keys($this->items['parameters']));
177
+    }
178
+
179
+    /**
180
+     * ArrayAccess methods
181
+     *
182
+     * Gives access to the combined GET, POST and urlParams arrays
183
+     *
184
+     * Examples:
185
+     *
186
+     * $var = $request['myvar'];
187
+     *
188
+     * or
189
+     *
190
+     * if(!isset($request['myvar']) {
191
+     * 	// Do something
192
+     * }
193
+     *
194
+     * $request['myvar'] = 'something'; // This throws an exception.
195
+     *
196
+     * @param string $offset The key to lookup
197
+     * @return boolean
198
+     */
199
+    public function offsetExists($offset) {
200
+        return isset($this->items['parameters'][$offset]);
201
+    }
202
+
203
+    /**
204
+     * @see offsetExists
205
+     */
206
+    public function offsetGet($offset) {
207
+        return isset($this->items['parameters'][$offset])
208
+            ? $this->items['parameters'][$offset]
209
+            : null;
210
+    }
211
+
212
+    /**
213
+     * @see offsetExists
214
+     */
215
+    public function offsetSet($offset, $value) {
216
+        throw new \RuntimeException('You cannot change the contents of the request object');
217
+    }
218
+
219
+    /**
220
+     * @see offsetExists
221
+     */
222
+    public function offsetUnset($offset) {
223
+        throw new \RuntimeException('You cannot change the contents of the request object');
224
+    }
225
+
226
+    /**
227
+     * Magic property accessors
228
+     * @param string $name
229
+     * @param mixed $value
230
+     */
231
+    public function __set($name, $value) {
232
+        throw new \RuntimeException('You cannot change the contents of the request object');
233
+    }
234
+
235
+    /**
236
+     * Access request variables by method and name.
237
+     * Examples:
238
+     *
239
+     * $request->post['myvar']; // Only look for POST variables
240
+     * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
241
+     * Looks in the combined GET, POST and urlParams array.
242
+     *
243
+     * If you access e.g. ->post but the current HTTP request method
244
+     * is GET a \LogicException will be thrown.
245
+     *
246
+     * @param string $name The key to look for.
247
+     * @throws \LogicException
248
+     * @return mixed|null
249
+     */
250
+    public function __get($name) {
251
+        switch($name) {
252
+            case 'put':
253
+            case 'patch':
254
+            case 'get':
255
+            case 'post':
256
+                if($this->method !== strtoupper($name)) {
257
+                    throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
258
+                }
259
+                return $this->getContent();
260
+            case 'files':
261
+            case 'server':
262
+            case 'env':
263
+            case 'cookies':
264
+            case 'urlParams':
265
+            case 'method':
266
+                return isset($this->items[$name])
267
+                    ? $this->items[$name]
268
+                    : null;
269
+            case 'parameters':
270
+            case 'params':
271
+                return $this->getContent();
272
+            default;
273
+                return isset($this[$name])
274
+                    ? $this[$name]
275
+                    : null;
276
+        }
277
+    }
278
+
279
+    /**
280
+     * @param string $name
281
+     * @return bool
282
+     */
283
+    public function __isset($name) {
284
+        if (in_array($name, $this->allowedKeys, true)) {
285
+            return true;
286
+        }
287
+        return isset($this->items['parameters'][$name]);
288
+    }
289
+
290
+    /**
291
+     * @param string $id
292
+     */
293
+    public function __unset($id) {
294
+        throw new \RuntimeException('You cannot change the contents of the request object');
295
+    }
296
+
297
+    /**
298
+     * Returns the value for a specific http header.
299
+     *
300
+     * This method returns null if the header did not exist.
301
+     *
302
+     * @param string $name
303
+     * @return string
304
+     */
305
+    public function getHeader($name) {
306
+
307
+        $name = strtoupper(str_replace(array('-'),array('_'),$name));
308
+        if (isset($this->server['HTTP_' . $name])) {
309
+            return $this->server['HTTP_' . $name];
310
+        }
311
+
312
+        // There's a few headers that seem to end up in the top-level
313
+        // server array.
314
+        switch($name) {
315
+            case 'CONTENT_TYPE' :
316
+            case 'CONTENT_LENGTH' :
317
+                if (isset($this->server[$name])) {
318
+                    return $this->server[$name];
319
+                }
320
+                break;
321
+
322
+        }
323
+
324
+        return null;
325
+    }
326
+
327
+    /**
328
+     * Lets you access post and get parameters by the index
329
+     * In case of json requests the encoded json body is accessed
330
+     *
331
+     * @param string $key the key which you want to access in the URL Parameter
332
+     *                     placeholder, $_POST or $_GET array.
333
+     *                     The priority how they're returned is the following:
334
+     *                     1. URL parameters
335
+     *                     2. POST parameters
336
+     *                     3. GET parameters
337
+     * @param mixed $default If the key is not found, this value will be returned
338
+     * @return mixed the content of the array
339
+     */
340
+    public function getParam($key, $default = null) {
341
+        return isset($this->parameters[$key])
342
+            ? $this->parameters[$key]
343
+            : $default;
344
+    }
345
+
346
+    /**
347
+     * Returns all params that were received, be it from the request
348
+     * (as GET or POST) or throuh the URL by the route
349
+     * @return array the array with all parameters
350
+     */
351
+    public function getParams() {
352
+        return $this->parameters;
353
+    }
354
+
355
+    /**
356
+     * Returns the method of the request
357
+     * @return string the method of the request (POST, GET, etc)
358
+     */
359
+    public function getMethod() {
360
+        return $this->method;
361
+    }
362
+
363
+    /**
364
+     * Shortcut for accessing an uploaded file through the $_FILES array
365
+     * @param string $key the key that will be taken from the $_FILES array
366
+     * @return array the file in the $_FILES element
367
+     */
368
+    public function getUploadedFile($key) {
369
+        return isset($this->files[$key]) ? $this->files[$key] : null;
370
+    }
371
+
372
+    /**
373
+     * Shortcut for getting env variables
374
+     * @param string $key the key that will be taken from the $_ENV array
375
+     * @return array the value in the $_ENV element
376
+     */
377
+    public function getEnv($key) {
378
+        return isset($this->env[$key]) ? $this->env[$key] : null;
379
+    }
380
+
381
+    /**
382
+     * Shortcut for getting cookie variables
383
+     * @param string $key the key that will be taken from the $_COOKIE array
384
+     * @return string the value in the $_COOKIE element
385
+     */
386
+    public function getCookie($key) {
387
+        return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
388
+    }
389
+
390
+    /**
391
+     * Returns the request body content.
392
+     *
393
+     * If the HTTP request method is PUT and the body
394
+     * not application/x-www-form-urlencoded or application/json a stream
395
+     * resource is returned, otherwise an array.
396
+     *
397
+     * @return array|string|resource The request body content or a resource to read the body stream.
398
+     *
399
+     * @throws \LogicException
400
+     */
401
+    protected function getContent() {
402
+        // If the content can't be parsed into an array then return a stream resource.
403
+        if ($this->method === 'PUT'
404
+            && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
405
+            && strpos($this->getHeader('Content-Type'), 'application/json') === false
406
+        ) {
407
+            if ($this->content === false) {
408
+                throw new \LogicException(
409
+                    '"put" can only be accessed once if not '
410
+                    . 'application/x-www-form-urlencoded or application/json.'
411
+                );
412
+            }
413
+            $this->content = false;
414
+            return fopen($this->inputStream, 'rb');
415
+        } else {
416
+            $this->decodeContent();
417
+            return $this->items['parameters'];
418
+        }
419
+    }
420
+
421
+    /**
422
+     * Attempt to decode the content and populate parameters
423
+     */
424
+    protected function decodeContent() {
425
+        if ($this->contentDecoded) {
426
+            return;
427
+        }
428
+        $params = [];
429
+
430
+        // 'application/json' must be decoded manually.
431
+        if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
432
+            $params = json_decode(file_get_contents($this->inputStream), true);
433
+            if(count($params) > 0) {
434
+                $this->items['params'] = $params;
435
+                if($this->method === 'POST') {
436
+                    $this->items['post'] = $params;
437
+                }
438
+            }
439
+
440
+        // Handle application/x-www-form-urlencoded for methods other than GET
441
+        // or post correctly
442
+        } elseif($this->method !== 'GET'
443
+                && $this->method !== 'POST'
444
+                && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
445
+
446
+            parse_str(file_get_contents($this->inputStream), $params);
447
+            if(is_array($params)) {
448
+                $this->items['params'] = $params;
449
+            }
450
+        }
451
+
452
+        if (is_array($params)) {
453
+            $this->items['parameters'] = array_merge($this->items['parameters'], $params);
454
+        }
455
+        $this->contentDecoded = true;
456
+    }
457
+
458
+
459
+    /**
460
+     * Checks if the CSRF check was correct
461
+     * @return bool true if CSRF check passed
462
+     */
463
+    public function passesCSRFCheck() {
464
+        if($this->csrfTokenManager === null) {
465
+            return false;
466
+        }
467
+
468
+        if(!$this->passesStrictCookieCheck()) {
469
+            return false;
470
+        }
471
+
472
+        if (isset($this->items['get']['requesttoken'])) {
473
+            $token = $this->items['get']['requesttoken'];
474
+        } elseif (isset($this->items['post']['requesttoken'])) {
475
+            $token = $this->items['post']['requesttoken'];
476
+        } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
477
+            $token = $this->items['server']['HTTP_REQUESTTOKEN'];
478
+        } else {
479
+            //no token found.
480
+            return false;
481
+        }
482
+        $token = new CsrfToken($token);
483
+
484
+        return $this->csrfTokenManager->isTokenValid($token);
485
+    }
486
+
487
+    /**
488
+     * Whether the cookie checks are required
489
+     *
490
+     * @return bool
491
+     */
492
+    private function cookieCheckRequired() {
493
+        if ($this->getHeader('OCS-APIREQUEST')) {
494
+            return false;
495
+        }
496
+        if($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
497
+            return false;
498
+        }
499
+
500
+        return true;
501
+    }
502
+
503
+    /**
504
+     * Wrapper around session_get_cookie_params
505
+     *
506
+     * @return array
507
+     */
508
+    protected function getCookieParams() {
509
+        return session_get_cookie_params();
510
+    }
511
+
512
+    /**
513
+     * Appends the __Host- prefix to the cookie if applicable
514
+     *
515
+     * @param string $name
516
+     * @return string
517
+     */
518
+    protected function getProtectedCookieName($name) {
519
+        $cookieParams = $this->getCookieParams();
520
+        $prefix = '';
521
+        if($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
522
+            $prefix = '__Host-';
523
+        }
524
+
525
+        return $prefix.$name;
526
+    }
527
+
528
+    /**
529
+     * Checks if the strict cookie has been sent with the request if the request
530
+     * is including any cookies.
531
+     *
532
+     * @return bool
533
+     * @since 9.1.0
534
+     */
535
+    public function passesStrictCookieCheck() {
536
+        if(!$this->cookieCheckRequired()) {
537
+            return true;
538
+        }
539
+
540
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
541
+        if($this->getCookie($cookieName) === 'true'
542
+            && $this->passesLaxCookieCheck()) {
543
+            return true;
544
+        }
545
+        return false;
546
+    }
547
+
548
+    /**
549
+     * Checks if the lax cookie has been sent with the request if the request
550
+     * is including any cookies.
551
+     *
552
+     * @return bool
553
+     * @since 9.1.0
554
+     */
555
+    public function passesLaxCookieCheck() {
556
+        if(!$this->cookieCheckRequired()) {
557
+            return true;
558
+        }
559
+
560
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
561
+        if($this->getCookie($cookieName) === 'true') {
562
+            return true;
563
+        }
564
+        return false;
565
+    }
566
+
567
+
568
+    /**
569
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
570
+     * If `mod_unique_id` is installed this value will be taken.
571
+     * @return string
572
+     */
573
+    public function getId() {
574
+        if(isset($this->server['UNIQUE_ID'])) {
575
+            return $this->server['UNIQUE_ID'];
576
+        }
577
+
578
+        if(empty($this->requestId)) {
579
+            $this->requestId = $this->secureRandom->generate(20);
580
+        }
581
+
582
+        return $this->requestId;
583
+    }
584
+
585
+    /**
586
+     * Returns the remote address, if the connection came from a trusted proxy
587
+     * and `forwarded_for_headers` has been configured then the IP address
588
+     * specified in this header will be returned instead.
589
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
590
+     * @return string IP address
591
+     */
592
+    public function getRemoteAddress() {
593
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
594
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
595
+
596
+        if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
597
+            $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
598
+                'HTTP_X_FORWARDED_FOR'
599
+                // only have one default, so we cannot ship an insecure product out of the box
600
+            ]);
601
+
602
+            foreach($forwardedForHeaders as $header) {
603
+                if(isset($this->server[$header])) {
604
+                    foreach(explode(',', $this->server[$header]) as $IP) {
605
+                        $IP = trim($IP);
606
+                        if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
607
+                            return $IP;
608
+                        }
609
+                    }
610
+                }
611
+            }
612
+        }
613
+
614
+        return $remoteAddress;
615
+    }
616
+
617
+    /**
618
+     * Check overwrite condition
619
+     * @param string $type
620
+     * @return bool
621
+     */
622
+    private function isOverwriteCondition($type = '') {
623
+        $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
624
+        $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
625
+        return $regex === '//' || preg_match($regex, $remoteAddr) === 1
626
+        || $type !== 'protocol';
627
+    }
628
+
629
+    /**
630
+     * Returns the server protocol. It respects one or more reverse proxies servers
631
+     * and load balancers
632
+     * @return string Server protocol (http or https)
633
+     */
634
+    public function getServerProtocol() {
635
+        if($this->config->getSystemValue('overwriteprotocol') !== ''
636
+            && $this->isOverwriteCondition('protocol')) {
637
+            return $this->config->getSystemValue('overwriteprotocol');
638
+        }
639
+
640
+        if (isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
641
+            if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
642
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
643
+                $proto = strtolower(trim($parts[0]));
644
+            } else {
645
+                $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
646
+            }
647
+
648
+            // Verify that the protocol is always HTTP or HTTPS
649
+            // default to http if an invalid value is provided
650
+            return $proto === 'https' ? 'https' : 'http';
651
+        }
652
+
653
+        if (isset($this->server['HTTPS'])
654
+            && $this->server['HTTPS'] !== null
655
+            && $this->server['HTTPS'] !== 'off'
656
+            && $this->server['HTTPS'] !== '') {
657
+            return 'https';
658
+        }
659
+
660
+        return 'http';
661
+    }
662
+
663
+    /**
664
+     * Returns the used HTTP protocol.
665
+     *
666
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
667
+     */
668
+    public function getHttpProtocol() {
669
+        $claimedProtocol = strtoupper($this->server['SERVER_PROTOCOL']);
670
+
671
+        $validProtocols = [
672
+            'HTTP/1.0',
673
+            'HTTP/1.1',
674
+            'HTTP/2',
675
+        ];
676
+
677
+        if(in_array($claimedProtocol, $validProtocols, true)) {
678
+            return $claimedProtocol;
679
+        }
680
+
681
+        return 'HTTP/1.1';
682
+    }
683
+
684
+    /**
685
+     * Returns the request uri, even if the website uses one or more
686
+     * reverse proxies
687
+     * @return string
688
+     */
689
+    public function getRequestUri() {
690
+        $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
691
+        if($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
692
+            $uri = $this->getScriptName() . substr($uri, strlen($this->server['SCRIPT_NAME']));
693
+        }
694
+        return $uri;
695
+    }
696
+
697
+    /**
698
+     * Get raw PathInfo from request (not urldecoded)
699
+     * @throws \Exception
700
+     * @return string Path info
701
+     */
702
+    public function getRawPathInfo() {
703
+        $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
704
+        // remove too many leading slashes - can be caused by reverse proxy configuration
705
+        if (strpos($requestUri, '/') === 0) {
706
+            $requestUri = '/' . ltrim($requestUri, '/');
707
+        }
708
+
709
+        $requestUri = preg_replace('%/{2,}%', '/', $requestUri);
710
+
711
+        // Remove the query string from REQUEST_URI
712
+        if ($pos = strpos($requestUri, '?')) {
713
+            $requestUri = substr($requestUri, 0, $pos);
714
+        }
715
+
716
+        $scriptName = $this->server['SCRIPT_NAME'];
717
+        $pathInfo = $requestUri;
718
+
719
+        // strip off the script name's dir and file name
720
+        // FIXME: Sabre does not really belong here
721
+        list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($scriptName);
722
+        if (!empty($path)) {
723
+            if($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
724
+                $pathInfo = substr($pathInfo, strlen($path));
725
+            } else {
726
+                throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
727
+            }
728
+        }
729
+        if (strpos($pathInfo, '/'.$name) === 0) {
730
+            $pathInfo = substr($pathInfo, strlen($name) + 1);
731
+        }
732
+        if (strpos($pathInfo, $name) === 0) {
733
+            $pathInfo = substr($pathInfo, strlen($name));
734
+        }
735
+        if($pathInfo === false || $pathInfo === '/'){
736
+            return '';
737
+        } else {
738
+            return $pathInfo;
739
+        }
740
+    }
741
+
742
+    /**
743
+     * Get PathInfo from request
744
+     * @throws \Exception
745
+     * @return string|false Path info or false when not found
746
+     */
747
+    public function getPathInfo() {
748
+        $pathInfo = $this->getRawPathInfo();
749
+        // following is taken from \Sabre\HTTP\URLUtil::decodePathSegment
750
+        $pathInfo = rawurldecode($pathInfo);
751
+        $encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
752
+
753
+        switch($encoding) {
754
+            case 'ISO-8859-1' :
755
+                $pathInfo = utf8_encode($pathInfo);
756
+        }
757
+        // end copy
758
+
759
+        return $pathInfo;
760
+    }
761
+
762
+    /**
763
+     * Returns the script name, even if the website uses one or more
764
+     * reverse proxies
765
+     * @return string the script name
766
+     */
767
+    public function getScriptName() {
768
+        $name = $this->server['SCRIPT_NAME'];
769
+        $overwriteWebRoot =  $this->config->getSystemValue('overwritewebroot');
770
+        if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
771
+            // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
772
+            $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -strlen('lib/private/appframework/http/')));
773
+            $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), strlen($serverRoot)));
774
+            $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
775
+        }
776
+        return $name;
777
+    }
778
+
779
+    /**
780
+     * Checks whether the user agent matches a given regex
781
+     * @param array $agent array of agent names
782
+     * @return bool true if at least one of the given agent matches, false otherwise
783
+     */
784
+    public function isUserAgent(array $agent) {
785
+        if (!isset($this->server['HTTP_USER_AGENT'])) {
786
+            return false;
787
+        }
788
+        foreach ($agent as $regex) {
789
+            if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
790
+                return true;
791
+            }
792
+        }
793
+        return false;
794
+    }
795
+
796
+    /**
797
+     * Returns the unverified server host from the headers without checking
798
+     * whether it is a trusted domain
799
+     * @return string Server host
800
+     */
801
+    public function getInsecureServerHost() {
802
+        $host = 'localhost';
803
+        if (isset($this->server['HTTP_X_FORWARDED_HOST'])) {
804
+            if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
805
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
806
+                $host = trim(current($parts));
807
+            } else {
808
+                $host = $this->server['HTTP_X_FORWARDED_HOST'];
809
+            }
810
+        } else {
811
+            if (isset($this->server['HTTP_HOST'])) {
812
+                $host = $this->server['HTTP_HOST'];
813
+            } else if (isset($this->server['SERVER_NAME'])) {
814
+                $host = $this->server['SERVER_NAME'];
815
+            }
816
+        }
817
+        return $host;
818
+    }
819
+
820
+
821
+    /**
822
+     * Returns the server host from the headers, or the first configured
823
+     * trusted domain if the host isn't in the trusted list
824
+     * @return string Server host
825
+     */
826
+    public function getServerHost() {
827
+        // overwritehost is always trusted
828
+        $host = $this->getOverwriteHost();
829
+        if ($host !== null) {
830
+            return $host;
831
+        }
832
+
833
+        // get the host from the headers
834
+        $host = $this->getInsecureServerHost();
835
+
836
+        // Verify that the host is a trusted domain if the trusted domains
837
+        // are defined
838
+        // If no trusted domain is provided the first trusted domain is returned
839
+        $trustedDomainHelper = new TrustedDomainHelper($this->config);
840
+        if ($trustedDomainHelper->isTrustedDomain($host)) {
841
+            return $host;
842
+        } else {
843
+            $trustedList = $this->config->getSystemValue('trusted_domains', []);
844
+            if(!empty($trustedList)) {
845
+                return $trustedList[0];
846
+            } else {
847
+                return '';
848
+            }
849
+        }
850
+    }
851
+
852
+    /**
853
+     * Returns the overwritehost setting from the config if set and
854
+     * if the overwrite condition is met
855
+     * @return string|null overwritehost value or null if not defined or the defined condition
856
+     * isn't met
857
+     */
858
+    private function getOverwriteHost() {
859
+        if($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
860
+            return $this->config->getSystemValue('overwritehost');
861
+        }
862
+        return null;
863
+    }
864 864
 
865 865
 }
Please login to merge, or discard this patch.
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
 	 * @param string $stream
129 129
 	 * @see http://www.php.net/manual/en/reserved.variables.php
130 130
 	 */
131
-	public function __construct(array $vars=array(),
131
+	public function __construct(array $vars = array(),
132 132
 								ISecureRandom $secureRandom = null,
133 133
 								IConfig $config,
134 134
 								CsrfTokenManager $csrfTokenManager = null,
@@ -139,11 +139,11 @@  discard block
 block discarded – undo
139 139
 		$this->config = $config;
140 140
 		$this->csrfTokenManager = $csrfTokenManager;
141 141
 
142
-		if(!array_key_exists('method', $vars)) {
142
+		if (!array_key_exists('method', $vars)) {
143 143
 			$vars['method'] = 'GET';
144 144
 		}
145 145
 
146
-		foreach($this->allowedKeys as $name) {
146
+		foreach ($this->allowedKeys as $name) {
147 147
 			$this->items[$name] = isset($vars[$name])
148 148
 				? $vars[$name]
149 149
 				: array();
@@ -248,12 +248,12 @@  discard block
 block discarded – undo
248 248
 	* @return mixed|null
249 249
 	*/
250 250
 	public function __get($name) {
251
-		switch($name) {
251
+		switch ($name) {
252 252
 			case 'put':
253 253
 			case 'patch':
254 254
 			case 'get':
255 255
 			case 'post':
256
-				if($this->method !== strtoupper($name)) {
256
+				if ($this->method !== strtoupper($name)) {
257 257
 					throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
258 258
 				}
259 259
 				return $this->getContent();
@@ -304,14 +304,14 @@  discard block
 block discarded – undo
304 304
 	 */
305 305
 	public function getHeader($name) {
306 306
 
307
-		$name = strtoupper(str_replace(array('-'),array('_'),$name));
308
-		if (isset($this->server['HTTP_' . $name])) {
309
-			return $this->server['HTTP_' . $name];
307
+		$name = strtoupper(str_replace(array('-'), array('_'), $name));
308
+		if (isset($this->server['HTTP_'.$name])) {
309
+			return $this->server['HTTP_'.$name];
310 310
 		}
311 311
 
312 312
 		// There's a few headers that seem to end up in the top-level
313 313
 		// server array.
314
-		switch($name) {
314
+		switch ($name) {
315 315
 			case 'CONTENT_TYPE' :
316 316
 			case 'CONTENT_LENGTH' :
317 317
 				if (isset($this->server[$name])) {
@@ -430,21 +430,21 @@  discard block
 block discarded – undo
430 430
 		// 'application/json' must be decoded manually.
431 431
 		if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
432 432
 			$params = json_decode(file_get_contents($this->inputStream), true);
433
-			if(count($params) > 0) {
433
+			if (count($params) > 0) {
434 434
 				$this->items['params'] = $params;
435
-				if($this->method === 'POST') {
435
+				if ($this->method === 'POST') {
436 436
 					$this->items['post'] = $params;
437 437
 				}
438 438
 			}
439 439
 
440 440
 		// Handle application/x-www-form-urlencoded for methods other than GET
441 441
 		// or post correctly
442
-		} elseif($this->method !== 'GET'
442
+		} elseif ($this->method !== 'GET'
443 443
 				&& $this->method !== 'POST'
444 444
 				&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
445 445
 
446 446
 			parse_str(file_get_contents($this->inputStream), $params);
447
-			if(is_array($params)) {
447
+			if (is_array($params)) {
448 448
 				$this->items['params'] = $params;
449 449
 			}
450 450
 		}
@@ -461,11 +461,11 @@  discard block
 block discarded – undo
461 461
 	 * @return bool true if CSRF check passed
462 462
 	 */
463 463
 	public function passesCSRFCheck() {
464
-		if($this->csrfTokenManager === null) {
464
+		if ($this->csrfTokenManager === null) {
465 465
 			return false;
466 466
 		}
467 467
 
468
-		if(!$this->passesStrictCookieCheck()) {
468
+		if (!$this->passesStrictCookieCheck()) {
469 469
 			return false;
470 470
 		}
471 471
 
@@ -493,7 +493,7 @@  discard block
 block discarded – undo
493 493
 		if ($this->getHeader('OCS-APIREQUEST')) {
494 494
 			return false;
495 495
 		}
496
-		if($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
496
+		if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
497 497
 			return false;
498 498
 		}
499 499
 
@@ -518,7 +518,7 @@  discard block
 block discarded – undo
518 518
 	protected function getProtectedCookieName($name) {
519 519
 		$cookieParams = $this->getCookieParams();
520 520
 		$prefix = '';
521
-		if($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
521
+		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
522 522
 			$prefix = '__Host-';
523 523
 		}
524 524
 
@@ -533,12 +533,12 @@  discard block
 block discarded – undo
533 533
 	 * @since 9.1.0
534 534
 	 */
535 535
 	public function passesStrictCookieCheck() {
536
-		if(!$this->cookieCheckRequired()) {
536
+		if (!$this->cookieCheckRequired()) {
537 537
 			return true;
538 538
 		}
539 539
 
540 540
 		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
541
-		if($this->getCookie($cookieName) === 'true'
541
+		if ($this->getCookie($cookieName) === 'true'
542 542
 			&& $this->passesLaxCookieCheck()) {
543 543
 			return true;
544 544
 		}
@@ -553,12 +553,12 @@  discard block
 block discarded – undo
553 553
 	 * @since 9.1.0
554 554
 	 */
555 555
 	public function passesLaxCookieCheck() {
556
-		if(!$this->cookieCheckRequired()) {
556
+		if (!$this->cookieCheckRequired()) {
557 557
 			return true;
558 558
 		}
559 559
 
560 560
 		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
561
-		if($this->getCookie($cookieName) === 'true') {
561
+		if ($this->getCookie($cookieName) === 'true') {
562 562
 			return true;
563 563
 		}
564 564
 		return false;
@@ -571,11 +571,11 @@  discard block
 block discarded – undo
571 571
 	 * @return string
572 572
 	 */
573 573
 	public function getId() {
574
-		if(isset($this->server['UNIQUE_ID'])) {
574
+		if (isset($this->server['UNIQUE_ID'])) {
575 575
 			return $this->server['UNIQUE_ID'];
576 576
 		}
577 577
 
578
-		if(empty($this->requestId)) {
578
+		if (empty($this->requestId)) {
579 579
 			$this->requestId = $this->secureRandom->generate(20);
580 580
 		}
581 581
 
@@ -593,15 +593,15 @@  discard block
 block discarded – undo
593 593
 		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
594 594
 		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
595 595
 
596
-		if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
596
+		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
597 597
 			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
598 598
 				'HTTP_X_FORWARDED_FOR'
599 599
 				// only have one default, so we cannot ship an insecure product out of the box
600 600
 			]);
601 601
 
602
-			foreach($forwardedForHeaders as $header) {
603
-				if(isset($this->server[$header])) {
604
-					foreach(explode(',', $this->server[$header]) as $IP) {
602
+			foreach ($forwardedForHeaders as $header) {
603
+				if (isset($this->server[$header])) {
604
+					foreach (explode(',', $this->server[$header]) as $IP) {
605 605
 						$IP = trim($IP);
606 606
 						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
607 607
 							return $IP;
@@ -620,7 +620,7 @@  discard block
 block discarded – undo
620 620
 	 * @return bool
621 621
 	 */
622 622
 	private function isOverwriteCondition($type = '') {
623
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
623
+		$regex = '/'.$this->config->getSystemValue('overwritecondaddr', '').'/';
624 624
 		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
625 625
 		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
626 626
 		|| $type !== 'protocol';
@@ -632,7 +632,7 @@  discard block
 block discarded – undo
632 632
 	 * @return string Server protocol (http or https)
633 633
 	 */
634 634
 	public function getServerProtocol() {
635
-		if($this->config->getSystemValue('overwriteprotocol') !== ''
635
+		if ($this->config->getSystemValue('overwriteprotocol') !== ''
636 636
 			&& $this->isOverwriteCondition('protocol')) {
637 637
 			return $this->config->getSystemValue('overwriteprotocol');
638 638
 		}
@@ -674,7 +674,7 @@  discard block
 block discarded – undo
674 674
 			'HTTP/2',
675 675
 		];
676 676
 
677
-		if(in_array($claimedProtocol, $validProtocols, true)) {
677
+		if (in_array($claimedProtocol, $validProtocols, true)) {
678 678
 			return $claimedProtocol;
679 679
 		}
680 680
 
@@ -688,8 +688,8 @@  discard block
 block discarded – undo
688 688
 	 */
689 689
 	public function getRequestUri() {
690 690
 		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
691
-		if($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
692
-			$uri = $this->getScriptName() . substr($uri, strlen($this->server['SCRIPT_NAME']));
691
+		if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
692
+			$uri = $this->getScriptName().substr($uri, strlen($this->server['SCRIPT_NAME']));
693 693
 		}
694 694
 		return $uri;
695 695
 	}
@@ -703,7 +703,7 @@  discard block
 block discarded – undo
703 703
 		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
704 704
 		// remove too many leading slashes - can be caused by reverse proxy configuration
705 705
 		if (strpos($requestUri, '/') === 0) {
706
-			$requestUri = '/' . ltrim($requestUri, '/');
706
+			$requestUri = '/'.ltrim($requestUri, '/');
707 707
 		}
708 708
 
709 709
 		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
@@ -720,7 +720,7 @@  discard block
 block discarded – undo
720 720
 		// FIXME: Sabre does not really belong here
721 721
 		list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($scriptName);
722 722
 		if (!empty($path)) {
723
-			if($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
723
+			if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
724 724
 				$pathInfo = substr($pathInfo, strlen($path));
725 725
 			} else {
726 726
 				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
@@ -732,7 +732,7 @@  discard block
 block discarded – undo
732 732
 		if (strpos($pathInfo, $name) === 0) {
733 733
 			$pathInfo = substr($pathInfo, strlen($name));
734 734
 		}
735
-		if($pathInfo === false || $pathInfo === '/'){
735
+		if ($pathInfo === false || $pathInfo === '/') {
736 736
 			return '';
737 737
 		} else {
738 738
 			return $pathInfo;
@@ -750,7 +750,7 @@  discard block
 block discarded – undo
750 750
 		$pathInfo = rawurldecode($pathInfo);
751 751
 		$encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
752 752
 
753
-		switch($encoding) {
753
+		switch ($encoding) {
754 754
 			case 'ISO-8859-1' :
755 755
 				$pathInfo = utf8_encode($pathInfo);
756 756
 		}
@@ -766,12 +766,12 @@  discard block
 block discarded – undo
766 766
 	 */
767 767
 	public function getScriptName() {
768 768
 		$name = $this->server['SCRIPT_NAME'];
769
-		$overwriteWebRoot =  $this->config->getSystemValue('overwritewebroot');
769
+		$overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
770 770
 		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
771 771
 			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
772 772
 			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -strlen('lib/private/appframework/http/')));
773 773
 			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), strlen($serverRoot)));
774
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
774
+			$name = '/'.ltrim($overwriteWebRoot.$suburi, '/');
775 775
 		}
776 776
 		return $name;
777 777
 	}
@@ -841,7 +841,7 @@  discard block
 block discarded – undo
841 841
 			return $host;
842 842
 		} else {
843 843
 			$trustedList = $this->config->getSystemValue('trusted_domains', []);
844
-			if(!empty($trustedList)) {
844
+			if (!empty($trustedList)) {
845 845
 				return $trustedList[0];
846 846
 			} else {
847 847
 				return '';
@@ -856,7 +856,7 @@  discard block
 block discarded – undo
856 856
 	 * isn't met
857 857
 	 */
858 858
 	private function getOverwriteHost() {
859
-		if($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
859
+		if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
860 860
 			return $this->config->getSystemValue('overwritehost');
861 861
 		}
862 862
 		return null;
Please login to merge, or discard this patch.
lib/private/User/User.php 1 patch
Indentation   +398 added lines, -398 removed lines patch added patch discarded remove patch
@@ -42,402 +42,402 @@
 block discarded – undo
42 42
 use \OCP\IUserBackend;
43 43
 
44 44
 class User implements IUser {
45
-	/** @var string $uid */
46
-	private $uid;
47
-
48
-	/** @var string $displayName */
49
-	private $displayName;
50
-
51
-	/** @var UserInterface $backend */
52
-	private $backend;
53
-
54
-	/** @var bool $enabled */
55
-	private $enabled;
56
-
57
-	/** @var Emitter|Manager $emitter */
58
-	private $emitter;
59
-
60
-	/** @var string $home */
61
-	private $home;
62
-
63
-	/** @var int $lastLogin */
64
-	private $lastLogin;
65
-
66
-	/** @var \OCP\IConfig $config */
67
-	private $config;
68
-
69
-	/** @var IAvatarManager */
70
-	private $avatarManager;
71
-
72
-	/** @var IURLGenerator */
73
-	private $urlGenerator;
74
-
75
-	/**
76
-	 * @param string $uid
77
-	 * @param UserInterface $backend
78
-	 * @param \OC\Hooks\Emitter $emitter
79
-	 * @param IConfig|null $config
80
-	 * @param IURLGenerator $urlGenerator
81
-	 */
82
-	public function __construct($uid, $backend, $emitter = null, IConfig $config = null, $urlGenerator = null) {
83
-		$this->uid = $uid;
84
-		$this->backend = $backend;
85
-		$this->emitter = $emitter;
86
-		if(is_null($config)) {
87
-			$config = \OC::$server->getConfig();
88
-		}
89
-		$this->config = $config;
90
-		$this->urlGenerator = $urlGenerator;
91
-		$enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
92
-		$this->enabled = ($enabled === 'true');
93
-		$this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
94
-		if (is_null($this->urlGenerator)) {
95
-			$this->urlGenerator = \OC::$server->getURLGenerator();
96
-		}
97
-	}
98
-
99
-	/**
100
-	 * get the user id
101
-	 *
102
-	 * @return string
103
-	 */
104
-	public function getUID() {
105
-		return $this->uid;
106
-	}
107
-
108
-	/**
109
-	 * get the display name for the user, if no specific display name is set it will fallback to the user id
110
-	 *
111
-	 * @return string
112
-	 */
113
-	public function getDisplayName() {
114
-		if (!isset($this->displayName)) {
115
-			$displayName = '';
116
-			if ($this->backend and $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
117
-				// get display name and strip whitespace from the beginning and end of it
118
-				$backendDisplayName = $this->backend->getDisplayName($this->uid);
119
-				if (is_string($backendDisplayName)) {
120
-					$displayName = trim($backendDisplayName);
121
-				}
122
-			}
123
-
124
-			if (!empty($displayName)) {
125
-				$this->displayName = $displayName;
126
-			} else {
127
-				$this->displayName = $this->uid;
128
-			}
129
-		}
130
-		return $this->displayName;
131
-	}
132
-
133
-	/**
134
-	 * set the displayname for the user
135
-	 *
136
-	 * @param string $displayName
137
-	 * @return bool
138
-	 */
139
-	public function setDisplayName($displayName) {
140
-		$displayName = trim($displayName);
141
-		if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName)) {
142
-			$result = $this->backend->setDisplayName($this->uid, $displayName);
143
-			if ($result) {
144
-				$this->displayName = $displayName;
145
-				$this->triggerChange('displayName', $displayName);
146
-			}
147
-			return $result !== false;
148
-		} else {
149
-			return false;
150
-		}
151
-	}
152
-
153
-	/**
154
-	 * set the email address of the user
155
-	 *
156
-	 * @param string|null $mailAddress
157
-	 * @return void
158
-	 * @since 9.0.0
159
-	 */
160
-	public function setEMailAddress($mailAddress) {
161
-		if($mailAddress === '') {
162
-			$this->config->deleteUserValue($this->uid, 'settings', 'email');
163
-		} else {
164
-			$this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
165
-		}
166
-		$this->triggerChange('eMailAddress', $mailAddress);
167
-	}
168
-
169
-	/**
170
-	 * returns the timestamp of the user's last login or 0 if the user did never
171
-	 * login
172
-	 *
173
-	 * @return int
174
-	 */
175
-	public function getLastLogin() {
176
-		return $this->lastLogin;
177
-	}
178
-
179
-	/**
180
-	 * updates the timestamp of the most recent login of this user
181
-	 */
182
-	public function updateLastLoginTimestamp() {
183
-		$firstTimeLogin = ($this->lastLogin === 0);
184
-		$this->lastLogin = time();
185
-		$this->config->setUserValue(
186
-			$this->uid, 'login', 'lastLogin', $this->lastLogin);
187
-
188
-		return $firstTimeLogin;
189
-	}
190
-
191
-	/**
192
-	 * Delete the user
193
-	 *
194
-	 * @return bool
195
-	 */
196
-	public function delete() {
197
-		if ($this->emitter) {
198
-			$this->emitter->emit('\OC\User', 'preDelete', array($this));
199
-		}
200
-		// get the home now because it won't return it after user deletion
201
-		$homePath = $this->getHome();
202
-		$result = $this->backend->deleteUser($this->uid);
203
-		if ($result) {
204
-
205
-			// FIXME: Feels like an hack - suggestions?
206
-
207
-			$groupManager = \OC::$server->getGroupManager();
208
-			// We have to delete the user from all groups
209
-			foreach ($groupManager->getUserGroupIds($this) as $groupId) {
210
-				$group = $groupManager->get($groupId);
211
-				if ($group) {
212
-					\OC_Hook::emit("OC_Group", "pre_removeFromGroup", ["run" => true, "uid" => $this->uid, "gid" => $groupId]);
213
-					$group->removeUser($this);
214
-					\OC_Hook::emit("OC_User", "post_removeFromGroup", ["uid" => $this->uid, "gid" => $groupId]);
215
-				}
216
-			}
217
-			// Delete the user's keys in preferences
218
-			\OC::$server->getConfig()->deleteAllUserValues($this->uid);
219
-
220
-			// Delete user files in /data/
221
-			if ($homePath !== false) {
222
-				// FIXME: this operates directly on FS, should use View instead...
223
-				// also this is not testable/mockable...
224
-				\OC_Helper::rmdirr($homePath);
225
-			}
226
-
227
-			// Delete the users entry in the storage table
228
-			Storage::remove('home::' . $this->uid);
229
-
230
-			\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
231
-			\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
232
-
233
-			$notification = \OC::$server->getNotificationManager()->createNotification();
234
-			$notification->setUser($this->uid);
235
-			\OC::$server->getNotificationManager()->markProcessed($notification);
236
-
237
-			if ($this->emitter) {
238
-				$this->emitter->emit('\OC\User', 'postDelete', array($this));
239
-			}
240
-		}
241
-		return !($result === false);
242
-	}
243
-
244
-	/**
245
-	 * Set the password of the user
246
-	 *
247
-	 * @param string $password
248
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
249
-	 * @return bool
250
-	 */
251
-	public function setPassword($password, $recoveryPassword = null) {
252
-		if ($this->emitter) {
253
-			$this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword));
254
-		}
255
-		if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
256
-			$result = $this->backend->setPassword($this->uid, $password);
257
-			if ($this->emitter) {
258
-				$this->emitter->emit('\OC\User', 'postSetPassword', array($this, $password, $recoveryPassword));
259
-			}
260
-			return !($result === false);
261
-		} else {
262
-			return false;
263
-		}
264
-	}
265
-
266
-	/**
267
-	 * get the users home folder to mount
268
-	 *
269
-	 * @return string
270
-	 */
271
-	public function getHome() {
272
-		if (!$this->home) {
273
-			if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
274
-				$this->home = $home;
275
-			} elseif ($this->config) {
276
-				$this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
277
-			} else {
278
-				$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
279
-			}
280
-		}
281
-		return $this->home;
282
-	}
283
-
284
-	/**
285
-	 * Get the name of the backend class the user is connected with
286
-	 *
287
-	 * @return string
288
-	 */
289
-	public function getBackendClassName() {
290
-		if($this->backend instanceof IUserBackend) {
291
-			return $this->backend->getBackendName();
292
-		}
293
-		return get_class($this->backend);
294
-	}
295
-
296
-	/**
297
-	 * check if the backend allows the user to change his avatar on Personal page
298
-	 *
299
-	 * @return bool
300
-	 */
301
-	public function canChangeAvatar() {
302
-		if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
303
-			return $this->backend->canChangeAvatar($this->uid);
304
-		}
305
-		return true;
306
-	}
307
-
308
-	/**
309
-	 * check if the backend supports changing passwords
310
-	 *
311
-	 * @return bool
312
-	 */
313
-	public function canChangePassword() {
314
-		return $this->backend->implementsActions(Backend::SET_PASSWORD);
315
-	}
316
-
317
-	/**
318
-	 * check if the backend supports changing display names
319
-	 *
320
-	 * @return bool
321
-	 */
322
-	public function canChangeDisplayName() {
323
-		if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
324
-			return false;
325
-		}
326
-		return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
327
-	}
328
-
329
-	/**
330
-	 * check if the user is enabled
331
-	 *
332
-	 * @return bool
333
-	 */
334
-	public function isEnabled() {
335
-		return $this->enabled;
336
-	}
337
-
338
-	/**
339
-	 * set the enabled status for the user
340
-	 *
341
-	 * @param bool $enabled
342
-	 */
343
-	public function setEnabled($enabled) {
344
-		$this->enabled = $enabled;
345
-		$enabled = ($enabled) ? 'true' : 'false';
346
-		$this->config->setUserValue($this->uid, 'core', 'enabled', $enabled);
347
-	}
348
-
349
-	/**
350
-	 * get the users email address
351
-	 *
352
-	 * @return string|null
353
-	 * @since 9.0.0
354
-	 */
355
-	public function getEMailAddress() {
356
-		return $this->config->getUserValue($this->uid, 'settings', 'email', null);
357
-	}
358
-
359
-	/**
360
-	 * get the users' quota
361
-	 *
362
-	 * @return string
363
-	 * @since 9.0.0
364
-	 */
365
-	public function getQuota() {
366
-		$quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
367
-		if($quota === 'default') {
368
-			$quota = $this->config->getAppValue('files', 'default_quota', 'none');
369
-		}
370
-		return $quota;
371
-	}
372
-
373
-	/**
374
-	 * set the users' quota
375
-	 *
376
-	 * @param string $quota
377
-	 * @return void
378
-	 * @since 9.0.0
379
-	 */
380
-	public function setQuota($quota) {
381
-		if($quota !== 'none' and $quota !== 'default') {
382
-			$quota = OC_Helper::computerFileSize($quota);
383
-			$quota = OC_Helper::humanFileSize($quota);
384
-		}
385
-		$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
386
-		$this->triggerChange('quota', $quota);
387
-	}
388
-
389
-	/**
390
-	 * get the avatar image if it exists
391
-	 *
392
-	 * @param int $size
393
-	 * @return IImage|null
394
-	 * @since 9.0.0
395
-	 */
396
-	public function getAvatarImage($size) {
397
-		// delay the initialization
398
-		if (is_null($this->avatarManager)) {
399
-			$this->avatarManager = \OC::$server->getAvatarManager();
400
-		}
401
-
402
-		$avatar = $this->avatarManager->getAvatar($this->uid);
403
-		$image = $avatar->get(-1);
404
-		if ($image) {
405
-			return $image;
406
-		}
407
-
408
-		return null;
409
-	}
410
-
411
-	/**
412
-	 * get the federation cloud id
413
-	 *
414
-	 * @return string
415
-	 * @since 9.0.0
416
-	 */
417
-	public function getCloudId() {
418
-		$uid = $this->getUID();
419
-		$server = $this->urlGenerator->getAbsoluteURL('/');
420
-		$server =  rtrim( $this->removeProtocolFromUrl($server), '/');
421
-		return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId();
422
-	}
423
-
424
-	/**
425
-	 * @param string $url
426
-	 * @return string
427
-	 */
428
-	private function removeProtocolFromUrl($url) {
429
-		if (strpos($url, 'https://') === 0) {
430
-			return substr($url, strlen('https://'));
431
-		} else if (strpos($url, 'http://') === 0) {
432
-			return substr($url, strlen('http://'));
433
-		}
434
-
435
-		return $url;
436
-	}
437
-
438
-	public function triggerChange($feature, $value = null) {
439
-		if ($this->emitter) {
440
-			$this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value));
441
-		}
442
-	}
45
+    /** @var string $uid */
46
+    private $uid;
47
+
48
+    /** @var string $displayName */
49
+    private $displayName;
50
+
51
+    /** @var UserInterface $backend */
52
+    private $backend;
53
+
54
+    /** @var bool $enabled */
55
+    private $enabled;
56
+
57
+    /** @var Emitter|Manager $emitter */
58
+    private $emitter;
59
+
60
+    /** @var string $home */
61
+    private $home;
62
+
63
+    /** @var int $lastLogin */
64
+    private $lastLogin;
65
+
66
+    /** @var \OCP\IConfig $config */
67
+    private $config;
68
+
69
+    /** @var IAvatarManager */
70
+    private $avatarManager;
71
+
72
+    /** @var IURLGenerator */
73
+    private $urlGenerator;
74
+
75
+    /**
76
+     * @param string $uid
77
+     * @param UserInterface $backend
78
+     * @param \OC\Hooks\Emitter $emitter
79
+     * @param IConfig|null $config
80
+     * @param IURLGenerator $urlGenerator
81
+     */
82
+    public function __construct($uid, $backend, $emitter = null, IConfig $config = null, $urlGenerator = null) {
83
+        $this->uid = $uid;
84
+        $this->backend = $backend;
85
+        $this->emitter = $emitter;
86
+        if(is_null($config)) {
87
+            $config = \OC::$server->getConfig();
88
+        }
89
+        $this->config = $config;
90
+        $this->urlGenerator = $urlGenerator;
91
+        $enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
92
+        $this->enabled = ($enabled === 'true');
93
+        $this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
94
+        if (is_null($this->urlGenerator)) {
95
+            $this->urlGenerator = \OC::$server->getURLGenerator();
96
+        }
97
+    }
98
+
99
+    /**
100
+     * get the user id
101
+     *
102
+     * @return string
103
+     */
104
+    public function getUID() {
105
+        return $this->uid;
106
+    }
107
+
108
+    /**
109
+     * get the display name for the user, if no specific display name is set it will fallback to the user id
110
+     *
111
+     * @return string
112
+     */
113
+    public function getDisplayName() {
114
+        if (!isset($this->displayName)) {
115
+            $displayName = '';
116
+            if ($this->backend and $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
117
+                // get display name and strip whitespace from the beginning and end of it
118
+                $backendDisplayName = $this->backend->getDisplayName($this->uid);
119
+                if (is_string($backendDisplayName)) {
120
+                    $displayName = trim($backendDisplayName);
121
+                }
122
+            }
123
+
124
+            if (!empty($displayName)) {
125
+                $this->displayName = $displayName;
126
+            } else {
127
+                $this->displayName = $this->uid;
128
+            }
129
+        }
130
+        return $this->displayName;
131
+    }
132
+
133
+    /**
134
+     * set the displayname for the user
135
+     *
136
+     * @param string $displayName
137
+     * @return bool
138
+     */
139
+    public function setDisplayName($displayName) {
140
+        $displayName = trim($displayName);
141
+        if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName)) {
142
+            $result = $this->backend->setDisplayName($this->uid, $displayName);
143
+            if ($result) {
144
+                $this->displayName = $displayName;
145
+                $this->triggerChange('displayName', $displayName);
146
+            }
147
+            return $result !== false;
148
+        } else {
149
+            return false;
150
+        }
151
+    }
152
+
153
+    /**
154
+     * set the email address of the user
155
+     *
156
+     * @param string|null $mailAddress
157
+     * @return void
158
+     * @since 9.0.0
159
+     */
160
+    public function setEMailAddress($mailAddress) {
161
+        if($mailAddress === '') {
162
+            $this->config->deleteUserValue($this->uid, 'settings', 'email');
163
+        } else {
164
+            $this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
165
+        }
166
+        $this->triggerChange('eMailAddress', $mailAddress);
167
+    }
168
+
169
+    /**
170
+     * returns the timestamp of the user's last login or 0 if the user did never
171
+     * login
172
+     *
173
+     * @return int
174
+     */
175
+    public function getLastLogin() {
176
+        return $this->lastLogin;
177
+    }
178
+
179
+    /**
180
+     * updates the timestamp of the most recent login of this user
181
+     */
182
+    public function updateLastLoginTimestamp() {
183
+        $firstTimeLogin = ($this->lastLogin === 0);
184
+        $this->lastLogin = time();
185
+        $this->config->setUserValue(
186
+            $this->uid, 'login', 'lastLogin', $this->lastLogin);
187
+
188
+        return $firstTimeLogin;
189
+    }
190
+
191
+    /**
192
+     * Delete the user
193
+     *
194
+     * @return bool
195
+     */
196
+    public function delete() {
197
+        if ($this->emitter) {
198
+            $this->emitter->emit('\OC\User', 'preDelete', array($this));
199
+        }
200
+        // get the home now because it won't return it after user deletion
201
+        $homePath = $this->getHome();
202
+        $result = $this->backend->deleteUser($this->uid);
203
+        if ($result) {
204
+
205
+            // FIXME: Feels like an hack - suggestions?
206
+
207
+            $groupManager = \OC::$server->getGroupManager();
208
+            // We have to delete the user from all groups
209
+            foreach ($groupManager->getUserGroupIds($this) as $groupId) {
210
+                $group = $groupManager->get($groupId);
211
+                if ($group) {
212
+                    \OC_Hook::emit("OC_Group", "pre_removeFromGroup", ["run" => true, "uid" => $this->uid, "gid" => $groupId]);
213
+                    $group->removeUser($this);
214
+                    \OC_Hook::emit("OC_User", "post_removeFromGroup", ["uid" => $this->uid, "gid" => $groupId]);
215
+                }
216
+            }
217
+            // Delete the user's keys in preferences
218
+            \OC::$server->getConfig()->deleteAllUserValues($this->uid);
219
+
220
+            // Delete user files in /data/
221
+            if ($homePath !== false) {
222
+                // FIXME: this operates directly on FS, should use View instead...
223
+                // also this is not testable/mockable...
224
+                \OC_Helper::rmdirr($homePath);
225
+            }
226
+
227
+            // Delete the users entry in the storage table
228
+            Storage::remove('home::' . $this->uid);
229
+
230
+            \OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
231
+            \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
232
+
233
+            $notification = \OC::$server->getNotificationManager()->createNotification();
234
+            $notification->setUser($this->uid);
235
+            \OC::$server->getNotificationManager()->markProcessed($notification);
236
+
237
+            if ($this->emitter) {
238
+                $this->emitter->emit('\OC\User', 'postDelete', array($this));
239
+            }
240
+        }
241
+        return !($result === false);
242
+    }
243
+
244
+    /**
245
+     * Set the password of the user
246
+     *
247
+     * @param string $password
248
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
249
+     * @return bool
250
+     */
251
+    public function setPassword($password, $recoveryPassword = null) {
252
+        if ($this->emitter) {
253
+            $this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword));
254
+        }
255
+        if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
256
+            $result = $this->backend->setPassword($this->uid, $password);
257
+            if ($this->emitter) {
258
+                $this->emitter->emit('\OC\User', 'postSetPassword', array($this, $password, $recoveryPassword));
259
+            }
260
+            return !($result === false);
261
+        } else {
262
+            return false;
263
+        }
264
+    }
265
+
266
+    /**
267
+     * get the users home folder to mount
268
+     *
269
+     * @return string
270
+     */
271
+    public function getHome() {
272
+        if (!$this->home) {
273
+            if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
274
+                $this->home = $home;
275
+            } elseif ($this->config) {
276
+                $this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
277
+            } else {
278
+                $this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
279
+            }
280
+        }
281
+        return $this->home;
282
+    }
283
+
284
+    /**
285
+     * Get the name of the backend class the user is connected with
286
+     *
287
+     * @return string
288
+     */
289
+    public function getBackendClassName() {
290
+        if($this->backend instanceof IUserBackend) {
291
+            return $this->backend->getBackendName();
292
+        }
293
+        return get_class($this->backend);
294
+    }
295
+
296
+    /**
297
+     * check if the backend allows the user to change his avatar on Personal page
298
+     *
299
+     * @return bool
300
+     */
301
+    public function canChangeAvatar() {
302
+        if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
303
+            return $this->backend->canChangeAvatar($this->uid);
304
+        }
305
+        return true;
306
+    }
307
+
308
+    /**
309
+     * check if the backend supports changing passwords
310
+     *
311
+     * @return bool
312
+     */
313
+    public function canChangePassword() {
314
+        return $this->backend->implementsActions(Backend::SET_PASSWORD);
315
+    }
316
+
317
+    /**
318
+     * check if the backend supports changing display names
319
+     *
320
+     * @return bool
321
+     */
322
+    public function canChangeDisplayName() {
323
+        if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
324
+            return false;
325
+        }
326
+        return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
327
+    }
328
+
329
+    /**
330
+     * check if the user is enabled
331
+     *
332
+     * @return bool
333
+     */
334
+    public function isEnabled() {
335
+        return $this->enabled;
336
+    }
337
+
338
+    /**
339
+     * set the enabled status for the user
340
+     *
341
+     * @param bool $enabled
342
+     */
343
+    public function setEnabled($enabled) {
344
+        $this->enabled = $enabled;
345
+        $enabled = ($enabled) ? 'true' : 'false';
346
+        $this->config->setUserValue($this->uid, 'core', 'enabled', $enabled);
347
+    }
348
+
349
+    /**
350
+     * get the users email address
351
+     *
352
+     * @return string|null
353
+     * @since 9.0.0
354
+     */
355
+    public function getEMailAddress() {
356
+        return $this->config->getUserValue($this->uid, 'settings', 'email', null);
357
+    }
358
+
359
+    /**
360
+     * get the users' quota
361
+     *
362
+     * @return string
363
+     * @since 9.0.0
364
+     */
365
+    public function getQuota() {
366
+        $quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
367
+        if($quota === 'default') {
368
+            $quota = $this->config->getAppValue('files', 'default_quota', 'none');
369
+        }
370
+        return $quota;
371
+    }
372
+
373
+    /**
374
+     * set the users' quota
375
+     *
376
+     * @param string $quota
377
+     * @return void
378
+     * @since 9.0.0
379
+     */
380
+    public function setQuota($quota) {
381
+        if($quota !== 'none' and $quota !== 'default') {
382
+            $quota = OC_Helper::computerFileSize($quota);
383
+            $quota = OC_Helper::humanFileSize($quota);
384
+        }
385
+        $this->config->setUserValue($this->uid, 'files', 'quota', $quota);
386
+        $this->triggerChange('quota', $quota);
387
+    }
388
+
389
+    /**
390
+     * get the avatar image if it exists
391
+     *
392
+     * @param int $size
393
+     * @return IImage|null
394
+     * @since 9.0.0
395
+     */
396
+    public function getAvatarImage($size) {
397
+        // delay the initialization
398
+        if (is_null($this->avatarManager)) {
399
+            $this->avatarManager = \OC::$server->getAvatarManager();
400
+        }
401
+
402
+        $avatar = $this->avatarManager->getAvatar($this->uid);
403
+        $image = $avatar->get(-1);
404
+        if ($image) {
405
+            return $image;
406
+        }
407
+
408
+        return null;
409
+    }
410
+
411
+    /**
412
+     * get the federation cloud id
413
+     *
414
+     * @return string
415
+     * @since 9.0.0
416
+     */
417
+    public function getCloudId() {
418
+        $uid = $this->getUID();
419
+        $server = $this->urlGenerator->getAbsoluteURL('/');
420
+        $server =  rtrim( $this->removeProtocolFromUrl($server), '/');
421
+        return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId();
422
+    }
423
+
424
+    /**
425
+     * @param string $url
426
+     * @return string
427
+     */
428
+    private function removeProtocolFromUrl($url) {
429
+        if (strpos($url, 'https://') === 0) {
430
+            return substr($url, strlen('https://'));
431
+        } else if (strpos($url, 'http://') === 0) {
432
+            return substr($url, strlen('http://'));
433
+        }
434
+
435
+        return $url;
436
+    }
437
+
438
+    public function triggerChange($feature, $value = null) {
439
+        if ($this->emitter) {
440
+            $this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value));
441
+        }
442
+    }
443 443
 }
Please login to merge, or discard this patch.
lib/base.php 1 patch
Indentation   +986 added lines, -986 removed lines patch added patch discarded remove patch
@@ -59,992 +59,992 @@
 block discarded – undo
59 59
  * OC_autoload!
60 60
  */
61 61
 class OC {
62
-	/**
63
-	 * Associative array for autoloading. classname => filename
64
-	 */
65
-	public static $CLASSPATH = array();
66
-	/**
67
-	 * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
68
-	 */
69
-	public static $SERVERROOT = '';
70
-	/**
71
-	 * the current request path relative to the Nextcloud root (e.g. files/index.php)
72
-	 */
73
-	private static $SUBURI = '';
74
-	/**
75
-	 * the Nextcloud root path for http requests (e.g. nextcloud/)
76
-	 */
77
-	public static $WEBROOT = '';
78
-	/**
79
-	 * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
80
-	 * web path in 'url'
81
-	 */
82
-	public static $APPSROOTS = array();
83
-
84
-	/**
85
-	 * @var string
86
-	 */
87
-	public static $configDir;
88
-
89
-	/**
90
-	 * requested app
91
-	 */
92
-	public static $REQUESTEDAPP = '';
93
-
94
-	/**
95
-	 * check if Nextcloud runs in cli mode
96
-	 */
97
-	public static $CLI = false;
98
-
99
-	/**
100
-	 * @var \OC\Autoloader $loader
101
-	 */
102
-	public static $loader = null;
103
-
104
-	/** @var \Composer\Autoload\ClassLoader $composerAutoloader */
105
-	public static $composerAutoloader = null;
106
-
107
-	/**
108
-	 * @var \OC\Server
109
-	 */
110
-	public static $server = null;
111
-
112
-	/**
113
-	 * @var \OC\Config
114
-	 */
115
-	private static $config = null;
116
-
117
-	/**
118
-	 * @throws \RuntimeException when the 3rdparty directory is missing or
119
-	 * the app path list is empty or contains an invalid path
120
-	 */
121
-	public static function initPaths() {
122
-		if(defined('PHPUNIT_CONFIG_DIR')) {
123
-			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
124
-		} elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
125
-			self::$configDir = OC::$SERVERROOT . '/tests/config/';
126
-		} elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
127
-			self::$configDir = rtrim($dir, '/') . '/';
128
-		} else {
129
-			self::$configDir = OC::$SERVERROOT . '/config/';
130
-		}
131
-		self::$config = new \OC\Config(self::$configDir);
132
-
133
-		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
134
-		/**
135
-		 * FIXME: The following lines are required because we can't yet instantiiate
136
-		 *        \OC::$server->getRequest() since \OC::$server does not yet exist.
137
-		 */
138
-		$params = [
139
-			'server' => [
140
-				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
141
-				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
142
-			],
143
-		];
144
-		$fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
145
-		$scriptName = $fakeRequest->getScriptName();
146
-		if (substr($scriptName, -1) == '/') {
147
-			$scriptName .= 'index.php';
148
-			//make sure suburi follows the same rules as scriptName
149
-			if (substr(OC::$SUBURI, -9) != 'index.php') {
150
-				if (substr(OC::$SUBURI, -1) != '/') {
151
-					OC::$SUBURI = OC::$SUBURI . '/';
152
-				}
153
-				OC::$SUBURI = OC::$SUBURI . 'index.php';
154
-			}
155
-		}
156
-
157
-
158
-		if (OC::$CLI) {
159
-			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
160
-		} else {
161
-			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
162
-				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
163
-
164
-				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
165
-					OC::$WEBROOT = '/' . OC::$WEBROOT;
166
-				}
167
-			} else {
168
-				// The scriptName is not ending with OC::$SUBURI
169
-				// This most likely means that we are calling from CLI.
170
-				// However some cron jobs still need to generate
171
-				// a web URL, so we use overwritewebroot as a fallback.
172
-				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
173
-			}
174
-
175
-			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
176
-			// slash which is required by URL generation.
177
-			if($_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
178
-					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
179
-				header('Location: '.\OC::$WEBROOT.'/');
180
-				exit();
181
-			}
182
-		}
183
-
184
-		// search the apps folder
185
-		$config_paths = self::$config->getValue('apps_paths', array());
186
-		if (!empty($config_paths)) {
187
-			foreach ($config_paths as $paths) {
188
-				if (isset($paths['url']) && isset($paths['path'])) {
189
-					$paths['url'] = rtrim($paths['url'], '/');
190
-					$paths['path'] = rtrim($paths['path'], '/');
191
-					OC::$APPSROOTS[] = $paths;
192
-				}
193
-			}
194
-		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
195
-			OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true);
196
-		} elseif (file_exists(OC::$SERVERROOT . '/../apps')) {
197
-			OC::$APPSROOTS[] = array(
198
-				'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps',
199
-				'url' => '/apps',
200
-				'writable' => true
201
-			);
202
-		}
203
-
204
-		if (empty(OC::$APPSROOTS)) {
205
-			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
206
-				. ' or the folder above. You can also configure the location in the config.php file.');
207
-		}
208
-		$paths = array();
209
-		foreach (OC::$APPSROOTS as $path) {
210
-			$paths[] = $path['path'];
211
-			if (!is_dir($path['path'])) {
212
-				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
213
-					. ' Nextcloud folder or the folder above. You can also configure the location in the'
214
-					. ' config.php file.', $path['path']));
215
-			}
216
-		}
217
-
218
-		// set the right include path
219
-		set_include_path(
220
-			implode(PATH_SEPARATOR, $paths)
221
-		);
222
-	}
223
-
224
-	public static function checkConfig() {
225
-		$l = \OC::$server->getL10N('lib');
226
-
227
-		// Create config if it does not already exist
228
-		$configFilePath = self::$configDir .'/config.php';
229
-		if(!file_exists($configFilePath)) {
230
-			@touch($configFilePath);
231
-		}
232
-
233
-		// Check if config is writable
234
-		$configFileWritable = is_writable($configFilePath);
235
-		if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
236
-			|| !$configFileWritable && self::checkUpgrade(false)) {
237
-
238
-			$urlGenerator = \OC::$server->getURLGenerator();
239
-
240
-			if (self::$CLI) {
241
-				echo $l->t('Cannot write into "config" directory!')."\n";
242
-				echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n";
243
-				echo "\n";
244
-				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n";
245
-				exit;
246
-			} else {
247
-				OC_Template::printErrorPage(
248
-					$l->t('Cannot write into "config" directory!'),
249
-					$l->t('This can usually be fixed by '
250
-					. '%sgiving the webserver write access to the config directory%s.',
251
-					 array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
252
-				);
253
-			}
254
-		}
255
-	}
256
-
257
-	public static function checkInstalled() {
258
-		if (defined('OC_CONSOLE')) {
259
-			return;
260
-		}
261
-		// Redirect to installer if not installed
262
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
263
-			if (OC::$CLI) {
264
-				throw new Exception('Not installed');
265
-			} else {
266
-				$url = OC::$WEBROOT . '/index.php';
267
-				header('Location: ' . $url);
268
-			}
269
-			exit();
270
-		}
271
-	}
272
-
273
-	public static function checkMaintenanceMode() {
274
-		// Allow ajax update script to execute without being stopped
275
-		if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') {
276
-			// send http status 503
277
-			header('HTTP/1.1 503 Service Temporarily Unavailable');
278
-			header('Status: 503 Service Temporarily Unavailable');
279
-			header('Retry-After: 120');
280
-
281
-			// render error page
282
-			$template = new OC_Template('', 'update.user', 'guest');
283
-			OC_Util::addScript('maintenance-check');
284
-			$template->printPage();
285
-			die();
286
-		}
287
-	}
288
-
289
-	/**
290
-	 * Checks if the version requires an update and shows
291
-	 * @param bool $showTemplate Whether an update screen should get shown
292
-	 * @return bool|void
293
-	 */
294
-	public static function checkUpgrade($showTemplate = true) {
295
-		if (\OCP\Util::needUpgrade()) {
296
-			$systemConfig = \OC::$server->getSystemConfig();
297
-			if ($showTemplate && !$systemConfig->getValue('maintenance', false)) {
298
-				self::printUpgradePage();
299
-				exit();
300
-			} else {
301
-				return true;
302
-			}
303
-		}
304
-		return false;
305
-	}
306
-
307
-	/**
308
-	 * Prints the upgrade page
309
-	 */
310
-	private static function printUpgradePage() {
311
-		$systemConfig = \OC::$server->getSystemConfig();
312
-
313
-		$disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
314
-		$tooBig = false;
315
-		if (!$disableWebUpdater) {
316
-			$apps = \OC::$server->getAppManager();
317
-			$tooBig = $apps->isInstalled('user_ldap') || $apps->isInstalled('user_shibboleth');
318
-			if (!$tooBig) {
319
-				// count users
320
-				$stats = \OC::$server->getUserManager()->countUsers();
321
-				$totalUsers = array_sum($stats);
322
-				$tooBig = ($totalUsers > 50);
323
-			}
324
-		}
325
-		if ($disableWebUpdater || $tooBig) {
326
-			// send http status 503
327
-			header('HTTP/1.1 503 Service Temporarily Unavailable');
328
-			header('Status: 503 Service Temporarily Unavailable');
329
-			header('Retry-After: 120');
330
-
331
-			// render error page
332
-			$template = new OC_Template('', 'update.use-cli', 'guest');
333
-			$template->assign('productName', 'nextcloud'); // for now
334
-			$template->assign('version', OC_Util::getVersionString());
335
-			$template->assign('tooBig', $tooBig);
336
-
337
-			$template->printPage();
338
-			die();
339
-		}
340
-
341
-		// check whether this is a core update or apps update
342
-		$installedVersion = $systemConfig->getValue('version', '0.0.0');
343
-		$currentVersion = implode('.', \OCP\Util::getVersion());
344
-
345
-		// if not a core upgrade, then it's apps upgrade
346
-		$isAppsOnlyUpgrade = (version_compare($currentVersion, $installedVersion, '='));
347
-
348
-		$oldTheme = $systemConfig->getValue('theme');
349
-		$systemConfig->setValue('theme', '');
350
-		OC_Util::addScript('config'); // needed for web root
351
-		OC_Util::addScript('update');
352
-
353
-		/** @var \OC\App\AppManager $appManager */
354
-		$appManager = \OC::$server->getAppManager();
355
-
356
-		$tmpl = new OC_Template('', 'update.admin', 'guest');
357
-		$tmpl->assign('version', OC_Util::getVersionString());
358
-		$tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
359
-
360
-		// get third party apps
361
-		$ocVersion = \OCP\Util::getVersion();
362
-		$incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
363
-		$incompatibleShippedApps = [];
364
-		foreach ($incompatibleApps as $appInfo) {
365
-			if ($appManager->isShipped($appInfo['id'])) {
366
-				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
367
-			}
368
-		}
369
-
370
-		if (!empty($incompatibleShippedApps)) {
371
-			$l = \OC::$server->getL10N('core');
372
-			$hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
373
-			throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
374
-		}
375
-
376
-		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
377
-		$tmpl->assign('incompatibleAppsList', $incompatibleApps);
378
-		$tmpl->assign('productName', 'Nextcloud'); // for now
379
-		$tmpl->assign('oldTheme', $oldTheme);
380
-		$tmpl->printPage();
381
-	}
382
-
383
-	public static function initSession() {
384
-		// prevents javascript from accessing php session cookies
385
-		ini_set('session.cookie_httponly', true);
386
-
387
-		// set the cookie path to the Nextcloud directory
388
-		$cookie_path = OC::$WEBROOT ? : '/';
389
-		ini_set('session.cookie_path', $cookie_path);
390
-
391
-		// Let the session name be changed in the initSession Hook
392
-		$sessionName = OC_Util::getInstanceId();
393
-
394
-		try {
395
-			// Allow session apps to create a custom session object
396
-			$useCustomSession = false;
397
-			$session = self::$server->getSession();
398
-			OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession));
399
-			if (!$useCustomSession) {
400
-				// set the session name to the instance id - which is unique
401
-				$session = new \OC\Session\Internal($sessionName);
402
-			}
403
-
404
-			$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
405
-			$session = $cryptoWrapper->wrapSession($session);
406
-			self::$server->setSession($session);
407
-
408
-			// if session can't be started break with http 500 error
409
-		} catch (Exception $e) {
410
-			\OCP\Util::logException('base', $e);
411
-			//show the user a detailed error page
412
-			OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
413
-			OC_Template::printExceptionErrorPage($e);
414
-			die();
415
-		}
416
-
417
-		$sessionLifeTime = self::getSessionLifeTime();
418
-
419
-		// session timeout
420
-		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
421
-			if (isset($_COOKIE[session_name()])) {
422
-				setcookie(session_name(), null, -1, self::$WEBROOT ? : '/');
423
-			}
424
-			\OC::$server->getUserSession()->logout();
425
-		}
426
-
427
-		$session->set('LAST_ACTIVITY', time());
428
-	}
429
-
430
-	/**
431
-	 * @return string
432
-	 */
433
-	private static function getSessionLifeTime() {
434
-		return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
435
-	}
436
-
437
-	public static function loadAppClassPaths() {
438
-		foreach (OC_App::getEnabledApps() as $app) {
439
-			$appPath = OC_App::getAppPath($app);
440
-			if ($appPath === false) {
441
-				continue;
442
-			}
443
-
444
-			$file = $appPath . '/appinfo/classpath.php';
445
-			if (file_exists($file)) {
446
-				require_once $file;
447
-			}
448
-		}
449
-	}
450
-
451
-	/**
452
-	 * Try to set some values to the required Nextcloud default
453
-	 */
454
-	public static function setRequiredIniValues() {
455
-		@ini_set('default_charset', 'UTF-8');
456
-		@ini_set('gd.jpeg_ignore_warning', 1);
457
-	}
458
-
459
-	/**
460
-	 * Send the same site cookies
461
-	 */
462
-	private static function sendSameSiteCookies() {
463
-		$cookieParams = session_get_cookie_params();
464
-		$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
465
-		$policies = [
466
-			'lax',
467
-			'strict',
468
-		];
469
-
470
-		// Append __Host to the cookie if it meets the requirements
471
-		$cookiePrefix = '';
472
-		if($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
473
-			$cookiePrefix = '__Host-';
474
-		}
475
-
476
-		foreach($policies as $policy) {
477
-			header(
478
-				sprintf(
479
-					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
480
-					$cookiePrefix,
481
-					$policy,
482
-					$cookieParams['path'],
483
-					$policy
484
-				),
485
-				false
486
-			);
487
-		}
488
-	}
489
-
490
-	/**
491
-	 * Same Site cookie to further mitigate CSRF attacks. This cookie has to
492
-	 * be set in every request if cookies are sent to add a second level of
493
-	 * defense against CSRF.
494
-	 *
495
-	 * If the cookie is not sent this will set the cookie and reload the page.
496
-	 * We use an additional cookie since we want to protect logout CSRF and
497
-	 * also we can't directly interfere with PHP's session mechanism.
498
-	 */
499
-	private static function performSameSiteCookieProtection() {
500
-		$request = \OC::$server->getRequest();
501
-
502
-		// Some user agents are notorious and don't really properly follow HTTP
503
-		// specifications. For those, have an automated opt-out. Since the protection
504
-		// for remote.php is applied in base.php as starting point we need to opt out
505
-		// here.
506
-		$incompatibleUserAgents = [
507
-			// OS X Finder
508
-			'/^WebDAVFS/',
509
-		];
510
-		if($request->isUserAgent($incompatibleUserAgents)) {
511
-			return;
512
-		}
513
-
514
-		if(count($_COOKIE) > 0) {
515
-			$requestUri = $request->getScriptName();
516
-			$processingScript = explode('/', $requestUri);
517
-			$processingScript = $processingScript[count($processingScript)-1];
518
-			// FIXME: In a SAML scenario we don't get any strict or lax cookie
519
-			// send for the ACS endpoint. Since we have some legacy code in Nextcloud
520
-			// (direct PHP files) the enforcement of lax cookies is performed here
521
-			// instead of the middleware.
522
-			//
523
-			// This means we cannot exclude some routes from the cookie validation,
524
-			// which normally is not a problem but is a little bit cumbersome for
525
-			// this use-case.
526
-			// Once the old legacy PHP endpoints have been removed we can move
527
-			// the verification into a middleware and also adds some exemptions.
528
-			//
529
-			// Questions about this code? Ask Lukas ;-)
530
-			$currentUrl = substr(explode('?',$request->getRequestUri(), 2)[0], strlen(\OC::$WEBROOT));
531
-			if($currentUrl === '/index.php/apps/user_saml/saml/acs' || $currentUrl === '/apps/user_saml/saml/acs') {
532
-				return;
533
-			}
534
-			// For the "index.php" endpoint only a lax cookie is required.
535
-			if($processingScript === 'index.php') {
536
-				if(!$request->passesLaxCookieCheck()) {
537
-					self::sendSameSiteCookies();
538
-					header('Location: '.$_SERVER['REQUEST_URI']);
539
-					exit();
540
-				}
541
-			} else {
542
-				// All other endpoints require the lax and the strict cookie
543
-				if(!$request->passesStrictCookieCheck()) {
544
-					self::sendSameSiteCookies();
545
-					// Debug mode gets access to the resources without strict cookie
546
-					// due to the fact that the SabreDAV browser also lives there.
547
-					if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
548
-						http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
549
-						exit();
550
-					}
551
-				}
552
-			}
553
-		} elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
554
-			self::sendSameSiteCookies();
555
-		}
556
-	}
557
-
558
-	public static function init() {
559
-		// calculate the root directories
560
-		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
561
-
562
-		// register autoloader
563
-		$loaderStart = microtime(true);
564
-		require_once __DIR__ . '/autoloader.php';
565
-		self::$loader = new \OC\Autoloader([
566
-			OC::$SERVERROOT . '/lib/private/legacy',
567
-		]);
568
-		if (defined('PHPUNIT_RUN')) {
569
-			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
570
-		}
571
-		spl_autoload_register(array(self::$loader, 'load'));
572
-		$loaderEnd = microtime(true);
573
-
574
-		self::$CLI = (php_sapi_name() == 'cli');
575
-
576
-		// Add default composer PSR-4 autoloader
577
-		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
578
-
579
-		try {
580
-			self::initPaths();
581
-			// setup 3rdparty autoloader
582
-			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
583
-			if (!file_exists($vendorAutoLoad)) {
584
-				throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
585
-			}
586
-			require_once $vendorAutoLoad;
587
-
588
-		} catch (\RuntimeException $e) {
589
-			if (!self::$CLI) {
590
-				$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
591
-				$protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1';
592
-				header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE);
593
-			}
594
-			// we can't use the template error page here, because this needs the
595
-			// DI container which isn't available yet
596
-			print($e->getMessage());
597
-			exit();
598
-		}
599
-
600
-		// setup the basic server
601
-		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
602
-		\OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
603
-		\OC::$server->getEventLogger()->start('boot', 'Initialize');
604
-
605
-		// Don't display errors and log them
606
-		error_reporting(E_ALL | E_STRICT);
607
-		@ini_set('display_errors', 0);
608
-		@ini_set('log_errors', 1);
609
-
610
-		if(!date_default_timezone_set('UTC')) {
611
-			throw new \RuntimeException('Could not set timezone to UTC');
612
-		};
613
-
614
-		//try to configure php to enable big file uploads.
615
-		//this doesn´t work always depending on the webserver and php configuration.
616
-		//Let´s try to overwrite some defaults anyway
617
-
618
-		//try to set the maximum execution time to 60min
619
-		@set_time_limit(3600);
620
-		@ini_set('max_execution_time', 3600);
621
-		@ini_set('max_input_time', 3600);
622
-
623
-		//try to set the maximum filesize to 10G
624
-		@ini_set('upload_max_filesize', '10G');
625
-		@ini_set('post_max_size', '10G');
626
-		@ini_set('file_uploads', '50');
627
-
628
-		self::setRequiredIniValues();
629
-		self::handleAuthHeaders();
630
-		self::registerAutoloaderCache();
631
-
632
-		// initialize intl fallback is necessary
633
-		\Patchwork\Utf8\Bootup::initIntl();
634
-		OC_Util::isSetLocaleWorking();
635
-
636
-		if (!defined('PHPUNIT_RUN')) {
637
-			OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger());
638
-			$debug = \OC::$server->getConfig()->getSystemValue('debug', false);
639
-			OC\Log\ErrorHandler::register($debug);
640
-		}
641
-
642
-		\OC::$server->getEventLogger()->start('init_session', 'Initialize session');
643
-		OC_App::loadApps(array('session'));
644
-		if (!self::$CLI) {
645
-			self::initSession();
646
-		}
647
-		\OC::$server->getEventLogger()->end('init_session');
648
-		self::checkConfig();
649
-		self::checkInstalled();
650
-
651
-		OC_Response::addSecurityHeaders();
652
-		if(self::$server->getRequest()->getServerProtocol() === 'https') {
653
-			ini_set('session.cookie_secure', true);
654
-		}
655
-
656
-		self::performSameSiteCookieProtection();
657
-
658
-		if (!defined('OC_CONSOLE')) {
659
-			$errors = OC_Util::checkServer(\OC::$server->getConfig());
660
-			if (count($errors) > 0) {
661
-				if (self::$CLI) {
662
-					// Convert l10n string into regular string for usage in database
663
-					$staticErrors = [];
664
-					foreach ($errors as $error) {
665
-						echo $error['error'] . "\n";
666
-						echo $error['hint'] . "\n\n";
667
-						$staticErrors[] = [
668
-							'error' => (string)$error['error'],
669
-							'hint' => (string)$error['hint'],
670
-						];
671
-					}
672
-
673
-					try {
674
-						\OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
675
-					} catch (\Exception $e) {
676
-						echo('Writing to database failed');
677
-					}
678
-					exit(1);
679
-				} else {
680
-					OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
681
-					OC_Util::addStyle('guest');
682
-					OC_Template::printGuestPage('', 'error', array('errors' => $errors));
683
-					exit;
684
-				}
685
-			} elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
686
-				\OC::$server->getConfig()->deleteAppValue('core', 'cronErrors');
687
-			}
688
-		}
689
-		//try to set the session lifetime
690
-		$sessionLifeTime = self::getSessionLifeTime();
691
-		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
692
-
693
-		$systemConfig = \OC::$server->getSystemConfig();
694
-
695
-		// User and Groups
696
-		if (!$systemConfig->getValue("installed", false)) {
697
-			self::$server->getSession()->set('user_id', '');
698
-		}
699
-
700
-		OC_User::useBackend(new \OC\User\Database());
701
-		\OC::$server->getGroupManager()->addBackend(new \OC\Group\Database());
702
-
703
-		// Subscribe to the hook
704
-		\OCP\Util::connectHook(
705
-			'\OCA\Files_Sharing\API\Server2Server',
706
-			'preLoginNameUsedAsUserName',
707
-			'\OC\User\Database',
708
-			'preLoginNameUsedAsUserName'
709
-		);
710
-
711
-		//setup extra user backends
712
-		if (!self::checkUpgrade(false)) {
713
-			OC_User::setupBackends();
714
-		} else {
715
-			// Run upgrades in incognito mode
716
-			OC_User::setIncognitoMode(true);
717
-		}
718
-
719
-		self::registerCacheHooks();
720
-		self::registerFilesystemHooks();
721
-		self::registerShareHooks();
722
-		self::registerLogRotate();
723
-		self::registerEncryptionWrapper();
724
-		self::registerEncryptionHooks();
725
-		self::registerAccountHooks();
726
-		self::registerSettingsHooks();
727
-
728
-		//make sure temporary files are cleaned up
729
-		$tmpManager = \OC::$server->getTempManager();
730
-		register_shutdown_function(array($tmpManager, 'clean'));
731
-		$lockProvider = \OC::$server->getLockingProvider();
732
-		register_shutdown_function(array($lockProvider, 'releaseAll'));
733
-
734
-		// Check whether the sample configuration has been copied
735
-		if($systemConfig->getValue('copied_sample_config', false)) {
736
-			$l = \OC::$server->getL10N('lib');
737
-			header('HTTP/1.1 503 Service Temporarily Unavailable');
738
-			header('Status: 503 Service Temporarily Unavailable');
739
-			OC_Template::printErrorPage(
740
-				$l->t('Sample configuration detected'),
741
-				$l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php')
742
-			);
743
-			return;
744
-		}
745
-
746
-		$request = \OC::$server->getRequest();
747
-		$host = $request->getInsecureServerHost();
748
-		/**
749
-		 * if the host passed in headers isn't trusted
750
-		 * FIXME: Should not be in here at all :see_no_evil:
751
-		 */
752
-		if (!OC::$CLI
753
-			// overwritehost is always trusted, workaround to not have to make
754
-			// \OC\AppFramework\Http\Request::getOverwriteHost public
755
-			&& self::$server->getConfig()->getSystemValue('overwritehost') === ''
756
-			&& !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
757
-			&& self::$server->getConfig()->getSystemValue('installed', false)
758
-		) {
759
-			// Allow access to CSS resources
760
-			$isScssRequest = false;
761
-			if(strpos($request->getPathInfo(), '/css/') === 0) {
762
-				$isScssRequest = true;
763
-			}
764
-
765
-			if (!$isScssRequest) {
766
-				header('HTTP/1.1 400 Bad Request');
767
-				header('Status: 400 Bad Request');
768
-
769
-				\OC::$server->getLogger()->warning(
770
-					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
771
-					[
772
-						'app' => 'core',
773
-						'remoteAddress' => $request->getRemoteAddress(),
774
-						'host' => $host,
775
-					]
776
-				);
777
-
778
-				$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
779
-				$tmpl->assign('domain', $host);
780
-				$tmpl->printPage();
781
-
782
-				exit();
783
-			}
784
-		}
785
-		\OC::$server->getEventLogger()->end('boot');
786
-	}
787
-
788
-	/**
789
-	 * register hooks for the cache
790
-	 */
791
-	public static function registerCacheHooks() {
792
-		//don't try to do this before we are properly setup
793
-		if (\OC::$server->getSystemConfig()->getValue('installed', false) && !self::checkUpgrade(false)) {
794
-
795
-			// NOTE: This will be replaced to use OCP
796
-			$userSession = self::$server->getUserSession();
797
-			$userSession->listen('\OC\User', 'postLogin', function () {
798
-				try {
799
-					$cache = new \OC\Cache\File();
800
-					$cache->gc();
801
-				} catch (\OC\ServerNotAvailableException $e) {
802
-					// not a GC exception, pass it on
803
-					throw $e;
804
-				} catch (\Exception $e) {
805
-					// a GC exception should not prevent users from using OC,
806
-					// so log the exception
807
-					\OC::$server->getLogger()->warning('Exception when running cache gc: ' . $e->getMessage(), array('app' => 'core'));
808
-				}
809
-			});
810
-		}
811
-	}
812
-
813
-	public static function registerSettingsHooks() {
814
-		$dispatcher = \OC::$server->getEventDispatcher();
815
-		$dispatcher->addListener(OCP\App\ManagerEvent::EVENT_APP_DISABLE, function($event) {
816
-			/** @var \OCP\App\ManagerEvent $event */
817
-			\OC::$server->getSettingsManager()->onAppDisabled($event->getAppID());
818
-		});
819
-		$dispatcher->addListener(OCP\App\ManagerEvent::EVENT_APP_UPDATE, function($event) {
820
-			/** @var \OCP\App\ManagerEvent $event */
821
-			$jobList = \OC::$server->getJobList();
822
-			$job = 'OC\\Settings\\RemoveOrphaned';
823
-			if(!($jobList->has($job, null))) {
824
-				$jobList->add($job);
825
-			}
826
-		});
827
-	}
828
-
829
-	private static function registerEncryptionWrapper() {
830
-		$manager = self::$server->getEncryptionManager();
831
-		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
832
-	}
833
-
834
-	private static function registerEncryptionHooks() {
835
-		$enabled = self::$server->getEncryptionManager()->isEnabled();
836
-		if ($enabled) {
837
-			\OCP\Util::connectHook('OCP\Share', 'post_shared', 'OC\Encryption\HookManager', 'postShared');
838
-			\OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OC\Encryption\HookManager', 'postUnshared');
839
-			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OC\Encryption\HookManager', 'postRename');
840
-			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', 'OC\Encryption\HookManager', 'postRestore');
841
-		}
842
-	}
843
-
844
-	private static function registerAccountHooks() {
845
-		$hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger());
846
-		\OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook');
847
-	}
848
-
849
-	/**
850
-	 * register hooks for the cache
851
-	 */
852
-	public static function registerLogRotate() {
853
-		$systemConfig = \OC::$server->getSystemConfig();
854
-		if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !self::checkUpgrade(false)) {
855
-			//don't try to do this before we are properly setup
856
-			//use custom logfile path if defined, otherwise use default of nextcloud.log in data directory
857
-			\OC::$server->getJobList()->add('OC\Log\Rotate');
858
-		}
859
-	}
860
-
861
-	/**
862
-	 * register hooks for the filesystem
863
-	 */
864
-	public static function registerFilesystemHooks() {
865
-		// Check for blacklisted files
866
-		OC_Hook::connect('OC_Filesystem', 'write', 'OC\Files\Filesystem', 'isBlacklisted');
867
-		OC_Hook::connect('OC_Filesystem', 'rename', 'OC\Files\Filesystem', 'isBlacklisted');
868
-	}
869
-
870
-	/**
871
-	 * register hooks for sharing
872
-	 */
873
-	public static function registerShareHooks() {
874
-		if (\OC::$server->getSystemConfig()->getValue('installed')) {
875
-			OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share20\Hooks', 'post_deleteUser');
876
-			OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share20\Hooks', 'post_removeFromGroup');
877
-			OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share20\Hooks', 'post_deleteGroup');
878
-		}
879
-	}
880
-
881
-	protected static function registerAutoloaderCache() {
882
-		// The class loader takes an optional low-latency cache, which MUST be
883
-		// namespaced. The instanceid is used for namespacing, but might be
884
-		// unavailable at this point. Furthermore, it might not be possible to
885
-		// generate an instanceid via \OC_Util::getInstanceId() because the
886
-		// config file may not be writable. As such, we only register a class
887
-		// loader cache if instanceid is available without trying to create one.
888
-		$instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null);
889
-		if ($instanceId) {
890
-			try {
891
-				$memcacheFactory = \OC::$server->getMemCacheFactory();
892
-				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
893
-			} catch (\Exception $ex) {
894
-			}
895
-		}
896
-	}
897
-
898
-	/**
899
-	 * Handle the request
900
-	 */
901
-	public static function handleRequest() {
902
-
903
-		\OC::$server->getEventLogger()->start('handle_request', 'Handle request');
904
-		$systemConfig = \OC::$server->getSystemConfig();
905
-		// load all the classpaths from the enabled apps so they are available
906
-		// in the routing files of each app
907
-		OC::loadAppClassPaths();
908
-
909
-		// Check if Nextcloud is installed or in maintenance (update) mode
910
-		if (!$systemConfig->getValue('installed', false)) {
911
-			\OC::$server->getSession()->clear();
912
-			$setupHelper = new OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(),
913
-				\OC::$server->getL10N('lib'), \OC::$server->getThemingDefaults(), \OC::$server->getLogger(),
914
-				\OC::$server->getSecureRandom());
915
-			$controller = new OC\Core\Controller\SetupController($setupHelper);
916
-			$controller->run($_POST);
917
-			exit();
918
-		}
919
-
920
-		$request = \OC::$server->getRequest();
921
-		$requestPath = $request->getRawPathInfo();
922
-		if ($requestPath === '/heartbeat') {
923
-			return;
924
-		}
925
-		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
926
-			self::checkMaintenanceMode();
927
-			self::checkUpgrade();
928
-		}
929
-
930
-		// emergency app disabling
931
-		if ($requestPath === '/disableapp'
932
-			&& $request->getMethod() === 'POST'
933
-			&& ((string)$request->getParam('appid')) !== ''
934
-		) {
935
-			\OCP\JSON::callCheck();
936
-			\OCP\JSON::checkAdminUser();
937
-			$appId = (string)$request->getParam('appid');
938
-			$appId = \OC_App::cleanAppId($appId);
939
-
940
-			\OC_App::disable($appId);
941
-			\OC_JSON::success();
942
-			exit();
943
-		}
944
-
945
-		// Always load authentication apps
946
-		OC_App::loadApps(['authentication']);
947
-
948
-		// Load minimum set of apps
949
-		if (!self::checkUpgrade(false)
950
-			&& !$systemConfig->getValue('maintenance', false)) {
951
-			// For logged-in users: Load everything
952
-			if(\OC::$server->getUserSession()->isLoggedIn()) {
953
-				OC_App::loadApps();
954
-			} else {
955
-				// For guests: Load only filesystem and logging
956
-				OC_App::loadApps(array('filesystem', 'logging'));
957
-				self::handleLogin($request);
958
-			}
959
-		}
960
-
961
-		if (!self::$CLI) {
962
-			try {
963
-				if (!$systemConfig->getValue('maintenance', false) && !self::checkUpgrade(false)) {
964
-					OC_App::loadApps(array('filesystem', 'logging'));
965
-					OC_App::loadApps();
966
-				}
967
-				OC_Util::setupFS();
968
-				OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo());
969
-				return;
970
-			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
971
-				//header('HTTP/1.0 404 Not Found');
972
-			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
973
-				OC_Response::setStatus(405);
974
-				return;
975
-			}
976
-		}
977
-
978
-		// Handle WebDAV
979
-		if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
980
-			// not allowed any more to prevent people
981
-			// mounting this root directly.
982
-			// Users need to mount remote.php/webdav instead.
983
-			header('HTTP/1.1 405 Method Not Allowed');
984
-			header('Status: 405 Method Not Allowed');
985
-			return;
986
-		}
987
-
988
-		// Someone is logged in
989
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
990
-			OC_App::loadApps();
991
-			OC_User::setupBackends();
992
-			OC_Util::setupFS();
993
-			// FIXME
994
-			// Redirect to default application
995
-			OC_Util::redirectToDefaultPage();
996
-		} else {
997
-			// Not handled and not logged in
998
-			header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
999
-		}
1000
-	}
1001
-
1002
-	/**
1003
-	 * Check login: apache auth, auth token, basic auth
1004
-	 *
1005
-	 * @param OCP\IRequest $request
1006
-	 * @return boolean
1007
-	 */
1008
-	static function handleLogin(OCP\IRequest $request) {
1009
-		$userSession = self::$server->getUserSession();
1010
-		if (OC_User::handleApacheAuth()) {
1011
-			return true;
1012
-		}
1013
-		if ($userSession->tryTokenLogin($request)) {
1014
-			return true;
1015
-		}
1016
-		if (isset($_COOKIE['nc_username'])
1017
-			&& isset($_COOKIE['nc_token'])
1018
-			&& isset($_COOKIE['nc_session_id'])
1019
-			&& $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1020
-			return true;
1021
-		}
1022
-		if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) {
1023
-			return true;
1024
-		}
1025
-		return false;
1026
-	}
1027
-
1028
-	protected static function handleAuthHeaders() {
1029
-		//copy http auth headers for apache+php-fcgid work around
1030
-		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1031
-			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1032
-		}
1033
-
1034
-		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1035
-		$vars = array(
1036
-			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1037
-			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1038
-		);
1039
-		foreach ($vars as $var) {
1040
-			if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1041
-				list($name, $password) = explode(':', base64_decode($matches[1]), 2);
1042
-				$_SERVER['PHP_AUTH_USER'] = $name;
1043
-				$_SERVER['PHP_AUTH_PW'] = $password;
1044
-				break;
1045
-			}
1046
-		}
1047
-	}
62
+    /**
63
+     * Associative array for autoloading. classname => filename
64
+     */
65
+    public static $CLASSPATH = array();
66
+    /**
67
+     * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
68
+     */
69
+    public static $SERVERROOT = '';
70
+    /**
71
+     * the current request path relative to the Nextcloud root (e.g. files/index.php)
72
+     */
73
+    private static $SUBURI = '';
74
+    /**
75
+     * the Nextcloud root path for http requests (e.g. nextcloud/)
76
+     */
77
+    public static $WEBROOT = '';
78
+    /**
79
+     * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
80
+     * web path in 'url'
81
+     */
82
+    public static $APPSROOTS = array();
83
+
84
+    /**
85
+     * @var string
86
+     */
87
+    public static $configDir;
88
+
89
+    /**
90
+     * requested app
91
+     */
92
+    public static $REQUESTEDAPP = '';
93
+
94
+    /**
95
+     * check if Nextcloud runs in cli mode
96
+     */
97
+    public static $CLI = false;
98
+
99
+    /**
100
+     * @var \OC\Autoloader $loader
101
+     */
102
+    public static $loader = null;
103
+
104
+    /** @var \Composer\Autoload\ClassLoader $composerAutoloader */
105
+    public static $composerAutoloader = null;
106
+
107
+    /**
108
+     * @var \OC\Server
109
+     */
110
+    public static $server = null;
111
+
112
+    /**
113
+     * @var \OC\Config
114
+     */
115
+    private static $config = null;
116
+
117
+    /**
118
+     * @throws \RuntimeException when the 3rdparty directory is missing or
119
+     * the app path list is empty or contains an invalid path
120
+     */
121
+    public static function initPaths() {
122
+        if(defined('PHPUNIT_CONFIG_DIR')) {
123
+            self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
124
+        } elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
125
+            self::$configDir = OC::$SERVERROOT . '/tests/config/';
126
+        } elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
127
+            self::$configDir = rtrim($dir, '/') . '/';
128
+        } else {
129
+            self::$configDir = OC::$SERVERROOT . '/config/';
130
+        }
131
+        self::$config = new \OC\Config(self::$configDir);
132
+
133
+        OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
134
+        /**
135
+         * FIXME: The following lines are required because we can't yet instantiiate
136
+         *        \OC::$server->getRequest() since \OC::$server does not yet exist.
137
+         */
138
+        $params = [
139
+            'server' => [
140
+                'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
141
+                'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
142
+            ],
143
+        ];
144
+        $fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
145
+        $scriptName = $fakeRequest->getScriptName();
146
+        if (substr($scriptName, -1) == '/') {
147
+            $scriptName .= 'index.php';
148
+            //make sure suburi follows the same rules as scriptName
149
+            if (substr(OC::$SUBURI, -9) != 'index.php') {
150
+                if (substr(OC::$SUBURI, -1) != '/') {
151
+                    OC::$SUBURI = OC::$SUBURI . '/';
152
+                }
153
+                OC::$SUBURI = OC::$SUBURI . 'index.php';
154
+            }
155
+        }
156
+
157
+
158
+        if (OC::$CLI) {
159
+            OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
160
+        } else {
161
+            if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
162
+                OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
163
+
164
+                if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
165
+                    OC::$WEBROOT = '/' . OC::$WEBROOT;
166
+                }
167
+            } else {
168
+                // The scriptName is not ending with OC::$SUBURI
169
+                // This most likely means that we are calling from CLI.
170
+                // However some cron jobs still need to generate
171
+                // a web URL, so we use overwritewebroot as a fallback.
172
+                OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
173
+            }
174
+
175
+            // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
176
+            // slash which is required by URL generation.
177
+            if($_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
178
+                    substr($_SERVER['REQUEST_URI'], -1) !== '/') {
179
+                header('Location: '.\OC::$WEBROOT.'/');
180
+                exit();
181
+            }
182
+        }
183
+
184
+        // search the apps folder
185
+        $config_paths = self::$config->getValue('apps_paths', array());
186
+        if (!empty($config_paths)) {
187
+            foreach ($config_paths as $paths) {
188
+                if (isset($paths['url']) && isset($paths['path'])) {
189
+                    $paths['url'] = rtrim($paths['url'], '/');
190
+                    $paths['path'] = rtrim($paths['path'], '/');
191
+                    OC::$APPSROOTS[] = $paths;
192
+                }
193
+            }
194
+        } elseif (file_exists(OC::$SERVERROOT . '/apps')) {
195
+            OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true);
196
+        } elseif (file_exists(OC::$SERVERROOT . '/../apps')) {
197
+            OC::$APPSROOTS[] = array(
198
+                'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps',
199
+                'url' => '/apps',
200
+                'writable' => true
201
+            );
202
+        }
203
+
204
+        if (empty(OC::$APPSROOTS)) {
205
+            throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
206
+                . ' or the folder above. You can also configure the location in the config.php file.');
207
+        }
208
+        $paths = array();
209
+        foreach (OC::$APPSROOTS as $path) {
210
+            $paths[] = $path['path'];
211
+            if (!is_dir($path['path'])) {
212
+                throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
213
+                    . ' Nextcloud folder or the folder above. You can also configure the location in the'
214
+                    . ' config.php file.', $path['path']));
215
+            }
216
+        }
217
+
218
+        // set the right include path
219
+        set_include_path(
220
+            implode(PATH_SEPARATOR, $paths)
221
+        );
222
+    }
223
+
224
+    public static function checkConfig() {
225
+        $l = \OC::$server->getL10N('lib');
226
+
227
+        // Create config if it does not already exist
228
+        $configFilePath = self::$configDir .'/config.php';
229
+        if(!file_exists($configFilePath)) {
230
+            @touch($configFilePath);
231
+        }
232
+
233
+        // Check if config is writable
234
+        $configFileWritable = is_writable($configFilePath);
235
+        if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
236
+            || !$configFileWritable && self::checkUpgrade(false)) {
237
+
238
+            $urlGenerator = \OC::$server->getURLGenerator();
239
+
240
+            if (self::$CLI) {
241
+                echo $l->t('Cannot write into "config" directory!')."\n";
242
+                echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n";
243
+                echo "\n";
244
+                echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n";
245
+                exit;
246
+            } else {
247
+                OC_Template::printErrorPage(
248
+                    $l->t('Cannot write into "config" directory!'),
249
+                    $l->t('This can usually be fixed by '
250
+                    . '%sgiving the webserver write access to the config directory%s.',
251
+                        array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'))
252
+                );
253
+            }
254
+        }
255
+    }
256
+
257
+    public static function checkInstalled() {
258
+        if (defined('OC_CONSOLE')) {
259
+            return;
260
+        }
261
+        // Redirect to installer if not installed
262
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
263
+            if (OC::$CLI) {
264
+                throw new Exception('Not installed');
265
+            } else {
266
+                $url = OC::$WEBROOT . '/index.php';
267
+                header('Location: ' . $url);
268
+            }
269
+            exit();
270
+        }
271
+    }
272
+
273
+    public static function checkMaintenanceMode() {
274
+        // Allow ajax update script to execute without being stopped
275
+        if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') {
276
+            // send http status 503
277
+            header('HTTP/1.1 503 Service Temporarily Unavailable');
278
+            header('Status: 503 Service Temporarily Unavailable');
279
+            header('Retry-After: 120');
280
+
281
+            // render error page
282
+            $template = new OC_Template('', 'update.user', 'guest');
283
+            OC_Util::addScript('maintenance-check');
284
+            $template->printPage();
285
+            die();
286
+        }
287
+    }
288
+
289
+    /**
290
+     * Checks if the version requires an update and shows
291
+     * @param bool $showTemplate Whether an update screen should get shown
292
+     * @return bool|void
293
+     */
294
+    public static function checkUpgrade($showTemplate = true) {
295
+        if (\OCP\Util::needUpgrade()) {
296
+            $systemConfig = \OC::$server->getSystemConfig();
297
+            if ($showTemplate && !$systemConfig->getValue('maintenance', false)) {
298
+                self::printUpgradePage();
299
+                exit();
300
+            } else {
301
+                return true;
302
+            }
303
+        }
304
+        return false;
305
+    }
306
+
307
+    /**
308
+     * Prints the upgrade page
309
+     */
310
+    private static function printUpgradePage() {
311
+        $systemConfig = \OC::$server->getSystemConfig();
312
+
313
+        $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
314
+        $tooBig = false;
315
+        if (!$disableWebUpdater) {
316
+            $apps = \OC::$server->getAppManager();
317
+            $tooBig = $apps->isInstalled('user_ldap') || $apps->isInstalled('user_shibboleth');
318
+            if (!$tooBig) {
319
+                // count users
320
+                $stats = \OC::$server->getUserManager()->countUsers();
321
+                $totalUsers = array_sum($stats);
322
+                $tooBig = ($totalUsers > 50);
323
+            }
324
+        }
325
+        if ($disableWebUpdater || $tooBig) {
326
+            // send http status 503
327
+            header('HTTP/1.1 503 Service Temporarily Unavailable');
328
+            header('Status: 503 Service Temporarily Unavailable');
329
+            header('Retry-After: 120');
330
+
331
+            // render error page
332
+            $template = new OC_Template('', 'update.use-cli', 'guest');
333
+            $template->assign('productName', 'nextcloud'); // for now
334
+            $template->assign('version', OC_Util::getVersionString());
335
+            $template->assign('tooBig', $tooBig);
336
+
337
+            $template->printPage();
338
+            die();
339
+        }
340
+
341
+        // check whether this is a core update or apps update
342
+        $installedVersion = $systemConfig->getValue('version', '0.0.0');
343
+        $currentVersion = implode('.', \OCP\Util::getVersion());
344
+
345
+        // if not a core upgrade, then it's apps upgrade
346
+        $isAppsOnlyUpgrade = (version_compare($currentVersion, $installedVersion, '='));
347
+
348
+        $oldTheme = $systemConfig->getValue('theme');
349
+        $systemConfig->setValue('theme', '');
350
+        OC_Util::addScript('config'); // needed for web root
351
+        OC_Util::addScript('update');
352
+
353
+        /** @var \OC\App\AppManager $appManager */
354
+        $appManager = \OC::$server->getAppManager();
355
+
356
+        $tmpl = new OC_Template('', 'update.admin', 'guest');
357
+        $tmpl->assign('version', OC_Util::getVersionString());
358
+        $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
359
+
360
+        // get third party apps
361
+        $ocVersion = \OCP\Util::getVersion();
362
+        $incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
363
+        $incompatibleShippedApps = [];
364
+        foreach ($incompatibleApps as $appInfo) {
365
+            if ($appManager->isShipped($appInfo['id'])) {
366
+                $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
367
+            }
368
+        }
369
+
370
+        if (!empty($incompatibleShippedApps)) {
371
+            $l = \OC::$server->getL10N('core');
372
+            $hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
373
+            throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
374
+        }
375
+
376
+        $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
377
+        $tmpl->assign('incompatibleAppsList', $incompatibleApps);
378
+        $tmpl->assign('productName', 'Nextcloud'); // for now
379
+        $tmpl->assign('oldTheme', $oldTheme);
380
+        $tmpl->printPage();
381
+    }
382
+
383
+    public static function initSession() {
384
+        // prevents javascript from accessing php session cookies
385
+        ini_set('session.cookie_httponly', true);
386
+
387
+        // set the cookie path to the Nextcloud directory
388
+        $cookie_path = OC::$WEBROOT ? : '/';
389
+        ini_set('session.cookie_path', $cookie_path);
390
+
391
+        // Let the session name be changed in the initSession Hook
392
+        $sessionName = OC_Util::getInstanceId();
393
+
394
+        try {
395
+            // Allow session apps to create a custom session object
396
+            $useCustomSession = false;
397
+            $session = self::$server->getSession();
398
+            OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession));
399
+            if (!$useCustomSession) {
400
+                // set the session name to the instance id - which is unique
401
+                $session = new \OC\Session\Internal($sessionName);
402
+            }
403
+
404
+            $cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
405
+            $session = $cryptoWrapper->wrapSession($session);
406
+            self::$server->setSession($session);
407
+
408
+            // if session can't be started break with http 500 error
409
+        } catch (Exception $e) {
410
+            \OCP\Util::logException('base', $e);
411
+            //show the user a detailed error page
412
+            OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
413
+            OC_Template::printExceptionErrorPage($e);
414
+            die();
415
+        }
416
+
417
+        $sessionLifeTime = self::getSessionLifeTime();
418
+
419
+        // session timeout
420
+        if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
421
+            if (isset($_COOKIE[session_name()])) {
422
+                setcookie(session_name(), null, -1, self::$WEBROOT ? : '/');
423
+            }
424
+            \OC::$server->getUserSession()->logout();
425
+        }
426
+
427
+        $session->set('LAST_ACTIVITY', time());
428
+    }
429
+
430
+    /**
431
+     * @return string
432
+     */
433
+    private static function getSessionLifeTime() {
434
+        return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
435
+    }
436
+
437
+    public static function loadAppClassPaths() {
438
+        foreach (OC_App::getEnabledApps() as $app) {
439
+            $appPath = OC_App::getAppPath($app);
440
+            if ($appPath === false) {
441
+                continue;
442
+            }
443
+
444
+            $file = $appPath . '/appinfo/classpath.php';
445
+            if (file_exists($file)) {
446
+                require_once $file;
447
+            }
448
+        }
449
+    }
450
+
451
+    /**
452
+     * Try to set some values to the required Nextcloud default
453
+     */
454
+    public static function setRequiredIniValues() {
455
+        @ini_set('default_charset', 'UTF-8');
456
+        @ini_set('gd.jpeg_ignore_warning', 1);
457
+    }
458
+
459
+    /**
460
+     * Send the same site cookies
461
+     */
462
+    private static function sendSameSiteCookies() {
463
+        $cookieParams = session_get_cookie_params();
464
+        $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
465
+        $policies = [
466
+            'lax',
467
+            'strict',
468
+        ];
469
+
470
+        // Append __Host to the cookie if it meets the requirements
471
+        $cookiePrefix = '';
472
+        if($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
473
+            $cookiePrefix = '__Host-';
474
+        }
475
+
476
+        foreach($policies as $policy) {
477
+            header(
478
+                sprintf(
479
+                    'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
480
+                    $cookiePrefix,
481
+                    $policy,
482
+                    $cookieParams['path'],
483
+                    $policy
484
+                ),
485
+                false
486
+            );
487
+        }
488
+    }
489
+
490
+    /**
491
+     * Same Site cookie to further mitigate CSRF attacks. This cookie has to
492
+     * be set in every request if cookies are sent to add a second level of
493
+     * defense against CSRF.
494
+     *
495
+     * If the cookie is not sent this will set the cookie and reload the page.
496
+     * We use an additional cookie since we want to protect logout CSRF and
497
+     * also we can't directly interfere with PHP's session mechanism.
498
+     */
499
+    private static function performSameSiteCookieProtection() {
500
+        $request = \OC::$server->getRequest();
501
+
502
+        // Some user agents are notorious and don't really properly follow HTTP
503
+        // specifications. For those, have an automated opt-out. Since the protection
504
+        // for remote.php is applied in base.php as starting point we need to opt out
505
+        // here.
506
+        $incompatibleUserAgents = [
507
+            // OS X Finder
508
+            '/^WebDAVFS/',
509
+        ];
510
+        if($request->isUserAgent($incompatibleUserAgents)) {
511
+            return;
512
+        }
513
+
514
+        if(count($_COOKIE) > 0) {
515
+            $requestUri = $request->getScriptName();
516
+            $processingScript = explode('/', $requestUri);
517
+            $processingScript = $processingScript[count($processingScript)-1];
518
+            // FIXME: In a SAML scenario we don't get any strict or lax cookie
519
+            // send for the ACS endpoint. Since we have some legacy code in Nextcloud
520
+            // (direct PHP files) the enforcement of lax cookies is performed here
521
+            // instead of the middleware.
522
+            //
523
+            // This means we cannot exclude some routes from the cookie validation,
524
+            // which normally is not a problem but is a little bit cumbersome for
525
+            // this use-case.
526
+            // Once the old legacy PHP endpoints have been removed we can move
527
+            // the verification into a middleware and also adds some exemptions.
528
+            //
529
+            // Questions about this code? Ask Lukas ;-)
530
+            $currentUrl = substr(explode('?',$request->getRequestUri(), 2)[0], strlen(\OC::$WEBROOT));
531
+            if($currentUrl === '/index.php/apps/user_saml/saml/acs' || $currentUrl === '/apps/user_saml/saml/acs') {
532
+                return;
533
+            }
534
+            // For the "index.php" endpoint only a lax cookie is required.
535
+            if($processingScript === 'index.php') {
536
+                if(!$request->passesLaxCookieCheck()) {
537
+                    self::sendSameSiteCookies();
538
+                    header('Location: '.$_SERVER['REQUEST_URI']);
539
+                    exit();
540
+                }
541
+            } else {
542
+                // All other endpoints require the lax and the strict cookie
543
+                if(!$request->passesStrictCookieCheck()) {
544
+                    self::sendSameSiteCookies();
545
+                    // Debug mode gets access to the resources without strict cookie
546
+                    // due to the fact that the SabreDAV browser also lives there.
547
+                    if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
548
+                        http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
549
+                        exit();
550
+                    }
551
+                }
552
+            }
553
+        } elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
554
+            self::sendSameSiteCookies();
555
+        }
556
+    }
557
+
558
+    public static function init() {
559
+        // calculate the root directories
560
+        OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
561
+
562
+        // register autoloader
563
+        $loaderStart = microtime(true);
564
+        require_once __DIR__ . '/autoloader.php';
565
+        self::$loader = new \OC\Autoloader([
566
+            OC::$SERVERROOT . '/lib/private/legacy',
567
+        ]);
568
+        if (defined('PHPUNIT_RUN')) {
569
+            self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
570
+        }
571
+        spl_autoload_register(array(self::$loader, 'load'));
572
+        $loaderEnd = microtime(true);
573
+
574
+        self::$CLI = (php_sapi_name() == 'cli');
575
+
576
+        // Add default composer PSR-4 autoloader
577
+        self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
578
+
579
+        try {
580
+            self::initPaths();
581
+            // setup 3rdparty autoloader
582
+            $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
583
+            if (!file_exists($vendorAutoLoad)) {
584
+                throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
585
+            }
586
+            require_once $vendorAutoLoad;
587
+
588
+        } catch (\RuntimeException $e) {
589
+            if (!self::$CLI) {
590
+                $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
591
+                $protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1';
592
+                header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE);
593
+            }
594
+            // we can't use the template error page here, because this needs the
595
+            // DI container which isn't available yet
596
+            print($e->getMessage());
597
+            exit();
598
+        }
599
+
600
+        // setup the basic server
601
+        self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
602
+        \OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
603
+        \OC::$server->getEventLogger()->start('boot', 'Initialize');
604
+
605
+        // Don't display errors and log them
606
+        error_reporting(E_ALL | E_STRICT);
607
+        @ini_set('display_errors', 0);
608
+        @ini_set('log_errors', 1);
609
+
610
+        if(!date_default_timezone_set('UTC')) {
611
+            throw new \RuntimeException('Could not set timezone to UTC');
612
+        };
613
+
614
+        //try to configure php to enable big file uploads.
615
+        //this doesn´t work always depending on the webserver and php configuration.
616
+        //Let´s try to overwrite some defaults anyway
617
+
618
+        //try to set the maximum execution time to 60min
619
+        @set_time_limit(3600);
620
+        @ini_set('max_execution_time', 3600);
621
+        @ini_set('max_input_time', 3600);
622
+
623
+        //try to set the maximum filesize to 10G
624
+        @ini_set('upload_max_filesize', '10G');
625
+        @ini_set('post_max_size', '10G');
626
+        @ini_set('file_uploads', '50');
627
+
628
+        self::setRequiredIniValues();
629
+        self::handleAuthHeaders();
630
+        self::registerAutoloaderCache();
631
+
632
+        // initialize intl fallback is necessary
633
+        \Patchwork\Utf8\Bootup::initIntl();
634
+        OC_Util::isSetLocaleWorking();
635
+
636
+        if (!defined('PHPUNIT_RUN')) {
637
+            OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger());
638
+            $debug = \OC::$server->getConfig()->getSystemValue('debug', false);
639
+            OC\Log\ErrorHandler::register($debug);
640
+        }
641
+
642
+        \OC::$server->getEventLogger()->start('init_session', 'Initialize session');
643
+        OC_App::loadApps(array('session'));
644
+        if (!self::$CLI) {
645
+            self::initSession();
646
+        }
647
+        \OC::$server->getEventLogger()->end('init_session');
648
+        self::checkConfig();
649
+        self::checkInstalled();
650
+
651
+        OC_Response::addSecurityHeaders();
652
+        if(self::$server->getRequest()->getServerProtocol() === 'https') {
653
+            ini_set('session.cookie_secure', true);
654
+        }
655
+
656
+        self::performSameSiteCookieProtection();
657
+
658
+        if (!defined('OC_CONSOLE')) {
659
+            $errors = OC_Util::checkServer(\OC::$server->getConfig());
660
+            if (count($errors) > 0) {
661
+                if (self::$CLI) {
662
+                    // Convert l10n string into regular string for usage in database
663
+                    $staticErrors = [];
664
+                    foreach ($errors as $error) {
665
+                        echo $error['error'] . "\n";
666
+                        echo $error['hint'] . "\n\n";
667
+                        $staticErrors[] = [
668
+                            'error' => (string)$error['error'],
669
+                            'hint' => (string)$error['hint'],
670
+                        ];
671
+                    }
672
+
673
+                    try {
674
+                        \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
675
+                    } catch (\Exception $e) {
676
+                        echo('Writing to database failed');
677
+                    }
678
+                    exit(1);
679
+                } else {
680
+                    OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
681
+                    OC_Util::addStyle('guest');
682
+                    OC_Template::printGuestPage('', 'error', array('errors' => $errors));
683
+                    exit;
684
+                }
685
+            } elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
686
+                \OC::$server->getConfig()->deleteAppValue('core', 'cronErrors');
687
+            }
688
+        }
689
+        //try to set the session lifetime
690
+        $sessionLifeTime = self::getSessionLifeTime();
691
+        @ini_set('gc_maxlifetime', (string)$sessionLifeTime);
692
+
693
+        $systemConfig = \OC::$server->getSystemConfig();
694
+
695
+        // User and Groups
696
+        if (!$systemConfig->getValue("installed", false)) {
697
+            self::$server->getSession()->set('user_id', '');
698
+        }
699
+
700
+        OC_User::useBackend(new \OC\User\Database());
701
+        \OC::$server->getGroupManager()->addBackend(new \OC\Group\Database());
702
+
703
+        // Subscribe to the hook
704
+        \OCP\Util::connectHook(
705
+            '\OCA\Files_Sharing\API\Server2Server',
706
+            'preLoginNameUsedAsUserName',
707
+            '\OC\User\Database',
708
+            'preLoginNameUsedAsUserName'
709
+        );
710
+
711
+        //setup extra user backends
712
+        if (!self::checkUpgrade(false)) {
713
+            OC_User::setupBackends();
714
+        } else {
715
+            // Run upgrades in incognito mode
716
+            OC_User::setIncognitoMode(true);
717
+        }
718
+
719
+        self::registerCacheHooks();
720
+        self::registerFilesystemHooks();
721
+        self::registerShareHooks();
722
+        self::registerLogRotate();
723
+        self::registerEncryptionWrapper();
724
+        self::registerEncryptionHooks();
725
+        self::registerAccountHooks();
726
+        self::registerSettingsHooks();
727
+
728
+        //make sure temporary files are cleaned up
729
+        $tmpManager = \OC::$server->getTempManager();
730
+        register_shutdown_function(array($tmpManager, 'clean'));
731
+        $lockProvider = \OC::$server->getLockingProvider();
732
+        register_shutdown_function(array($lockProvider, 'releaseAll'));
733
+
734
+        // Check whether the sample configuration has been copied
735
+        if($systemConfig->getValue('copied_sample_config', false)) {
736
+            $l = \OC::$server->getL10N('lib');
737
+            header('HTTP/1.1 503 Service Temporarily Unavailable');
738
+            header('Status: 503 Service Temporarily Unavailable');
739
+            OC_Template::printErrorPage(
740
+                $l->t('Sample configuration detected'),
741
+                $l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php')
742
+            );
743
+            return;
744
+        }
745
+
746
+        $request = \OC::$server->getRequest();
747
+        $host = $request->getInsecureServerHost();
748
+        /**
749
+         * if the host passed in headers isn't trusted
750
+         * FIXME: Should not be in here at all :see_no_evil:
751
+         */
752
+        if (!OC::$CLI
753
+            // overwritehost is always trusted, workaround to not have to make
754
+            // \OC\AppFramework\Http\Request::getOverwriteHost public
755
+            && self::$server->getConfig()->getSystemValue('overwritehost') === ''
756
+            && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
757
+            && self::$server->getConfig()->getSystemValue('installed', false)
758
+        ) {
759
+            // Allow access to CSS resources
760
+            $isScssRequest = false;
761
+            if(strpos($request->getPathInfo(), '/css/') === 0) {
762
+                $isScssRequest = true;
763
+            }
764
+
765
+            if (!$isScssRequest) {
766
+                header('HTTP/1.1 400 Bad Request');
767
+                header('Status: 400 Bad Request');
768
+
769
+                \OC::$server->getLogger()->warning(
770
+                    'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
771
+                    [
772
+                        'app' => 'core',
773
+                        'remoteAddress' => $request->getRemoteAddress(),
774
+                        'host' => $host,
775
+                    ]
776
+                );
777
+
778
+                $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
779
+                $tmpl->assign('domain', $host);
780
+                $tmpl->printPage();
781
+
782
+                exit();
783
+            }
784
+        }
785
+        \OC::$server->getEventLogger()->end('boot');
786
+    }
787
+
788
+    /**
789
+     * register hooks for the cache
790
+     */
791
+    public static function registerCacheHooks() {
792
+        //don't try to do this before we are properly setup
793
+        if (\OC::$server->getSystemConfig()->getValue('installed', false) && !self::checkUpgrade(false)) {
794
+
795
+            // NOTE: This will be replaced to use OCP
796
+            $userSession = self::$server->getUserSession();
797
+            $userSession->listen('\OC\User', 'postLogin', function () {
798
+                try {
799
+                    $cache = new \OC\Cache\File();
800
+                    $cache->gc();
801
+                } catch (\OC\ServerNotAvailableException $e) {
802
+                    // not a GC exception, pass it on
803
+                    throw $e;
804
+                } catch (\Exception $e) {
805
+                    // a GC exception should not prevent users from using OC,
806
+                    // so log the exception
807
+                    \OC::$server->getLogger()->warning('Exception when running cache gc: ' . $e->getMessage(), array('app' => 'core'));
808
+                }
809
+            });
810
+        }
811
+    }
812
+
813
+    public static function registerSettingsHooks() {
814
+        $dispatcher = \OC::$server->getEventDispatcher();
815
+        $dispatcher->addListener(OCP\App\ManagerEvent::EVENT_APP_DISABLE, function($event) {
816
+            /** @var \OCP\App\ManagerEvent $event */
817
+            \OC::$server->getSettingsManager()->onAppDisabled($event->getAppID());
818
+        });
819
+        $dispatcher->addListener(OCP\App\ManagerEvent::EVENT_APP_UPDATE, function($event) {
820
+            /** @var \OCP\App\ManagerEvent $event */
821
+            $jobList = \OC::$server->getJobList();
822
+            $job = 'OC\\Settings\\RemoveOrphaned';
823
+            if(!($jobList->has($job, null))) {
824
+                $jobList->add($job);
825
+            }
826
+        });
827
+    }
828
+
829
+    private static function registerEncryptionWrapper() {
830
+        $manager = self::$server->getEncryptionManager();
831
+        \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
832
+    }
833
+
834
+    private static function registerEncryptionHooks() {
835
+        $enabled = self::$server->getEncryptionManager()->isEnabled();
836
+        if ($enabled) {
837
+            \OCP\Util::connectHook('OCP\Share', 'post_shared', 'OC\Encryption\HookManager', 'postShared');
838
+            \OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OC\Encryption\HookManager', 'postUnshared');
839
+            \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OC\Encryption\HookManager', 'postRename');
840
+            \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', 'OC\Encryption\HookManager', 'postRestore');
841
+        }
842
+    }
843
+
844
+    private static function registerAccountHooks() {
845
+        $hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger());
846
+        \OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook');
847
+    }
848
+
849
+    /**
850
+     * register hooks for the cache
851
+     */
852
+    public static function registerLogRotate() {
853
+        $systemConfig = \OC::$server->getSystemConfig();
854
+        if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !self::checkUpgrade(false)) {
855
+            //don't try to do this before we are properly setup
856
+            //use custom logfile path if defined, otherwise use default of nextcloud.log in data directory
857
+            \OC::$server->getJobList()->add('OC\Log\Rotate');
858
+        }
859
+    }
860
+
861
+    /**
862
+     * register hooks for the filesystem
863
+     */
864
+    public static function registerFilesystemHooks() {
865
+        // Check for blacklisted files
866
+        OC_Hook::connect('OC_Filesystem', 'write', 'OC\Files\Filesystem', 'isBlacklisted');
867
+        OC_Hook::connect('OC_Filesystem', 'rename', 'OC\Files\Filesystem', 'isBlacklisted');
868
+    }
869
+
870
+    /**
871
+     * register hooks for sharing
872
+     */
873
+    public static function registerShareHooks() {
874
+        if (\OC::$server->getSystemConfig()->getValue('installed')) {
875
+            OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share20\Hooks', 'post_deleteUser');
876
+            OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share20\Hooks', 'post_removeFromGroup');
877
+            OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share20\Hooks', 'post_deleteGroup');
878
+        }
879
+    }
880
+
881
+    protected static function registerAutoloaderCache() {
882
+        // The class loader takes an optional low-latency cache, which MUST be
883
+        // namespaced. The instanceid is used for namespacing, but might be
884
+        // unavailable at this point. Furthermore, it might not be possible to
885
+        // generate an instanceid via \OC_Util::getInstanceId() because the
886
+        // config file may not be writable. As such, we only register a class
887
+        // loader cache if instanceid is available without trying to create one.
888
+        $instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null);
889
+        if ($instanceId) {
890
+            try {
891
+                $memcacheFactory = \OC::$server->getMemCacheFactory();
892
+                self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
893
+            } catch (\Exception $ex) {
894
+            }
895
+        }
896
+    }
897
+
898
+    /**
899
+     * Handle the request
900
+     */
901
+    public static function handleRequest() {
902
+
903
+        \OC::$server->getEventLogger()->start('handle_request', 'Handle request');
904
+        $systemConfig = \OC::$server->getSystemConfig();
905
+        // load all the classpaths from the enabled apps so they are available
906
+        // in the routing files of each app
907
+        OC::loadAppClassPaths();
908
+
909
+        // Check if Nextcloud is installed or in maintenance (update) mode
910
+        if (!$systemConfig->getValue('installed', false)) {
911
+            \OC::$server->getSession()->clear();
912
+            $setupHelper = new OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(),
913
+                \OC::$server->getL10N('lib'), \OC::$server->getThemingDefaults(), \OC::$server->getLogger(),
914
+                \OC::$server->getSecureRandom());
915
+            $controller = new OC\Core\Controller\SetupController($setupHelper);
916
+            $controller->run($_POST);
917
+            exit();
918
+        }
919
+
920
+        $request = \OC::$server->getRequest();
921
+        $requestPath = $request->getRawPathInfo();
922
+        if ($requestPath === '/heartbeat') {
923
+            return;
924
+        }
925
+        if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
926
+            self::checkMaintenanceMode();
927
+            self::checkUpgrade();
928
+        }
929
+
930
+        // emergency app disabling
931
+        if ($requestPath === '/disableapp'
932
+            && $request->getMethod() === 'POST'
933
+            && ((string)$request->getParam('appid')) !== ''
934
+        ) {
935
+            \OCP\JSON::callCheck();
936
+            \OCP\JSON::checkAdminUser();
937
+            $appId = (string)$request->getParam('appid');
938
+            $appId = \OC_App::cleanAppId($appId);
939
+
940
+            \OC_App::disable($appId);
941
+            \OC_JSON::success();
942
+            exit();
943
+        }
944
+
945
+        // Always load authentication apps
946
+        OC_App::loadApps(['authentication']);
947
+
948
+        // Load minimum set of apps
949
+        if (!self::checkUpgrade(false)
950
+            && !$systemConfig->getValue('maintenance', false)) {
951
+            // For logged-in users: Load everything
952
+            if(\OC::$server->getUserSession()->isLoggedIn()) {
953
+                OC_App::loadApps();
954
+            } else {
955
+                // For guests: Load only filesystem and logging
956
+                OC_App::loadApps(array('filesystem', 'logging'));
957
+                self::handleLogin($request);
958
+            }
959
+        }
960
+
961
+        if (!self::$CLI) {
962
+            try {
963
+                if (!$systemConfig->getValue('maintenance', false) && !self::checkUpgrade(false)) {
964
+                    OC_App::loadApps(array('filesystem', 'logging'));
965
+                    OC_App::loadApps();
966
+                }
967
+                OC_Util::setupFS();
968
+                OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo());
969
+                return;
970
+            } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
971
+                //header('HTTP/1.0 404 Not Found');
972
+            } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
973
+                OC_Response::setStatus(405);
974
+                return;
975
+            }
976
+        }
977
+
978
+        // Handle WebDAV
979
+        if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
980
+            // not allowed any more to prevent people
981
+            // mounting this root directly.
982
+            // Users need to mount remote.php/webdav instead.
983
+            header('HTTP/1.1 405 Method Not Allowed');
984
+            header('Status: 405 Method Not Allowed');
985
+            return;
986
+        }
987
+
988
+        // Someone is logged in
989
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
990
+            OC_App::loadApps();
991
+            OC_User::setupBackends();
992
+            OC_Util::setupFS();
993
+            // FIXME
994
+            // Redirect to default application
995
+            OC_Util::redirectToDefaultPage();
996
+        } else {
997
+            // Not handled and not logged in
998
+            header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
999
+        }
1000
+    }
1001
+
1002
+    /**
1003
+     * Check login: apache auth, auth token, basic auth
1004
+     *
1005
+     * @param OCP\IRequest $request
1006
+     * @return boolean
1007
+     */
1008
+    static function handleLogin(OCP\IRequest $request) {
1009
+        $userSession = self::$server->getUserSession();
1010
+        if (OC_User::handleApacheAuth()) {
1011
+            return true;
1012
+        }
1013
+        if ($userSession->tryTokenLogin($request)) {
1014
+            return true;
1015
+        }
1016
+        if (isset($_COOKIE['nc_username'])
1017
+            && isset($_COOKIE['nc_token'])
1018
+            && isset($_COOKIE['nc_session_id'])
1019
+            && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1020
+            return true;
1021
+        }
1022
+        if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) {
1023
+            return true;
1024
+        }
1025
+        return false;
1026
+    }
1027
+
1028
+    protected static function handleAuthHeaders() {
1029
+        //copy http auth headers for apache+php-fcgid work around
1030
+        if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1031
+            $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1032
+        }
1033
+
1034
+        // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1035
+        $vars = array(
1036
+            'HTTP_AUTHORIZATION', // apache+php-cgi work around
1037
+            'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1038
+        );
1039
+        foreach ($vars as $var) {
1040
+            if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1041
+                list($name, $password) = explode(':', base64_decode($matches[1]), 2);
1042
+                $_SERVER['PHP_AUTH_USER'] = $name;
1043
+                $_SERVER['PHP_AUTH_PW'] = $password;
1044
+                break;
1045
+            }
1046
+        }
1047
+    }
1048 1048
 }
1049 1049
 
1050 1050
 OC::init();
Please login to merge, or discard this patch.