Completed
Pull Request — stable10 (#4802)
by Morris
22:59 queued 10:49
created
apps/user_ldap/lib/Access.php 3 patches
Doc Comments   +9 added lines, -7 removed lines patch added patch discarded remove patch
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
 
398 398
 	/**
399 399
 	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
400
-	 * @param string $dn the dn of the user object
400
+	 * @param string $fdn the dn of the user object
401 401
 	 * @param string $ldapName optional, the display name of the object
402 402
 	 * @return string|false with with the name to use in ownCloud
403 403
 	 */
@@ -414,7 +414,7 @@  discard block
 block discarded – undo
414 414
 
415 415
 	/**
416 416
 	 * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
417
-	 * @param string $dn the dn of the user object
417
+	 * @param string $fdn the dn of the user object
418 418
 	 * @param string $ldapName optional, the display name of the object
419 419
 	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
420 420
 	 * @return string|false with with the name to use in ownCloud
@@ -674,7 +674,7 @@  discard block
 block discarded – undo
674 674
 	 * the login filter.
675 675
 	 *
676 676
 	 * @param string $loginName
677
-	 * @param array $attributes optional, list of attributes to read
677
+	 * @param string[] $attributes optional, list of attributes to read
678 678
 	 * @return array
679 679
 	 */
680 680
 	public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
@@ -747,7 +747,7 @@  discard block
 block discarded – undo
747 747
 
748 748
 	/**
749 749
 	 * @param string $filter
750
-	 * @param string|string[] $attr
750
+	 * @param string[] $attr
751 751
 	 * @param int $limit
752 752
 	 * @param int $offset
753 753
 	 * @return array
@@ -795,7 +795,7 @@  discard block
 block discarded – undo
795 795
 
796 796
 	/**
797 797
 	 * @param string $filter
798
-	 * @param string|string[] $attr
798
+	 * @param string[] $attr
799 799
 	 * @param int $limit
800 800
 	 * @param int $offset
801 801
 	 * @return false|int
@@ -845,6 +845,7 @@  discard block
 block discarded – undo
845 845
 	 * retrieved. Results will according to the order in the array.
846 846
 	 * @param int $limit optional, maximum results to be counted
847 847
 	 * @param int $offset optional, a starting point
848
+	 * @param string $filter
848 849
 	 * @return array|false array with the search result as first value and pagedSearchOK as
849 850
 	 * second | false if not successful
850 851
 	 */
@@ -891,7 +892,7 @@  discard block
 block discarded – undo
891 892
 	 * @param bool $pagedSearchOK whether a paged search has been executed
892 893
 	 * @param bool $skipHandling required for paged search when cookies to
893 894
 	 * prior results need to be gained
894
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
895
+	 * @return null|boolean cookie validity, true if we have more pages, false otherwise.
895 896
 	 */
896 897
 	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
897 898
 		$cookie = null;
@@ -1100,7 +1101,7 @@  discard block
 block discarded – undo
1100 1101
 
1101 1102
 	/**
1102 1103
 	 * @param string $name
1103
-	 * @return bool|mixed|string
1104
+	 * @return string
1104 1105
 	 */
1105 1106
 	public function sanitizeUsername($name) {
1106 1107
 		if($this->connection->ldapIgnoreNamingRules) {
@@ -1124,6 +1125,7 @@  discard block
 block discarded – undo
1124 1125
 	* escapes (user provided) parts for LDAP filter
1125 1126
 	* @param string $input, the provided value
1126 1127
 	* @param bool $allowAsterisk whether in * at the beginning should be preserved
1128
+	* @param string $input
1127 1129
 	* @return string the escaped string
1128 1130
 	*/
1129 1131
 	public function escapeFilterPart($input, $allowAsterisk = false) {
Please login to merge, or discard this patch.
Indentation   +1641 added lines, -1641 removed lines patch added patch discarded remove patch
@@ -50,1399 +50,1399 @@  discard block
 block discarded – undo
50 50
  * @package OCA\User_LDAP
51 51
  */
52 52
 class Access extends LDAPUtility implements IUserTools {
53
-	/**
54
-	 * @var \OCA\User_LDAP\Connection
55
-	 */
56
-	public $connection;
57
-	/** @var Manager */
58
-	public $userManager;
59
-	//never ever check this var directly, always use getPagedSearchResultState
60
-	protected $pagedSearchedSuccessful;
61
-
62
-	/**
63
-	 * @var string[] $cookies an array of returned Paged Result cookies
64
-	 */
65
-	protected $cookies = array();
66
-
67
-	/**
68
-	 * @var string $lastCookie the last cookie returned from a Paged Results
69
-	 * operation, defaults to an empty string
70
-	 */
71
-	protected $lastCookie = '';
72
-
73
-	/**
74
-	 * @var AbstractMapping $userMapper
75
-	 */
76
-	protected $userMapper;
77
-
78
-	/**
79
-	* @var AbstractMapping $userMapper
80
-	*/
81
-	protected $groupMapper;
53
+    /**
54
+     * @var \OCA\User_LDAP\Connection
55
+     */
56
+    public $connection;
57
+    /** @var Manager */
58
+    public $userManager;
59
+    //never ever check this var directly, always use getPagedSearchResultState
60
+    protected $pagedSearchedSuccessful;
61
+
62
+    /**
63
+     * @var string[] $cookies an array of returned Paged Result cookies
64
+     */
65
+    protected $cookies = array();
66
+
67
+    /**
68
+     * @var string $lastCookie the last cookie returned from a Paged Results
69
+     * operation, defaults to an empty string
70
+     */
71
+    protected $lastCookie = '';
72
+
73
+    /**
74
+     * @var AbstractMapping $userMapper
75
+     */
76
+    protected $userMapper;
77
+
78
+    /**
79
+     * @var AbstractMapping $userMapper
80
+     */
81
+    protected $groupMapper;
82 82
 	
83
-	/**
84
-	 * @var \OCA\User_LDAP\Helper
85
-	 */
86
-	private $helper;
87
-
88
-	public function __construct(Connection $connection, ILDAPWrapper $ldap,
89
-		Manager $userManager, Helper $helper) {
90
-		parent::__construct($ldap);
91
-		$this->connection = $connection;
92
-		$this->userManager = $userManager;
93
-		$this->userManager->setLdapAccess($this);
94
-		$this->helper = $helper;
95
-	}
96
-
97
-	/**
98
-	 * sets the User Mapper
99
-	 * @param AbstractMapping $mapper
100
-	 */
101
-	public function setUserMapper(AbstractMapping $mapper) {
102
-		$this->userMapper = $mapper;
103
-	}
104
-
105
-	/**
106
-	 * returns the User Mapper
107
-	 * @throws \Exception
108
-	 * @return AbstractMapping
109
-	 */
110
-	public function getUserMapper() {
111
-		if(is_null($this->userMapper)) {
112
-			throw new \Exception('UserMapper was not assigned to this Access instance.');
113
-		}
114
-		return $this->userMapper;
115
-	}
116
-
117
-	/**
118
-	 * sets the Group Mapper
119
-	 * @param AbstractMapping $mapper
120
-	 */
121
-	public function setGroupMapper(AbstractMapping $mapper) {
122
-		$this->groupMapper = $mapper;
123
-	}
124
-
125
-	/**
126
-	 * returns the Group Mapper
127
-	 * @throws \Exception
128
-	 * @return AbstractMapping
129
-	 */
130
-	public function getGroupMapper() {
131
-		if(is_null($this->groupMapper)) {
132
-			throw new \Exception('GroupMapper was not assigned to this Access instance.');
133
-		}
134
-		return $this->groupMapper;
135
-	}
136
-
137
-	/**
138
-	 * @return bool
139
-	 */
140
-	private function checkConnection() {
141
-		return ($this->connection instanceof Connection);
142
-	}
143
-
144
-	/**
145
-	 * returns the Connection instance
146
-	 * @return \OCA\User_LDAP\Connection
147
-	 */
148
-	public function getConnection() {
149
-		return $this->connection;
150
-	}
151
-
152
-	/**
153
-	 * reads a given attribute for an LDAP record identified by a DN
154
-	 * @param string $dn the record in question
155
-	 * @param string $attr the attribute that shall be retrieved
156
-	 *        if empty, just check the record's existence
157
-	 * @param string $filter
158
-	 * @return array|false an array of values on success or an empty
159
-	 *          array if $attr is empty, false otherwise
160
-	 */
161
-	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
162
-		if(!$this->checkConnection()) {
163
-			\OCP\Util::writeLog('user_ldap',
164
-				'No LDAP Connector assigned, access impossible for readAttribute.',
165
-				\OCP\Util::WARN);
166
-			return false;
167
-		}
168
-		$cr = $this->connection->getConnectionResource();
169
-		if(!$this->ldap->isResource($cr)) {
170
-			//LDAP not available
171
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
172
-			return false;
173
-		}
174
-		//Cancel possibly running Paged Results operation, otherwise we run in
175
-		//LDAP protocol errors
176
-		$this->abandonPagedSearch();
177
-		// openLDAP requires that we init a new Paged Search. Not needed by AD,
178
-		// but does not hurt either.
179
-		$pagingSize = intval($this->connection->ldapPagingSize);
180
-		// 0 won't result in replies, small numbers may leave out groups
181
-		// (cf. #12306), 500 is default for paging and should work everywhere.
182
-		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
183
-		$this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0);
184
-		$dn = $this->helper->DNasBaseParameter($dn);
185
-		$rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
186
-		if(!$this->ldap->isResource($rr)) {
187
-			if(!empty($attr)) {
188
-				//do not throw this message on userExists check, irritates
189
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
190
-			}
191
-			//in case an error occurs , e.g. object does not exist
192
-			return false;
193
-		}
194
-		if (empty($attr) && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
195
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
196
-			return array();
197
-		}
198
-		$er = $this->ldap->firstEntry($cr, $rr);
199
-		if(!$this->ldap->isResource($er)) {
200
-			//did not match the filter, return false
201
-			return false;
202
-		}
203
-		//LDAP attributes are not case sensitive
204
-		$result = \OCP\Util::mb_array_change_key_case(
205
-				$this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
206
-		$attr = mb_strtolower($attr, 'UTF-8');
207
-
208
-		if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
209
-			$values = array();
210
-			for($i=0;$i<$result[$attr]['count'];$i++) {
211
-				if($this->resemblesDN($attr)) {
212
-					$values[] = $this->helper->sanitizeDN($result[$attr][$i]);
213
-				} elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
214
-					$values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
215
-				} else {
216
-					$values[] = $result[$attr][$i];
217
-				}
218
-			}
219
-			return $values;
220
-		}
221
-		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
222
-		return false;
223
-	}
224
-
225
-	/**
226
-	 * checks whether the given attributes value is probably a DN
227
-	 * @param string $attr the attribute in question
228
-	 * @return boolean if so true, otherwise false
229
-	 */
230
-	private function resemblesDN($attr) {
231
-		$resemblingAttributes = array(
232
-			'dn',
233
-			'uniquemember',
234
-			'member',
235
-			// memberOf is an "operational" attribute, without a definition in any RFC
236
-			'memberof'
237
-		);
238
-		return in_array($attr, $resemblingAttributes);
239
-	}
240
-
241
-	/**
242
-	 * checks whether the given string is probably a DN
243
-	 * @param string $string
244
-	 * @return boolean
245
-	 */
246
-	public function stringResemblesDN($string) {
247
-		$r = $this->ldap->explodeDN($string, 0);
248
-		// if exploding a DN succeeds and does not end up in
249
-		// an empty array except for $r[count] being 0.
250
-		return (is_array($r) && count($r) > 1);
251
-	}
252
-
253
-	/**
254
-	 * returns a DN-string that is cleaned from not domain parts, e.g.
255
-	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
256
-	 * becomes dc=foobar,dc=server,dc=org
257
-	 * @param string $dn
258
-	 * @return string
259
-	 */
260
-	public function getDomainDNFromDN($dn) {
261
-		$allParts = $this->ldap->explodeDN($dn, 0);
262
-		if($allParts === false) {
263
-			//not a valid DN
264
-			return '';
265
-		}
266
-		$domainParts = array();
267
-		$dcFound = false;
268
-		foreach($allParts as $part) {
269
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
270
-				$dcFound = true;
271
-			}
272
-			if($dcFound) {
273
-				$domainParts[] = $part;
274
-			}
275
-		}
276
-		$domainDN = implode(',', $domainParts);
277
-		return $domainDN;
278
-	}
279
-
280
-	/**
281
-	 * returns the LDAP DN for the given internal ownCloud name of the group
282
-	 * @param string $name the ownCloud name in question
283
-	 * @return string|false LDAP DN on success, otherwise false
284
-	 */
285
-	public function groupname2dn($name) {
286
-		return $this->groupMapper->getDNByName($name);
287
-	}
288
-
289
-	/**
290
-	 * returns the LDAP DN for the given internal ownCloud name of the user
291
-	 * @param string $name the ownCloud name in question
292
-	 * @return string|false with the LDAP DN on success, otherwise false
293
-	 */
294
-	public function username2dn($name) {
295
-		$fdn = $this->userMapper->getDNByName($name);
296
-
297
-		//Check whether the DN belongs to the Base, to avoid issues on multi-
298
-		//server setups
299
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
300
-			return $fdn;
301
-		}
302
-
303
-		return false;
304
-	}
305
-
306
-	/**
83
+    /**
84
+     * @var \OCA\User_LDAP\Helper
85
+     */
86
+    private $helper;
87
+
88
+    public function __construct(Connection $connection, ILDAPWrapper $ldap,
89
+        Manager $userManager, Helper $helper) {
90
+        parent::__construct($ldap);
91
+        $this->connection = $connection;
92
+        $this->userManager = $userManager;
93
+        $this->userManager->setLdapAccess($this);
94
+        $this->helper = $helper;
95
+    }
96
+
97
+    /**
98
+     * sets the User Mapper
99
+     * @param AbstractMapping $mapper
100
+     */
101
+    public function setUserMapper(AbstractMapping $mapper) {
102
+        $this->userMapper = $mapper;
103
+    }
104
+
105
+    /**
106
+     * returns the User Mapper
107
+     * @throws \Exception
108
+     * @return AbstractMapping
109
+     */
110
+    public function getUserMapper() {
111
+        if(is_null($this->userMapper)) {
112
+            throw new \Exception('UserMapper was not assigned to this Access instance.');
113
+        }
114
+        return $this->userMapper;
115
+    }
116
+
117
+    /**
118
+     * sets the Group Mapper
119
+     * @param AbstractMapping $mapper
120
+     */
121
+    public function setGroupMapper(AbstractMapping $mapper) {
122
+        $this->groupMapper = $mapper;
123
+    }
124
+
125
+    /**
126
+     * returns the Group Mapper
127
+     * @throws \Exception
128
+     * @return AbstractMapping
129
+     */
130
+    public function getGroupMapper() {
131
+        if(is_null($this->groupMapper)) {
132
+            throw new \Exception('GroupMapper was not assigned to this Access instance.');
133
+        }
134
+        return $this->groupMapper;
135
+    }
136
+
137
+    /**
138
+     * @return bool
139
+     */
140
+    private function checkConnection() {
141
+        return ($this->connection instanceof Connection);
142
+    }
143
+
144
+    /**
145
+     * returns the Connection instance
146
+     * @return \OCA\User_LDAP\Connection
147
+     */
148
+    public function getConnection() {
149
+        return $this->connection;
150
+    }
151
+
152
+    /**
153
+     * reads a given attribute for an LDAP record identified by a DN
154
+     * @param string $dn the record in question
155
+     * @param string $attr the attribute that shall be retrieved
156
+     *        if empty, just check the record's existence
157
+     * @param string $filter
158
+     * @return array|false an array of values on success or an empty
159
+     *          array if $attr is empty, false otherwise
160
+     */
161
+    public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
162
+        if(!$this->checkConnection()) {
163
+            \OCP\Util::writeLog('user_ldap',
164
+                'No LDAP Connector assigned, access impossible for readAttribute.',
165
+                \OCP\Util::WARN);
166
+            return false;
167
+        }
168
+        $cr = $this->connection->getConnectionResource();
169
+        if(!$this->ldap->isResource($cr)) {
170
+            //LDAP not available
171
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
172
+            return false;
173
+        }
174
+        //Cancel possibly running Paged Results operation, otherwise we run in
175
+        //LDAP protocol errors
176
+        $this->abandonPagedSearch();
177
+        // openLDAP requires that we init a new Paged Search. Not needed by AD,
178
+        // but does not hurt either.
179
+        $pagingSize = intval($this->connection->ldapPagingSize);
180
+        // 0 won't result in replies, small numbers may leave out groups
181
+        // (cf. #12306), 500 is default for paging and should work everywhere.
182
+        $maxResults = $pagingSize > 20 ? $pagingSize : 500;
183
+        $this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0);
184
+        $dn = $this->helper->DNasBaseParameter($dn);
185
+        $rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
186
+        if(!$this->ldap->isResource($rr)) {
187
+            if(!empty($attr)) {
188
+                //do not throw this message on userExists check, irritates
189
+                \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
190
+            }
191
+            //in case an error occurs , e.g. object does not exist
192
+            return false;
193
+        }
194
+        if (empty($attr) && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
195
+            \OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
196
+            return array();
197
+        }
198
+        $er = $this->ldap->firstEntry($cr, $rr);
199
+        if(!$this->ldap->isResource($er)) {
200
+            //did not match the filter, return false
201
+            return false;
202
+        }
203
+        //LDAP attributes are not case sensitive
204
+        $result = \OCP\Util::mb_array_change_key_case(
205
+                $this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
206
+        $attr = mb_strtolower($attr, 'UTF-8');
207
+
208
+        if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
209
+            $values = array();
210
+            for($i=0;$i<$result[$attr]['count'];$i++) {
211
+                if($this->resemblesDN($attr)) {
212
+                    $values[] = $this->helper->sanitizeDN($result[$attr][$i]);
213
+                } elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
214
+                    $values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
215
+                } else {
216
+                    $values[] = $result[$attr][$i];
217
+                }
218
+            }
219
+            return $values;
220
+        }
221
+        \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
222
+        return false;
223
+    }
224
+
225
+    /**
226
+     * checks whether the given attributes value is probably a DN
227
+     * @param string $attr the attribute in question
228
+     * @return boolean if so true, otherwise false
229
+     */
230
+    private function resemblesDN($attr) {
231
+        $resemblingAttributes = array(
232
+            'dn',
233
+            'uniquemember',
234
+            'member',
235
+            // memberOf is an "operational" attribute, without a definition in any RFC
236
+            'memberof'
237
+        );
238
+        return in_array($attr, $resemblingAttributes);
239
+    }
240
+
241
+    /**
242
+     * checks whether the given string is probably a DN
243
+     * @param string $string
244
+     * @return boolean
245
+     */
246
+    public function stringResemblesDN($string) {
247
+        $r = $this->ldap->explodeDN($string, 0);
248
+        // if exploding a DN succeeds and does not end up in
249
+        // an empty array except for $r[count] being 0.
250
+        return (is_array($r) && count($r) > 1);
251
+    }
252
+
253
+    /**
254
+     * returns a DN-string that is cleaned from not domain parts, e.g.
255
+     * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
256
+     * becomes dc=foobar,dc=server,dc=org
257
+     * @param string $dn
258
+     * @return string
259
+     */
260
+    public function getDomainDNFromDN($dn) {
261
+        $allParts = $this->ldap->explodeDN($dn, 0);
262
+        if($allParts === false) {
263
+            //not a valid DN
264
+            return '';
265
+        }
266
+        $domainParts = array();
267
+        $dcFound = false;
268
+        foreach($allParts as $part) {
269
+            if(!$dcFound && strpos($part, 'dc=') === 0) {
270
+                $dcFound = true;
271
+            }
272
+            if($dcFound) {
273
+                $domainParts[] = $part;
274
+            }
275
+        }
276
+        $domainDN = implode(',', $domainParts);
277
+        return $domainDN;
278
+    }
279
+
280
+    /**
281
+     * returns the LDAP DN for the given internal ownCloud name of the group
282
+     * @param string $name the ownCloud name in question
283
+     * @return string|false LDAP DN on success, otherwise false
284
+     */
285
+    public function groupname2dn($name) {
286
+        return $this->groupMapper->getDNByName($name);
287
+    }
288
+
289
+    /**
290
+     * returns the LDAP DN for the given internal ownCloud name of the user
291
+     * @param string $name the ownCloud name in question
292
+     * @return string|false with the LDAP DN on success, otherwise false
293
+     */
294
+    public function username2dn($name) {
295
+        $fdn = $this->userMapper->getDNByName($name);
296
+
297
+        //Check whether the DN belongs to the Base, to avoid issues on multi-
298
+        //server setups
299
+        if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
300
+            return $fdn;
301
+        }
302
+
303
+        return false;
304
+    }
305
+
306
+    /**
307 307
 	public function ocname2dn($name, $isUser) {
308
-	 * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
309
-	 * @param string $fdn the dn of the group object
310
-	 * @param string $ldapName optional, the display name of the object
311
-	 * @return string|false with the name to use in ownCloud, false on DN outside of search DN
312
-	 */
313
-	public function dn2groupname($fdn, $ldapName = null) {
314
-		//To avoid bypassing the base DN settings under certain circumstances
315
-		//with the group support, check whether the provided DN matches one of
316
-		//the given Bases
317
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
318
-			return false;
319
-		}
320
-
321
-		return $this->dn2ocname($fdn, $ldapName, false);
322
-	}
323
-
324
-	/**
325
-	 * accepts an array of group DNs and tests whether they match the user
326
-	 * filter by doing read operations against the group entries. Returns an
327
-	 * array of DNs that match the filter.
328
-	 *
329
-	 * @param string[] $groupDNs
330
-	 * @return string[]
331
-	 */
332
-	public function groupsMatchFilter($groupDNs) {
333
-		$validGroupDNs = [];
334
-		foreach($groupDNs as $dn) {
335
-			$cacheKey = 'groupsMatchFilter-'.$dn;
336
-			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
337
-			if(!is_null($groupMatchFilter)) {
338
-				if($groupMatchFilter) {
339
-					$validGroupDNs[] = $dn;
340
-				}
341
-				continue;
342
-			}
343
-
344
-			// Check the base DN first. If this is not met already, we don't
345
-			// need to ask the server at all.
346
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
347
-				$this->connection->writeToCache($cacheKey, false);
348
-				continue;
349
-			}
350
-
351
-			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
352
-			if(is_array($result)) {
353
-				$this->connection->writeToCache($cacheKey, true);
354
-				$validGroupDNs[] = $dn;
355
-			} else {
356
-				$this->connection->writeToCache($cacheKey, false);
357
-			}
358
-
359
-		}
360
-		return $validGroupDNs;
361
-	}
362
-
363
-	/**
364
-	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
365
-	 * @param string $dn the dn of the user object
366
-	 * @param string $ldapName optional, the display name of the object
367
-	 * @return string|false with with the name to use in ownCloud
368
-	 */
369
-	public function dn2username($fdn, $ldapName = null) {
370
-		//To avoid bypassing the base DN settings under certain circumstances
371
-		//with the group support, check whether the provided DN matches one of
372
-		//the given Bases
373
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
374
-			return false;
375
-		}
376
-
377
-		return $this->dn2ocname($fdn, $ldapName, true);
378
-	}
379
-
380
-	/**
381
-	 * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
382
-	 * @param string $dn the dn of the user object
383
-	 * @param string $ldapName optional, the display name of the object
384
-	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
385
-	 * @return string|false with with the name to use in ownCloud
386
-	 */
387
-	public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
388
-		if($isUser) {
389
-			$mapper = $this->getUserMapper();
390
-			$nameAttribute = $this->connection->ldapUserDisplayName;
391
-		} else {
392
-			$mapper = $this->getGroupMapper();
393
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
394
-		}
395
-
396
-		//let's try to retrieve the ownCloud name from the mappings table
397
-		$ocName = $mapper->getNameByDN($fdn);
398
-		if(is_string($ocName)) {
399
-			return $ocName;
400
-		}
401
-
402
-		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
403
-		$uuid = $this->getUUID($fdn, $isUser);
404
-		if(is_string($uuid)) {
405
-			$ocName = $mapper->getNameByUUID($uuid);
406
-			if(is_string($ocName)) {
407
-				$mapper->setDNbyUUID($fdn, $uuid);
408
-				return $ocName;
409
-			}
410
-		} else {
411
-			//If the UUID can't be detected something is foul.
412
-			\OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
413
-			return false;
414
-		}
415
-
416
-		if(is_null($ldapName)) {
417
-			$ldapName = $this->readAttribute($fdn, $nameAttribute);
418
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
419
-				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
420
-				return false;
421
-			}
422
-			$ldapName = $ldapName[0];
423
-		}
424
-
425
-		if($isUser) {
426
-			$usernameAttribute = $this->connection->ldapExpertUsernameAttr;
427
-			if(!empty($usernameAttribute)) {
428
-				$username = $this->readAttribute($fdn, $usernameAttribute);
429
-				$username = $username[0];
430
-			} else {
431
-				$username = $uuid;
432
-			}
433
-			$intName = $this->sanitizeUsername($username);
434
-		} else {
435
-			$intName = $ldapName;
436
-		}
437
-
438
-		//a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
439
-		//disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
440
-		//NOTE: mind, disabling cache affects only this instance! Using it
441
-		// outside of core user management will still cache the user as non-existing.
442
-		$originalTTL = $this->connection->ldapCacheTTL;
443
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
444
-		if(($isUser && !\OCP\User::userExists($intName))
445
-			|| (!$isUser && !\OC_Group::groupExists($intName))) {
446
-			if($mapper->map($fdn, $intName, $uuid)) {
447
-				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
448
-				return $intName;
449
-			}
450
-		}
451
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
452
-
453
-		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
454
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
455
-			return $altName;
456
-		}
457
-
458
-		//if everything else did not help..
459
-		\OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
460
-		return false;
461
-	}
462
-
463
-	/**
464
-	 * gives back the user names as they are used ownClod internally
465
-	 * @param array $ldapUsers as returned by fetchList()
466
-	 * @return array an array with the user names to use in ownCloud
467
-	 *
468
-	 * gives back the user names as they are used ownClod internally
469
-	 */
470
-	public function ownCloudUserNames($ldapUsers) {
471
-		return $this->ldap2ownCloudNames($ldapUsers, true);
472
-	}
473
-
474
-	/**
475
-	 * gives back the group names as they are used ownClod internally
476
-	 * @param array $ldapGroups as returned by fetchList()
477
-	 * @return array an array with the group names to use in ownCloud
478
-	 *
479
-	 * gives back the group names as they are used ownClod internally
480
-	 */
481
-	public function ownCloudGroupNames($ldapGroups) {
482
-		return $this->ldap2ownCloudNames($ldapGroups, false);
483
-	}
484
-
485
-	/**
486
-	 * @param array $ldapObjects as returned by fetchList()
487
-	 * @param bool $isUsers
488
-	 * @return array
489
-	 */
490
-	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
491
-		if($isUsers) {
492
-			$nameAttribute = $this->connection->ldapUserDisplayName;
493
-			$sndAttribute  = $this->connection->ldapUserDisplayName2;
494
-		} else {
495
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
496
-		}
497
-		$ownCloudNames = array();
498
-
499
-		foreach($ldapObjects as $ldapObject) {
500
-			$nameByLDAP = null;
501
-			if(    isset($ldapObject[$nameAttribute])
502
-				&& is_array($ldapObject[$nameAttribute])
503
-				&& isset($ldapObject[$nameAttribute][0])
504
-			) {
505
-				// might be set, but not necessarily. if so, we use it.
506
-				$nameByLDAP = $ldapObject[$nameAttribute][0];
507
-			}
508
-
509
-			$ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
510
-			if($ocName) {
511
-				$ownCloudNames[] = $ocName;
512
-				if($isUsers) {
513
-					//cache the user names so it does not need to be retrieved
514
-					//again later (e.g. sharing dialogue).
515
-					if(is_null($nameByLDAP)) {
516
-						continue;
517
-					}
518
-					$sndName = isset($ldapObject[$sndAttribute][0])
519
-						? $ldapObject[$sndAttribute][0] : '';
520
-					$this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
521
-				}
522
-			}
523
-		}
524
-		return $ownCloudNames;
525
-	}
526
-
527
-	/**
528
-	 * caches the user display name
529
-	 * @param string $ocName the internal ownCloud username
530
-	 * @param string|false $home the home directory path
531
-	 */
532
-	public function cacheUserHome($ocName, $home) {
533
-		$cacheKey = 'getHome'.$ocName;
534
-		$this->connection->writeToCache($cacheKey, $home);
535
-	}
536
-
537
-	/**
538
-	 * caches a user as existing
539
-	 * @param string $ocName the internal ownCloud username
540
-	 */
541
-	public function cacheUserExists($ocName) {
542
-		$this->connection->writeToCache('userExists'.$ocName, true);
543
-	}
544
-
545
-	/**
546
-	 * caches the user display name
547
-	 * @param string $ocName the internal ownCloud username
548
-	 * @param string $displayName the display name
549
-	 * @param string $displayName2 the second display name
550
-	 */
551
-	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
552
-		$user = $this->userManager->get($ocName);
553
-		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
554
-		$cacheKeyTrunk = 'getDisplayName';
555
-		$this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
556
-	}
557
-
558
-	/**
559
-	 * creates a unique name for internal ownCloud use for users. Don't call it directly.
560
-	 * @param string $name the display name of the object
561
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful
562
-	 *
563
-	 * Instead of using this method directly, call
564
-	 * createAltInternalOwnCloudName($name, true)
565
-	 */
566
-	private function _createAltInternalOwnCloudNameForUsers($name) {
567
-		$attempts = 0;
568
-		//while loop is just a precaution. If a name is not generated within
569
-		//20 attempts, something else is very wrong. Avoids infinite loop.
570
-		while($attempts < 20){
571
-			$altName = $name . '_' . rand(1000,9999);
572
-			if(!\OCP\User::userExists($altName)) {
573
-				return $altName;
574
-			}
575
-			$attempts++;
576
-		}
577
-		return false;
578
-	}
579
-
580
-	/**
581
-	 * creates a unique name for internal ownCloud use for groups. Don't call it directly.
582
-	 * @param string $name the display name of the object
583
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful.
584
-	 *
585
-	 * Instead of using this method directly, call
586
-	 * createAltInternalOwnCloudName($name, false)
587
-	 *
588
-	 * Group names are also used as display names, so we do a sequential
589
-	 * numbering, e.g. Developers_42 when there are 41 other groups called
590
-	 * "Developers"
591
-	 */
592
-	private function _createAltInternalOwnCloudNameForGroups($name) {
593
-		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
594
-		if(!($usedNames) || count($usedNames) === 0) {
595
-			$lastNo = 1; //will become name_2
596
-		} else {
597
-			natsort($usedNames);
598
-			$lastName = array_pop($usedNames);
599
-			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
600
-		}
601
-		$altName = $name.'_'.strval($lastNo+1);
602
-		unset($usedNames);
603
-
604
-		$attempts = 1;
605
-		while($attempts < 21){
606
-			// Check to be really sure it is unique
607
-			// while loop is just a precaution. If a name is not generated within
608
-			// 20 attempts, something else is very wrong. Avoids infinite loop.
609
-			if(!\OC_Group::groupExists($altName)) {
610
-				return $altName;
611
-			}
612
-			$altName = $name . '_' . ($lastNo + $attempts);
613
-			$attempts++;
614
-		}
615
-		return false;
616
-	}
617
-
618
-	/**
619
-	 * creates a unique name for internal ownCloud use.
620
-	 * @param string $name the display name of the object
621
-	 * @param boolean $isUser whether name should be created for a user (true) or a group (false)
622
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful
623
-	 */
624
-	private function createAltInternalOwnCloudName($name, $isUser) {
625
-		$originalTTL = $this->connection->ldapCacheTTL;
626
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
627
-		if($isUser) {
628
-			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
629
-		} else {
630
-			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
631
-		}
632
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
633
-
634
-		return $altName;
635
-	}
636
-
637
-	/**
638
-	 * fetches a list of users according to a provided loginName and utilizing
639
-	 * the login filter.
640
-	 *
641
-	 * @param string $loginName
642
-	 * @param array $attributes optional, list of attributes to read
643
-	 * @return array
644
-	 */
645
-	public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
646
-		$loginName = $this->escapeFilterPart($loginName);
647
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
648
-		$users = $this->fetchListOfUsers($filter, $attributes);
649
-		return $users;
650
-	}
651
-
652
-	/**
653
-	 * counts the number of users according to a provided loginName and
654
-	 * utilizing the login filter.
655
-	 *
656
-	 * @param string $loginName
657
-	 * @return array
658
-	 */
659
-	public function countUsersByLoginName($loginName) {
660
-		$loginName = $this->escapeFilterPart($loginName);
661
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
662
-		$users = $this->countUsers($filter);
663
-		return $users;
664
-	}
665
-
666
-	/**
667
-	 * @param string $filter
668
-	 * @param string|string[] $attr
669
-	 * @param int $limit
670
-	 * @param int $offset
671
-	 * @return array
672
-	 */
673
-	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
674
-		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
675
-		$this->batchApplyUserAttributes($ldapRecords);
676
-		return $this->fetchList($ldapRecords, (count($attr) > 1));
677
-	}
678
-
679
-	/**
680
-	 * provided with an array of LDAP user records the method will fetch the
681
-	 * user object and requests it to process the freshly fetched attributes and
682
-	 * and their values
683
-	 * @param array $ldapRecords
684
-	 */
685
-	public function batchApplyUserAttributes(array $ldapRecords){
686
-		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
687
-		foreach($ldapRecords as $userRecord) {
688
-			if(!isset($userRecord[$displayNameAttribute])) {
689
-				// displayName is obligatory
690
-				continue;
691
-			}
692
-			$ocName  = $this->dn2ocname($userRecord['dn'][0]);
693
-			if($ocName === false) {
694
-				continue;
695
-			}
696
-			$this->cacheUserExists($ocName);
697
-			$user = $this->userManager->get($ocName);
698
-			if($user instanceof OfflineUser) {
699
-				$user->unmark();
700
-				$user = $this->userManager->get($ocName);
701
-			}
702
-			if ($user !== null) {
703
-				$user->processAttributes($userRecord);
704
-			} else {
705
-				\OC::$server->getLogger()->debug(
706
-					"The ldap user manager returned null for $ocName",
707
-					['app'=>'user_ldap']
708
-				);
709
-			}
710
-		}
711
-	}
712
-
713
-	/**
714
-	 * @param string $filter
715
-	 * @param string|string[] $attr
716
-	 * @param int $limit
717
-	 * @param int $offset
718
-	 * @return array
719
-	 */
720
-	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
721
-		return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
722
-	}
723
-
724
-	/**
725
-	 * @param array $list
726
-	 * @param bool $manyAttributes
727
-	 * @return array
728
-	 */
729
-	private function fetchList($list, $manyAttributes) {
730
-		if(is_array($list)) {
731
-			if($manyAttributes) {
732
-				return $list;
733
-			} else {
734
-				$list = array_reduce($list, function($carry, $item) {
735
-					$attribute = array_keys($item)[0];
736
-					$carry[] = $item[$attribute][0];
737
-					return $carry;
738
-				}, array());
739
-				return array_unique($list, SORT_LOCALE_STRING);
740
-			}
741
-		}
742
-
743
-		//error cause actually, maybe throw an exception in future.
744
-		return array();
745
-	}
746
-
747
-	/**
748
-	 * executes an LDAP search, optimized for Users
749
-	 * @param string $filter the LDAP filter for the search
750
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
751
-	 * @param integer $limit
752
-	 * @param integer $offset
753
-	 * @return array with the search result
754
-	 *
755
-	 * Executes an LDAP search
756
-	 */
757
-	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
758
-		return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
759
-	}
760
-
761
-	/**
762
-	 * @param string $filter
763
-	 * @param string|string[] $attr
764
-	 * @param int $limit
765
-	 * @param int $offset
766
-	 * @return false|int
767
-	 */
768
-	public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
769
-		return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
770
-	}
771
-
772
-	/**
773
-	 * executes an LDAP search, optimized for Groups
774
-	 * @param string $filter the LDAP filter for the search
775
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
776
-	 * @param integer $limit
777
-	 * @param integer $offset
778
-	 * @return array with the search result
779
-	 *
780
-	 * Executes an LDAP search
781
-	 */
782
-	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
783
-		return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
784
-	}
785
-
786
-	/**
787
-	 * returns the number of available groups
788
-	 * @param string $filter the LDAP search filter
789
-	 * @param string[] $attr optional
790
-	 * @param int|null $limit
791
-	 * @param int|null $offset
792
-	 * @return int|bool
793
-	 */
794
-	public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
795
-		return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
796
-	}
797
-
798
-	/**
799
-	 * returns the number of available objects on the base DN
800
-	 *
801
-	 * @param int|null $limit
802
-	 * @param int|null $offset
803
-	 * @return int|bool
804
-	 */
805
-	public function countObjects($limit = null, $offset = null) {
806
-		return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
807
-	}
808
-
809
-	/**
810
-	 * retrieved. Results will according to the order in the array.
811
-	 * @param int $limit optional, maximum results to be counted
812
-	 * @param int $offset optional, a starting point
813
-	 * @return array|false array with the search result as first value and pagedSearchOK as
814
-	 * second | false if not successful
815
-	 */
816
-	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
817
-		if(!is_null($attr) && !is_array($attr)) {
818
-			$attr = array(mb_strtolower($attr, 'UTF-8'));
819
-		}
820
-
821
-		// See if we have a resource, in case not cancel with message
822
-		$cr = $this->connection->getConnectionResource();
823
-		if(!$this->ldap->isResource($cr)) {
824
-			// Seems like we didn't find any resource.
825
-			// Return an empty array just like before.
826
-			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
827
-			return false;
828
-		}
829
-
830
-		//check whether paged search should be attempted
831
-		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
832
-
833
-		$linkResources = array_pad(array(), count($base), $cr);
834
-		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
835
-		$error = $this->ldap->errno($cr);
836
-		if(!is_array($sr) || $error !== 0) {
837
-			\OCP\Util::writeLog('user_ldap',
838
-				'Error when searching: '.$this->ldap->error($cr).
839
-					' code '.$this->ldap->errno($cr),
840
-				\OCP\Util::ERROR);
841
-			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
842
-			return false;
843
-		}
844
-
845
-		return array($sr, $pagedSearchOK);
846
-	}
847
-
848
-	/**
849
-	 * processes an LDAP paged search operation
850
-	 * @param array $sr the array containing the LDAP search resources
851
-	 * @param string $filter the LDAP filter for the search
852
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
853
-	 * @param int $iFoundItems number of results in the search operation
854
-	 * @param int $limit maximum results to be counted
855
-	 * @param int $offset a starting point
856
-	 * @param bool $pagedSearchOK whether a paged search has been executed
857
-	 * @param bool $skipHandling required for paged search when cookies to
858
-	 * prior results need to be gained
859
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
860
-	 */
861
-	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
862
-		$cookie = null;
863
-		if($pagedSearchOK) {
864
-			$cr = $this->connection->getConnectionResource();
865
-			foreach($sr as $key => $res) {
866
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
867
-					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
868
-				}
869
-			}
870
-
871
-			//browsing through prior pages to get the cookie for the new one
872
-			if($skipHandling) {
873
-				return;
874
-			}
875
-			// if count is bigger, then the server does not support
876
-			// paged search. Instead, he did a normal search. We set a
877
-			// flag here, so the callee knows how to deal with it.
878
-			if($iFoundItems <= $limit) {
879
-				$this->pagedSearchedSuccessful = true;
880
-			}
881
-		} else {
882
-			if(!is_null($limit)) {
883
-				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
884
-			}
885
-		}
886
-		/* ++ Fixing RHDS searches with pages with zero results ++
308
+     * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
309
+     * @param string $fdn the dn of the group object
310
+     * @param string $ldapName optional, the display name of the object
311
+     * @return string|false with the name to use in ownCloud, false on DN outside of search DN
312
+     */
313
+    public function dn2groupname($fdn, $ldapName = null) {
314
+        //To avoid bypassing the base DN settings under certain circumstances
315
+        //with the group support, check whether the provided DN matches one of
316
+        //the given Bases
317
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
318
+            return false;
319
+        }
320
+
321
+        return $this->dn2ocname($fdn, $ldapName, false);
322
+    }
323
+
324
+    /**
325
+     * accepts an array of group DNs and tests whether they match the user
326
+     * filter by doing read operations against the group entries. Returns an
327
+     * array of DNs that match the filter.
328
+     *
329
+     * @param string[] $groupDNs
330
+     * @return string[]
331
+     */
332
+    public function groupsMatchFilter($groupDNs) {
333
+        $validGroupDNs = [];
334
+        foreach($groupDNs as $dn) {
335
+            $cacheKey = 'groupsMatchFilter-'.$dn;
336
+            $groupMatchFilter = $this->connection->getFromCache($cacheKey);
337
+            if(!is_null($groupMatchFilter)) {
338
+                if($groupMatchFilter) {
339
+                    $validGroupDNs[] = $dn;
340
+                }
341
+                continue;
342
+            }
343
+
344
+            // Check the base DN first. If this is not met already, we don't
345
+            // need to ask the server at all.
346
+            if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
347
+                $this->connection->writeToCache($cacheKey, false);
348
+                continue;
349
+            }
350
+
351
+            $result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
352
+            if(is_array($result)) {
353
+                $this->connection->writeToCache($cacheKey, true);
354
+                $validGroupDNs[] = $dn;
355
+            } else {
356
+                $this->connection->writeToCache($cacheKey, false);
357
+            }
358
+
359
+        }
360
+        return $validGroupDNs;
361
+    }
362
+
363
+    /**
364
+     * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
365
+     * @param string $dn the dn of the user object
366
+     * @param string $ldapName optional, the display name of the object
367
+     * @return string|false with with the name to use in ownCloud
368
+     */
369
+    public function dn2username($fdn, $ldapName = null) {
370
+        //To avoid bypassing the base DN settings under certain circumstances
371
+        //with the group support, check whether the provided DN matches one of
372
+        //the given Bases
373
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
374
+            return false;
375
+        }
376
+
377
+        return $this->dn2ocname($fdn, $ldapName, true);
378
+    }
379
+
380
+    /**
381
+     * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
382
+     * @param string $dn the dn of the user object
383
+     * @param string $ldapName optional, the display name of the object
384
+     * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
385
+     * @return string|false with with the name to use in ownCloud
386
+     */
387
+    public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
388
+        if($isUser) {
389
+            $mapper = $this->getUserMapper();
390
+            $nameAttribute = $this->connection->ldapUserDisplayName;
391
+        } else {
392
+            $mapper = $this->getGroupMapper();
393
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
394
+        }
395
+
396
+        //let's try to retrieve the ownCloud name from the mappings table
397
+        $ocName = $mapper->getNameByDN($fdn);
398
+        if(is_string($ocName)) {
399
+            return $ocName;
400
+        }
401
+
402
+        //second try: get the UUID and check if it is known. Then, update the DN and return the name.
403
+        $uuid = $this->getUUID($fdn, $isUser);
404
+        if(is_string($uuid)) {
405
+            $ocName = $mapper->getNameByUUID($uuid);
406
+            if(is_string($ocName)) {
407
+                $mapper->setDNbyUUID($fdn, $uuid);
408
+                return $ocName;
409
+            }
410
+        } else {
411
+            //If the UUID can't be detected something is foul.
412
+            \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
413
+            return false;
414
+        }
415
+
416
+        if(is_null($ldapName)) {
417
+            $ldapName = $this->readAttribute($fdn, $nameAttribute);
418
+            if(!isset($ldapName[0]) && empty($ldapName[0])) {
419
+                \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
420
+                return false;
421
+            }
422
+            $ldapName = $ldapName[0];
423
+        }
424
+
425
+        if($isUser) {
426
+            $usernameAttribute = $this->connection->ldapExpertUsernameAttr;
427
+            if(!empty($usernameAttribute)) {
428
+                $username = $this->readAttribute($fdn, $usernameAttribute);
429
+                $username = $username[0];
430
+            } else {
431
+                $username = $uuid;
432
+            }
433
+            $intName = $this->sanitizeUsername($username);
434
+        } else {
435
+            $intName = $ldapName;
436
+        }
437
+
438
+        //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
439
+        //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
440
+        //NOTE: mind, disabling cache affects only this instance! Using it
441
+        // outside of core user management will still cache the user as non-existing.
442
+        $originalTTL = $this->connection->ldapCacheTTL;
443
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
444
+        if(($isUser && !\OCP\User::userExists($intName))
445
+            || (!$isUser && !\OC_Group::groupExists($intName))) {
446
+            if($mapper->map($fdn, $intName, $uuid)) {
447
+                $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
448
+                return $intName;
449
+            }
450
+        }
451
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
452
+
453
+        $altName = $this->createAltInternalOwnCloudName($intName, $isUser);
454
+        if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
455
+            return $altName;
456
+        }
457
+
458
+        //if everything else did not help..
459
+        \OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
460
+        return false;
461
+    }
462
+
463
+    /**
464
+     * gives back the user names as they are used ownClod internally
465
+     * @param array $ldapUsers as returned by fetchList()
466
+     * @return array an array with the user names to use in ownCloud
467
+     *
468
+     * gives back the user names as they are used ownClod internally
469
+     */
470
+    public function ownCloudUserNames($ldapUsers) {
471
+        return $this->ldap2ownCloudNames($ldapUsers, true);
472
+    }
473
+
474
+    /**
475
+     * gives back the group names as they are used ownClod internally
476
+     * @param array $ldapGroups as returned by fetchList()
477
+     * @return array an array with the group names to use in ownCloud
478
+     *
479
+     * gives back the group names as they are used ownClod internally
480
+     */
481
+    public function ownCloudGroupNames($ldapGroups) {
482
+        return $this->ldap2ownCloudNames($ldapGroups, false);
483
+    }
484
+
485
+    /**
486
+     * @param array $ldapObjects as returned by fetchList()
487
+     * @param bool $isUsers
488
+     * @return array
489
+     */
490
+    private function ldap2ownCloudNames($ldapObjects, $isUsers) {
491
+        if($isUsers) {
492
+            $nameAttribute = $this->connection->ldapUserDisplayName;
493
+            $sndAttribute  = $this->connection->ldapUserDisplayName2;
494
+        } else {
495
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
496
+        }
497
+        $ownCloudNames = array();
498
+
499
+        foreach($ldapObjects as $ldapObject) {
500
+            $nameByLDAP = null;
501
+            if(    isset($ldapObject[$nameAttribute])
502
+                && is_array($ldapObject[$nameAttribute])
503
+                && isset($ldapObject[$nameAttribute][0])
504
+            ) {
505
+                // might be set, but not necessarily. if so, we use it.
506
+                $nameByLDAP = $ldapObject[$nameAttribute][0];
507
+            }
508
+
509
+            $ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
510
+            if($ocName) {
511
+                $ownCloudNames[] = $ocName;
512
+                if($isUsers) {
513
+                    //cache the user names so it does not need to be retrieved
514
+                    //again later (e.g. sharing dialogue).
515
+                    if(is_null($nameByLDAP)) {
516
+                        continue;
517
+                    }
518
+                    $sndName = isset($ldapObject[$sndAttribute][0])
519
+                        ? $ldapObject[$sndAttribute][0] : '';
520
+                    $this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
521
+                }
522
+            }
523
+        }
524
+        return $ownCloudNames;
525
+    }
526
+
527
+    /**
528
+     * caches the user display name
529
+     * @param string $ocName the internal ownCloud username
530
+     * @param string|false $home the home directory path
531
+     */
532
+    public function cacheUserHome($ocName, $home) {
533
+        $cacheKey = 'getHome'.$ocName;
534
+        $this->connection->writeToCache($cacheKey, $home);
535
+    }
536
+
537
+    /**
538
+     * caches a user as existing
539
+     * @param string $ocName the internal ownCloud username
540
+     */
541
+    public function cacheUserExists($ocName) {
542
+        $this->connection->writeToCache('userExists'.$ocName, true);
543
+    }
544
+
545
+    /**
546
+     * caches the user display name
547
+     * @param string $ocName the internal ownCloud username
548
+     * @param string $displayName the display name
549
+     * @param string $displayName2 the second display name
550
+     */
551
+    public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
552
+        $user = $this->userManager->get($ocName);
553
+        $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
554
+        $cacheKeyTrunk = 'getDisplayName';
555
+        $this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
556
+    }
557
+
558
+    /**
559
+     * creates a unique name for internal ownCloud use for users. Don't call it directly.
560
+     * @param string $name the display name of the object
561
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful
562
+     *
563
+     * Instead of using this method directly, call
564
+     * createAltInternalOwnCloudName($name, true)
565
+     */
566
+    private function _createAltInternalOwnCloudNameForUsers($name) {
567
+        $attempts = 0;
568
+        //while loop is just a precaution. If a name is not generated within
569
+        //20 attempts, something else is very wrong. Avoids infinite loop.
570
+        while($attempts < 20){
571
+            $altName = $name . '_' . rand(1000,9999);
572
+            if(!\OCP\User::userExists($altName)) {
573
+                return $altName;
574
+            }
575
+            $attempts++;
576
+        }
577
+        return false;
578
+    }
579
+
580
+    /**
581
+     * creates a unique name for internal ownCloud use for groups. Don't call it directly.
582
+     * @param string $name the display name of the object
583
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful.
584
+     *
585
+     * Instead of using this method directly, call
586
+     * createAltInternalOwnCloudName($name, false)
587
+     *
588
+     * Group names are also used as display names, so we do a sequential
589
+     * numbering, e.g. Developers_42 when there are 41 other groups called
590
+     * "Developers"
591
+     */
592
+    private function _createAltInternalOwnCloudNameForGroups($name) {
593
+        $usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
594
+        if(!($usedNames) || count($usedNames) === 0) {
595
+            $lastNo = 1; //will become name_2
596
+        } else {
597
+            natsort($usedNames);
598
+            $lastName = array_pop($usedNames);
599
+            $lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
600
+        }
601
+        $altName = $name.'_'.strval($lastNo+1);
602
+        unset($usedNames);
603
+
604
+        $attempts = 1;
605
+        while($attempts < 21){
606
+            // Check to be really sure it is unique
607
+            // while loop is just a precaution. If a name is not generated within
608
+            // 20 attempts, something else is very wrong. Avoids infinite loop.
609
+            if(!\OC_Group::groupExists($altName)) {
610
+                return $altName;
611
+            }
612
+            $altName = $name . '_' . ($lastNo + $attempts);
613
+            $attempts++;
614
+        }
615
+        return false;
616
+    }
617
+
618
+    /**
619
+     * creates a unique name for internal ownCloud use.
620
+     * @param string $name the display name of the object
621
+     * @param boolean $isUser whether name should be created for a user (true) or a group (false)
622
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful
623
+     */
624
+    private function createAltInternalOwnCloudName($name, $isUser) {
625
+        $originalTTL = $this->connection->ldapCacheTTL;
626
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
627
+        if($isUser) {
628
+            $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
629
+        } else {
630
+            $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
631
+        }
632
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
633
+
634
+        return $altName;
635
+    }
636
+
637
+    /**
638
+     * fetches a list of users according to a provided loginName and utilizing
639
+     * the login filter.
640
+     *
641
+     * @param string $loginName
642
+     * @param array $attributes optional, list of attributes to read
643
+     * @return array
644
+     */
645
+    public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
646
+        $loginName = $this->escapeFilterPart($loginName);
647
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
648
+        $users = $this->fetchListOfUsers($filter, $attributes);
649
+        return $users;
650
+    }
651
+
652
+    /**
653
+     * counts the number of users according to a provided loginName and
654
+     * utilizing the login filter.
655
+     *
656
+     * @param string $loginName
657
+     * @return array
658
+     */
659
+    public function countUsersByLoginName($loginName) {
660
+        $loginName = $this->escapeFilterPart($loginName);
661
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
662
+        $users = $this->countUsers($filter);
663
+        return $users;
664
+    }
665
+
666
+    /**
667
+     * @param string $filter
668
+     * @param string|string[] $attr
669
+     * @param int $limit
670
+     * @param int $offset
671
+     * @return array
672
+     */
673
+    public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
674
+        $ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
675
+        $this->batchApplyUserAttributes($ldapRecords);
676
+        return $this->fetchList($ldapRecords, (count($attr) > 1));
677
+    }
678
+
679
+    /**
680
+     * provided with an array of LDAP user records the method will fetch the
681
+     * user object and requests it to process the freshly fetched attributes and
682
+     * and their values
683
+     * @param array $ldapRecords
684
+     */
685
+    public function batchApplyUserAttributes(array $ldapRecords){
686
+        $displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
687
+        foreach($ldapRecords as $userRecord) {
688
+            if(!isset($userRecord[$displayNameAttribute])) {
689
+                // displayName is obligatory
690
+                continue;
691
+            }
692
+            $ocName  = $this->dn2ocname($userRecord['dn'][0]);
693
+            if($ocName === false) {
694
+                continue;
695
+            }
696
+            $this->cacheUserExists($ocName);
697
+            $user = $this->userManager->get($ocName);
698
+            if($user instanceof OfflineUser) {
699
+                $user->unmark();
700
+                $user = $this->userManager->get($ocName);
701
+            }
702
+            if ($user !== null) {
703
+                $user->processAttributes($userRecord);
704
+            } else {
705
+                \OC::$server->getLogger()->debug(
706
+                    "The ldap user manager returned null for $ocName",
707
+                    ['app'=>'user_ldap']
708
+                );
709
+            }
710
+        }
711
+    }
712
+
713
+    /**
714
+     * @param string $filter
715
+     * @param string|string[] $attr
716
+     * @param int $limit
717
+     * @param int $offset
718
+     * @return array
719
+     */
720
+    public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
721
+        return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
722
+    }
723
+
724
+    /**
725
+     * @param array $list
726
+     * @param bool $manyAttributes
727
+     * @return array
728
+     */
729
+    private function fetchList($list, $manyAttributes) {
730
+        if(is_array($list)) {
731
+            if($manyAttributes) {
732
+                return $list;
733
+            } else {
734
+                $list = array_reduce($list, function($carry, $item) {
735
+                    $attribute = array_keys($item)[0];
736
+                    $carry[] = $item[$attribute][0];
737
+                    return $carry;
738
+                }, array());
739
+                return array_unique($list, SORT_LOCALE_STRING);
740
+            }
741
+        }
742
+
743
+        //error cause actually, maybe throw an exception in future.
744
+        return array();
745
+    }
746
+
747
+    /**
748
+     * executes an LDAP search, optimized for Users
749
+     * @param string $filter the LDAP filter for the search
750
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
751
+     * @param integer $limit
752
+     * @param integer $offset
753
+     * @return array with the search result
754
+     *
755
+     * Executes an LDAP search
756
+     */
757
+    public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
758
+        return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
759
+    }
760
+
761
+    /**
762
+     * @param string $filter
763
+     * @param string|string[] $attr
764
+     * @param int $limit
765
+     * @param int $offset
766
+     * @return false|int
767
+     */
768
+    public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
769
+        return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
770
+    }
771
+
772
+    /**
773
+     * executes an LDAP search, optimized for Groups
774
+     * @param string $filter the LDAP filter for the search
775
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
776
+     * @param integer $limit
777
+     * @param integer $offset
778
+     * @return array with the search result
779
+     *
780
+     * Executes an LDAP search
781
+     */
782
+    public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
783
+        return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
784
+    }
785
+
786
+    /**
787
+     * returns the number of available groups
788
+     * @param string $filter the LDAP search filter
789
+     * @param string[] $attr optional
790
+     * @param int|null $limit
791
+     * @param int|null $offset
792
+     * @return int|bool
793
+     */
794
+    public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
795
+        return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
796
+    }
797
+
798
+    /**
799
+     * returns the number of available objects on the base DN
800
+     *
801
+     * @param int|null $limit
802
+     * @param int|null $offset
803
+     * @return int|bool
804
+     */
805
+    public function countObjects($limit = null, $offset = null) {
806
+        return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
807
+    }
808
+
809
+    /**
810
+     * retrieved. Results will according to the order in the array.
811
+     * @param int $limit optional, maximum results to be counted
812
+     * @param int $offset optional, a starting point
813
+     * @return array|false array with the search result as first value and pagedSearchOK as
814
+     * second | false if not successful
815
+     */
816
+    private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
817
+        if(!is_null($attr) && !is_array($attr)) {
818
+            $attr = array(mb_strtolower($attr, 'UTF-8'));
819
+        }
820
+
821
+        // See if we have a resource, in case not cancel with message
822
+        $cr = $this->connection->getConnectionResource();
823
+        if(!$this->ldap->isResource($cr)) {
824
+            // Seems like we didn't find any resource.
825
+            // Return an empty array just like before.
826
+            \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
827
+            return false;
828
+        }
829
+
830
+        //check whether paged search should be attempted
831
+        $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
832
+
833
+        $linkResources = array_pad(array(), count($base), $cr);
834
+        $sr = $this->ldap->search($linkResources, $base, $filter, $attr);
835
+        $error = $this->ldap->errno($cr);
836
+        if(!is_array($sr) || $error !== 0) {
837
+            \OCP\Util::writeLog('user_ldap',
838
+                'Error when searching: '.$this->ldap->error($cr).
839
+                    ' code '.$this->ldap->errno($cr),
840
+                \OCP\Util::ERROR);
841
+            \OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
842
+            return false;
843
+        }
844
+
845
+        return array($sr, $pagedSearchOK);
846
+    }
847
+
848
+    /**
849
+     * processes an LDAP paged search operation
850
+     * @param array $sr the array containing the LDAP search resources
851
+     * @param string $filter the LDAP filter for the search
852
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
853
+     * @param int $iFoundItems number of results in the search operation
854
+     * @param int $limit maximum results to be counted
855
+     * @param int $offset a starting point
856
+     * @param bool $pagedSearchOK whether a paged search has been executed
857
+     * @param bool $skipHandling required for paged search when cookies to
858
+     * prior results need to be gained
859
+     * @return bool cookie validity, true if we have more pages, false otherwise.
860
+     */
861
+    private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
862
+        $cookie = null;
863
+        if($pagedSearchOK) {
864
+            $cr = $this->connection->getConnectionResource();
865
+            foreach($sr as $key => $res) {
866
+                if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
867
+                    $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
868
+                }
869
+            }
870
+
871
+            //browsing through prior pages to get the cookie for the new one
872
+            if($skipHandling) {
873
+                return;
874
+            }
875
+            // if count is bigger, then the server does not support
876
+            // paged search. Instead, he did a normal search. We set a
877
+            // flag here, so the callee knows how to deal with it.
878
+            if($iFoundItems <= $limit) {
879
+                $this->pagedSearchedSuccessful = true;
880
+            }
881
+        } else {
882
+            if(!is_null($limit)) {
883
+                \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
884
+            }
885
+        }
886
+        /* ++ Fixing RHDS searches with pages with zero results ++
887 887
 		 * Return cookie status. If we don't have more pages, with RHDS
888 888
 		 * cookie is null, with openldap cookie is an empty string and
889 889
 		 * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0
890 890
 		 */
891
-		return !empty($cookie) || $cookie === '0';
892
-	}
893
-
894
-	/**
895
-	 * executes an LDAP search, but counts the results only
896
-	 * @param string $filter the LDAP filter for the search
897
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
898
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
899
-	 * retrieved. Results will according to the order in the array.
900
-	 * @param int $limit optional, maximum results to be counted
901
-	 * @param int $offset optional, a starting point
902
-	 * @param bool $skipHandling indicates whether the pages search operation is
903
-	 * completed
904
-	 * @return int|false Integer or false if the search could not be initialized
905
-	 *
906
-	 */
907
-	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
908
-		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
909
-
910
-		$limitPerPage = intval($this->connection->ldapPagingSize);
911
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
912
-			$limitPerPage = $limit;
913
-		}
914
-
915
-		$counter = 0;
916
-		$count = null;
917
-		$this->connection->getConnectionResource();
918
-
919
-		do {
920
-			$search = $this->executeSearch($filter, $base, $attr,
921
-										   $limitPerPage, $offset);
922
-			if($search === false) {
923
-				return $counter > 0 ? $counter : false;
924
-			}
925
-			list($sr, $pagedSearchOK) = $search;
926
-
927
-			/* ++ Fixing RHDS searches with pages with zero results ++
891
+        return !empty($cookie) || $cookie === '0';
892
+    }
893
+
894
+    /**
895
+     * executes an LDAP search, but counts the results only
896
+     * @param string $filter the LDAP filter for the search
897
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
898
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
899
+     * retrieved. Results will according to the order in the array.
900
+     * @param int $limit optional, maximum results to be counted
901
+     * @param int $offset optional, a starting point
902
+     * @param bool $skipHandling indicates whether the pages search operation is
903
+     * completed
904
+     * @return int|false Integer or false if the search could not be initialized
905
+     *
906
+     */
907
+    private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
908
+        \OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
909
+
910
+        $limitPerPage = intval($this->connection->ldapPagingSize);
911
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
912
+            $limitPerPage = $limit;
913
+        }
914
+
915
+        $counter = 0;
916
+        $count = null;
917
+        $this->connection->getConnectionResource();
918
+
919
+        do {
920
+            $search = $this->executeSearch($filter, $base, $attr,
921
+                                            $limitPerPage, $offset);
922
+            if($search === false) {
923
+                return $counter > 0 ? $counter : false;
924
+            }
925
+            list($sr, $pagedSearchOK) = $search;
926
+
927
+            /* ++ Fixing RHDS searches with pages with zero results ++
928 928
 			 * countEntriesInSearchResults() method signature changed
929 929
 			 * by removing $limit and &$hasHitLimit parameters
930 930
 			 */
931
-			$count = $this->countEntriesInSearchResults($sr);
932
-			$counter += $count;
931
+            $count = $this->countEntriesInSearchResults($sr);
932
+            $counter += $count;
933 933
 
934
-			$hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
935
-										$offset, $pagedSearchOK, $skipHandling);
936
-			$offset += $limitPerPage;
937
-			/* ++ Fixing RHDS searches with pages with zero results ++
934
+            $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
935
+                                        $offset, $pagedSearchOK, $skipHandling);
936
+            $offset += $limitPerPage;
937
+            /* ++ Fixing RHDS searches with pages with zero results ++
938 938
 			 * Continue now depends on $hasMorePages value
939 939
 			 */
940
-			$continue = $pagedSearchOK && $hasMorePages;
941
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
942
-
943
-		return $counter;
944
-	}
945
-
946
-	/**
947
-	 * @param array $searchResults
948
-	 * @return int
949
-	 */
950
-	private function countEntriesInSearchResults($searchResults) {
951
-		$cr = $this->connection->getConnectionResource();
952
-		$counter = 0;
953
-
954
-		foreach($searchResults as $res) {
955
-			$count = intval($this->ldap->countEntries($cr, $res));
956
-			$counter += $count;
957
-		}
958
-
959
-		return $counter;
960
-	}
961
-
962
-	/**
963
-	 * Executes an LDAP search
964
-	 * @param string $filter the LDAP filter for the search
965
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
966
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
967
-	 * @param int $limit
968
-	 * @param int $offset
969
-	 * @param bool $skipHandling
970
-	 * @return array with the search result
971
-	 */
972
-	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
973
-		if($limit <= 0) {
974
-			//otherwise search will fail
975
-			$limit = null;
976
-		}
977
-
978
-		/* ++ Fixing RHDS searches with pages with zero results ++
940
+            $continue = $pagedSearchOK && $hasMorePages;
941
+        } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
942
+
943
+        return $counter;
944
+    }
945
+
946
+    /**
947
+     * @param array $searchResults
948
+     * @return int
949
+     */
950
+    private function countEntriesInSearchResults($searchResults) {
951
+        $cr = $this->connection->getConnectionResource();
952
+        $counter = 0;
953
+
954
+        foreach($searchResults as $res) {
955
+            $count = intval($this->ldap->countEntries($cr, $res));
956
+            $counter += $count;
957
+        }
958
+
959
+        return $counter;
960
+    }
961
+
962
+    /**
963
+     * Executes an LDAP search
964
+     * @param string $filter the LDAP filter for the search
965
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
966
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
967
+     * @param int $limit
968
+     * @param int $offset
969
+     * @param bool $skipHandling
970
+     * @return array with the search result
971
+     */
972
+    private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
973
+        if($limit <= 0) {
974
+            //otherwise search will fail
975
+            $limit = null;
976
+        }
977
+
978
+        /* ++ Fixing RHDS searches with pages with zero results ++
979 979
 		 * As we can have pages with zero results and/or pages with less
980 980
 		 * than $limit results but with a still valid server 'cookie',
981 981
 		 * loops through until we get $continue equals true and
982 982
 		 * $findings['count'] < $limit
983 983
 		 */
984
-		$findings = array();
985
-		$savedoffset = $offset;
986
-		do {
987
-			$continue = false;
988
-			$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
989
-			if($search === false) {
990
-				return array();
991
-			}
992
-			list($sr, $pagedSearchOK) = $search;
993
-			$cr = $this->connection->getConnectionResource();
994
-
995
-			if($skipHandling) {
996
-				//i.e. result do not need to be fetched, we just need the cookie
997
-				//thus pass 1 or any other value as $iFoundItems because it is not
998
-				//used
999
-				$this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
1000
-								$offset, $pagedSearchOK,
1001
-								$skipHandling);
1002
-				return array();
1003
-			}
1004
-
1005
-			foreach($sr as $res) {
1006
-				$findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1007
-			}
1008
-
1009
-			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
1010
-								$limit, $offset, $pagedSearchOK,
1011
-										$skipHandling);
1012
-			$offset += $limit;
1013
-		} while ($continue && $pagedSearchOK && $findings['count'] < $limit);
1014
-		// reseting offset
1015
-		$offset = $savedoffset;
1016
-
1017
-		// if we're here, probably no connection resource is returned.
1018
-		// to make ownCloud behave nicely, we simply give back an empty array.
1019
-		if(is_null($findings)) {
1020
-			return array();
1021
-		}
1022
-
1023
-		if(!is_null($attr)) {
1024
-			$selection = array();
1025
-			$i = 0;
1026
-			foreach($findings as $item) {
1027
-				if(!is_array($item)) {
1028
-					continue;
1029
-				}
1030
-				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1031
-				foreach($attr as $key) {
1032
-					$key = mb_strtolower($key, 'UTF-8');
1033
-					if(isset($item[$key])) {
1034
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1035
-							unset($item[$key]['count']);
1036
-						}
1037
-						if($key !== 'dn') {
1038
-							$selection[$i][$key] = $this->resemblesDN($key) ?
1039
-								$this->helper->sanitizeDN($item[$key])
1040
-								: $item[$key];
1041
-						} else {
1042
-							$selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1043
-						}
1044
-					}
1045
-
1046
-				}
1047
-				$i++;
1048
-			}
1049
-			$findings = $selection;
1050
-		}
1051
-		//we slice the findings, when
1052
-		//a) paged search unsuccessful, though attempted
1053
-		//b) no paged search, but limit set
1054
-		if((!$this->getPagedSearchResultState()
1055
-			&& $pagedSearchOK)
1056
-			|| (
1057
-				!$pagedSearchOK
1058
-				&& !is_null($limit)
1059
-			)
1060
-		) {
1061
-			$findings = array_slice($findings, intval($offset), $limit);
1062
-		}
1063
-		return $findings;
1064
-	}
1065
-
1066
-	/**
1067
-	 * @param string $name
1068
-	 * @return bool|mixed|string
1069
-	 */
1070
-	public function sanitizeUsername($name) {
1071
-		if($this->connection->ldapIgnoreNamingRules) {
1072
-			return $name;
1073
-		}
1074
-
1075
-		// Transliteration
1076
-		// latin characters to ASCII
1077
-		$name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1078
-
1079
-		// Replacements
1080
-		$name = str_replace(' ', '_', $name);
1081
-
1082
-		// Every remaining disallowed characters will be removed
1083
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1084
-
1085
-		return $name;
1086
-	}
1087
-
1088
-	/**
1089
-	* escapes (user provided) parts for LDAP filter
1090
-	* @param string $input, the provided value
1091
-	* @param bool $allowAsterisk whether in * at the beginning should be preserved
1092
-	* @return string the escaped string
1093
-	*/
1094
-	public function escapeFilterPart($input, $allowAsterisk = false) {
1095
-		$asterisk = '';
1096
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1097
-			$asterisk = '*';
1098
-			$input = mb_substr($input, 1, null, 'UTF-8');
1099
-		}
1100
-		$search  = array('*', '\\', '(', ')');
1101
-		$replace = array('\\*', '\\\\', '\\(', '\\)');
1102
-		return $asterisk . str_replace($search, $replace, $input);
1103
-	}
1104
-
1105
-	/**
1106
-	 * combines the input filters with AND
1107
-	 * @param string[] $filters the filters to connect
1108
-	 * @return string the combined filter
1109
-	 */
1110
-	public function combineFilterWithAnd($filters) {
1111
-		return $this->combineFilter($filters, '&');
1112
-	}
1113
-
1114
-	/**
1115
-	 * combines the input filters with OR
1116
-	 * @param string[] $filters the filters to connect
1117
-	 * @return string the combined filter
1118
-	 * Combines Filter arguments with OR
1119
-	 */
1120
-	public function combineFilterWithOr($filters) {
1121
-		return $this->combineFilter($filters, '|');
1122
-	}
1123
-
1124
-	/**
1125
-	 * combines the input filters with given operator
1126
-	 * @param string[] $filters the filters to connect
1127
-	 * @param string $operator either & or |
1128
-	 * @return string the combined filter
1129
-	 */
1130
-	private function combineFilter($filters, $operator) {
1131
-		$combinedFilter = '('.$operator;
1132
-		foreach($filters as $filter) {
1133
-			if(!empty($filter) && $filter[0] !== '(') {
1134
-				$filter = '('.$filter.')';
1135
-			}
1136
-			$combinedFilter.=$filter;
1137
-		}
1138
-		$combinedFilter.=')';
1139
-		return $combinedFilter;
1140
-	}
1141
-
1142
-	/**
1143
-	 * creates a filter part for to perform search for users
1144
-	 * @param string $search the search term
1145
-	 * @return string the final filter part to use in LDAP searches
1146
-	 */
1147
-	public function getFilterPartForUserSearch($search) {
1148
-		return $this->getFilterPartForSearch($search,
1149
-			$this->connection->ldapAttributesForUserSearch,
1150
-			$this->connection->ldapUserDisplayName);
1151
-	}
1152
-
1153
-	/**
1154
-	 * creates a filter part for to perform search for groups
1155
-	 * @param string $search the search term
1156
-	 * @return string the final filter part to use in LDAP searches
1157
-	 */
1158
-	public function getFilterPartForGroupSearch($search) {
1159
-		return $this->getFilterPartForSearch($search,
1160
-			$this->connection->ldapAttributesForGroupSearch,
1161
-			$this->connection->ldapGroupDisplayName);
1162
-	}
1163
-
1164
-	/**
1165
-	 * creates a filter part for searches by splitting up the given search
1166
-	 * string into single words
1167
-	 * @param string $search the search term
1168
-	 * @param string[] $searchAttributes needs to have at least two attributes,
1169
-	 * otherwise it does not make sense :)
1170
-	 * @return string the final filter part to use in LDAP searches
1171
-	 * @throws \Exception
1172
-	 */
1173
-	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1174
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1175
-			throw new \Exception('searchAttributes must be an array with at least two string');
1176
-		}
1177
-		$searchWords = explode(' ', trim($search));
1178
-		$wordFilters = array();
1179
-		foreach($searchWords as $word) {
1180
-			$word = $this->prepareSearchTerm($word);
1181
-			//every word needs to appear at least once
1182
-			$wordMatchOneAttrFilters = array();
1183
-			foreach($searchAttributes as $attr) {
1184
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1185
-			}
1186
-			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1187
-		}
1188
-		return $this->combineFilterWithAnd($wordFilters);
1189
-	}
1190
-
1191
-	/**
1192
-	 * creates a filter part for searches
1193
-	 * @param string $search the search term
1194
-	 * @param string[]|null $searchAttributes
1195
-	 * @param string $fallbackAttribute a fallback attribute in case the user
1196
-	 * did not define search attributes. Typically the display name attribute.
1197
-	 * @return string the final filter part to use in LDAP searches
1198
-	 */
1199
-	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1200
-		$filter = array();
1201
-		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1202
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1203
-			try {
1204
-				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1205
-			} catch(\Exception $e) {
1206
-				\OCP\Util::writeLog(
1207
-					'user_ldap',
1208
-					'Creating advanced filter for search failed, falling back to simple method.',
1209
-					\OCP\Util::INFO
1210
-				);
1211
-			}
1212
-		}
1213
-
1214
-		$search = $this->prepareSearchTerm($search);
1215
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1216
-			if(empty($fallbackAttribute)) {
1217
-				return '';
1218
-			}
1219
-			$filter[] = $fallbackAttribute . '=' . $search;
1220
-		} else {
1221
-			foreach($searchAttributes as $attribute) {
1222
-				$filter[] = $attribute . '=' . $search;
1223
-			}
1224
-		}
1225
-		if(count($filter) === 1) {
1226
-			return '('.$filter[0].')';
1227
-		}
1228
-		return $this->combineFilterWithOr($filter);
1229
-	}
1230
-
1231
-	/**
1232
-	 * returns the search term depending on whether we are allowed
1233
-	 * list users found by ldap with the current input appended by
1234
-	 * a *
1235
-	 * @return string
1236
-	 */
1237
-	private function prepareSearchTerm($term) {
1238
-		$config = \OC::$server->getConfig();
1239
-
1240
-		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1241
-
1242
-		$result = empty($term) ? '*' :
1243
-			$allowEnum !== 'no' ? $term . '*' : $term;
1244
-		return $result;
1245
-	}
1246
-
1247
-	/**
1248
-	 * returns the filter used for counting users
1249
-	 * @return string
1250
-	 */
1251
-	public function getFilterForUserCount() {
1252
-		$filter = $this->combineFilterWithAnd(array(
1253
-			$this->connection->ldapUserFilter,
1254
-			$this->connection->ldapUserDisplayName . '=*'
1255
-		));
1256
-
1257
-		return $filter;
1258
-	}
1259
-
1260
-	/**
1261
-	 * @param string $name
1262
-	 * @param string $password
1263
-	 * @return bool
1264
-	 */
1265
-	public function areCredentialsValid($name, $password) {
1266
-		$name = $this->helper->DNasBaseParameter($name);
1267
-		$testConnection = clone $this->connection;
1268
-		$credentials = array(
1269
-			'ldapAgentName' => $name,
1270
-			'ldapAgentPassword' => $password
1271
-		);
1272
-		if(!$testConnection->setConfiguration($credentials)) {
1273
-			return false;
1274
-		}
1275
-		return $testConnection->bind();
1276
-	}
1277
-
1278
-	/**
1279
-	 * reverse lookup of a DN given a known UUID
1280
-	 *
1281
-	 * @param string $uuid
1282
-	 * @return string
1283
-	 * @throws \Exception
1284
-	 */
1285
-	public function getUserDnByUuid($uuid) {
1286
-		$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1287
-		$filter       = $this->connection->ldapUserFilter;
1288
-		$base         = $this->connection->ldapBaseUsers;
1289
-
1290
-		if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1291
-			// Sacrebleu! The UUID attribute is unknown :( We need first an
1292
-			// existing DN to be able to reliably detect it.
1293
-			$result = $this->search($filter, $base, ['dn'], 1);
1294
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1295
-				throw new \Exception('Cannot determine UUID attribute');
1296
-			}
1297
-			$dn = $result[0]['dn'][0];
1298
-			if(!$this->detectUuidAttribute($dn, true)) {
1299
-				throw new \Exception('Cannot determine UUID attribute');
1300
-			}
1301
-		} else {
1302
-			// The UUID attribute is either known or an override is given.
1303
-			// By calling this method we ensure that $this->connection->$uuidAttr
1304
-			// is definitely set
1305
-			if(!$this->detectUuidAttribute('', true)) {
1306
-				throw new \Exception('Cannot determine UUID attribute');
1307
-			}
1308
-		}
1309
-
1310
-		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1311
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1312
-			$uuid = $this->formatGuid2ForFilterUser($uuid);
1313
-		}
1314
-
1315
-		$filter = $uuidAttr . '=' . $uuid;
1316
-		$result = $this->searchUsers($filter, ['dn'], 2);
1317
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1318
-			// we put the count into account to make sure that this is
1319
-			// really unique
1320
-			return $result[0]['dn'][0];
1321
-		}
1322
-
1323
-		throw new \Exception('Cannot determine UUID attribute');
1324
-	}
1325
-
1326
-	/**
1327
-	 * auto-detects the directory's UUID attribute
1328
-	 * @param string $dn a known DN used to check against
1329
-	 * @param bool $isUser
1330
-	 * @param bool $force the detection should be run, even if it is not set to auto
1331
-	 * @return bool true on success, false otherwise
1332
-	 */
1333
-	private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1334
-		if($isUser) {
1335
-			$uuidAttr     = 'ldapUuidUserAttribute';
1336
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1337
-		} else {
1338
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1339
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1340
-		}
1341
-
1342
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1343
-			return true;
1344
-		}
1345
-
1346
-		if(!empty($uuidOverride) && !$force) {
1347
-			$this->connection->$uuidAttr = $uuidOverride;
1348
-			return true;
1349
-		}
1350
-
1351
-		// for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1352
-		$testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1353
-
1354
-		foreach($testAttributes as $attribute) {
1355
-			$value = $this->readAttribute($dn, $attribute);
1356
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1357
-				\OCP\Util::writeLog('user_ldap',
1358
-									'Setting '.$attribute.' as '.$uuidAttr,
1359
-									\OCP\Util::DEBUG);
1360
-				$this->connection->$uuidAttr = $attribute;
1361
-				return true;
1362
-			}
1363
-		}
1364
-		\OCP\Util::writeLog('user_ldap',
1365
-							'Could not autodetect the UUID attribute',
1366
-							\OCP\Util::ERROR);
1367
-
1368
-		return false;
1369
-	}
1370
-
1371
-	/**
1372
-	 * @param string $dn
1373
-	 * @param bool $isUser
1374
-	 * @return string|bool
1375
-	 */
1376
-	public function getUUID($dn, $isUser = true) {
1377
-		if($isUser) {
1378
-			$uuidAttr     = 'ldapUuidUserAttribute';
1379
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1380
-		} else {
1381
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1382
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1383
-		}
1384
-
1385
-		$uuid = false;
1386
-		if($this->detectUuidAttribute($dn, $isUser)) {
1387
-			$uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1388
-			if( !is_array($uuid)
1389
-				&& !empty($uuidOverride)
1390
-				&& $this->detectUuidAttribute($dn, $isUser, true)) {
1391
-					$uuid = $this->readAttribute($dn,
1392
-												 $this->connection->$uuidAttr);
1393
-			}
1394
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1395
-				$uuid = $uuid[0];
1396
-			}
1397
-		}
1398
-
1399
-		return $uuid;
1400
-	}
1401
-
1402
-	/**
1403
-	 * converts a binary ObjectGUID into a string representation
1404
-	 * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1405
-	 * @return string
1406
-	 * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1407
-	 */
1408
-	private function convertObjectGUID2Str($oguid) {
1409
-		$hex_guid = bin2hex($oguid);
1410
-		$hex_guid_to_guid_str = '';
1411
-		for($k = 1; $k <= 4; ++$k) {
1412
-			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1413
-		}
1414
-		$hex_guid_to_guid_str .= '-';
1415
-		for($k = 1; $k <= 2; ++$k) {
1416
-			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1417
-		}
1418
-		$hex_guid_to_guid_str .= '-';
1419
-		for($k = 1; $k <= 2; ++$k) {
1420
-			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1421
-		}
1422
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1423
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1424
-
1425
-		return strtoupper($hex_guid_to_guid_str);
1426
-	}
1427
-
1428
-	/**
1429
-	 * the first three blocks of the string-converted GUID happen to be in
1430
-	 * reverse order. In order to use it in a filter, this needs to be
1431
-	 * corrected. Furthermore the dashes need to be replaced and \\ preprended
1432
-	 * to every two hax figures.
1433
-	 *
1434
-	 * If an invalid string is passed, it will be returned without change.
1435
-	 *
1436
-	 * @param string $guid
1437
-	 * @return string
1438
-	 */
1439
-	public function formatGuid2ForFilterUser($guid) {
1440
-		if(!is_string($guid)) {
1441
-			throw new \InvalidArgumentException('String expected');
1442
-		}
1443
-		$blocks = explode('-', $guid);
1444
-		if(count($blocks) !== 5) {
1445
-			/*
984
+        $findings = array();
985
+        $savedoffset = $offset;
986
+        do {
987
+            $continue = false;
988
+            $search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
989
+            if($search === false) {
990
+                return array();
991
+            }
992
+            list($sr, $pagedSearchOK) = $search;
993
+            $cr = $this->connection->getConnectionResource();
994
+
995
+            if($skipHandling) {
996
+                //i.e. result do not need to be fetched, we just need the cookie
997
+                //thus pass 1 or any other value as $iFoundItems because it is not
998
+                //used
999
+                $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
1000
+                                $offset, $pagedSearchOK,
1001
+                                $skipHandling);
1002
+                return array();
1003
+            }
1004
+
1005
+            foreach($sr as $res) {
1006
+                $findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1007
+            }
1008
+
1009
+            $continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
1010
+                                $limit, $offset, $pagedSearchOK,
1011
+                                        $skipHandling);
1012
+            $offset += $limit;
1013
+        } while ($continue && $pagedSearchOK && $findings['count'] < $limit);
1014
+        // reseting offset
1015
+        $offset = $savedoffset;
1016
+
1017
+        // if we're here, probably no connection resource is returned.
1018
+        // to make ownCloud behave nicely, we simply give back an empty array.
1019
+        if(is_null($findings)) {
1020
+            return array();
1021
+        }
1022
+
1023
+        if(!is_null($attr)) {
1024
+            $selection = array();
1025
+            $i = 0;
1026
+            foreach($findings as $item) {
1027
+                if(!is_array($item)) {
1028
+                    continue;
1029
+                }
1030
+                $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1031
+                foreach($attr as $key) {
1032
+                    $key = mb_strtolower($key, 'UTF-8');
1033
+                    if(isset($item[$key])) {
1034
+                        if(is_array($item[$key]) && isset($item[$key]['count'])) {
1035
+                            unset($item[$key]['count']);
1036
+                        }
1037
+                        if($key !== 'dn') {
1038
+                            $selection[$i][$key] = $this->resemblesDN($key) ?
1039
+                                $this->helper->sanitizeDN($item[$key])
1040
+                                : $item[$key];
1041
+                        } else {
1042
+                            $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1043
+                        }
1044
+                    }
1045
+
1046
+                }
1047
+                $i++;
1048
+            }
1049
+            $findings = $selection;
1050
+        }
1051
+        //we slice the findings, when
1052
+        //a) paged search unsuccessful, though attempted
1053
+        //b) no paged search, but limit set
1054
+        if((!$this->getPagedSearchResultState()
1055
+            && $pagedSearchOK)
1056
+            || (
1057
+                !$pagedSearchOK
1058
+                && !is_null($limit)
1059
+            )
1060
+        ) {
1061
+            $findings = array_slice($findings, intval($offset), $limit);
1062
+        }
1063
+        return $findings;
1064
+    }
1065
+
1066
+    /**
1067
+     * @param string $name
1068
+     * @return bool|mixed|string
1069
+     */
1070
+    public function sanitizeUsername($name) {
1071
+        if($this->connection->ldapIgnoreNamingRules) {
1072
+            return $name;
1073
+        }
1074
+
1075
+        // Transliteration
1076
+        // latin characters to ASCII
1077
+        $name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1078
+
1079
+        // Replacements
1080
+        $name = str_replace(' ', '_', $name);
1081
+
1082
+        // Every remaining disallowed characters will be removed
1083
+        $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1084
+
1085
+        return $name;
1086
+    }
1087
+
1088
+    /**
1089
+     * escapes (user provided) parts for LDAP filter
1090
+     * @param string $input, the provided value
1091
+     * @param bool $allowAsterisk whether in * at the beginning should be preserved
1092
+     * @return string the escaped string
1093
+     */
1094
+    public function escapeFilterPart($input, $allowAsterisk = false) {
1095
+        $asterisk = '';
1096
+        if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1097
+            $asterisk = '*';
1098
+            $input = mb_substr($input, 1, null, 'UTF-8');
1099
+        }
1100
+        $search  = array('*', '\\', '(', ')');
1101
+        $replace = array('\\*', '\\\\', '\\(', '\\)');
1102
+        return $asterisk . str_replace($search, $replace, $input);
1103
+    }
1104
+
1105
+    /**
1106
+     * combines the input filters with AND
1107
+     * @param string[] $filters the filters to connect
1108
+     * @return string the combined filter
1109
+     */
1110
+    public function combineFilterWithAnd($filters) {
1111
+        return $this->combineFilter($filters, '&');
1112
+    }
1113
+
1114
+    /**
1115
+     * combines the input filters with OR
1116
+     * @param string[] $filters the filters to connect
1117
+     * @return string the combined filter
1118
+     * Combines Filter arguments with OR
1119
+     */
1120
+    public function combineFilterWithOr($filters) {
1121
+        return $this->combineFilter($filters, '|');
1122
+    }
1123
+
1124
+    /**
1125
+     * combines the input filters with given operator
1126
+     * @param string[] $filters the filters to connect
1127
+     * @param string $operator either & or |
1128
+     * @return string the combined filter
1129
+     */
1130
+    private function combineFilter($filters, $operator) {
1131
+        $combinedFilter = '('.$operator;
1132
+        foreach($filters as $filter) {
1133
+            if(!empty($filter) && $filter[0] !== '(') {
1134
+                $filter = '('.$filter.')';
1135
+            }
1136
+            $combinedFilter.=$filter;
1137
+        }
1138
+        $combinedFilter.=')';
1139
+        return $combinedFilter;
1140
+    }
1141
+
1142
+    /**
1143
+     * creates a filter part for to perform search for users
1144
+     * @param string $search the search term
1145
+     * @return string the final filter part to use in LDAP searches
1146
+     */
1147
+    public function getFilterPartForUserSearch($search) {
1148
+        return $this->getFilterPartForSearch($search,
1149
+            $this->connection->ldapAttributesForUserSearch,
1150
+            $this->connection->ldapUserDisplayName);
1151
+    }
1152
+
1153
+    /**
1154
+     * creates a filter part for to perform search for groups
1155
+     * @param string $search the search term
1156
+     * @return string the final filter part to use in LDAP searches
1157
+     */
1158
+    public function getFilterPartForGroupSearch($search) {
1159
+        return $this->getFilterPartForSearch($search,
1160
+            $this->connection->ldapAttributesForGroupSearch,
1161
+            $this->connection->ldapGroupDisplayName);
1162
+    }
1163
+
1164
+    /**
1165
+     * creates a filter part for searches by splitting up the given search
1166
+     * string into single words
1167
+     * @param string $search the search term
1168
+     * @param string[] $searchAttributes needs to have at least two attributes,
1169
+     * otherwise it does not make sense :)
1170
+     * @return string the final filter part to use in LDAP searches
1171
+     * @throws \Exception
1172
+     */
1173
+    private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1174
+        if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1175
+            throw new \Exception('searchAttributes must be an array with at least two string');
1176
+        }
1177
+        $searchWords = explode(' ', trim($search));
1178
+        $wordFilters = array();
1179
+        foreach($searchWords as $word) {
1180
+            $word = $this->prepareSearchTerm($word);
1181
+            //every word needs to appear at least once
1182
+            $wordMatchOneAttrFilters = array();
1183
+            foreach($searchAttributes as $attr) {
1184
+                $wordMatchOneAttrFilters[] = $attr . '=' . $word;
1185
+            }
1186
+            $wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1187
+        }
1188
+        return $this->combineFilterWithAnd($wordFilters);
1189
+    }
1190
+
1191
+    /**
1192
+     * creates a filter part for searches
1193
+     * @param string $search the search term
1194
+     * @param string[]|null $searchAttributes
1195
+     * @param string $fallbackAttribute a fallback attribute in case the user
1196
+     * did not define search attributes. Typically the display name attribute.
1197
+     * @return string the final filter part to use in LDAP searches
1198
+     */
1199
+    private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1200
+        $filter = array();
1201
+        $haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1202
+        if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1203
+            try {
1204
+                return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1205
+            } catch(\Exception $e) {
1206
+                \OCP\Util::writeLog(
1207
+                    'user_ldap',
1208
+                    'Creating advanced filter for search failed, falling back to simple method.',
1209
+                    \OCP\Util::INFO
1210
+                );
1211
+            }
1212
+        }
1213
+
1214
+        $search = $this->prepareSearchTerm($search);
1215
+        if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1216
+            if(empty($fallbackAttribute)) {
1217
+                return '';
1218
+            }
1219
+            $filter[] = $fallbackAttribute . '=' . $search;
1220
+        } else {
1221
+            foreach($searchAttributes as $attribute) {
1222
+                $filter[] = $attribute . '=' . $search;
1223
+            }
1224
+        }
1225
+        if(count($filter) === 1) {
1226
+            return '('.$filter[0].')';
1227
+        }
1228
+        return $this->combineFilterWithOr($filter);
1229
+    }
1230
+
1231
+    /**
1232
+     * returns the search term depending on whether we are allowed
1233
+     * list users found by ldap with the current input appended by
1234
+     * a *
1235
+     * @return string
1236
+     */
1237
+    private function prepareSearchTerm($term) {
1238
+        $config = \OC::$server->getConfig();
1239
+
1240
+        $allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1241
+
1242
+        $result = empty($term) ? '*' :
1243
+            $allowEnum !== 'no' ? $term . '*' : $term;
1244
+        return $result;
1245
+    }
1246
+
1247
+    /**
1248
+     * returns the filter used for counting users
1249
+     * @return string
1250
+     */
1251
+    public function getFilterForUserCount() {
1252
+        $filter = $this->combineFilterWithAnd(array(
1253
+            $this->connection->ldapUserFilter,
1254
+            $this->connection->ldapUserDisplayName . '=*'
1255
+        ));
1256
+
1257
+        return $filter;
1258
+    }
1259
+
1260
+    /**
1261
+     * @param string $name
1262
+     * @param string $password
1263
+     * @return bool
1264
+     */
1265
+    public function areCredentialsValid($name, $password) {
1266
+        $name = $this->helper->DNasBaseParameter($name);
1267
+        $testConnection = clone $this->connection;
1268
+        $credentials = array(
1269
+            'ldapAgentName' => $name,
1270
+            'ldapAgentPassword' => $password
1271
+        );
1272
+        if(!$testConnection->setConfiguration($credentials)) {
1273
+            return false;
1274
+        }
1275
+        return $testConnection->bind();
1276
+    }
1277
+
1278
+    /**
1279
+     * reverse lookup of a DN given a known UUID
1280
+     *
1281
+     * @param string $uuid
1282
+     * @return string
1283
+     * @throws \Exception
1284
+     */
1285
+    public function getUserDnByUuid($uuid) {
1286
+        $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1287
+        $filter       = $this->connection->ldapUserFilter;
1288
+        $base         = $this->connection->ldapBaseUsers;
1289
+
1290
+        if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1291
+            // Sacrebleu! The UUID attribute is unknown :( We need first an
1292
+            // existing DN to be able to reliably detect it.
1293
+            $result = $this->search($filter, $base, ['dn'], 1);
1294
+            if(!isset($result[0]) || !isset($result[0]['dn'])) {
1295
+                throw new \Exception('Cannot determine UUID attribute');
1296
+            }
1297
+            $dn = $result[0]['dn'][0];
1298
+            if(!$this->detectUuidAttribute($dn, true)) {
1299
+                throw new \Exception('Cannot determine UUID attribute');
1300
+            }
1301
+        } else {
1302
+            // The UUID attribute is either known or an override is given.
1303
+            // By calling this method we ensure that $this->connection->$uuidAttr
1304
+            // is definitely set
1305
+            if(!$this->detectUuidAttribute('', true)) {
1306
+                throw new \Exception('Cannot determine UUID attribute');
1307
+            }
1308
+        }
1309
+
1310
+        $uuidAttr = $this->connection->ldapUuidUserAttribute;
1311
+        if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1312
+            $uuid = $this->formatGuid2ForFilterUser($uuid);
1313
+        }
1314
+
1315
+        $filter = $uuidAttr . '=' . $uuid;
1316
+        $result = $this->searchUsers($filter, ['dn'], 2);
1317
+        if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1318
+            // we put the count into account to make sure that this is
1319
+            // really unique
1320
+            return $result[0]['dn'][0];
1321
+        }
1322
+
1323
+        throw new \Exception('Cannot determine UUID attribute');
1324
+    }
1325
+
1326
+    /**
1327
+     * auto-detects the directory's UUID attribute
1328
+     * @param string $dn a known DN used to check against
1329
+     * @param bool $isUser
1330
+     * @param bool $force the detection should be run, even if it is not set to auto
1331
+     * @return bool true on success, false otherwise
1332
+     */
1333
+    private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1334
+        if($isUser) {
1335
+            $uuidAttr     = 'ldapUuidUserAttribute';
1336
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1337
+        } else {
1338
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1339
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1340
+        }
1341
+
1342
+        if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1343
+            return true;
1344
+        }
1345
+
1346
+        if(!empty($uuidOverride) && !$force) {
1347
+            $this->connection->$uuidAttr = $uuidOverride;
1348
+            return true;
1349
+        }
1350
+
1351
+        // for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1352
+        $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1353
+
1354
+        foreach($testAttributes as $attribute) {
1355
+            $value = $this->readAttribute($dn, $attribute);
1356
+            if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1357
+                \OCP\Util::writeLog('user_ldap',
1358
+                                    'Setting '.$attribute.' as '.$uuidAttr,
1359
+                                    \OCP\Util::DEBUG);
1360
+                $this->connection->$uuidAttr = $attribute;
1361
+                return true;
1362
+            }
1363
+        }
1364
+        \OCP\Util::writeLog('user_ldap',
1365
+                            'Could not autodetect the UUID attribute',
1366
+                            \OCP\Util::ERROR);
1367
+
1368
+        return false;
1369
+    }
1370
+
1371
+    /**
1372
+     * @param string $dn
1373
+     * @param bool $isUser
1374
+     * @return string|bool
1375
+     */
1376
+    public function getUUID($dn, $isUser = true) {
1377
+        if($isUser) {
1378
+            $uuidAttr     = 'ldapUuidUserAttribute';
1379
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1380
+        } else {
1381
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1382
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1383
+        }
1384
+
1385
+        $uuid = false;
1386
+        if($this->detectUuidAttribute($dn, $isUser)) {
1387
+            $uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1388
+            if( !is_array($uuid)
1389
+                && !empty($uuidOverride)
1390
+                && $this->detectUuidAttribute($dn, $isUser, true)) {
1391
+                    $uuid = $this->readAttribute($dn,
1392
+                                                    $this->connection->$uuidAttr);
1393
+            }
1394
+            if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1395
+                $uuid = $uuid[0];
1396
+            }
1397
+        }
1398
+
1399
+        return $uuid;
1400
+    }
1401
+
1402
+    /**
1403
+     * converts a binary ObjectGUID into a string representation
1404
+     * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1405
+     * @return string
1406
+     * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1407
+     */
1408
+    private function convertObjectGUID2Str($oguid) {
1409
+        $hex_guid = bin2hex($oguid);
1410
+        $hex_guid_to_guid_str = '';
1411
+        for($k = 1; $k <= 4; ++$k) {
1412
+            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1413
+        }
1414
+        $hex_guid_to_guid_str .= '-';
1415
+        for($k = 1; $k <= 2; ++$k) {
1416
+            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1417
+        }
1418
+        $hex_guid_to_guid_str .= '-';
1419
+        for($k = 1; $k <= 2; ++$k) {
1420
+            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1421
+        }
1422
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1423
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1424
+
1425
+        return strtoupper($hex_guid_to_guid_str);
1426
+    }
1427
+
1428
+    /**
1429
+     * the first three blocks of the string-converted GUID happen to be in
1430
+     * reverse order. In order to use it in a filter, this needs to be
1431
+     * corrected. Furthermore the dashes need to be replaced and \\ preprended
1432
+     * to every two hax figures.
1433
+     *
1434
+     * If an invalid string is passed, it will be returned without change.
1435
+     *
1436
+     * @param string $guid
1437
+     * @return string
1438
+     */
1439
+    public function formatGuid2ForFilterUser($guid) {
1440
+        if(!is_string($guid)) {
1441
+            throw new \InvalidArgumentException('String expected');
1442
+        }
1443
+        $blocks = explode('-', $guid);
1444
+        if(count($blocks) !== 5) {
1445
+            /*
1446 1446
 			 * Why not throw an Exception instead? This method is a utility
1447 1447
 			 * called only when trying to figure out whether a "missing" known
1448 1448
 			 * LDAP user was or was not renamed on the LDAP server. And this
@@ -1453,275 +1453,275 @@  discard block
 block discarded – undo
1453 1453
 			 * an exception here would kill the experience for a valid, acting
1454 1454
 			 * user. Instead we write a log message.
1455 1455
 			 */
1456
-			\OC::$server->getLogger()->info(
1457
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1458
-				'({uuid}) probably does not match UUID configuration.',
1459
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1460
-			);
1461
-			return $guid;
1462
-		}
1463
-		for($i=0; $i < 3; $i++) {
1464
-			$pairs = str_split($blocks[$i], 2);
1465
-			$pairs = array_reverse($pairs);
1466
-			$blocks[$i] = implode('', $pairs);
1467
-		}
1468
-		for($i=0; $i < 5; $i++) {
1469
-			$pairs = str_split($blocks[$i], 2);
1470
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1471
-		}
1472
-		return implode('', $blocks);
1473
-	}
1474
-
1475
-	/**
1476
-	 * gets a SID of the domain of the given dn
1477
-	 * @param string $dn
1478
-	 * @return string|bool
1479
-	 */
1480
-	public function getSID($dn) {
1481
-		$domainDN = $this->getDomainDNFromDN($dn);
1482
-		$cacheKey = 'getSID-'.$domainDN;
1483
-		$sid = $this->connection->getFromCache($cacheKey);
1484
-		if(!is_null($sid)) {
1485
-			return $sid;
1486
-		}
1487
-
1488
-		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1489
-		if(!is_array($objectSid) || empty($objectSid)) {
1490
-			$this->connection->writeToCache($cacheKey, false);
1491
-			return false;
1492
-		}
1493
-		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
1494
-		$this->connection->writeToCache($cacheKey, $domainObjectSid);
1495
-
1496
-		return $domainObjectSid;
1497
-	}
1498
-
1499
-	/**
1500
-	 * converts a binary SID into a string representation
1501
-	 * @param string $sid
1502
-	 * @return string
1503
-	 */
1504
-	public function convertSID2Str($sid) {
1505
-		// The format of a SID binary string is as follows:
1506
-		// 1 byte for the revision level
1507
-		// 1 byte for the number n of variable sub-ids
1508
-		// 6 bytes for identifier authority value
1509
-		// n*4 bytes for n sub-ids
1510
-		//
1511
-		// Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1512
-		//  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1513
-		$revision = ord($sid[0]);
1514
-		$numberSubID = ord($sid[1]);
1515
-
1516
-		$subIdStart = 8; // 1 + 1 + 6
1517
-		$subIdLength = 4;
1518
-		if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1519
-			// Incorrect number of bytes present.
1520
-			return '';
1521
-		}
1522
-
1523
-		// 6 bytes = 48 bits can be represented using floats without loss of
1524
-		// precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1525
-		$iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1526
-
1527
-		$subIDs = array();
1528
-		for ($i = 0; $i < $numberSubID; $i++) {
1529
-			$subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1530
-			$subIDs[] = sprintf('%u', $subID[1]);
1531
-		}
1532
-
1533
-		// Result for example above: S-1-5-21-249921958-728525901-1594176202
1534
-		return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1535
-	}
1536
-
1537
-	/**
1538
-	 * checks if the given DN is part of the given base DN(s)
1539
-	 * @param string $dn the DN
1540
-	 * @param string[] $bases array containing the allowed base DN or DNs
1541
-	 * @return bool
1542
-	 */
1543
-	public function isDNPartOfBase($dn, $bases) {
1544
-		$belongsToBase = false;
1545
-		$bases = $this->helper->sanitizeDN($bases);
1546
-
1547
-		foreach($bases as $base) {
1548
-			$belongsToBase = true;
1549
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1550
-				$belongsToBase = false;
1551
-			}
1552
-			if($belongsToBase) {
1553
-				break;
1554
-			}
1555
-		}
1556
-		return $belongsToBase;
1557
-	}
1558
-
1559
-	/**
1560
-	 * resets a running Paged Search operation
1561
-	 */
1562
-	private function abandonPagedSearch() {
1563
-		if($this->connection->hasPagedResultSupport) {
1564
-			$cr = $this->connection->getConnectionResource();
1565
-			$this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1566
-			$this->getPagedSearchResultState();
1567
-			$this->lastCookie = '';
1568
-			$this->cookies = array();
1569
-		}
1570
-	}
1571
-
1572
-	/**
1573
-	 * get a cookie for the next LDAP paged search
1574
-	 * @param string $base a string with the base DN for the search
1575
-	 * @param string $filter the search filter to identify the correct search
1576
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1577
-	 * @param int $offset the offset for the new search to identify the correct search really good
1578
-	 * @return string containing the key or empty if none is cached
1579
-	 */
1580
-	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1581
-		if($offset === 0) {
1582
-			return '';
1583
-		}
1584
-		$offset -= $limit;
1585
-		//we work with cache here
1586
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1587
-		$cookie = '';
1588
-		if(isset($this->cookies[$cacheKey])) {
1589
-			$cookie = $this->cookies[$cacheKey];
1590
-			if(is_null($cookie)) {
1591
-				$cookie = '';
1592
-			}
1593
-		}
1594
-		return $cookie;
1595
-	}
1596
-
1597
-	/**
1598
-	 * checks whether an LDAP paged search operation has more pages that can be
1599
-	 * retrieved, typically when offset and limit are provided.
1600
-	 *
1601
-	 * Be very careful to use it: the last cookie value, which is inspected, can
1602
-	 * be reset by other operations. Best, call it immediately after a search(),
1603
-	 * searchUsers() or searchGroups() call. count-methods are probably safe as
1604
-	 * well. Don't rely on it with any fetchList-method.
1605
-	 * @return bool
1606
-	 */
1607
-	public function hasMoreResults() {
1608
-		if(!$this->connection->hasPagedResultSupport) {
1609
-			return false;
1610
-		}
1611
-
1612
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1613
-			// as in RFC 2696, when all results are returned, the cookie will
1614
-			// be empty.
1615
-			return false;
1616
-		}
1617
-
1618
-		return true;
1619
-	}
1620
-
1621
-	/**
1622
-	 * set a cookie for LDAP paged search run
1623
-	 * @param string $base a string with the base DN for the search
1624
-	 * @param string $filter the search filter to identify the correct search
1625
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1626
-	 * @param int $offset the offset for the run search to identify the correct search really good
1627
-	 * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1628
-	 * @return void
1629
-	 */
1630
-	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1631
-		// allow '0' for 389ds
1632
-		if(!empty($cookie) || $cookie === '0') {
1633
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1634
-			$this->cookies[$cacheKey] = $cookie;
1635
-			$this->lastCookie = $cookie;
1636
-		}
1637
-	}
1638
-
1639
-	/**
1640
-	 * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1641
-	 * @return boolean|null true on success, null or false otherwise
1642
-	 */
1643
-	public function getPagedSearchResultState() {
1644
-		$result = $this->pagedSearchedSuccessful;
1645
-		$this->pagedSearchedSuccessful = null;
1646
-		return $result;
1647
-	}
1648
-
1649
-	/**
1650
-	 * Prepares a paged search, if possible
1651
-	 * @param string $filter the LDAP filter for the search
1652
-	 * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1653
-	 * @param string[] $attr optional, when a certain attribute shall be filtered outside
1654
-	 * @param int $limit
1655
-	 * @param int $offset
1656
-	 * @return bool|true
1657
-	 */
1658
-	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1659
-		$pagedSearchOK = false;
1660
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1661
-			$offset = intval($offset); //can be null
1662
-			\OCP\Util::writeLog('user_ldap',
1663
-				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1664
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1665
-				\OCP\Util::DEBUG);
1666
-			//get the cookie from the search for the previous search, required by LDAP
1667
-			foreach($bases as $base) {
1668
-
1669
-				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1670
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1671
-					// no cookie known, although the offset is not 0. Maybe cache run out. We need
1672
-					// to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1673
-					// searching was designed by MSFT?)
1674
-					// 		Lukas: No, but thanks to reading that source I finally know!
1675
-					// '0' is valid, because 389ds
1676
-					$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
1677
-					//a bit recursive, $offset of 0 is the exit
1678
-					\OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
1679
-					$this->search($filter, array($base), $attr, $limit, $reOffset, true);
1680
-					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1681
-					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1682
-					//TODO: remember this, probably does not change in the next request...
1683
-					if(empty($cookie) && $cookie !== '0') {
1684
-						// '0' is valid, because 389ds
1685
-						$cookie = null;
1686
-					}
1687
-				}
1688
-				if(!is_null($cookie)) {
1689
-					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1690
-					$this->abandonPagedSearch();
1691
-					$pagedSearchOK = $this->ldap->controlPagedResult(
1692
-						$this->connection->getConnectionResource(), $limit,
1693
-						false, $cookie);
1694
-					if(!$pagedSearchOK) {
1695
-						return false;
1696
-					}
1697
-					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1698
-				} else {
1699
-					\OCP\Util::writeLog('user_ldap',
1700
-						'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1701
-						\OCP\Util::INFO);
1702
-				}
1703
-
1704
-			}
1705
-		/* ++ Fixing RHDS searches with pages with zero results ++
1456
+            \OC::$server->getLogger()->info(
1457
+                'Passed string does not resemble a valid GUID. Known UUID ' .
1458
+                '({uuid}) probably does not match UUID configuration.',
1459
+                [ 'app' => 'user_ldap', 'uuid' => $guid ]
1460
+            );
1461
+            return $guid;
1462
+        }
1463
+        for($i=0; $i < 3; $i++) {
1464
+            $pairs = str_split($blocks[$i], 2);
1465
+            $pairs = array_reverse($pairs);
1466
+            $blocks[$i] = implode('', $pairs);
1467
+        }
1468
+        for($i=0; $i < 5; $i++) {
1469
+            $pairs = str_split($blocks[$i], 2);
1470
+            $blocks[$i] = '\\' . implode('\\', $pairs);
1471
+        }
1472
+        return implode('', $blocks);
1473
+    }
1474
+
1475
+    /**
1476
+     * gets a SID of the domain of the given dn
1477
+     * @param string $dn
1478
+     * @return string|bool
1479
+     */
1480
+    public function getSID($dn) {
1481
+        $domainDN = $this->getDomainDNFromDN($dn);
1482
+        $cacheKey = 'getSID-'.$domainDN;
1483
+        $sid = $this->connection->getFromCache($cacheKey);
1484
+        if(!is_null($sid)) {
1485
+            return $sid;
1486
+        }
1487
+
1488
+        $objectSid = $this->readAttribute($domainDN, 'objectsid');
1489
+        if(!is_array($objectSid) || empty($objectSid)) {
1490
+            $this->connection->writeToCache($cacheKey, false);
1491
+            return false;
1492
+        }
1493
+        $domainObjectSid = $this->convertSID2Str($objectSid[0]);
1494
+        $this->connection->writeToCache($cacheKey, $domainObjectSid);
1495
+
1496
+        return $domainObjectSid;
1497
+    }
1498
+
1499
+    /**
1500
+     * converts a binary SID into a string representation
1501
+     * @param string $sid
1502
+     * @return string
1503
+     */
1504
+    public function convertSID2Str($sid) {
1505
+        // The format of a SID binary string is as follows:
1506
+        // 1 byte for the revision level
1507
+        // 1 byte for the number n of variable sub-ids
1508
+        // 6 bytes for identifier authority value
1509
+        // n*4 bytes for n sub-ids
1510
+        //
1511
+        // Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1512
+        //  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1513
+        $revision = ord($sid[0]);
1514
+        $numberSubID = ord($sid[1]);
1515
+
1516
+        $subIdStart = 8; // 1 + 1 + 6
1517
+        $subIdLength = 4;
1518
+        if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1519
+            // Incorrect number of bytes present.
1520
+            return '';
1521
+        }
1522
+
1523
+        // 6 bytes = 48 bits can be represented using floats without loss of
1524
+        // precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1525
+        $iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1526
+
1527
+        $subIDs = array();
1528
+        for ($i = 0; $i < $numberSubID; $i++) {
1529
+            $subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1530
+            $subIDs[] = sprintf('%u', $subID[1]);
1531
+        }
1532
+
1533
+        // Result for example above: S-1-5-21-249921958-728525901-1594176202
1534
+        return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1535
+    }
1536
+
1537
+    /**
1538
+     * checks if the given DN is part of the given base DN(s)
1539
+     * @param string $dn the DN
1540
+     * @param string[] $bases array containing the allowed base DN or DNs
1541
+     * @return bool
1542
+     */
1543
+    public function isDNPartOfBase($dn, $bases) {
1544
+        $belongsToBase = false;
1545
+        $bases = $this->helper->sanitizeDN($bases);
1546
+
1547
+        foreach($bases as $base) {
1548
+            $belongsToBase = true;
1549
+            if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1550
+                $belongsToBase = false;
1551
+            }
1552
+            if($belongsToBase) {
1553
+                break;
1554
+            }
1555
+        }
1556
+        return $belongsToBase;
1557
+    }
1558
+
1559
+    /**
1560
+     * resets a running Paged Search operation
1561
+     */
1562
+    private function abandonPagedSearch() {
1563
+        if($this->connection->hasPagedResultSupport) {
1564
+            $cr = $this->connection->getConnectionResource();
1565
+            $this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1566
+            $this->getPagedSearchResultState();
1567
+            $this->lastCookie = '';
1568
+            $this->cookies = array();
1569
+        }
1570
+    }
1571
+
1572
+    /**
1573
+     * get a cookie for the next LDAP paged search
1574
+     * @param string $base a string with the base DN for the search
1575
+     * @param string $filter the search filter to identify the correct search
1576
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1577
+     * @param int $offset the offset for the new search to identify the correct search really good
1578
+     * @return string containing the key or empty if none is cached
1579
+     */
1580
+    private function getPagedResultCookie($base, $filter, $limit, $offset) {
1581
+        if($offset === 0) {
1582
+            return '';
1583
+        }
1584
+        $offset -= $limit;
1585
+        //we work with cache here
1586
+        $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1587
+        $cookie = '';
1588
+        if(isset($this->cookies[$cacheKey])) {
1589
+            $cookie = $this->cookies[$cacheKey];
1590
+            if(is_null($cookie)) {
1591
+                $cookie = '';
1592
+            }
1593
+        }
1594
+        return $cookie;
1595
+    }
1596
+
1597
+    /**
1598
+     * checks whether an LDAP paged search operation has more pages that can be
1599
+     * retrieved, typically when offset and limit are provided.
1600
+     *
1601
+     * Be very careful to use it: the last cookie value, which is inspected, can
1602
+     * be reset by other operations. Best, call it immediately after a search(),
1603
+     * searchUsers() or searchGroups() call. count-methods are probably safe as
1604
+     * well. Don't rely on it with any fetchList-method.
1605
+     * @return bool
1606
+     */
1607
+    public function hasMoreResults() {
1608
+        if(!$this->connection->hasPagedResultSupport) {
1609
+            return false;
1610
+        }
1611
+
1612
+        if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1613
+            // as in RFC 2696, when all results are returned, the cookie will
1614
+            // be empty.
1615
+            return false;
1616
+        }
1617
+
1618
+        return true;
1619
+    }
1620
+
1621
+    /**
1622
+     * set a cookie for LDAP paged search run
1623
+     * @param string $base a string with the base DN for the search
1624
+     * @param string $filter the search filter to identify the correct search
1625
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1626
+     * @param int $offset the offset for the run search to identify the correct search really good
1627
+     * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1628
+     * @return void
1629
+     */
1630
+    private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1631
+        // allow '0' for 389ds
1632
+        if(!empty($cookie) || $cookie === '0') {
1633
+            $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1634
+            $this->cookies[$cacheKey] = $cookie;
1635
+            $this->lastCookie = $cookie;
1636
+        }
1637
+    }
1638
+
1639
+    /**
1640
+     * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1641
+     * @return boolean|null true on success, null or false otherwise
1642
+     */
1643
+    public function getPagedSearchResultState() {
1644
+        $result = $this->pagedSearchedSuccessful;
1645
+        $this->pagedSearchedSuccessful = null;
1646
+        return $result;
1647
+    }
1648
+
1649
+    /**
1650
+     * Prepares a paged search, if possible
1651
+     * @param string $filter the LDAP filter for the search
1652
+     * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1653
+     * @param string[] $attr optional, when a certain attribute shall be filtered outside
1654
+     * @param int $limit
1655
+     * @param int $offset
1656
+     * @return bool|true
1657
+     */
1658
+    private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1659
+        $pagedSearchOK = false;
1660
+        if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1661
+            $offset = intval($offset); //can be null
1662
+            \OCP\Util::writeLog('user_ldap',
1663
+                'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1664
+                .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1665
+                \OCP\Util::DEBUG);
1666
+            //get the cookie from the search for the previous search, required by LDAP
1667
+            foreach($bases as $base) {
1668
+
1669
+                $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1670
+                if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1671
+                    // no cookie known, although the offset is not 0. Maybe cache run out. We need
1672
+                    // to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1673
+                    // searching was designed by MSFT?)
1674
+                    // 		Lukas: No, but thanks to reading that source I finally know!
1675
+                    // '0' is valid, because 389ds
1676
+                    $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
1677
+                    //a bit recursive, $offset of 0 is the exit
1678
+                    \OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
1679
+                    $this->search($filter, array($base), $attr, $limit, $reOffset, true);
1680
+                    $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1681
+                    //still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1682
+                    //TODO: remember this, probably does not change in the next request...
1683
+                    if(empty($cookie) && $cookie !== '0') {
1684
+                        // '0' is valid, because 389ds
1685
+                        $cookie = null;
1686
+                    }
1687
+                }
1688
+                if(!is_null($cookie)) {
1689
+                    //since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1690
+                    $this->abandonPagedSearch();
1691
+                    $pagedSearchOK = $this->ldap->controlPagedResult(
1692
+                        $this->connection->getConnectionResource(), $limit,
1693
+                        false, $cookie);
1694
+                    if(!$pagedSearchOK) {
1695
+                        return false;
1696
+                    }
1697
+                    \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1698
+                } else {
1699
+                    \OCP\Util::writeLog('user_ldap',
1700
+                        'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1701
+                        \OCP\Util::INFO);
1702
+                }
1703
+
1704
+            }
1705
+        /* ++ Fixing RHDS searches with pages with zero results ++
1706 1706
 		 * We coudn't get paged searches working with our RHDS for login ($limit = 0),
1707 1707
 		 * due to pages with zero results.
1708 1708
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1709 1709
 		 * if we don't have a previous paged search.
1710 1710
 		 */
1711
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1712
-			// a search without limit was requested. However, if we do use
1713
-			// Paged Search once, we always must do it. This requires us to
1714
-			// initialize it with the configured page size.
1715
-			$this->abandonPagedSearch();
1716
-			// in case someone set it to 0 … use 500, otherwise no results will
1717
-			// be returned.
1718
-			$pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1719
-			$pagedSearchOK = $this->ldap->controlPagedResult(
1720
-				$this->connection->getConnectionResource(), $pageSize, false, ''
1721
-			);
1722
-		}
1723
-
1724
-		return $pagedSearchOK;
1725
-	}
1711
+        } else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1712
+            // a search without limit was requested. However, if we do use
1713
+            // Paged Search once, we always must do it. This requires us to
1714
+            // initialize it with the configured page size.
1715
+            $this->abandonPagedSearch();
1716
+            // in case someone set it to 0 … use 500, otherwise no results will
1717
+            // be returned.
1718
+            $pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1719
+            $pagedSearchOK = $this->ldap->controlPagedResult(
1720
+                $this->connection->getConnectionResource(), $pageSize, false, ''
1721
+            );
1722
+        }
1723
+
1724
+        return $pagedSearchOK;
1725
+    }
1726 1726
 
1727 1727
 }
Please login to merge, or discard this patch.
Spacing   +156 added lines, -157 removed lines patch added patch discarded remove patch
@@ -108,7 +108,7 @@  discard block
 block discarded – undo
108 108
 	 * @return AbstractMapping
109 109
 	 */
110 110
 	public function getUserMapper() {
111
-		if(is_null($this->userMapper)) {
111
+		if (is_null($this->userMapper)) {
112 112
 			throw new \Exception('UserMapper was not assigned to this Access instance.');
113 113
 		}
114 114
 		return $this->userMapper;
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
 	 * @return AbstractMapping
129 129
 	 */
130 130
 	public function getGroupMapper() {
131
-		if(is_null($this->groupMapper)) {
131
+		if (is_null($this->groupMapper)) {
132 132
 			throw new \Exception('GroupMapper was not assigned to this Access instance.');
133 133
 		}
134 134
 		return $this->groupMapper;
@@ -159,14 +159,14 @@  discard block
 block discarded – undo
159 159
 	 *          array if $attr is empty, false otherwise
160 160
 	 */
161 161
 	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
162
-		if(!$this->checkConnection()) {
162
+		if (!$this->checkConnection()) {
163 163
 			\OCP\Util::writeLog('user_ldap',
164 164
 				'No LDAP Connector assigned, access impossible for readAttribute.',
165 165
 				\OCP\Util::WARN);
166 166
 			return false;
167 167
 		}
168 168
 		$cr = $this->connection->getConnectionResource();
169
-		if(!$this->ldap->isResource($cr)) {
169
+		if (!$this->ldap->isResource($cr)) {
170 170
 			//LDAP not available
171 171
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
172 172
 			return false;
@@ -183,8 +183,8 @@  discard block
 block discarded – undo
183 183
 		$this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0);
184 184
 		$dn = $this->helper->DNasBaseParameter($dn);
185 185
 		$rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
186
-		if(!$this->ldap->isResource($rr)) {
187
-			if(!empty($attr)) {
186
+		if (!$this->ldap->isResource($rr)) {
187
+			if (!empty($attr)) {
188 188
 				//do not throw this message on userExists check, irritates
189 189
 				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
190 190
 			}
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 			return array();
197 197
 		}
198 198
 		$er = $this->ldap->firstEntry($cr, $rr);
199
-		if(!$this->ldap->isResource($er)) {
199
+		if (!$this->ldap->isResource($er)) {
200 200
 			//did not match the filter, return false
201 201
 			return false;
202 202
 		}
@@ -205,12 +205,12 @@  discard block
 block discarded – undo
205 205
 				$this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
206 206
 		$attr = mb_strtolower($attr, 'UTF-8');
207 207
 
208
-		if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
208
+		if (isset($result[$attr]) && $result[$attr]['count'] > 0) {
209 209
 			$values = array();
210
-			for($i=0;$i<$result[$attr]['count'];$i++) {
211
-				if($this->resemblesDN($attr)) {
210
+			for ($i = 0; $i < $result[$attr]['count']; $i++) {
211
+				if ($this->resemblesDN($attr)) {
212 212
 					$values[] = $this->helper->sanitizeDN($result[$attr][$i]);
213
-				} elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
213
+				} elseif (strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
214 214
 					$values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
215 215
 				} else {
216 216
 					$values[] = $result[$attr][$i];
@@ -259,17 +259,17 @@  discard block
 block discarded – undo
259 259
 	 */
260 260
 	public function getDomainDNFromDN($dn) {
261 261
 		$allParts = $this->ldap->explodeDN($dn, 0);
262
-		if($allParts === false) {
262
+		if ($allParts === false) {
263 263
 			//not a valid DN
264 264
 			return '';
265 265
 		}
266 266
 		$domainParts = array();
267 267
 		$dcFound = false;
268
-		foreach($allParts as $part) {
269
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
268
+		foreach ($allParts as $part) {
269
+			if (!$dcFound && strpos($part, 'dc=') === 0) {
270 270
 				$dcFound = true;
271 271
 			}
272
-			if($dcFound) {
272
+			if ($dcFound) {
273 273
 				$domainParts[] = $part;
274 274
 			}
275 275
 		}
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
 
297 297
 		//Check whether the DN belongs to the Base, to avoid issues on multi-
298 298
 		//server setups
299
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
299
+		if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
300 300
 			return $fdn;
301 301
 		}
302 302
 
@@ -314,7 +314,7 @@  discard block
 block discarded – undo
314 314
 		//To avoid bypassing the base DN settings under certain circumstances
315 315
 		//with the group support, check whether the provided DN matches one of
316 316
 		//the given Bases
317
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
317
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
318 318
 			return false;
319 319
 		}
320 320
 
@@ -331,11 +331,11 @@  discard block
 block discarded – undo
331 331
 	 */
332 332
 	public function groupsMatchFilter($groupDNs) {
333 333
 		$validGroupDNs = [];
334
-		foreach($groupDNs as $dn) {
334
+		foreach ($groupDNs as $dn) {
335 335
 			$cacheKey = 'groupsMatchFilter-'.$dn;
336 336
 			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
337
-			if(!is_null($groupMatchFilter)) {
338
-				if($groupMatchFilter) {
337
+			if (!is_null($groupMatchFilter)) {
338
+				if ($groupMatchFilter) {
339 339
 					$validGroupDNs[] = $dn;
340 340
 				}
341 341
 				continue;
@@ -343,13 +343,13 @@  discard block
 block discarded – undo
343 343
 
344 344
 			// Check the base DN first. If this is not met already, we don't
345 345
 			// need to ask the server at all.
346
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
346
+			if (!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
347 347
 				$this->connection->writeToCache($cacheKey, false);
348 348
 				continue;
349 349
 			}
350 350
 
351 351
 			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
352
-			if(is_array($result)) {
352
+			if (is_array($result)) {
353 353
 				$this->connection->writeToCache($cacheKey, true);
354 354
 				$validGroupDNs[] = $dn;
355 355
 			} else {
@@ -370,7 +370,7 @@  discard block
 block discarded – undo
370 370
 		//To avoid bypassing the base DN settings under certain circumstances
371 371
 		//with the group support, check whether the provided DN matches one of
372 372
 		//the given Bases
373
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
373
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
374 374
 			return false;
375 375
 		}
376 376
 
@@ -385,7 +385,7 @@  discard block
 block discarded – undo
385 385
 	 * @return string|false with with the name to use in ownCloud
386 386
 	 */
387 387
 	public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
388
-		if($isUser) {
388
+		if ($isUser) {
389 389
 			$mapper = $this->getUserMapper();
390 390
 			$nameAttribute = $this->connection->ldapUserDisplayName;
391 391
 		} else {
@@ -395,15 +395,15 @@  discard block
 block discarded – undo
395 395
 
396 396
 		//let's try to retrieve the ownCloud name from the mappings table
397 397
 		$ocName = $mapper->getNameByDN($fdn);
398
-		if(is_string($ocName)) {
398
+		if (is_string($ocName)) {
399 399
 			return $ocName;
400 400
 		}
401 401
 
402 402
 		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
403 403
 		$uuid = $this->getUUID($fdn, $isUser);
404
-		if(is_string($uuid)) {
404
+		if (is_string($uuid)) {
405 405
 			$ocName = $mapper->getNameByUUID($uuid);
406
-			if(is_string($ocName)) {
406
+			if (is_string($ocName)) {
407 407
 				$mapper->setDNbyUUID($fdn, $uuid);
408 408
 				return $ocName;
409 409
 			}
@@ -413,18 +413,18 @@  discard block
 block discarded – undo
413 413
 			return false;
414 414
 		}
415 415
 
416
-		if(is_null($ldapName)) {
416
+		if (is_null($ldapName)) {
417 417
 			$ldapName = $this->readAttribute($fdn, $nameAttribute);
418
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
418
+			if (!isset($ldapName[0]) && empty($ldapName[0])) {
419 419
 				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
420 420
 				return false;
421 421
 			}
422 422
 			$ldapName = $ldapName[0];
423 423
 		}
424 424
 
425
-		if($isUser) {
425
+		if ($isUser) {
426 426
 			$usernameAttribute = $this->connection->ldapExpertUsernameAttr;
427
-			if(!empty($usernameAttribute)) {
427
+			if (!empty($usernameAttribute)) {
428 428
 				$username = $this->readAttribute($fdn, $usernameAttribute);
429 429
 				$username = $username[0];
430 430
 			} else {
@@ -441,9 +441,9 @@  discard block
 block discarded – undo
441 441
 		// outside of core user management will still cache the user as non-existing.
442 442
 		$originalTTL = $this->connection->ldapCacheTTL;
443 443
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
444
-		if(($isUser && !\OCP\User::userExists($intName))
444
+		if (($isUser && !\OCP\User::userExists($intName))
445 445
 			|| (!$isUser && !\OC_Group::groupExists($intName))) {
446
-			if($mapper->map($fdn, $intName, $uuid)) {
446
+			if ($mapper->map($fdn, $intName, $uuid)) {
447 447
 				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
448 448
 				return $intName;
449 449
 			}
@@ -451,7 +451,7 @@  discard block
 block discarded – undo
451 451
 		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
452 452
 
453 453
 		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
454
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
454
+		if (is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
455 455
 			return $altName;
456 456
 		}
457 457
 
@@ -488,7 +488,7 @@  discard block
 block discarded – undo
488 488
 	 * @return array
489 489
 	 */
490 490
 	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
491
-		if($isUsers) {
491
+		if ($isUsers) {
492 492
 			$nameAttribute = $this->connection->ldapUserDisplayName;
493 493
 			$sndAttribute  = $this->connection->ldapUserDisplayName2;
494 494
 		} else {
@@ -496,9 +496,9 @@  discard block
 block discarded – undo
496 496
 		}
497 497
 		$ownCloudNames = array();
498 498
 
499
-		foreach($ldapObjects as $ldapObject) {
499
+		foreach ($ldapObjects as $ldapObject) {
500 500
 			$nameByLDAP = null;
501
-			if(    isset($ldapObject[$nameAttribute])
501
+			if (isset($ldapObject[$nameAttribute])
502 502
 				&& is_array($ldapObject[$nameAttribute])
503 503
 				&& isset($ldapObject[$nameAttribute][0])
504 504
 			) {
@@ -507,12 +507,12 @@  discard block
 block discarded – undo
507 507
 			}
508 508
 
509 509
 			$ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
510
-			if($ocName) {
510
+			if ($ocName) {
511 511
 				$ownCloudNames[] = $ocName;
512
-				if($isUsers) {
512
+				if ($isUsers) {
513 513
 					//cache the user names so it does not need to be retrieved
514 514
 					//again later (e.g. sharing dialogue).
515
-					if(is_null($nameByLDAP)) {
515
+					if (is_null($nameByLDAP)) {
516 516
 						continue;
517 517
 					}
518 518
 					$sndName = isset($ldapObject[$sndAttribute][0])
@@ -567,9 +567,9 @@  discard block
 block discarded – undo
567 567
 		$attempts = 0;
568 568
 		//while loop is just a precaution. If a name is not generated within
569 569
 		//20 attempts, something else is very wrong. Avoids infinite loop.
570
-		while($attempts < 20){
571
-			$altName = $name . '_' . rand(1000,9999);
572
-			if(!\OCP\User::userExists($altName)) {
570
+		while ($attempts < 20) {
571
+			$altName = $name.'_'.rand(1000, 9999);
572
+			if (!\OCP\User::userExists($altName)) {
573 573
 				return $altName;
574 574
 			}
575 575
 			$attempts++;
@@ -591,25 +591,25 @@  discard block
 block discarded – undo
591 591
 	 */
592 592
 	private function _createAltInternalOwnCloudNameForGroups($name) {
593 593
 		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
594
-		if(!($usedNames) || count($usedNames) === 0) {
594
+		if (!($usedNames) || count($usedNames) === 0) {
595 595
 			$lastNo = 1; //will become name_2
596 596
 		} else {
597 597
 			natsort($usedNames);
598 598
 			$lastName = array_pop($usedNames);
599 599
 			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
600 600
 		}
601
-		$altName = $name.'_'.strval($lastNo+1);
601
+		$altName = $name.'_'.strval($lastNo + 1);
602 602
 		unset($usedNames);
603 603
 
604 604
 		$attempts = 1;
605
-		while($attempts < 21){
605
+		while ($attempts < 21) {
606 606
 			// Check to be really sure it is unique
607 607
 			// while loop is just a precaution. If a name is not generated within
608 608
 			// 20 attempts, something else is very wrong. Avoids infinite loop.
609
-			if(!\OC_Group::groupExists($altName)) {
609
+			if (!\OC_Group::groupExists($altName)) {
610 610
 				return $altName;
611 611
 			}
612
-			$altName = $name . '_' . ($lastNo + $attempts);
612
+			$altName = $name.'_'.($lastNo + $attempts);
613 613
 			$attempts++;
614 614
 		}
615 615
 		return false;
@@ -624,7 +624,7 @@  discard block
 block discarded – undo
624 624
 	private function createAltInternalOwnCloudName($name, $isUser) {
625 625
 		$originalTTL = $this->connection->ldapCacheTTL;
626 626
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
627
-		if($isUser) {
627
+		if ($isUser) {
628 628
 			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
629 629
 		} else {
630 630
 			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
@@ -682,20 +682,20 @@  discard block
 block discarded – undo
682 682
 	 * and their values
683 683
 	 * @param array $ldapRecords
684 684
 	 */
685
-	public function batchApplyUserAttributes(array $ldapRecords){
685
+	public function batchApplyUserAttributes(array $ldapRecords) {
686 686
 		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
687
-		foreach($ldapRecords as $userRecord) {
688
-			if(!isset($userRecord[$displayNameAttribute])) {
687
+		foreach ($ldapRecords as $userRecord) {
688
+			if (!isset($userRecord[$displayNameAttribute])) {
689 689
 				// displayName is obligatory
690 690
 				continue;
691 691
 			}
692
-			$ocName  = $this->dn2ocname($userRecord['dn'][0]);
693
-			if($ocName === false) {
692
+			$ocName = $this->dn2ocname($userRecord['dn'][0]);
693
+			if ($ocName === false) {
694 694
 				continue;
695 695
 			}
696 696
 			$this->cacheUserExists($ocName);
697 697
 			$user = $this->userManager->get($ocName);
698
-			if($user instanceof OfflineUser) {
698
+			if ($user instanceof OfflineUser) {
699 699
 				$user->unmark();
700 700
 				$user = $this->userManager->get($ocName);
701 701
 			}
@@ -727,8 +727,8 @@  discard block
 block discarded – undo
727 727
 	 * @return array
728 728
 	 */
729 729
 	private function fetchList($list, $manyAttributes) {
730
-		if(is_array($list)) {
731
-			if($manyAttributes) {
730
+		if (is_array($list)) {
731
+			if ($manyAttributes) {
732 732
 				return $list;
733 733
 			} else {
734 734
 				$list = array_reduce($list, function($carry, $item) {
@@ -814,13 +814,13 @@  discard block
 block discarded – undo
814 814
 	 * second | false if not successful
815 815
 	 */
816 816
 	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
817
-		if(!is_null($attr) && !is_array($attr)) {
817
+		if (!is_null($attr) && !is_array($attr)) {
818 818
 			$attr = array(mb_strtolower($attr, 'UTF-8'));
819 819
 		}
820 820
 
821 821
 		// See if we have a resource, in case not cancel with message
822 822
 		$cr = $this->connection->getConnectionResource();
823
-		if(!$this->ldap->isResource($cr)) {
823
+		if (!$this->ldap->isResource($cr)) {
824 824
 			// Seems like we didn't find any resource.
825 825
 			// Return an empty array just like before.
826 826
 			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
@@ -833,7 +833,7 @@  discard block
 block discarded – undo
833 833
 		$linkResources = array_pad(array(), count($base), $cr);
834 834
 		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
835 835
 		$error = $this->ldap->errno($cr);
836
-		if(!is_array($sr) || $error !== 0) {
836
+		if (!is_array($sr) || $error !== 0) {
837 837
 			\OCP\Util::writeLog('user_ldap',
838 838
 				'Error when searching: '.$this->ldap->error($cr).
839 839
 					' code '.$this->ldap->errno($cr),
@@ -860,26 +860,26 @@  discard block
 block discarded – undo
860 860
 	 */
861 861
 	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
862 862
 		$cookie = null;
863
-		if($pagedSearchOK) {
863
+		if ($pagedSearchOK) {
864 864
 			$cr = $this->connection->getConnectionResource();
865
-			foreach($sr as $key => $res) {
866
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
865
+			foreach ($sr as $key => $res) {
866
+				if ($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
867 867
 					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
868 868
 				}
869 869
 			}
870 870
 
871 871
 			//browsing through prior pages to get the cookie for the new one
872
-			if($skipHandling) {
872
+			if ($skipHandling) {
873 873
 				return;
874 874
 			}
875 875
 			// if count is bigger, then the server does not support
876 876
 			// paged search. Instead, he did a normal search. We set a
877 877
 			// flag here, so the callee knows how to deal with it.
878
-			if($iFoundItems <= $limit) {
878
+			if ($iFoundItems <= $limit) {
879 879
 				$this->pagedSearchedSuccessful = true;
880 880
 			}
881 881
 		} else {
882
-			if(!is_null($limit)) {
882
+			if (!is_null($limit)) {
883 883
 				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
884 884
 			}
885 885
 		}
@@ -908,7 +908,7 @@  discard block
 block discarded – undo
908 908
 		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
909 909
 
910 910
 		$limitPerPage = intval($this->connection->ldapPagingSize);
911
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
911
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
912 912
 			$limitPerPage = $limit;
913 913
 		}
914 914
 
@@ -919,7 +919,7 @@  discard block
 block discarded – undo
919 919
 		do {
920 920
 			$search = $this->executeSearch($filter, $base, $attr,
921 921
 										   $limitPerPage, $offset);
922
-			if($search === false) {
922
+			if ($search === false) {
923 923
 				return $counter > 0 ? $counter : false;
924 924
 			}
925 925
 			list($sr, $pagedSearchOK) = $search;
@@ -938,7 +938,7 @@  discard block
 block discarded – undo
938 938
 			 * Continue now depends on $hasMorePages value
939 939
 			 */
940 940
 			$continue = $pagedSearchOK && $hasMorePages;
941
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
941
+		} while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
942 942
 
943 943
 		return $counter;
944 944
 	}
@@ -951,7 +951,7 @@  discard block
 block discarded – undo
951 951
 		$cr = $this->connection->getConnectionResource();
952 952
 		$counter = 0;
953 953
 
954
-		foreach($searchResults as $res) {
954
+		foreach ($searchResults as $res) {
955 955
 			$count = intval($this->ldap->countEntries($cr, $res));
956 956
 			$counter += $count;
957 957
 		}
@@ -970,7 +970,7 @@  discard block
 block discarded – undo
970 970
 	 * @return array with the search result
971 971
 	 */
972 972
 	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
973
-		if($limit <= 0) {
973
+		if ($limit <= 0) {
974 974
 			//otherwise search will fail
975 975
 			$limit = null;
976 976
 		}
@@ -986,13 +986,13 @@  discard block
 block discarded – undo
986 986
 		do {
987 987
 			$continue = false;
988 988
 			$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
989
-			if($search === false) {
989
+			if ($search === false) {
990 990
 				return array();
991 991
 			}
992 992
 			list($sr, $pagedSearchOK) = $search;
993 993
 			$cr = $this->connection->getConnectionResource();
994 994
 
995
-			if($skipHandling) {
995
+			if ($skipHandling) {
996 996
 				//i.e. result do not need to be fetched, we just need the cookie
997 997
 				//thus pass 1 or any other value as $iFoundItems because it is not
998 998
 				//used
@@ -1002,8 +1002,8 @@  discard block
 block discarded – undo
1002 1002
 				return array();
1003 1003
 			}
1004 1004
 
1005
-			foreach($sr as $res) {
1006
-				$findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1005
+			foreach ($sr as $res) {
1006
+				$findings = array_merge($findings, $this->ldap->getEntries($cr, $res));
1007 1007
 			}
1008 1008
 
1009 1009
 			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
@@ -1016,25 +1016,25 @@  discard block
 block discarded – undo
1016 1016
 
1017 1017
 		// if we're here, probably no connection resource is returned.
1018 1018
 		// to make ownCloud behave nicely, we simply give back an empty array.
1019
-		if(is_null($findings)) {
1019
+		if (is_null($findings)) {
1020 1020
 			return array();
1021 1021
 		}
1022 1022
 
1023
-		if(!is_null($attr)) {
1023
+		if (!is_null($attr)) {
1024 1024
 			$selection = array();
1025 1025
 			$i = 0;
1026
-			foreach($findings as $item) {
1027
-				if(!is_array($item)) {
1026
+			foreach ($findings as $item) {
1027
+				if (!is_array($item)) {
1028 1028
 					continue;
1029 1029
 				}
1030 1030
 				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1031
-				foreach($attr as $key) {
1031
+				foreach ($attr as $key) {
1032 1032
 					$key = mb_strtolower($key, 'UTF-8');
1033
-					if(isset($item[$key])) {
1034
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1033
+					if (isset($item[$key])) {
1034
+						if (is_array($item[$key]) && isset($item[$key]['count'])) {
1035 1035
 							unset($item[$key]['count']);
1036 1036
 						}
1037
-						if($key !== 'dn') {
1037
+						if ($key !== 'dn') {
1038 1038
 							$selection[$i][$key] = $this->resemblesDN($key) ?
1039 1039
 								$this->helper->sanitizeDN($item[$key])
1040 1040
 								: $item[$key];
@@ -1051,7 +1051,7 @@  discard block
 block discarded – undo
1051 1051
 		//we slice the findings, when
1052 1052
 		//a) paged search unsuccessful, though attempted
1053 1053
 		//b) no paged search, but limit set
1054
-		if((!$this->getPagedSearchResultState()
1054
+		if ((!$this->getPagedSearchResultState()
1055 1055
 			&& $pagedSearchOK)
1056 1056
 			|| (
1057 1057
 				!$pagedSearchOK
@@ -1068,7 +1068,7 @@  discard block
 block discarded – undo
1068 1068
 	 * @return bool|mixed|string
1069 1069
 	 */
1070 1070
 	public function sanitizeUsername($name) {
1071
-		if($this->connection->ldapIgnoreNamingRules) {
1071
+		if ($this->connection->ldapIgnoreNamingRules) {
1072 1072
 			return $name;
1073 1073
 		}
1074 1074
 
@@ -1093,13 +1093,13 @@  discard block
 block discarded – undo
1093 1093
 	*/
1094 1094
 	public function escapeFilterPart($input, $allowAsterisk = false) {
1095 1095
 		$asterisk = '';
1096
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1096
+		if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1097 1097
 			$asterisk = '*';
1098 1098
 			$input = mb_substr($input, 1, null, 'UTF-8');
1099 1099
 		}
1100 1100
 		$search  = array('*', '\\', '(', ')');
1101 1101
 		$replace = array('\\*', '\\\\', '\\(', '\\)');
1102
-		return $asterisk . str_replace($search, $replace, $input);
1102
+		return $asterisk.str_replace($search, $replace, $input);
1103 1103
 	}
1104 1104
 
1105 1105
 	/**
@@ -1129,13 +1129,13 @@  discard block
 block discarded – undo
1129 1129
 	 */
1130 1130
 	private function combineFilter($filters, $operator) {
1131 1131
 		$combinedFilter = '('.$operator;
1132
-		foreach($filters as $filter) {
1133
-			if(!empty($filter) && $filter[0] !== '(') {
1132
+		foreach ($filters as $filter) {
1133
+			if (!empty($filter) && $filter[0] !== '(') {
1134 1134
 				$filter = '('.$filter.')';
1135 1135
 			}
1136
-			$combinedFilter.=$filter;
1136
+			$combinedFilter .= $filter;
1137 1137
 		}
1138
-		$combinedFilter.=')';
1138
+		$combinedFilter .= ')';
1139 1139
 		return $combinedFilter;
1140 1140
 	}
1141 1141
 
@@ -1171,17 +1171,17 @@  discard block
 block discarded – undo
1171 1171
 	 * @throws \Exception
1172 1172
 	 */
1173 1173
 	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1174
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1174
+		if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1175 1175
 			throw new \Exception('searchAttributes must be an array with at least two string');
1176 1176
 		}
1177 1177
 		$searchWords = explode(' ', trim($search));
1178 1178
 		$wordFilters = array();
1179
-		foreach($searchWords as $word) {
1179
+		foreach ($searchWords as $word) {
1180 1180
 			$word = $this->prepareSearchTerm($word);
1181 1181
 			//every word needs to appear at least once
1182 1182
 			$wordMatchOneAttrFilters = array();
1183
-			foreach($searchAttributes as $attr) {
1184
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1183
+			foreach ($searchAttributes as $attr) {
1184
+				$wordMatchOneAttrFilters[] = $attr.'='.$word;
1185 1185
 			}
1186 1186
 			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1187 1187
 		}
@@ -1199,10 +1199,10 @@  discard block
 block discarded – undo
1199 1199
 	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1200 1200
 		$filter = array();
1201 1201
 		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1202
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1202
+		if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1203 1203
 			try {
1204 1204
 				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1205
-			} catch(\Exception $e) {
1205
+			} catch (\Exception $e) {
1206 1206
 				\OCP\Util::writeLog(
1207 1207
 					'user_ldap',
1208 1208
 					'Creating advanced filter for search failed, falling back to simple method.',
@@ -1212,17 +1212,17 @@  discard block
 block discarded – undo
1212 1212
 		}
1213 1213
 
1214 1214
 		$search = $this->prepareSearchTerm($search);
1215
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1216
-			if(empty($fallbackAttribute)) {
1215
+		if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1216
+			if (empty($fallbackAttribute)) {
1217 1217
 				return '';
1218 1218
 			}
1219
-			$filter[] = $fallbackAttribute . '=' . $search;
1219
+			$filter[] = $fallbackAttribute.'='.$search;
1220 1220
 		} else {
1221
-			foreach($searchAttributes as $attribute) {
1222
-				$filter[] = $attribute . '=' . $search;
1221
+			foreach ($searchAttributes as $attribute) {
1222
+				$filter[] = $attribute.'='.$search;
1223 1223
 			}
1224 1224
 		}
1225
-		if(count($filter) === 1) {
1225
+		if (count($filter) === 1) {
1226 1226
 			return '('.$filter[0].')';
1227 1227
 		}
1228 1228
 		return $this->combineFilterWithOr($filter);
@@ -1239,8 +1239,7 @@  discard block
 block discarded – undo
1239 1239
 
1240 1240
 		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1241 1241
 
1242
-		$result = empty($term) ? '*' :
1243
-			$allowEnum !== 'no' ? $term . '*' : $term;
1242
+		$result = empty($term) ? '*' : $allowEnum !== 'no' ? $term.'*' : $term;
1244 1243
 		return $result;
1245 1244
 	}
1246 1245
 
@@ -1251,7 +1250,7 @@  discard block
 block discarded – undo
1251 1250
 	public function getFilterForUserCount() {
1252 1251
 		$filter = $this->combineFilterWithAnd(array(
1253 1252
 			$this->connection->ldapUserFilter,
1254
-			$this->connection->ldapUserDisplayName . '=*'
1253
+			$this->connection->ldapUserDisplayName.'=*'
1255 1254
 		));
1256 1255
 
1257 1256
 		return $filter;
@@ -1269,7 +1268,7 @@  discard block
 block discarded – undo
1269 1268
 			'ldapAgentName' => $name,
1270 1269
 			'ldapAgentPassword' => $password
1271 1270
 		);
1272
-		if(!$testConnection->setConfiguration($credentials)) {
1271
+		if (!$testConnection->setConfiguration($credentials)) {
1273 1272
 			return false;
1274 1273
 		}
1275 1274
 		return $testConnection->bind();
@@ -1287,34 +1286,34 @@  discard block
 block discarded – undo
1287 1286
 		$filter       = $this->connection->ldapUserFilter;
1288 1287
 		$base         = $this->connection->ldapBaseUsers;
1289 1288
 
1290
-		if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1289
+		if ($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1291 1290
 			// Sacrebleu! The UUID attribute is unknown :( We need first an
1292 1291
 			// existing DN to be able to reliably detect it.
1293 1292
 			$result = $this->search($filter, $base, ['dn'], 1);
1294
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1293
+			if (!isset($result[0]) || !isset($result[0]['dn'])) {
1295 1294
 				throw new \Exception('Cannot determine UUID attribute');
1296 1295
 			}
1297 1296
 			$dn = $result[0]['dn'][0];
1298
-			if(!$this->detectUuidAttribute($dn, true)) {
1297
+			if (!$this->detectUuidAttribute($dn, true)) {
1299 1298
 				throw new \Exception('Cannot determine UUID attribute');
1300 1299
 			}
1301 1300
 		} else {
1302 1301
 			// The UUID attribute is either known or an override is given.
1303 1302
 			// By calling this method we ensure that $this->connection->$uuidAttr
1304 1303
 			// is definitely set
1305
-			if(!$this->detectUuidAttribute('', true)) {
1304
+			if (!$this->detectUuidAttribute('', true)) {
1306 1305
 				throw new \Exception('Cannot determine UUID attribute');
1307 1306
 			}
1308 1307
 		}
1309 1308
 
1310 1309
 		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1311
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1310
+		if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1312 1311
 			$uuid = $this->formatGuid2ForFilterUser($uuid);
1313 1312
 		}
1314 1313
 
1315
-		$filter = $uuidAttr . '=' . $uuid;
1314
+		$filter = $uuidAttr.'='.$uuid;
1316 1315
 		$result = $this->searchUsers($filter, ['dn'], 2);
1317
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1316
+		if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1318 1317
 			// we put the count into account to make sure that this is
1319 1318
 			// really unique
1320 1319
 			return $result[0]['dn'][0];
@@ -1331,7 +1330,7 @@  discard block
 block discarded – undo
1331 1330
 	 * @return bool true on success, false otherwise
1332 1331
 	 */
1333 1332
 	private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1334
-		if($isUser) {
1333
+		if ($isUser) {
1335 1334
 			$uuidAttr     = 'ldapUuidUserAttribute';
1336 1335
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1337 1336
 		} else {
@@ -1339,11 +1338,11 @@  discard block
 block discarded – undo
1339 1338
 			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1340 1339
 		}
1341 1340
 
1342
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1341
+		if (($this->connection->$uuidAttr !== 'auto') && !$force) {
1343 1342
 			return true;
1344 1343
 		}
1345 1344
 
1346
-		if(!empty($uuidOverride) && !$force) {
1345
+		if (!empty($uuidOverride) && !$force) {
1347 1346
 			$this->connection->$uuidAttr = $uuidOverride;
1348 1347
 			return true;
1349 1348
 		}
@@ -1351,9 +1350,9 @@  discard block
 block discarded – undo
1351 1350
 		// for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1352 1351
 		$testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1353 1352
 
1354
-		foreach($testAttributes as $attribute) {
1353
+		foreach ($testAttributes as $attribute) {
1355 1354
 			$value = $this->readAttribute($dn, $attribute);
1356
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1355
+			if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1357 1356
 				\OCP\Util::writeLog('user_ldap',
1358 1357
 									'Setting '.$attribute.' as '.$uuidAttr,
1359 1358
 									\OCP\Util::DEBUG);
@@ -1374,7 +1373,7 @@  discard block
 block discarded – undo
1374 1373
 	 * @return string|bool
1375 1374
 	 */
1376 1375
 	public function getUUID($dn, $isUser = true) {
1377
-		if($isUser) {
1376
+		if ($isUser) {
1378 1377
 			$uuidAttr     = 'ldapUuidUserAttribute';
1379 1378
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1380 1379
 		} else {
@@ -1383,15 +1382,15 @@  discard block
 block discarded – undo
1383 1382
 		}
1384 1383
 
1385 1384
 		$uuid = false;
1386
-		if($this->detectUuidAttribute($dn, $isUser)) {
1385
+		if ($this->detectUuidAttribute($dn, $isUser)) {
1387 1386
 			$uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1388
-			if( !is_array($uuid)
1387
+			if (!is_array($uuid)
1389 1388
 				&& !empty($uuidOverride)
1390 1389
 				&& $this->detectUuidAttribute($dn, $isUser, true)) {
1391 1390
 					$uuid = $this->readAttribute($dn,
1392 1391
 												 $this->connection->$uuidAttr);
1393 1392
 			}
1394
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1393
+			if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1395 1394
 				$uuid = $uuid[0];
1396 1395
 			}
1397 1396
 		}
@@ -1408,19 +1407,19 @@  discard block
 block discarded – undo
1408 1407
 	private function convertObjectGUID2Str($oguid) {
1409 1408
 		$hex_guid = bin2hex($oguid);
1410 1409
 		$hex_guid_to_guid_str = '';
1411
-		for($k = 1; $k <= 4; ++$k) {
1410
+		for ($k = 1; $k <= 4; ++$k) {
1412 1411
 			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1413 1412
 		}
1414 1413
 		$hex_guid_to_guid_str .= '-';
1415
-		for($k = 1; $k <= 2; ++$k) {
1414
+		for ($k = 1; $k <= 2; ++$k) {
1416 1415
 			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1417 1416
 		}
1418 1417
 		$hex_guid_to_guid_str .= '-';
1419
-		for($k = 1; $k <= 2; ++$k) {
1418
+		for ($k = 1; $k <= 2; ++$k) {
1420 1419
 			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1421 1420
 		}
1422
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1423
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1421
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 16, 4);
1422
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 20);
1424 1423
 
1425 1424
 		return strtoupper($hex_guid_to_guid_str);
1426 1425
 	}
@@ -1437,11 +1436,11 @@  discard block
 block discarded – undo
1437 1436
 	 * @return string
1438 1437
 	 */
1439 1438
 	public function formatGuid2ForFilterUser($guid) {
1440
-		if(!is_string($guid)) {
1439
+		if (!is_string($guid)) {
1441 1440
 			throw new \InvalidArgumentException('String expected');
1442 1441
 		}
1443 1442
 		$blocks = explode('-', $guid);
1444
-		if(count($blocks) !== 5) {
1443
+		if (count($blocks) !== 5) {
1445 1444
 			/*
1446 1445
 			 * Why not throw an Exception instead? This method is a utility
1447 1446
 			 * called only when trying to figure out whether a "missing" known
@@ -1454,20 +1453,20 @@  discard block
 block discarded – undo
1454 1453
 			 * user. Instead we write a log message.
1455 1454
 			 */
1456 1455
 			\OC::$server->getLogger()->info(
1457
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1456
+				'Passed string does not resemble a valid GUID. Known UUID '.
1458 1457
 				'({uuid}) probably does not match UUID configuration.',
1459
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1458
+				['app' => 'user_ldap', 'uuid' => $guid]
1460 1459
 			);
1461 1460
 			return $guid;
1462 1461
 		}
1463
-		for($i=0; $i < 3; $i++) {
1462
+		for ($i = 0; $i < 3; $i++) {
1464 1463
 			$pairs = str_split($blocks[$i], 2);
1465 1464
 			$pairs = array_reverse($pairs);
1466 1465
 			$blocks[$i] = implode('', $pairs);
1467 1466
 		}
1468
-		for($i=0; $i < 5; $i++) {
1467
+		for ($i = 0; $i < 5; $i++) {
1469 1468
 			$pairs = str_split($blocks[$i], 2);
1470
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1469
+			$blocks[$i] = '\\'.implode('\\', $pairs);
1471 1470
 		}
1472 1471
 		return implode('', $blocks);
1473 1472
 	}
@@ -1481,12 +1480,12 @@  discard block
 block discarded – undo
1481 1480
 		$domainDN = $this->getDomainDNFromDN($dn);
1482 1481
 		$cacheKey = 'getSID-'.$domainDN;
1483 1482
 		$sid = $this->connection->getFromCache($cacheKey);
1484
-		if(!is_null($sid)) {
1483
+		if (!is_null($sid)) {
1485 1484
 			return $sid;
1486 1485
 		}
1487 1486
 
1488 1487
 		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1489
-		if(!is_array($objectSid) || empty($objectSid)) {
1488
+		if (!is_array($objectSid) || empty($objectSid)) {
1490 1489
 			$this->connection->writeToCache($cacheKey, false);
1491 1490
 			return false;
1492 1491
 		}
@@ -1544,12 +1543,12 @@  discard block
 block discarded – undo
1544 1543
 		$belongsToBase = false;
1545 1544
 		$bases = $this->helper->sanitizeDN($bases);
1546 1545
 
1547
-		foreach($bases as $base) {
1546
+		foreach ($bases as $base) {
1548 1547
 			$belongsToBase = true;
1549
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1548
+			if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1550 1549
 				$belongsToBase = false;
1551 1550
 			}
1552
-			if($belongsToBase) {
1551
+			if ($belongsToBase) {
1553 1552
 				break;
1554 1553
 			}
1555 1554
 		}
@@ -1560,7 +1559,7 @@  discard block
 block discarded – undo
1560 1559
 	 * resets a running Paged Search operation
1561 1560
 	 */
1562 1561
 	private function abandonPagedSearch() {
1563
-		if($this->connection->hasPagedResultSupport) {
1562
+		if ($this->connection->hasPagedResultSupport) {
1564 1563
 			$cr = $this->connection->getConnectionResource();
1565 1564
 			$this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1566 1565
 			$this->getPagedSearchResultState();
@@ -1578,16 +1577,16 @@  discard block
 block discarded – undo
1578 1577
 	 * @return string containing the key or empty if none is cached
1579 1578
 	 */
1580 1579
 	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1581
-		if($offset === 0) {
1580
+		if ($offset === 0) {
1582 1581
 			return '';
1583 1582
 		}
1584 1583
 		$offset -= $limit;
1585 1584
 		//we work with cache here
1586
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1585
+		$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1587 1586
 		$cookie = '';
1588
-		if(isset($this->cookies[$cacheKey])) {
1587
+		if (isset($this->cookies[$cacheKey])) {
1589 1588
 			$cookie = $this->cookies[$cacheKey];
1590
-			if(is_null($cookie)) {
1589
+			if (is_null($cookie)) {
1591 1590
 				$cookie = '';
1592 1591
 			}
1593 1592
 		}
@@ -1605,11 +1604,11 @@  discard block
 block discarded – undo
1605 1604
 	 * @return bool
1606 1605
 	 */
1607 1606
 	public function hasMoreResults() {
1608
-		if(!$this->connection->hasPagedResultSupport) {
1607
+		if (!$this->connection->hasPagedResultSupport) {
1609 1608
 			return false;
1610 1609
 		}
1611 1610
 
1612
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1611
+		if (empty($this->lastCookie) && $this->lastCookie !== '0') {
1613 1612
 			// as in RFC 2696, when all results are returned, the cookie will
1614 1613
 			// be empty.
1615 1614
 			return false;
@@ -1629,8 +1628,8 @@  discard block
 block discarded – undo
1629 1628
 	 */
1630 1629
 	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1631 1630
 		// allow '0' for 389ds
1632
-		if(!empty($cookie) || $cookie === '0') {
1633
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1631
+		if (!empty($cookie) || $cookie === '0') {
1632
+			$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1634 1633
 			$this->cookies[$cacheKey] = $cookie;
1635 1634
 			$this->lastCookie = $cookie;
1636 1635
 		}
@@ -1657,17 +1656,17 @@  discard block
 block discarded – undo
1657 1656
 	 */
1658 1657
 	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1659 1658
 		$pagedSearchOK = false;
1660
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1659
+		if ($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1661 1660
 			$offset = intval($offset); //can be null
1662 1661
 			\OCP\Util::writeLog('user_ldap',
1663 1662
 				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1664
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1663
+				.' attr '.print_r($attr, true).' limit '.$limit.' offset '.$offset,
1665 1664
 				\OCP\Util::DEBUG);
1666 1665
 			//get the cookie from the search for the previous search, required by LDAP
1667
-			foreach($bases as $base) {
1666
+			foreach ($bases as $base) {
1668 1667
 
1669 1668
 				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1670
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1669
+				if (empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1671 1670
 					// no cookie known, although the offset is not 0. Maybe cache run out. We need
1672 1671
 					// to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1673 1672
 					// searching was designed by MSFT?)
@@ -1680,18 +1679,18 @@  discard block
 block discarded – undo
1680 1679
 					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1681 1680
 					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1682 1681
 					//TODO: remember this, probably does not change in the next request...
1683
-					if(empty($cookie) && $cookie !== '0') {
1682
+					if (empty($cookie) && $cookie !== '0') {
1684 1683
 						// '0' is valid, because 389ds
1685 1684
 						$cookie = null;
1686 1685
 					}
1687 1686
 				}
1688
-				if(!is_null($cookie)) {
1687
+				if (!is_null($cookie)) {
1689 1688
 					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1690 1689
 					$this->abandonPagedSearch();
1691 1690
 					$pagedSearchOK = $this->ldap->controlPagedResult(
1692 1691
 						$this->connection->getConnectionResource(), $limit,
1693 1692
 						false, $cookie);
1694
-					if(!$pagedSearchOK) {
1693
+					if (!$pagedSearchOK) {
1695 1694
 						return false;
1696 1695
 					}
1697 1696
 					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
@@ -1708,7 +1707,7 @@  discard block
 block discarded – undo
1708 1707
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1709 1708
 		 * if we don't have a previous paged search.
1710 1709
 		 */
1711
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1710
+		} else if ($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1712 1711
 			// a search without limit was requested. However, if we do use
1713 1712
 			// Paged Search once, we always must do it. This requires us to
1714 1713
 			// initialize it with the configured page size.
Please login to merge, or discard this patch.
apps/user_ldap/lib/Wizard.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1104,7 +1104,7 @@
 block discarded – undo
1104 1104
 	}
1105 1105
 
1106 1106
 	/**
1107
-	 * @param array $reqs
1107
+	 * @param string[] $reqs
1108 1108
 	 * @return bool
1109 1109
 	 */
1110 1110
 	private function checkRequirements($reqs) {
Please login to merge, or discard this patch.
Indentation   +1317 added lines, -1317 removed lines patch added patch discarded remove patch
@@ -37,1323 +37,1323 @@
 block discarded – undo
37 37
 use OC\ServerNotAvailableException;
38 38
 
39 39
 class Wizard extends LDAPUtility {
40
-	/** @var \OCP\IL10N */
41
-	static protected $l;
42
-	protected $access;
43
-	protected $cr;
44
-	protected $configuration;
45
-	protected $result;
46
-	protected $resultCache = array();
47
-
48
-	const LRESULT_PROCESSED_OK = 2;
49
-	const LRESULT_PROCESSED_INVALID = 3;
50
-	const LRESULT_PROCESSED_SKIP = 4;
51
-
52
-	const LFILTER_LOGIN      = 2;
53
-	const LFILTER_USER_LIST  = 3;
54
-	const LFILTER_GROUP_LIST = 4;
55
-
56
-	const LFILTER_MODE_ASSISTED = 2;
57
-	const LFILTER_MODE_RAW = 1;
58
-
59
-	const LDAP_NW_TIMEOUT = 4;
60
-
61
-	/**
62
-	 * Constructor
63
-	 * @param Configuration $configuration an instance of Configuration
64
-	 * @param ILDAPWrapper $ldap an instance of ILDAPWrapper
65
-	 * @param Access $access
66
-	 */
67
-	public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
68
-		parent::__construct($ldap);
69
-		$this->configuration = $configuration;
70
-		if(is_null(Wizard::$l)) {
71
-			Wizard::$l = \OC::$server->getL10N('user_ldap');
72
-		}
73
-		$this->access = $access;
74
-		$this->result = new WizardResult();
75
-	}
76
-
77
-	public function  __destruct() {
78
-		if($this->result->hasChanges()) {
79
-			$this->configuration->saveConfiguration();
80
-		}
81
-	}
82
-
83
-	/**
84
-	 * counts entries in the LDAP directory
85
-	 *
86
-	 * @param string $filter the LDAP search filter
87
-	 * @param string $type a string being either 'users' or 'groups';
88
-	 * @return bool|int
89
-	 * @throws \Exception
90
-	 */
91
-	public function countEntries($filter, $type) {
92
-		$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
93
-		if($type === 'users') {
94
-			$reqs[] = 'ldapUserFilter';
95
-		}
96
-		if(!$this->checkRequirements($reqs)) {
97
-			throw new \Exception('Requirements not met', 400);
98
-		}
99
-
100
-		$attr = array('dn'); // default
101
-		$limit = 1001;
102
-		if($type === 'groups') {
103
-			$result =  $this->access->countGroups($filter, $attr, $limit);
104
-		} else if($type === 'users') {
105
-			$result = $this->access->countUsers($filter, $attr, $limit);
106
-		} else if ($type === 'objects') {
107
-			$result = $this->access->countObjects($limit);
108
-		} else {
109
-			throw new \Exception('internal error: invalid object type', 500);
110
-		}
111
-
112
-		return $result;
113
-	}
114
-
115
-	/**
116
-	 * formats the return value of a count operation to the string to be
117
-	 * inserted.
118
-	 *
119
-	 * @param bool|int $count
120
-	 * @return int|string
121
-	 */
122
-	private function formatCountResult($count) {
123
-		$formatted = ($count !== false) ? $count : 0;
124
-		if($formatted > 1000) {
125
-			$formatted = '> 1000';
126
-		}
127
-		return $formatted;
128
-	}
129
-
130
-	public function countGroups() {
131
-		$filter = $this->configuration->ldapGroupFilter;
132
-
133
-		if(empty($filter)) {
134
-			$output = self::$l->n('%s group found', '%s groups found', 0, array(0));
135
-			$this->result->addChange('ldap_group_count', $output);
136
-			return $this->result;
137
-		}
138
-
139
-		try {
140
-			$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
141
-		} catch (\Exception $e) {
142
-			//400 can be ignored, 500 is forwarded
143
-			if($e->getCode() === 500) {
144
-				throw $e;
145
-			}
146
-			return false;
147
-		}
148
-		$output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
149
-		$this->result->addChange('ldap_group_count', $output);
150
-		return $this->result;
151
-	}
152
-
153
-	/**
154
-	 * @return WizardResult
155
-	 * @throws \Exception
156
-	 */
157
-	public function countUsers() {
158
-		$filter = $this->access->getFilterForUserCount();
159
-
160
-		$usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
161
-		$output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
162
-		$this->result->addChange('ldap_user_count', $output);
163
-		return $this->result;
164
-	}
165
-
166
-	/**
167
-	 * counts any objects in the currently set base dn
168
-	 *
169
-	 * @return WizardResult
170
-	 * @throws \Exception
171
-	 */
172
-	public function countInBaseDN() {
173
-		// we don't need to provide a filter in this case
174
-		$total = $this->countEntries(null, 'objects');
175
-		if($total === false) {
176
-			throw new \Exception('invalid results received');
177
-		}
178
-		$this->result->addChange('ldap_test_base', $total);
179
-		return $this->result;
180
-	}
181
-
182
-	/**
183
-	 * counts users with a specified attribute
184
-	 * @param string $attr
185
-	 * @param bool $existsCheck
186
-	 * @return int|bool
187
-	 */
188
-	public function countUsersWithAttribute($attr, $existsCheck = false) {
189
-		if(!$this->checkRequirements(array('ldapHost',
190
-										   'ldapPort',
191
-										   'ldapBase',
192
-										   'ldapUserFilter',
193
-										   ))) {
194
-			return  false;
195
-		}
196
-
197
-		$filter = $this->access->combineFilterWithAnd(array(
198
-			$this->configuration->ldapUserFilter,
199
-			$attr . '=*'
200
-		));
201
-
202
-		$limit = ($existsCheck === false) ? null : 1;
203
-
204
-		return $this->access->countUsers($filter, array('dn'), $limit);
205
-	}
206
-
207
-	/**
208
-	 * detects the display name attribute. If a setting is already present that
209
-	 * returns at least one hit, the detection will be canceled.
210
-	 * @return WizardResult|bool
211
-	 * @throws \Exception
212
-	 */
213
-	public function detectUserDisplayNameAttribute() {
214
-		if(!$this->checkRequirements(array('ldapHost',
215
-										'ldapPort',
216
-										'ldapBase',
217
-										'ldapUserFilter',
218
-										))) {
219
-			return  false;
220
-		}
221
-
222
-		$attr = $this->configuration->ldapUserDisplayName;
223
-		if($attr !== 'displayName' && !empty($attr)) {
224
-			// most likely not the default value with upper case N,
225
-			// verify it still produces a result
226
-			$count = intval($this->countUsersWithAttribute($attr, true));
227
-			if($count > 0) {
228
-				//no change, but we sent it back to make sure the user interface
229
-				//is still correct, even if the ajax call was cancelled meanwhile
230
-				$this->result->addChange('ldap_display_name', $attr);
231
-				return $this->result;
232
-			}
233
-		}
234
-
235
-		// first attribute that has at least one result wins
236
-		$displayNameAttrs = array('displayname', 'cn');
237
-		foreach ($displayNameAttrs as $attr) {
238
-			$count = intval($this->countUsersWithAttribute($attr, true));
239
-
240
-			if($count > 0) {
241
-				$this->applyFind('ldap_display_name', $attr);
242
-				return $this->result;
243
-			}
244
-		};
245
-
246
-		throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced ldap settings.'));
247
-	}
248
-
249
-	/**
250
-	 * detects the most often used email attribute for users applying to the
251
-	 * user list filter. If a setting is already present that returns at least
252
-	 * one hit, the detection will be canceled.
253
-	 * @return WizardResult|bool
254
-	 */
255
-	public function detectEmailAttribute() {
256
-		if(!$this->checkRequirements(array('ldapHost',
257
-										   'ldapPort',
258
-										   'ldapBase',
259
-										   'ldapUserFilter',
260
-										   ))) {
261
-			return  false;
262
-		}
263
-
264
-		$attr = $this->configuration->ldapEmailAttribute;
265
-		if(!empty($attr)) {
266
-			$count = intval($this->countUsersWithAttribute($attr, true));
267
-			if($count > 0) {
268
-				return false;
269
-			}
270
-			$writeLog = true;
271
-		} else {
272
-			$writeLog = false;
273
-		}
274
-
275
-		$emailAttributes = array('mail', 'mailPrimaryAddress');
276
-		$winner = '';
277
-		$maxUsers = 0;
278
-		foreach($emailAttributes as $attr) {
279
-			$count = $this->countUsersWithAttribute($attr);
280
-			if($count > $maxUsers) {
281
-				$maxUsers = $count;
282
-				$winner = $attr;
283
-			}
284
-		}
285
-
286
-		if($winner !== '') {
287
-			$this->applyFind('ldap_email_attr', $winner);
288
-			if($writeLog) {
289
-				\OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
290
-					'automatically been reset, because the original value ' .
291
-					'did not return any results.', \OCP\Util::INFO);
292
-			}
293
-		}
294
-
295
-		return $this->result;
296
-	}
297
-
298
-	/**
299
-	 * @return WizardResult
300
-	 * @throws \Exception
301
-	 */
302
-	public function determineAttributes() {
303
-		if(!$this->checkRequirements(array('ldapHost',
304
-										   'ldapPort',
305
-										   'ldapBase',
306
-										   'ldapUserFilter',
307
-										   ))) {
308
-			return  false;
309
-		}
310
-
311
-		$attributes = $this->getUserAttributes();
312
-
313
-		natcasesort($attributes);
314
-		$attributes = array_values($attributes);
315
-
316
-		$this->result->addOptions('ldap_loginfilter_attributes', $attributes);
317
-
318
-		$selected = $this->configuration->ldapLoginFilterAttributes;
319
-		if(is_array($selected) && !empty($selected)) {
320
-			$this->result->addChange('ldap_loginfilter_attributes', $selected);
321
-		}
322
-
323
-		return $this->result;
324
-	}
325
-
326
-	/**
327
-	 * detects the available LDAP attributes
328
-	 * @return array|false The instance's WizardResult instance
329
-	 * @throws \Exception
330
-	 */
331
-	private function getUserAttributes() {
332
-		if(!$this->checkRequirements(array('ldapHost',
333
-										   'ldapPort',
334
-										   'ldapBase',
335
-										   'ldapUserFilter',
336
-										   ))) {
337
-			return  false;
338
-		}
339
-		$cr = $this->getConnection();
340
-		if(!$cr) {
341
-			throw new \Exception('Could not connect to LDAP');
342
-		}
343
-
344
-		$base = $this->configuration->ldapBase[0];
345
-		$filter = $this->configuration->ldapUserFilter;
346
-		$rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
347
-		if(!$this->ldap->isResource($rr)) {
348
-			return false;
349
-		}
350
-		$er = $this->ldap->firstEntry($cr, $rr);
351
-		$attributes = $this->ldap->getAttributes($cr, $er);
352
-		$pureAttributes = array();
353
-		for($i = 0; $i < $attributes['count']; $i++) {
354
-			$pureAttributes[] = $attributes[$i];
355
-		}
356
-
357
-		return $pureAttributes;
358
-	}
359
-
360
-	/**
361
-	 * detects the available LDAP groups
362
-	 * @return WizardResult|false the instance's WizardResult instance
363
-	 */
364
-	public function determineGroupsForGroups() {
365
-		return $this->determineGroups('ldap_groupfilter_groups',
366
-									  'ldapGroupFilterGroups',
367
-									  false);
368
-	}
369
-
370
-	/**
371
-	 * detects the available LDAP groups
372
-	 * @return WizardResult|false the instance's WizardResult instance
373
-	 */
374
-	public function determineGroupsForUsers() {
375
-		return $this->determineGroups('ldap_userfilter_groups',
376
-									  'ldapUserFilterGroups');
377
-	}
378
-
379
-	/**
380
-	 * detects the available LDAP groups
381
-	 * @param string $dbKey
382
-	 * @param string $confKey
383
-	 * @param bool $testMemberOf
384
-	 * @return WizardResult|false the instance's WizardResult instance
385
-	 * @throws \Exception
386
-	 */
387
-	private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
388
-		if(!$this->checkRequirements(array('ldapHost',
389
-										   'ldapPort',
390
-										   'ldapBase',
391
-										   ))) {
392
-			return  false;
393
-		}
394
-		$cr = $this->getConnection();
395
-		if(!$cr) {
396
-			throw new \Exception('Could not connect to LDAP');
397
-		}
398
-
399
-		$this->fetchGroups($dbKey, $confKey);
400
-
401
-		if($testMemberOf) {
402
-			$this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
403
-			$this->result->markChange();
404
-			if(!$this->configuration->hasMemberOfFilterSupport) {
405
-				throw new \Exception('memberOf is not supported by the server');
406
-			}
407
-		}
408
-
409
-		return $this->result;
410
-	}
411
-
412
-	/**
413
-	 * fetches all groups from LDAP and adds them to the result object
414
-	 *
415
-	 * @param string $dbKey
416
-	 * @param string $confKey
417
-	 * @return array $groupEntries
418
-	 * @throws \Exception
419
-	 */
420
-	public function fetchGroups($dbKey, $confKey) {
421
-		$obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames');
422
-
423
-		$filterParts = array();
424
-		foreach($obclasses as $obclass) {
425
-			$filterParts[] = 'objectclass='.$obclass;
426
-		}
427
-		//we filter for everything
428
-		//- that looks like a group and
429
-		//- has the group display name set
430
-		$filter = $this->access->combineFilterWithOr($filterParts);
431
-		$filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*'));
432
-
433
-		$groupNames = array();
434
-		$groupEntries = array();
435
-		$limit = 400;
436
-		$offset = 0;
437
-		do {
438
-			// we need to request dn additionally here, otherwise memberOf
439
-			// detection will fail later
440
-			$result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
441
-			foreach($result as $item) {
442
-				if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
443
-					// just in case - no issue known
444
-					continue;
445
-				}
446
-				$groupNames[] = $item['cn'][0];
447
-				$groupEntries[] = $item;
448
-			}
449
-			$offset += $limit;
450
-		} while ($this->access->hasMoreResults());
451
-
452
-		if(count($groupNames) > 0) {
453
-			natsort($groupNames);
454
-			$this->result->addOptions($dbKey, array_values($groupNames));
455
-		} else {
456
-			throw new \Exception(self::$l->t('Could not find the desired feature'));
457
-		}
458
-
459
-		$setFeatures = $this->configuration->$confKey;
460
-		if(is_array($setFeatures) && !empty($setFeatures)) {
461
-			//something is already configured? pre-select it.
462
-			$this->result->addChange($dbKey, $setFeatures);
463
-		}
464
-		return $groupEntries;
465
-	}
466
-
467
-	public function determineGroupMemberAssoc() {
468
-		if(!$this->checkRequirements(array('ldapHost',
469
-										   'ldapPort',
470
-										   'ldapGroupFilter',
471
-										   ))) {
472
-			return  false;
473
-		}
474
-		$attribute = $this->detectGroupMemberAssoc();
475
-		if($attribute === false) {
476
-			return false;
477
-		}
478
-		$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
479
-		$this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
480
-
481
-		return $this->result;
482
-	}
483
-
484
-	/**
485
-	 * Detects the available object classes
486
-	 * @return WizardResult|false the instance's WizardResult instance
487
-	 * @throws \Exception
488
-	 */
489
-	public function determineGroupObjectClasses() {
490
-		if(!$this->checkRequirements(array('ldapHost',
491
-										   'ldapPort',
492
-										   'ldapBase',
493
-										   ))) {
494
-			return  false;
495
-		}
496
-		$cr = $this->getConnection();
497
-		if(!$cr) {
498
-			throw new \Exception('Could not connect to LDAP');
499
-		}
500
-
501
-		$obclasses = array('groupOfNames', 'group', 'posixGroup', '*');
502
-		$this->determineFeature($obclasses,
503
-								'objectclass',
504
-								'ldap_groupfilter_objectclass',
505
-								'ldapGroupFilterObjectclass',
506
-								false);
507
-
508
-		return $this->result;
509
-	}
510
-
511
-	/**
512
-	 * detects the available object classes
513
-	 * @return WizardResult
514
-	 * @throws \Exception
515
-	 */
516
-	public function determineUserObjectClasses() {
517
-		if(!$this->checkRequirements(array('ldapHost',
518
-										   'ldapPort',
519
-										   'ldapBase',
520
-										   ))) {
521
-			return  false;
522
-		}
523
-		$cr = $this->getConnection();
524
-		if(!$cr) {
525
-			throw new \Exception('Could not connect to LDAP');
526
-		}
527
-
528
-		$obclasses = array('inetOrgPerson', 'person', 'organizationalPerson',
529
-						   'user', 'posixAccount', '*');
530
-		$filter = $this->configuration->ldapUserFilter;
531
-		//if filter is empty, it is probably the first time the wizard is called
532
-		//then, apply suggestions.
533
-		$this->determineFeature($obclasses,
534
-								'objectclass',
535
-								'ldap_userfilter_objectclass',
536
-								'ldapUserFilterObjectclass',
537
-								empty($filter));
538
-
539
-		return $this->result;
540
-	}
541
-
542
-	/**
543
-	 * @return WizardResult|false
544
-	 * @throws \Exception
545
-	 */
546
-	public function getGroupFilter() {
547
-		if(!$this->checkRequirements(array('ldapHost',
548
-										   'ldapPort',
549
-										   'ldapBase',
550
-										   ))) {
551
-			return false;
552
-		}
553
-		//make sure the use display name is set
554
-		$displayName = $this->configuration->ldapGroupDisplayName;
555
-		if(empty($displayName)) {
556
-			$d = $this->configuration->getDefaults();
557
-			$this->applyFind('ldap_group_display_name',
558
-							 $d['ldap_group_display_name']);
559
-		}
560
-		$filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST);
561
-
562
-		$this->applyFind('ldap_group_filter', $filter);
563
-		return $this->result;
564
-	}
565
-
566
-	/**
567
-	 * @return WizardResult|false
568
-	 * @throws \Exception
569
-	 */
570
-	public function getUserListFilter() {
571
-		if(!$this->checkRequirements(array('ldapHost',
572
-										   'ldapPort',
573
-										   'ldapBase',
574
-										   ))) {
575
-			return false;
576
-		}
577
-		//make sure the use display name is set
578
-		$displayName = $this->configuration->ldapUserDisplayName;
579
-		if(empty($displayName)) {
580
-			$d = $this->configuration->getDefaults();
581
-			$this->applyFind('ldap_display_name', $d['ldap_display_name']);
582
-		}
583
-		$filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
584
-		if(!$filter) {
585
-			throw new \Exception('Cannot create filter');
586
-		}
587
-
588
-		$this->applyFind('ldap_userlist_filter', $filter);
589
-		return $this->result;
590
-	}
591
-
592
-	/**
593
-	 * @return bool|WizardResult
594
-	 * @throws \Exception
595
-	 */
596
-	public function getUserLoginFilter() {
597
-		if(!$this->checkRequirements(array('ldapHost',
598
-										   'ldapPort',
599
-										   'ldapBase',
600
-										   'ldapUserFilter',
601
-										   ))) {
602
-			return false;
603
-		}
604
-
605
-		$filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
606
-		if(!$filter) {
607
-			throw new \Exception('Cannot create filter');
608
-		}
609
-
610
-		$this->applyFind('ldap_login_filter', $filter);
611
-		return $this->result;
612
-	}
613
-
614
-	/**
615
-	 * @return bool|WizardResult
616
-	 * @param string $loginName
617
-	 * @throws \Exception
618
-	 */
619
-	public function testLoginName($loginName) {
620
-		if(!$this->checkRequirements(array('ldapHost',
621
-			'ldapPort',
622
-			'ldapBase',
623
-			'ldapLoginFilter',
624
-		))) {
625
-			return false;
626
-		}
627
-
628
-		$cr = $this->access->connection->getConnectionResource();
629
-		if(!$this->ldap->isResource($cr)) {
630
-			throw new \Exception('connection error');
631
-		}
632
-
633
-		if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
634
-			=== false) {
635
-			throw new \Exception('missing placeholder');
636
-		}
637
-
638
-		$users = $this->access->countUsersByLoginName($loginName);
639
-		if($this->ldap->errno($cr) !== 0) {
640
-			throw new \Exception($this->ldap->error($cr));
641
-		}
642
-		$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
643
-		$this->result->addChange('ldap_test_loginname', $users);
644
-		$this->result->addChange('ldap_test_effective_filter', $filter);
645
-		return $this->result;
646
-	}
647
-
648
-	/**
649
-	 * Tries to determine the port, requires given Host, User DN and Password
650
-	 * @return WizardResult|false WizardResult on success, false otherwise
651
-	 * @throws \Exception
652
-	 */
653
-	public function guessPortAndTLS() {
654
-		if(!$this->checkRequirements(array('ldapHost',
655
-										   ))) {
656
-			return false;
657
-		}
658
-		$this->checkHost();
659
-		$portSettings = $this->getPortSettingsToTry();
660
-
661
-		if(!is_array($portSettings)) {
662
-			throw new \Exception(print_r($portSettings, true));
663
-		}
664
-
665
-		//proceed from the best configuration and return on first success
666
-		foreach($portSettings as $setting) {
667
-			$p = $setting['port'];
668
-			$t = $setting['tls'];
669
-			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
670
-			//connectAndBind may throw Exception, it needs to be catched by the
671
-			//callee of this method
672
-
673
-			try {
674
-				$settingsFound = $this->connectAndBind($p, $t);
675
-			} catch (\Exception $e) {
676
-				// any reply other than -1 (= cannot connect) is already okay,
677
-				// because then we found the server
678
-				// unavailable startTLS returns -11
679
-				if($e->getCode() > 0) {
680
-					$settingsFound = true;
681
-				} else {
682
-					throw $e;
683
-				}
684
-			}
685
-
686
-			if ($settingsFound === true) {
687
-				$config = array(
688
-					'ldapPort' => $p,
689
-					'ldapTLS' => intval($t)
690
-				);
691
-				$this->configuration->setConfiguration($config);
692
-				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
693
-				$this->result->addChange('ldap_port', $p);
694
-				return $this->result;
695
-			}
696
-		}
697
-
698
-		//custom port, undetected (we do not brute force)
699
-		return false;
700
-	}
701
-
702
-	/**
703
-	 * tries to determine a base dn from User DN or LDAP Host
704
-	 * @return WizardResult|false WizardResult on success, false otherwise
705
-	 */
706
-	public function guessBaseDN() {
707
-		if(!$this->checkRequirements(array('ldapHost',
708
-										   'ldapPort',
709
-										   ))) {
710
-			return false;
711
-		}
712
-
713
-		//check whether a DN is given in the agent name (99.9% of all cases)
714
-		$base = null;
715
-		$i = stripos($this->configuration->ldapAgentName, 'dc=');
716
-		if($i !== false) {
717
-			$base = substr($this->configuration->ldapAgentName, $i);
718
-			if($this->testBaseDN($base)) {
719
-				$this->applyFind('ldap_base', $base);
720
-				return $this->result;
721
-			}
722
-		}
723
-
724
-		//this did not help :(
725
-		//Let's see whether we can parse the Host URL and convert the domain to
726
-		//a base DN
727
-		$helper = new Helper();
728
-		$domain = $helper->getDomainFromURL($this->configuration->ldapHost);
729
-		if(!$domain) {
730
-			return false;
731
-		}
732
-
733
-		$dparts = explode('.', $domain);
734
-		while(count($dparts) > 0) {
735
-			$base2 = 'dc=' . implode(',dc=', $dparts);
736
-			if ($base !== $base2 && $this->testBaseDN($base2)) {
737
-				$this->applyFind('ldap_base', $base2);
738
-				return $this->result;
739
-			}
740
-			array_shift($dparts);
741
-		}
742
-
743
-		return false;
744
-	}
745
-
746
-	/**
747
-	 * sets the found value for the configuration key in the WizardResult
748
-	 * as well as in the Configuration instance
749
-	 * @param string $key the configuration key
750
-	 * @param string $value the (detected) value
751
-	 *
752
-	 */
753
-	private function applyFind($key, $value) {
754
-		$this->result->addChange($key, $value);
755
-		$this->configuration->setConfiguration(array($key => $value));
756
-	}
757
-
758
-	/**
759
-	 * Checks, whether a port was entered in the Host configuration
760
-	 * field. In this case the port will be stripped off, but also stored as
761
-	 * setting.
762
-	 */
763
-	private function checkHost() {
764
-		$host = $this->configuration->ldapHost;
765
-		$hostInfo = parse_url($host);
766
-
767
-		//removes Port from Host
768
-		if(is_array($hostInfo) && isset($hostInfo['port'])) {
769
-			$port = $hostInfo['port'];
770
-			$host = str_replace(':'.$port, '', $host);
771
-			$this->applyFind('ldap_host', $host);
772
-			$this->applyFind('ldap_port', $port);
773
-		}
774
-	}
775
-
776
-	/**
777
-	 * tries to detect the group member association attribute which is
778
-	 * one of 'uniqueMember', 'memberUid', 'member'
779
-	 * @return string|false, string with the attribute name, false on error
780
-	 * @throws \Exception
781
-	 */
782
-	private function detectGroupMemberAssoc() {
783
-		$possibleAttrs = array('uniqueMember', 'memberUid', 'member');
784
-		$filter = $this->configuration->ldapGroupFilter;
785
-		if(empty($filter)) {
786
-			return false;
787
-		}
788
-		$cr = $this->getConnection();
789
-		if(!$cr) {
790
-			throw new \Exception('Could not connect to LDAP');
791
-		}
792
-		$base = $this->configuration->ldapBase[0];
793
-		$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
794
-		if(!$this->ldap->isResource($rr)) {
795
-			return false;
796
-		}
797
-		$er = $this->ldap->firstEntry($cr, $rr);
798
-		while(is_resource($er)) {
799
-			$this->ldap->getDN($cr, $er);
800
-			$attrs = $this->ldap->getAttributes($cr, $er);
801
-			$result = array();
802
-			$possibleAttrsCount = count($possibleAttrs);
803
-			for($i = 0; $i < $possibleAttrsCount; $i++) {
804
-				if(isset($attrs[$possibleAttrs[$i]])) {
805
-					$result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
806
-				}
807
-			}
808
-			if(!empty($result)) {
809
-				natsort($result);
810
-				return key($result);
811
-			}
812
-
813
-			$er = $this->ldap->nextEntry($cr, $er);
814
-		}
815
-
816
-		return false;
817
-	}
818
-
819
-	/**
820
-	 * Checks whether for a given BaseDN results will be returned
821
-	 * @param string $base the BaseDN to test
822
-	 * @return bool true on success, false otherwise
823
-	 * @throws \Exception
824
-	 */
825
-	private function testBaseDN($base) {
826
-		$cr = $this->getConnection();
827
-		if(!$cr) {
828
-			throw new \Exception('Could not connect to LDAP');
829
-		}
830
-
831
-		//base is there, let's validate it. If we search for anything, we should
832
-		//get a result set > 0 on a proper base
833
-		$rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
834
-		if(!$this->ldap->isResource($rr)) {
835
-			$errorNo  = $this->ldap->errno($cr);
836
-			$errorMsg = $this->ldap->error($cr);
837
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
838
-							' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO);
839
-			return false;
840
-		}
841
-		$entries = $this->ldap->countEntries($cr, $rr);
842
-		return ($entries !== false) && ($entries > 0);
843
-	}
844
-
845
-	/**
846
-	 * Checks whether the server supports memberOf in LDAP Filter.
847
-	 * Note: at least in OpenLDAP, availability of memberOf is dependent on
848
-	 * a configured objectClass. I.e. not necessarily for all available groups
849
-	 * memberOf does work.
850
-	 *
851
-	 * @return bool true if it does, false otherwise
852
-	 * @throws \Exception
853
-	 */
854
-	private function testMemberOf() {
855
-		$cr = $this->getConnection();
856
-		if(!$cr) {
857
-			throw new \Exception('Could not connect to LDAP');
858
-		}
859
-		$result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
860
-		if(is_int($result) &&  $result > 0) {
861
-			return true;
862
-		}
863
-		return false;
864
-	}
865
-
866
-	/**
867
-	 * creates an LDAP Filter from given configuration
868
-	 * @param integer $filterType int, for which use case the filter shall be created
869
-	 * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or
870
-	 * self::LFILTER_GROUP_LIST
871
-	 * @return string|false string with the filter on success, false otherwise
872
-	 * @throws \Exception
873
-	 */
874
-	private function composeLdapFilter($filterType) {
875
-		$filter = '';
876
-		$parts = 0;
877
-		switch ($filterType) {
878
-			case self::LFILTER_USER_LIST:
879
-				$objcs = $this->configuration->ldapUserFilterObjectclass;
880
-				//glue objectclasses
881
-				if(is_array($objcs) && count($objcs) > 0) {
882
-					$filter .= '(|';
883
-					foreach($objcs as $objc) {
884
-						$filter .= '(objectclass=' . $objc . ')';
885
-					}
886
-					$filter .= ')';
887
-					$parts++;
888
-				}
889
-				//glue group memberships
890
-				if($this->configuration->hasMemberOfFilterSupport) {
891
-					$cns = $this->configuration->ldapUserFilterGroups;
892
-					if(is_array($cns) && count($cns) > 0) {
893
-						$filter .= '(|';
894
-						$cr = $this->getConnection();
895
-						if(!$cr) {
896
-							throw new \Exception('Could not connect to LDAP');
897
-						}
898
-						$base = $this->configuration->ldapBase[0];
899
-						foreach($cns as $cn) {
900
-							$rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
901
-							if(!$this->ldap->isResource($rr)) {
902
-								continue;
903
-							}
904
-							$er = $this->ldap->firstEntry($cr, $rr);
905
-							$attrs = $this->ldap->getAttributes($cr, $er);
906
-							$dn = $this->ldap->getDN($cr, $er);
907
-							if(empty($dn)) {
908
-								continue;
909
-							}
910
-							$filterPart = '(memberof=' . $dn . ')';
911
-							if(isset($attrs['primaryGroupToken'])) {
912
-								$pgt = $attrs['primaryGroupToken'][0];
913
-								$primaryFilterPart = '(primaryGroupID=' . $pgt .')';
914
-								$filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
915
-							}
916
-							$filter .= $filterPart;
917
-						}
918
-						$filter .= ')';
919
-					}
920
-					$parts++;
921
-				}
922
-				//wrap parts in AND condition
923
-				if($parts > 1) {
924
-					$filter = '(&' . $filter . ')';
925
-				}
926
-				if(empty($filter)) {
927
-					$filter = '(objectclass=*)';
928
-				}
929
-				break;
930
-
931
-			case self::LFILTER_GROUP_LIST:
932
-				$objcs = $this->configuration->ldapGroupFilterObjectclass;
933
-				//glue objectclasses
934
-				if(is_array($objcs) && count($objcs) > 0) {
935
-					$filter .= '(|';
936
-					foreach($objcs as $objc) {
937
-						$filter .= '(objectclass=' . $objc . ')';
938
-					}
939
-					$filter .= ')';
940
-					$parts++;
941
-				}
942
-				//glue group memberships
943
-				$cns = $this->configuration->ldapGroupFilterGroups;
944
-				if(is_array($cns) && count($cns) > 0) {
945
-					$filter .= '(|';
946
-					$base = $this->configuration->ldapBase[0];
947
-					foreach($cns as $cn) {
948
-						$filter .= '(cn=' . $cn . ')';
949
-					}
950
-					$filter .= ')';
951
-				}
952
-				$parts++;
953
-				//wrap parts in AND condition
954
-				if($parts > 1) {
955
-					$filter = '(&' . $filter . ')';
956
-				}
957
-				break;
958
-
959
-			case self::LFILTER_LOGIN:
960
-				$ulf = $this->configuration->ldapUserFilter;
961
-				$loginpart = '=%uid';
962
-				$filterUsername = '';
963
-				$userAttributes = $this->getUserAttributes();
964
-				$userAttributes = array_change_key_case(array_flip($userAttributes));
965
-				$parts = 0;
966
-
967
-				if($this->configuration->ldapLoginFilterUsername === '1') {
968
-					$attr = '';
969
-					if(isset($userAttributes['uid'])) {
970
-						$attr = 'uid';
971
-					} else if(isset($userAttributes['samaccountname'])) {
972
-						$attr = 'samaccountname';
973
-					} else if(isset($userAttributes['cn'])) {
974
-						//fallback
975
-						$attr = 'cn';
976
-					}
977
-					if(!empty($attr)) {
978
-						$filterUsername = '(' . $attr . $loginpart . ')';
979
-						$parts++;
980
-					}
981
-				}
982
-
983
-				$filterEmail = '';
984
-				if($this->configuration->ldapLoginFilterEmail === '1') {
985
-					$filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
986
-					$parts++;
987
-				}
988
-
989
-				$filterAttributes = '';
990
-				$attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
991
-				if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
992
-					$filterAttributes = '(|';
993
-					foreach($attrsToFilter as $attribute) {
994
-						$filterAttributes .= '(' . $attribute . $loginpart . ')';
995
-					}
996
-					$filterAttributes .= ')';
997
-					$parts++;
998
-				}
999
-
1000
-				$filterLogin = '';
1001
-				if($parts > 1) {
1002
-					$filterLogin = '(|';
1003
-				}
1004
-				$filterLogin .= $filterUsername;
1005
-				$filterLogin .= $filterEmail;
1006
-				$filterLogin .= $filterAttributes;
1007
-				if($parts > 1) {
1008
-					$filterLogin .= ')';
1009
-				}
1010
-
1011
-				$filter = '(&'.$ulf.$filterLogin.')';
1012
-				break;
1013
-		}
1014
-
1015
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG);
1016
-
1017
-		return $filter;
1018
-	}
1019
-
1020
-	/**
1021
-	 * Connects and Binds to an LDAP Server
1022
-	 * @param int $port the port to connect with
1023
-	 * @param bool $tls whether startTLS is to be used
1024
-	 * @param bool $ncc
1025
-	 * @return bool
1026
-	 * @throws \Exception
1027
-	 */
1028
-	private function connectAndBind($port = 389, $tls = false, $ncc = false) {
1029
-		if($ncc) {
1030
-			//No certificate check
1031
-			//FIXME: undo afterwards
1032
-			putenv('LDAPTLS_REQCERT=never');
1033
-		}
1034
-
1035
-		//connect, does not really trigger any server communication
1036
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG);
1037
-		$host = $this->configuration->ldapHost;
1038
-		$hostInfo = parse_url($host);
1039
-		if(!$hostInfo) {
1040
-			throw new \Exception(self::$l->t('Invalid Host'));
1041
-		}
1042
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1043
-		$cr = $this->ldap->connect($host, $port);
1044
-		if(!is_resource($cr)) {
1045
-			throw new \Exception(self::$l->t('Invalid Host'));
1046
-		}
1047
-
1048
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG);
1049
-		//set LDAP options
1050
-		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1051
-		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1052
-		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1053
-
1054
-		try {
1055
-			if($tls) {
1056
-				$isTlsWorking = @$this->ldap->startTls($cr);
1057
-				if(!$isTlsWorking) {
1058
-					return false;
1059
-				}
1060
-			}
1061
-
1062
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG);
1063
-			//interesting part: do the bind!
1064
-			$login = $this->ldap->bind($cr,
1065
-				$this->configuration->ldapAgentName,
1066
-				$this->configuration->ldapAgentPassword
1067
-			);
1068
-			$errNo = $this->ldap->errno($cr);
1069
-			$error = ldap_error($cr);
1070
-			$this->ldap->unbind($cr);
1071
-		} catch(ServerNotAvailableException $e) {
1072
-			return false;
1073
-		}
1074
-
1075
-		if($login === true) {
1076
-			$this->ldap->unbind($cr);
1077
-			if($ncc) {
1078
-				throw new \Exception('Certificate cannot be validated.');
1079
-			}
1080
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1081
-			return true;
1082
-		}
1083
-
1084
-		if($errNo === -1 || ($errNo === 2 && $ncc)) {
1085
-			//host, port or TLS wrong
1086
-			return false;
1087
-		} else if ($errNo === 2) {
1088
-			return $this->connectAndBind($port, $tls, true);
1089
-		}
1090
-		throw new \Exception($error, $errNo);
1091
-	}
1092
-
1093
-	/**
1094
-	 * checks whether a valid combination of agent and password has been
1095
-	 * provided (either two values or nothing for anonymous connect)
1096
-	 * @return bool, true if everything is fine, false otherwise
1097
-	 */
1098
-	private function checkAgentRequirements() {
1099
-		$agent = $this->configuration->ldapAgentName;
1100
-		$pwd = $this->configuration->ldapAgentPassword;
1101
-
1102
-		return ( (!empty($agent) && !empty($pwd))
1103
-		       || (empty($agent) &&  empty($pwd)));
1104
-	}
1105
-
1106
-	/**
1107
-	 * @param array $reqs
1108
-	 * @return bool
1109
-	 */
1110
-	private function checkRequirements($reqs) {
1111
-		$this->checkAgentRequirements();
1112
-		foreach($reqs as $option) {
1113
-			$value = $this->configuration->$option;
1114
-			if(empty($value)) {
1115
-				return false;
1116
-			}
1117
-		}
1118
-		return true;
1119
-	}
1120
-
1121
-	/**
1122
-	 * does a cumulativeSearch on LDAP to get different values of a
1123
-	 * specified attribute
1124
-	 * @param string[] $filters array, the filters that shall be used in the search
1125
-	 * @param string $attr the attribute of which a list of values shall be returned
1126
-	 * @param int $dnReadLimit the amount of how many DNs should be analyzed.
1127
-	 * The lower, the faster
1128
-	 * @param string $maxF string. if not null, this variable will have the filter that
1129
-	 * yields most result entries
1130
-	 * @return array|false an array with the values on success, false otherwise
1131
-	 */
1132
-	public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) {
1133
-		$dnRead = array();
1134
-		$foundItems = array();
1135
-		$maxEntries = 0;
1136
-		if(!is_array($this->configuration->ldapBase)
1137
-		   || !isset($this->configuration->ldapBase[0])) {
1138
-			return false;
1139
-		}
1140
-		$base = $this->configuration->ldapBase[0];
1141
-		$cr = $this->getConnection();
1142
-		if(!$this->ldap->isResource($cr)) {
1143
-			return false;
1144
-		}
1145
-		$lastFilter = null;
1146
-		if(isset($filters[count($filters)-1])) {
1147
-			$lastFilter = $filters[count($filters)-1];
1148
-		}
1149
-		foreach($filters as $filter) {
1150
-			if($lastFilter === $filter && count($foundItems) > 0) {
1151
-				//skip when the filter is a wildcard and results were found
1152
-				continue;
1153
-			}
1154
-			// 20k limit for performance and reason
1155
-			$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1156
-			if(!$this->ldap->isResource($rr)) {
1157
-				continue;
1158
-			}
1159
-			$entries = $this->ldap->countEntries($cr, $rr);
1160
-			$getEntryFunc = 'firstEntry';
1161
-			if(($entries !== false) && ($entries > 0)) {
1162
-				if(!is_null($maxF) && $entries > $maxEntries) {
1163
-					$maxEntries = $entries;
1164
-					$maxF = $filter;
1165
-				}
1166
-				$dnReadCount = 0;
1167
-				do {
1168
-					$entry = $this->ldap->$getEntryFunc($cr, $rr);
1169
-					$getEntryFunc = 'nextEntry';
1170
-					if(!$this->ldap->isResource($entry)) {
1171
-						continue 2;
1172
-					}
1173
-					$rr = $entry; //will be expected by nextEntry next round
1174
-					$attributes = $this->ldap->getAttributes($cr, $entry);
1175
-					$dn = $this->ldap->getDN($cr, $entry);
1176
-					if($dn === false || in_array($dn, $dnRead)) {
1177
-						continue;
1178
-					}
1179
-					$newItems = array();
1180
-					$state = $this->getAttributeValuesFromEntry($attributes,
1181
-																$attr,
1182
-																$newItems);
1183
-					$dnReadCount++;
1184
-					$foundItems = array_merge($foundItems, $newItems);
1185
-					$this->resultCache[$dn][$attr] = $newItems;
1186
-					$dnRead[] = $dn;
1187
-				} while(($state === self::LRESULT_PROCESSED_SKIP
1188
-						|| $this->ldap->isResource($entry))
1189
-						&& ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1190
-			}
1191
-		}
1192
-
1193
-		return array_unique($foundItems);
1194
-	}
1195
-
1196
-	/**
1197
-	 * determines if and which $attr are available on the LDAP server
1198
-	 * @param string[] $objectclasses the objectclasses to use as search filter
1199
-	 * @param string $attr the attribute to look for
1200
-	 * @param string $dbkey the dbkey of the setting the feature is connected to
1201
-	 * @param string $confkey the confkey counterpart for the $dbkey as used in the
1202
-	 * Configuration class
1203
-	 * @param bool $po whether the objectClass with most result entries
1204
-	 * shall be pre-selected via the result
1205
-	 * @return array|false list of found items.
1206
-	 * @throws \Exception
1207
-	 */
1208
-	private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1209
-		$cr = $this->getConnection();
1210
-		if(!$cr) {
1211
-			throw new \Exception('Could not connect to LDAP');
1212
-		}
1213
-		$p = 'objectclass=';
1214
-		foreach($objectclasses as $key => $value) {
1215
-			$objectclasses[$key] = $p.$value;
1216
-		}
1217
-		$maxEntryObjC = '';
1218
-
1219
-		//how deep to dig?
1220
-		//When looking for objectclasses, testing few entries is sufficient,
1221
-		$dig = 3;
1222
-
1223
-		$availableFeatures =
1224
-			$this->cumulativeSearchOnAttribute($objectclasses, $attr,
1225
-											   $dig, $maxEntryObjC);
1226
-		if(is_array($availableFeatures)
1227
-		   && count($availableFeatures) > 0) {
1228
-			natcasesort($availableFeatures);
1229
-			//natcasesort keeps indices, but we must get rid of them for proper
1230
-			//sorting in the web UI. Therefore: array_values
1231
-			$this->result->addOptions($dbkey, array_values($availableFeatures));
1232
-		} else {
1233
-			throw new \Exception(self::$l->t('Could not find the desired feature'));
1234
-		}
1235
-
1236
-		$setFeatures = $this->configuration->$confkey;
1237
-		if(is_array($setFeatures) && !empty($setFeatures)) {
1238
-			//something is already configured? pre-select it.
1239
-			$this->result->addChange($dbkey, $setFeatures);
1240
-		} else if($po && !empty($maxEntryObjC)) {
1241
-			//pre-select objectclass with most result entries
1242
-			$maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1243
-			$this->applyFind($dbkey, $maxEntryObjC);
1244
-			$this->result->addChange($dbkey, $maxEntryObjC);
1245
-		}
1246
-
1247
-		return $availableFeatures;
1248
-	}
1249
-
1250
-	/**
1251
-	 * appends a list of values fr
1252
-	 * @param resource $result the return value from ldap_get_attributes
1253
-	 * @param string $attribute the attribute values to look for
1254
-	 * @param array &$known new values will be appended here
1255
-	 * @return int, state on of the class constants LRESULT_PROCESSED_OK,
1256
-	 * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1257
-	 */
1258
-	private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1259
-		if(!is_array($result)
1260
-		   || !isset($result['count'])
1261
-		   || !$result['count'] > 0) {
1262
-			return self::LRESULT_PROCESSED_INVALID;
1263
-		}
1264
-
1265
-		// strtolower on all keys for proper comparison
1266
-		$result = \OCP\Util::mb_array_change_key_case($result);
1267
-		$attribute = strtolower($attribute);
1268
-		if(isset($result[$attribute])) {
1269
-			foreach($result[$attribute] as $key => $val) {
1270
-				if($key === 'count') {
1271
-					continue;
1272
-				}
1273
-				if(!in_array($val, $known)) {
1274
-					$known[] = $val;
1275
-				}
1276
-			}
1277
-			return self::LRESULT_PROCESSED_OK;
1278
-		} else {
1279
-			return self::LRESULT_PROCESSED_SKIP;
1280
-		}
1281
-	}
1282
-
1283
-	/**
1284
-	 * @return bool|mixed
1285
-	 */
1286
-	private function getConnection() {
1287
-		if(!is_null($this->cr)) {
1288
-			return $this->cr;
1289
-		}
1290
-
1291
-		$cr = $this->ldap->connect(
1292
-			$this->configuration->ldapHost,
1293
-			$this->configuration->ldapPort
1294
-		);
1295
-
1296
-		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1297
-		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1298
-		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1299
-		if($this->configuration->ldapTLS === 1) {
1300
-			$this->ldap->startTls($cr);
1301
-		}
1302
-
1303
-		$lo = @$this->ldap->bind($cr,
1304
-								 $this->configuration->ldapAgentName,
1305
-								 $this->configuration->ldapAgentPassword);
1306
-		if($lo === true) {
1307
-			$this->$cr = $cr;
1308
-			return $cr;
1309
-		}
1310
-
1311
-		return false;
1312
-	}
1313
-
1314
-	/**
1315
-	 * @return array
1316
-	 */
1317
-	private function getDefaultLdapPortSettings() {
1318
-		static $settings = array(
1319
-								array('port' => 7636, 'tls' => false),
1320
-								array('port' =>  636, 'tls' => false),
1321
-								array('port' => 7389, 'tls' => true),
1322
-								array('port' =>  389, 'tls' => true),
1323
-								array('port' => 7389, 'tls' => false),
1324
-								array('port' =>  389, 'tls' => false),
1325
-						  );
1326
-		return $settings;
1327
-	}
1328
-
1329
-	/**
1330
-	 * @return array
1331
-	 */
1332
-	private function getPortSettingsToTry() {
1333
-		//389 ← LDAP / Unencrypted or StartTLS
1334
-		//636 ← LDAPS / SSL
1335
-		//7xxx ← UCS. need to be checked first, because both ports may be open
1336
-		$host = $this->configuration->ldapHost;
1337
-		$port = intval($this->configuration->ldapPort);
1338
-		$portSettings = array();
1339
-
1340
-		//In case the port is already provided, we will check this first
1341
-		if($port > 0) {
1342
-			$hostInfo = parse_url($host);
1343
-			if(!(is_array($hostInfo)
1344
-				&& isset($hostInfo['scheme'])
1345
-				&& stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1346
-				$portSettings[] = array('port' => $port, 'tls' => true);
1347
-			}
1348
-			$portSettings[] =array('port' => $port, 'tls' => false);
1349
-		}
1350
-
1351
-		//default ports
1352
-		$portSettings = array_merge($portSettings,
1353
-		                            $this->getDefaultLdapPortSettings());
1354
-
1355
-		return $portSettings;
1356
-	}
40
+    /** @var \OCP\IL10N */
41
+    static protected $l;
42
+    protected $access;
43
+    protected $cr;
44
+    protected $configuration;
45
+    protected $result;
46
+    protected $resultCache = array();
47
+
48
+    const LRESULT_PROCESSED_OK = 2;
49
+    const LRESULT_PROCESSED_INVALID = 3;
50
+    const LRESULT_PROCESSED_SKIP = 4;
51
+
52
+    const LFILTER_LOGIN      = 2;
53
+    const LFILTER_USER_LIST  = 3;
54
+    const LFILTER_GROUP_LIST = 4;
55
+
56
+    const LFILTER_MODE_ASSISTED = 2;
57
+    const LFILTER_MODE_RAW = 1;
58
+
59
+    const LDAP_NW_TIMEOUT = 4;
60
+
61
+    /**
62
+     * Constructor
63
+     * @param Configuration $configuration an instance of Configuration
64
+     * @param ILDAPWrapper $ldap an instance of ILDAPWrapper
65
+     * @param Access $access
66
+     */
67
+    public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
68
+        parent::__construct($ldap);
69
+        $this->configuration = $configuration;
70
+        if(is_null(Wizard::$l)) {
71
+            Wizard::$l = \OC::$server->getL10N('user_ldap');
72
+        }
73
+        $this->access = $access;
74
+        $this->result = new WizardResult();
75
+    }
76
+
77
+    public function  __destruct() {
78
+        if($this->result->hasChanges()) {
79
+            $this->configuration->saveConfiguration();
80
+        }
81
+    }
82
+
83
+    /**
84
+     * counts entries in the LDAP directory
85
+     *
86
+     * @param string $filter the LDAP search filter
87
+     * @param string $type a string being either 'users' or 'groups';
88
+     * @return bool|int
89
+     * @throws \Exception
90
+     */
91
+    public function countEntries($filter, $type) {
92
+        $reqs = array('ldapHost', 'ldapPort', 'ldapBase');
93
+        if($type === 'users') {
94
+            $reqs[] = 'ldapUserFilter';
95
+        }
96
+        if(!$this->checkRequirements($reqs)) {
97
+            throw new \Exception('Requirements not met', 400);
98
+        }
99
+
100
+        $attr = array('dn'); // default
101
+        $limit = 1001;
102
+        if($type === 'groups') {
103
+            $result =  $this->access->countGroups($filter, $attr, $limit);
104
+        } else if($type === 'users') {
105
+            $result = $this->access->countUsers($filter, $attr, $limit);
106
+        } else if ($type === 'objects') {
107
+            $result = $this->access->countObjects($limit);
108
+        } else {
109
+            throw new \Exception('internal error: invalid object type', 500);
110
+        }
111
+
112
+        return $result;
113
+    }
114
+
115
+    /**
116
+     * formats the return value of a count operation to the string to be
117
+     * inserted.
118
+     *
119
+     * @param bool|int $count
120
+     * @return int|string
121
+     */
122
+    private function formatCountResult($count) {
123
+        $formatted = ($count !== false) ? $count : 0;
124
+        if($formatted > 1000) {
125
+            $formatted = '> 1000';
126
+        }
127
+        return $formatted;
128
+    }
129
+
130
+    public function countGroups() {
131
+        $filter = $this->configuration->ldapGroupFilter;
132
+
133
+        if(empty($filter)) {
134
+            $output = self::$l->n('%s group found', '%s groups found', 0, array(0));
135
+            $this->result->addChange('ldap_group_count', $output);
136
+            return $this->result;
137
+        }
138
+
139
+        try {
140
+            $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
141
+        } catch (\Exception $e) {
142
+            //400 can be ignored, 500 is forwarded
143
+            if($e->getCode() === 500) {
144
+                throw $e;
145
+            }
146
+            return false;
147
+        }
148
+        $output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
149
+        $this->result->addChange('ldap_group_count', $output);
150
+        return $this->result;
151
+    }
152
+
153
+    /**
154
+     * @return WizardResult
155
+     * @throws \Exception
156
+     */
157
+    public function countUsers() {
158
+        $filter = $this->access->getFilterForUserCount();
159
+
160
+        $usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
161
+        $output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
162
+        $this->result->addChange('ldap_user_count', $output);
163
+        return $this->result;
164
+    }
165
+
166
+    /**
167
+     * counts any objects in the currently set base dn
168
+     *
169
+     * @return WizardResult
170
+     * @throws \Exception
171
+     */
172
+    public function countInBaseDN() {
173
+        // we don't need to provide a filter in this case
174
+        $total = $this->countEntries(null, 'objects');
175
+        if($total === false) {
176
+            throw new \Exception('invalid results received');
177
+        }
178
+        $this->result->addChange('ldap_test_base', $total);
179
+        return $this->result;
180
+    }
181
+
182
+    /**
183
+     * counts users with a specified attribute
184
+     * @param string $attr
185
+     * @param bool $existsCheck
186
+     * @return int|bool
187
+     */
188
+    public function countUsersWithAttribute($attr, $existsCheck = false) {
189
+        if(!$this->checkRequirements(array('ldapHost',
190
+                                            'ldapPort',
191
+                                            'ldapBase',
192
+                                            'ldapUserFilter',
193
+                                            ))) {
194
+            return  false;
195
+        }
196
+
197
+        $filter = $this->access->combineFilterWithAnd(array(
198
+            $this->configuration->ldapUserFilter,
199
+            $attr . '=*'
200
+        ));
201
+
202
+        $limit = ($existsCheck === false) ? null : 1;
203
+
204
+        return $this->access->countUsers($filter, array('dn'), $limit);
205
+    }
206
+
207
+    /**
208
+     * detects the display name attribute. If a setting is already present that
209
+     * returns at least one hit, the detection will be canceled.
210
+     * @return WizardResult|bool
211
+     * @throws \Exception
212
+     */
213
+    public function detectUserDisplayNameAttribute() {
214
+        if(!$this->checkRequirements(array('ldapHost',
215
+                                        'ldapPort',
216
+                                        'ldapBase',
217
+                                        'ldapUserFilter',
218
+                                        ))) {
219
+            return  false;
220
+        }
221
+
222
+        $attr = $this->configuration->ldapUserDisplayName;
223
+        if($attr !== 'displayName' && !empty($attr)) {
224
+            // most likely not the default value with upper case N,
225
+            // verify it still produces a result
226
+            $count = intval($this->countUsersWithAttribute($attr, true));
227
+            if($count > 0) {
228
+                //no change, but we sent it back to make sure the user interface
229
+                //is still correct, even if the ajax call was cancelled meanwhile
230
+                $this->result->addChange('ldap_display_name', $attr);
231
+                return $this->result;
232
+            }
233
+        }
234
+
235
+        // first attribute that has at least one result wins
236
+        $displayNameAttrs = array('displayname', 'cn');
237
+        foreach ($displayNameAttrs as $attr) {
238
+            $count = intval($this->countUsersWithAttribute($attr, true));
239
+
240
+            if($count > 0) {
241
+                $this->applyFind('ldap_display_name', $attr);
242
+                return $this->result;
243
+            }
244
+        };
245
+
246
+        throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced ldap settings.'));
247
+    }
248
+
249
+    /**
250
+     * detects the most often used email attribute for users applying to the
251
+     * user list filter. If a setting is already present that returns at least
252
+     * one hit, the detection will be canceled.
253
+     * @return WizardResult|bool
254
+     */
255
+    public function detectEmailAttribute() {
256
+        if(!$this->checkRequirements(array('ldapHost',
257
+                                            'ldapPort',
258
+                                            'ldapBase',
259
+                                            'ldapUserFilter',
260
+                                            ))) {
261
+            return  false;
262
+        }
263
+
264
+        $attr = $this->configuration->ldapEmailAttribute;
265
+        if(!empty($attr)) {
266
+            $count = intval($this->countUsersWithAttribute($attr, true));
267
+            if($count > 0) {
268
+                return false;
269
+            }
270
+            $writeLog = true;
271
+        } else {
272
+            $writeLog = false;
273
+        }
274
+
275
+        $emailAttributes = array('mail', 'mailPrimaryAddress');
276
+        $winner = '';
277
+        $maxUsers = 0;
278
+        foreach($emailAttributes as $attr) {
279
+            $count = $this->countUsersWithAttribute($attr);
280
+            if($count > $maxUsers) {
281
+                $maxUsers = $count;
282
+                $winner = $attr;
283
+            }
284
+        }
285
+
286
+        if($winner !== '') {
287
+            $this->applyFind('ldap_email_attr', $winner);
288
+            if($writeLog) {
289
+                \OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
290
+                    'automatically been reset, because the original value ' .
291
+                    'did not return any results.', \OCP\Util::INFO);
292
+            }
293
+        }
294
+
295
+        return $this->result;
296
+    }
297
+
298
+    /**
299
+     * @return WizardResult
300
+     * @throws \Exception
301
+     */
302
+    public function determineAttributes() {
303
+        if(!$this->checkRequirements(array('ldapHost',
304
+                                            'ldapPort',
305
+                                            'ldapBase',
306
+                                            'ldapUserFilter',
307
+                                            ))) {
308
+            return  false;
309
+        }
310
+
311
+        $attributes = $this->getUserAttributes();
312
+
313
+        natcasesort($attributes);
314
+        $attributes = array_values($attributes);
315
+
316
+        $this->result->addOptions('ldap_loginfilter_attributes', $attributes);
317
+
318
+        $selected = $this->configuration->ldapLoginFilterAttributes;
319
+        if(is_array($selected) && !empty($selected)) {
320
+            $this->result->addChange('ldap_loginfilter_attributes', $selected);
321
+        }
322
+
323
+        return $this->result;
324
+    }
325
+
326
+    /**
327
+     * detects the available LDAP attributes
328
+     * @return array|false The instance's WizardResult instance
329
+     * @throws \Exception
330
+     */
331
+    private function getUserAttributes() {
332
+        if(!$this->checkRequirements(array('ldapHost',
333
+                                            'ldapPort',
334
+                                            'ldapBase',
335
+                                            'ldapUserFilter',
336
+                                            ))) {
337
+            return  false;
338
+        }
339
+        $cr = $this->getConnection();
340
+        if(!$cr) {
341
+            throw new \Exception('Could not connect to LDAP');
342
+        }
343
+
344
+        $base = $this->configuration->ldapBase[0];
345
+        $filter = $this->configuration->ldapUserFilter;
346
+        $rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
347
+        if(!$this->ldap->isResource($rr)) {
348
+            return false;
349
+        }
350
+        $er = $this->ldap->firstEntry($cr, $rr);
351
+        $attributes = $this->ldap->getAttributes($cr, $er);
352
+        $pureAttributes = array();
353
+        for($i = 0; $i < $attributes['count']; $i++) {
354
+            $pureAttributes[] = $attributes[$i];
355
+        }
356
+
357
+        return $pureAttributes;
358
+    }
359
+
360
+    /**
361
+     * detects the available LDAP groups
362
+     * @return WizardResult|false the instance's WizardResult instance
363
+     */
364
+    public function determineGroupsForGroups() {
365
+        return $this->determineGroups('ldap_groupfilter_groups',
366
+                                        'ldapGroupFilterGroups',
367
+                                        false);
368
+    }
369
+
370
+    /**
371
+     * detects the available LDAP groups
372
+     * @return WizardResult|false the instance's WizardResult instance
373
+     */
374
+    public function determineGroupsForUsers() {
375
+        return $this->determineGroups('ldap_userfilter_groups',
376
+                                        'ldapUserFilterGroups');
377
+    }
378
+
379
+    /**
380
+     * detects the available LDAP groups
381
+     * @param string $dbKey
382
+     * @param string $confKey
383
+     * @param bool $testMemberOf
384
+     * @return WizardResult|false the instance's WizardResult instance
385
+     * @throws \Exception
386
+     */
387
+    private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
388
+        if(!$this->checkRequirements(array('ldapHost',
389
+                                            'ldapPort',
390
+                                            'ldapBase',
391
+                                            ))) {
392
+            return  false;
393
+        }
394
+        $cr = $this->getConnection();
395
+        if(!$cr) {
396
+            throw new \Exception('Could not connect to LDAP');
397
+        }
398
+
399
+        $this->fetchGroups($dbKey, $confKey);
400
+
401
+        if($testMemberOf) {
402
+            $this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
403
+            $this->result->markChange();
404
+            if(!$this->configuration->hasMemberOfFilterSupport) {
405
+                throw new \Exception('memberOf is not supported by the server');
406
+            }
407
+        }
408
+
409
+        return $this->result;
410
+    }
411
+
412
+    /**
413
+     * fetches all groups from LDAP and adds them to the result object
414
+     *
415
+     * @param string $dbKey
416
+     * @param string $confKey
417
+     * @return array $groupEntries
418
+     * @throws \Exception
419
+     */
420
+    public function fetchGroups($dbKey, $confKey) {
421
+        $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames');
422
+
423
+        $filterParts = array();
424
+        foreach($obclasses as $obclass) {
425
+            $filterParts[] = 'objectclass='.$obclass;
426
+        }
427
+        //we filter for everything
428
+        //- that looks like a group and
429
+        //- has the group display name set
430
+        $filter = $this->access->combineFilterWithOr($filterParts);
431
+        $filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*'));
432
+
433
+        $groupNames = array();
434
+        $groupEntries = array();
435
+        $limit = 400;
436
+        $offset = 0;
437
+        do {
438
+            // we need to request dn additionally here, otherwise memberOf
439
+            // detection will fail later
440
+            $result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
441
+            foreach($result as $item) {
442
+                if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
443
+                    // just in case - no issue known
444
+                    continue;
445
+                }
446
+                $groupNames[] = $item['cn'][0];
447
+                $groupEntries[] = $item;
448
+            }
449
+            $offset += $limit;
450
+        } while ($this->access->hasMoreResults());
451
+
452
+        if(count($groupNames) > 0) {
453
+            natsort($groupNames);
454
+            $this->result->addOptions($dbKey, array_values($groupNames));
455
+        } else {
456
+            throw new \Exception(self::$l->t('Could not find the desired feature'));
457
+        }
458
+
459
+        $setFeatures = $this->configuration->$confKey;
460
+        if(is_array($setFeatures) && !empty($setFeatures)) {
461
+            //something is already configured? pre-select it.
462
+            $this->result->addChange($dbKey, $setFeatures);
463
+        }
464
+        return $groupEntries;
465
+    }
466
+
467
+    public function determineGroupMemberAssoc() {
468
+        if(!$this->checkRequirements(array('ldapHost',
469
+                                            'ldapPort',
470
+                                            'ldapGroupFilter',
471
+                                            ))) {
472
+            return  false;
473
+        }
474
+        $attribute = $this->detectGroupMemberAssoc();
475
+        if($attribute === false) {
476
+            return false;
477
+        }
478
+        $this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
479
+        $this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
480
+
481
+        return $this->result;
482
+    }
483
+
484
+    /**
485
+     * Detects the available object classes
486
+     * @return WizardResult|false the instance's WizardResult instance
487
+     * @throws \Exception
488
+     */
489
+    public function determineGroupObjectClasses() {
490
+        if(!$this->checkRequirements(array('ldapHost',
491
+                                            'ldapPort',
492
+                                            'ldapBase',
493
+                                            ))) {
494
+            return  false;
495
+        }
496
+        $cr = $this->getConnection();
497
+        if(!$cr) {
498
+            throw new \Exception('Could not connect to LDAP');
499
+        }
500
+
501
+        $obclasses = array('groupOfNames', 'group', 'posixGroup', '*');
502
+        $this->determineFeature($obclasses,
503
+                                'objectclass',
504
+                                'ldap_groupfilter_objectclass',
505
+                                'ldapGroupFilterObjectclass',
506
+                                false);
507
+
508
+        return $this->result;
509
+    }
510
+
511
+    /**
512
+     * detects the available object classes
513
+     * @return WizardResult
514
+     * @throws \Exception
515
+     */
516
+    public function determineUserObjectClasses() {
517
+        if(!$this->checkRequirements(array('ldapHost',
518
+                                            'ldapPort',
519
+                                            'ldapBase',
520
+                                            ))) {
521
+            return  false;
522
+        }
523
+        $cr = $this->getConnection();
524
+        if(!$cr) {
525
+            throw new \Exception('Could not connect to LDAP');
526
+        }
527
+
528
+        $obclasses = array('inetOrgPerson', 'person', 'organizationalPerson',
529
+                            'user', 'posixAccount', '*');
530
+        $filter = $this->configuration->ldapUserFilter;
531
+        //if filter is empty, it is probably the first time the wizard is called
532
+        //then, apply suggestions.
533
+        $this->determineFeature($obclasses,
534
+                                'objectclass',
535
+                                'ldap_userfilter_objectclass',
536
+                                'ldapUserFilterObjectclass',
537
+                                empty($filter));
538
+
539
+        return $this->result;
540
+    }
541
+
542
+    /**
543
+     * @return WizardResult|false
544
+     * @throws \Exception
545
+     */
546
+    public function getGroupFilter() {
547
+        if(!$this->checkRequirements(array('ldapHost',
548
+                                            'ldapPort',
549
+                                            'ldapBase',
550
+                                            ))) {
551
+            return false;
552
+        }
553
+        //make sure the use display name is set
554
+        $displayName = $this->configuration->ldapGroupDisplayName;
555
+        if(empty($displayName)) {
556
+            $d = $this->configuration->getDefaults();
557
+            $this->applyFind('ldap_group_display_name',
558
+                                $d['ldap_group_display_name']);
559
+        }
560
+        $filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST);
561
+
562
+        $this->applyFind('ldap_group_filter', $filter);
563
+        return $this->result;
564
+    }
565
+
566
+    /**
567
+     * @return WizardResult|false
568
+     * @throws \Exception
569
+     */
570
+    public function getUserListFilter() {
571
+        if(!$this->checkRequirements(array('ldapHost',
572
+                                            'ldapPort',
573
+                                            'ldapBase',
574
+                                            ))) {
575
+            return false;
576
+        }
577
+        //make sure the use display name is set
578
+        $displayName = $this->configuration->ldapUserDisplayName;
579
+        if(empty($displayName)) {
580
+            $d = $this->configuration->getDefaults();
581
+            $this->applyFind('ldap_display_name', $d['ldap_display_name']);
582
+        }
583
+        $filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
584
+        if(!$filter) {
585
+            throw new \Exception('Cannot create filter');
586
+        }
587
+
588
+        $this->applyFind('ldap_userlist_filter', $filter);
589
+        return $this->result;
590
+    }
591
+
592
+    /**
593
+     * @return bool|WizardResult
594
+     * @throws \Exception
595
+     */
596
+    public function getUserLoginFilter() {
597
+        if(!$this->checkRequirements(array('ldapHost',
598
+                                            'ldapPort',
599
+                                            'ldapBase',
600
+                                            'ldapUserFilter',
601
+                                            ))) {
602
+            return false;
603
+        }
604
+
605
+        $filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
606
+        if(!$filter) {
607
+            throw new \Exception('Cannot create filter');
608
+        }
609
+
610
+        $this->applyFind('ldap_login_filter', $filter);
611
+        return $this->result;
612
+    }
613
+
614
+    /**
615
+     * @return bool|WizardResult
616
+     * @param string $loginName
617
+     * @throws \Exception
618
+     */
619
+    public function testLoginName($loginName) {
620
+        if(!$this->checkRequirements(array('ldapHost',
621
+            'ldapPort',
622
+            'ldapBase',
623
+            'ldapLoginFilter',
624
+        ))) {
625
+            return false;
626
+        }
627
+
628
+        $cr = $this->access->connection->getConnectionResource();
629
+        if(!$this->ldap->isResource($cr)) {
630
+            throw new \Exception('connection error');
631
+        }
632
+
633
+        if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
634
+            === false) {
635
+            throw new \Exception('missing placeholder');
636
+        }
637
+
638
+        $users = $this->access->countUsersByLoginName($loginName);
639
+        if($this->ldap->errno($cr) !== 0) {
640
+            throw new \Exception($this->ldap->error($cr));
641
+        }
642
+        $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
643
+        $this->result->addChange('ldap_test_loginname', $users);
644
+        $this->result->addChange('ldap_test_effective_filter', $filter);
645
+        return $this->result;
646
+    }
647
+
648
+    /**
649
+     * Tries to determine the port, requires given Host, User DN and Password
650
+     * @return WizardResult|false WizardResult on success, false otherwise
651
+     * @throws \Exception
652
+     */
653
+    public function guessPortAndTLS() {
654
+        if(!$this->checkRequirements(array('ldapHost',
655
+                                            ))) {
656
+            return false;
657
+        }
658
+        $this->checkHost();
659
+        $portSettings = $this->getPortSettingsToTry();
660
+
661
+        if(!is_array($portSettings)) {
662
+            throw new \Exception(print_r($portSettings, true));
663
+        }
664
+
665
+        //proceed from the best configuration and return on first success
666
+        foreach($portSettings as $setting) {
667
+            $p = $setting['port'];
668
+            $t = $setting['tls'];
669
+            \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
670
+            //connectAndBind may throw Exception, it needs to be catched by the
671
+            //callee of this method
672
+
673
+            try {
674
+                $settingsFound = $this->connectAndBind($p, $t);
675
+            } catch (\Exception $e) {
676
+                // any reply other than -1 (= cannot connect) is already okay,
677
+                // because then we found the server
678
+                // unavailable startTLS returns -11
679
+                if($e->getCode() > 0) {
680
+                    $settingsFound = true;
681
+                } else {
682
+                    throw $e;
683
+                }
684
+            }
685
+
686
+            if ($settingsFound === true) {
687
+                $config = array(
688
+                    'ldapPort' => $p,
689
+                    'ldapTLS' => intval($t)
690
+                );
691
+                $this->configuration->setConfiguration($config);
692
+                \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
693
+                $this->result->addChange('ldap_port', $p);
694
+                return $this->result;
695
+            }
696
+        }
697
+
698
+        //custom port, undetected (we do not brute force)
699
+        return false;
700
+    }
701
+
702
+    /**
703
+     * tries to determine a base dn from User DN or LDAP Host
704
+     * @return WizardResult|false WizardResult on success, false otherwise
705
+     */
706
+    public function guessBaseDN() {
707
+        if(!$this->checkRequirements(array('ldapHost',
708
+                                            'ldapPort',
709
+                                            ))) {
710
+            return false;
711
+        }
712
+
713
+        //check whether a DN is given in the agent name (99.9% of all cases)
714
+        $base = null;
715
+        $i = stripos($this->configuration->ldapAgentName, 'dc=');
716
+        if($i !== false) {
717
+            $base = substr($this->configuration->ldapAgentName, $i);
718
+            if($this->testBaseDN($base)) {
719
+                $this->applyFind('ldap_base', $base);
720
+                return $this->result;
721
+            }
722
+        }
723
+
724
+        //this did not help :(
725
+        //Let's see whether we can parse the Host URL and convert the domain to
726
+        //a base DN
727
+        $helper = new Helper();
728
+        $domain = $helper->getDomainFromURL($this->configuration->ldapHost);
729
+        if(!$domain) {
730
+            return false;
731
+        }
732
+
733
+        $dparts = explode('.', $domain);
734
+        while(count($dparts) > 0) {
735
+            $base2 = 'dc=' . implode(',dc=', $dparts);
736
+            if ($base !== $base2 && $this->testBaseDN($base2)) {
737
+                $this->applyFind('ldap_base', $base2);
738
+                return $this->result;
739
+            }
740
+            array_shift($dparts);
741
+        }
742
+
743
+        return false;
744
+    }
745
+
746
+    /**
747
+     * sets the found value for the configuration key in the WizardResult
748
+     * as well as in the Configuration instance
749
+     * @param string $key the configuration key
750
+     * @param string $value the (detected) value
751
+     *
752
+     */
753
+    private function applyFind($key, $value) {
754
+        $this->result->addChange($key, $value);
755
+        $this->configuration->setConfiguration(array($key => $value));
756
+    }
757
+
758
+    /**
759
+     * Checks, whether a port was entered in the Host configuration
760
+     * field. In this case the port will be stripped off, but also stored as
761
+     * setting.
762
+     */
763
+    private function checkHost() {
764
+        $host = $this->configuration->ldapHost;
765
+        $hostInfo = parse_url($host);
766
+
767
+        //removes Port from Host
768
+        if(is_array($hostInfo) && isset($hostInfo['port'])) {
769
+            $port = $hostInfo['port'];
770
+            $host = str_replace(':'.$port, '', $host);
771
+            $this->applyFind('ldap_host', $host);
772
+            $this->applyFind('ldap_port', $port);
773
+        }
774
+    }
775
+
776
+    /**
777
+     * tries to detect the group member association attribute which is
778
+     * one of 'uniqueMember', 'memberUid', 'member'
779
+     * @return string|false, string with the attribute name, false on error
780
+     * @throws \Exception
781
+     */
782
+    private function detectGroupMemberAssoc() {
783
+        $possibleAttrs = array('uniqueMember', 'memberUid', 'member');
784
+        $filter = $this->configuration->ldapGroupFilter;
785
+        if(empty($filter)) {
786
+            return false;
787
+        }
788
+        $cr = $this->getConnection();
789
+        if(!$cr) {
790
+            throw new \Exception('Could not connect to LDAP');
791
+        }
792
+        $base = $this->configuration->ldapBase[0];
793
+        $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
794
+        if(!$this->ldap->isResource($rr)) {
795
+            return false;
796
+        }
797
+        $er = $this->ldap->firstEntry($cr, $rr);
798
+        while(is_resource($er)) {
799
+            $this->ldap->getDN($cr, $er);
800
+            $attrs = $this->ldap->getAttributes($cr, $er);
801
+            $result = array();
802
+            $possibleAttrsCount = count($possibleAttrs);
803
+            for($i = 0; $i < $possibleAttrsCount; $i++) {
804
+                if(isset($attrs[$possibleAttrs[$i]])) {
805
+                    $result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
806
+                }
807
+            }
808
+            if(!empty($result)) {
809
+                natsort($result);
810
+                return key($result);
811
+            }
812
+
813
+            $er = $this->ldap->nextEntry($cr, $er);
814
+        }
815
+
816
+        return false;
817
+    }
818
+
819
+    /**
820
+     * Checks whether for a given BaseDN results will be returned
821
+     * @param string $base the BaseDN to test
822
+     * @return bool true on success, false otherwise
823
+     * @throws \Exception
824
+     */
825
+    private function testBaseDN($base) {
826
+        $cr = $this->getConnection();
827
+        if(!$cr) {
828
+            throw new \Exception('Could not connect to LDAP');
829
+        }
830
+
831
+        //base is there, let's validate it. If we search for anything, we should
832
+        //get a result set > 0 on a proper base
833
+        $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
834
+        if(!$this->ldap->isResource($rr)) {
835
+            $errorNo  = $this->ldap->errno($cr);
836
+            $errorMsg = $this->ldap->error($cr);
837
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
838
+                            ' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO);
839
+            return false;
840
+        }
841
+        $entries = $this->ldap->countEntries($cr, $rr);
842
+        return ($entries !== false) && ($entries > 0);
843
+    }
844
+
845
+    /**
846
+     * Checks whether the server supports memberOf in LDAP Filter.
847
+     * Note: at least in OpenLDAP, availability of memberOf is dependent on
848
+     * a configured objectClass. I.e. not necessarily for all available groups
849
+     * memberOf does work.
850
+     *
851
+     * @return bool true if it does, false otherwise
852
+     * @throws \Exception
853
+     */
854
+    private function testMemberOf() {
855
+        $cr = $this->getConnection();
856
+        if(!$cr) {
857
+            throw new \Exception('Could not connect to LDAP');
858
+        }
859
+        $result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
860
+        if(is_int($result) &&  $result > 0) {
861
+            return true;
862
+        }
863
+        return false;
864
+    }
865
+
866
+    /**
867
+     * creates an LDAP Filter from given configuration
868
+     * @param integer $filterType int, for which use case the filter shall be created
869
+     * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or
870
+     * self::LFILTER_GROUP_LIST
871
+     * @return string|false string with the filter on success, false otherwise
872
+     * @throws \Exception
873
+     */
874
+    private function composeLdapFilter($filterType) {
875
+        $filter = '';
876
+        $parts = 0;
877
+        switch ($filterType) {
878
+            case self::LFILTER_USER_LIST:
879
+                $objcs = $this->configuration->ldapUserFilterObjectclass;
880
+                //glue objectclasses
881
+                if(is_array($objcs) && count($objcs) > 0) {
882
+                    $filter .= '(|';
883
+                    foreach($objcs as $objc) {
884
+                        $filter .= '(objectclass=' . $objc . ')';
885
+                    }
886
+                    $filter .= ')';
887
+                    $parts++;
888
+                }
889
+                //glue group memberships
890
+                if($this->configuration->hasMemberOfFilterSupport) {
891
+                    $cns = $this->configuration->ldapUserFilterGroups;
892
+                    if(is_array($cns) && count($cns) > 0) {
893
+                        $filter .= '(|';
894
+                        $cr = $this->getConnection();
895
+                        if(!$cr) {
896
+                            throw new \Exception('Could not connect to LDAP');
897
+                        }
898
+                        $base = $this->configuration->ldapBase[0];
899
+                        foreach($cns as $cn) {
900
+                            $rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
901
+                            if(!$this->ldap->isResource($rr)) {
902
+                                continue;
903
+                            }
904
+                            $er = $this->ldap->firstEntry($cr, $rr);
905
+                            $attrs = $this->ldap->getAttributes($cr, $er);
906
+                            $dn = $this->ldap->getDN($cr, $er);
907
+                            if(empty($dn)) {
908
+                                continue;
909
+                            }
910
+                            $filterPart = '(memberof=' . $dn . ')';
911
+                            if(isset($attrs['primaryGroupToken'])) {
912
+                                $pgt = $attrs['primaryGroupToken'][0];
913
+                                $primaryFilterPart = '(primaryGroupID=' . $pgt .')';
914
+                                $filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
915
+                            }
916
+                            $filter .= $filterPart;
917
+                        }
918
+                        $filter .= ')';
919
+                    }
920
+                    $parts++;
921
+                }
922
+                //wrap parts in AND condition
923
+                if($parts > 1) {
924
+                    $filter = '(&' . $filter . ')';
925
+                }
926
+                if(empty($filter)) {
927
+                    $filter = '(objectclass=*)';
928
+                }
929
+                break;
930
+
931
+            case self::LFILTER_GROUP_LIST:
932
+                $objcs = $this->configuration->ldapGroupFilterObjectclass;
933
+                //glue objectclasses
934
+                if(is_array($objcs) && count($objcs) > 0) {
935
+                    $filter .= '(|';
936
+                    foreach($objcs as $objc) {
937
+                        $filter .= '(objectclass=' . $objc . ')';
938
+                    }
939
+                    $filter .= ')';
940
+                    $parts++;
941
+                }
942
+                //glue group memberships
943
+                $cns = $this->configuration->ldapGroupFilterGroups;
944
+                if(is_array($cns) && count($cns) > 0) {
945
+                    $filter .= '(|';
946
+                    $base = $this->configuration->ldapBase[0];
947
+                    foreach($cns as $cn) {
948
+                        $filter .= '(cn=' . $cn . ')';
949
+                    }
950
+                    $filter .= ')';
951
+                }
952
+                $parts++;
953
+                //wrap parts in AND condition
954
+                if($parts > 1) {
955
+                    $filter = '(&' . $filter . ')';
956
+                }
957
+                break;
958
+
959
+            case self::LFILTER_LOGIN:
960
+                $ulf = $this->configuration->ldapUserFilter;
961
+                $loginpart = '=%uid';
962
+                $filterUsername = '';
963
+                $userAttributes = $this->getUserAttributes();
964
+                $userAttributes = array_change_key_case(array_flip($userAttributes));
965
+                $parts = 0;
966
+
967
+                if($this->configuration->ldapLoginFilterUsername === '1') {
968
+                    $attr = '';
969
+                    if(isset($userAttributes['uid'])) {
970
+                        $attr = 'uid';
971
+                    } else if(isset($userAttributes['samaccountname'])) {
972
+                        $attr = 'samaccountname';
973
+                    } else if(isset($userAttributes['cn'])) {
974
+                        //fallback
975
+                        $attr = 'cn';
976
+                    }
977
+                    if(!empty($attr)) {
978
+                        $filterUsername = '(' . $attr . $loginpart . ')';
979
+                        $parts++;
980
+                    }
981
+                }
982
+
983
+                $filterEmail = '';
984
+                if($this->configuration->ldapLoginFilterEmail === '1') {
985
+                    $filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
986
+                    $parts++;
987
+                }
988
+
989
+                $filterAttributes = '';
990
+                $attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
991
+                if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
992
+                    $filterAttributes = '(|';
993
+                    foreach($attrsToFilter as $attribute) {
994
+                        $filterAttributes .= '(' . $attribute . $loginpart . ')';
995
+                    }
996
+                    $filterAttributes .= ')';
997
+                    $parts++;
998
+                }
999
+
1000
+                $filterLogin = '';
1001
+                if($parts > 1) {
1002
+                    $filterLogin = '(|';
1003
+                }
1004
+                $filterLogin .= $filterUsername;
1005
+                $filterLogin .= $filterEmail;
1006
+                $filterLogin .= $filterAttributes;
1007
+                if($parts > 1) {
1008
+                    $filterLogin .= ')';
1009
+                }
1010
+
1011
+                $filter = '(&'.$ulf.$filterLogin.')';
1012
+                break;
1013
+        }
1014
+
1015
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG);
1016
+
1017
+        return $filter;
1018
+    }
1019
+
1020
+    /**
1021
+     * Connects and Binds to an LDAP Server
1022
+     * @param int $port the port to connect with
1023
+     * @param bool $tls whether startTLS is to be used
1024
+     * @param bool $ncc
1025
+     * @return bool
1026
+     * @throws \Exception
1027
+     */
1028
+    private function connectAndBind($port = 389, $tls = false, $ncc = false) {
1029
+        if($ncc) {
1030
+            //No certificate check
1031
+            //FIXME: undo afterwards
1032
+            putenv('LDAPTLS_REQCERT=never');
1033
+        }
1034
+
1035
+        //connect, does not really trigger any server communication
1036
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG);
1037
+        $host = $this->configuration->ldapHost;
1038
+        $hostInfo = parse_url($host);
1039
+        if(!$hostInfo) {
1040
+            throw new \Exception(self::$l->t('Invalid Host'));
1041
+        }
1042
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1043
+        $cr = $this->ldap->connect($host, $port);
1044
+        if(!is_resource($cr)) {
1045
+            throw new \Exception(self::$l->t('Invalid Host'));
1046
+        }
1047
+
1048
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG);
1049
+        //set LDAP options
1050
+        $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1051
+        $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1052
+        $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1053
+
1054
+        try {
1055
+            if($tls) {
1056
+                $isTlsWorking = @$this->ldap->startTls($cr);
1057
+                if(!$isTlsWorking) {
1058
+                    return false;
1059
+                }
1060
+            }
1061
+
1062
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG);
1063
+            //interesting part: do the bind!
1064
+            $login = $this->ldap->bind($cr,
1065
+                $this->configuration->ldapAgentName,
1066
+                $this->configuration->ldapAgentPassword
1067
+            );
1068
+            $errNo = $this->ldap->errno($cr);
1069
+            $error = ldap_error($cr);
1070
+            $this->ldap->unbind($cr);
1071
+        } catch(ServerNotAvailableException $e) {
1072
+            return false;
1073
+        }
1074
+
1075
+        if($login === true) {
1076
+            $this->ldap->unbind($cr);
1077
+            if($ncc) {
1078
+                throw new \Exception('Certificate cannot be validated.');
1079
+            }
1080
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1081
+            return true;
1082
+        }
1083
+
1084
+        if($errNo === -1 || ($errNo === 2 && $ncc)) {
1085
+            //host, port or TLS wrong
1086
+            return false;
1087
+        } else if ($errNo === 2) {
1088
+            return $this->connectAndBind($port, $tls, true);
1089
+        }
1090
+        throw new \Exception($error, $errNo);
1091
+    }
1092
+
1093
+    /**
1094
+     * checks whether a valid combination of agent and password has been
1095
+     * provided (either two values or nothing for anonymous connect)
1096
+     * @return bool, true if everything is fine, false otherwise
1097
+     */
1098
+    private function checkAgentRequirements() {
1099
+        $agent = $this->configuration->ldapAgentName;
1100
+        $pwd = $this->configuration->ldapAgentPassword;
1101
+
1102
+        return ( (!empty($agent) && !empty($pwd))
1103
+               || (empty($agent) &&  empty($pwd)));
1104
+    }
1105
+
1106
+    /**
1107
+     * @param array $reqs
1108
+     * @return bool
1109
+     */
1110
+    private function checkRequirements($reqs) {
1111
+        $this->checkAgentRequirements();
1112
+        foreach($reqs as $option) {
1113
+            $value = $this->configuration->$option;
1114
+            if(empty($value)) {
1115
+                return false;
1116
+            }
1117
+        }
1118
+        return true;
1119
+    }
1120
+
1121
+    /**
1122
+     * does a cumulativeSearch on LDAP to get different values of a
1123
+     * specified attribute
1124
+     * @param string[] $filters array, the filters that shall be used in the search
1125
+     * @param string $attr the attribute of which a list of values shall be returned
1126
+     * @param int $dnReadLimit the amount of how many DNs should be analyzed.
1127
+     * The lower, the faster
1128
+     * @param string $maxF string. if not null, this variable will have the filter that
1129
+     * yields most result entries
1130
+     * @return array|false an array with the values on success, false otherwise
1131
+     */
1132
+    public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) {
1133
+        $dnRead = array();
1134
+        $foundItems = array();
1135
+        $maxEntries = 0;
1136
+        if(!is_array($this->configuration->ldapBase)
1137
+           || !isset($this->configuration->ldapBase[0])) {
1138
+            return false;
1139
+        }
1140
+        $base = $this->configuration->ldapBase[0];
1141
+        $cr = $this->getConnection();
1142
+        if(!$this->ldap->isResource($cr)) {
1143
+            return false;
1144
+        }
1145
+        $lastFilter = null;
1146
+        if(isset($filters[count($filters)-1])) {
1147
+            $lastFilter = $filters[count($filters)-1];
1148
+        }
1149
+        foreach($filters as $filter) {
1150
+            if($lastFilter === $filter && count($foundItems) > 0) {
1151
+                //skip when the filter is a wildcard and results were found
1152
+                continue;
1153
+            }
1154
+            // 20k limit for performance and reason
1155
+            $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1156
+            if(!$this->ldap->isResource($rr)) {
1157
+                continue;
1158
+            }
1159
+            $entries = $this->ldap->countEntries($cr, $rr);
1160
+            $getEntryFunc = 'firstEntry';
1161
+            if(($entries !== false) && ($entries > 0)) {
1162
+                if(!is_null($maxF) && $entries > $maxEntries) {
1163
+                    $maxEntries = $entries;
1164
+                    $maxF = $filter;
1165
+                }
1166
+                $dnReadCount = 0;
1167
+                do {
1168
+                    $entry = $this->ldap->$getEntryFunc($cr, $rr);
1169
+                    $getEntryFunc = 'nextEntry';
1170
+                    if(!$this->ldap->isResource($entry)) {
1171
+                        continue 2;
1172
+                    }
1173
+                    $rr = $entry; //will be expected by nextEntry next round
1174
+                    $attributes = $this->ldap->getAttributes($cr, $entry);
1175
+                    $dn = $this->ldap->getDN($cr, $entry);
1176
+                    if($dn === false || in_array($dn, $dnRead)) {
1177
+                        continue;
1178
+                    }
1179
+                    $newItems = array();
1180
+                    $state = $this->getAttributeValuesFromEntry($attributes,
1181
+                                                                $attr,
1182
+                                                                $newItems);
1183
+                    $dnReadCount++;
1184
+                    $foundItems = array_merge($foundItems, $newItems);
1185
+                    $this->resultCache[$dn][$attr] = $newItems;
1186
+                    $dnRead[] = $dn;
1187
+                } while(($state === self::LRESULT_PROCESSED_SKIP
1188
+                        || $this->ldap->isResource($entry))
1189
+                        && ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1190
+            }
1191
+        }
1192
+
1193
+        return array_unique($foundItems);
1194
+    }
1195
+
1196
+    /**
1197
+     * determines if and which $attr are available on the LDAP server
1198
+     * @param string[] $objectclasses the objectclasses to use as search filter
1199
+     * @param string $attr the attribute to look for
1200
+     * @param string $dbkey the dbkey of the setting the feature is connected to
1201
+     * @param string $confkey the confkey counterpart for the $dbkey as used in the
1202
+     * Configuration class
1203
+     * @param bool $po whether the objectClass with most result entries
1204
+     * shall be pre-selected via the result
1205
+     * @return array|false list of found items.
1206
+     * @throws \Exception
1207
+     */
1208
+    private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1209
+        $cr = $this->getConnection();
1210
+        if(!$cr) {
1211
+            throw new \Exception('Could not connect to LDAP');
1212
+        }
1213
+        $p = 'objectclass=';
1214
+        foreach($objectclasses as $key => $value) {
1215
+            $objectclasses[$key] = $p.$value;
1216
+        }
1217
+        $maxEntryObjC = '';
1218
+
1219
+        //how deep to dig?
1220
+        //When looking for objectclasses, testing few entries is sufficient,
1221
+        $dig = 3;
1222
+
1223
+        $availableFeatures =
1224
+            $this->cumulativeSearchOnAttribute($objectclasses, $attr,
1225
+                                                $dig, $maxEntryObjC);
1226
+        if(is_array($availableFeatures)
1227
+           && count($availableFeatures) > 0) {
1228
+            natcasesort($availableFeatures);
1229
+            //natcasesort keeps indices, but we must get rid of them for proper
1230
+            //sorting in the web UI. Therefore: array_values
1231
+            $this->result->addOptions($dbkey, array_values($availableFeatures));
1232
+        } else {
1233
+            throw new \Exception(self::$l->t('Could not find the desired feature'));
1234
+        }
1235
+
1236
+        $setFeatures = $this->configuration->$confkey;
1237
+        if(is_array($setFeatures) && !empty($setFeatures)) {
1238
+            //something is already configured? pre-select it.
1239
+            $this->result->addChange($dbkey, $setFeatures);
1240
+        } else if($po && !empty($maxEntryObjC)) {
1241
+            //pre-select objectclass with most result entries
1242
+            $maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1243
+            $this->applyFind($dbkey, $maxEntryObjC);
1244
+            $this->result->addChange($dbkey, $maxEntryObjC);
1245
+        }
1246
+
1247
+        return $availableFeatures;
1248
+    }
1249
+
1250
+    /**
1251
+     * appends a list of values fr
1252
+     * @param resource $result the return value from ldap_get_attributes
1253
+     * @param string $attribute the attribute values to look for
1254
+     * @param array &$known new values will be appended here
1255
+     * @return int, state on of the class constants LRESULT_PROCESSED_OK,
1256
+     * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1257
+     */
1258
+    private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1259
+        if(!is_array($result)
1260
+           || !isset($result['count'])
1261
+           || !$result['count'] > 0) {
1262
+            return self::LRESULT_PROCESSED_INVALID;
1263
+        }
1264
+
1265
+        // strtolower on all keys for proper comparison
1266
+        $result = \OCP\Util::mb_array_change_key_case($result);
1267
+        $attribute = strtolower($attribute);
1268
+        if(isset($result[$attribute])) {
1269
+            foreach($result[$attribute] as $key => $val) {
1270
+                if($key === 'count') {
1271
+                    continue;
1272
+                }
1273
+                if(!in_array($val, $known)) {
1274
+                    $known[] = $val;
1275
+                }
1276
+            }
1277
+            return self::LRESULT_PROCESSED_OK;
1278
+        } else {
1279
+            return self::LRESULT_PROCESSED_SKIP;
1280
+        }
1281
+    }
1282
+
1283
+    /**
1284
+     * @return bool|mixed
1285
+     */
1286
+    private function getConnection() {
1287
+        if(!is_null($this->cr)) {
1288
+            return $this->cr;
1289
+        }
1290
+
1291
+        $cr = $this->ldap->connect(
1292
+            $this->configuration->ldapHost,
1293
+            $this->configuration->ldapPort
1294
+        );
1295
+
1296
+        $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1297
+        $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1298
+        $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1299
+        if($this->configuration->ldapTLS === 1) {
1300
+            $this->ldap->startTls($cr);
1301
+        }
1302
+
1303
+        $lo = @$this->ldap->bind($cr,
1304
+                                    $this->configuration->ldapAgentName,
1305
+                                    $this->configuration->ldapAgentPassword);
1306
+        if($lo === true) {
1307
+            $this->$cr = $cr;
1308
+            return $cr;
1309
+        }
1310
+
1311
+        return false;
1312
+    }
1313
+
1314
+    /**
1315
+     * @return array
1316
+     */
1317
+    private function getDefaultLdapPortSettings() {
1318
+        static $settings = array(
1319
+                                array('port' => 7636, 'tls' => false),
1320
+                                array('port' =>  636, 'tls' => false),
1321
+                                array('port' => 7389, 'tls' => true),
1322
+                                array('port' =>  389, 'tls' => true),
1323
+                                array('port' => 7389, 'tls' => false),
1324
+                                array('port' =>  389, 'tls' => false),
1325
+                            );
1326
+        return $settings;
1327
+    }
1328
+
1329
+    /**
1330
+     * @return array
1331
+     */
1332
+    private function getPortSettingsToTry() {
1333
+        //389 ← LDAP / Unencrypted or StartTLS
1334
+        //636 ← LDAPS / SSL
1335
+        //7xxx ← UCS. need to be checked first, because both ports may be open
1336
+        $host = $this->configuration->ldapHost;
1337
+        $port = intval($this->configuration->ldapPort);
1338
+        $portSettings = array();
1339
+
1340
+        //In case the port is already provided, we will check this first
1341
+        if($port > 0) {
1342
+            $hostInfo = parse_url($host);
1343
+            if(!(is_array($hostInfo)
1344
+                && isset($hostInfo['scheme'])
1345
+                && stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1346
+                $portSettings[] = array('port' => $port, 'tls' => true);
1347
+            }
1348
+            $portSettings[] =array('port' => $port, 'tls' => false);
1349
+        }
1350
+
1351
+        //default ports
1352
+        $portSettings = array_merge($portSettings,
1353
+                                    $this->getDefaultLdapPortSettings());
1354
+
1355
+        return $portSettings;
1356
+    }
1357 1357
 
1358 1358
 
1359 1359
 }
Please login to merge, or discard this patch.
Spacing   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -67,7 +67,7 @@  discard block
 block discarded – undo
67 67
 	public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
68 68
 		parent::__construct($ldap);
69 69
 		$this->configuration = $configuration;
70
-		if(is_null(Wizard::$l)) {
70
+		if (is_null(Wizard::$l)) {
71 71
 			Wizard::$l = \OC::$server->getL10N('user_ldap');
72 72
 		}
73 73
 		$this->access = $access;
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
 	}
76 76
 
77 77
 	public function  __destruct() {
78
-		if($this->result->hasChanges()) {
78
+		if ($this->result->hasChanges()) {
79 79
 			$this->configuration->saveConfiguration();
80 80
 		}
81 81
 	}
@@ -90,18 +90,18 @@  discard block
 block discarded – undo
90 90
 	 */
91 91
 	public function countEntries($filter, $type) {
92 92
 		$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
93
-		if($type === 'users') {
93
+		if ($type === 'users') {
94 94
 			$reqs[] = 'ldapUserFilter';
95 95
 		}
96
-		if(!$this->checkRequirements($reqs)) {
96
+		if (!$this->checkRequirements($reqs)) {
97 97
 			throw new \Exception('Requirements not met', 400);
98 98
 		}
99 99
 
100 100
 		$attr = array('dn'); // default
101 101
 		$limit = 1001;
102
-		if($type === 'groups') {
103
-			$result =  $this->access->countGroups($filter, $attr, $limit);
104
-		} else if($type === 'users') {
102
+		if ($type === 'groups') {
103
+			$result = $this->access->countGroups($filter, $attr, $limit);
104
+		} else if ($type === 'users') {
105 105
 			$result = $this->access->countUsers($filter, $attr, $limit);
106 106
 		} else if ($type === 'objects') {
107 107
 			$result = $this->access->countObjects($limit);
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
 	 */
122 122
 	private function formatCountResult($count) {
123 123
 		$formatted = ($count !== false) ? $count : 0;
124
-		if($formatted > 1000) {
124
+		if ($formatted > 1000) {
125 125
 			$formatted = '> 1000';
126 126
 		}
127 127
 		return $formatted;
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
 	public function countGroups() {
131 131
 		$filter = $this->configuration->ldapGroupFilter;
132 132
 
133
-		if(empty($filter)) {
133
+		if (empty($filter)) {
134 134
 			$output = self::$l->n('%s group found', '%s groups found', 0, array(0));
135 135
 			$this->result->addChange('ldap_group_count', $output);
136 136
 			return $this->result;
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 			$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
141 141
 		} catch (\Exception $e) {
142 142
 			//400 can be ignored, 500 is forwarded
143
-			if($e->getCode() === 500) {
143
+			if ($e->getCode() === 500) {
144 144
 				throw $e;
145 145
 			}
146 146
 			return false;
@@ -172,7 +172,7 @@  discard block
 block discarded – undo
172 172
 	public function countInBaseDN() {
173 173
 		// we don't need to provide a filter in this case
174 174
 		$total = $this->countEntries(null, 'objects');
175
-		if($total === false) {
175
+		if ($total === false) {
176 176
 			throw new \Exception('invalid results received');
177 177
 		}
178 178
 		$this->result->addChange('ldap_test_base', $total);
@@ -186,7 +186,7 @@  discard block
 block discarded – undo
186 186
 	 * @return int|bool
187 187
 	 */
188 188
 	public function countUsersWithAttribute($attr, $existsCheck = false) {
189
-		if(!$this->checkRequirements(array('ldapHost',
189
+		if (!$this->checkRequirements(array('ldapHost',
190 190
 										   'ldapPort',
191 191
 										   'ldapBase',
192 192
 										   'ldapUserFilter',
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 
197 197
 		$filter = $this->access->combineFilterWithAnd(array(
198 198
 			$this->configuration->ldapUserFilter,
199
-			$attr . '=*'
199
+			$attr.'=*'
200 200
 		));
201 201
 
202 202
 		$limit = ($existsCheck === false) ? null : 1;
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
 	 * @throws \Exception
212 212
 	 */
213 213
 	public function detectUserDisplayNameAttribute() {
214
-		if(!$this->checkRequirements(array('ldapHost',
214
+		if (!$this->checkRequirements(array('ldapHost',
215 215
 										'ldapPort',
216 216
 										'ldapBase',
217 217
 										'ldapUserFilter',
@@ -220,11 +220,11 @@  discard block
 block discarded – undo
220 220
 		}
221 221
 
222 222
 		$attr = $this->configuration->ldapUserDisplayName;
223
-		if($attr !== 'displayName' && !empty($attr)) {
223
+		if ($attr !== 'displayName' && !empty($attr)) {
224 224
 			// most likely not the default value with upper case N,
225 225
 			// verify it still produces a result
226 226
 			$count = intval($this->countUsersWithAttribute($attr, true));
227
-			if($count > 0) {
227
+			if ($count > 0) {
228 228
 				//no change, but we sent it back to make sure the user interface
229 229
 				//is still correct, even if the ajax call was cancelled meanwhile
230 230
 				$this->result->addChange('ldap_display_name', $attr);
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
 		foreach ($displayNameAttrs as $attr) {
238 238
 			$count = intval($this->countUsersWithAttribute($attr, true));
239 239
 
240
-			if($count > 0) {
240
+			if ($count > 0) {
241 241
 				$this->applyFind('ldap_display_name', $attr);
242 242
 				return $this->result;
243 243
 			}
@@ -253,7 +253,7 @@  discard block
 block discarded – undo
253 253
 	 * @return WizardResult|bool
254 254
 	 */
255 255
 	public function detectEmailAttribute() {
256
-		if(!$this->checkRequirements(array('ldapHost',
256
+		if (!$this->checkRequirements(array('ldapHost',
257 257
 										   'ldapPort',
258 258
 										   'ldapBase',
259 259
 										   'ldapUserFilter',
@@ -262,9 +262,9 @@  discard block
 block discarded – undo
262 262
 		}
263 263
 
264 264
 		$attr = $this->configuration->ldapEmailAttribute;
265
-		if(!empty($attr)) {
265
+		if (!empty($attr)) {
266 266
 			$count = intval($this->countUsersWithAttribute($attr, true));
267
-			if($count > 0) {
267
+			if ($count > 0) {
268 268
 				return false;
269 269
 			}
270 270
 			$writeLog = true;
@@ -275,19 +275,19 @@  discard block
 block discarded – undo
275 275
 		$emailAttributes = array('mail', 'mailPrimaryAddress');
276 276
 		$winner = '';
277 277
 		$maxUsers = 0;
278
-		foreach($emailAttributes as $attr) {
278
+		foreach ($emailAttributes as $attr) {
279 279
 			$count = $this->countUsersWithAttribute($attr);
280
-			if($count > $maxUsers) {
280
+			if ($count > $maxUsers) {
281 281
 				$maxUsers = $count;
282 282
 				$winner = $attr;
283 283
 			}
284 284
 		}
285 285
 
286
-		if($winner !== '') {
286
+		if ($winner !== '') {
287 287
 			$this->applyFind('ldap_email_attr', $winner);
288
-			if($writeLog) {
289
-				\OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
290
-					'automatically been reset, because the original value ' .
288
+			if ($writeLog) {
289
+				\OCP\Util::writeLog('user_ldap', 'The mail attribute has '.
290
+					'automatically been reset, because the original value '.
291 291
 					'did not return any results.', \OCP\Util::INFO);
292 292
 			}
293 293
 		}
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
 	 * @throws \Exception
301 301
 	 */
302 302
 	public function determineAttributes() {
303
-		if(!$this->checkRequirements(array('ldapHost',
303
+		if (!$this->checkRequirements(array('ldapHost',
304 304
 										   'ldapPort',
305 305
 										   'ldapBase',
306 306
 										   'ldapUserFilter',
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
 		$this->result->addOptions('ldap_loginfilter_attributes', $attributes);
317 317
 
318 318
 		$selected = $this->configuration->ldapLoginFilterAttributes;
319
-		if(is_array($selected) && !empty($selected)) {
319
+		if (is_array($selected) && !empty($selected)) {
320 320
 			$this->result->addChange('ldap_loginfilter_attributes', $selected);
321 321
 		}
322 322
 
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 	 * @throws \Exception
330 330
 	 */
331 331
 	private function getUserAttributes() {
332
-		if(!$this->checkRequirements(array('ldapHost',
332
+		if (!$this->checkRequirements(array('ldapHost',
333 333
 										   'ldapPort',
334 334
 										   'ldapBase',
335 335
 										   'ldapUserFilter',
@@ -337,20 +337,20 @@  discard block
 block discarded – undo
337 337
 			return  false;
338 338
 		}
339 339
 		$cr = $this->getConnection();
340
-		if(!$cr) {
340
+		if (!$cr) {
341 341
 			throw new \Exception('Could not connect to LDAP');
342 342
 		}
343 343
 
344 344
 		$base = $this->configuration->ldapBase[0];
345 345
 		$filter = $this->configuration->ldapUserFilter;
346 346
 		$rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
347
-		if(!$this->ldap->isResource($rr)) {
347
+		if (!$this->ldap->isResource($rr)) {
348 348
 			return false;
349 349
 		}
350 350
 		$er = $this->ldap->firstEntry($cr, $rr);
351 351
 		$attributes = $this->ldap->getAttributes($cr, $er);
352 352
 		$pureAttributes = array();
353
-		for($i = 0; $i < $attributes['count']; $i++) {
353
+		for ($i = 0; $i < $attributes['count']; $i++) {
354 354
 			$pureAttributes[] = $attributes[$i];
355 355
 		}
356 356
 
@@ -385,23 +385,23 @@  discard block
 block discarded – undo
385 385
 	 * @throws \Exception
386 386
 	 */
387 387
 	private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
388
-		if(!$this->checkRequirements(array('ldapHost',
388
+		if (!$this->checkRequirements(array('ldapHost',
389 389
 										   'ldapPort',
390 390
 										   'ldapBase',
391 391
 										   ))) {
392 392
 			return  false;
393 393
 		}
394 394
 		$cr = $this->getConnection();
395
-		if(!$cr) {
395
+		if (!$cr) {
396 396
 			throw new \Exception('Could not connect to LDAP');
397 397
 		}
398 398
 
399 399
 		$this->fetchGroups($dbKey, $confKey);
400 400
 
401
-		if($testMemberOf) {
401
+		if ($testMemberOf) {
402 402
 			$this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
403 403
 			$this->result->markChange();
404
-			if(!$this->configuration->hasMemberOfFilterSupport) {
404
+			if (!$this->configuration->hasMemberOfFilterSupport) {
405 405
 				throw new \Exception('memberOf is not supported by the server');
406 406
 			}
407 407
 		}
@@ -421,7 +421,7 @@  discard block
 block discarded – undo
421 421
 		$obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames');
422 422
 
423 423
 		$filterParts = array();
424
-		foreach($obclasses as $obclass) {
424
+		foreach ($obclasses as $obclass) {
425 425
 			$filterParts[] = 'objectclass='.$obclass;
426 426
 		}
427 427
 		//we filter for everything
@@ -438,8 +438,8 @@  discard block
 block discarded – undo
438 438
 			// we need to request dn additionally here, otherwise memberOf
439 439
 			// detection will fail later
440 440
 			$result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
441
-			foreach($result as $item) {
442
-				if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
441
+			foreach ($result as $item) {
442
+				if (!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
443 443
 					// just in case - no issue known
444 444
 					continue;
445 445
 				}
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
 			$offset += $limit;
450 450
 		} while ($this->access->hasMoreResults());
451 451
 
452
-		if(count($groupNames) > 0) {
452
+		if (count($groupNames) > 0) {
453 453
 			natsort($groupNames);
454 454
 			$this->result->addOptions($dbKey, array_values($groupNames));
455 455
 		} else {
@@ -457,7 +457,7 @@  discard block
 block discarded – undo
457 457
 		}
458 458
 
459 459
 		$setFeatures = $this->configuration->$confKey;
460
-		if(is_array($setFeatures) && !empty($setFeatures)) {
460
+		if (is_array($setFeatures) && !empty($setFeatures)) {
461 461
 			//something is already configured? pre-select it.
462 462
 			$this->result->addChange($dbKey, $setFeatures);
463 463
 		}
@@ -465,14 +465,14 @@  discard block
 block discarded – undo
465 465
 	}
466 466
 
467 467
 	public function determineGroupMemberAssoc() {
468
-		if(!$this->checkRequirements(array('ldapHost',
468
+		if (!$this->checkRequirements(array('ldapHost',
469 469
 										   'ldapPort',
470 470
 										   'ldapGroupFilter',
471 471
 										   ))) {
472 472
 			return  false;
473 473
 		}
474 474
 		$attribute = $this->detectGroupMemberAssoc();
475
-		if($attribute === false) {
475
+		if ($attribute === false) {
476 476
 			return false;
477 477
 		}
478 478
 		$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
@@ -487,14 +487,14 @@  discard block
 block discarded – undo
487 487
 	 * @throws \Exception
488 488
 	 */
489 489
 	public function determineGroupObjectClasses() {
490
-		if(!$this->checkRequirements(array('ldapHost',
490
+		if (!$this->checkRequirements(array('ldapHost',
491 491
 										   'ldapPort',
492 492
 										   'ldapBase',
493 493
 										   ))) {
494 494
 			return  false;
495 495
 		}
496 496
 		$cr = $this->getConnection();
497
-		if(!$cr) {
497
+		if (!$cr) {
498 498
 			throw new \Exception('Could not connect to LDAP');
499 499
 		}
500 500
 
@@ -514,14 +514,14 @@  discard block
 block discarded – undo
514 514
 	 * @throws \Exception
515 515
 	 */
516 516
 	public function determineUserObjectClasses() {
517
-		if(!$this->checkRequirements(array('ldapHost',
517
+		if (!$this->checkRequirements(array('ldapHost',
518 518
 										   'ldapPort',
519 519
 										   'ldapBase',
520 520
 										   ))) {
521 521
 			return  false;
522 522
 		}
523 523
 		$cr = $this->getConnection();
524
-		if(!$cr) {
524
+		if (!$cr) {
525 525
 			throw new \Exception('Could not connect to LDAP');
526 526
 		}
527 527
 
@@ -544,7 +544,7 @@  discard block
 block discarded – undo
544 544
 	 * @throws \Exception
545 545
 	 */
546 546
 	public function getGroupFilter() {
547
-		if(!$this->checkRequirements(array('ldapHost',
547
+		if (!$this->checkRequirements(array('ldapHost',
548 548
 										   'ldapPort',
549 549
 										   'ldapBase',
550 550
 										   ))) {
@@ -552,7 +552,7 @@  discard block
 block discarded – undo
552 552
 		}
553 553
 		//make sure the use display name is set
554 554
 		$displayName = $this->configuration->ldapGroupDisplayName;
555
-		if(empty($displayName)) {
555
+		if (empty($displayName)) {
556 556
 			$d = $this->configuration->getDefaults();
557 557
 			$this->applyFind('ldap_group_display_name',
558 558
 							 $d['ldap_group_display_name']);
@@ -568,7 +568,7 @@  discard block
 block discarded – undo
568 568
 	 * @throws \Exception
569 569
 	 */
570 570
 	public function getUserListFilter() {
571
-		if(!$this->checkRequirements(array('ldapHost',
571
+		if (!$this->checkRequirements(array('ldapHost',
572 572
 										   'ldapPort',
573 573
 										   'ldapBase',
574 574
 										   ))) {
@@ -576,12 +576,12 @@  discard block
 block discarded – undo
576 576
 		}
577 577
 		//make sure the use display name is set
578 578
 		$displayName = $this->configuration->ldapUserDisplayName;
579
-		if(empty($displayName)) {
579
+		if (empty($displayName)) {
580 580
 			$d = $this->configuration->getDefaults();
581 581
 			$this->applyFind('ldap_display_name', $d['ldap_display_name']);
582 582
 		}
583 583
 		$filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
584
-		if(!$filter) {
584
+		if (!$filter) {
585 585
 			throw new \Exception('Cannot create filter');
586 586
 		}
587 587
 
@@ -594,7 +594,7 @@  discard block
 block discarded – undo
594 594
 	 * @throws \Exception
595 595
 	 */
596 596
 	public function getUserLoginFilter() {
597
-		if(!$this->checkRequirements(array('ldapHost',
597
+		if (!$this->checkRequirements(array('ldapHost',
598 598
 										   'ldapPort',
599 599
 										   'ldapBase',
600 600
 										   'ldapUserFilter',
@@ -603,7 +603,7 @@  discard block
 block discarded – undo
603 603
 		}
604 604
 
605 605
 		$filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
606
-		if(!$filter) {
606
+		if (!$filter) {
607 607
 			throw new \Exception('Cannot create filter');
608 608
 		}
609 609
 
@@ -617,7 +617,7 @@  discard block
 block discarded – undo
617 617
 	 * @throws \Exception
618 618
 	 */
619 619
 	public function testLoginName($loginName) {
620
-		if(!$this->checkRequirements(array('ldapHost',
620
+		if (!$this->checkRequirements(array('ldapHost',
621 621
 			'ldapPort',
622 622
 			'ldapBase',
623 623
 			'ldapLoginFilter',
@@ -626,17 +626,17 @@  discard block
 block discarded – undo
626 626
 		}
627 627
 
628 628
 		$cr = $this->access->connection->getConnectionResource();
629
-		if(!$this->ldap->isResource($cr)) {
629
+		if (!$this->ldap->isResource($cr)) {
630 630
 			throw new \Exception('connection error');
631 631
 		}
632 632
 
633
-		if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
633
+		if (mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
634 634
 			=== false) {
635 635
 			throw new \Exception('missing placeholder');
636 636
 		}
637 637
 
638 638
 		$users = $this->access->countUsersByLoginName($loginName);
639
-		if($this->ldap->errno($cr) !== 0) {
639
+		if ($this->ldap->errno($cr) !== 0) {
640 640
 			throw new \Exception($this->ldap->error($cr));
641 641
 		}
642 642
 		$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
@@ -651,22 +651,22 @@  discard block
 block discarded – undo
651 651
 	 * @throws \Exception
652 652
 	 */
653 653
 	public function guessPortAndTLS() {
654
-		if(!$this->checkRequirements(array('ldapHost',
654
+		if (!$this->checkRequirements(array('ldapHost',
655 655
 										   ))) {
656 656
 			return false;
657 657
 		}
658 658
 		$this->checkHost();
659 659
 		$portSettings = $this->getPortSettingsToTry();
660 660
 
661
-		if(!is_array($portSettings)) {
661
+		if (!is_array($portSettings)) {
662 662
 			throw new \Exception(print_r($portSettings, true));
663 663
 		}
664 664
 
665 665
 		//proceed from the best configuration and return on first success
666
-		foreach($portSettings as $setting) {
666
+		foreach ($portSettings as $setting) {
667 667
 			$p = $setting['port'];
668 668
 			$t = $setting['tls'];
669
-			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
669
+			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '.$p.', TLS '.$t, \OCP\Util::DEBUG);
670 670
 			//connectAndBind may throw Exception, it needs to be catched by the
671 671
 			//callee of this method
672 672
 
@@ -676,7 +676,7 @@  discard block
 block discarded – undo
676 676
 				// any reply other than -1 (= cannot connect) is already okay,
677 677
 				// because then we found the server
678 678
 				// unavailable startTLS returns -11
679
-				if($e->getCode() > 0) {
679
+				if ($e->getCode() > 0) {
680 680
 					$settingsFound = true;
681 681
 				} else {
682 682
 					throw $e;
@@ -689,7 +689,7 @@  discard block
 block discarded – undo
689 689
 					'ldapTLS' => intval($t)
690 690
 				);
691 691
 				$this->configuration->setConfiguration($config);
692
-				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
692
+				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port '.$p, \OCP\Util::DEBUG);
693 693
 				$this->result->addChange('ldap_port', $p);
694 694
 				return $this->result;
695 695
 			}
@@ -704,7 +704,7 @@  discard block
 block discarded – undo
704 704
 	 * @return WizardResult|false WizardResult on success, false otherwise
705 705
 	 */
706 706
 	public function guessBaseDN() {
707
-		if(!$this->checkRequirements(array('ldapHost',
707
+		if (!$this->checkRequirements(array('ldapHost',
708 708
 										   'ldapPort',
709 709
 										   ))) {
710 710
 			return false;
@@ -713,9 +713,9 @@  discard block
 block discarded – undo
713 713
 		//check whether a DN is given in the agent name (99.9% of all cases)
714 714
 		$base = null;
715 715
 		$i = stripos($this->configuration->ldapAgentName, 'dc=');
716
-		if($i !== false) {
716
+		if ($i !== false) {
717 717
 			$base = substr($this->configuration->ldapAgentName, $i);
718
-			if($this->testBaseDN($base)) {
718
+			if ($this->testBaseDN($base)) {
719 719
 				$this->applyFind('ldap_base', $base);
720 720
 				return $this->result;
721 721
 			}
@@ -726,13 +726,13 @@  discard block
 block discarded – undo
726 726
 		//a base DN
727 727
 		$helper = new Helper();
728 728
 		$domain = $helper->getDomainFromURL($this->configuration->ldapHost);
729
-		if(!$domain) {
729
+		if (!$domain) {
730 730
 			return false;
731 731
 		}
732 732
 
733 733
 		$dparts = explode('.', $domain);
734
-		while(count($dparts) > 0) {
735
-			$base2 = 'dc=' . implode(',dc=', $dparts);
734
+		while (count($dparts) > 0) {
735
+			$base2 = 'dc='.implode(',dc=', $dparts);
736 736
 			if ($base !== $base2 && $this->testBaseDN($base2)) {
737 737
 				$this->applyFind('ldap_base', $base2);
738 738
 				return $this->result;
@@ -765,7 +765,7 @@  discard block
 block discarded – undo
765 765
 		$hostInfo = parse_url($host);
766 766
 
767 767
 		//removes Port from Host
768
-		if(is_array($hostInfo) && isset($hostInfo['port'])) {
768
+		if (is_array($hostInfo) && isset($hostInfo['port'])) {
769 769
 			$port = $hostInfo['port'];
770 770
 			$host = str_replace(':'.$port, '', $host);
771 771
 			$this->applyFind('ldap_host', $host);
@@ -782,30 +782,30 @@  discard block
 block discarded – undo
782 782
 	private function detectGroupMemberAssoc() {
783 783
 		$possibleAttrs = array('uniqueMember', 'memberUid', 'member');
784 784
 		$filter = $this->configuration->ldapGroupFilter;
785
-		if(empty($filter)) {
785
+		if (empty($filter)) {
786 786
 			return false;
787 787
 		}
788 788
 		$cr = $this->getConnection();
789
-		if(!$cr) {
789
+		if (!$cr) {
790 790
 			throw new \Exception('Could not connect to LDAP');
791 791
 		}
792 792
 		$base = $this->configuration->ldapBase[0];
793 793
 		$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
794
-		if(!$this->ldap->isResource($rr)) {
794
+		if (!$this->ldap->isResource($rr)) {
795 795
 			return false;
796 796
 		}
797 797
 		$er = $this->ldap->firstEntry($cr, $rr);
798
-		while(is_resource($er)) {
798
+		while (is_resource($er)) {
799 799
 			$this->ldap->getDN($cr, $er);
800 800
 			$attrs = $this->ldap->getAttributes($cr, $er);
801 801
 			$result = array();
802 802
 			$possibleAttrsCount = count($possibleAttrs);
803
-			for($i = 0; $i < $possibleAttrsCount; $i++) {
804
-				if(isset($attrs[$possibleAttrs[$i]])) {
803
+			for ($i = 0; $i < $possibleAttrsCount; $i++) {
804
+				if (isset($attrs[$possibleAttrs[$i]])) {
805 805
 					$result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
806 806
 				}
807 807
 			}
808
-			if(!empty($result)) {
808
+			if (!empty($result)) {
809 809
 				natsort($result);
810 810
 				return key($result);
811 811
 			}
@@ -824,14 +824,14 @@  discard block
 block discarded – undo
824 824
 	 */
825 825
 	private function testBaseDN($base) {
826 826
 		$cr = $this->getConnection();
827
-		if(!$cr) {
827
+		if (!$cr) {
828 828
 			throw new \Exception('Could not connect to LDAP');
829 829
 		}
830 830
 
831 831
 		//base is there, let's validate it. If we search for anything, we should
832 832
 		//get a result set > 0 on a proper base
833 833
 		$rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
834
-		if(!$this->ldap->isResource($rr)) {
834
+		if (!$this->ldap->isResource($rr)) {
835 835
 			$errorNo  = $this->ldap->errno($cr);
836 836
 			$errorMsg = $this->ldap->error($cr);
837 837
 			\OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
@@ -853,11 +853,11 @@  discard block
 block discarded – undo
853 853
 	 */
854 854
 	private function testMemberOf() {
855 855
 		$cr = $this->getConnection();
856
-		if(!$cr) {
856
+		if (!$cr) {
857 857
 			throw new \Exception('Could not connect to LDAP');
858 858
 		}
859 859
 		$result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
860
-		if(is_int($result) &&  $result > 0) {
860
+		if (is_int($result) && $result > 0) {
861 861
 			return true;
862 862
 		}
863 863
 		return false;
@@ -878,40 +878,40 @@  discard block
 block discarded – undo
878 878
 			case self::LFILTER_USER_LIST:
879 879
 				$objcs = $this->configuration->ldapUserFilterObjectclass;
880 880
 				//glue objectclasses
881
-				if(is_array($objcs) && count($objcs) > 0) {
881
+				if (is_array($objcs) && count($objcs) > 0) {
882 882
 					$filter .= '(|';
883
-					foreach($objcs as $objc) {
884
-						$filter .= '(objectclass=' . $objc . ')';
883
+					foreach ($objcs as $objc) {
884
+						$filter .= '(objectclass='.$objc.')';
885 885
 					}
886 886
 					$filter .= ')';
887 887
 					$parts++;
888 888
 				}
889 889
 				//glue group memberships
890
-				if($this->configuration->hasMemberOfFilterSupport) {
890
+				if ($this->configuration->hasMemberOfFilterSupport) {
891 891
 					$cns = $this->configuration->ldapUserFilterGroups;
892
-					if(is_array($cns) && count($cns) > 0) {
892
+					if (is_array($cns) && count($cns) > 0) {
893 893
 						$filter .= '(|';
894 894
 						$cr = $this->getConnection();
895
-						if(!$cr) {
895
+						if (!$cr) {
896 896
 							throw new \Exception('Could not connect to LDAP');
897 897
 						}
898 898
 						$base = $this->configuration->ldapBase[0];
899
-						foreach($cns as $cn) {
900
-							$rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
901
-							if(!$this->ldap->isResource($rr)) {
899
+						foreach ($cns as $cn) {
900
+							$rr = $this->ldap->search($cr, $base, 'cn='.$cn, array('dn', 'primaryGroupToken'));
901
+							if (!$this->ldap->isResource($rr)) {
902 902
 								continue;
903 903
 							}
904 904
 							$er = $this->ldap->firstEntry($cr, $rr);
905 905
 							$attrs = $this->ldap->getAttributes($cr, $er);
906 906
 							$dn = $this->ldap->getDN($cr, $er);
907
-							if(empty($dn)) {
907
+							if (empty($dn)) {
908 908
 								continue;
909 909
 							}
910
-							$filterPart = '(memberof=' . $dn . ')';
911
-							if(isset($attrs['primaryGroupToken'])) {
910
+							$filterPart = '(memberof='.$dn.')';
911
+							if (isset($attrs['primaryGroupToken'])) {
912 912
 								$pgt = $attrs['primaryGroupToken'][0];
913
-								$primaryFilterPart = '(primaryGroupID=' . $pgt .')';
914
-								$filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
913
+								$primaryFilterPart = '(primaryGroupID='.$pgt.')';
914
+								$filterPart = '(|'.$filterPart.$primaryFilterPart.')';
915 915
 							}
916 916
 							$filter .= $filterPart;
917 917
 						}
@@ -920,10 +920,10 @@  discard block
 block discarded – undo
920 920
 					$parts++;
921 921
 				}
922 922
 				//wrap parts in AND condition
923
-				if($parts > 1) {
924
-					$filter = '(&' . $filter . ')';
923
+				if ($parts > 1) {
924
+					$filter = '(&'.$filter.')';
925 925
 				}
926
-				if(empty($filter)) {
926
+				if (empty($filter)) {
927 927
 					$filter = '(objectclass=*)';
928 928
 				}
929 929
 				break;
@@ -931,28 +931,28 @@  discard block
 block discarded – undo
931 931
 			case self::LFILTER_GROUP_LIST:
932 932
 				$objcs = $this->configuration->ldapGroupFilterObjectclass;
933 933
 				//glue objectclasses
934
-				if(is_array($objcs) && count($objcs) > 0) {
934
+				if (is_array($objcs) && count($objcs) > 0) {
935 935
 					$filter .= '(|';
936
-					foreach($objcs as $objc) {
937
-						$filter .= '(objectclass=' . $objc . ')';
936
+					foreach ($objcs as $objc) {
937
+						$filter .= '(objectclass='.$objc.')';
938 938
 					}
939 939
 					$filter .= ')';
940 940
 					$parts++;
941 941
 				}
942 942
 				//glue group memberships
943 943
 				$cns = $this->configuration->ldapGroupFilterGroups;
944
-				if(is_array($cns) && count($cns) > 0) {
944
+				if (is_array($cns) && count($cns) > 0) {
945 945
 					$filter .= '(|';
946 946
 					$base = $this->configuration->ldapBase[0];
947
-					foreach($cns as $cn) {
948
-						$filter .= '(cn=' . $cn . ')';
947
+					foreach ($cns as $cn) {
948
+						$filter .= '(cn='.$cn.')';
949 949
 					}
950 950
 					$filter .= ')';
951 951
 				}
952 952
 				$parts++;
953 953
 				//wrap parts in AND condition
954
-				if($parts > 1) {
955
-					$filter = '(&' . $filter . ')';
954
+				if ($parts > 1) {
955
+					$filter = '(&'.$filter.')';
956 956
 				}
957 957
 				break;
958 958
 
@@ -964,47 +964,47 @@  discard block
 block discarded – undo
964 964
 				$userAttributes = array_change_key_case(array_flip($userAttributes));
965 965
 				$parts = 0;
966 966
 
967
-				if($this->configuration->ldapLoginFilterUsername === '1') {
967
+				if ($this->configuration->ldapLoginFilterUsername === '1') {
968 968
 					$attr = '';
969
-					if(isset($userAttributes['uid'])) {
969
+					if (isset($userAttributes['uid'])) {
970 970
 						$attr = 'uid';
971
-					} else if(isset($userAttributes['samaccountname'])) {
971
+					} else if (isset($userAttributes['samaccountname'])) {
972 972
 						$attr = 'samaccountname';
973
-					} else if(isset($userAttributes['cn'])) {
973
+					} else if (isset($userAttributes['cn'])) {
974 974
 						//fallback
975 975
 						$attr = 'cn';
976 976
 					}
977
-					if(!empty($attr)) {
978
-						$filterUsername = '(' . $attr . $loginpart . ')';
977
+					if (!empty($attr)) {
978
+						$filterUsername = '('.$attr.$loginpart.')';
979 979
 						$parts++;
980 980
 					}
981 981
 				}
982 982
 
983 983
 				$filterEmail = '';
984
-				if($this->configuration->ldapLoginFilterEmail === '1') {
984
+				if ($this->configuration->ldapLoginFilterEmail === '1') {
985 985
 					$filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
986 986
 					$parts++;
987 987
 				}
988 988
 
989 989
 				$filterAttributes = '';
990 990
 				$attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
991
-				if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
991
+				if (is_array($attrsToFilter) && count($attrsToFilter) > 0) {
992 992
 					$filterAttributes = '(|';
993
-					foreach($attrsToFilter as $attribute) {
994
-						$filterAttributes .= '(' . $attribute . $loginpart . ')';
993
+					foreach ($attrsToFilter as $attribute) {
994
+						$filterAttributes .= '('.$attribute.$loginpart.')';
995 995
 					}
996 996
 					$filterAttributes .= ')';
997 997
 					$parts++;
998 998
 				}
999 999
 
1000 1000
 				$filterLogin = '';
1001
-				if($parts > 1) {
1001
+				if ($parts > 1) {
1002 1002
 					$filterLogin = '(|';
1003 1003
 				}
1004 1004
 				$filterLogin .= $filterUsername;
1005 1005
 				$filterLogin .= $filterEmail;
1006 1006
 				$filterLogin .= $filterAttributes;
1007
-				if($parts > 1) {
1007
+				if ($parts > 1) {
1008 1008
 					$filterLogin .= ')';
1009 1009
 				}
1010 1010
 
@@ -1026,7 +1026,7 @@  discard block
 block discarded – undo
1026 1026
 	 * @throws \Exception
1027 1027
 	 */
1028 1028
 	private function connectAndBind($port = 389, $tls = false, $ncc = false) {
1029
-		if($ncc) {
1029
+		if ($ncc) {
1030 1030
 			//No certificate check
1031 1031
 			//FIXME: undo afterwards
1032 1032
 			putenv('LDAPTLS_REQCERT=never');
@@ -1036,12 +1036,12 @@  discard block
 block discarded – undo
1036 1036
 		\OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG);
1037 1037
 		$host = $this->configuration->ldapHost;
1038 1038
 		$hostInfo = parse_url($host);
1039
-		if(!$hostInfo) {
1039
+		if (!$hostInfo) {
1040 1040
 			throw new \Exception(self::$l->t('Invalid Host'));
1041 1041
 		}
1042 1042
 		\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1043 1043
 		$cr = $this->ldap->connect($host, $port);
1044
-		if(!is_resource($cr)) {
1044
+		if (!is_resource($cr)) {
1045 1045
 			throw new \Exception(self::$l->t('Invalid Host'));
1046 1046
 		}
1047 1047
 
@@ -1052,9 +1052,9 @@  discard block
 block discarded – undo
1052 1052
 		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1053 1053
 
1054 1054
 		try {
1055
-			if($tls) {
1055
+			if ($tls) {
1056 1056
 				$isTlsWorking = @$this->ldap->startTls($cr);
1057
-				if(!$isTlsWorking) {
1057
+				if (!$isTlsWorking) {
1058 1058
 					return false;
1059 1059
 				}
1060 1060
 			}
@@ -1068,20 +1068,20 @@  discard block
 block discarded – undo
1068 1068
 			$errNo = $this->ldap->errno($cr);
1069 1069
 			$error = ldap_error($cr);
1070 1070
 			$this->ldap->unbind($cr);
1071
-		} catch(ServerNotAvailableException $e) {
1071
+		} catch (ServerNotAvailableException $e) {
1072 1072
 			return false;
1073 1073
 		}
1074 1074
 
1075
-		if($login === true) {
1075
+		if ($login === true) {
1076 1076
 			$this->ldap->unbind($cr);
1077
-			if($ncc) {
1077
+			if ($ncc) {
1078 1078
 				throw new \Exception('Certificate cannot be validated.');
1079 1079
 			}
1080
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1080
+			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '.$port.' TLS '.intval($tls), \OCP\Util::DEBUG);
1081 1081
 			return true;
1082 1082
 		}
1083 1083
 
1084
-		if($errNo === -1 || ($errNo === 2 && $ncc)) {
1084
+		if ($errNo === -1 || ($errNo === 2 && $ncc)) {
1085 1085
 			//host, port or TLS wrong
1086 1086
 			return false;
1087 1087
 		} else if ($errNo === 2) {
@@ -1099,8 +1099,8 @@  discard block
 block discarded – undo
1099 1099
 		$agent = $this->configuration->ldapAgentName;
1100 1100
 		$pwd = $this->configuration->ldapAgentPassword;
1101 1101
 
1102
-		return ( (!empty($agent) && !empty($pwd))
1103
-		       || (empty($agent) &&  empty($pwd)));
1102
+		return ((!empty($agent) && !empty($pwd))
1103
+		       || (empty($agent) && empty($pwd)));
1104 1104
 	}
1105 1105
 
1106 1106
 	/**
@@ -1109,9 +1109,9 @@  discard block
 block discarded – undo
1109 1109
 	 */
1110 1110
 	private function checkRequirements($reqs) {
1111 1111
 		$this->checkAgentRequirements();
1112
-		foreach($reqs as $option) {
1112
+		foreach ($reqs as $option) {
1113 1113
 			$value = $this->configuration->$option;
1114
-			if(empty($value)) {
1114
+			if (empty($value)) {
1115 1115
 				return false;
1116 1116
 			}
1117 1117
 		}
@@ -1133,33 +1133,33 @@  discard block
 block discarded – undo
1133 1133
 		$dnRead = array();
1134 1134
 		$foundItems = array();
1135 1135
 		$maxEntries = 0;
1136
-		if(!is_array($this->configuration->ldapBase)
1136
+		if (!is_array($this->configuration->ldapBase)
1137 1137
 		   || !isset($this->configuration->ldapBase[0])) {
1138 1138
 			return false;
1139 1139
 		}
1140 1140
 		$base = $this->configuration->ldapBase[0];
1141 1141
 		$cr = $this->getConnection();
1142
-		if(!$this->ldap->isResource($cr)) {
1142
+		if (!$this->ldap->isResource($cr)) {
1143 1143
 			return false;
1144 1144
 		}
1145 1145
 		$lastFilter = null;
1146
-		if(isset($filters[count($filters)-1])) {
1147
-			$lastFilter = $filters[count($filters)-1];
1146
+		if (isset($filters[count($filters) - 1])) {
1147
+			$lastFilter = $filters[count($filters) - 1];
1148 1148
 		}
1149
-		foreach($filters as $filter) {
1150
-			if($lastFilter === $filter && count($foundItems) > 0) {
1149
+		foreach ($filters as $filter) {
1150
+			if ($lastFilter === $filter && count($foundItems) > 0) {
1151 1151
 				//skip when the filter is a wildcard and results were found
1152 1152
 				continue;
1153 1153
 			}
1154 1154
 			// 20k limit for performance and reason
1155 1155
 			$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1156
-			if(!$this->ldap->isResource($rr)) {
1156
+			if (!$this->ldap->isResource($rr)) {
1157 1157
 				continue;
1158 1158
 			}
1159 1159
 			$entries = $this->ldap->countEntries($cr, $rr);
1160 1160
 			$getEntryFunc = 'firstEntry';
1161
-			if(($entries !== false) && ($entries > 0)) {
1162
-				if(!is_null($maxF) && $entries > $maxEntries) {
1161
+			if (($entries !== false) && ($entries > 0)) {
1162
+				if (!is_null($maxF) && $entries > $maxEntries) {
1163 1163
 					$maxEntries = $entries;
1164 1164
 					$maxF = $filter;
1165 1165
 				}
@@ -1167,13 +1167,13 @@  discard block
 block discarded – undo
1167 1167
 				do {
1168 1168
 					$entry = $this->ldap->$getEntryFunc($cr, $rr);
1169 1169
 					$getEntryFunc = 'nextEntry';
1170
-					if(!$this->ldap->isResource($entry)) {
1170
+					if (!$this->ldap->isResource($entry)) {
1171 1171
 						continue 2;
1172 1172
 					}
1173 1173
 					$rr = $entry; //will be expected by nextEntry next round
1174 1174
 					$attributes = $this->ldap->getAttributes($cr, $entry);
1175 1175
 					$dn = $this->ldap->getDN($cr, $entry);
1176
-					if($dn === false || in_array($dn, $dnRead)) {
1176
+					if ($dn === false || in_array($dn, $dnRead)) {
1177 1177
 						continue;
1178 1178
 					}
1179 1179
 					$newItems = array();
@@ -1184,7 +1184,7 @@  discard block
 block discarded – undo
1184 1184
 					$foundItems = array_merge($foundItems, $newItems);
1185 1185
 					$this->resultCache[$dn][$attr] = $newItems;
1186 1186
 					$dnRead[] = $dn;
1187
-				} while(($state === self::LRESULT_PROCESSED_SKIP
1187
+				} while (($state === self::LRESULT_PROCESSED_SKIP
1188 1188
 						|| $this->ldap->isResource($entry))
1189 1189
 						&& ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1190 1190
 			}
@@ -1207,11 +1207,11 @@  discard block
 block discarded – undo
1207 1207
 	 */
1208 1208
 	private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1209 1209
 		$cr = $this->getConnection();
1210
-		if(!$cr) {
1210
+		if (!$cr) {
1211 1211
 			throw new \Exception('Could not connect to LDAP');
1212 1212
 		}
1213 1213
 		$p = 'objectclass=';
1214
-		foreach($objectclasses as $key => $value) {
1214
+		foreach ($objectclasses as $key => $value) {
1215 1215
 			$objectclasses[$key] = $p.$value;
1216 1216
 		}
1217 1217
 		$maxEntryObjC = '';
@@ -1223,7 +1223,7 @@  discard block
 block discarded – undo
1223 1223
 		$availableFeatures =
1224 1224
 			$this->cumulativeSearchOnAttribute($objectclasses, $attr,
1225 1225
 											   $dig, $maxEntryObjC);
1226
-		if(is_array($availableFeatures)
1226
+		if (is_array($availableFeatures)
1227 1227
 		   && count($availableFeatures) > 0) {
1228 1228
 			natcasesort($availableFeatures);
1229 1229
 			//natcasesort keeps indices, but we must get rid of them for proper
@@ -1234,10 +1234,10 @@  discard block
 block discarded – undo
1234 1234
 		}
1235 1235
 
1236 1236
 		$setFeatures = $this->configuration->$confkey;
1237
-		if(is_array($setFeatures) && !empty($setFeatures)) {
1237
+		if (is_array($setFeatures) && !empty($setFeatures)) {
1238 1238
 			//something is already configured? pre-select it.
1239 1239
 			$this->result->addChange($dbkey, $setFeatures);
1240
-		} else if($po && !empty($maxEntryObjC)) {
1240
+		} else if ($po && !empty($maxEntryObjC)) {
1241 1241
 			//pre-select objectclass with most result entries
1242 1242
 			$maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1243 1243
 			$this->applyFind($dbkey, $maxEntryObjC);
@@ -1256,7 +1256,7 @@  discard block
 block discarded – undo
1256 1256
 	 * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1257 1257
 	 */
1258 1258
 	private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1259
-		if(!is_array($result)
1259
+		if (!is_array($result)
1260 1260
 		   || !isset($result['count'])
1261 1261
 		   || !$result['count'] > 0) {
1262 1262
 			return self::LRESULT_PROCESSED_INVALID;
@@ -1265,12 +1265,12 @@  discard block
 block discarded – undo
1265 1265
 		// strtolower on all keys for proper comparison
1266 1266
 		$result = \OCP\Util::mb_array_change_key_case($result);
1267 1267
 		$attribute = strtolower($attribute);
1268
-		if(isset($result[$attribute])) {
1269
-			foreach($result[$attribute] as $key => $val) {
1270
-				if($key === 'count') {
1268
+		if (isset($result[$attribute])) {
1269
+			foreach ($result[$attribute] as $key => $val) {
1270
+				if ($key === 'count') {
1271 1271
 					continue;
1272 1272
 				}
1273
-				if(!in_array($val, $known)) {
1273
+				if (!in_array($val, $known)) {
1274 1274
 					$known[] = $val;
1275 1275
 				}
1276 1276
 			}
@@ -1284,7 +1284,7 @@  discard block
 block discarded – undo
1284 1284
 	 * @return bool|mixed
1285 1285
 	 */
1286 1286
 	private function getConnection() {
1287
-		if(!is_null($this->cr)) {
1287
+		if (!is_null($this->cr)) {
1288 1288
 			return $this->cr;
1289 1289
 		}
1290 1290
 
@@ -1296,14 +1296,14 @@  discard block
 block discarded – undo
1296 1296
 		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1297 1297
 		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1298 1298
 		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1299
-		if($this->configuration->ldapTLS === 1) {
1299
+		if ($this->configuration->ldapTLS === 1) {
1300 1300
 			$this->ldap->startTls($cr);
1301 1301
 		}
1302 1302
 
1303 1303
 		$lo = @$this->ldap->bind($cr,
1304 1304
 								 $this->configuration->ldapAgentName,
1305 1305
 								 $this->configuration->ldapAgentPassword);
1306
-		if($lo === true) {
1306
+		if ($lo === true) {
1307 1307
 			$this->$cr = $cr;
1308 1308
 			return $cr;
1309 1309
 		}
@@ -1338,14 +1338,14 @@  discard block
 block discarded – undo
1338 1338
 		$portSettings = array();
1339 1339
 
1340 1340
 		//In case the port is already provided, we will check this first
1341
-		if($port > 0) {
1341
+		if ($port > 0) {
1342 1342
 			$hostInfo = parse_url($host);
1343
-			if(!(is_array($hostInfo)
1343
+			if (!(is_array($hostInfo)
1344 1344
 				&& isset($hostInfo['scheme'])
1345 1345
 				&& stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1346 1346
 				$portSettings[] = array('port' => $port, 'tls' => true);
1347 1347
 			}
1348
-			$portSettings[] =array('port' => $port, 'tls' => false);
1348
+			$portSettings[] = array('port' => $port, 'tls' => false);
1349 1349
 		}
1350 1350
 
1351 1351
 		//default ports
Please login to merge, or discard this patch.
lib/private/Files/Cache/Wrapper/CacheJail.php 3 patches
Doc Comments   +4 added lines, -1 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 
72 72
 	/**
73 73
 	 * @param array $entry
74
-	 * @return array
74
+	 * @return string
75 75
 	 */
76 76
 	protected function formatCacheEntry($entry) {
77 77
 		if (isset($entry['path'])) {
@@ -190,6 +190,9 @@  discard block
 block discarded – undo
190 190
 		return $this->cache->getStatus($this->getSourcePath($file));
191 191
 	}
192 192
 
193
+	/**
194
+	 * @param \OCP\Files\Cache\ICacheEntry[] $results
195
+	 */
193 196
 	private function formatSearchResults($results) {
194 197
 		$results = array_filter($results, array($this, 'filterCacheEntry'));
195 198
 		$results = array_values($results);
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
 		if ($path === '') {
52 52
 			return $this->root;
53 53
 		} else {
54
-			return $this->root . '/' . ltrim($path, '/');
54
+			return $this->root.'/'.ltrim($path, '/');
55 55
 		}
56 56
 	}
57 57
 
@@ -66,7 +66,7 @@  discard block
 block discarded – undo
66 66
 		$rootLength = strlen($this->root) + 1;
67 67
 		if ($path === $this->root) {
68 68
 			return '';
69
-		} else if (substr($path, 0, $rootLength) === $this->root . '/') {
69
+		} else if (substr($path, 0, $rootLength) === $this->root.'/') {
70 70
 			return substr($path, $rootLength);
71 71
 		} else {
72 72
 			return null;
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 
87 87
 	protected function filterCacheEntry($entry) {
88 88
 		$rootLength = strlen($this->root) + 1;
89
-		return ($entry['path'] === $this->root) or (substr($entry['path'], 0, $rootLength) === $this->root . '/');
89
+		return ($entry['path'] === $this->root) or (substr($entry['path'], 0, $rootLength) === $this->root.'/');
90 90
 	}
91 91
 
92 92
 	/**
Please login to merge, or discard this patch.
Indentation   +253 added lines, -253 removed lines patch added patch discarded remove patch
@@ -32,283 +32,283 @@
 block discarded – undo
32 32
  * Jail to a subdirectory of the wrapped cache
33 33
  */
34 34
 class CacheJail extends CacheWrapper {
35
-	/**
36
-	 * @var string
37
-	 */
38
-	protected $root;
35
+    /**
36
+     * @var string
37
+     */
38
+    protected $root;
39 39
 
40
-	/**
41
-	 * @param \OCP\Files\Cache\ICache $cache
42
-	 * @param string $root
43
-	 */
44
-	public function __construct($cache, $root) {
45
-		parent::__construct($cache);
46
-		$this->root = $root;
47
-	}
40
+    /**
41
+     * @param \OCP\Files\Cache\ICache $cache
42
+     * @param string $root
43
+     */
44
+    public function __construct($cache, $root) {
45
+        parent::__construct($cache);
46
+        $this->root = $root;
47
+    }
48 48
 
49
-	protected function getSourcePath($path) {
50
-		if ($path === '') {
51
-			return $this->root;
52
-		} else {
53
-			return $this->root . '/' . ltrim($path, '/');
54
-		}
55
-	}
49
+    protected function getSourcePath($path) {
50
+        if ($path === '') {
51
+            return $this->root;
52
+        } else {
53
+            return $this->root . '/' . ltrim($path, '/');
54
+        }
55
+    }
56 56
 
57
-	/**
58
-	 * @param string $path
59
-	 * @return null|string the jailed path or null if the path is outside the jail
60
-	 */
61
-	protected function getJailedPath($path) {
62
-		if ($this->root === '') {
63
-			return $path;
64
-		}
65
-		$rootLength = strlen($this->root) + 1;
66
-		if ($path === $this->root) {
67
-			return '';
68
-		} else if (substr($path, 0, $rootLength) === $this->root . '/') {
69
-			return substr($path, $rootLength);
70
-		} else {
71
-			return null;
72
-		}
73
-	}
57
+    /**
58
+     * @param string $path
59
+     * @return null|string the jailed path or null if the path is outside the jail
60
+     */
61
+    protected function getJailedPath($path) {
62
+        if ($this->root === '') {
63
+            return $path;
64
+        }
65
+        $rootLength = strlen($this->root) + 1;
66
+        if ($path === $this->root) {
67
+            return '';
68
+        } else if (substr($path, 0, $rootLength) === $this->root . '/') {
69
+            return substr($path, $rootLength);
70
+        } else {
71
+            return null;
72
+        }
73
+    }
74 74
 
75
-	/**
76
-	 * @param array $entry
77
-	 * @return array
78
-	 */
79
-	protected function formatCacheEntry($entry) {
80
-		if (isset($entry['path'])) {
81
-			$entry['path'] = $this->getJailedPath($entry['path']);
82
-		}
83
-		return $entry;
84
-	}
75
+    /**
76
+     * @param array $entry
77
+     * @return array
78
+     */
79
+    protected function formatCacheEntry($entry) {
80
+        if (isset($entry['path'])) {
81
+            $entry['path'] = $this->getJailedPath($entry['path']);
82
+        }
83
+        return $entry;
84
+    }
85 85
 
86
-	protected function filterCacheEntry($entry) {
87
-		$rootLength = strlen($this->root) + 1;
88
-		return ($entry['path'] === $this->root) or (substr($entry['path'], 0, $rootLength) === $this->root . '/');
89
-	}
86
+    protected function filterCacheEntry($entry) {
87
+        $rootLength = strlen($this->root) + 1;
88
+        return ($entry['path'] === $this->root) or (substr($entry['path'], 0, $rootLength) === $this->root . '/');
89
+    }
90 90
 
91
-	/**
92
-	 * get the stored metadata of a file or folder
93
-	 *
94
-	 * @param string /int $file
95
-	 * @return array|false
96
-	 */
97
-	public function get($file) {
98
-		if (is_string($file) or $file == '') {
99
-			$file = $this->getSourcePath($file);
100
-		}
101
-		return parent::get($file);
102
-	}
91
+    /**
92
+     * get the stored metadata of a file or folder
93
+     *
94
+     * @param string /int $file
95
+     * @return array|false
96
+     */
97
+    public function get($file) {
98
+        if (is_string($file) or $file == '') {
99
+            $file = $this->getSourcePath($file);
100
+        }
101
+        return parent::get($file);
102
+    }
103 103
 
104
-	/**
105
-	 * insert meta data for a new file or folder
106
-	 *
107
-	 * @param string $file
108
-	 * @param array $data
109
-	 *
110
-	 * @return int file id
111
-	 * @throws \RuntimeException
112
-	 */
113
-	public function insert($file, array $data) {
114
-		return $this->cache->insert($this->getSourcePath($file), $data);
115
-	}
104
+    /**
105
+     * insert meta data for a new file or folder
106
+     *
107
+     * @param string $file
108
+     * @param array $data
109
+     *
110
+     * @return int file id
111
+     * @throws \RuntimeException
112
+     */
113
+    public function insert($file, array $data) {
114
+        return $this->cache->insert($this->getSourcePath($file), $data);
115
+    }
116 116
 
117
-	/**
118
-	 * update the metadata in the cache
119
-	 *
120
-	 * @param int $id
121
-	 * @param array $data
122
-	 */
123
-	public function update($id, array $data) {
124
-		$this->cache->update($id, $data);
125
-	}
117
+    /**
118
+     * update the metadata in the cache
119
+     *
120
+     * @param int $id
121
+     * @param array $data
122
+     */
123
+    public function update($id, array $data) {
124
+        $this->cache->update($id, $data);
125
+    }
126 126
 
127
-	/**
128
-	 * get the file id for a file
129
-	 *
130
-	 * @param string $file
131
-	 * @return int
132
-	 */
133
-	public function getId($file) {
134
-		return $this->cache->getId($this->getSourcePath($file));
135
-	}
127
+    /**
128
+     * get the file id for a file
129
+     *
130
+     * @param string $file
131
+     * @return int
132
+     */
133
+    public function getId($file) {
134
+        return $this->cache->getId($this->getSourcePath($file));
135
+    }
136 136
 
137
-	/**
138
-	 * get the id of the parent folder of a file
139
-	 *
140
-	 * @param string $file
141
-	 * @return int
142
-	 */
143
-	public function getParentId($file) {
144
-		if ($file === '') {
145
-			return -1;
146
-		} else {
147
-			return $this->cache->getParentId($this->getSourcePath($file));
148
-		}
149
-	}
137
+    /**
138
+     * get the id of the parent folder of a file
139
+     *
140
+     * @param string $file
141
+     * @return int
142
+     */
143
+    public function getParentId($file) {
144
+        if ($file === '') {
145
+            return -1;
146
+        } else {
147
+            return $this->cache->getParentId($this->getSourcePath($file));
148
+        }
149
+    }
150 150
 
151
-	/**
152
-	 * check if a file is available in the cache
153
-	 *
154
-	 * @param string $file
155
-	 * @return bool
156
-	 */
157
-	public function inCache($file) {
158
-		return $this->cache->inCache($this->getSourcePath($file));
159
-	}
151
+    /**
152
+     * check if a file is available in the cache
153
+     *
154
+     * @param string $file
155
+     * @return bool
156
+     */
157
+    public function inCache($file) {
158
+        return $this->cache->inCache($this->getSourcePath($file));
159
+    }
160 160
 
161
-	/**
162
-	 * remove a file or folder from the cache
163
-	 *
164
-	 * @param string $file
165
-	 */
166
-	public function remove($file) {
167
-		$this->cache->remove($this->getSourcePath($file));
168
-	}
161
+    /**
162
+     * remove a file or folder from the cache
163
+     *
164
+     * @param string $file
165
+     */
166
+    public function remove($file) {
167
+        $this->cache->remove($this->getSourcePath($file));
168
+    }
169 169
 
170
-	/**
171
-	 * Move a file or folder in the cache
172
-	 *
173
-	 * @param string $source
174
-	 * @param string $target
175
-	 */
176
-	public function move($source, $target) {
177
-		$this->cache->move($this->getSourcePath($source), $this->getSourcePath($target));
178
-	}
170
+    /**
171
+     * Move a file or folder in the cache
172
+     *
173
+     * @param string $source
174
+     * @param string $target
175
+     */
176
+    public function move($source, $target) {
177
+        $this->cache->move($this->getSourcePath($source), $this->getSourcePath($target));
178
+    }
179 179
 
180
-	/**
181
-	 * remove all entries for files that are stored on the storage from the cache
182
-	 */
183
-	public function clear() {
184
-		$this->cache->remove($this->root);
185
-	}
180
+    /**
181
+     * remove all entries for files that are stored on the storage from the cache
182
+     */
183
+    public function clear() {
184
+        $this->cache->remove($this->root);
185
+    }
186 186
 
187
-	/**
188
-	 * @param string $file
189
-	 *
190
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
191
-	 */
192
-	public function getStatus($file) {
193
-		return $this->cache->getStatus($this->getSourcePath($file));
194
-	}
187
+    /**
188
+     * @param string $file
189
+     *
190
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
191
+     */
192
+    public function getStatus($file) {
193
+        return $this->cache->getStatus($this->getSourcePath($file));
194
+    }
195 195
 
196
-	private function formatSearchResults($results) {
197
-		$results = array_filter($results, array($this, 'filterCacheEntry'));
198
-		$results = array_values($results);
199
-		return array_map(array($this, 'formatCacheEntry'), $results);
200
-	}
196
+    private function formatSearchResults($results) {
197
+        $results = array_filter($results, array($this, 'filterCacheEntry'));
198
+        $results = array_values($results);
199
+        return array_map(array($this, 'formatCacheEntry'), $results);
200
+    }
201 201
 
202
-	/**
203
-	 * search for files matching $pattern
204
-	 *
205
-	 * @param string $pattern
206
-	 * @return array an array of file data
207
-	 */
208
-	public function search($pattern) {
209
-		$results = $this->cache->search($pattern);
210
-		return $this->formatSearchResults($results);
211
-	}
202
+    /**
203
+     * search for files matching $pattern
204
+     *
205
+     * @param string $pattern
206
+     * @return array an array of file data
207
+     */
208
+    public function search($pattern) {
209
+        $results = $this->cache->search($pattern);
210
+        return $this->formatSearchResults($results);
211
+    }
212 212
 
213
-	/**
214
-	 * search for files by mimetype
215
-	 *
216
-	 * @param string $mimetype
217
-	 * @return array
218
-	 */
219
-	public function searchByMime($mimetype) {
220
-		$results = $this->cache->searchByMime($mimetype);
221
-		return $this->formatSearchResults($results);
222
-	}
213
+    /**
214
+     * search for files by mimetype
215
+     *
216
+     * @param string $mimetype
217
+     * @return array
218
+     */
219
+    public function searchByMime($mimetype) {
220
+        $results = $this->cache->searchByMime($mimetype);
221
+        return $this->formatSearchResults($results);
222
+    }
223 223
 
224
-	/**
225
-	 * search for files by mimetype
226
-	 *
227
-	 * @param string|int $tag name or tag id
228
-	 * @param string $userId owner of the tags
229
-	 * @return array
230
-	 */
231
-	public function searchByTag($tag, $userId) {
232
-		$results = $this->cache->searchByTag($tag, $userId);
233
-		return $this->formatSearchResults($results);
234
-	}
224
+    /**
225
+     * search for files by mimetype
226
+     *
227
+     * @param string|int $tag name or tag id
228
+     * @param string $userId owner of the tags
229
+     * @return array
230
+     */
231
+    public function searchByTag($tag, $userId) {
232
+        $results = $this->cache->searchByTag($tag, $userId);
233
+        return $this->formatSearchResults($results);
234
+    }
235 235
 
236
-	/**
237
-	 * update the folder size and the size of all parent folders
238
-	 *
239
-	 * @param string|boolean $path
240
-	 * @param array $data (optional) meta data of the folder
241
-	 */
242
-	public function correctFolderSize($path, $data = null) {
243
-		if ($this->cache instanceof Cache) {
244
-			$this->cache->correctFolderSize($this->getSourcePath($path), $data);
245
-		}
246
-	}
236
+    /**
237
+     * update the folder size and the size of all parent folders
238
+     *
239
+     * @param string|boolean $path
240
+     * @param array $data (optional) meta data of the folder
241
+     */
242
+    public function correctFolderSize($path, $data = null) {
243
+        if ($this->cache instanceof Cache) {
244
+            $this->cache->correctFolderSize($this->getSourcePath($path), $data);
245
+        }
246
+    }
247 247
 
248
-	/**
249
-	 * get the size of a folder and set it in the cache
250
-	 *
251
-	 * @param string $path
252
-	 * @param array $entry (optional) meta data of the folder
253
-	 * @return int
254
-	 */
255
-	public function calculateFolderSize($path, $entry = null) {
256
-		if ($this->cache instanceof Cache) {
257
-			return $this->cache->calculateFolderSize($this->getSourcePath($path), $entry);
258
-		} else {
259
-			return 0;
260
-		}
248
+    /**
249
+     * get the size of a folder and set it in the cache
250
+     *
251
+     * @param string $path
252
+     * @param array $entry (optional) meta data of the folder
253
+     * @return int
254
+     */
255
+    public function calculateFolderSize($path, $entry = null) {
256
+        if ($this->cache instanceof Cache) {
257
+            return $this->cache->calculateFolderSize($this->getSourcePath($path), $entry);
258
+        } else {
259
+            return 0;
260
+        }
261 261
 
262
-	}
262
+    }
263 263
 
264
-	/**
265
-	 * get all file ids on the files on the storage
266
-	 *
267
-	 * @return int[]
268
-	 */
269
-	public function getAll() {
270
-		// not supported
271
-		return array();
272
-	}
264
+    /**
265
+     * get all file ids on the files on the storage
266
+     *
267
+     * @return int[]
268
+     */
269
+    public function getAll() {
270
+        // not supported
271
+        return array();
272
+    }
273 273
 
274
-	/**
275
-	 * find a folder in the cache which has not been fully scanned
276
-	 *
277
-	 * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
278
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
279
-	 * likely the folder where we stopped scanning previously
280
-	 *
281
-	 * @return string|bool the path of the folder or false when no folder matched
282
-	 */
283
-	public function getIncomplete() {
284
-		// not supported
285
-		return false;
286
-	}
274
+    /**
275
+     * find a folder in the cache which has not been fully scanned
276
+     *
277
+     * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
278
+     * use the one with the highest id gives the best result with the background scanner, since that is most
279
+     * likely the folder where we stopped scanning previously
280
+     *
281
+     * @return string|bool the path of the folder or false when no folder matched
282
+     */
283
+    public function getIncomplete() {
284
+        // not supported
285
+        return false;
286
+    }
287 287
 
288
-	/**
289
-	 * get the path of a file on this storage by it's id
290
-	 *
291
-	 * @param int $id
292
-	 * @return string|null
293
-	 */
294
-	public function getPathById($id) {
295
-		$path = $this->cache->getPathById($id);
296
-		return $this->getJailedPath($path);
297
-	}
288
+    /**
289
+     * get the path of a file on this storage by it's id
290
+     *
291
+     * @param int $id
292
+     * @return string|null
293
+     */
294
+    public function getPathById($id) {
295
+        $path = $this->cache->getPathById($id);
296
+        return $this->getJailedPath($path);
297
+    }
298 298
 
299
-	/**
300
-	 * Move a file or folder in the cache
301
-	 *
302
-	 * Note that this should make sure the entries are removed from the source cache
303
-	 *
304
-	 * @param \OCP\Files\Cache\ICache $sourceCache
305
-	 * @param string $sourcePath
306
-	 * @param string $targetPath
307
-	 */
308
-	public function moveFromCache(\OCP\Files\Cache\ICache $sourceCache, $sourcePath, $targetPath) {
309
-		if ($sourceCache === $this) {
310
-			return $this->move($sourcePath, $targetPath);
311
-		}
312
-		return $this->cache->moveFromCache($sourceCache, $sourcePath, $this->getSourcePath($targetPath));
313
-	}
299
+    /**
300
+     * Move a file or folder in the cache
301
+     *
302
+     * Note that this should make sure the entries are removed from the source cache
303
+     *
304
+     * @param \OCP\Files\Cache\ICache $sourceCache
305
+     * @param string $sourcePath
306
+     * @param string $targetPath
307
+     */
308
+    public function moveFromCache(\OCP\Files\Cache\ICache $sourceCache, $sourcePath, $targetPath) {
309
+        if ($sourceCache === $this) {
310
+            return $this->move($sourcePath, $targetPath);
311
+        }
312
+        return $this->cache->moveFromCache($sourceCache, $sourcePath, $this->getSourcePath($targetPath));
313
+    }
314 314
 }
Please login to merge, or discard this patch.
lib/private/IntegrityCheck/Helpers/FileAccessHelper.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -53,7 +53,7 @@
 block discarded – undo
53 53
 	 * Wrapper around file_put_contents($filename, $data)
54 54
 	 *
55 55
 	 * @param string $filename
56
-	 * @param $data
56
+	 * @param string $data
57 57
 	 * @return int|false
58 58
 	 */
59 59
 	public function file_put_contents($filename, $data) {
Please login to merge, or discard this patch.
Indentation   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -29,34 +29,34 @@
 block discarded – undo
29 29
  * @package OC\IntegrityCheck\Helpers
30 30
  */
31 31
 class FileAccessHelper {
32
-	/**
33
-	 * Wrapper around file_get_contents($filename, $data)
34
-	 *
35
-	 * @param string $filename
36
-	 * @return string|false
37
-	 */
38
-	public function file_get_contents($filename) {
39
-		return file_get_contents($filename);
40
-	}
32
+    /**
33
+     * Wrapper around file_get_contents($filename, $data)
34
+     *
35
+     * @param string $filename
36
+     * @return string|false
37
+     */
38
+    public function file_get_contents($filename) {
39
+        return file_get_contents($filename);
40
+    }
41 41
 
42
-	/**
43
-	 * Wrapper around file_exists($filename)
44
-	 *
45
-	 * @param string $filename
46
-	 * @return bool
47
-	 */
48
-	public function file_exists($filename) {
49
-		return file_exists($filename);
50
-	}
42
+    /**
43
+     * Wrapper around file_exists($filename)
44
+     *
45
+     * @param string $filename
46
+     * @return bool
47
+     */
48
+    public function file_exists($filename) {
49
+        return file_exists($filename);
50
+    }
51 51
 
52
-	/**
53
-	 * Wrapper around file_put_contents($filename, $data)
54
-	 *
55
-	 * @param string $filename
56
-	 * @param $data
57
-	 * @return int|false
58
-	 */
59
-	public function file_put_contents($filename, $data) {
60
-		return file_put_contents($filename, $data);
61
-	}
52
+    /**
53
+     * Wrapper around file_put_contents($filename, $data)
54
+     *
55
+     * @param string $filename
56
+     * @param $data
57
+     * @return int|false
58
+     */
59
+    public function file_put_contents($filename, $data) {
60
+        return file_put_contents($filename, $data);
61
+    }
62 62
 }
Please login to merge, or discard this patch.
lib/private/legacy/db.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -151,7 +151,6 @@
 block discarded – undo
151 151
 	/**
152 152
 	 * saves database schema to xml file
153 153
 	 * @param string $file name of file
154
-	 * @param int $mode
155 154
 	 * @return bool
156 155
 	 *
157 156
 	 * TODO: write more documentation
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -53,7 +53,7 @@  discard block
 block discarded – undo
53 53
 	 *
54 54
 	 * SQL query via Doctrine prepare(), needs to be execute()'d!
55 55
 	 */
56
-	static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) {
56
+	static public function prepare($query, $limit = null, $offset = null, $isManipulation = null) {
57 57
 		$connection = \OC::$server->getDatabaseConnection();
58 58
 
59 59
 		if ($isManipulation === null) {
@@ -63,7 +63,7 @@  discard block
 block discarded – undo
63 63
 
64 64
 		// return the result
65 65
 		try {
66
-			$result =$connection->prepare($query, $limit, $offset);
66
+			$result = $connection->prepare($query, $limit, $offset);
67 67
 		} catch (\Doctrine\DBAL\DBALException $e) {
68 68
 			throw new \OC\DatabaseException($e->getMessage(), $query);
69 69
 		}
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
 	 * @param string $sql
80 80
 	 * @return bool
81 81
 	 */
82
-	static public function isManipulation( $sql ) {
82
+	static public function isManipulation($sql) {
83 83
 		$selectOccurrence = stripos($sql, 'SELECT');
84 84
 		if ($selectOccurrence !== false && $selectOccurrence < 10) {
85 85
 			return false;
@@ -108,7 +108,7 @@  discard block
 block discarded – undo
108 108
 	 * @return OC_DB_StatementWrapper
109 109
 	 * @throws \OC\DatabaseException
110 110
 	 */
111
-	static public function executeAudited( $stmt, array $parameters = null) {
111
+	static public function executeAudited($stmt, array $parameters = null) {
112 112
 		if (is_string($stmt)) {
113 113
 			// convert to an array with 'sql'
114 114
 			if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT
@@ -121,14 +121,14 @@  discard block
 block discarded – undo
121 121
 		}
122 122
 		if (is_array($stmt)) {
123 123
 			// convert to prepared statement
124
-			if ( ! array_key_exists('sql', $stmt) ) {
124
+			if (!array_key_exists('sql', $stmt)) {
125 125
 				$message = 'statement array must at least contain key \'sql\'';
126 126
 				throw new \OC\DatabaseException($message);
127 127
 			}
128
-			if ( ! array_key_exists('limit', $stmt) ) {
128
+			if (!array_key_exists('limit', $stmt)) {
129 129
 				$stmt['limit'] = null;
130 130
 			}
131
-			if ( ! array_key_exists('limit', $stmt) ) {
131
+			if (!array_key_exists('limit', $stmt)) {
132 132
 				$stmt['offset'] = null;
133 133
 			}
134 134
 			$stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']);
@@ -139,9 +139,9 @@  discard block
 block discarded – undo
139 139
 			self::raiseExceptionOnError($result, 'Could not execute statement');
140 140
 		} else {
141 141
 			if (is_object($stmt)) {
142
-				$message = 'Expected a prepared statement or array got ' . get_class($stmt);
142
+				$message = 'Expected a prepared statement or array got '.get_class($stmt);
143 143
 			} else {
144
-				$message = 'Expected a prepared statement or array got ' . gettype($stmt);
144
+				$message = 'Expected a prepared statement or array got '.gettype($stmt);
145 145
 			}
146 146
 			throw new \OC\DatabaseException($message);
147 147
 		}
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 	 *
169 169
 	 * TODO: write more documentation
170 170
 	 */
171
-	public static function createDbFromStructure( $file ) {
171
+	public static function createDbFromStructure($file) {
172 172
 		$schemaManager = self::getMDB2SchemaManager();
173 173
 		$result = $schemaManager->createDbFromStructure($file);
174 174
 		return $result;
@@ -208,11 +208,11 @@  discard block
 block discarded – undo
208 208
 	 * @throws \OC\DatabaseException
209 209
 	 */
210 210
 	public static function raiseExceptionOnError($result, $message = null) {
211
-		if($result === false) {
211
+		if ($result === false) {
212 212
 			if ($message === null) {
213 213
 				$message = self::getErrorMessage();
214 214
 			} else {
215
-				$message .= ', Root cause:' . self::getErrorMessage();
215
+				$message .= ', Root cause:'.self::getErrorMessage();
216 216
 			}
217 217
 			throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode());
218 218
 		}
Please login to merge, or discard this patch.
Indentation   +210 added lines, -210 removed lines patch added patch discarded remove patch
@@ -33,227 +33,227 @@
 block discarded – undo
33 33
  */
34 34
 class OC_DB {
35 35
 
36
-	/**
37
-	 * get MDB2 schema manager
38
-	 *
39
-	 * @return \OC\DB\MDB2SchemaManager
40
-	 */
41
-	private static function getMDB2SchemaManager() {
42
-		return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection());
43
-	}
36
+    /**
37
+     * get MDB2 schema manager
38
+     *
39
+     * @return \OC\DB\MDB2SchemaManager
40
+     */
41
+    private static function getMDB2SchemaManager() {
42
+        return new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection());
43
+    }
44 44
 
45
-	/**
46
-	 * Prepare a SQL query
47
-	 * @param string $query Query string
48
-	 * @param int $limit
49
-	 * @param int $offset
50
-	 * @param bool $isManipulation
51
-	 * @throws \OC\DatabaseException
52
-	 * @return OC_DB_StatementWrapper prepared SQL query
53
-	 *
54
-	 * SQL query via Doctrine prepare(), needs to be execute()'d!
55
-	 */
56
-	static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) {
57
-		$connection = \OC::$server->getDatabaseConnection();
45
+    /**
46
+     * Prepare a SQL query
47
+     * @param string $query Query string
48
+     * @param int $limit
49
+     * @param int $offset
50
+     * @param bool $isManipulation
51
+     * @throws \OC\DatabaseException
52
+     * @return OC_DB_StatementWrapper prepared SQL query
53
+     *
54
+     * SQL query via Doctrine prepare(), needs to be execute()'d!
55
+     */
56
+    static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) {
57
+        $connection = \OC::$server->getDatabaseConnection();
58 58
 
59
-		if ($isManipulation === null) {
60
-			//try to guess, so we return the number of rows on manipulations
61
-			$isManipulation = self::isManipulation($query);
62
-		}
59
+        if ($isManipulation === null) {
60
+            //try to guess, so we return the number of rows on manipulations
61
+            $isManipulation = self::isManipulation($query);
62
+        }
63 63
 
64
-		// return the result
65
-		try {
66
-			$result =$connection->prepare($query, $limit, $offset);
67
-		} catch (\Doctrine\DBAL\DBALException $e) {
68
-			throw new \OC\DatabaseException($e->getMessage(), $query);
69
-		}
70
-		// differentiate between query and manipulation
71
-		$result = new OC_DB_StatementWrapper($result, $isManipulation);
72
-		return $result;
73
-	}
64
+        // return the result
65
+        try {
66
+            $result =$connection->prepare($query, $limit, $offset);
67
+        } catch (\Doctrine\DBAL\DBALException $e) {
68
+            throw new \OC\DatabaseException($e->getMessage(), $query);
69
+        }
70
+        // differentiate between query and manipulation
71
+        $result = new OC_DB_StatementWrapper($result, $isManipulation);
72
+        return $result;
73
+    }
74 74
 
75
-	/**
76
-	 * tries to guess the type of statement based on the first 10 characters
77
-	 * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements
78
-	 *
79
-	 * @param string $sql
80
-	 * @return bool
81
-	 */
82
-	static public function isManipulation( $sql ) {
83
-		$selectOccurrence = stripos($sql, 'SELECT');
84
-		if ($selectOccurrence !== false && $selectOccurrence < 10) {
85
-			return false;
86
-		}
87
-		$insertOccurrence = stripos($sql, 'INSERT');
88
-		if ($insertOccurrence !== false && $insertOccurrence < 10) {
89
-			return true;
90
-		}
91
-		$updateOccurrence = stripos($sql, 'UPDATE');
92
-		if ($updateOccurrence !== false && $updateOccurrence < 10) {
93
-			return true;
94
-		}
95
-		$deleteOccurrence = stripos($sql, 'DELETE');
96
-		if ($deleteOccurrence !== false && $deleteOccurrence < 10) {
97
-			return true;
98
-		}
99
-		return false;
100
-	}
75
+    /**
76
+     * tries to guess the type of statement based on the first 10 characters
77
+     * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements
78
+     *
79
+     * @param string $sql
80
+     * @return bool
81
+     */
82
+    static public function isManipulation( $sql ) {
83
+        $selectOccurrence = stripos($sql, 'SELECT');
84
+        if ($selectOccurrence !== false && $selectOccurrence < 10) {
85
+            return false;
86
+        }
87
+        $insertOccurrence = stripos($sql, 'INSERT');
88
+        if ($insertOccurrence !== false && $insertOccurrence < 10) {
89
+            return true;
90
+        }
91
+        $updateOccurrence = stripos($sql, 'UPDATE');
92
+        if ($updateOccurrence !== false && $updateOccurrence < 10) {
93
+            return true;
94
+        }
95
+        $deleteOccurrence = stripos($sql, 'DELETE');
96
+        if ($deleteOccurrence !== false && $deleteOccurrence < 10) {
97
+            return true;
98
+        }
99
+        return false;
100
+    }
101 101
 
102
-	/**
103
-	 * execute a prepared statement, on error write log and throw exception
104
-	 * @param mixed $stmt OC_DB_StatementWrapper,
105
-	 *					  an array with 'sql' and optionally 'limit' and 'offset' keys
106
-	 *					.. or a simple sql query string
107
-	 * @param array $parameters
108
-	 * @return OC_DB_StatementWrapper
109
-	 * @throws \OC\DatabaseException
110
-	 */
111
-	static public function executeAudited( $stmt, array $parameters = null) {
112
-		if (is_string($stmt)) {
113
-			// convert to an array with 'sql'
114
-			if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT
115
-				// TODO try to convert LIMIT OFFSET notation to parameters
116
-				$message = 'LIMIT and OFFSET are forbidden for portability reasons,'
117
-						 . ' pass an array with \'limit\' and \'offset\' instead';
118
-				throw new \OC\DatabaseException($message);
119
-			}
120
-			$stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null);
121
-		}
122
-		if (is_array($stmt)) {
123
-			// convert to prepared statement
124
-			if ( ! array_key_exists('sql', $stmt) ) {
125
-				$message = 'statement array must at least contain key \'sql\'';
126
-				throw new \OC\DatabaseException($message);
127
-			}
128
-			if ( ! array_key_exists('limit', $stmt) ) {
129
-				$stmt['limit'] = null;
130
-			}
131
-			if ( ! array_key_exists('limit', $stmt) ) {
132
-				$stmt['offset'] = null;
133
-			}
134
-			$stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']);
135
-		}
136
-		self::raiseExceptionOnError($stmt, 'Could not prepare statement');
137
-		if ($stmt instanceof OC_DB_StatementWrapper) {
138
-			$result = $stmt->execute($parameters);
139
-			self::raiseExceptionOnError($result, 'Could not execute statement');
140
-		} else {
141
-			if (is_object($stmt)) {
142
-				$message = 'Expected a prepared statement or array got ' . get_class($stmt);
143
-			} else {
144
-				$message = 'Expected a prepared statement or array got ' . gettype($stmt);
145
-			}
146
-			throw new \OC\DatabaseException($message);
147
-		}
148
-		return $result;
149
-	}
102
+    /**
103
+     * execute a prepared statement, on error write log and throw exception
104
+     * @param mixed $stmt OC_DB_StatementWrapper,
105
+     *					  an array with 'sql' and optionally 'limit' and 'offset' keys
106
+     *					.. or a simple sql query string
107
+     * @param array $parameters
108
+     * @return OC_DB_StatementWrapper
109
+     * @throws \OC\DatabaseException
110
+     */
111
+    static public function executeAudited( $stmt, array $parameters = null) {
112
+        if (is_string($stmt)) {
113
+            // convert to an array with 'sql'
114
+            if (stripos($stmt, 'LIMIT') !== false) { //OFFSET requires LIMIT, so we only need to check for LIMIT
115
+                // TODO try to convert LIMIT OFFSET notation to parameters
116
+                $message = 'LIMIT and OFFSET are forbidden for portability reasons,'
117
+                            . ' pass an array with \'limit\' and \'offset\' instead';
118
+                throw new \OC\DatabaseException($message);
119
+            }
120
+            $stmt = array('sql' => $stmt, 'limit' => null, 'offset' => null);
121
+        }
122
+        if (is_array($stmt)) {
123
+            // convert to prepared statement
124
+            if ( ! array_key_exists('sql', $stmt) ) {
125
+                $message = 'statement array must at least contain key \'sql\'';
126
+                throw new \OC\DatabaseException($message);
127
+            }
128
+            if ( ! array_key_exists('limit', $stmt) ) {
129
+                $stmt['limit'] = null;
130
+            }
131
+            if ( ! array_key_exists('limit', $stmt) ) {
132
+                $stmt['offset'] = null;
133
+            }
134
+            $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']);
135
+        }
136
+        self::raiseExceptionOnError($stmt, 'Could not prepare statement');
137
+        if ($stmt instanceof OC_DB_StatementWrapper) {
138
+            $result = $stmt->execute($parameters);
139
+            self::raiseExceptionOnError($result, 'Could not execute statement');
140
+        } else {
141
+            if (is_object($stmt)) {
142
+                $message = 'Expected a prepared statement or array got ' . get_class($stmt);
143
+            } else {
144
+                $message = 'Expected a prepared statement or array got ' . gettype($stmt);
145
+            }
146
+            throw new \OC\DatabaseException($message);
147
+        }
148
+        return $result;
149
+    }
150 150
 
151
-	/**
152
-	 * saves database schema to xml file
153
-	 * @param string $file name of file
154
-	 * @param int $mode
155
-	 * @return bool
156
-	 *
157
-	 * TODO: write more documentation
158
-	 */
159
-	public static function getDbStructure($file) {
160
-		$schemaManager = self::getMDB2SchemaManager();
161
-		return $schemaManager->getDbStructure($file);
162
-	}
151
+    /**
152
+     * saves database schema to xml file
153
+     * @param string $file name of file
154
+     * @param int $mode
155
+     * @return bool
156
+     *
157
+     * TODO: write more documentation
158
+     */
159
+    public static function getDbStructure($file) {
160
+        $schemaManager = self::getMDB2SchemaManager();
161
+        return $schemaManager->getDbStructure($file);
162
+    }
163 163
 
164
-	/**
165
-	 * Creates tables from XML file
166
-	 * @param string $file file to read structure from
167
-	 * @return bool
168
-	 *
169
-	 * TODO: write more documentation
170
-	 */
171
-	public static function createDbFromStructure( $file ) {
172
-		$schemaManager = self::getMDB2SchemaManager();
173
-		$result = $schemaManager->createDbFromStructure($file);
174
-		return $result;
175
-	}
164
+    /**
165
+     * Creates tables from XML file
166
+     * @param string $file file to read structure from
167
+     * @return bool
168
+     *
169
+     * TODO: write more documentation
170
+     */
171
+    public static function createDbFromStructure( $file ) {
172
+        $schemaManager = self::getMDB2SchemaManager();
173
+        $result = $schemaManager->createDbFromStructure($file);
174
+        return $result;
175
+    }
176 176
 
177
-	/**
178
-	 * update the database schema
179
-	 * @param string $file file to read structure from
180
-	 * @throws Exception
181
-	 * @return string|boolean
182
-	 */
183
-	public static function updateDbFromStructure($file) {
184
-		$schemaManager = self::getMDB2SchemaManager();
185
-		try {
186
-			$result = $schemaManager->updateDbFromStructure($file);
187
-		} catch (Exception $e) {
188
-			\OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL);
189
-			throw $e;
190
-		}
191
-		return $result;
192
-	}
177
+    /**
178
+     * update the database schema
179
+     * @param string $file file to read structure from
180
+     * @throws Exception
181
+     * @return string|boolean
182
+     */
183
+    public static function updateDbFromStructure($file) {
184
+        $schemaManager = self::getMDB2SchemaManager();
185
+        try {
186
+            $result = $schemaManager->updateDbFromStructure($file);
187
+        } catch (Exception $e) {
188
+            \OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', \OCP\Util::FATAL);
189
+            throw $e;
190
+        }
191
+        return $result;
192
+    }
193 193
 
194
-	/**
195
-	 * simulate the database schema update
196
-	 * @param string $file file to read structure from
197
-	 * @throws Exception
198
-	 * @return string|boolean
199
-	 */
200
-	public static function simulateUpdateDbFromStructure($file) {
201
-		$schemaManager = self::getMDB2SchemaManager();
202
-		try {
203
-			$result = $schemaManager->simulateUpdateDbFromStructure($file);
204
-		} catch (Exception $e) {
205
-			\OCP\Util::writeLog('core', 'Simulated database structure update failed ('.$e.')', \OCP\Util::FATAL);
206
-			throw $e;
207
-		}
208
-		return $result;
209
-	}
194
+    /**
195
+     * simulate the database schema update
196
+     * @param string $file file to read structure from
197
+     * @throws Exception
198
+     * @return string|boolean
199
+     */
200
+    public static function simulateUpdateDbFromStructure($file) {
201
+        $schemaManager = self::getMDB2SchemaManager();
202
+        try {
203
+            $result = $schemaManager->simulateUpdateDbFromStructure($file);
204
+        } catch (Exception $e) {
205
+            \OCP\Util::writeLog('core', 'Simulated database structure update failed ('.$e.')', \OCP\Util::FATAL);
206
+            throw $e;
207
+        }
208
+        return $result;
209
+    }
210 210
 
211
-	/**
212
-	 * remove all tables defined in a database structure xml file
213
-	 * @param string $file the xml file describing the tables
214
-	 */
215
-	public static function removeDBStructure($file) {
216
-		$schemaManager = self::getMDB2SchemaManager();
217
-		$schemaManager->removeDBStructure($file);
218
-	}
211
+    /**
212
+     * remove all tables defined in a database structure xml file
213
+     * @param string $file the xml file describing the tables
214
+     */
215
+    public static function removeDBStructure($file) {
216
+        $schemaManager = self::getMDB2SchemaManager();
217
+        $schemaManager->removeDBStructure($file);
218
+    }
219 219
 
220
-	/**
221
-	 * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException
222
-	 * @param mixed $result
223
-	 * @param string $message
224
-	 * @return void
225
-	 * @throws \OC\DatabaseException
226
-	 */
227
-	public static function raiseExceptionOnError($result, $message = null) {
228
-		if($result === false) {
229
-			if ($message === null) {
230
-				$message = self::getErrorMessage();
231
-			} else {
232
-				$message .= ', Root cause:' . self::getErrorMessage();
233
-			}
234
-			throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode());
235
-		}
236
-	}
220
+    /**
221
+     * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException
222
+     * @param mixed $result
223
+     * @param string $message
224
+     * @return void
225
+     * @throws \OC\DatabaseException
226
+     */
227
+    public static function raiseExceptionOnError($result, $message = null) {
228
+        if($result === false) {
229
+            if ($message === null) {
230
+                $message = self::getErrorMessage();
231
+            } else {
232
+                $message .= ', Root cause:' . self::getErrorMessage();
233
+            }
234
+            throw new \OC\DatabaseException($message, \OC::$server->getDatabaseConnection()->errorCode());
235
+        }
236
+    }
237 237
 
238
-	/**
239
-	 * returns the error code and message as a string for logging
240
-	 * works with DoctrineException
241
-	 * @return string
242
-	 */
243
-	public static function getErrorMessage() {
244
-		$connection = \OC::$server->getDatabaseConnection();
245
-		return $connection->getError();
246
-	}
238
+    /**
239
+     * returns the error code and message as a string for logging
240
+     * works with DoctrineException
241
+     * @return string
242
+     */
243
+    public static function getErrorMessage() {
244
+        $connection = \OC::$server->getDatabaseConnection();
245
+        return $connection->getError();
246
+    }
247 247
 
248
-	/**
249
-	 * Checks if a table exists in the database - the database prefix will be prepended
250
-	 *
251
-	 * @param string $table
252
-	 * @return bool
253
-	 * @throws \OC\DatabaseException
254
-	 */
255
-	public static function tableExists($table) {
256
-		$connection = \OC::$server->getDatabaseConnection();
257
-		return $connection->tableExists($table);
258
-	}
248
+    /**
249
+     * Checks if a table exists in the database - the database prefix will be prepended
250
+     *
251
+     * @param string $table
252
+     * @return bool
253
+     * @throws \OC\DatabaseException
254
+     */
255
+    public static function tableExists($table) {
256
+        $connection = \OC::$server->getDatabaseConnection();
257
+        return $connection->tableExists($table);
258
+    }
259 259
 }
Please login to merge, or discard this patch.
lib/private/User/User.php 3 patches
Doc Comments   +4 added lines patch added patch discarded remove patch
@@ -413,6 +413,10 @@
 block discarded – undo
413 413
 		return $url;
414 414
 	}
415 415
 
416
+	/**
417
+	 * @param string $feature
418
+	 * @param string $value
419
+	 */
416 420
 	public function triggerChange($feature, $value = null) {
417 421
 		if ($this->emitter) {
418 422
 			$this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value));
Please login to merge, or discard this patch.
Indentation   +382 added lines, -382 removed lines patch added patch discarded remove patch
@@ -40,386 +40,386 @@
 block discarded – undo
40 40
 use OCP\UserInterface;
41 41
 
42 42
 class User implements IUser {
43
-	/** @var string $uid */
44
-	private $uid;
45
-
46
-	/** @var string $displayName */
47
-	private $displayName;
48
-
49
-	/** @var UserInterface $backend */
50
-	private $backend;
51
-
52
-	/** @var bool $enabled */
53
-	private $enabled;
54
-
55
-	/** @var Emitter|Manager $emitter */
56
-	private $emitter;
57
-
58
-	/** @var string $home */
59
-	private $home;
60
-
61
-	/** @var int $lastLogin */
62
-	private $lastLogin;
63
-
64
-	/** @var \OCP\IConfig $config */
65
-	private $config;
66
-
67
-	/** @var IAvatarManager */
68
-	private $avatarManager;
69
-
70
-	/** @var IURLGenerator */
71
-	private $urlGenerator;
72
-
73
-	/**
74
-	 * @param string $uid
75
-	 * @param UserInterface $backend
76
-	 * @param \OC\Hooks\Emitter $emitter
77
-	 * @param IConfig|null $config
78
-	 * @param IURLGenerator $urlGenerator
79
-	 */
80
-	public function __construct($uid, $backend, $emitter = null, IConfig $config = null, $urlGenerator = null) {
81
-		$this->uid = $uid;
82
-		$this->backend = $backend;
83
-		$this->emitter = $emitter;
84
-		if(is_null($config)) {
85
-			$config = \OC::$server->getConfig();
86
-		}
87
-		$this->config = $config;
88
-		$this->urlGenerator = $urlGenerator;
89
-		$enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
90
-		$this->enabled = ($enabled === 'true');
91
-		$this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
92
-		if (is_null($this->urlGenerator)) {
93
-			$this->urlGenerator = \OC::$server->getURLGenerator();
94
-		}
95
-	}
96
-
97
-	/**
98
-	 * get the user id
99
-	 *
100
-	 * @return string
101
-	 */
102
-	public function getUID() {
103
-		return $this->uid;
104
-	}
105
-
106
-	/**
107
-	 * get the display name for the user, if no specific display name is set it will fallback to the user id
108
-	 *
109
-	 * @return string
110
-	 */
111
-	public function getDisplayName() {
112
-		if (!isset($this->displayName)) {
113
-			$displayName = '';
114
-			if ($this->backend and $this->backend->implementsActions(\OC\User\Backend::GET_DISPLAYNAME)) {
115
-				// get display name and strip whitespace from the beginning and end of it
116
-				$backendDisplayName = $this->backend->getDisplayName($this->uid);
117
-				if (is_string($backendDisplayName)) {
118
-					$displayName = trim($backendDisplayName);
119
-				}
120
-			}
121
-
122
-			if (!empty($displayName)) {
123
-				$this->displayName = $displayName;
124
-			} else {
125
-				$this->displayName = $this->uid;
126
-			}
127
-		}
128
-		return $this->displayName;
129
-	}
130
-
131
-	/**
132
-	 * set the displayname for the user
133
-	 *
134
-	 * @param string $displayName
135
-	 * @return bool
136
-	 */
137
-	public function setDisplayName($displayName) {
138
-		$displayName = trim($displayName);
139
-		if ($this->backend->implementsActions(\OC\User\Backend::SET_DISPLAYNAME) && !empty($displayName)) {
140
-			$result = $this->backend->setDisplayName($this->uid, $displayName);
141
-			if ($result) {
142
-				$this->displayName = $displayName;
143
-				$this->triggerChange('displayName', $displayName);
144
-			}
145
-			return $result !== false;
146
-		} else {
147
-			return false;
148
-		}
149
-	}
150
-
151
-	/**
152
-	 * set the email address of the user
153
-	 *
154
-	 * @param string|null $mailAddress
155
-	 * @return void
156
-	 * @since 9.0.0
157
-	 */
158
-	public function setEMailAddress($mailAddress) {
159
-		if($mailAddress === '') {
160
-			$this->config->deleteUserValue($this->uid, 'settings', 'email');
161
-		} else {
162
-			$this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
163
-		}
164
-		$this->triggerChange('eMailAddress', $mailAddress);
165
-	}
166
-
167
-	/**
168
-	 * returns the timestamp of the user's last login or 0 if the user did never
169
-	 * login
170
-	 *
171
-	 * @return int
172
-	 */
173
-	public function getLastLogin() {
174
-		return $this->lastLogin;
175
-	}
176
-
177
-	/**
178
-	 * updates the timestamp of the most recent login of this user
179
-	 */
180
-	public function updateLastLoginTimestamp() {
181
-		$this->lastLogin = time();
182
-		\OC::$server->getConfig()->setUserValue(
183
-			$this->uid, 'login', 'lastLogin', $this->lastLogin);
184
-	}
185
-
186
-	/**
187
-	 * Delete the user
188
-	 *
189
-	 * @return bool
190
-	 */
191
-	public function delete() {
192
-		if ($this->emitter) {
193
-			$this->emitter->emit('\OC\User', 'preDelete', array($this));
194
-		}
195
-		$result = $this->backend->deleteUser($this->uid);
196
-		if ($result) {
197
-
198
-			// FIXME: Feels like an hack - suggestions?
199
-
200
-			// We have to delete the user from all groups
201
-			foreach (\OC_Group::getUserGroups($this->uid) as $i) {
202
-				\OC_Group::removeFromGroup($this->uid, $i);
203
-			}
204
-			// Delete the user's keys in preferences
205
-			\OC::$server->getConfig()->deleteAllUserValues($this->uid);
206
-
207
-			// Delete user files in /data/
208
-			\OC_Helper::rmdirr(\OC_User::getHome($this->uid));
209
-
210
-			// Delete the users entry in the storage table
211
-			\OC\Files\Cache\Storage::remove('home::' . $this->uid);
212
-
213
-			\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
214
-			\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
215
-
216
-			$notification = \OC::$server->getNotificationManager()->createNotification();
217
-			$notification->setUser($this->uid);
218
-			\OC::$server->getNotificationManager()->markProcessed($notification);
219
-
220
-			if ($this->emitter) {
221
-				$this->emitter->emit('\OC\User', 'postDelete', array($this));
222
-			}
223
-		}
224
-		return !($result === false);
225
-	}
226
-
227
-	/**
228
-	 * Set the password of the user
229
-	 *
230
-	 * @param string $password
231
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
232
-	 * @return bool
233
-	 */
234
-	public function setPassword($password, $recoveryPassword = null) {
235
-		if ($this->emitter) {
236
-			$this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword));
237
-		}
238
-		if ($this->backend->implementsActions(\OC\User\Backend::SET_PASSWORD)) {
239
-			$result = $this->backend->setPassword($this->uid, $password);
240
-			if ($this->emitter) {
241
-				$this->emitter->emit('\OC\User', 'postSetPassword', array($this, $password, $recoveryPassword));
242
-			}
243
-			return !($result === false);
244
-		} else {
245
-			return false;
246
-		}
247
-	}
248
-
249
-	/**
250
-	 * get the users home folder to mount
251
-	 *
252
-	 * @return string
253
-	 */
254
-	public function getHome() {
255
-		if (!$this->home) {
256
-			if ($this->backend->implementsActions(\OC\User\Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
257
-				$this->home = $home;
258
-			} elseif ($this->config) {
259
-				$this->home = $this->config->getSystemValue('datadirectory') . '/' . $this->uid;
260
-			} else {
261
-				$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
262
-			}
263
-		}
264
-		return $this->home;
265
-	}
266
-
267
-	/**
268
-	 * Get the name of the backend class the user is connected with
269
-	 *
270
-	 * @return string
271
-	 */
272
-	public function getBackendClassName() {
273
-		if($this->backend instanceof \OCP\IUserBackend) {
274
-			return $this->backend->getBackendName();
275
-		}
276
-		return get_class($this->backend);
277
-	}
278
-
279
-	/**
280
-	 * check if the backend allows the user to change his avatar on Personal page
281
-	 *
282
-	 * @return bool
283
-	 */
284
-	public function canChangeAvatar() {
285
-		if ($this->backend->implementsActions(\OC\User\Backend::PROVIDE_AVATAR)) {
286
-			return $this->backend->canChangeAvatar($this->uid);
287
-		}
288
-		return true;
289
-	}
290
-
291
-	/**
292
-	 * check if the backend supports changing passwords
293
-	 *
294
-	 * @return bool
295
-	 */
296
-	public function canChangePassword() {
297
-		return $this->backend->implementsActions(\OC\User\Backend::SET_PASSWORD);
298
-	}
299
-
300
-	/**
301
-	 * check if the backend supports changing display names
302
-	 *
303
-	 * @return bool
304
-	 */
305
-	public function canChangeDisplayName() {
306
-		if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
307
-			return false;
308
-		}
309
-		return $this->backend->implementsActions(\OC\User\Backend::SET_DISPLAYNAME);
310
-	}
311
-
312
-	/**
313
-	 * check if the user is enabled
314
-	 *
315
-	 * @return bool
316
-	 */
317
-	public function isEnabled() {
318
-		return $this->enabled;
319
-	}
320
-
321
-	/**
322
-	 * set the enabled status for the user
323
-	 *
324
-	 * @param bool $enabled
325
-	 */
326
-	public function setEnabled($enabled) {
327
-		$this->enabled = $enabled;
328
-		$enabled = ($enabled) ? 'true' : 'false';
329
-		$this->config->setUserValue($this->uid, 'core', 'enabled', $enabled);
330
-	}
331
-
332
-	/**
333
-	 * get the users email address
334
-	 *
335
-	 * @return string|null
336
-	 * @since 9.0.0
337
-	 */
338
-	public function getEMailAddress() {
339
-		return $this->config->getUserValue($this->uid, 'settings', 'email', null);
340
-	}
341
-
342
-	/**
343
-	 * get the users' quota
344
-	 *
345
-	 * @return string
346
-	 * @since 9.0.0
347
-	 */
348
-	public function getQuota() {
349
-		$quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
350
-		if($quota === 'default') {
351
-			$quota = $this->config->getAppValue('files', 'default_quota', 'none');
352
-		}
353
-		return $quota;
354
-	}
355
-
356
-	/**
357
-	 * set the users' quota
358
-	 *
359
-	 * @param string $quota
360
-	 * @return void
361
-	 * @since 9.0.0
362
-	 */
363
-	public function setQuota($quota) {
364
-		if($quota !== 'none' and $quota !== 'default') {
365
-			$quota = OC_Helper::computerFileSize($quota);
366
-			$quota = OC_Helper::humanFileSize($quota);
367
-		}
368
-		$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
369
-		$this->triggerChange('quota', $quota);
370
-	}
371
-
372
-	/**
373
-	 * get the avatar image if it exists
374
-	 *
375
-	 * @param int $size
376
-	 * @return IImage|null
377
-	 * @since 9.0.0
378
-	 */
379
-	public function getAvatarImage($size) {
380
-		// delay the initialization
381
-		if (is_null($this->avatarManager)) {
382
-			$this->avatarManager = \OC::$server->getAvatarManager();
383
-		}
384
-
385
-		$avatar = $this->avatarManager->getAvatar($this->uid);
386
-		$image = $avatar->get(-1);
387
-		if ($image) {
388
-			return $image;
389
-		}
390
-
391
-		return null;
392
-	}
393
-
394
-	/**
395
-	 * get the federation cloud id
396
-	 *
397
-	 * @return string
398
-	 * @since 9.0.0
399
-	 */
400
-	public function getCloudId() {
401
-		$uid = $this->getUID();
402
-		$server = $this->urlGenerator->getAbsoluteURL('/');
403
-		return $uid . '@' . rtrim( $this->removeProtocolFromUrl($server), '/');
404
-	}
405
-
406
-	/**
407
-	 * @param string $url
408
-	 * @return string
409
-	 */
410
-	private function removeProtocolFromUrl($url) {
411
-		if (strpos($url, 'https://') === 0) {
412
-			return substr($url, strlen('https://'));
413
-		} else if (strpos($url, 'http://') === 0) {
414
-			return substr($url, strlen('http://'));
415
-		}
416
-
417
-		return $url;
418
-	}
419
-
420
-	public function triggerChange($feature, $value = null) {
421
-		if ($this->emitter) {
422
-			$this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value));
423
-		}
424
-	}
43
+    /** @var string $uid */
44
+    private $uid;
45
+
46
+    /** @var string $displayName */
47
+    private $displayName;
48
+
49
+    /** @var UserInterface $backend */
50
+    private $backend;
51
+
52
+    /** @var bool $enabled */
53
+    private $enabled;
54
+
55
+    /** @var Emitter|Manager $emitter */
56
+    private $emitter;
57
+
58
+    /** @var string $home */
59
+    private $home;
60
+
61
+    /** @var int $lastLogin */
62
+    private $lastLogin;
63
+
64
+    /** @var \OCP\IConfig $config */
65
+    private $config;
66
+
67
+    /** @var IAvatarManager */
68
+    private $avatarManager;
69
+
70
+    /** @var IURLGenerator */
71
+    private $urlGenerator;
72
+
73
+    /**
74
+     * @param string $uid
75
+     * @param UserInterface $backend
76
+     * @param \OC\Hooks\Emitter $emitter
77
+     * @param IConfig|null $config
78
+     * @param IURLGenerator $urlGenerator
79
+     */
80
+    public function __construct($uid, $backend, $emitter = null, IConfig $config = null, $urlGenerator = null) {
81
+        $this->uid = $uid;
82
+        $this->backend = $backend;
83
+        $this->emitter = $emitter;
84
+        if(is_null($config)) {
85
+            $config = \OC::$server->getConfig();
86
+        }
87
+        $this->config = $config;
88
+        $this->urlGenerator = $urlGenerator;
89
+        $enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
90
+        $this->enabled = ($enabled === 'true');
91
+        $this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
92
+        if (is_null($this->urlGenerator)) {
93
+            $this->urlGenerator = \OC::$server->getURLGenerator();
94
+        }
95
+    }
96
+
97
+    /**
98
+     * get the user id
99
+     *
100
+     * @return string
101
+     */
102
+    public function getUID() {
103
+        return $this->uid;
104
+    }
105
+
106
+    /**
107
+     * get the display name for the user, if no specific display name is set it will fallback to the user id
108
+     *
109
+     * @return string
110
+     */
111
+    public function getDisplayName() {
112
+        if (!isset($this->displayName)) {
113
+            $displayName = '';
114
+            if ($this->backend and $this->backend->implementsActions(\OC\User\Backend::GET_DISPLAYNAME)) {
115
+                // get display name and strip whitespace from the beginning and end of it
116
+                $backendDisplayName = $this->backend->getDisplayName($this->uid);
117
+                if (is_string($backendDisplayName)) {
118
+                    $displayName = trim($backendDisplayName);
119
+                }
120
+            }
121
+
122
+            if (!empty($displayName)) {
123
+                $this->displayName = $displayName;
124
+            } else {
125
+                $this->displayName = $this->uid;
126
+            }
127
+        }
128
+        return $this->displayName;
129
+    }
130
+
131
+    /**
132
+     * set the displayname for the user
133
+     *
134
+     * @param string $displayName
135
+     * @return bool
136
+     */
137
+    public function setDisplayName($displayName) {
138
+        $displayName = trim($displayName);
139
+        if ($this->backend->implementsActions(\OC\User\Backend::SET_DISPLAYNAME) && !empty($displayName)) {
140
+            $result = $this->backend->setDisplayName($this->uid, $displayName);
141
+            if ($result) {
142
+                $this->displayName = $displayName;
143
+                $this->triggerChange('displayName', $displayName);
144
+            }
145
+            return $result !== false;
146
+        } else {
147
+            return false;
148
+        }
149
+    }
150
+
151
+    /**
152
+     * set the email address of the user
153
+     *
154
+     * @param string|null $mailAddress
155
+     * @return void
156
+     * @since 9.0.0
157
+     */
158
+    public function setEMailAddress($mailAddress) {
159
+        if($mailAddress === '') {
160
+            $this->config->deleteUserValue($this->uid, 'settings', 'email');
161
+        } else {
162
+            $this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
163
+        }
164
+        $this->triggerChange('eMailAddress', $mailAddress);
165
+    }
166
+
167
+    /**
168
+     * returns the timestamp of the user's last login or 0 if the user did never
169
+     * login
170
+     *
171
+     * @return int
172
+     */
173
+    public function getLastLogin() {
174
+        return $this->lastLogin;
175
+    }
176
+
177
+    /**
178
+     * updates the timestamp of the most recent login of this user
179
+     */
180
+    public function updateLastLoginTimestamp() {
181
+        $this->lastLogin = time();
182
+        \OC::$server->getConfig()->setUserValue(
183
+            $this->uid, 'login', 'lastLogin', $this->lastLogin);
184
+    }
185
+
186
+    /**
187
+     * Delete the user
188
+     *
189
+     * @return bool
190
+     */
191
+    public function delete() {
192
+        if ($this->emitter) {
193
+            $this->emitter->emit('\OC\User', 'preDelete', array($this));
194
+        }
195
+        $result = $this->backend->deleteUser($this->uid);
196
+        if ($result) {
197
+
198
+            // FIXME: Feels like an hack - suggestions?
199
+
200
+            // We have to delete the user from all groups
201
+            foreach (\OC_Group::getUserGroups($this->uid) as $i) {
202
+                \OC_Group::removeFromGroup($this->uid, $i);
203
+            }
204
+            // Delete the user's keys in preferences
205
+            \OC::$server->getConfig()->deleteAllUserValues($this->uid);
206
+
207
+            // Delete user files in /data/
208
+            \OC_Helper::rmdirr(\OC_User::getHome($this->uid));
209
+
210
+            // Delete the users entry in the storage table
211
+            \OC\Files\Cache\Storage::remove('home::' . $this->uid);
212
+
213
+            \OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
214
+            \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
215
+
216
+            $notification = \OC::$server->getNotificationManager()->createNotification();
217
+            $notification->setUser($this->uid);
218
+            \OC::$server->getNotificationManager()->markProcessed($notification);
219
+
220
+            if ($this->emitter) {
221
+                $this->emitter->emit('\OC\User', 'postDelete', array($this));
222
+            }
223
+        }
224
+        return !($result === false);
225
+    }
226
+
227
+    /**
228
+     * Set the password of the user
229
+     *
230
+     * @param string $password
231
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
232
+     * @return bool
233
+     */
234
+    public function setPassword($password, $recoveryPassword = null) {
235
+        if ($this->emitter) {
236
+            $this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword));
237
+        }
238
+        if ($this->backend->implementsActions(\OC\User\Backend::SET_PASSWORD)) {
239
+            $result = $this->backend->setPassword($this->uid, $password);
240
+            if ($this->emitter) {
241
+                $this->emitter->emit('\OC\User', 'postSetPassword', array($this, $password, $recoveryPassword));
242
+            }
243
+            return !($result === false);
244
+        } else {
245
+            return false;
246
+        }
247
+    }
248
+
249
+    /**
250
+     * get the users home folder to mount
251
+     *
252
+     * @return string
253
+     */
254
+    public function getHome() {
255
+        if (!$this->home) {
256
+            if ($this->backend->implementsActions(\OC\User\Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
257
+                $this->home = $home;
258
+            } elseif ($this->config) {
259
+                $this->home = $this->config->getSystemValue('datadirectory') . '/' . $this->uid;
260
+            } else {
261
+                $this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
262
+            }
263
+        }
264
+        return $this->home;
265
+    }
266
+
267
+    /**
268
+     * Get the name of the backend class the user is connected with
269
+     *
270
+     * @return string
271
+     */
272
+    public function getBackendClassName() {
273
+        if($this->backend instanceof \OCP\IUserBackend) {
274
+            return $this->backend->getBackendName();
275
+        }
276
+        return get_class($this->backend);
277
+    }
278
+
279
+    /**
280
+     * check if the backend allows the user to change his avatar on Personal page
281
+     *
282
+     * @return bool
283
+     */
284
+    public function canChangeAvatar() {
285
+        if ($this->backend->implementsActions(\OC\User\Backend::PROVIDE_AVATAR)) {
286
+            return $this->backend->canChangeAvatar($this->uid);
287
+        }
288
+        return true;
289
+    }
290
+
291
+    /**
292
+     * check if the backend supports changing passwords
293
+     *
294
+     * @return bool
295
+     */
296
+    public function canChangePassword() {
297
+        return $this->backend->implementsActions(\OC\User\Backend::SET_PASSWORD);
298
+    }
299
+
300
+    /**
301
+     * check if the backend supports changing display names
302
+     *
303
+     * @return bool
304
+     */
305
+    public function canChangeDisplayName() {
306
+        if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
307
+            return false;
308
+        }
309
+        return $this->backend->implementsActions(\OC\User\Backend::SET_DISPLAYNAME);
310
+    }
311
+
312
+    /**
313
+     * check if the user is enabled
314
+     *
315
+     * @return bool
316
+     */
317
+    public function isEnabled() {
318
+        return $this->enabled;
319
+    }
320
+
321
+    /**
322
+     * set the enabled status for the user
323
+     *
324
+     * @param bool $enabled
325
+     */
326
+    public function setEnabled($enabled) {
327
+        $this->enabled = $enabled;
328
+        $enabled = ($enabled) ? 'true' : 'false';
329
+        $this->config->setUserValue($this->uid, 'core', 'enabled', $enabled);
330
+    }
331
+
332
+    /**
333
+     * get the users email address
334
+     *
335
+     * @return string|null
336
+     * @since 9.0.0
337
+     */
338
+    public function getEMailAddress() {
339
+        return $this->config->getUserValue($this->uid, 'settings', 'email', null);
340
+    }
341
+
342
+    /**
343
+     * get the users' quota
344
+     *
345
+     * @return string
346
+     * @since 9.0.0
347
+     */
348
+    public function getQuota() {
349
+        $quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
350
+        if($quota === 'default') {
351
+            $quota = $this->config->getAppValue('files', 'default_quota', 'none');
352
+        }
353
+        return $quota;
354
+    }
355
+
356
+    /**
357
+     * set the users' quota
358
+     *
359
+     * @param string $quota
360
+     * @return void
361
+     * @since 9.0.0
362
+     */
363
+    public function setQuota($quota) {
364
+        if($quota !== 'none' and $quota !== 'default') {
365
+            $quota = OC_Helper::computerFileSize($quota);
366
+            $quota = OC_Helper::humanFileSize($quota);
367
+        }
368
+        $this->config->setUserValue($this->uid, 'files', 'quota', $quota);
369
+        $this->triggerChange('quota', $quota);
370
+    }
371
+
372
+    /**
373
+     * get the avatar image if it exists
374
+     *
375
+     * @param int $size
376
+     * @return IImage|null
377
+     * @since 9.0.0
378
+     */
379
+    public function getAvatarImage($size) {
380
+        // delay the initialization
381
+        if (is_null($this->avatarManager)) {
382
+            $this->avatarManager = \OC::$server->getAvatarManager();
383
+        }
384
+
385
+        $avatar = $this->avatarManager->getAvatar($this->uid);
386
+        $image = $avatar->get(-1);
387
+        if ($image) {
388
+            return $image;
389
+        }
390
+
391
+        return null;
392
+    }
393
+
394
+    /**
395
+     * get the federation cloud id
396
+     *
397
+     * @return string
398
+     * @since 9.0.0
399
+     */
400
+    public function getCloudId() {
401
+        $uid = $this->getUID();
402
+        $server = $this->urlGenerator->getAbsoluteURL('/');
403
+        return $uid . '@' . rtrim( $this->removeProtocolFromUrl($server), '/');
404
+    }
405
+
406
+    /**
407
+     * @param string $url
408
+     * @return string
409
+     */
410
+    private function removeProtocolFromUrl($url) {
411
+        if (strpos($url, 'https://') === 0) {
412
+            return substr($url, strlen('https://'));
413
+        } else if (strpos($url, 'http://') === 0) {
414
+            return substr($url, strlen('http://'));
415
+        }
416
+
417
+        return $url;
418
+    }
419
+
420
+    public function triggerChange($feature, $value = null) {
421
+        if ($this->emitter) {
422
+            $this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value));
423
+        }
424
+    }
425 425
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
 		$this->uid = $uid;
82 82
 		$this->backend = $backend;
83 83
 		$this->emitter = $emitter;
84
-		if(is_null($config)) {
84
+		if (is_null($config)) {
85 85
 			$config = \OC::$server->getConfig();
86 86
 		}
87 87
 		$this->config = $config;
@@ -156,7 +156,7 @@  discard block
 block discarded – undo
156 156
 	 * @since 9.0.0
157 157
 	 */
158 158
 	public function setEMailAddress($mailAddress) {
159
-		if($mailAddress === '') {
159
+		if ($mailAddress === '') {
160 160
 			$this->config->deleteUserValue($this->uid, 'settings', 'email');
161 161
 		} else {
162 162
 			$this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
 			\OC_Helper::rmdirr(\OC_User::getHome($this->uid));
209 209
 
210 210
 			// Delete the users entry in the storage table
211
-			\OC\Files\Cache\Storage::remove('home::' . $this->uid);
211
+			\OC\Files\Cache\Storage::remove('home::'.$this->uid);
212 212
 
213 213
 			\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
214 214
 			\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
@@ -256,9 +256,9 @@  discard block
 block discarded – undo
256 256
 			if ($this->backend->implementsActions(\OC\User\Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
257 257
 				$this->home = $home;
258 258
 			} elseif ($this->config) {
259
-				$this->home = $this->config->getSystemValue('datadirectory') . '/' . $this->uid;
259
+				$this->home = $this->config->getSystemValue('datadirectory').'/'.$this->uid;
260 260
 			} else {
261
-				$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
261
+				$this->home = \OC::$SERVERROOT.'/data/'.$this->uid;
262 262
 			}
263 263
 		}
264 264
 		return $this->home;
@@ -270,7 +270,7 @@  discard block
 block discarded – undo
270 270
 	 * @return string
271 271
 	 */
272 272
 	public function getBackendClassName() {
273
-		if($this->backend instanceof \OCP\IUserBackend) {
273
+		if ($this->backend instanceof \OCP\IUserBackend) {
274 274
 			return $this->backend->getBackendName();
275 275
 		}
276 276
 		return get_class($this->backend);
@@ -347,7 +347,7 @@  discard block
 block discarded – undo
347 347
 	 */
348 348
 	public function getQuota() {
349 349
 		$quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
350
-		if($quota === 'default') {
350
+		if ($quota === 'default') {
351 351
 			$quota = $this->config->getAppValue('files', 'default_quota', 'none');
352 352
 		}
353 353
 		return $quota;
@@ -361,7 +361,7 @@  discard block
 block discarded – undo
361 361
 	 * @since 9.0.0
362 362
 	 */
363 363
 	public function setQuota($quota) {
364
-		if($quota !== 'none' and $quota !== 'default') {
364
+		if ($quota !== 'none' and $quota !== 'default') {
365 365
 			$quota = OC_Helper::computerFileSize($quota);
366 366
 			$quota = OC_Helper::humanFileSize($quota);
367 367
 		}
@@ -400,7 +400,7 @@  discard block
 block discarded – undo
400 400
 	public function getCloudId() {
401 401
 		$uid = $this->getUID();
402 402
 		$server = $this->urlGenerator->getAbsoluteURL('/');
403
-		return $uid . '@' . rtrim( $this->removeProtocolFromUrl($server), '/');
403
+		return $uid.'@'.rtrim($this->removeProtocolFromUrl($server), '/');
404 404
 	}
405 405
 
406 406
 	/**
Please login to merge, or discard this patch.
lib/private/AllConfig.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -116,8 +116,8 @@
 block discarded – undo
116 116
 	 * Looks up a system wide defined value
117 117
 	 *
118 118
 	 * @param string $key the key of the value, under which it was saved
119
-	 * @param mixed $default the default value to be returned if the value isn't set
120
-	 * @return mixed the value or $default
119
+	 * @param string|false $default the default value to be returned if the value isn't set
120
+	 * @return string the value or $default
121 121
 	 */
122 122
 	public function getSystemValue($key, $default = '') {
123 123
 		return $this->systemConfig->getValue($key, $default);
Please login to merge, or discard this patch.
Indentation   +397 added lines, -397 removed lines patch added patch discarded remove patch
@@ -37,401 +37,401 @@
 block discarded – undo
37 37
  * Class to combine all the configuration options ownCloud offers
38 38
  */
39 39
 class AllConfig implements \OCP\IConfig {
40
-	/** @var SystemConfig */
41
-	private $systemConfig;
42
-
43
-	/** @var IDBConnection */
44
-	private $connection;
45
-
46
-	/**
47
-	 * 3 dimensional array with the following structure:
48
-	 * [ $userId =>
49
-	 *     [ $appId =>
50
-	 *         [ $key => $value ]
51
-	 *     ]
52
-	 * ]
53
-	 *
54
-	 * database table: preferences
55
-	 *
56
-	 * methods that use this:
57
-	 *   - setUserValue
58
-	 *   - getUserValue
59
-	 *   - getUserKeys
60
-	 *   - deleteUserValue
61
-	 *   - deleteAllUserValues
62
-	 *   - deleteAppFromAllUsers
63
-	 *
64
-	 * @var CappedMemoryCache $userCache
65
-	 */
66
-	private $userCache;
67
-
68
-	/**
69
-	 * @param SystemConfig $systemConfig
70
-	 */
71
-	function __construct(SystemConfig $systemConfig) {
72
-		$this->userCache = new CappedMemoryCache();
73
-		$this->systemConfig = $systemConfig;
74
-	}
75
-
76
-	/**
77
-	 * TODO - FIXME This fixes an issue with base.php that cause cyclic
78
-	 * dependencies, especially with autoconfig setup
79
-	 *
80
-	 * Replace this by properly injected database connection. Currently the
81
-	 * base.php triggers the getDatabaseConnection too early which causes in
82
-	 * autoconfig setup case a too early distributed database connection and
83
-	 * the autoconfig then needs to reinit all already initialized dependencies
84
-	 * that use the database connection.
85
-	 *
86
-	 * otherwise a SQLite database is created in the wrong directory
87
-	 * because the database connection was created with an uninitialized config
88
-	 */
89
-	private function fixDIInit() {
90
-		if($this->connection === null) {
91
-			$this->connection = \OC::$server->getDatabaseConnection();
92
-		}
93
-	}
94
-
95
-	/**
96
-	 * Sets and deletes system wide values
97
-	 *
98
-	 * @param array $configs Associative array with `key => value` pairs
99
-	 *                       If value is null, the config key will be deleted
100
-	 */
101
-	public function setSystemValues(array $configs) {
102
-		$this->systemConfig->setValues($configs);
103
-	}
104
-
105
-	/**
106
-	 * Sets a new system wide value
107
-	 *
108
-	 * @param string $key the key of the value, under which will be saved
109
-	 * @param mixed $value the value that should be stored
110
-	 */
111
-	public function setSystemValue($key, $value) {
112
-		$this->systemConfig->setValue($key, $value);
113
-	}
114
-
115
-	/**
116
-	 * Looks up a system wide defined value
117
-	 *
118
-	 * @param string $key the key of the value, under which it was saved
119
-	 * @param mixed $default the default value to be returned if the value isn't set
120
-	 * @return mixed the value or $default
121
-	 */
122
-	public function getSystemValue($key, $default = '') {
123
-		return $this->systemConfig->getValue($key, $default);
124
-	}
125
-
126
-	/**
127
-	 * Looks up a system wide defined value and filters out sensitive data
128
-	 *
129
-	 * @param string $key the key of the value, under which it was saved
130
-	 * @param mixed $default the default value to be returned if the value isn't set
131
-	 * @return mixed the value or $default
132
-	 */
133
-	public function getFilteredSystemValue($key, $default = '') {
134
-		return $this->systemConfig->getFilteredValue($key, $default);
135
-	}
136
-
137
-	/**
138
-	 * Delete a system wide defined value
139
-	 *
140
-	 * @param string $key the key of the value, under which it was saved
141
-	 */
142
-	public function deleteSystemValue($key) {
143
-		$this->systemConfig->deleteValue($key);
144
-	}
145
-
146
-	/**
147
-	 * Get all keys stored for an app
148
-	 *
149
-	 * @param string $appName the appName that we stored the value under
150
-	 * @return string[] the keys stored for the app
151
-	 */
152
-	public function getAppKeys($appName) {
153
-		return \OC::$server->getAppConfig()->getKeys($appName);
154
-	}
155
-
156
-	/**
157
-	 * Writes a new app wide value
158
-	 *
159
-	 * @param string $appName the appName that we want to store the value under
160
-	 * @param string $key the key of the value, under which will be saved
161
-	 * @param string|float|int $value the value that should be stored
162
-	 */
163
-	public function setAppValue($appName, $key, $value) {
164
-		\OC::$server->getAppConfig()->setValue($appName, $key, $value);
165
-	}
166
-
167
-	/**
168
-	 * Looks up an app wide defined value
169
-	 *
170
-	 * @param string $appName the appName that we stored the value under
171
-	 * @param string $key the key of the value, under which it was saved
172
-	 * @param string $default the default value to be returned if the value isn't set
173
-	 * @return string the saved value
174
-	 */
175
-	public function getAppValue($appName, $key, $default = '') {
176
-		return \OC::$server->getAppConfig()->getValue($appName, $key, $default);
177
-	}
178
-
179
-	/**
180
-	 * Delete an app wide defined value
181
-	 *
182
-	 * @param string $appName the appName that we stored the value under
183
-	 * @param string $key the key of the value, under which it was saved
184
-	 */
185
-	public function deleteAppValue($appName, $key) {
186
-		\OC::$server->getAppConfig()->deleteKey($appName, $key);
187
-	}
188
-
189
-	/**
190
-	 * Removes all keys in appconfig belonging to the app
191
-	 *
192
-	 * @param string $appName the appName the configs are stored under
193
-	 */
194
-	public function deleteAppValues($appName) {
195
-		\OC::$server->getAppConfig()->deleteApp($appName);
196
-	}
197
-
198
-
199
-	/**
200
-	 * Set a user defined value
201
-	 *
202
-	 * @param string $userId the userId of the user that we want to store the value under
203
-	 * @param string $appName the appName that we want to store the value under
204
-	 * @param string $key the key under which the value is being stored
205
-	 * @param string|float|int $value the value that you want to store
206
-	 * @param string $preCondition only update if the config value was previously the value passed as $preCondition
207
-	 * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
208
-	 * @throws \UnexpectedValueException when trying to store an unexpected value
209
-	 */
210
-	public function setUserValue($userId, $appName, $key, $value, $preCondition = null) {
211
-		if (!is_int($value) && !is_float($value) && !is_string($value)) {
212
-			throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value');
213
-		}
214
-
215
-		// TODO - FIXME
216
-		$this->fixDIInit();
217
-
218
-		$preconditionArray = [];
219
-		if (isset($preCondition)) {
220
-			$preconditionArray = [
221
-				'configvalue' => $preCondition,
222
-			];
223
-		}
224
-
225
-		$this->connection->setValues('preferences', [
226
-			'userid' => $userId,
227
-			'appid' => $appName,
228
-			'configkey' => $key,
229
-		], [
230
-			'configvalue' => $value,
231
-		], $preconditionArray);
232
-
233
-		// only add to the cache if we already loaded data for the user
234
-		if (isset($this->userCache[$userId])) {
235
-			if (!isset($this->userCache[$userId][$appName])) {
236
-				$this->userCache[$userId][$appName] = array();
237
-			}
238
-			$this->userCache[$userId][$appName][$key] = $value;
239
-		}
240
-	}
241
-
242
-	/**
243
-	 * Getting a user defined value
244
-	 *
245
-	 * @param string $userId the userId of the user that we want to store the value under
246
-	 * @param string $appName the appName that we stored the value under
247
-	 * @param string $key the key under which the value is being stored
248
-	 * @param mixed $default the default value to be returned if the value isn't set
249
-	 * @return string
250
-	 */
251
-	public function getUserValue($userId, $appName, $key, $default = '') {
252
-		$data = $this->getUserValues($userId);
253
-		if (isset($data[$appName]) and isset($data[$appName][$key])) {
254
-			return $data[$appName][$key];
255
-		} else {
256
-			return $default;
257
-		}
258
-	}
259
-
260
-	/**
261
-	 * Get the keys of all stored by an app for the user
262
-	 *
263
-	 * @param string $userId the userId of the user that we want to store the value under
264
-	 * @param string $appName the appName that we stored the value under
265
-	 * @return string[]
266
-	 */
267
-	public function getUserKeys($userId, $appName) {
268
-		$data = $this->getUserValues($userId);
269
-		if (isset($data[$appName])) {
270
-			return array_keys($data[$appName]);
271
-		} else {
272
-			return array();
273
-		}
274
-	}
275
-
276
-	/**
277
-	 * Delete a user value
278
-	 *
279
-	 * @param string $userId the userId of the user that we want to store the value under
280
-	 * @param string $appName the appName that we stored the value under
281
-	 * @param string $key the key under which the value is being stored
282
-	 */
283
-	public function deleteUserValue($userId, $appName, $key) {
284
-		// TODO - FIXME
285
-		$this->fixDIInit();
286
-
287
-		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
288
-				'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
289
-		$this->connection->executeUpdate($sql, array($userId, $appName, $key));
290
-
291
-		if (isset($this->userCache[$userId]) and isset($this->userCache[$userId][$appName])) {
292
-			unset($this->userCache[$userId][$appName][$key]);
293
-		}
294
-	}
295
-
296
-	/**
297
-	 * Delete all user values
298
-	 *
299
-	 * @param string $userId the userId of the user that we want to remove all values from
300
-	 */
301
-	public function deleteAllUserValues($userId) {
302
-		// TODO - FIXME
303
-		$this->fixDIInit();
304
-
305
-		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
306
-			'WHERE `userid` = ?';
307
-		$this->connection->executeUpdate($sql, array($userId));
308
-
309
-		unset($this->userCache[$userId]);
310
-	}
311
-
312
-	/**
313
-	 * Delete all user related values of one app
314
-	 *
315
-	 * @param string $appName the appName of the app that we want to remove all values from
316
-	 */
317
-	public function deleteAppFromAllUsers($appName) {
318
-		// TODO - FIXME
319
-		$this->fixDIInit();
320
-
321
-		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
322
-				'WHERE `appid` = ?';
323
-		$this->connection->executeUpdate($sql, array($appName));
324
-
325
-		foreach ($this->userCache as &$userCache) {
326
-			unset($userCache[$appName]);
327
-		}
328
-	}
329
-
330
-	/**
331
-	 * Returns all user configs sorted by app of one user
332
-	 *
333
-	 * @param string $userId the user ID to get the app configs from
334
-	 * @return array[] - 2 dimensional array with the following structure:
335
-	 *     [ $appId =>
336
-	 *         [ $key => $value ]
337
-	 *     ]
338
-	 */
339
-	private function getUserValues($userId) {
340
-		// TODO - FIXME
341
-		$this->fixDIInit();
342
-
343
-		if (isset($this->userCache[$userId])) {
344
-			return $this->userCache[$userId];
345
-		}
346
-		$data = array();
347
-		$query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
348
-		$result = $this->connection->executeQuery($query, array($userId));
349
-		while ($row = $result->fetch()) {
350
-			$appId = $row['appid'];
351
-			if (!isset($data[$appId])) {
352
-				$data[$appId] = array();
353
-			}
354
-			$data[$appId][$row['configkey']] = $row['configvalue'];
355
-		}
356
-		$this->userCache[$userId] = $data;
357
-		return $data;
358
-	}
359
-
360
-	/**
361
-	 * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs.
362
-	 *
363
-	 * @param string $appName app to get the value for
364
-	 * @param string $key the key to get the value for
365
-	 * @param array $userIds the user IDs to fetch the values for
366
-	 * @return array Mapped values: userId => value
367
-	 */
368
-	public function getUserValueForUsers($appName, $key, $userIds) {
369
-		// TODO - FIXME
370
-		$this->fixDIInit();
371
-
372
-		if (empty($userIds) || !is_array($userIds)) {
373
-			return array();
374
-		}
375
-
376
-		$chunkedUsers = array_chunk($userIds, 50, true);
377
-		$placeholders50 = implode(',', array_fill(0, 50, '?'));
378
-
379
-		$userValues = array();
380
-		foreach ($chunkedUsers as $chunk) {
381
-			$queryParams = $chunk;
382
-			// create [$app, $key, $chunkedUsers]
383
-			array_unshift($queryParams, $key);
384
-			array_unshift($queryParams, $appName);
385
-
386
-			$placeholders = (sizeof($chunk) == 50) ? $placeholders50 :  implode(',', array_fill(0, sizeof($chunk), '?'));
387
-
388
-			$query    = 'SELECT `userid`, `configvalue` ' .
389
-						'FROM `*PREFIX*preferences` ' .
390
-						'WHERE `appid` = ? AND `configkey` = ? ' .
391
-						'AND `userid` IN (' . $placeholders . ')';
392
-			$result = $this->connection->executeQuery($query, $queryParams);
393
-
394
-			while ($row = $result->fetch()) {
395
-				$userValues[$row['userid']] = $row['configvalue'];
396
-			}
397
-		}
398
-
399
-		return $userValues;
400
-	}
401
-
402
-	/**
403
-	 * Determines the users that have the given value set for a specific app-key-pair
404
-	 *
405
-	 * @param string $appName the app to get the user for
406
-	 * @param string $key the key to get the user for
407
-	 * @param string $value the value to get the user for
408
-	 * @return array of user IDs
409
-	 */
410
-	public function getUsersForUserValue($appName, $key, $value) {
411
-		// TODO - FIXME
412
-		$this->fixDIInit();
413
-
414
-		$sql  = 'SELECT `userid` FROM `*PREFIX*preferences` ' .
415
-				'WHERE `appid` = ? AND `configkey` = ? ';
416
-
417
-		if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
418
-			//oracle hack: need to explicitly cast CLOB to CHAR for comparison
419
-			$sql .= 'AND to_char(`configvalue`) = ?';
420
-		} else {
421
-			$sql .= 'AND `configvalue` = ?';
422
-		}
423
-
424
-		$result = $this->connection->executeQuery($sql, array($appName, $key, $value));
425
-
426
-		$userIDs = array();
427
-		while ($row = $result->fetch()) {
428
-			$userIDs[] = $row['userid'];
429
-		}
430
-
431
-		return $userIDs;
432
-	}
433
-
434
-	public function getSystemConfig() {
435
-		return $this->systemConfig;
436
-	}
40
+    /** @var SystemConfig */
41
+    private $systemConfig;
42
+
43
+    /** @var IDBConnection */
44
+    private $connection;
45
+
46
+    /**
47
+     * 3 dimensional array with the following structure:
48
+     * [ $userId =>
49
+     *     [ $appId =>
50
+     *         [ $key => $value ]
51
+     *     ]
52
+     * ]
53
+     *
54
+     * database table: preferences
55
+     *
56
+     * methods that use this:
57
+     *   - setUserValue
58
+     *   - getUserValue
59
+     *   - getUserKeys
60
+     *   - deleteUserValue
61
+     *   - deleteAllUserValues
62
+     *   - deleteAppFromAllUsers
63
+     *
64
+     * @var CappedMemoryCache $userCache
65
+     */
66
+    private $userCache;
67
+
68
+    /**
69
+     * @param SystemConfig $systemConfig
70
+     */
71
+    function __construct(SystemConfig $systemConfig) {
72
+        $this->userCache = new CappedMemoryCache();
73
+        $this->systemConfig = $systemConfig;
74
+    }
75
+
76
+    /**
77
+     * TODO - FIXME This fixes an issue with base.php that cause cyclic
78
+     * dependencies, especially with autoconfig setup
79
+     *
80
+     * Replace this by properly injected database connection. Currently the
81
+     * base.php triggers the getDatabaseConnection too early which causes in
82
+     * autoconfig setup case a too early distributed database connection and
83
+     * the autoconfig then needs to reinit all already initialized dependencies
84
+     * that use the database connection.
85
+     *
86
+     * otherwise a SQLite database is created in the wrong directory
87
+     * because the database connection was created with an uninitialized config
88
+     */
89
+    private function fixDIInit() {
90
+        if($this->connection === null) {
91
+            $this->connection = \OC::$server->getDatabaseConnection();
92
+        }
93
+    }
94
+
95
+    /**
96
+     * Sets and deletes system wide values
97
+     *
98
+     * @param array $configs Associative array with `key => value` pairs
99
+     *                       If value is null, the config key will be deleted
100
+     */
101
+    public function setSystemValues(array $configs) {
102
+        $this->systemConfig->setValues($configs);
103
+    }
104
+
105
+    /**
106
+     * Sets a new system wide value
107
+     *
108
+     * @param string $key the key of the value, under which will be saved
109
+     * @param mixed $value the value that should be stored
110
+     */
111
+    public function setSystemValue($key, $value) {
112
+        $this->systemConfig->setValue($key, $value);
113
+    }
114
+
115
+    /**
116
+     * Looks up a system wide defined value
117
+     *
118
+     * @param string $key the key of the value, under which it was saved
119
+     * @param mixed $default the default value to be returned if the value isn't set
120
+     * @return mixed the value or $default
121
+     */
122
+    public function getSystemValue($key, $default = '') {
123
+        return $this->systemConfig->getValue($key, $default);
124
+    }
125
+
126
+    /**
127
+     * Looks up a system wide defined value and filters out sensitive data
128
+     *
129
+     * @param string $key the key of the value, under which it was saved
130
+     * @param mixed $default the default value to be returned if the value isn't set
131
+     * @return mixed the value or $default
132
+     */
133
+    public function getFilteredSystemValue($key, $default = '') {
134
+        return $this->systemConfig->getFilteredValue($key, $default);
135
+    }
136
+
137
+    /**
138
+     * Delete a system wide defined value
139
+     *
140
+     * @param string $key the key of the value, under which it was saved
141
+     */
142
+    public function deleteSystemValue($key) {
143
+        $this->systemConfig->deleteValue($key);
144
+    }
145
+
146
+    /**
147
+     * Get all keys stored for an app
148
+     *
149
+     * @param string $appName the appName that we stored the value under
150
+     * @return string[] the keys stored for the app
151
+     */
152
+    public function getAppKeys($appName) {
153
+        return \OC::$server->getAppConfig()->getKeys($appName);
154
+    }
155
+
156
+    /**
157
+     * Writes a new app wide value
158
+     *
159
+     * @param string $appName the appName that we want to store the value under
160
+     * @param string $key the key of the value, under which will be saved
161
+     * @param string|float|int $value the value that should be stored
162
+     */
163
+    public function setAppValue($appName, $key, $value) {
164
+        \OC::$server->getAppConfig()->setValue($appName, $key, $value);
165
+    }
166
+
167
+    /**
168
+     * Looks up an app wide defined value
169
+     *
170
+     * @param string $appName the appName that we stored the value under
171
+     * @param string $key the key of the value, under which it was saved
172
+     * @param string $default the default value to be returned if the value isn't set
173
+     * @return string the saved value
174
+     */
175
+    public function getAppValue($appName, $key, $default = '') {
176
+        return \OC::$server->getAppConfig()->getValue($appName, $key, $default);
177
+    }
178
+
179
+    /**
180
+     * Delete an app wide defined value
181
+     *
182
+     * @param string $appName the appName that we stored the value under
183
+     * @param string $key the key of the value, under which it was saved
184
+     */
185
+    public function deleteAppValue($appName, $key) {
186
+        \OC::$server->getAppConfig()->deleteKey($appName, $key);
187
+    }
188
+
189
+    /**
190
+     * Removes all keys in appconfig belonging to the app
191
+     *
192
+     * @param string $appName the appName the configs are stored under
193
+     */
194
+    public function deleteAppValues($appName) {
195
+        \OC::$server->getAppConfig()->deleteApp($appName);
196
+    }
197
+
198
+
199
+    /**
200
+     * Set a user defined value
201
+     *
202
+     * @param string $userId the userId of the user that we want to store the value under
203
+     * @param string $appName the appName that we want to store the value under
204
+     * @param string $key the key under which the value is being stored
205
+     * @param string|float|int $value the value that you want to store
206
+     * @param string $preCondition only update if the config value was previously the value passed as $preCondition
207
+     * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
208
+     * @throws \UnexpectedValueException when trying to store an unexpected value
209
+     */
210
+    public function setUserValue($userId, $appName, $key, $value, $preCondition = null) {
211
+        if (!is_int($value) && !is_float($value) && !is_string($value)) {
212
+            throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value');
213
+        }
214
+
215
+        // TODO - FIXME
216
+        $this->fixDIInit();
217
+
218
+        $preconditionArray = [];
219
+        if (isset($preCondition)) {
220
+            $preconditionArray = [
221
+                'configvalue' => $preCondition,
222
+            ];
223
+        }
224
+
225
+        $this->connection->setValues('preferences', [
226
+            'userid' => $userId,
227
+            'appid' => $appName,
228
+            'configkey' => $key,
229
+        ], [
230
+            'configvalue' => $value,
231
+        ], $preconditionArray);
232
+
233
+        // only add to the cache if we already loaded data for the user
234
+        if (isset($this->userCache[$userId])) {
235
+            if (!isset($this->userCache[$userId][$appName])) {
236
+                $this->userCache[$userId][$appName] = array();
237
+            }
238
+            $this->userCache[$userId][$appName][$key] = $value;
239
+        }
240
+    }
241
+
242
+    /**
243
+     * Getting a user defined value
244
+     *
245
+     * @param string $userId the userId of the user that we want to store the value under
246
+     * @param string $appName the appName that we stored the value under
247
+     * @param string $key the key under which the value is being stored
248
+     * @param mixed $default the default value to be returned if the value isn't set
249
+     * @return string
250
+     */
251
+    public function getUserValue($userId, $appName, $key, $default = '') {
252
+        $data = $this->getUserValues($userId);
253
+        if (isset($data[$appName]) and isset($data[$appName][$key])) {
254
+            return $data[$appName][$key];
255
+        } else {
256
+            return $default;
257
+        }
258
+    }
259
+
260
+    /**
261
+     * Get the keys of all stored by an app for the user
262
+     *
263
+     * @param string $userId the userId of the user that we want to store the value under
264
+     * @param string $appName the appName that we stored the value under
265
+     * @return string[]
266
+     */
267
+    public function getUserKeys($userId, $appName) {
268
+        $data = $this->getUserValues($userId);
269
+        if (isset($data[$appName])) {
270
+            return array_keys($data[$appName]);
271
+        } else {
272
+            return array();
273
+        }
274
+    }
275
+
276
+    /**
277
+     * Delete a user value
278
+     *
279
+     * @param string $userId the userId of the user that we want to store the value under
280
+     * @param string $appName the appName that we stored the value under
281
+     * @param string $key the key under which the value is being stored
282
+     */
283
+    public function deleteUserValue($userId, $appName, $key) {
284
+        // TODO - FIXME
285
+        $this->fixDIInit();
286
+
287
+        $sql  = 'DELETE FROM `*PREFIX*preferences` '.
288
+                'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
289
+        $this->connection->executeUpdate($sql, array($userId, $appName, $key));
290
+
291
+        if (isset($this->userCache[$userId]) and isset($this->userCache[$userId][$appName])) {
292
+            unset($this->userCache[$userId][$appName][$key]);
293
+        }
294
+    }
295
+
296
+    /**
297
+     * Delete all user values
298
+     *
299
+     * @param string $userId the userId of the user that we want to remove all values from
300
+     */
301
+    public function deleteAllUserValues($userId) {
302
+        // TODO - FIXME
303
+        $this->fixDIInit();
304
+
305
+        $sql  = 'DELETE FROM `*PREFIX*preferences` '.
306
+            'WHERE `userid` = ?';
307
+        $this->connection->executeUpdate($sql, array($userId));
308
+
309
+        unset($this->userCache[$userId]);
310
+    }
311
+
312
+    /**
313
+     * Delete all user related values of one app
314
+     *
315
+     * @param string $appName the appName of the app that we want to remove all values from
316
+     */
317
+    public function deleteAppFromAllUsers($appName) {
318
+        // TODO - FIXME
319
+        $this->fixDIInit();
320
+
321
+        $sql  = 'DELETE FROM `*PREFIX*preferences` '.
322
+                'WHERE `appid` = ?';
323
+        $this->connection->executeUpdate($sql, array($appName));
324
+
325
+        foreach ($this->userCache as &$userCache) {
326
+            unset($userCache[$appName]);
327
+        }
328
+    }
329
+
330
+    /**
331
+     * Returns all user configs sorted by app of one user
332
+     *
333
+     * @param string $userId the user ID to get the app configs from
334
+     * @return array[] - 2 dimensional array with the following structure:
335
+     *     [ $appId =>
336
+     *         [ $key => $value ]
337
+     *     ]
338
+     */
339
+    private function getUserValues($userId) {
340
+        // TODO - FIXME
341
+        $this->fixDIInit();
342
+
343
+        if (isset($this->userCache[$userId])) {
344
+            return $this->userCache[$userId];
345
+        }
346
+        $data = array();
347
+        $query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
348
+        $result = $this->connection->executeQuery($query, array($userId));
349
+        while ($row = $result->fetch()) {
350
+            $appId = $row['appid'];
351
+            if (!isset($data[$appId])) {
352
+                $data[$appId] = array();
353
+            }
354
+            $data[$appId][$row['configkey']] = $row['configvalue'];
355
+        }
356
+        $this->userCache[$userId] = $data;
357
+        return $data;
358
+    }
359
+
360
+    /**
361
+     * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs.
362
+     *
363
+     * @param string $appName app to get the value for
364
+     * @param string $key the key to get the value for
365
+     * @param array $userIds the user IDs to fetch the values for
366
+     * @return array Mapped values: userId => value
367
+     */
368
+    public function getUserValueForUsers($appName, $key, $userIds) {
369
+        // TODO - FIXME
370
+        $this->fixDIInit();
371
+
372
+        if (empty($userIds) || !is_array($userIds)) {
373
+            return array();
374
+        }
375
+
376
+        $chunkedUsers = array_chunk($userIds, 50, true);
377
+        $placeholders50 = implode(',', array_fill(0, 50, '?'));
378
+
379
+        $userValues = array();
380
+        foreach ($chunkedUsers as $chunk) {
381
+            $queryParams = $chunk;
382
+            // create [$app, $key, $chunkedUsers]
383
+            array_unshift($queryParams, $key);
384
+            array_unshift($queryParams, $appName);
385
+
386
+            $placeholders = (sizeof($chunk) == 50) ? $placeholders50 :  implode(',', array_fill(0, sizeof($chunk), '?'));
387
+
388
+            $query    = 'SELECT `userid`, `configvalue` ' .
389
+                        'FROM `*PREFIX*preferences` ' .
390
+                        'WHERE `appid` = ? AND `configkey` = ? ' .
391
+                        'AND `userid` IN (' . $placeholders . ')';
392
+            $result = $this->connection->executeQuery($query, $queryParams);
393
+
394
+            while ($row = $result->fetch()) {
395
+                $userValues[$row['userid']] = $row['configvalue'];
396
+            }
397
+        }
398
+
399
+        return $userValues;
400
+    }
401
+
402
+    /**
403
+     * Determines the users that have the given value set for a specific app-key-pair
404
+     *
405
+     * @param string $appName the app to get the user for
406
+     * @param string $key the key to get the user for
407
+     * @param string $value the value to get the user for
408
+     * @return array of user IDs
409
+     */
410
+    public function getUsersForUserValue($appName, $key, $value) {
411
+        // TODO - FIXME
412
+        $this->fixDIInit();
413
+
414
+        $sql  = 'SELECT `userid` FROM `*PREFIX*preferences` ' .
415
+                'WHERE `appid` = ? AND `configkey` = ? ';
416
+
417
+        if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
418
+            //oracle hack: need to explicitly cast CLOB to CHAR for comparison
419
+            $sql .= 'AND to_char(`configvalue`) = ?';
420
+        } else {
421
+            $sql .= 'AND `configvalue` = ?';
422
+        }
423
+
424
+        $result = $this->connection->executeQuery($sql, array($appName, $key, $value));
425
+
426
+        $userIDs = array();
427
+        while ($row = $result->fetch()) {
428
+            $userIDs[] = $row['userid'];
429
+        }
430
+
431
+        return $userIDs;
432
+    }
433
+
434
+    public function getSystemConfig() {
435
+        return $this->systemConfig;
436
+    }
437 437
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -87,7 +87,7 @@  discard block
 block discarded – undo
87 87
 	 * because the database connection was created with an uninitialized config
88 88
 	 */
89 89
 	private function fixDIInit() {
90
-		if($this->connection === null) {
90
+		if ($this->connection === null) {
91 91
 			$this->connection = \OC::$server->getDatabaseConnection();
92 92
 		}
93 93
 	}
@@ -284,7 +284,7 @@  discard block
 block discarded – undo
284 284
 		// TODO - FIXME
285 285
 		$this->fixDIInit();
286 286
 
287
-		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
287
+		$sql = 'DELETE FROM `*PREFIX*preferences` '.
288 288
 				'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
289 289
 		$this->connection->executeUpdate($sql, array($userId, $appName, $key));
290 290
 
@@ -302,7 +302,7 @@  discard block
 block discarded – undo
302 302
 		// TODO - FIXME
303 303
 		$this->fixDIInit();
304 304
 
305
-		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
305
+		$sql = 'DELETE FROM `*PREFIX*preferences` '.
306 306
 			'WHERE `userid` = ?';
307 307
 		$this->connection->executeUpdate($sql, array($userId));
308 308
 
@@ -318,7 +318,7 @@  discard block
 block discarded – undo
318 318
 		// TODO - FIXME
319 319
 		$this->fixDIInit();
320 320
 
321
-		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
321
+		$sql = 'DELETE FROM `*PREFIX*preferences` '.
322 322
 				'WHERE `appid` = ?';
323 323
 		$this->connection->executeUpdate($sql, array($appName));
324 324
 
@@ -383,12 +383,12 @@  discard block
 block discarded – undo
383 383
 			array_unshift($queryParams, $key);
384 384
 			array_unshift($queryParams, $appName);
385 385
 
386
-			$placeholders = (sizeof($chunk) == 50) ? $placeholders50 :  implode(',', array_fill(0, sizeof($chunk), '?'));
386
+			$placeholders = (sizeof($chunk) == 50) ? $placeholders50 : implode(',', array_fill(0, sizeof($chunk), '?'));
387 387
 
388
-			$query    = 'SELECT `userid`, `configvalue` ' .
389
-						'FROM `*PREFIX*preferences` ' .
390
-						'WHERE `appid` = ? AND `configkey` = ? ' .
391
-						'AND `userid` IN (' . $placeholders . ')';
388
+			$query = 'SELECT `userid`, `configvalue` '.
389
+						'FROM `*PREFIX*preferences` '.
390
+						'WHERE `appid` = ? AND `configkey` = ? '.
391
+						'AND `userid` IN ('.$placeholders.')';
392 392
 			$result = $this->connection->executeQuery($query, $queryParams);
393 393
 
394 394
 			while ($row = $result->fetch()) {
@@ -411,10 +411,10 @@  discard block
 block discarded – undo
411 411
 		// TODO - FIXME
412 412
 		$this->fixDIInit();
413 413
 
414
-		$sql  = 'SELECT `userid` FROM `*PREFIX*preferences` ' .
414
+		$sql = 'SELECT `userid` FROM `*PREFIX*preferences` '.
415 415
 				'WHERE `appid` = ? AND `configkey` = ? ';
416 416
 
417
-		if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
417
+		if ($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
418 418
 			//oracle hack: need to explicitly cast CLOB to CHAR for comparison
419 419
 			$sql .= 'AND to_char(`configvalue`) = ?';
420 420
 		} else {
Please login to merge, or discard this patch.
lib/public/AppFramework/Http/OCSResponse.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -50,8 +50,8 @@
 block discarded – undo
50 50
 	 * @param int $statuscode
51 51
 	 * @param string $message
52 52
 	 * @param array $data
53
-	 * @param int|string $itemscount
54
-	 * @param int|string $itemsperpage
53
+	 * @param string $itemscount
54
+	 * @param string $itemsperpage
55 55
 	 * @since 8.1.0
56 56
 	 */
57 57
 	public function __construct($format, $statuscode, $message,
Please login to merge, or discard this patch.
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -37,56 +37,56 @@
 block discarded – undo
37 37
  */
38 38
 class OCSResponse extends Response {
39 39
 
40
-	private $data;
41
-	private $format;
42
-	private $statuscode;
43
-	private $message;
44
-	private $itemscount;
45
-	private $itemsperpage;
40
+    private $data;
41
+    private $format;
42
+    private $statuscode;
43
+    private $message;
44
+    private $itemscount;
45
+    private $itemsperpage;
46 46
 
47
-	/**
48
-	 * generates the xml or json response for the API call from an multidimenional data array.
49
-	 * @param string $format
50
-	 * @param int $statuscode
51
-	 * @param string $message
52
-	 * @param array $data
53
-	 * @param int|string $itemscount
54
-	 * @param int|string $itemsperpage
55
-	 * @since 8.1.0
56
-	 */
57
-	public function __construct($format, $statuscode, $message,
58
-								$data=[], $itemscount='',
59
-								$itemsperpage='') {
60
-		$this->format = $format;
61
-		$this->statuscode = $statuscode;
62
-		$this->message = $message;
63
-		$this->data = $data;
64
-		$this->itemscount = $itemscount;
65
-		$this->itemsperpage = $itemsperpage;
47
+    /**
48
+     * generates the xml or json response for the API call from an multidimenional data array.
49
+     * @param string $format
50
+     * @param int $statuscode
51
+     * @param string $message
52
+     * @param array $data
53
+     * @param int|string $itemscount
54
+     * @param int|string $itemsperpage
55
+     * @since 8.1.0
56
+     */
57
+    public function __construct($format, $statuscode, $message,
58
+                                $data=[], $itemscount='',
59
+                                $itemsperpage='') {
60
+        $this->format = $format;
61
+        $this->statuscode = $statuscode;
62
+        $this->message = $message;
63
+        $this->data = $data;
64
+        $this->itemscount = $itemscount;
65
+        $this->itemsperpage = $itemsperpage;
66 66
 
67
-		// set the correct header based on the format parameter
68
-		if ($format === 'json') {
69
-			$this->addHeader(
70
-				'Content-Type', 'application/json; charset=utf-8'
71
-			);
72
-		} else {
73
-			$this->addHeader(
74
-				'Content-Type', 'application/xml; charset=utf-8'
75
-			);
76
-		}
77
-	}
67
+        // set the correct header based on the format parameter
68
+        if ($format === 'json') {
69
+            $this->addHeader(
70
+                'Content-Type', 'application/json; charset=utf-8'
71
+            );
72
+        } else {
73
+            $this->addHeader(
74
+                'Content-Type', 'application/xml; charset=utf-8'
75
+            );
76
+        }
77
+    }
78 78
 
79
-	/**
80
-	 * @return string
81
-	 * @since 8.1.0
82
-	 */
83
-	public function render() {
84
-		$r = new \OC_OCS_Result($this->data, $this->statuscode, $this->message);
85
-		$r->setTotalItems($this->itemscount);
86
-		$r->setItemsPerPage($this->itemsperpage);
79
+    /**
80
+     * @return string
81
+     * @since 8.1.0
82
+     */
83
+    public function render() {
84
+        $r = new \OC_OCS_Result($this->data, $this->statuscode, $this->message);
85
+        $r->setTotalItems($this->itemscount);
86
+        $r->setItemsPerPage($this->itemsperpage);
87 87
 
88
-		return \OC_API::renderResult($this->format, $r->getMeta(), $r->getData());
89
-	}
88
+        return \OC_API::renderResult($this->format, $r->getMeta(), $r->getData());
89
+    }
90 90
 
91 91
 
92 92
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -55,8 +55,8 @@
 block discarded – undo
55 55
 	 * @since 8.1.0
56 56
 	 */
57 57
 	public function __construct($format, $statuscode, $message,
58
-								$data=[], $itemscount='',
59
-								$itemsperpage='') {
58
+								$data = [], $itemscount = '',
59
+								$itemsperpage = '') {
60 60
 		$this->format = $format;
61 61
 		$this->statuscode = $statuscode;
62 62
 		$this->message = $message;
Please login to merge, or discard this patch.
lib/private/legacy/files.php 4 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -334,7 +334,7 @@
 block discarded – undo
334 334
 	 *
335 335
 	 * @param int $size file size in bytes
336 336
 	 * @param array $files override '.htaccess' and '.user.ini' locations
337
-	 * @return bool false on failure, size on success
337
+	 * @return integer false on failure, size on success
338 338
 	 */
339 339
 	public static function setUploadLimit($size, $files = []) {
340 340
 		//don't allow user to break his config
Please login to merge, or discard this patch.
Braces   +6 added lines, -12 removed lines patch added patch discarded remove patch
@@ -83,13 +83,11 @@  discard block
 block discarded – undo
83 83
 			    if (count($rangeArray) > 1) {
84 84
 				$type = 'multipart/byteranges; boundary='.self::getBoundary();
85 85
 				// no Content-Length header here
86
-			    }
87
-			    else {
86
+			    } else {
88 87
 				header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true);
89 88
 				OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1);
90 89
 			    }
91
-			}
92
-			else {
90
+			} else {
93 91
 			    OC_Response::setContentLengthHeader($fileSize);
94 92
 			}
95 93
 		}
@@ -223,13 +221,11 @@  discard block
 block discarded – undo
223 221
 				if ($minOffset >= $fileSize) {
224 222
 					break;
225 223
 				}
226
-			}
227
-			elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
224
+			} elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
228 225
 				// case: x-
229 226
 				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
230 227
 				break;
231
-			}
232
-			elseif (is_numeric($ranges[1])) {
228
+			} elseif (is_numeric($ranges[1])) {
233 229
 				// case: -x
234 230
 				if ($ranges[1] > $fileSize) {
235 231
 					$ranges[1] = $fileSize;
@@ -277,8 +273,7 @@  discard block
 block discarded – undo
277 273
 			try {
278 274
 			    if (count($rangeArray) == 1) {
279 275
 				$view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']);
280
-			    }
281
-			    else {
276
+			    } else {
282 277
 				// check if file is seekable (if not throw UnseekableException)
283 278
 				// we have to check it before body contents
284 279
 				$view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']);
@@ -301,8 +296,7 @@  discard block
 block discarded – undo
301 296
 			    self::sendHeaders($filename, $name, array());
302 297
 			    $view->readfile($filename);
303 298
 			}
304
-		}
305
-		else {
299
+		} else {
306 300
 		    $view->readfile($filename);
307 301
 		}
308 302
 	}
Please login to merge, or discard this patch.
Indentation   +384 added lines, -384 removed lines patch added patch discarded remove patch
@@ -46,392 +46,392 @@
 block discarded – undo
46 46
  *
47 47
  */
48 48
 class OC_Files {
49
-	const FILE = 1;
50
-	const ZIP_FILES = 2;
51
-	const ZIP_DIR = 3;
52
-
53
-	const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB
54
-
55
-
56
-	private static $multipartBoundary = '';
57
-
58
-	/**
59
-	 * @return string
60
-	 */
61
-	private static function getBoundary() {
62
-		if (empty(self::$multipartBoundary)) {
63
-			self::$multipartBoundary = md5(mt_rand());
64
-		}
65
-		return self::$multipartBoundary;
66
-	}
67
-
68
-	/**
69
-	 * @param string $filename
70
-	 * @param string $name
71
-	 * @param array $rangeArray ('from'=>int,'to'=>int), ...
72
-	 */
73
-	private static function sendHeaders($filename, $name, array $rangeArray) {
74
-		OC_Response::setContentDispositionHeader($name, 'attachment');
75
-		header('Content-Transfer-Encoding: binary', true);
76
-		OC_Response::disableCaching();
77
-		$fileSize = \OC\Files\Filesystem::filesize($filename);
78
-		$type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
79
-		if ($fileSize > -1) {
80
-			if (!empty($rangeArray)) {
81
-			    header('HTTP/1.1 206 Partial Content', true);
82
-			    header('Accept-Ranges: bytes', true);
83
-			    if (count($rangeArray) > 1) {
84
-				$type = 'multipart/byteranges; boundary='.self::getBoundary();
85
-				// no Content-Length header here
86
-			    }
87
-			    else {
88
-				header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true);
89
-				OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1);
90
-			    }
91
-			}
92
-			else {
93
-			    OC_Response::setContentLengthHeader($fileSize);
94
-			}
95
-		}
96
-		header('Content-Type: '.$type, true);
97
-	}
98
-
99
-	/**
100
-	 * return the content of a file or return a zip file containing multiple files
101
-	 *
102
-	 * @param string $dir
103
-	 * @param string $files ; separated list of files to download
104
-	 * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
105
-	 */
106
-	public static function get($dir, $files, $params = null) {
107
-
108
-		$view = \OC\Files\Filesystem::getView();
109
-		$getType = self::FILE;
110
-		$filename = $dir;
111
-		try {
112
-
113
-			if (is_array($files) && count($files) === 1) {
114
-				$files = $files[0];
115
-			}
116
-
117
-			if (!is_array($files)) {
118
-				$filename = $dir . '/' . $files;
119
-				if (!$view->is_dir($filename)) {
120
-					self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params);
121
-					return;
122
-				}
123
-			}
124
-
125
-			$name = 'download';
126
-			if (is_array($files)) {
127
-				$getType = self::ZIP_FILES;
128
-				$basename = basename($dir);
129
-				if ($basename) {
130
-					$name = $basename;
131
-				}
132
-
133
-				$filename = $dir . '/' . $name;
134
-			} else {
135
-				$filename = $dir . '/' . $files;
136
-				$getType = self::ZIP_DIR;
137
-				// downloading root ?
138
-				if ($files !== '') {
139
-					$name = $files;
140
-				}
141
-			}
142
-
143
-			$streamer = new Streamer();
144
-			OC_Util::obEnd();
145
-
146
-			self::lockFiles($view, $dir, $files);
147
-
148
-			$streamer->sendHeaders($name);
149
-			$executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time'));
150
-			set_time_limit(0);
151
-			if ($getType === self::ZIP_FILES) {
152
-				foreach ($files as $file) {
153
-					$file = $dir . '/' . $file;
154
-					if (\OC\Files\Filesystem::is_file($file)) {
155
-						$fileSize = \OC\Files\Filesystem::filesize($file);
156
-						$fh = \OC\Files\Filesystem::fopen($file, 'r');
157
-						$streamer->addFileFromStream($fh, basename($file), $fileSize);
158
-						fclose($fh);
159
-					} elseif (\OC\Files\Filesystem::is_dir($file)) {
160
-						$streamer->addDirRecursive($file);
161
-					}
162
-				}
163
-			} elseif ($getType === self::ZIP_DIR) {
164
-				$file = $dir . '/' . $files;
165
-				$streamer->addDirRecursive($file);
166
-			}
167
-			$streamer->finalize();
168
-			set_time_limit($executionTime);
169
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
170
-		} catch (\OCP\Lock\LockedException $ex) {
171
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
172
-			OC::$server->getLogger()->logException($ex);
173
-			$l = \OC::$server->getL10N('core');
174
-			$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
175
-			\OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint);
176
-		} catch (\OCP\Files\ForbiddenException $ex) {
177
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
178
-			OC::$server->getLogger()->logException($ex);
179
-			$l = \OC::$server->getL10N('core');
180
-			\OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage());
181
-		} catch (\Exception $ex) {
182
-			self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
183
-			OC::$server->getLogger()->logException($ex);
184
-			$l = \OC::$server->getL10N('core');
185
-			$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
186
-			\OC_Template::printErrorPage($l->t('Can\'t read file'), $hint);
187
-		}
188
-	}
189
-
190
-	/**
191
-	 * @param string $rangeHeaderPos
192
-	 * @param int $fileSize
193
-	 * @return array $rangeArray ('from'=>int,'to'=>int), ...
194
-	 */
195
-	private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) {
196
-		$rArray=explode(',', $rangeHeaderPos);
197
-		$minOffset = 0;
198
-		$ind = 0;
199
-
200
-		$rangeArray = array();
201
-
202
-		foreach ($rArray as $value) {
203
-			$ranges = explode('-', $value);
204
-			if (is_numeric($ranges[0])) {
205
-				if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999
206
-					$ranges[0] = $minOffset;
207
-				}
208
-				if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999
209
-					$ind--;
210
-					$ranges[0] = $rangeArray[$ind]['from'];
211
-				}
212
-			}
213
-
214
-			if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) {
215
-				// case: x-x
216
-				if ($ranges[1] >= $fileSize) {
217
-					$ranges[1] = $fileSize-1;
218
-				}
219
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize );
220
-				$minOffset = $ranges[1] + 1;
221
-				if ($minOffset >= $fileSize) {
222
-					break;
223
-				}
224
-			}
225
-			elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
226
-				// case: x-
227
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
228
-				break;
229
-			}
230
-			elseif (is_numeric($ranges[1])) {
231
-				// case: -x
232
-				if ($ranges[1] > $fileSize) {
233
-					$ranges[1] = $fileSize;
234
-				}
235
-				$rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize );
236
-				break;
237
-			}
238
-		}
239
-		return $rangeArray;
240
-	}
241
-
242
-	/**
243
-	 * @param View $view
244
-	 * @param string $name
245
-	 * @param string $dir
246
-	 * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
247
-	 */
248
-	private static function getSingleFile($view, $dir, $name, $params) {
249
-		$filename = $dir . '/' . $name;
250
-		OC_Util::obEnd();
251
-		$view->lockFile($filename, ILockingProvider::LOCK_SHARED);
49
+    const FILE = 1;
50
+    const ZIP_FILES = 2;
51
+    const ZIP_DIR = 3;
52
+
53
+    const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB
54
+
55
+
56
+    private static $multipartBoundary = '';
57
+
58
+    /**
59
+     * @return string
60
+     */
61
+    private static function getBoundary() {
62
+        if (empty(self::$multipartBoundary)) {
63
+            self::$multipartBoundary = md5(mt_rand());
64
+        }
65
+        return self::$multipartBoundary;
66
+    }
67
+
68
+    /**
69
+     * @param string $filename
70
+     * @param string $name
71
+     * @param array $rangeArray ('from'=>int,'to'=>int), ...
72
+     */
73
+    private static function sendHeaders($filename, $name, array $rangeArray) {
74
+        OC_Response::setContentDispositionHeader($name, 'attachment');
75
+        header('Content-Transfer-Encoding: binary', true);
76
+        OC_Response::disableCaching();
77
+        $fileSize = \OC\Files\Filesystem::filesize($filename);
78
+        $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
79
+        if ($fileSize > -1) {
80
+            if (!empty($rangeArray)) {
81
+                header('HTTP/1.1 206 Partial Content', true);
82
+                header('Accept-Ranges: bytes', true);
83
+                if (count($rangeArray) > 1) {
84
+                $type = 'multipart/byteranges; boundary='.self::getBoundary();
85
+                // no Content-Length header here
86
+                }
87
+                else {
88
+                header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true);
89
+                OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1);
90
+                }
91
+            }
92
+            else {
93
+                OC_Response::setContentLengthHeader($fileSize);
94
+            }
95
+        }
96
+        header('Content-Type: '.$type, true);
97
+    }
98
+
99
+    /**
100
+     * return the content of a file or return a zip file containing multiple files
101
+     *
102
+     * @param string $dir
103
+     * @param string $files ; separated list of files to download
104
+     * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
105
+     */
106
+    public static function get($dir, $files, $params = null) {
107
+
108
+        $view = \OC\Files\Filesystem::getView();
109
+        $getType = self::FILE;
110
+        $filename = $dir;
111
+        try {
112
+
113
+            if (is_array($files) && count($files) === 1) {
114
+                $files = $files[0];
115
+            }
116
+
117
+            if (!is_array($files)) {
118
+                $filename = $dir . '/' . $files;
119
+                if (!$view->is_dir($filename)) {
120
+                    self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params);
121
+                    return;
122
+                }
123
+            }
124
+
125
+            $name = 'download';
126
+            if (is_array($files)) {
127
+                $getType = self::ZIP_FILES;
128
+                $basename = basename($dir);
129
+                if ($basename) {
130
+                    $name = $basename;
131
+                }
132
+
133
+                $filename = $dir . '/' . $name;
134
+            } else {
135
+                $filename = $dir . '/' . $files;
136
+                $getType = self::ZIP_DIR;
137
+                // downloading root ?
138
+                if ($files !== '') {
139
+                    $name = $files;
140
+                }
141
+            }
142
+
143
+            $streamer = new Streamer();
144
+            OC_Util::obEnd();
145
+
146
+            self::lockFiles($view, $dir, $files);
147
+
148
+            $streamer->sendHeaders($name);
149
+            $executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time'));
150
+            set_time_limit(0);
151
+            if ($getType === self::ZIP_FILES) {
152
+                foreach ($files as $file) {
153
+                    $file = $dir . '/' . $file;
154
+                    if (\OC\Files\Filesystem::is_file($file)) {
155
+                        $fileSize = \OC\Files\Filesystem::filesize($file);
156
+                        $fh = \OC\Files\Filesystem::fopen($file, 'r');
157
+                        $streamer->addFileFromStream($fh, basename($file), $fileSize);
158
+                        fclose($fh);
159
+                    } elseif (\OC\Files\Filesystem::is_dir($file)) {
160
+                        $streamer->addDirRecursive($file);
161
+                    }
162
+                }
163
+            } elseif ($getType === self::ZIP_DIR) {
164
+                $file = $dir . '/' . $files;
165
+                $streamer->addDirRecursive($file);
166
+            }
167
+            $streamer->finalize();
168
+            set_time_limit($executionTime);
169
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
170
+        } catch (\OCP\Lock\LockedException $ex) {
171
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
172
+            OC::$server->getLogger()->logException($ex);
173
+            $l = \OC::$server->getL10N('core');
174
+            $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
175
+            \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint);
176
+        } catch (\OCP\Files\ForbiddenException $ex) {
177
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
178
+            OC::$server->getLogger()->logException($ex);
179
+            $l = \OC::$server->getL10N('core');
180
+            \OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage());
181
+        } catch (\Exception $ex) {
182
+            self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
183
+            OC::$server->getLogger()->logException($ex);
184
+            $l = \OC::$server->getL10N('core');
185
+            $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
186
+            \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint);
187
+        }
188
+    }
189
+
190
+    /**
191
+     * @param string $rangeHeaderPos
192
+     * @param int $fileSize
193
+     * @return array $rangeArray ('from'=>int,'to'=>int), ...
194
+     */
195
+    private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) {
196
+        $rArray=explode(',', $rangeHeaderPos);
197
+        $minOffset = 0;
198
+        $ind = 0;
199
+
200
+        $rangeArray = array();
201
+
202
+        foreach ($rArray as $value) {
203
+            $ranges = explode('-', $value);
204
+            if (is_numeric($ranges[0])) {
205
+                if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999
206
+                    $ranges[0] = $minOffset;
207
+                }
208
+                if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999
209
+                    $ind--;
210
+                    $ranges[0] = $rangeArray[$ind]['from'];
211
+                }
212
+            }
213
+
214
+            if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) {
215
+                // case: x-x
216
+                if ($ranges[1] >= $fileSize) {
217
+                    $ranges[1] = $fileSize-1;
218
+                }
219
+                $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize );
220
+                $minOffset = $ranges[1] + 1;
221
+                if ($minOffset >= $fileSize) {
222
+                    break;
223
+                }
224
+            }
225
+            elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
226
+                // case: x-
227
+                $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
228
+                break;
229
+            }
230
+            elseif (is_numeric($ranges[1])) {
231
+                // case: -x
232
+                if ($ranges[1] > $fileSize) {
233
+                    $ranges[1] = $fileSize;
234
+                }
235
+                $rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize );
236
+                break;
237
+            }
238
+        }
239
+        return $rangeArray;
240
+    }
241
+
242
+    /**
243
+     * @param View $view
244
+     * @param string $name
245
+     * @param string $dir
246
+     * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
247
+     */
248
+    private static function getSingleFile($view, $dir, $name, $params) {
249
+        $filename = $dir . '/' . $name;
250
+        OC_Util::obEnd();
251
+        $view->lockFile($filename, ILockingProvider::LOCK_SHARED);
252 252
 		
253
-		$rangeArray = array();
253
+        $rangeArray = array();
254 254
 
255
-		if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') {
256
-			$rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), 
257
-								 \OC\Files\Filesystem::filesize($filename));
258
-		}
255
+        if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') {
256
+            $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), 
257
+                                    \OC\Files\Filesystem::filesize($filename));
258
+        }
259 259
 		
260
-		if (\OC\Files\Filesystem::isReadable($filename)) {
261
-			self::sendHeaders($filename, $name, $rangeArray);
262
-		} elseif (!\OC\Files\Filesystem::file_exists($filename)) {
263
-			header("HTTP/1.0 404 Not Found");
264
-			$tmpl = new OC_Template('', '404', 'guest');
265
-			$tmpl->printPage();
266
-			exit();
267
-		} else {
268
-			header("HTTP/1.0 403 Forbidden");
269
-			die('403 Forbidden');
270
-		}
271
-		if (isset($params['head']) && $params['head']) {
272
-			return;
273
-		}
274
-		if (!empty($rangeArray)) {
275
-			try {
276
-			    if (count($rangeArray) == 1) {
277
-				$view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']);
278
-			    }
279
-			    else {
280
-				// check if file is seekable (if not throw UnseekableException)
281
-				// we have to check it before body contents
282
-				$view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']);
283
-
284
-				$type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
285
-
286
-				foreach ($rangeArray as $range) {
287
-				    echo "\r\n--".self::getBoundary()."\r\n".
288
-				         "Content-type: ".$type."\r\n".
289
-				         "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n";
290
-				    $view->readfilePart($filename, $range['from'], $range['to']);
291
-				}
292
-				echo "\r\n--".self::getBoundary()."--\r\n";
293
-			    }
294
-			} catch (\OCP\Files\UnseekableException $ex) {
295
-			    // file is unseekable
296
-			    header_remove('Accept-Ranges');
297
-			    header_remove('Content-Range');
298
-			    header("HTTP/1.1 200 OK");
299
-			    self::sendHeaders($filename, $name, array());
300
-			    $view->readfile($filename);
301
-			}
302
-		}
303
-		else {
304
-		    $view->readfile($filename);
305
-		}
306
-	}
307
-
308
-	/**
309
-	 * @param View $view
310
-	 * @param string $dir
311
-	 * @param string[]|string $files
312
-	 */
313
-	public static function lockFiles($view, $dir, $files) {
314
-		if (!is_array($files)) {
315
-			$file = $dir . '/' . $files;
316
-			$files = [$file];
317
-		}
318
-		foreach ($files as $file) {
319
-			$file = $dir . '/' . $file;
320
-			$view->lockFile($file, ILockingProvider::LOCK_SHARED);
321
-			if ($view->is_dir($file)) {
322
-				$contents = $view->getDirectoryContent($file);
323
-				$contents = array_map(function($fileInfo) use ($file) {
324
-					/** @var \OCP\Files\FileInfo $fileInfo */
325
-					return $file . '/' . $fileInfo->getName();
326
-				}, $contents);
327
-				self::lockFiles($view, $dir, $contents);
328
-			}
329
-		}
330
-	}
331
-
332
-	/**
333
-	 * set the maximum upload size limit for apache hosts using .htaccess
334
-	 *
335
-	 * @param int $size file size in bytes
336
-	 * @param array $files override '.htaccess' and '.user.ini' locations
337
-	 * @return bool false on failure, size on success
338
-	 */
339
-	public static function setUploadLimit($size, $files = []) {
340
-		//don't allow user to break his config
341
-		$size = intval($size);
342
-		if ($size < self::UPLOAD_MIN_LIMIT_BYTES) {
343
-			return false;
344
-		}
345
-		$size = OC_Helper::phpFileSize($size);
346
-
347
-		$phpValueKeys = array(
348
-			'upload_max_filesize',
349
-			'post_max_size'
350
-		);
351
-
352
-		// default locations if not overridden by $files
353
-		$files = array_merge([
354
-			'.htaccess' => OC::$SERVERROOT . '/.htaccess',
355
-			'.user.ini' => OC::$SERVERROOT . '/.user.ini'
356
-		], $files);
357
-
358
-		$updateFiles = [
359
-			$files['.htaccess'] => [
360
-				'pattern' => '/php_value %1$s (\S)*/',
361
-				'setting' => 'php_value %1$s %2$s'
362
-			],
363
-			$files['.user.ini'] => [
364
-				'pattern' => '/%1$s=(\S)*/',
365
-				'setting' => '%1$s=%2$s'
366
-			]
367
-		];
368
-
369
-		$success = true;
370
-
371
-		foreach ($updateFiles as $filename => $patternMap) {
372
-			// suppress warnings from fopen()
373
-			$handle = @fopen($filename, 'r+');
374
-			if (!$handle) {
375
-				\OCP\Util::writeLog('files',
376
-					'Can\'t write upload limit to ' . $filename . '. Please check the file permissions',
377
-					\OCP\Util::WARN);
378
-				$success = false;
379
-				continue; // try to update as many files as possible
380
-			}
381
-
382
-			$content = '';
383
-			while (!feof($handle)) {
384
-				$content .= fread($handle, 1000);
385
-			}
386
-
387
-			foreach ($phpValueKeys as $key) {
388
-				$pattern = vsprintf($patternMap['pattern'], [$key]);
389
-				$setting = vsprintf($patternMap['setting'], [$key, $size]);
390
-				$hasReplaced = 0;
391
-				$newContent = preg_replace($pattern, $setting, $content, 2, $hasReplaced);
392
-				if ($newContent !== null) {
393
-					$content = $newContent;
394
-				}
395
-				if ($hasReplaced === 0) {
396
-					$content .= "\n" . $setting;
397
-				}
398
-			}
399
-
400
-			// write file back
401
-			ftruncate($handle, 0);
402
-			rewind($handle);
403
-			fwrite($handle, $content);
404
-
405
-			fclose($handle);
406
-		}
407
-
408
-		if ($success) {
409
-			return OC_Helper::computerFileSize($size);
410
-		}
411
-		return false;
412
-	}
413
-
414
-	/**
415
-	 * @param string $dir
416
-	 * @param $files
417
-	 * @param integer $getType
418
-	 * @param View $view
419
-	 * @param string $filename
420
-	 */
421
-	private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) {
422
-		if ($getType === self::FILE) {
423
-			$view->unlockFile($filename, ILockingProvider::LOCK_SHARED);
424
-		}
425
-		if ($getType === self::ZIP_FILES) {
426
-			foreach ($files as $file) {
427
-				$file = $dir . '/' . $file;
428
-				$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
429
-			}
430
-		}
431
-		if ($getType === self::ZIP_DIR) {
432
-			$file = $dir . '/' . $files;
433
-			$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
434
-		}
435
-	}
260
+        if (\OC\Files\Filesystem::isReadable($filename)) {
261
+            self::sendHeaders($filename, $name, $rangeArray);
262
+        } elseif (!\OC\Files\Filesystem::file_exists($filename)) {
263
+            header("HTTP/1.0 404 Not Found");
264
+            $tmpl = new OC_Template('', '404', 'guest');
265
+            $tmpl->printPage();
266
+            exit();
267
+        } else {
268
+            header("HTTP/1.0 403 Forbidden");
269
+            die('403 Forbidden');
270
+        }
271
+        if (isset($params['head']) && $params['head']) {
272
+            return;
273
+        }
274
+        if (!empty($rangeArray)) {
275
+            try {
276
+                if (count($rangeArray) == 1) {
277
+                $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']);
278
+                }
279
+                else {
280
+                // check if file is seekable (if not throw UnseekableException)
281
+                // we have to check it before body contents
282
+                $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']);
283
+
284
+                $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename));
285
+
286
+                foreach ($rangeArray as $range) {
287
+                    echo "\r\n--".self::getBoundary()."\r\n".
288
+                            "Content-type: ".$type."\r\n".
289
+                            "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n";
290
+                    $view->readfilePart($filename, $range['from'], $range['to']);
291
+                }
292
+                echo "\r\n--".self::getBoundary()."--\r\n";
293
+                }
294
+            } catch (\OCP\Files\UnseekableException $ex) {
295
+                // file is unseekable
296
+                header_remove('Accept-Ranges');
297
+                header_remove('Content-Range');
298
+                header("HTTP/1.1 200 OK");
299
+                self::sendHeaders($filename, $name, array());
300
+                $view->readfile($filename);
301
+            }
302
+        }
303
+        else {
304
+            $view->readfile($filename);
305
+        }
306
+    }
307
+
308
+    /**
309
+     * @param View $view
310
+     * @param string $dir
311
+     * @param string[]|string $files
312
+     */
313
+    public static function lockFiles($view, $dir, $files) {
314
+        if (!is_array($files)) {
315
+            $file = $dir . '/' . $files;
316
+            $files = [$file];
317
+        }
318
+        foreach ($files as $file) {
319
+            $file = $dir . '/' . $file;
320
+            $view->lockFile($file, ILockingProvider::LOCK_SHARED);
321
+            if ($view->is_dir($file)) {
322
+                $contents = $view->getDirectoryContent($file);
323
+                $contents = array_map(function($fileInfo) use ($file) {
324
+                    /** @var \OCP\Files\FileInfo $fileInfo */
325
+                    return $file . '/' . $fileInfo->getName();
326
+                }, $contents);
327
+                self::lockFiles($view, $dir, $contents);
328
+            }
329
+        }
330
+    }
331
+
332
+    /**
333
+     * set the maximum upload size limit for apache hosts using .htaccess
334
+     *
335
+     * @param int $size file size in bytes
336
+     * @param array $files override '.htaccess' and '.user.ini' locations
337
+     * @return bool false on failure, size on success
338
+     */
339
+    public static function setUploadLimit($size, $files = []) {
340
+        //don't allow user to break his config
341
+        $size = intval($size);
342
+        if ($size < self::UPLOAD_MIN_LIMIT_BYTES) {
343
+            return false;
344
+        }
345
+        $size = OC_Helper::phpFileSize($size);
346
+
347
+        $phpValueKeys = array(
348
+            'upload_max_filesize',
349
+            'post_max_size'
350
+        );
351
+
352
+        // default locations if not overridden by $files
353
+        $files = array_merge([
354
+            '.htaccess' => OC::$SERVERROOT . '/.htaccess',
355
+            '.user.ini' => OC::$SERVERROOT . '/.user.ini'
356
+        ], $files);
357
+
358
+        $updateFiles = [
359
+            $files['.htaccess'] => [
360
+                'pattern' => '/php_value %1$s (\S)*/',
361
+                'setting' => 'php_value %1$s %2$s'
362
+            ],
363
+            $files['.user.ini'] => [
364
+                'pattern' => '/%1$s=(\S)*/',
365
+                'setting' => '%1$s=%2$s'
366
+            ]
367
+        ];
368
+
369
+        $success = true;
370
+
371
+        foreach ($updateFiles as $filename => $patternMap) {
372
+            // suppress warnings from fopen()
373
+            $handle = @fopen($filename, 'r+');
374
+            if (!$handle) {
375
+                \OCP\Util::writeLog('files',
376
+                    'Can\'t write upload limit to ' . $filename . '. Please check the file permissions',
377
+                    \OCP\Util::WARN);
378
+                $success = false;
379
+                continue; // try to update as many files as possible
380
+            }
381
+
382
+            $content = '';
383
+            while (!feof($handle)) {
384
+                $content .= fread($handle, 1000);
385
+            }
386
+
387
+            foreach ($phpValueKeys as $key) {
388
+                $pattern = vsprintf($patternMap['pattern'], [$key]);
389
+                $setting = vsprintf($patternMap['setting'], [$key, $size]);
390
+                $hasReplaced = 0;
391
+                $newContent = preg_replace($pattern, $setting, $content, 2, $hasReplaced);
392
+                if ($newContent !== null) {
393
+                    $content = $newContent;
394
+                }
395
+                if ($hasReplaced === 0) {
396
+                    $content .= "\n" . $setting;
397
+                }
398
+            }
399
+
400
+            // write file back
401
+            ftruncate($handle, 0);
402
+            rewind($handle);
403
+            fwrite($handle, $content);
404
+
405
+            fclose($handle);
406
+        }
407
+
408
+        if ($success) {
409
+            return OC_Helper::computerFileSize($size);
410
+        }
411
+        return false;
412
+    }
413
+
414
+    /**
415
+     * @param string $dir
416
+     * @param $files
417
+     * @param integer $getType
418
+     * @param View $view
419
+     * @param string $filename
420
+     */
421
+    private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) {
422
+        if ($getType === self::FILE) {
423
+            $view->unlockFile($filename, ILockingProvider::LOCK_SHARED);
424
+        }
425
+        if ($getType === self::ZIP_FILES) {
426
+            foreach ($files as $file) {
427
+                $file = $dir . '/' . $file;
428
+                $view->unlockFile($file, ILockingProvider::LOCK_SHARED);
429
+            }
430
+        }
431
+        if ($getType === self::ZIP_DIR) {
432
+            $file = $dir . '/' . $files;
433
+            $view->unlockFile($file, ILockingProvider::LOCK_SHARED);
434
+        }
435
+    }
436 436
 
437 437
 }
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -115,7 +115,7 @@  discard block
 block discarded – undo
115 115
 			}
116 116
 
117 117
 			if (!is_array($files)) {
118
-				$filename = $dir . '/' . $files;
118
+				$filename = $dir.'/'.$files;
119 119
 				if (!$view->is_dir($filename)) {
120 120
 					self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params);
121 121
 					return;
@@ -130,9 +130,9 @@  discard block
 block discarded – undo
130 130
 					$name = $basename;
131 131
 				}
132 132
 
133
-				$filename = $dir . '/' . $name;
133
+				$filename = $dir.'/'.$name;
134 134
 			} else {
135
-				$filename = $dir . '/' . $files;
135
+				$filename = $dir.'/'.$files;
136 136
 				$getType = self::ZIP_DIR;
137 137
 				// downloading root ?
138 138
 				if ($files !== '') {
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
 			set_time_limit(0);
151 151
 			if ($getType === self::ZIP_FILES) {
152 152
 				foreach ($files as $file) {
153
-					$file = $dir . '/' . $file;
153
+					$file = $dir.'/'.$file;
154 154
 					if (\OC\Files\Filesystem::is_file($file)) {
155 155
 						$fileSize = \OC\Files\Filesystem::filesize($file);
156 156
 						$fh = \OC\Files\Filesystem::fopen($file, 'r');
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
 					}
162 162
 				}
163 163
 			} elseif ($getType === self::ZIP_DIR) {
164
-				$file = $dir . '/' . $files;
164
+				$file = $dir.'/'.$files;
165 165
 				$streamer->addDirRecursive($file);
166 166
 			}
167 167
 			$streamer->finalize();
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 	 * @return array $rangeArray ('from'=>int,'to'=>int), ...
194 194
 	 */
195 195
 	private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) {
196
-		$rArray=explode(',', $rangeHeaderPos);
196
+		$rArray = explode(',', $rangeHeaderPos);
197 197
 		$minOffset = 0;
198 198
 		$ind = 0;
199 199
 
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
 				if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999
206 206
 					$ranges[0] = $minOffset;
207 207
 				}
208
-				if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999
208
+				if ($ind > 0 && $rangeArray[$ind - 1]['to'] + 1 == $ranges[0]) { // case: bytes=500-600,601-999
209 209
 					$ind--;
210 210
 					$ranges[0] = $rangeArray[$ind]['from'];
211 211
 				}
@@ -214,9 +214,9 @@  discard block
 block discarded – undo
214 214
 			if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) {
215 215
 				// case: x-x
216 216
 				if ($ranges[1] >= $fileSize) {
217
-					$ranges[1] = $fileSize-1;
217
+					$ranges[1] = $fileSize - 1;
218 218
 				}
219
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize );
219
+				$rangeArray[$ind++] = array('from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize);
220 220
 				$minOffset = $ranges[1] + 1;
221 221
 				if ($minOffset >= $fileSize) {
222 222
 					break;
@@ -224,7 +224,7 @@  discard block
 block discarded – undo
224 224
 			}
225 225
 			elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) {
226 226
 				// case: x-
227
-				$rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize );
227
+				$rangeArray[$ind++] = array('from' => $ranges[0], 'to' => $fileSize - 1, 'size' => $fileSize);
228 228
 				break;
229 229
 			}
230 230
 			elseif (is_numeric($ranges[1])) {
@@ -232,7 +232,7 @@  discard block
 block discarded – undo
232 232
 				if ($ranges[1] > $fileSize) {
233 233
 					$ranges[1] = $fileSize;
234 234
 				}
235
-				$rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize );
235
+				$rangeArray[$ind++] = array('from' => $fileSize - $ranges[1], 'to' => $fileSize - 1, 'size' => $fileSize);
236 236
 				break;
237 237
 			}
238 238
 		}
@@ -246,7 +246,7 @@  discard block
 block discarded – undo
246 246
 	 * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
247 247
 	 */
248 248
 	private static function getSingleFile($view, $dir, $name, $params) {
249
-		$filename = $dir . '/' . $name;
249
+		$filename = $dir.'/'.$name;
250 250
 		OC_Util::obEnd();
251 251
 		$view->lockFile($filename, ILockingProvider::LOCK_SHARED);
252 252
 		
@@ -312,17 +312,17 @@  discard block
 block discarded – undo
312 312
 	 */
313 313
 	public static function lockFiles($view, $dir, $files) {
314 314
 		if (!is_array($files)) {
315
-			$file = $dir . '/' . $files;
315
+			$file = $dir.'/'.$files;
316 316
 			$files = [$file];
317 317
 		}
318 318
 		foreach ($files as $file) {
319
-			$file = $dir . '/' . $file;
319
+			$file = $dir.'/'.$file;
320 320
 			$view->lockFile($file, ILockingProvider::LOCK_SHARED);
321 321
 			if ($view->is_dir($file)) {
322 322
 				$contents = $view->getDirectoryContent($file);
323 323
 				$contents = array_map(function($fileInfo) use ($file) {
324 324
 					/** @var \OCP\Files\FileInfo $fileInfo */
325
-					return $file . '/' . $fileInfo->getName();
325
+					return $file.'/'.$fileInfo->getName();
326 326
 				}, $contents);
327 327
 				self::lockFiles($view, $dir, $contents);
328 328
 			}
@@ -351,8 +351,8 @@  discard block
 block discarded – undo
351 351
 
352 352
 		// default locations if not overridden by $files
353 353
 		$files = array_merge([
354
-			'.htaccess' => OC::$SERVERROOT . '/.htaccess',
355
-			'.user.ini' => OC::$SERVERROOT . '/.user.ini'
354
+			'.htaccess' => OC::$SERVERROOT.'/.htaccess',
355
+			'.user.ini' => OC::$SERVERROOT.'/.user.ini'
356 356
 		], $files);
357 357
 
358 358
 		$updateFiles = [
@@ -373,7 +373,7 @@  discard block
 block discarded – undo
373 373
 			$handle = @fopen($filename, 'r+');
374 374
 			if (!$handle) {
375 375
 				\OCP\Util::writeLog('files',
376
-					'Can\'t write upload limit to ' . $filename . '. Please check the file permissions',
376
+					'Can\'t write upload limit to '.$filename.'. Please check the file permissions',
377 377
 					\OCP\Util::WARN);
378 378
 				$success = false;
379 379
 				continue; // try to update as many files as possible
@@ -393,7 +393,7 @@  discard block
 block discarded – undo
393 393
 					$content = $newContent;
394 394
 				}
395 395
 				if ($hasReplaced === 0) {
396
-					$content .= "\n" . $setting;
396
+					$content .= "\n".$setting;
397 397
 				}
398 398
 			}
399 399
 
@@ -424,12 +424,12 @@  discard block
 block discarded – undo
424 424
 		}
425 425
 		if ($getType === self::ZIP_FILES) {
426 426
 			foreach ($files as $file) {
427
-				$file = $dir . '/' . $file;
427
+				$file = $dir.'/'.$file;
428 428
 				$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
429 429
 			}
430 430
 		}
431 431
 		if ($getType === self::ZIP_DIR) {
432
-			$file = $dir . '/' . $files;
432
+			$file = $dir.'/'.$files;
433 433
 			$view->unlockFile($file, ILockingProvider::LOCK_SHARED);
434 434
 		}
435 435
 	}
Please login to merge, or discard this patch.