Completed
Push — stable9 ( 11047b...318578 )
by Lukas
20:03 queued 09:36
created
resources/updater-fixes/sabre/dav/lib/DAV/CorePlugin.php 3 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
      *
72 72
      * @param RequestInterface $request
73 73
      * @param ResponseInterface $response
74
-     * @return bool
74
+     * @return null|false
75 75
      */
76 76
     function httpGet(RequestInterface $request, ResponseInterface $response) {
77 77
 
@@ -275,7 +275,7 @@  discard block
 block discarded – undo
275 275
      *
276 276
      * @param RequestInterface $request
277 277
      * @param ResponseInterface $response
278
-     * @return void
278
+     * @return boolean
279 279
      */
280 280
     function httpDelete(RequestInterface $request, ResponseInterface $response) {
281 281
 
@@ -308,7 +308,7 @@  discard block
 block discarded – undo
308 308
      *
309 309
      * @param RequestInterface $request
310 310
      * @param ResponseInterface $response
311
-     * @return void
311
+     * @return boolean
312 312
      */
313 313
     function httpPropFind(RequestInterface $request, ResponseInterface $response) {
314 314
 
Please login to merge, or discard this patch.
Spacing   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -32,23 +32,23 @@  discard block
 block discarded – undo
32 32
     function initialize(Server $server) {
33 33
 
34 34
         $this->server = $server;
35
-        $server->on('method:GET',       [$this, 'httpGet']);
36
-        $server->on('method:OPTIONS',   [$this, 'httpOptions']);
37
-        $server->on('method:HEAD',      [$this, 'httpHead']);
38
-        $server->on('method:DELETE',    [$this, 'httpDelete']);
39
-        $server->on('method:PROPFIND',  [$this, 'httpPropFind']);
35
+        $server->on('method:GET', [$this, 'httpGet']);
36
+        $server->on('method:OPTIONS', [$this, 'httpOptions']);
37
+        $server->on('method:HEAD', [$this, 'httpHead']);
38
+        $server->on('method:DELETE', [$this, 'httpDelete']);
39
+        $server->on('method:PROPFIND', [$this, 'httpPropFind']);
40 40
         $server->on('method:PROPPATCH', [$this, 'httpPropPatch']);
41
-        $server->on('method:PUT',       [$this, 'httpPut']);
42
-        $server->on('method:MKCOL',     [$this, 'httpMkcol']);
43
-        $server->on('method:MOVE',      [$this, 'httpMove']);
44
-        $server->on('method:COPY',      [$this, 'httpCopy']);
45
-        $server->on('method:REPORT',    [$this, 'httpReport']);
46
-
47
-        $server->on('propPatch',        [$this, 'propPatchProtectedPropertyCheck'], 90);
48
-        $server->on('propPatch',        [$this, 'propPatchNodeUpdate'], 200);
49
-        $server->on('propFind',         [$this, 'propFind']);
50
-        $server->on('propFind',         [$this, 'propFindNode'], 120);
51
-        $server->on('propFind',         [$this, 'propFindLate'], 200);
41
+        $server->on('method:PUT', [$this, 'httpPut']);
42
+        $server->on('method:MKCOL', [$this, 'httpMkcol']);
43
+        $server->on('method:MOVE', [$this, 'httpMove']);
44
+        $server->on('method:COPY', [$this, 'httpCopy']);
45
+        $server->on('method:REPORT', [$this, 'httpReport']);
46
+
47
+        $server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90);
48
+        $server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200);
49
+        $server->on('propFind', [$this, 'propFind']);
50
+        $server->on('propFind', [$this, 'propFindNode'], 120);
51
+        $server->on('propFind', [$this, 'propFindLate'], 200);
52 52
 
53 53
     }
54 54
 
@@ -155,15 +155,15 @@  discard block
 block discarded – undo
155 155
                 $start = $range[0];
156 156
                 $end = $range[1] ? $range[1] : $nodeSize - 1;
157 157
                 if ($start >= $nodeSize)
158
-                    throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
158
+                    throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$range[0].') exceeded the size of the entity ('.$nodeSize.')');
159 159
 
160
-                if ($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
160
+                if ($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset ('.$range[1].') is lower than the start offset ('.$range[0].')');
161 161
                 if ($end >= $nodeSize) $end = $nodeSize - 1;
162 162
 
163 163
             } else {
164 164
 
165 165
                 $start = $nodeSize - $range[1];
166
-                $end  = $nodeSize - 1;
166
+                $end = $nodeSize - 1;
167 167
 
168 168
                 if ($start < 0) $start = 0;
169 169
 
@@ -174,14 +174,14 @@  discard block
 block discarded – undo
174 174
             // if fseek failed.
175 175
             if (!stream_get_meta_data($body)['seekable'] || fseek($body, $start, SEEK_SET) === -1) {
176 176
                 $consumeBlock = 8192;
177
-                for ($consumed = 0; $start - $consumed > 0;){
178
-                    if (feof($body)) throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $start . ') exceeded the size of the entity (' . $consumed . ')');
177
+                for ($consumed = 0; $start - $consumed > 0;) {
178
+                    if (feof($body)) throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$start.') exceeded the size of the entity ('.$consumed.')');
179 179
                     $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
180 180
                 }
181 181
             }
182 182
 
183 183
             $response->setHeader('Content-Length', $end - $start + 1);
184
-            $response->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $nodeSize);
184
+            $response->setHeader('Content-Range', 'bytes '.$start.'-'.$end.'/'.$nodeSize);
185 185
             $response->setStatus(206);
186 186
             $response->setBody($body);
187 187
 
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
             // multi-status.
393 393
             $ok = true;
394 394
             foreach ($result as $prop => $code) {
395
-                if ((int)$code > 299) {
395
+                if ((int) $code > 299) {
396 396
                     $ok = false;
397 397
                 }
398 398
             }
Please login to merge, or discard this patch.
Braces   +71 added lines, -27 removed lines patch added patch discarded remove patch
@@ -78,7 +78,9 @@  discard block
 block discarded – undo
78 78
         $path = $request->getPath();
79 79
         $node = $this->server->tree->getNodeForPath($path, 0);
80 80
 
81
-        if (!$node instanceof IFile) return;
81
+        if (!$node instanceof IFile) {
82
+            return;
83
+        }
82 84
 
83 85
         $body = $node->get();
84 86
 
@@ -132,17 +134,23 @@  discard block
 block discarded – undo
132 134
 
133 135
                 // It's a date. We must check if the entity is modified since
134 136
                 // the specified date.
135
-                if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
136
-                else {
137
+                if (!isset($httpHeaders['Last-Modified'])) {
138
+                    $ignoreRangeHeader = true;
139
+                } else {
137 140
                     $modified = new \DateTime($httpHeaders['Last-Modified']);
138
-                    if ($modified > $ifRangeDate) $ignoreRangeHeader = true;
141
+                    if ($modified > $ifRangeDate) {
142
+                        $ignoreRangeHeader = true;
143
+                    }
139 144
                 }
140 145
 
141 146
             } catch (\Exception $e) {
142 147
 
143 148
                 // It's an entity. We can do a simple comparison.
144
-                if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
145
-                elseif ($httpHeaders['ETag'] !== $ifRange) $ignoreRangeHeader = true;
149
+                if (!isset($httpHeaders['ETag'])) {
150
+                    $ignoreRangeHeader = true;
151
+                } elseif ($httpHeaders['ETag'] !== $ifRange) {
152
+                    $ignoreRangeHeader = true;
153
+                }
146 154
             }
147 155
         }
148 156
 
@@ -154,18 +162,25 @@  discard block
 block discarded – undo
154 162
 
155 163
                 $start = $range[0];
156 164
                 $end = $range[1] ? $range[1] : $nodeSize - 1;
157
-                if ($start >= $nodeSize)
158
-                    throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
165
+                if ($start >= $nodeSize) {
166
+                                    throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
167
+                }
159 168
 
160
-                if ($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
161
-                if ($end >= $nodeSize) $end = $nodeSize - 1;
169
+                if ($end < $start) {
170
+                    throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
171
+                }
172
+                if ($end >= $nodeSize) {
173
+                    $end = $nodeSize - 1;
174
+                }
162 175
 
163 176
             } else {
164 177
 
165 178
                 $start = $nodeSize - $range[1];
166 179
                 $end  = $nodeSize - 1;
167 180
 
168
-                if ($start < 0) $start = 0;
181
+                if ($start < 0) {
182
+                    $start = 0;
183
+                }
169 184
 
170 185
             }
171 186
 
@@ -175,7 +190,9 @@  discard block
 block discarded – undo
175 190
             if (!stream_get_meta_data($body)['seekable'] || fseek($body, $start, SEEK_SET) === -1) {
176 191
                 $consumeBlock = 8192;
177 192
                 for ($consumed = 0; $start - $consumed > 0;){
178
-                    if (feof($body)) throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $start . ') exceeded the size of the entity (' . $consumed . ')');
193
+                    if (feof($body)) {
194
+                        throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $start . ') exceeded the size of the entity (' . $consumed . ')');
195
+                    }
179 196
                     $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
180 197
                 }
181 198
             }
@@ -187,7 +204,9 @@  discard block
 block discarded – undo
187 204
 
188 205
         } else {
189 206
 
190
-            if ($nodeSize) $response->setHeader('Content-Length', $nodeSize);
207
+            if ($nodeSize) {
208
+                $response->setHeader('Content-Length', $nodeSize);
209
+            }
191 210
             $response->setStatus(200);
192 211
             $response->setBody($body);
193 212
 
@@ -281,7 +300,9 @@  discard block
 block discarded – undo
281 300
 
282 301
         $path = $request->getPath();
283 302
 
284
-        if (!$this->server->emit('beforeUnbind', [$path])) return false;
303
+        if (!$this->server->emit('beforeUnbind', [$path])) {
304
+            return false;
305
+        }
285 306
         $this->server->tree->delete($path);
286 307
         $this->server->emit('afterUnbind', [$path]);
287 308
 
@@ -329,7 +350,9 @@  discard block
 block discarded – undo
329 350
 
330 351
         $depth = $this->server->getHTTPDepth(1);
331 352
         // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
332
-        if (!$this->server->enablePropfindDepthInfinity && $depth != 0) $depth = 1;
353
+        if (!$this->server->enablePropfindDepthInfinity && $depth != 0) {
354
+            $depth = 1;
355
+        }
333 356
 
334 357
         $newProperties = $this->server->getPropertiesForPath($path, $propFindXml->properties, $depth);
335 358
 
@@ -506,14 +529,18 @@  discard block
 block discarded – undo
506 529
             $node = $this->server->tree->getNodeForPath($path);
507 530
 
508 531
             // If the node is a collection, we'll deny it
509
-            if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.');
532
+            if (!($node instanceof IFile)) {
533
+                throw new Exception\Conflict('PUT is not allowed on non-files.');
534
+            }
510 535
 
511 536
             if (!$this->server->updateFile($path, $body, $etag)) {
512 537
                 return false;
513 538
             }
514 539
 
515 540
             $response->setHeader('Content-Length', '0');
516
-            if ($etag) $response->setHeader('ETag', $etag);
541
+            if ($etag) {
542
+                $response->setHeader('ETag', $etag);
543
+            }
517 544
             $response->setStatus(204);
518 545
 
519 546
         } else {
@@ -526,7 +553,9 @@  discard block
 block discarded – undo
526 553
             }
527 554
 
528 555
             $response->setHeader('Content-Length', '0');
529
-            if ($etag) $response->setHeader('ETag', $etag);
556
+            if ($etag) {
557
+                $response->setHeader('ETag', $etag);
558
+            }
530 559
             $response->setStatus(201);
531 560
 
532 561
         }
@@ -570,8 +599,9 @@  discard block
 block discarded – undo
570 599
 
571 600
             $properties = $mkcol->getProperties();
572 601
 
573
-            if (!isset($properties['{DAV:}resourcetype']))
574
-                throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
602
+            if (!isset($properties['{DAV:}resourcetype'])) {
603
+                            throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
604
+            }
575 605
 
576 606
             $resourceType = $properties['{DAV:}resourcetype']->getValue();
577 607
             unset($properties['{DAV:}resourcetype']);
@@ -623,12 +653,20 @@  discard block
 block discarded – undo
623 653
 
624 654
         if ($moveInfo['destinationExists']) {
625 655
 
626
-            if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) return false;
656
+            if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) {
657
+                return false;
658
+            }
627 659
 
628 660
         }
629
-        if (!$this->server->emit('beforeUnbind', [$path])) return false;
630
-        if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) return false;
631
-        if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) return false;
661
+        if (!$this->server->emit('beforeUnbind', [$path])) {
662
+            return false;
663
+        }
664
+        if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) {
665
+            return false;
666
+        }
667
+        if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) {
668
+            return false;
669
+        }
632 670
 
633 671
         if ($moveInfo['destinationExists']) {
634 672
 
@@ -674,11 +712,15 @@  discard block
 block discarded – undo
674 712
         $copyInfo = $this->server->getCopyAndMoveInfo($request);
675 713
 
676 714
         if ($copyInfo['destinationExists']) {
677
-            if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false;
715
+            if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) {
716
+                return false;
717
+            }
678 718
             $this->server->tree->delete($copyInfo['destination']);
679 719
 
680 720
         }
681
-        if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false;
721
+        if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) {
722
+            return false;
723
+        }
682 724
         $this->server->tree->copy($path, $copyInfo['destination']);
683 725
         $this->server->emit('afterBind', [$copyInfo['destination']]);
684 726
 
@@ -871,7 +913,9 @@  discard block
 block discarded – undo
871 913
             // If we already have a sync-token from the current propFind
872 914
             // request, we can re-use that.
873 915
             $val = $propFind->get('{http://sabredav.org/ns}sync-token');
874
-            if ($val) return $val;
916
+            if ($val) {
917
+                return $val;
918
+            }
875 919
 
876 920
             $val = $propFind->get('{DAV:}sync-token');
877 921
             if ($val && is_scalar($val)) {
Please login to merge, or discard this patch.
settings/controller/appsettingscontroller.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@
 block discarded – undo
98 98
 	}
99 99
 
100 100
 	/**
101
-	 * @param string|int $category
101
+	 * @param string $category
102 102
 	 * @return int
103 103
 	 */
104 104
 	protected function getCategory($category) {
Please login to merge, or discard this patch.
Indentation   +277 added lines, -277 removed lines patch added patch discarded remove patch
@@ -43,281 +43,281 @@
 block discarded – undo
43 43
  * @package OC\Settings\Controller
44 44
  */
45 45
 class AppSettingsController extends Controller {
46
-	const CAT_ENABLED = 0;
47
-	const CAT_DISABLED = 1;
48
-
49
-	/** @var \OCP\IL10N */
50
-	private $l10n;
51
-	/** @var IConfig */
52
-	private $config;
53
-	/** @var \OCP\ICache */
54
-	private $cache;
55
-	/** @var INavigationManager */
56
-	private $navigationManager;
57
-	/** @var IAppManager */
58
-	private $appManager;
59
-	/** @var OCSClient */
60
-	private $ocsClient;
61
-
62
-	/**
63
-	 * @param string $appName
64
-	 * @param IRequest $request
65
-	 * @param IL10N $l10n
66
-	 * @param IConfig $config
67
-	 * @param ICacheFactory $cache
68
-	 * @param INavigationManager $navigationManager
69
-	 * @param IAppManager $appManager
70
-	 * @param OCSClient $ocsClient
71
-	 */
72
-	public function __construct($appName,
73
-								IRequest $request,
74
-								IL10N $l10n,
75
-								IConfig $config,
76
-								ICacheFactory $cache,
77
-								INavigationManager $navigationManager,
78
-								IAppManager $appManager,
79
-								OCSClient $ocsClient) {
80
-		parent::__construct($appName, $request);
81
-		$this->l10n = $l10n;
82
-		$this->config = $config;
83
-		$this->cache = $cache->create($appName);
84
-		$this->navigationManager = $navigationManager;
85
-		$this->appManager = $appManager;
86
-		$this->ocsClient = $ocsClient;
87
-	}
88
-
89
-	/**
90
-	 * Enables or disables the display of experimental apps
91
-	 * @param bool $state
92
-	 * @return DataResponse
93
-	 */
94
-	public function changeExperimentalConfigState($state) {
95
-		$this->config->setSystemValue('appstore.experimental.enabled', $state);
96
-		$this->appManager->clearAppsCache();
97
-		return new DataResponse();
98
-	}
99
-
100
-	/**
101
-	 * @param string|int $category
102
-	 * @return int
103
-	 */
104
-	protected function getCategory($category) {
105
-		if (is_string($category)) {
106
-			foreach ($this->listCategories() as $cat) {
107
-				if (isset($cat['ident']) && $cat['ident'] === $category) {
108
-					$category = (int) $cat['id'];
109
-					break;
110
-				}
111
-			}
112
-
113
-			// Didn't find the category, falling back to enabled
114
-			if (is_string($category)) {
115
-				$category = self::CAT_ENABLED;
116
-			}
117
-		}
118
-		return (int) $category;
119
-	}
120
-
121
-	/**
122
-	 * @NoCSRFRequired
123
-	 * @param string $category
124
-	 * @return TemplateResponse
125
-	 */
126
-	public function viewApps($category = '') {
127
-		$categoryId = $this->getCategory($category);
128
-		if ($categoryId === self::CAT_ENABLED) {
129
-			// Do not use an arbitrary input string, because we put the category in html
130
-			$category = 'enabled';
131
-		}
132
-
133
-		$params = [];
134
-		$params['experimentalEnabled'] = $this->config->getSystemValue('appstore.experimental.enabled', false);
135
-		$params['category'] = $category;
136
-		$params['appstoreEnabled'] = $this->config->getSystemValue('appstoreenabled', true) === true;
137
-		$this->navigationManager->setActiveEntry('core_apps');
138
-
139
-		$templateResponse = new TemplateResponse($this->appName, 'apps', $params, 'user');
140
-		$policy = new ContentSecurityPolicy();
141
-		$policy->addAllowedImageDomain('https://apps.owncloud.com');
142
-		$templateResponse->setContentSecurityPolicy($policy);
143
-
144
-		return $templateResponse;
145
-	}
146
-
147
-	/**
148
-	 * Get all available categories
149
-	 * @return array
150
-	 */
151
-	public function listCategories() {
152
-
153
-		if(!is_null($this->cache->get('listCategories'))) {
154
-			return $this->cache->get('listCategories');
155
-		}
156
-		$categories = [
157
-			['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled')],
158
-			['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Not enabled')],
159
-		];
160
-
161
-		if($this->ocsClient->isAppStoreEnabled()) {
162
-			// apps from external repo via OCS
163
-			$ocs = $this->ocsClient->getCategories(\OCP\Util::getVersion());
164
-			if ($ocs) {
165
-				foreach($ocs as $k => $v) {
166
-					$name = str_replace('ownCloud ', '', $v);
167
-					$ident = str_replace(' ', '-', urlencode(strtolower($name)));
168
-					$categories[] = [
169
-						'id' => $k,
170
-						'ident' => $ident,
171
-						'displayName' => $name,
172
-					];
173
-				}
174
-			}
175
-		}
176
-
177
-		$this->cache->set('listCategories', $categories, 3600);
178
-
179
-		return $categories;
180
-	}
181
-
182
-	/**
183
-	 * Get all available apps in a category
184
-	 *
185
-	 * @param string $category
186
-	 * @param bool $includeUpdateInfo Should we check whether there is an update
187
-	 *                                in the app store?
188
-	 * @return array
189
-	 */
190
-	public function listApps($category = '', $includeUpdateInfo = true) {
191
-		$category = $this->getCategory($category);
192
-		$cacheName = 'listApps-' . $category . '-' . (int) $includeUpdateInfo;
193
-
194
-		if(!is_null($this->cache->get($cacheName))) {
195
-			$apps = $this->cache->get($cacheName);
196
-		} else {
197
-			switch ($category) {
198
-				// installed apps
199
-				case 0:
200
-					$apps = $this->getInstalledApps($includeUpdateInfo);
201
-					usort($apps, function ($a, $b) {
202
-						$a = (string)$a['name'];
203
-						$b = (string)$b['name'];
204
-						if ($a === $b) {
205
-							return 0;
206
-						}
207
-						return ($a < $b) ? -1 : 1;
208
-					});
209
-					$version = \OCP\Util::getVersion();
210
-					foreach($apps as $key => $app) {
211
-						if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
212
-							$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], $version);
213
-
214
-							if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
215
-								$apps[$key]['level'] = $remoteAppEntry['level'];
216
-							}
217
-						}
218
-					}
219
-					break;
220
-				// not-installed apps
221
-				case 1:
222
-					$apps = \OC_App::listAllApps(true, $includeUpdateInfo, $this->ocsClient);
223
-					$apps = array_filter($apps, function ($app) {
224
-						return !$app['active'];
225
-					});
226
-					$version = \OCP\Util::getVersion();
227
-					foreach($apps as $key => $app) {
228
-						if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
229
-							$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], $version);
230
-
231
-							if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
232
-								$apps[$key]['level'] = $remoteAppEntry['level'];
233
-							}
234
-						}
235
-					}
236
-					usort($apps, function ($a, $b) {
237
-						$a = (string)$a['name'];
238
-						$b = (string)$b['name'];
239
-						if ($a === $b) {
240
-							return 0;
241
-						}
242
-						return ($a < $b) ? -1 : 1;
243
-					});
244
-					break;
245
-				default:
246
-					$filter = $this->config->getSystemValue('appstore.experimental.enabled', false) ? 'all' : 'approved';
247
-
248
-					$apps = \OC_App::getAppstoreApps($filter, $category, $this->ocsClient);
249
-					if (!$apps) {
250
-						$apps = array();
251
-					} else {
252
-						// don't list installed apps
253
-						$installedApps = $this->getInstalledApps(false);
254
-						$installedApps = array_map(function ($app) {
255
-							if (isset($app['ocsid'])) {
256
-								return $app['ocsid'];
257
-							}
258
-							return $app['id'];
259
-						}, $installedApps);
260
-						$apps = array_filter($apps, function ($app) use ($installedApps) {
261
-							return !in_array($app['id'], $installedApps);
262
-						});
263
-					}
264
-
265
-					// sort by score
266
-					usort($apps, function ($a, $b) {
267
-						$a = (int)$a['score'];
268
-						$b = (int)$b['score'];
269
-						if ($a === $b) {
270
-							return 0;
271
-						}
272
-						return ($a > $b) ? -1 : 1;
273
-					});
274
-					break;
275
-			}
276
-		}
277
-
278
-		// fix groups to be an array
279
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($this->config), $this->l10n);
280
-		$apps = array_map(function($app) use ($dependencyAnalyzer) {
281
-
282
-			// fix groups
283
-			$groups = array();
284
-			if (is_string($app['groups'])) {
285
-				$groups = json_decode($app['groups']);
286
-			}
287
-			$app['groups'] = $groups;
288
-			$app['canUnInstall'] = !$app['active'] && $app['removable'];
289
-
290
-			// fix licence vs license
291
-			if (isset($app['license']) && !isset($app['licence'])) {
292
-				$app['licence'] = $app['license'];
293
-			}
294
-
295
-			// analyse dependencies
296
-			$missing = $dependencyAnalyzer->analyze($app);
297
-			$app['canInstall'] = empty($missing);
298
-			$app['missingDependencies'] = $missing;
299
-
300
-			$app['missingMinOwnCloudVersion'] = !isset($app['dependencies']['owncloud']['@attributes']['min-version']);
301
-			$app['missingMaxOwnCloudVersion'] = !isset($app['dependencies']['owncloud']['@attributes']['max-version']);
302
-
303
-			return $app;
304
-		}, $apps);
305
-
306
-		$this->cache->set($cacheName, $apps, 300);
307
-
308
-		return ['apps' => $apps, 'status' => 'success'];
309
-	}
310
-
311
-	/**
312
-	 * @param bool $includeUpdateInfo Should we check whether there is an update
313
-	 *                                in the app store?
314
-	 * @return array
315
-	 */
316
-	private function getInstalledApps($includeUpdateInfo = true) {
317
-		$apps = \OC_App::listAllApps(true, $includeUpdateInfo, $this->ocsClient);
318
-		$apps = array_filter($apps, function ($app) {
319
-			return $app['active'];
320
-		});
321
-		return $apps;
322
-	}
46
+    const CAT_ENABLED = 0;
47
+    const CAT_DISABLED = 1;
48
+
49
+    /** @var \OCP\IL10N */
50
+    private $l10n;
51
+    /** @var IConfig */
52
+    private $config;
53
+    /** @var \OCP\ICache */
54
+    private $cache;
55
+    /** @var INavigationManager */
56
+    private $navigationManager;
57
+    /** @var IAppManager */
58
+    private $appManager;
59
+    /** @var OCSClient */
60
+    private $ocsClient;
61
+
62
+    /**
63
+     * @param string $appName
64
+     * @param IRequest $request
65
+     * @param IL10N $l10n
66
+     * @param IConfig $config
67
+     * @param ICacheFactory $cache
68
+     * @param INavigationManager $navigationManager
69
+     * @param IAppManager $appManager
70
+     * @param OCSClient $ocsClient
71
+     */
72
+    public function __construct($appName,
73
+                                IRequest $request,
74
+                                IL10N $l10n,
75
+                                IConfig $config,
76
+                                ICacheFactory $cache,
77
+                                INavigationManager $navigationManager,
78
+                                IAppManager $appManager,
79
+                                OCSClient $ocsClient) {
80
+        parent::__construct($appName, $request);
81
+        $this->l10n = $l10n;
82
+        $this->config = $config;
83
+        $this->cache = $cache->create($appName);
84
+        $this->navigationManager = $navigationManager;
85
+        $this->appManager = $appManager;
86
+        $this->ocsClient = $ocsClient;
87
+    }
88
+
89
+    /**
90
+     * Enables or disables the display of experimental apps
91
+     * @param bool $state
92
+     * @return DataResponse
93
+     */
94
+    public function changeExperimentalConfigState($state) {
95
+        $this->config->setSystemValue('appstore.experimental.enabled', $state);
96
+        $this->appManager->clearAppsCache();
97
+        return new DataResponse();
98
+    }
99
+
100
+    /**
101
+     * @param string|int $category
102
+     * @return int
103
+     */
104
+    protected function getCategory($category) {
105
+        if (is_string($category)) {
106
+            foreach ($this->listCategories() as $cat) {
107
+                if (isset($cat['ident']) && $cat['ident'] === $category) {
108
+                    $category = (int) $cat['id'];
109
+                    break;
110
+                }
111
+            }
112
+
113
+            // Didn't find the category, falling back to enabled
114
+            if (is_string($category)) {
115
+                $category = self::CAT_ENABLED;
116
+            }
117
+        }
118
+        return (int) $category;
119
+    }
120
+
121
+    /**
122
+     * @NoCSRFRequired
123
+     * @param string $category
124
+     * @return TemplateResponse
125
+     */
126
+    public function viewApps($category = '') {
127
+        $categoryId = $this->getCategory($category);
128
+        if ($categoryId === self::CAT_ENABLED) {
129
+            // Do not use an arbitrary input string, because we put the category in html
130
+            $category = 'enabled';
131
+        }
132
+
133
+        $params = [];
134
+        $params['experimentalEnabled'] = $this->config->getSystemValue('appstore.experimental.enabled', false);
135
+        $params['category'] = $category;
136
+        $params['appstoreEnabled'] = $this->config->getSystemValue('appstoreenabled', true) === true;
137
+        $this->navigationManager->setActiveEntry('core_apps');
138
+
139
+        $templateResponse = new TemplateResponse($this->appName, 'apps', $params, 'user');
140
+        $policy = new ContentSecurityPolicy();
141
+        $policy->addAllowedImageDomain('https://apps.owncloud.com');
142
+        $templateResponse->setContentSecurityPolicy($policy);
143
+
144
+        return $templateResponse;
145
+    }
146
+
147
+    /**
148
+     * Get all available categories
149
+     * @return array
150
+     */
151
+    public function listCategories() {
152
+
153
+        if(!is_null($this->cache->get('listCategories'))) {
154
+            return $this->cache->get('listCategories');
155
+        }
156
+        $categories = [
157
+            ['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled')],
158
+            ['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Not enabled')],
159
+        ];
160
+
161
+        if($this->ocsClient->isAppStoreEnabled()) {
162
+            // apps from external repo via OCS
163
+            $ocs = $this->ocsClient->getCategories(\OCP\Util::getVersion());
164
+            if ($ocs) {
165
+                foreach($ocs as $k => $v) {
166
+                    $name = str_replace('ownCloud ', '', $v);
167
+                    $ident = str_replace(' ', '-', urlencode(strtolower($name)));
168
+                    $categories[] = [
169
+                        'id' => $k,
170
+                        'ident' => $ident,
171
+                        'displayName' => $name,
172
+                    ];
173
+                }
174
+            }
175
+        }
176
+
177
+        $this->cache->set('listCategories', $categories, 3600);
178
+
179
+        return $categories;
180
+    }
181
+
182
+    /**
183
+     * Get all available apps in a category
184
+     *
185
+     * @param string $category
186
+     * @param bool $includeUpdateInfo Should we check whether there is an update
187
+     *                                in the app store?
188
+     * @return array
189
+     */
190
+    public function listApps($category = '', $includeUpdateInfo = true) {
191
+        $category = $this->getCategory($category);
192
+        $cacheName = 'listApps-' . $category . '-' . (int) $includeUpdateInfo;
193
+
194
+        if(!is_null($this->cache->get($cacheName))) {
195
+            $apps = $this->cache->get($cacheName);
196
+        } else {
197
+            switch ($category) {
198
+                // installed apps
199
+                case 0:
200
+                    $apps = $this->getInstalledApps($includeUpdateInfo);
201
+                    usort($apps, function ($a, $b) {
202
+                        $a = (string)$a['name'];
203
+                        $b = (string)$b['name'];
204
+                        if ($a === $b) {
205
+                            return 0;
206
+                        }
207
+                        return ($a < $b) ? -1 : 1;
208
+                    });
209
+                    $version = \OCP\Util::getVersion();
210
+                    foreach($apps as $key => $app) {
211
+                        if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
212
+                            $remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], $version);
213
+
214
+                            if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
215
+                                $apps[$key]['level'] = $remoteAppEntry['level'];
216
+                            }
217
+                        }
218
+                    }
219
+                    break;
220
+                // not-installed apps
221
+                case 1:
222
+                    $apps = \OC_App::listAllApps(true, $includeUpdateInfo, $this->ocsClient);
223
+                    $apps = array_filter($apps, function ($app) {
224
+                        return !$app['active'];
225
+                    });
226
+                    $version = \OCP\Util::getVersion();
227
+                    foreach($apps as $key => $app) {
228
+                        if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
229
+                            $remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], $version);
230
+
231
+                            if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
232
+                                $apps[$key]['level'] = $remoteAppEntry['level'];
233
+                            }
234
+                        }
235
+                    }
236
+                    usort($apps, function ($a, $b) {
237
+                        $a = (string)$a['name'];
238
+                        $b = (string)$b['name'];
239
+                        if ($a === $b) {
240
+                            return 0;
241
+                        }
242
+                        return ($a < $b) ? -1 : 1;
243
+                    });
244
+                    break;
245
+                default:
246
+                    $filter = $this->config->getSystemValue('appstore.experimental.enabled', false) ? 'all' : 'approved';
247
+
248
+                    $apps = \OC_App::getAppstoreApps($filter, $category, $this->ocsClient);
249
+                    if (!$apps) {
250
+                        $apps = array();
251
+                    } else {
252
+                        // don't list installed apps
253
+                        $installedApps = $this->getInstalledApps(false);
254
+                        $installedApps = array_map(function ($app) {
255
+                            if (isset($app['ocsid'])) {
256
+                                return $app['ocsid'];
257
+                            }
258
+                            return $app['id'];
259
+                        }, $installedApps);
260
+                        $apps = array_filter($apps, function ($app) use ($installedApps) {
261
+                            return !in_array($app['id'], $installedApps);
262
+                        });
263
+                    }
264
+
265
+                    // sort by score
266
+                    usort($apps, function ($a, $b) {
267
+                        $a = (int)$a['score'];
268
+                        $b = (int)$b['score'];
269
+                        if ($a === $b) {
270
+                            return 0;
271
+                        }
272
+                        return ($a > $b) ? -1 : 1;
273
+                    });
274
+                    break;
275
+            }
276
+        }
277
+
278
+        // fix groups to be an array
279
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($this->config), $this->l10n);
280
+        $apps = array_map(function($app) use ($dependencyAnalyzer) {
281
+
282
+            // fix groups
283
+            $groups = array();
284
+            if (is_string($app['groups'])) {
285
+                $groups = json_decode($app['groups']);
286
+            }
287
+            $app['groups'] = $groups;
288
+            $app['canUnInstall'] = !$app['active'] && $app['removable'];
289
+
290
+            // fix licence vs license
291
+            if (isset($app['license']) && !isset($app['licence'])) {
292
+                $app['licence'] = $app['license'];
293
+            }
294
+
295
+            // analyse dependencies
296
+            $missing = $dependencyAnalyzer->analyze($app);
297
+            $app['canInstall'] = empty($missing);
298
+            $app['missingDependencies'] = $missing;
299
+
300
+            $app['missingMinOwnCloudVersion'] = !isset($app['dependencies']['owncloud']['@attributes']['min-version']);
301
+            $app['missingMaxOwnCloudVersion'] = !isset($app['dependencies']['owncloud']['@attributes']['max-version']);
302
+
303
+            return $app;
304
+        }, $apps);
305
+
306
+        $this->cache->set($cacheName, $apps, 300);
307
+
308
+        return ['apps' => $apps, 'status' => 'success'];
309
+    }
310
+
311
+    /**
312
+     * @param bool $includeUpdateInfo Should we check whether there is an update
313
+     *                                in the app store?
314
+     * @return array
315
+     */
316
+    private function getInstalledApps($includeUpdateInfo = true) {
317
+        $apps = \OC_App::listAllApps(true, $includeUpdateInfo, $this->ocsClient);
318
+        $apps = array_filter($apps, function ($app) {
319
+            return $app['active'];
320
+        });
321
+        return $apps;
322
+    }
323 323
 }
Please login to merge, or discard this patch.
Spacing   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -150,19 +150,19 @@  discard block
 block discarded – undo
150 150
 	 */
151 151
 	public function listCategories() {
152 152
 
153
-		if(!is_null($this->cache->get('listCategories'))) {
153
+		if (!is_null($this->cache->get('listCategories'))) {
154 154
 			return $this->cache->get('listCategories');
155 155
 		}
156 156
 		$categories = [
157
-			['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled')],
158
-			['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Not enabled')],
157
+			['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string) $this->l10n->t('Enabled')],
158
+			['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string) $this->l10n->t('Not enabled')],
159 159
 		];
160 160
 
161
-		if($this->ocsClient->isAppStoreEnabled()) {
161
+		if ($this->ocsClient->isAppStoreEnabled()) {
162 162
 			// apps from external repo via OCS
163 163
 			$ocs = $this->ocsClient->getCategories(\OCP\Util::getVersion());
164 164
 			if ($ocs) {
165
-				foreach($ocs as $k => $v) {
165
+				foreach ($ocs as $k => $v) {
166 166
 					$name = str_replace('ownCloud ', '', $v);
167 167
 					$ident = str_replace(' ', '-', urlencode(strtolower($name)));
168 168
 					$categories[] = [
@@ -189,29 +189,29 @@  discard block
 block discarded – undo
189 189
 	 */
190 190
 	public function listApps($category = '', $includeUpdateInfo = true) {
191 191
 		$category = $this->getCategory($category);
192
-		$cacheName = 'listApps-' . $category . '-' . (int) $includeUpdateInfo;
192
+		$cacheName = 'listApps-'.$category.'-'.(int) $includeUpdateInfo;
193 193
 
194
-		if(!is_null($this->cache->get($cacheName))) {
194
+		if (!is_null($this->cache->get($cacheName))) {
195 195
 			$apps = $this->cache->get($cacheName);
196 196
 		} else {
197 197
 			switch ($category) {
198 198
 				// installed apps
199 199
 				case 0:
200 200
 					$apps = $this->getInstalledApps($includeUpdateInfo);
201
-					usort($apps, function ($a, $b) {
202
-						$a = (string)$a['name'];
203
-						$b = (string)$b['name'];
201
+					usort($apps, function($a, $b) {
202
+						$a = (string) $a['name'];
203
+						$b = (string) $b['name'];
204 204
 						if ($a === $b) {
205 205
 							return 0;
206 206
 						}
207 207
 						return ($a < $b) ? -1 : 1;
208 208
 					});
209 209
 					$version = \OCP\Util::getVersion();
210
-					foreach($apps as $key => $app) {
211
-						if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
210
+					foreach ($apps as $key => $app) {
211
+						if (!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
212 212
 							$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], $version);
213 213
 
214
-							if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
214
+							if (is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
215 215
 								$apps[$key]['level'] = $remoteAppEntry['level'];
216 216
 							}
217 217
 						}
@@ -220,22 +220,22 @@  discard block
 block discarded – undo
220 220
 				// not-installed apps
221 221
 				case 1:
222 222
 					$apps = \OC_App::listAllApps(true, $includeUpdateInfo, $this->ocsClient);
223
-					$apps = array_filter($apps, function ($app) {
223
+					$apps = array_filter($apps, function($app) {
224 224
 						return !$app['active'];
225 225
 					});
226 226
 					$version = \OCP\Util::getVersion();
227
-					foreach($apps as $key => $app) {
228
-						if(!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
227
+					foreach ($apps as $key => $app) {
228
+						if (!array_key_exists('level', $app) && array_key_exists('ocsid', $app)) {
229 229
 							$remoteAppEntry = $this->ocsClient->getApplication($app['ocsid'], $version);
230 230
 
231
-							if(is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
231
+							if (is_array($remoteAppEntry) && array_key_exists('level', $remoteAppEntry)) {
232 232
 								$apps[$key]['level'] = $remoteAppEntry['level'];
233 233
 							}
234 234
 						}
235 235
 					}
236
-					usort($apps, function ($a, $b) {
237
-						$a = (string)$a['name'];
238
-						$b = (string)$b['name'];
236
+					usort($apps, function($a, $b) {
237
+						$a = (string) $a['name'];
238
+						$b = (string) $b['name'];
239 239
 						if ($a === $b) {
240 240
 							return 0;
241 241
 						}
@@ -251,21 +251,21 @@  discard block
 block discarded – undo
251 251
 					} else {
252 252
 						// don't list installed apps
253 253
 						$installedApps = $this->getInstalledApps(false);
254
-						$installedApps = array_map(function ($app) {
254
+						$installedApps = array_map(function($app) {
255 255
 							if (isset($app['ocsid'])) {
256 256
 								return $app['ocsid'];
257 257
 							}
258 258
 							return $app['id'];
259 259
 						}, $installedApps);
260
-						$apps = array_filter($apps, function ($app) use ($installedApps) {
260
+						$apps = array_filter($apps, function($app) use ($installedApps) {
261 261
 							return !in_array($app['id'], $installedApps);
262 262
 						});
263 263
 					}
264 264
 
265 265
 					// sort by score
266
-					usort($apps, function ($a, $b) {
267
-						$a = (int)$a['score'];
268
-						$b = (int)$b['score'];
266
+					usort($apps, function($a, $b) {
267
+						$a = (int) $a['score'];
268
+						$b = (int) $b['score'];
269 269
 						if ($a === $b) {
270 270
 							return 0;
271 271
 						}
@@ -315,7 +315,7 @@  discard block
 block discarded – undo
315 315
 	 */
316 316
 	private function getInstalledApps($includeUpdateInfo = true) {
317 317
 		$apps = \OC_App::listAllApps(true, $includeUpdateInfo, $this->ocsClient);
318
-		$apps = array_filter($apps, function ($app) {
318
+		$apps = array_filter($apps, function($app) {
319 319
 			return $app['active'];
320 320
 		});
321 321
 		return $apps;
Please login to merge, or discard this patch.
settings/controller/certificatecontroller.php 2 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 	 *
72 72
 	 * @NoAdminRequired
73 73
 	 * @NoSubadminRequired
74
-	 * @return array
74
+	 * @return DataResponse
75 75
 	 */
76 76
 	public function addPersonalRootCertificate() {
77 77
 		return $this->addCertificate($this->userCertificateManager);
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
 	 * Add a new root certificate to a trust store
82 82
 	 *
83 83
 	 * @param ICertificateManager $certificateManager
84
-	 * @return array
84
+	 * @return DataResponse
85 85
 	 */
86 86
 	private function addCertificate(ICertificateManager $certificateManager) {
87 87
 		$headers = [];
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 	/**
160 160
 	 * Add a new personal root certificate to the system's trust store
161 161
 	 *
162
-	 * @return array
162
+	 * @return DataResponse
163 163
 	 */
164 164
 	public function addSystemRootCertificate() {
165 165
 		return $this->addCertificate($this->systemCertificateManager);
Please login to merge, or discard this patch.
Indentation   +144 added lines, -144 removed lines patch added patch discarded remove patch
@@ -37,148 +37,148 @@
 block discarded – undo
37 37
  * @package OC\Settings\Controller
38 38
  */
39 39
 class CertificateController extends Controller {
40
-	/** @var ICertificateManager */
41
-	private $userCertificateManager;
42
-	/** @var ICertificateManager  */
43
-	private $systemCertificateManager;
44
-	/** @var IL10N */
45
-	private $l10n;
46
-	/** @var IAppManager */
47
-	private $appManager;
48
-
49
-	/**
50
-	 * @param string $appName
51
-	 * @param IRequest $request
52
-	 * @param ICertificateManager $userCertificateManager
53
-	 * @param ICertificateManager $systemCertificateManager
54
-	 * @param IL10N $l10n
55
-	 * @param IAppManager $appManager
56
-	 */
57
-	public function __construct($appName,
58
-								IRequest $request,
59
-								ICertificateManager $userCertificateManager,
60
-								ICertificateManager $systemCertificateManager,
61
-								IL10N $l10n,
62
-								IAppManager $appManager) {
63
-		parent::__construct($appName, $request);
64
-		$this->userCertificateManager = $userCertificateManager;
65
-		$this->systemCertificateManager = $systemCertificateManager;
66
-		$this->l10n = $l10n;
67
-		$this->appManager = $appManager;
68
-	}
69
-
70
-	/**
71
-	 * Add a new personal root certificate to the users' trust store
72
-	 *
73
-	 * @NoAdminRequired
74
-	 * @NoSubadminRequired
75
-	 * @return array
76
-	 */
77
-	public function addPersonalRootCertificate() {
78
-		return $this->addCertificate($this->userCertificateManager);
79
-	}
80
-
81
-	/**
82
-	 * Add a new root certificate to a trust store
83
-	 *
84
-	 * @param ICertificateManager $certificateManager
85
-	 * @return array
86
-	 */
87
-	private function addCertificate(ICertificateManager $certificateManager) {
88
-		$headers = [];
89
-		if ($this->request->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE_8])) {
90
-			// due to upload iframe workaround, need to set content-type to text/plain
91
-			$headers['Content-Type'] = 'text/plain';
92
-		}
93
-
94
-		if ($this->isCertificateImportAllowed() === false) {
95
-			return new DataResponse(['message' => 'Individual certificate management disabled'], Http::STATUS_FORBIDDEN, $headers);
96
-		}
97
-
98
-		$file = $this->request->getUploadedFile('rootcert_import');
99
-		if (empty($file)) {
100
-			return new DataResponse(['message' => 'No file uploaded'], Http::STATUS_UNPROCESSABLE_ENTITY, $headers);
101
-		}
102
-
103
-		try {
104
-			$certificate = $certificateManager->addCertificate(file_get_contents($file['tmp_name']), $file['name']);
105
-			return new DataResponse(
106
-				[
107
-					'name' => $certificate->getName(),
108
-					'commonName' => $certificate->getCommonName(),
109
-					'organization' => $certificate->getOrganization(),
110
-					'validFrom' => $certificate->getIssueDate()->getTimestamp(),
111
-					'validTill' => $certificate->getExpireDate()->getTimestamp(),
112
-					'validFromString' => $this->l10n->l('date', $certificate->getIssueDate()),
113
-					'validTillString' => $this->l10n->l('date', $certificate->getExpireDate()),
114
-					'issuer' => $certificate->getIssuerName(),
115
-					'issuerOrganization' => $certificate->getIssuerOrganization(),
116
-				],
117
-				Http::STATUS_OK,
118
-				$headers
119
-			);
120
-		} catch (\Exception $e) {
121
-			return new DataResponse('An error occurred.', Http::STATUS_UNPROCESSABLE_ENTITY, $headers);
122
-		}
123
-	}
124
-
125
-	/**
126
-	 * Removes a personal root certificate from the users' trust store
127
-	 *
128
-	 * @NoAdminRequired
129
-	 * @NoSubadminRequired
130
-	 * @param string $certificateIdentifier
131
-	 * @return DataResponse
132
-	 */
133
-	public function removePersonalRootCertificate($certificateIdentifier) {
134
-
135
-		if ($this->isCertificateImportAllowed() === false) {
136
-			return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN);
137
-		}
138
-
139
-		$this->userCertificateManager->removeCertificate($certificateIdentifier);
140
-		return new DataResponse();
141
-	}
142
-
143
-	/**
144
-	 * check if certificate import is allowed
145
-	 *
146
-	 * @return bool
147
-	 */
148
-	protected function isCertificateImportAllowed() {
149
-		$externalStorageEnabled = $this->appManager->isEnabledForUser('files_external');
150
-		if ($externalStorageEnabled) {
151
-			/** @var \OCA\Files_External\Service\BackendService $backendService */
152
-			$backendService = \OC_Mount_Config::$app->getContainer()->query('\OCA\Files_External\Service\BackendService');
153
-			if ($backendService->isUserMountingAllowed()) {
154
-				return true;
155
-			}
156
-		}
157
-		return false;
158
-	}
159
-
160
-	/**
161
-	 * Add a new personal root certificate to the system's trust store
162
-	 *
163
-	 * @return array
164
-	 */
165
-	public function addSystemRootCertificate() {
166
-		return $this->addCertificate($this->systemCertificateManager);
167
-	}
168
-
169
-	/**
170
-	 * Removes a personal root certificate from the users' trust store
171
-	 *
172
-	 * @param string $certificateIdentifier
173
-	 * @return DataResponse
174
-	 */
175
-	public function removeSystemRootCertificate($certificateIdentifier) {
176
-
177
-		if ($this->isCertificateImportAllowed() === false) {
178
-			return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN);
179
-		}
180
-
181
-		$this->systemCertificateManager->removeCertificate($certificateIdentifier);
182
-		return new DataResponse();
183
-	}
40
+    /** @var ICertificateManager */
41
+    private $userCertificateManager;
42
+    /** @var ICertificateManager  */
43
+    private $systemCertificateManager;
44
+    /** @var IL10N */
45
+    private $l10n;
46
+    /** @var IAppManager */
47
+    private $appManager;
48
+
49
+    /**
50
+     * @param string $appName
51
+     * @param IRequest $request
52
+     * @param ICertificateManager $userCertificateManager
53
+     * @param ICertificateManager $systemCertificateManager
54
+     * @param IL10N $l10n
55
+     * @param IAppManager $appManager
56
+     */
57
+    public function __construct($appName,
58
+                                IRequest $request,
59
+                                ICertificateManager $userCertificateManager,
60
+                                ICertificateManager $systemCertificateManager,
61
+                                IL10N $l10n,
62
+                                IAppManager $appManager) {
63
+        parent::__construct($appName, $request);
64
+        $this->userCertificateManager = $userCertificateManager;
65
+        $this->systemCertificateManager = $systemCertificateManager;
66
+        $this->l10n = $l10n;
67
+        $this->appManager = $appManager;
68
+    }
69
+
70
+    /**
71
+     * Add a new personal root certificate to the users' trust store
72
+     *
73
+     * @NoAdminRequired
74
+     * @NoSubadminRequired
75
+     * @return array
76
+     */
77
+    public function addPersonalRootCertificate() {
78
+        return $this->addCertificate($this->userCertificateManager);
79
+    }
80
+
81
+    /**
82
+     * Add a new root certificate to a trust store
83
+     *
84
+     * @param ICertificateManager $certificateManager
85
+     * @return array
86
+     */
87
+    private function addCertificate(ICertificateManager $certificateManager) {
88
+        $headers = [];
89
+        if ($this->request->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE_8])) {
90
+            // due to upload iframe workaround, need to set content-type to text/plain
91
+            $headers['Content-Type'] = 'text/plain';
92
+        }
93
+
94
+        if ($this->isCertificateImportAllowed() === false) {
95
+            return new DataResponse(['message' => 'Individual certificate management disabled'], Http::STATUS_FORBIDDEN, $headers);
96
+        }
97
+
98
+        $file = $this->request->getUploadedFile('rootcert_import');
99
+        if (empty($file)) {
100
+            return new DataResponse(['message' => 'No file uploaded'], Http::STATUS_UNPROCESSABLE_ENTITY, $headers);
101
+        }
102
+
103
+        try {
104
+            $certificate = $certificateManager->addCertificate(file_get_contents($file['tmp_name']), $file['name']);
105
+            return new DataResponse(
106
+                [
107
+                    'name' => $certificate->getName(),
108
+                    'commonName' => $certificate->getCommonName(),
109
+                    'organization' => $certificate->getOrganization(),
110
+                    'validFrom' => $certificate->getIssueDate()->getTimestamp(),
111
+                    'validTill' => $certificate->getExpireDate()->getTimestamp(),
112
+                    'validFromString' => $this->l10n->l('date', $certificate->getIssueDate()),
113
+                    'validTillString' => $this->l10n->l('date', $certificate->getExpireDate()),
114
+                    'issuer' => $certificate->getIssuerName(),
115
+                    'issuerOrganization' => $certificate->getIssuerOrganization(),
116
+                ],
117
+                Http::STATUS_OK,
118
+                $headers
119
+            );
120
+        } catch (\Exception $e) {
121
+            return new DataResponse('An error occurred.', Http::STATUS_UNPROCESSABLE_ENTITY, $headers);
122
+        }
123
+    }
124
+
125
+    /**
126
+     * Removes a personal root certificate from the users' trust store
127
+     *
128
+     * @NoAdminRequired
129
+     * @NoSubadminRequired
130
+     * @param string $certificateIdentifier
131
+     * @return DataResponse
132
+     */
133
+    public function removePersonalRootCertificate($certificateIdentifier) {
134
+
135
+        if ($this->isCertificateImportAllowed() === false) {
136
+            return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN);
137
+        }
138
+
139
+        $this->userCertificateManager->removeCertificate($certificateIdentifier);
140
+        return new DataResponse();
141
+    }
142
+
143
+    /**
144
+     * check if certificate import is allowed
145
+     *
146
+     * @return bool
147
+     */
148
+    protected function isCertificateImportAllowed() {
149
+        $externalStorageEnabled = $this->appManager->isEnabledForUser('files_external');
150
+        if ($externalStorageEnabled) {
151
+            /** @var \OCA\Files_External\Service\BackendService $backendService */
152
+            $backendService = \OC_Mount_Config::$app->getContainer()->query('\OCA\Files_External\Service\BackendService');
153
+            if ($backendService->isUserMountingAllowed()) {
154
+                return true;
155
+            }
156
+        }
157
+        return false;
158
+    }
159
+
160
+    /**
161
+     * Add a new personal root certificate to the system's trust store
162
+     *
163
+     * @return array
164
+     */
165
+    public function addSystemRootCertificate() {
166
+        return $this->addCertificate($this->systemCertificateManager);
167
+    }
168
+
169
+    /**
170
+     * Removes a personal root certificate from the users' trust store
171
+     *
172
+     * @param string $certificateIdentifier
173
+     * @return DataResponse
174
+     */
175
+    public function removeSystemRootCertificate($certificateIdentifier) {
176
+
177
+        if ($this->isCertificateImportAllowed() === false) {
178
+            return new DataResponse('Individual certificate management disabled', Http::STATUS_FORBIDDEN);
179
+        }
180
+
181
+        $this->systemCertificateManager->removeCertificate($certificateIdentifier);
182
+        return new DataResponse();
183
+    }
184 184
 }
Please login to merge, or discard this patch.
settings/controller/checksetupcontroller.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -268,7 +268,7 @@
 block discarded – undo
268 268
 
269 269
 	/**
270 270
 	 * @NoCSRFRequired
271
-	 * @return DataResponse
271
+	 * @return DataDisplayResponse
272 272
 	 */
273 273
 	public function getFailedIntegrityCheckFiles() {
274 274
 		if(!$this->checker->isCodeCheckEnforced()) {
Please login to merge, or discard this patch.
Indentation   +293 added lines, -293 removed lines patch added patch discarded remove patch
@@ -44,242 +44,242 @@  discard block
 block discarded – undo
44 44
  * @package OC\Settings\Controller
45 45
  */
46 46
 class CheckSetupController extends Controller {
47
-	/** @var IConfig */
48
-	private $config;
49
-	/** @var IClientService */
50
-	private $clientService;
51
-	/** @var \OC_Util */
52
-	private $util;
53
-	/** @var IURLGenerator */
54
-	private $urlGenerator;
55
-	/** @var IL10N */
56
-	private $l10n;
57
-	/** @var Checker */
58
-	private $checker;
59
-
60
-	/**
61
-	 * @param string $AppName
62
-	 * @param IRequest $request
63
-	 * @param IConfig $config
64
-	 * @param IClientService $clientService
65
-	 * @param IURLGenerator $urlGenerator
66
-	 * @param \OC_Util $util
67
-	 * @param IL10N $l10n
68
-	 * @param Checker $checker
69
-	 */
70
-	public function __construct($AppName,
71
-								IRequest $request,
72
-								IConfig $config,
73
-								IClientService $clientService,
74
-								IURLGenerator $urlGenerator,
75
-								\OC_Util $util,
76
-								IL10N $l10n,
77
-								Checker $checker) {
78
-		parent::__construct($AppName, $request);
79
-		$this->config = $config;
80
-		$this->clientService = $clientService;
81
-		$this->util = $util;
82
-		$this->urlGenerator = $urlGenerator;
83
-		$this->l10n = $l10n;
84
-		$this->checker = $checker;
85
-	}
86
-
87
-	/**
88
-	 * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP
89
-	 * @return bool
90
-	 */
91
-	private function isInternetConnectionWorking() {
92
-		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
93
-			return false;
94
-		}
95
-
96
-		try {
97
-			$client = $this->clientService->newClient();
98
-			$client->get('https://www.owncloud.org/');
99
-			$client->get('http://www.owncloud.org/');
100
-			return true;
101
-		} catch (\Exception $e) {
102
-			return false;
103
-		}
104
-	}
105
-
106
-	/**
107
-	 * Checks whether a local memcache is installed or not
108
-	 * @return bool
109
-	 */
110
-	private function isMemcacheConfigured() {
111
-		return $this->config->getSystemValue('memcache.local', null) !== null;
112
-	}
113
-
114
-	/**
115
-	 * Whether /dev/urandom is available to the PHP controller
116
-	 *
117
-	 * @return bool
118
-	 */
119
-	private function isUrandomAvailable() {
120
-		if(@file_exists('/dev/urandom')) {
121
-			$file = fopen('/dev/urandom', 'rb');
122
-			if($file) {
123
-				fclose($file);
124
-				return true;
125
-			}
126
-		}
127
-
128
-		return false;
129
-	}
130
-
131
-	/**
132
-	 * Public for the sake of unit-testing
133
-	 *
134
-	 * @return array
135
-	 */
136
-	protected function getCurlVersion() {
137
-		return curl_version();
138
-	}
139
-
140
-	/**
141
-	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
142
-	 * have multiple bugs which likely lead to problems in combination with
143
-	 * functionality required by ownCloud such as SNI.
144
-	 *
145
-	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
146
-	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
147
-	 * @return string
148
-	 */
149
-	private function isUsedTlsLibOutdated() {
150
-		// Appstore is disabled by default in EE
151
-		$appStoreDefault = false;
152
-		if (\OC_Util::getEditionString() === '') {
153
-			$appStoreDefault = true;
154
-		}
155
-
156
-		// Don't run check when:
157
-		// 1. Server has `has_internet_connection` set to false
158
-		// 2. AppStore AND S2S is disabled
159
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
160
-			return '';
161
-		}
162
-		if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)
163
-			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
164
-			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
165
-			return '';
166
-		}
167
-
168
-		$versionString = $this->getCurlVersion();
169
-		if(isset($versionString['ssl_version'])) {
170
-			$versionString = $versionString['ssl_version'];
171
-		} else {
172
-			return '';
173
-		}
174
-
175
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
176
-		if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)) {
177
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
178
-		}
179
-
180
-		// Check if at least OpenSSL after 1.01d or 1.0.2b
181
-		if(strpos($versionString, 'OpenSSL/') === 0) {
182
-			$majorVersion = substr($versionString, 8, 5);
183
-			$patchRelease = substr($versionString, 13, 6);
184
-
185
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
186
-				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
187
-				return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
188
-			}
189
-		}
190
-
191
-		// Check if NSS and perform heuristic check
192
-		if(strpos($versionString, 'NSS/') === 0) {
193
-			try {
194
-				$firstClient = $this->clientService->newClient();
195
-				$firstClient->get('https://www.owncloud.org/');
196
-
197
-				$secondClient = $this->clientService->newClient();
198
-				$secondClient->get('https://owncloud.org/');
199
-			} catch (ClientException $e) {
200
-				if($e->getResponse()->getStatusCode() === 400) {
201
-					return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
202
-				}
203
-			}
204
-		}
205
-
206
-		return '';
207
-	}
208
-
209
-	/**
210
-	 * Whether the php version is still supported (at time of release)
211
-	 * according to: https://secure.php.net/supported-versions.php
212
-	 *
213
-	 * @return array
214
-	 */
215
-	private function isPhpSupported() {
216
-		$eol = false;
217
-
218
-		//PHP 5.4 is EOL on 14 Sep 2015
219
-		if (version_compare(PHP_VERSION, '5.5.0') === -1) {
220
-			$eol = true;
221
-		}
222
-
223
-		return ['eol' => $eol, 'version' => PHP_VERSION];
224
-	}
225
-
226
-	/**
227
-	 * Check if the reverse proxy configuration is working as expected
228
-	 *
229
-	 * @return bool
230
-	 */
231
-	private function forwardedForHeadersWorking() {
232
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
233
-		$remoteAddress = $this->request->getRemoteAddress();
234
-
235
-		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
236
-			return false;
237
-		}
238
-
239
-		// either not enabled or working correctly
240
-		return true;
241
-	}
242
-
243
-	/**
244
-	 * Checks if the correct memcache module for PHP is installed. Only
245
-	 * fails if memcached is configured and the working module is not installed.
246
-	 *
247
-	 * @return bool
248
-	 */
249
-	private function isCorrectMemcachedPHPModuleInstalled() {
250
-		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
251
-			return true;
252
-		}
253
-
254
-		// there are two different memcached modules for PHP
255
-		// we only support memcached and not memcache
256
-		// https://code.google.com/p/memcached/wiki/PHPClientComparison
257
-		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
258
-	}
259
-
260
-	/**
261
-	 * @return RedirectResponse
262
-	 */
263
-	public function rescanFailedIntegrityCheck() {
264
-		$this->checker->runInstanceVerification();
265
-		return new RedirectResponse(
266
-			$this->urlGenerator->linkToRoute('settings_admin')
267
-		);
268
-	}
269
-
270
-	/**
271
-	 * @NoCSRFRequired
272
-	 * @return DataResponse
273
-	 */
274
-	public function getFailedIntegrityCheckFiles() {
275
-		if(!$this->checker->isCodeCheckEnforced()) {
276
-			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
277
-		}
278
-
279
-		$completeResults = $this->checker->getResults();
280
-
281
-		if(!empty($completeResults)) {
282
-			$formattedTextResponse = 'Technical information
47
+    /** @var IConfig */
48
+    private $config;
49
+    /** @var IClientService */
50
+    private $clientService;
51
+    /** @var \OC_Util */
52
+    private $util;
53
+    /** @var IURLGenerator */
54
+    private $urlGenerator;
55
+    /** @var IL10N */
56
+    private $l10n;
57
+    /** @var Checker */
58
+    private $checker;
59
+
60
+    /**
61
+     * @param string $AppName
62
+     * @param IRequest $request
63
+     * @param IConfig $config
64
+     * @param IClientService $clientService
65
+     * @param IURLGenerator $urlGenerator
66
+     * @param \OC_Util $util
67
+     * @param IL10N $l10n
68
+     * @param Checker $checker
69
+     */
70
+    public function __construct($AppName,
71
+                                IRequest $request,
72
+                                IConfig $config,
73
+                                IClientService $clientService,
74
+                                IURLGenerator $urlGenerator,
75
+                                \OC_Util $util,
76
+                                IL10N $l10n,
77
+                                Checker $checker) {
78
+        parent::__construct($AppName, $request);
79
+        $this->config = $config;
80
+        $this->clientService = $clientService;
81
+        $this->util = $util;
82
+        $this->urlGenerator = $urlGenerator;
83
+        $this->l10n = $l10n;
84
+        $this->checker = $checker;
85
+    }
86
+
87
+    /**
88
+     * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP
89
+     * @return bool
90
+     */
91
+    private function isInternetConnectionWorking() {
92
+        if ($this->config->getSystemValue('has_internet_connection', true) === false) {
93
+            return false;
94
+        }
95
+
96
+        try {
97
+            $client = $this->clientService->newClient();
98
+            $client->get('https://www.owncloud.org/');
99
+            $client->get('http://www.owncloud.org/');
100
+            return true;
101
+        } catch (\Exception $e) {
102
+            return false;
103
+        }
104
+    }
105
+
106
+    /**
107
+     * Checks whether a local memcache is installed or not
108
+     * @return bool
109
+     */
110
+    private function isMemcacheConfigured() {
111
+        return $this->config->getSystemValue('memcache.local', null) !== null;
112
+    }
113
+
114
+    /**
115
+     * Whether /dev/urandom is available to the PHP controller
116
+     *
117
+     * @return bool
118
+     */
119
+    private function isUrandomAvailable() {
120
+        if(@file_exists('/dev/urandom')) {
121
+            $file = fopen('/dev/urandom', 'rb');
122
+            if($file) {
123
+                fclose($file);
124
+                return true;
125
+            }
126
+        }
127
+
128
+        return false;
129
+    }
130
+
131
+    /**
132
+     * Public for the sake of unit-testing
133
+     *
134
+     * @return array
135
+     */
136
+    protected function getCurlVersion() {
137
+        return curl_version();
138
+    }
139
+
140
+    /**
141
+     * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
142
+     * have multiple bugs which likely lead to problems in combination with
143
+     * functionality required by ownCloud such as SNI.
144
+     *
145
+     * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
146
+     * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
147
+     * @return string
148
+     */
149
+    private function isUsedTlsLibOutdated() {
150
+        // Appstore is disabled by default in EE
151
+        $appStoreDefault = false;
152
+        if (\OC_Util::getEditionString() === '') {
153
+            $appStoreDefault = true;
154
+        }
155
+
156
+        // Don't run check when:
157
+        // 1. Server has `has_internet_connection` set to false
158
+        // 2. AppStore AND S2S is disabled
159
+        if(!$this->config->getSystemValue('has_internet_connection', true)) {
160
+            return '';
161
+        }
162
+        if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)
163
+            && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
164
+            && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
165
+            return '';
166
+        }
167
+
168
+        $versionString = $this->getCurlVersion();
169
+        if(isset($versionString['ssl_version'])) {
170
+            $versionString = $versionString['ssl_version'];
171
+        } else {
172
+            return '';
173
+        }
174
+
175
+        $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
176
+        if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)) {
177
+            $features = (string)$this->l10n->t('Federated Cloud Sharing');
178
+        }
179
+
180
+        // Check if at least OpenSSL after 1.01d or 1.0.2b
181
+        if(strpos($versionString, 'OpenSSL/') === 0) {
182
+            $majorVersion = substr($versionString, 8, 5);
183
+            $patchRelease = substr($versionString, 13, 6);
184
+
185
+            if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
186
+                ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
187
+                return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
188
+            }
189
+        }
190
+
191
+        // Check if NSS and perform heuristic check
192
+        if(strpos($versionString, 'NSS/') === 0) {
193
+            try {
194
+                $firstClient = $this->clientService->newClient();
195
+                $firstClient->get('https://www.owncloud.org/');
196
+
197
+                $secondClient = $this->clientService->newClient();
198
+                $secondClient->get('https://owncloud.org/');
199
+            } catch (ClientException $e) {
200
+                if($e->getResponse()->getStatusCode() === 400) {
201
+                    return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
202
+                }
203
+            }
204
+        }
205
+
206
+        return '';
207
+    }
208
+
209
+    /**
210
+     * Whether the php version is still supported (at time of release)
211
+     * according to: https://secure.php.net/supported-versions.php
212
+     *
213
+     * @return array
214
+     */
215
+    private function isPhpSupported() {
216
+        $eol = false;
217
+
218
+        //PHP 5.4 is EOL on 14 Sep 2015
219
+        if (version_compare(PHP_VERSION, '5.5.0') === -1) {
220
+            $eol = true;
221
+        }
222
+
223
+        return ['eol' => $eol, 'version' => PHP_VERSION];
224
+    }
225
+
226
+    /**
227
+     * Check if the reverse proxy configuration is working as expected
228
+     *
229
+     * @return bool
230
+     */
231
+    private function forwardedForHeadersWorking() {
232
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
233
+        $remoteAddress = $this->request->getRemoteAddress();
234
+
235
+        if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
236
+            return false;
237
+        }
238
+
239
+        // either not enabled or working correctly
240
+        return true;
241
+    }
242
+
243
+    /**
244
+     * Checks if the correct memcache module for PHP is installed. Only
245
+     * fails if memcached is configured and the working module is not installed.
246
+     *
247
+     * @return bool
248
+     */
249
+    private function isCorrectMemcachedPHPModuleInstalled() {
250
+        if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
251
+            return true;
252
+        }
253
+
254
+        // there are two different memcached modules for PHP
255
+        // we only support memcached and not memcache
256
+        // https://code.google.com/p/memcached/wiki/PHPClientComparison
257
+        return !(!extension_loaded('memcached') && extension_loaded('memcache'));
258
+    }
259
+
260
+    /**
261
+     * @return RedirectResponse
262
+     */
263
+    public function rescanFailedIntegrityCheck() {
264
+        $this->checker->runInstanceVerification();
265
+        return new RedirectResponse(
266
+            $this->urlGenerator->linkToRoute('settings_admin')
267
+        );
268
+    }
269
+
270
+    /**
271
+     * @NoCSRFRequired
272
+     * @return DataResponse
273
+     */
274
+    public function getFailedIntegrityCheckFiles() {
275
+        if(!$this->checker->isCodeCheckEnforced()) {
276
+            return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
277
+        }
278
+
279
+        $completeResults = $this->checker->getResults();
280
+
281
+        if(!empty($completeResults)) {
282
+            $formattedTextResponse = 'Technical information
283 283
 =====================
284 284
 The following list covers which files have failed the integrity check. Please read
285 285
 the previous linked documentation to learn more about the errors and how to fix
@@ -288,64 +288,64 @@  discard block
 block discarded – undo
288 288
 Results
289 289
 =======
290 290
 ';
291
-			foreach($completeResults as $context => $contextResult) {
292
-				$formattedTextResponse .= "- $context\n";
293
-
294
-				foreach($contextResult as $category => $result) {
295
-					$formattedTextResponse .= "\t- $category\n";
296
-					if($category !== 'EXCEPTION') {
297
-						foreach ($result as $key => $results) {
298
-							$formattedTextResponse .= "\t\t- $key\n";
299
-						}
300
-					} else {
301
-						foreach ($result as $key => $results) {
302
-							$formattedTextResponse .= "\t\t- $results\n";
303
-						}
304
-					}
305
-
306
-				}
307
-			}
308
-
309
-			$formattedTextResponse .= '
291
+            foreach($completeResults as $context => $contextResult) {
292
+                $formattedTextResponse .= "- $context\n";
293
+
294
+                foreach($contextResult as $category => $result) {
295
+                    $formattedTextResponse .= "\t- $category\n";
296
+                    if($category !== 'EXCEPTION') {
297
+                        foreach ($result as $key => $results) {
298
+                            $formattedTextResponse .= "\t\t- $key\n";
299
+                        }
300
+                    } else {
301
+                        foreach ($result as $key => $results) {
302
+                            $formattedTextResponse .= "\t\t- $results\n";
303
+                        }
304
+                    }
305
+
306
+                }
307
+            }
308
+
309
+            $formattedTextResponse .= '
310 310
 Raw output
311 311
 ==========
312 312
 ';
313
-			$formattedTextResponse .= print_r($completeResults, true);
314
-		} else {
315
-			$formattedTextResponse = 'No errors have been found.';
316
-		}
317
-
318
-
319
-		$response = new DataDisplayResponse(
320
-			$formattedTextResponse,
321
-			Http::STATUS_OK,
322
-			[
323
-				'Content-Type' => 'text/plain',
324
-			]
325
-		);
326
-
327
-		return $response;
328
-	}
329
-
330
-	/**
331
-	 * @return DataResponse
332
-	 */
333
-	public function check() {
334
-		return new DataResponse(
335
-			[
336
-				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
337
-				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
338
-				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
339
-				'isUrandomAvailable' => $this->isUrandomAvailable(),
340
-				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
341
-				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
342
-				'phpSupported' => $this->isPhpSupported(),
343
-				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
344
-				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
345
-				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
346
-				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
347
-				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
348
-			]
349
-		);
350
-	}
313
+            $formattedTextResponse .= print_r($completeResults, true);
314
+        } else {
315
+            $formattedTextResponse = 'No errors have been found.';
316
+        }
317
+
318
+
319
+        $response = new DataDisplayResponse(
320
+            $formattedTextResponse,
321
+            Http::STATUS_OK,
322
+            [
323
+                'Content-Type' => 'text/plain',
324
+            ]
325
+        );
326
+
327
+        return $response;
328
+    }
329
+
330
+    /**
331
+     * @return DataResponse
332
+     */
333
+    public function check() {
334
+        return new DataResponse(
335
+            [
336
+                'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
337
+                'isMemcacheConfigured' => $this->isMemcacheConfigured(),
338
+                'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
339
+                'isUrandomAvailable' => $this->isUrandomAvailable(),
340
+                'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
341
+                'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
342
+                'phpSupported' => $this->isPhpSupported(),
343
+                'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
344
+                'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
345
+                'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
346
+                'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
347
+                'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
348
+            ]
349
+        );
350
+    }
351 351
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -117,9 +117,9 @@  discard block
 block discarded – undo
117 117
 	 * @return bool
118 118
 	 */
119 119
 	private function isUrandomAvailable() {
120
-		if(@file_exists('/dev/urandom')) {
120
+		if (@file_exists('/dev/urandom')) {
121 121
 			$file = fopen('/dev/urandom', 'rb');
122
-			if($file) {
122
+			if ($file) {
123 123
 				fclose($file);
124 124
 				return true;
125 125
 			}
@@ -156,40 +156,40 @@  discard block
 block discarded – undo
156 156
 		// Don't run check when:
157 157
 		// 1. Server has `has_internet_connection` set to false
158 158
 		// 2. AppStore AND S2S is disabled
159
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
159
+		if (!$this->config->getSystemValue('has_internet_connection', true)) {
160 160
 			return '';
161 161
 		}
162
-		if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)
162
+		if (!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)
163 163
 			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
164 164
 			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
165 165
 			return '';
166 166
 		}
167 167
 
168 168
 		$versionString = $this->getCurlVersion();
169
-		if(isset($versionString['ssl_version'])) {
169
+		if (isset($versionString['ssl_version'])) {
170 170
 			$versionString = $versionString['ssl_version'];
171 171
 		} else {
172 172
 			return '';
173 173
 		}
174 174
 
175
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
176
-		if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)) {
177
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
175
+		$features = (string) $this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
176
+		if (!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)) {
177
+			$features = (string) $this->l10n->t('Federated Cloud Sharing');
178 178
 		}
179 179
 
180 180
 		// Check if at least OpenSSL after 1.01d or 1.0.2b
181
-		if(strpos($versionString, 'OpenSSL/') === 0) {
181
+		if (strpos($versionString, 'OpenSSL/') === 0) {
182 182
 			$majorVersion = substr($versionString, 8, 5);
183 183
 			$patchRelease = substr($versionString, 13, 6);
184 184
 
185
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
185
+			if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
186 186
 				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
187 187
 				return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
188 188
 			}
189 189
 		}
190 190
 
191 191
 		// Check if NSS and perform heuristic check
192
-		if(strpos($versionString, 'NSS/') === 0) {
192
+		if (strpos($versionString, 'NSS/') === 0) {
193 193
 			try {
194 194
 				$firstClient = $this->clientService->newClient();
195 195
 				$firstClient->get('https://www.owncloud.org/');
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
 				$secondClient = $this->clientService->newClient();
198 198
 				$secondClient->get('https://owncloud.org/');
199 199
 			} catch (ClientException $e) {
200
-				if($e->getResponse()->getStatusCode() === 400) {
200
+				if ($e->getResponse()->getStatusCode() === 400) {
201 201
 					return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
202 202
 				}
203 203
 			}
@@ -277,13 +277,13 @@  discard block
 block discarded – undo
277 277
 	 * @return DataResponse
278 278
 	 */
279 279
 	public function getFailedIntegrityCheckFiles() {
280
-		if(!$this->checker->isCodeCheckEnforced()) {
280
+		if (!$this->checker->isCodeCheckEnforced()) {
281 281
 			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
282 282
 		}
283 283
 
284 284
 		$completeResults = $this->checker->getResults();
285 285
 
286
-		if(!empty($completeResults)) {
286
+		if (!empty($completeResults)) {
287 287
 			$formattedTextResponse = 'Technical information
288 288
 =====================
289 289
 The following list covers which files have failed the integrity check. Please read
@@ -293,12 +293,12 @@  discard block
 block discarded – undo
293 293
 Results
294 294
 =======
295 295
 ';
296
-			foreach($completeResults as $context => $contextResult) {
296
+			foreach ($completeResults as $context => $contextResult) {
297 297
 				$formattedTextResponse .= "- $context\n";
298 298
 
299
-				foreach($contextResult as $category => $result) {
299
+				foreach ($contextResult as $category => $result) {
300 300
 					$formattedTextResponse .= "\t- $category\n";
301
-					if($category !== 'EXCEPTION') {
301
+					if ($category !== 'EXCEPTION') {
302 302
 						foreach ($result as $key => $results) {
303 303
 							$formattedTextResponse .= "\t\t- $key\n";
304 304
 						}
Please login to merge, or discard this patch.
apps/files_sharing/lib/helper.php 4 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -303,6 +303,7 @@
 block discarded – undo
303 303
 	 * get default share folder
304 304
 	 *
305 305
 	 * @param \OC\Files\View
306
+	 * @param View $view
306 307
 	 * @return string
307 308
 	 */
308 309
 	public static function getShareFolder($view = null) {
Please login to merge, or discard this patch.
Indentation   +300 added lines, -300 removed lines patch added patch discarded remove patch
@@ -36,305 +36,305 @@
 block discarded – undo
36 36
 
37 37
 class Helper {
38 38
 
39
-	public static function registerHooks() {
40
-		\OCP\Util::connectHook('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook');
41
-		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook');
42
-		\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
43
-		\OCP\Util::connectHook('OC_Appconfig', 'post_set_value', '\OCA\Files\Share\Maintainer', 'configChangeHook');
44
-
45
-		\OCP\Util::connectHook('OCP\Share', 'post_shared', '\OC\Files\Cache\Shared_Updater', 'postShareHook');
46
-		\OCP\Util::connectHook('OCP\Share', 'post_unshare', '\OC\Files\Cache\Shared_Updater', 'postUnshareHook');
47
-		\OCP\Util::connectHook('OCP\Share', 'post_unshareFromSelf', '\OC\Files\Cache\Shared_Updater', 'postUnshareFromSelfHook');
48
-
49
-		\OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
50
-	}
51
-
52
-	/**
53
-	 * Sets up the filesystem and user for public sharing
54
-	 * @param string $token string share token
55
-	 * @param string $relativePath optional path relative to the share
56
-	 * @param string $password optional password
57
-	 * @return array
58
-	 */
59
-	public static function setupFromToken($token, $relativePath = null, $password = null) {
60
-		\OC_User::setIncognitoMode(true);
61
-
62
-		$linkItem = \OCP\Share::getShareByToken($token, !$password);
63
-		if($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) {
64
-			\OC_Response::setStatus(404);
65
-			\OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
66
-			exit;
67
-		}
68
-
69
-		if(!isset($linkItem['uid_owner']) || !isset($linkItem['file_source'])) {
70
-			\OC_Response::setStatus(500);
71
-			\OCP\Util::writeLog('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")', \OCP\Util::WARN);
72
-			exit;
73
-		}
74
-
75
-		$rootLinkItem = \OCP\Share::resolveReShare($linkItem);
76
-		$path = null;
77
-		if (isset($rootLinkItem['uid_owner'])) {
78
-			\OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
79
-			\OC_Util::tearDownFS();
80
-			\OC_Util::setupFS($rootLinkItem['uid_owner']);
81
-		}
82
-
83
-		try {
84
-			$path = Filesystem::getPath($linkItem['file_source']);
85
-		} catch (NotFoundException $e) {
86
-			\OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
87
-			\OC_Response::setStatus(404);
88
-			\OCP\JSON::error(array('success' => false));
89
-			exit();
90
-		}
91
-
92
-		if (!isset($linkItem['item_type'])) {
93
-			\OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR);
94
-			\OC_Response::setStatus(404);
95
-			\OCP\JSON::error(array('success' => false));
96
-			exit();
97
-		}
98
-
99
-		if (isset($linkItem['share_with']) && (int)$linkItem['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
100
-			if (!self::authenticate($linkItem, $password)) {
101
-				\OC_Response::setStatus(403);
102
-				\OCP\JSON::error(array('success' => false));
103
-				exit();
104
-			}
105
-		}
106
-
107
-		$basePath = $path;
108
-
109
-		if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
110
-			$path .= Filesystem::normalizePath($relativePath);
111
-		}
112
-
113
-		return array(
114
-			'linkItem' => $linkItem,
115
-			'basePath' => $basePath,
116
-			'realPath' => $path
117
-		);
118
-	}
119
-
120
-	/**
121
-	 * Authenticate link item with the given password
122
-	 * or with the session if no password was given.
123
-	 * @param array $linkItem link item array
124
-	 * @param string $password optional password
125
-	 *
126
-	 * @return boolean true if authorized, false otherwise
127
-	 */
128
-	public static function authenticate($linkItem, $password = null) {
129
-		if ($password !== null) {
130
-			if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) {
131
-				// Check Password
132
-				$newHash = '';
133
-				if(\OC::$server->getHasher()->verify($password, $linkItem['share_with'], $newHash)) {
134
-					// Save item id in session for future requests
135
-					\OC::$server->getSession()->set('public_link_authenticated', (string) $linkItem['id']);
136
-
137
-					/**
138
-					 * FIXME: Migrate old hashes to new hash format
139
-					 * Due to the fact that there is no reasonable functionality to update the password
140
-					 * of an existing share no migration is yet performed there.
141
-					 * The only possibility is to update the existing share which will result in a new
142
-					 * share ID and is a major hack.
143
-					 *
144
-					 * In the future the migration should be performed once there is a proper method
145
-					 * to update the share's password. (for example `$share->updatePassword($password)`
146
-					 *
147
-					 * @link https://github.com/owncloud/core/issues/10671
148
-					 */
149
-					if(!empty($newHash)) {
150
-
151
-					}
152
-				} else {
153
-					return false;
154
-				}
155
-			} else {
156
-				\OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type']
157
-					.' for share id '.$linkItem['id'], \OCP\Util::ERROR);
158
-				return false;
159
-			}
160
-
161
-		}
162
-		else {
163
-			// not authenticated ?
164
-			if ( ! \OC::$server->getSession()->exists('public_link_authenticated')
165
-				|| \OC::$server->getSession()->get('public_link_authenticated') !== (string)$linkItem['id']) {
166
-				return false;
167
-			}
168
-		}
169
-		return true;
170
-	}
171
-
172
-	public static function getSharesFromItem($target) {
173
-		$result = array();
174
-		$owner = Filesystem::getOwner($target);
175
-		Filesystem::initMountPoints($owner);
176
-		$info = Filesystem::getFileInfo($target);
177
-		$ownerView = new View('/'.$owner.'/files');
178
-		if ( $owner != User::getUser() ) {
179
-			$path = $ownerView->getPath($info['fileid']);
180
-		} else {
181
-			$path = $target;
182
-		}
183
-
184
-
185
-		$ids = array();
186
-		while ($path !== dirname($path)) {
187
-			$info = $ownerView->getFileInfo($path);
188
-			if ($info instanceof \OC\Files\FileInfo) {
189
-				$ids[] = $info['fileid'];
190
-			} else {
191
-				\OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
192
-			}
193
-			$path = dirname($path);
194
-		}
195
-
196
-		if (!empty($ids)) {
197
-
198
-			$idList = array_chunk($ids, 99, true);
199
-
200
-			foreach ($idList as $subList) {
201
-				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
202
-				$query = \OCP\DB::prepare($statement);
203
-				$r = $query->execute();
204
-				$result = array_merge($result, $r->fetchAll());
205
-			}
206
-		}
207
-
208
-		return $result;
209
-	}
210
-
211
-	/**
212
-	 * get the UID of the owner of the file and the path to the file relative to
213
-	 * owners files folder
214
-	 *
215
-	 * @param $filename
216
-	 * @return array
217
-	 * @throws \OC\User\NoUserException
218
-	 */
219
-	public static function getUidAndFilename($filename) {
220
-		$uid = Filesystem::getOwner($filename);
221
-		$userManager = \OC::$server->getUserManager();
222
-		// if the user with the UID doesn't exists, e.g. because the UID points
223
-		// to a remote user with a federated cloud ID we use the current logged-in
224
-		// user. We need a valid local user to create the share
225
-		if (!$userManager->userExists($uid)) {
226
-			$uid = User::getUser();
227
-		}
228
-		Filesystem::initMountPoints($uid);
229
-		if ( $uid != User::getUser() ) {
230
-			$info = Filesystem::getFileInfo($filename);
231
-			$ownerView = new View('/'.$uid.'/files');
232
-			try {
233
-				$filename = $ownerView->getPath($info['fileid']);
234
-			} catch (NotFoundException $e) {
235
-				$filename = null;
236
-			}
237
-		}
238
-		return [$uid, $filename];
239
-	}
240
-
241
-	/**
242
-	 * Format a path to be relative to the /user/files/ directory
243
-	 * @param string $path the absolute path
244
-	 * @return string e.g. turns '/admin/files/test.txt' into 'test.txt'
245
-	 */
246
-	public static function stripUserFilesPath($path) {
247
-		$trimmed = ltrim($path, '/');
248
-		$split = explode('/', $trimmed);
249
-
250
-		// it is not a file relative to data/user/files
251
-		if (count($split) < 3 || $split[1] !== 'files') {
252
-			return false;
253
-		}
254
-
255
-		$sliced = array_slice($split, 2);
256
-		$relPath = implode('/', $sliced);
257
-
258
-		return $relPath;
259
-	}
260
-
261
-	/**
262
-	 * check if file name already exists and generate unique target
263
-	 *
264
-	 * @param string $path
265
-	 * @param array $excludeList
266
-	 * @param View $view
267
-	 * @return string $path
268
-	 */
269
-	public static function generateUniqueTarget($path, $excludeList, $view) {
270
-		$pathinfo = pathinfo($path);
271
-		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
272
-		$name = $pathinfo['filename'];
273
-		$dir = $pathinfo['dirname'];
274
-		$i = 2;
275
-		while ($view->file_exists($path) || in_array($path, $excludeList)) {
276
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
277
-			$i++;
278
-		}
279
-
280
-		return $path;
281
-	}
282
-
283
-	/**
284
-	 * allow users from other ownCloud instances to mount public links share by this instance
285
-	 * @return bool
286
-	 */
287
-	public static function isOutgoingServer2serverShareEnabled() {
288
-		$appConfig = \OC::$server->getAppConfig();
289
-		$result = $appConfig->getValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
290
-		return ($result === 'yes') ? true : false;
291
-	}
292
-
293
-	/**
294
-	 * allow user to mount public links from onther ownClouds
295
-	 * @return bool
296
-	 */
297
-	public static function isIncomingServer2serverShareEnabled() {
298
-		$appConfig = \OC::$server->getAppConfig();
299
-		$result = $appConfig->getValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
300
-		return ($result === 'yes') ? true : false;
301
-	}
302
-
303
-	/**
304
-	 * get default share folder
305
-	 *
306
-	 * @param \OC\Files\View
307
-	 * @return string
308
-	 */
309
-	public static function getShareFolder($view = null) {
310
-		if ($view === null) {
311
-			$view = Filesystem::getView();
312
-		}
313
-		$shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
314
-		$shareFolder = Filesystem::normalizePath($shareFolder);
315
-
316
-		if (!$view->file_exists($shareFolder)) {
317
-			$dir = '';
318
-			$subdirs = explode('/', $shareFolder);
319
-			foreach ($subdirs as $subdir) {
320
-				$dir = $dir . '/' . $subdir;
321
-				if (!$view->is_dir($dir)) {
322
-					$view->mkdir($dir);
323
-				}
324
-			}
325
-		}
326
-
327
-		return $shareFolder;
328
-
329
-	}
330
-
331
-	/**
332
-	 * set default share folder
333
-	 *
334
-	 * @param string $shareFolder
335
-	 */
336
-	public static function setShareFolder($shareFolder) {
337
-		\OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
338
-	}
39
+    public static function registerHooks() {
40
+        \OCP\Util::connectHook('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook');
41
+        \OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook');
42
+        \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
43
+        \OCP\Util::connectHook('OC_Appconfig', 'post_set_value', '\OCA\Files\Share\Maintainer', 'configChangeHook');
44
+
45
+        \OCP\Util::connectHook('OCP\Share', 'post_shared', '\OC\Files\Cache\Shared_Updater', 'postShareHook');
46
+        \OCP\Util::connectHook('OCP\Share', 'post_unshare', '\OC\Files\Cache\Shared_Updater', 'postUnshareHook');
47
+        \OCP\Util::connectHook('OCP\Share', 'post_unshareFromSelf', '\OC\Files\Cache\Shared_Updater', 'postUnshareFromSelfHook');
48
+
49
+        \OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
50
+    }
51
+
52
+    /**
53
+     * Sets up the filesystem and user for public sharing
54
+     * @param string $token string share token
55
+     * @param string $relativePath optional path relative to the share
56
+     * @param string $password optional password
57
+     * @return array
58
+     */
59
+    public static function setupFromToken($token, $relativePath = null, $password = null) {
60
+        \OC_User::setIncognitoMode(true);
61
+
62
+        $linkItem = \OCP\Share::getShareByToken($token, !$password);
63
+        if($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) {
64
+            \OC_Response::setStatus(404);
65
+            \OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
66
+            exit;
67
+        }
68
+
69
+        if(!isset($linkItem['uid_owner']) || !isset($linkItem['file_source'])) {
70
+            \OC_Response::setStatus(500);
71
+            \OCP\Util::writeLog('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")', \OCP\Util::WARN);
72
+            exit;
73
+        }
74
+
75
+        $rootLinkItem = \OCP\Share::resolveReShare($linkItem);
76
+        $path = null;
77
+        if (isset($rootLinkItem['uid_owner'])) {
78
+            \OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
79
+            \OC_Util::tearDownFS();
80
+            \OC_Util::setupFS($rootLinkItem['uid_owner']);
81
+        }
82
+
83
+        try {
84
+            $path = Filesystem::getPath($linkItem['file_source']);
85
+        } catch (NotFoundException $e) {
86
+            \OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
87
+            \OC_Response::setStatus(404);
88
+            \OCP\JSON::error(array('success' => false));
89
+            exit();
90
+        }
91
+
92
+        if (!isset($linkItem['item_type'])) {
93
+            \OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR);
94
+            \OC_Response::setStatus(404);
95
+            \OCP\JSON::error(array('success' => false));
96
+            exit();
97
+        }
98
+
99
+        if (isset($linkItem['share_with']) && (int)$linkItem['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
100
+            if (!self::authenticate($linkItem, $password)) {
101
+                \OC_Response::setStatus(403);
102
+                \OCP\JSON::error(array('success' => false));
103
+                exit();
104
+            }
105
+        }
106
+
107
+        $basePath = $path;
108
+
109
+        if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
110
+            $path .= Filesystem::normalizePath($relativePath);
111
+        }
112
+
113
+        return array(
114
+            'linkItem' => $linkItem,
115
+            'basePath' => $basePath,
116
+            'realPath' => $path
117
+        );
118
+    }
119
+
120
+    /**
121
+     * Authenticate link item with the given password
122
+     * or with the session if no password was given.
123
+     * @param array $linkItem link item array
124
+     * @param string $password optional password
125
+     *
126
+     * @return boolean true if authorized, false otherwise
127
+     */
128
+    public static function authenticate($linkItem, $password = null) {
129
+        if ($password !== null) {
130
+            if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) {
131
+                // Check Password
132
+                $newHash = '';
133
+                if(\OC::$server->getHasher()->verify($password, $linkItem['share_with'], $newHash)) {
134
+                    // Save item id in session for future requests
135
+                    \OC::$server->getSession()->set('public_link_authenticated', (string) $linkItem['id']);
136
+
137
+                    /**
138
+                     * FIXME: Migrate old hashes to new hash format
139
+                     * Due to the fact that there is no reasonable functionality to update the password
140
+                     * of an existing share no migration is yet performed there.
141
+                     * The only possibility is to update the existing share which will result in a new
142
+                     * share ID and is a major hack.
143
+                     *
144
+                     * In the future the migration should be performed once there is a proper method
145
+                     * to update the share's password. (for example `$share->updatePassword($password)`
146
+                     *
147
+                     * @link https://github.com/owncloud/core/issues/10671
148
+                     */
149
+                    if(!empty($newHash)) {
150
+
151
+                    }
152
+                } else {
153
+                    return false;
154
+                }
155
+            } else {
156
+                \OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type']
157
+                    .' for share id '.$linkItem['id'], \OCP\Util::ERROR);
158
+                return false;
159
+            }
160
+
161
+        }
162
+        else {
163
+            // not authenticated ?
164
+            if ( ! \OC::$server->getSession()->exists('public_link_authenticated')
165
+                || \OC::$server->getSession()->get('public_link_authenticated') !== (string)$linkItem['id']) {
166
+                return false;
167
+            }
168
+        }
169
+        return true;
170
+    }
171
+
172
+    public static function getSharesFromItem($target) {
173
+        $result = array();
174
+        $owner = Filesystem::getOwner($target);
175
+        Filesystem::initMountPoints($owner);
176
+        $info = Filesystem::getFileInfo($target);
177
+        $ownerView = new View('/'.$owner.'/files');
178
+        if ( $owner != User::getUser() ) {
179
+            $path = $ownerView->getPath($info['fileid']);
180
+        } else {
181
+            $path = $target;
182
+        }
183
+
184
+
185
+        $ids = array();
186
+        while ($path !== dirname($path)) {
187
+            $info = $ownerView->getFileInfo($path);
188
+            if ($info instanceof \OC\Files\FileInfo) {
189
+                $ids[] = $info['fileid'];
190
+            } else {
191
+                \OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
192
+            }
193
+            $path = dirname($path);
194
+        }
195
+
196
+        if (!empty($ids)) {
197
+
198
+            $idList = array_chunk($ids, 99, true);
199
+
200
+            foreach ($idList as $subList) {
201
+                $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
202
+                $query = \OCP\DB::prepare($statement);
203
+                $r = $query->execute();
204
+                $result = array_merge($result, $r->fetchAll());
205
+            }
206
+        }
207
+
208
+        return $result;
209
+    }
210
+
211
+    /**
212
+     * get the UID of the owner of the file and the path to the file relative to
213
+     * owners files folder
214
+     *
215
+     * @param $filename
216
+     * @return array
217
+     * @throws \OC\User\NoUserException
218
+     */
219
+    public static function getUidAndFilename($filename) {
220
+        $uid = Filesystem::getOwner($filename);
221
+        $userManager = \OC::$server->getUserManager();
222
+        // if the user with the UID doesn't exists, e.g. because the UID points
223
+        // to a remote user with a federated cloud ID we use the current logged-in
224
+        // user. We need a valid local user to create the share
225
+        if (!$userManager->userExists($uid)) {
226
+            $uid = User::getUser();
227
+        }
228
+        Filesystem::initMountPoints($uid);
229
+        if ( $uid != User::getUser() ) {
230
+            $info = Filesystem::getFileInfo($filename);
231
+            $ownerView = new View('/'.$uid.'/files');
232
+            try {
233
+                $filename = $ownerView->getPath($info['fileid']);
234
+            } catch (NotFoundException $e) {
235
+                $filename = null;
236
+            }
237
+        }
238
+        return [$uid, $filename];
239
+    }
240
+
241
+    /**
242
+     * Format a path to be relative to the /user/files/ directory
243
+     * @param string $path the absolute path
244
+     * @return string e.g. turns '/admin/files/test.txt' into 'test.txt'
245
+     */
246
+    public static function stripUserFilesPath($path) {
247
+        $trimmed = ltrim($path, '/');
248
+        $split = explode('/', $trimmed);
249
+
250
+        // it is not a file relative to data/user/files
251
+        if (count($split) < 3 || $split[1] !== 'files') {
252
+            return false;
253
+        }
254
+
255
+        $sliced = array_slice($split, 2);
256
+        $relPath = implode('/', $sliced);
257
+
258
+        return $relPath;
259
+    }
260
+
261
+    /**
262
+     * check if file name already exists and generate unique target
263
+     *
264
+     * @param string $path
265
+     * @param array $excludeList
266
+     * @param View $view
267
+     * @return string $path
268
+     */
269
+    public static function generateUniqueTarget($path, $excludeList, $view) {
270
+        $pathinfo = pathinfo($path);
271
+        $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
272
+        $name = $pathinfo['filename'];
273
+        $dir = $pathinfo['dirname'];
274
+        $i = 2;
275
+        while ($view->file_exists($path) || in_array($path, $excludeList)) {
276
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
277
+            $i++;
278
+        }
279
+
280
+        return $path;
281
+    }
282
+
283
+    /**
284
+     * allow users from other ownCloud instances to mount public links share by this instance
285
+     * @return bool
286
+     */
287
+    public static function isOutgoingServer2serverShareEnabled() {
288
+        $appConfig = \OC::$server->getAppConfig();
289
+        $result = $appConfig->getValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
290
+        return ($result === 'yes') ? true : false;
291
+    }
292
+
293
+    /**
294
+     * allow user to mount public links from onther ownClouds
295
+     * @return bool
296
+     */
297
+    public static function isIncomingServer2serverShareEnabled() {
298
+        $appConfig = \OC::$server->getAppConfig();
299
+        $result = $appConfig->getValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
300
+        return ($result === 'yes') ? true : false;
301
+    }
302
+
303
+    /**
304
+     * get default share folder
305
+     *
306
+     * @param \OC\Files\View
307
+     * @return string
308
+     */
309
+    public static function getShareFolder($view = null) {
310
+        if ($view === null) {
311
+            $view = Filesystem::getView();
312
+        }
313
+        $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
314
+        $shareFolder = Filesystem::normalizePath($shareFolder);
315
+
316
+        if (!$view->file_exists($shareFolder)) {
317
+            $dir = '';
318
+            $subdirs = explode('/', $shareFolder);
319
+            foreach ($subdirs as $subdir) {
320
+                $dir = $dir . '/' . $subdir;
321
+                if (!$view->is_dir($dir)) {
322
+                    $view->mkdir($dir);
323
+                }
324
+            }
325
+        }
326
+
327
+        return $shareFolder;
328
+
329
+    }
330
+
331
+    /**
332
+     * set default share folder
333
+     *
334
+     * @param string $shareFolder
335
+     */
336
+    public static function setShareFolder($shareFolder) {
337
+        \OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
338
+    }
339 339
 
340 340
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -60,15 +60,15 @@  discard block
 block discarded – undo
60 60
 		\OC_User::setIncognitoMode(true);
61 61
 
62 62
 		$linkItem = \OCP\Share::getShareByToken($token, !$password);
63
-		if($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) {
63
+		if ($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) {
64 64
 			\OC_Response::setStatus(404);
65 65
 			\OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
66 66
 			exit;
67 67
 		}
68 68
 
69
-		if(!isset($linkItem['uid_owner']) || !isset($linkItem['file_source'])) {
69
+		if (!isset($linkItem['uid_owner']) || !isset($linkItem['file_source'])) {
70 70
 			\OC_Response::setStatus(500);
71
-			\OCP\Util::writeLog('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")', \OCP\Util::WARN);
71
+			\OCP\Util::writeLog('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("'.$token.'")', \OCP\Util::WARN);
72 72
 			exit;
73 73
 		}
74 74
 
@@ -90,13 +90,13 @@  discard block
 block discarded – undo
90 90
 		}
91 91
 
92 92
 		if (!isset($linkItem['item_type'])) {
93
-			\OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR);
93
+			\OCP\Util::writeLog('share', 'No item type set for share id: '.$linkItem['id'], \OCP\Util::ERROR);
94 94
 			\OC_Response::setStatus(404);
95 95
 			\OCP\JSON::error(array('success' => false));
96 96
 			exit();
97 97
 		}
98 98
 
99
-		if (isset($linkItem['share_with']) && (int)$linkItem['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
99
+		if (isset($linkItem['share_with']) && (int) $linkItem['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
100 100
 			if (!self::authenticate($linkItem, $password)) {
101 101
 				\OC_Response::setStatus(403);
102 102
 				\OCP\JSON::error(array('success' => false));
@@ -106,7 +106,7 @@  discard block
 block discarded – undo
106 106
 
107 107
 		$basePath = $path;
108 108
 
109
-		if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
109
+		if ($relativePath !== null && Filesystem::isReadable($basePath.$relativePath)) {
110 110
 			$path .= Filesystem::normalizePath($relativePath);
111 111
 		}
112 112
 
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
 			if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) {
131 131
 				// Check Password
132 132
 				$newHash = '';
133
-				if(\OC::$server->getHasher()->verify($password, $linkItem['share_with'], $newHash)) {
133
+				if (\OC::$server->getHasher()->verify($password, $linkItem['share_with'], $newHash)) {
134 134
 					// Save item id in session for future requests
135 135
 					\OC::$server->getSession()->set('public_link_authenticated', (string) $linkItem['id']);
136 136
 
@@ -146,7 +146,7 @@  discard block
 block discarded – undo
146 146
 					 *
147 147
 					 * @link https://github.com/owncloud/core/issues/10671
148 148
 					 */
149
-					if(!empty($newHash)) {
149
+					if (!empty($newHash)) {
150 150
 
151 151
 					}
152 152
 				} else {
@@ -161,8 +161,8 @@  discard block
 block discarded – undo
161 161
 		}
162 162
 		else {
163 163
 			// not authenticated ?
164
-			if ( ! \OC::$server->getSession()->exists('public_link_authenticated')
165
-				|| \OC::$server->getSession()->get('public_link_authenticated') !== (string)$linkItem['id']) {
164
+			if (!\OC::$server->getSession()->exists('public_link_authenticated')
165
+				|| \OC::$server->getSession()->get('public_link_authenticated') !== (string) $linkItem['id']) {
166 166
 				return false;
167 167
 			}
168 168
 		}
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
 		Filesystem::initMountPoints($owner);
176 176
 		$info = Filesystem::getFileInfo($target);
177 177
 		$ownerView = new View('/'.$owner.'/files');
178
-		if ( $owner != User::getUser() ) {
178
+		if ($owner != User::getUser()) {
179 179
 			$path = $ownerView->getPath($info['fileid']);
180 180
 		} else {
181 181
 			$path = $target;
@@ -188,7 +188,7 @@  discard block
 block discarded – undo
188 188
 			if ($info instanceof \OC\Files\FileInfo) {
189 189
 				$ids[] = $info['fileid'];
190 190
 			} else {
191
-				\OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
191
+				\OCP\Util::writeLog('sharing', 'No fileinfo available for: '.$path, \OCP\Util::WARN);
192 192
 			}
193 193
 			$path = dirname($path);
194 194
 		}
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
 			$idList = array_chunk($ids, 99, true);
199 199
 
200 200
 			foreach ($idList as $subList) {
201
-				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
201
+				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (".implode(',', $subList).") AND `share_type` IN (0, 1, 2)";
202 202
 				$query = \OCP\DB::prepare($statement);
203 203
 				$r = $query->execute();
204 204
 				$result = array_merge($result, $r->fetchAll());
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 			$uid = User::getUser();
227 227
 		}
228 228
 		Filesystem::initMountPoints($uid);
229
-		if ( $uid != User::getUser() ) {
229
+		if ($uid != User::getUser()) {
230 230
 			$info = Filesystem::getFileInfo($filename);
231 231
 			$ownerView = new View('/'.$uid.'/files');
232 232
 			try {
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
 		$dir = $pathinfo['dirname'];
274 274
 		$i = 2;
275 275
 		while ($view->file_exists($path) || in_array($path, $excludeList)) {
276
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
276
+			$path = Filesystem::normalizePath($dir.'/'.$name.' ('.$i.')'.$ext);
277 277
 			$i++;
278 278
 		}
279 279
 
@@ -317,7 +317,7 @@  discard block
 block discarded – undo
317 317
 			$dir = '';
318 318
 			$subdirs = explode('/', $shareFolder);
319 319
 			foreach ($subdirs as $subdir) {
320
-				$dir = $dir . '/' . $subdir;
320
+				$dir = $dir.'/'.$subdir;
321 321
 				if (!$view->is_dir($dir)) {
322 322
 					$view->mkdir($dir);
323 323
 				}
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -158,8 +158,7 @@
 block discarded – undo
158 158
 				return false;
159 159
 			}
160 160
 
161
-		}
162
-		else {
161
+		} else {
163 162
 			// not authenticated ?
164 163
 			if ( ! \OC::$server->getSession()->exists('public_link_authenticated')
165 164
 				|| \OC::$server->getSession()->get('public_link_authenticated') !== (string)$linkItem['id']) {
Please login to merge, or discard this patch.
apps/files_sharing/lib/sharedmount.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -99,7 +99,7 @@
 block discarded – undo
99 99
 	 * @param string $path
100 100
 	 * @param View $view
101 101
 	 * @param SharedMount[] $mountpoints
102
-	 * @return mixed
102
+	 * @return string
103 103
 	 */
104 104
 	private function generateUniqueTarget($path, $view, array $mountpoints) {
105 105
 		$pathinfo = pathinfo($path);
Please login to merge, or discard this patch.
Indentation   +206 added lines, -206 removed lines patch added patch discarded remove patch
@@ -36,212 +36,212 @@
 block discarded – undo
36 36
  * Shared mount points can be moved by the user
37 37
  */
38 38
 class SharedMount extends MountPoint implements MoveableMount {
39
-	/**
40
-	 * @var \OC\Files\Storage\Shared $storage
41
-	 */
42
-	protected $storage = null;
43
-
44
-	/**
45
-	 * @var \OC\Files\View
46
-	 */
47
-	private $recipientView;
48
-
49
-	/**
50
-	 * @var string
51
-	 */
52
-	private $user;
53
-
54
-	/**
55
-	 * @param string $storage
56
-	 * @param SharedMount[] $mountpoints
57
-	 * @param array|null $arguments
58
-	 * @param \OCP\Files\Storage\IStorageFactory $loader
59
-	 */
60
-	public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
61
-		$this->user = $arguments['user'];
62
-		$this->recipientView = new View('/' . $this->user . '/files');
63
-		$newMountPoint = $this->verifyMountPoint($arguments['share'], $mountpoints);
64
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
65
-		$arguments['ownerView'] = new View('/' . $arguments['share']['uid_owner'] . '/files');
66
-		parent::__construct($storage, $absMountPoint, $arguments, $loader);
67
-	}
68
-
69
-	/**
70
-	 * check if the parent folder exists otherwise move the mount point up
71
-	 *
72
-	 * @param array $share
73
-	 * @param SharedMount[] $mountpoints
74
-	 * @return string
75
-	 */
76
-	private function verifyMountPoint(&$share, array $mountpoints) {
77
-
78
-		$mountPoint = basename($share['file_target']);
79
-		$parent = dirname($share['file_target']);
80
-
81
-		if (!$this->recipientView->is_dir($parent)) {
82
-			$parent = Helper::getShareFolder($this->recipientView);
83
-		}
84
-
85
-		$newMountPoint = $this->generateUniqueTarget(
86
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
87
-			$this->recipientView,
88
-			$mountpoints
89
-		);
90
-
91
-		if ($newMountPoint !== $share['file_target']) {
92
-			$this->updateFileTarget($newMountPoint, $share);
93
-			$share['file_target'] = $newMountPoint;
94
-			$share['unique_name'] = true;
95
-		}
96
-
97
-		return $newMountPoint;
98
-	}
99
-
100
-	/**
101
-	 * @param string $path
102
-	 * @param View $view
103
-	 * @param SharedMount[] $mountpoints
104
-	 * @return mixed
105
-	 */
106
-	private function generateUniqueTarget($path, $view, array $mountpoints) {
107
-		$pathinfo = pathinfo($path);
108
-		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
109
-		$name = $pathinfo['filename'];
110
-		$dir = $pathinfo['dirname'];
111
-
112
-		// Helper function to find existing mount points
113
-		$mountpointExists = function($path) use ($mountpoints) {
114
-			foreach ($mountpoints as $mountpoint) {
115
-				if ($mountpoint->getShare()['file_target'] === $path) {
116
-					return true;
117
-				}
118
-			}
119
-			return false;
120
-		};
121
-
122
-		$i = 2;
123
-		while ($view->file_exists($path) || $mountpointExists($path)) {
124
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
125
-			$i++;
126
-		}
127
-
128
-		return $path;
129
-	}
130
-
131
-	/**
132
-	 * update fileTarget in the database if the mount point changed
133
-	 *
134
-	 * @param string $newPath
135
-	 * @param array $share reference to the share which should be modified
136
-	 * @return bool
137
-	 */
138
-	private function updateFileTarget($newPath, &$share) {
139
-		// if the user renames a mount point from a group share we need to create a new db entry
140
-		// for the unique name
141
-		if ($share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP && empty($share['unique_name'])) {
142
-			$query = \OCP\DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,'
143
-			.' `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
144
-			.' `file_target`, `token`, `parent`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
145
-			$arguments = array($share['item_type'], $share['item_source'], $share['item_target'],
146
-				2, $this->user, $share['uid_owner'], $share['permissions'], $share['stime'], $share['file_source'],
147
-				$newPath, $share['token'], $share['id']);
148
-		} else {
149
-			// rename mount point
150
-			$query = \OCP\DB::prepare(
151
-					'Update `*PREFIX*share`
39
+    /**
40
+     * @var \OC\Files\Storage\Shared $storage
41
+     */
42
+    protected $storage = null;
43
+
44
+    /**
45
+     * @var \OC\Files\View
46
+     */
47
+    private $recipientView;
48
+
49
+    /**
50
+     * @var string
51
+     */
52
+    private $user;
53
+
54
+    /**
55
+     * @param string $storage
56
+     * @param SharedMount[] $mountpoints
57
+     * @param array|null $arguments
58
+     * @param \OCP\Files\Storage\IStorageFactory $loader
59
+     */
60
+    public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
61
+        $this->user = $arguments['user'];
62
+        $this->recipientView = new View('/' . $this->user . '/files');
63
+        $newMountPoint = $this->verifyMountPoint($arguments['share'], $mountpoints);
64
+        $absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
65
+        $arguments['ownerView'] = new View('/' . $arguments['share']['uid_owner'] . '/files');
66
+        parent::__construct($storage, $absMountPoint, $arguments, $loader);
67
+    }
68
+
69
+    /**
70
+     * check if the parent folder exists otherwise move the mount point up
71
+     *
72
+     * @param array $share
73
+     * @param SharedMount[] $mountpoints
74
+     * @return string
75
+     */
76
+    private function verifyMountPoint(&$share, array $mountpoints) {
77
+
78
+        $mountPoint = basename($share['file_target']);
79
+        $parent = dirname($share['file_target']);
80
+
81
+        if (!$this->recipientView->is_dir($parent)) {
82
+            $parent = Helper::getShareFolder($this->recipientView);
83
+        }
84
+
85
+        $newMountPoint = $this->generateUniqueTarget(
86
+            \OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
87
+            $this->recipientView,
88
+            $mountpoints
89
+        );
90
+
91
+        if ($newMountPoint !== $share['file_target']) {
92
+            $this->updateFileTarget($newMountPoint, $share);
93
+            $share['file_target'] = $newMountPoint;
94
+            $share['unique_name'] = true;
95
+        }
96
+
97
+        return $newMountPoint;
98
+    }
99
+
100
+    /**
101
+     * @param string $path
102
+     * @param View $view
103
+     * @param SharedMount[] $mountpoints
104
+     * @return mixed
105
+     */
106
+    private function generateUniqueTarget($path, $view, array $mountpoints) {
107
+        $pathinfo = pathinfo($path);
108
+        $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
109
+        $name = $pathinfo['filename'];
110
+        $dir = $pathinfo['dirname'];
111
+
112
+        // Helper function to find existing mount points
113
+        $mountpointExists = function($path) use ($mountpoints) {
114
+            foreach ($mountpoints as $mountpoint) {
115
+                if ($mountpoint->getShare()['file_target'] === $path) {
116
+                    return true;
117
+                }
118
+            }
119
+            return false;
120
+        };
121
+
122
+        $i = 2;
123
+        while ($view->file_exists($path) || $mountpointExists($path)) {
124
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
125
+            $i++;
126
+        }
127
+
128
+        return $path;
129
+    }
130
+
131
+    /**
132
+     * update fileTarget in the database if the mount point changed
133
+     *
134
+     * @param string $newPath
135
+     * @param array $share reference to the share which should be modified
136
+     * @return bool
137
+     */
138
+    private function updateFileTarget($newPath, &$share) {
139
+        // if the user renames a mount point from a group share we need to create a new db entry
140
+        // for the unique name
141
+        if ($share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP && empty($share['unique_name'])) {
142
+            $query = \OCP\DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,'
143
+            .' `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
144
+            .' `file_target`, `token`, `parent`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
145
+            $arguments = array($share['item_type'], $share['item_source'], $share['item_target'],
146
+                2, $this->user, $share['uid_owner'], $share['permissions'], $share['stime'], $share['file_source'],
147
+                $newPath, $share['token'], $share['id']);
148
+        } else {
149
+            // rename mount point
150
+            $query = \OCP\DB::prepare(
151
+                    'Update `*PREFIX*share`
152 152
 						SET `file_target` = ?
153 153
 						WHERE `id` = ?'
154
-			);
155
-			$arguments = array($newPath, $share['id']);
156
-		}
157
-
158
-		$result = $query->execute($arguments);
159
-
160
-		return $result === 1 ? true : false;
161
-	}
162
-
163
-	/**
164
-	 * Format a path to be relative to the /user/files/ directory
165
-	 *
166
-	 * @param string $path the absolute path
167
-	 * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
168
-	 * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
169
-	 */
170
-	protected function stripUserFilesPath($path) {
171
-		$trimmed = ltrim($path, '/');
172
-		$split = explode('/', $trimmed);
173
-
174
-		// it is not a file relative to data/user/files
175
-		if (count($split) < 3 || $split[1] !== 'files') {
176
-			\OCP\Util::writeLog('file sharing',
177
-				'Can not strip userid and "files/" from path: ' . $path,
178
-				\OCP\Util::ERROR);
179
-			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
180
-		}
181
-
182
-		// skip 'user' and 'files'
183
-		$sliced = array_slice($split, 2);
184
-		$relPath = implode('/', $sliced);
185
-
186
-		return '/' . $relPath;
187
-	}
188
-
189
-	/**
190
-	 * Move the mount point to $target
191
-	 *
192
-	 * @param string $target the target mount point
193
-	 * @return bool
194
-	 */
195
-	public function moveMount($target) {
196
-
197
-		$relTargetPath = $this->stripUserFilesPath($target);
198
-		$share = $this->storage->getShare();
199
-
200
-		$result = true;
201
-
202
-		if (!empty($share['grouped'])) {
203
-			foreach ($share['grouped'] as $s) {
204
-				$result = $this->updateFileTarget($relTargetPath, $s) && $result;
205
-			}
206
-		} else {
207
-			$result = $this->updateFileTarget($relTargetPath, $share) && $result;
208
-		}
209
-
210
-		if ($result) {
211
-			$this->setMountPoint($target);
212
-			$this->storage->setUniqueName();
213
-			$this->storage->setMountPoint($relTargetPath);
214
-
215
-		} else {
216
-			\OCP\Util::writeLog('file sharing',
217
-				'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
218
-				\OCP\Util::ERROR);
219
-		}
220
-
221
-		return $result;
222
-	}
223
-
224
-	/**
225
-	 * Remove the mount points
226
-	 *
227
-	 * @return bool
228
-	 */
229
-	public function removeMount() {
230
-		$mountManager = \OC\Files\Filesystem::getMountManager();
231
-		/** @var $storage \OC\Files\Storage\Shared */
232
-		$storage = $this->getStorage();
233
-		$result = $storage->unshareStorage();
234
-		$mountManager->removeMount($this->mountPoint);
235
-
236
-		return $result;
237
-	}
238
-
239
-	/**
240
-	 * @return array
241
-	 */
242
-	public function getShare() {
243
-		/** @var $storage \OC\Files\Storage\Shared */
244
-		$storage = $this->getStorage();
245
-		return $storage->getShare();
246
-	}
154
+            );
155
+            $arguments = array($newPath, $share['id']);
156
+        }
157
+
158
+        $result = $query->execute($arguments);
159
+
160
+        return $result === 1 ? true : false;
161
+    }
162
+
163
+    /**
164
+     * Format a path to be relative to the /user/files/ directory
165
+     *
166
+     * @param string $path the absolute path
167
+     * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
168
+     * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
169
+     */
170
+    protected function stripUserFilesPath($path) {
171
+        $trimmed = ltrim($path, '/');
172
+        $split = explode('/', $trimmed);
173
+
174
+        // it is not a file relative to data/user/files
175
+        if (count($split) < 3 || $split[1] !== 'files') {
176
+            \OCP\Util::writeLog('file sharing',
177
+                'Can not strip userid and "files/" from path: ' . $path,
178
+                \OCP\Util::ERROR);
179
+            throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
180
+        }
181
+
182
+        // skip 'user' and 'files'
183
+        $sliced = array_slice($split, 2);
184
+        $relPath = implode('/', $sliced);
185
+
186
+        return '/' . $relPath;
187
+    }
188
+
189
+    /**
190
+     * Move the mount point to $target
191
+     *
192
+     * @param string $target the target mount point
193
+     * @return bool
194
+     */
195
+    public function moveMount($target) {
196
+
197
+        $relTargetPath = $this->stripUserFilesPath($target);
198
+        $share = $this->storage->getShare();
199
+
200
+        $result = true;
201
+
202
+        if (!empty($share['grouped'])) {
203
+            foreach ($share['grouped'] as $s) {
204
+                $result = $this->updateFileTarget($relTargetPath, $s) && $result;
205
+            }
206
+        } else {
207
+            $result = $this->updateFileTarget($relTargetPath, $share) && $result;
208
+        }
209
+
210
+        if ($result) {
211
+            $this->setMountPoint($target);
212
+            $this->storage->setUniqueName();
213
+            $this->storage->setMountPoint($relTargetPath);
214
+
215
+        } else {
216
+            \OCP\Util::writeLog('file sharing',
217
+                'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
218
+                \OCP\Util::ERROR);
219
+        }
220
+
221
+        return $result;
222
+    }
223
+
224
+    /**
225
+     * Remove the mount points
226
+     *
227
+     * @return bool
228
+     */
229
+    public function removeMount() {
230
+        $mountManager = \OC\Files\Filesystem::getMountManager();
231
+        /** @var $storage \OC\Files\Storage\Shared */
232
+        $storage = $this->getStorage();
233
+        $result = $storage->unshareStorage();
234
+        $mountManager->removeMount($this->mountPoint);
235
+
236
+        return $result;
237
+    }
238
+
239
+    /**
240
+     * @return array
241
+     */
242
+    public function getShare() {
243
+        /** @var $storage \OC\Files\Storage\Shared */
244
+        $storage = $this->getStorage();
245
+        return $storage->getShare();
246
+    }
247 247
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -59,10 +59,10 @@  discard block
 block discarded – undo
59 59
 	 */
60 60
 	public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
61 61
 		$this->user = $arguments['user'];
62
-		$this->recipientView = new View('/' . $this->user . '/files');
62
+		$this->recipientView = new View('/'.$this->user.'/files');
63 63
 		$newMountPoint = $this->verifyMountPoint($arguments['share'], $mountpoints);
64
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
65
-		$arguments['ownerView'] = new View('/' . $arguments['share']['uid_owner'] . '/files');
64
+		$absMountPoint = '/'.$this->user.'/files'.$newMountPoint;
65
+		$arguments['ownerView'] = new View('/'.$arguments['share']['uid_owner'].'/files');
66 66
 		parent::__construct($storage, $absMountPoint, $arguments, $loader);
67 67
 	}
68 68
 
@@ -83,7 +83,7 @@  discard block
 block discarded – undo
83 83
 		}
84 84
 
85 85
 		$newMountPoint = $this->generateUniqueTarget(
86
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
86
+			\OC\Files\Filesystem::normalizePath($parent.'/'.$mountPoint),
87 87
 			$this->recipientView,
88 88
 			$mountpoints
89 89
 		);
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
 
122 122
 		$i = 2;
123 123
 		while ($view->file_exists($path) || $mountpointExists($path)) {
124
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
124
+			$path = Filesystem::normalizePath($dir.'/'.$name.' ('.$i.')'.$ext);
125 125
 			$i++;
126 126
 		}
127 127
 
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
 		// it is not a file relative to data/user/files
175 175
 		if (count($split) < 3 || $split[1] !== 'files') {
176 176
 			\OCP\Util::writeLog('file sharing',
177
-				'Can not strip userid and "files/" from path: ' . $path,
177
+				'Can not strip userid and "files/" from path: '.$path,
178 178
 				\OCP\Util::ERROR);
179 179
 			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
180 180
 		}
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
 		$sliced = array_slice($split, 2);
184 184
 		$relPath = implode('/', $sliced);
185 185
 
186
-		return '/' . $relPath;
186
+		return '/'.$relPath;
187 187
 	}
188 188
 
189 189
 	/**
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 
215 215
 		} else {
216 216
 			\OCP\Util::writeLog('file sharing',
217
-				'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
217
+				'Could not rename mount point for shared folder "'.$this->getMountPoint().'" to "'.$target.'"',
218 218
 				\OCP\Util::ERROR);
219 219
 		}
220 220
 
Please login to merge, or discard this patch.
lib/private/console/application.php 3 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -26,7 +26,6 @@
 block discarded – undo
26 26
 namespace OC\Console;
27 27
 
28 28
 use OC_App;
29
-use OC_Defaults;
30 29
 use OCP\Console\ConsoleEvent;
31 30
 use OCP\IConfig;
32 31
 use OCP\IRequest;
Please login to merge, or discard this patch.
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -39,110 +39,110 @@
 block discarded – undo
39 39
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
40 40
 
41 41
 class Application {
42
-	/** @var IConfig */
43
-	private $config;
44
-	/** @var EventDispatcherInterface */
45
-	private $dispatcher;
46
-	/** @var IRequest */
47
-	private $request;
42
+    /** @var IConfig */
43
+    private $config;
44
+    /** @var EventDispatcherInterface */
45
+    private $dispatcher;
46
+    /** @var IRequest */
47
+    private $request;
48 48
 
49
-	/**
50
-	 * @param IConfig $config
51
-	 * @param EventDispatcherInterface $dispatcher
52
-	 * @param IRequest $request
53
-	 */
54
-	public function __construct(IConfig $config, EventDispatcherInterface $dispatcher, IRequest $request) {
55
-		$defaults = \OC::$server->getThemingDefaults();
56
-		$this->config = $config;
57
-		$this->application = new SymfonyApplication($defaults->getName(), \OC_Util::getVersionString());
58
-		$this->dispatcher = $dispatcher;
59
-		$this->request = $request;
60
-	}
49
+    /**
50
+     * @param IConfig $config
51
+     * @param EventDispatcherInterface $dispatcher
52
+     * @param IRequest $request
53
+     */
54
+    public function __construct(IConfig $config, EventDispatcherInterface $dispatcher, IRequest $request) {
55
+        $defaults = \OC::$server->getThemingDefaults();
56
+        $this->config = $config;
57
+        $this->application = new SymfonyApplication($defaults->getName(), \OC_Util::getVersionString());
58
+        $this->dispatcher = $dispatcher;
59
+        $this->request = $request;
60
+    }
61 61
 
62
-	/**
63
-	 * @param InputInterface $input
64
-	 * @param OutputInterface $output
65
-	 * @throws \Exception
66
-	 */
67
-	public function loadCommands(InputInterface $input, OutputInterface $output) {
68
-		// $application is required to be defined in the register_command scripts
69
-		$application = $this->application;
70
-		$inputDefinition = $application->getDefinition();
71
-		$inputDefinition->addOption(
72
-			new InputOption(
73
-				'no-warnings', 
74
-				null, 
75
-				InputOption::VALUE_NONE, 
76
-				'Skip global warnings, show command output only', 
77
-				null
78
-			)
79
-		);
80
-		try {
81
-			$input->bind($inputDefinition);
82
-		} catch (\RuntimeException $e) {
83
-			//expected if there are extra options
84
-		}
85
-		if ($input->getOption('no-warnings')) {
86
-			$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
87
-		}
88
-		require_once __DIR__ . '/../../../core/register_command.php';
89
-		if ($this->config->getSystemValue('installed', false)) {
90
-			if (\OCP\Util::needUpgrade()) {
91
-				$output->writeln("Nextcloud or one of the apps require upgrade - only a limited number of commands are available");
92
-				$output->writeln("You may use your browser or the occ upgrade command to do the upgrade");
93
-			} elseif ($this->config->getSystemValue('maintenance', false)) {
94
-				$output->writeln("Nextcloud is in maintenance mode - no app have been loaded");
95
-			} else {
96
-				OC_App::loadApps();
97
-				foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) {
98
-					$appPath = \OC_App::getAppPath($app);
99
-					if($appPath === false) {
100
-						continue;
101
-					}
102
-					\OC::$loader->addValidRoot($appPath);
103
-					$file = $appPath . '/appinfo/register_command.php';
104
-					if (file_exists($file)) {
105
-						require $file;
106
-					}
107
-				}
108
-			}
109
-		} else {
110
-			$output->writeln("Nextcloud is not installed - only a limited number of commands are available");
111
-		}
112
-		$input = new ArgvInput();
113
-		if ($input->getFirstArgument() !== 'check') {
114
-			$errors = \OC_Util::checkServer(\OC::$server->getConfig());
115
-			if (!empty($errors)) {
116
-				foreach ($errors as $error) {
117
-					$output->writeln((string)$error['error']);
118
-					$output->writeln((string)$error['hint']);
119
-					$output->writeln('');
120
-				}
121
-				throw new \Exception("Environment not properly prepared.");
122
-			}
123
-		}
124
-	}
62
+    /**
63
+     * @param InputInterface $input
64
+     * @param OutputInterface $output
65
+     * @throws \Exception
66
+     */
67
+    public function loadCommands(InputInterface $input, OutputInterface $output) {
68
+        // $application is required to be defined in the register_command scripts
69
+        $application = $this->application;
70
+        $inputDefinition = $application->getDefinition();
71
+        $inputDefinition->addOption(
72
+            new InputOption(
73
+                'no-warnings', 
74
+                null, 
75
+                InputOption::VALUE_NONE, 
76
+                'Skip global warnings, show command output only', 
77
+                null
78
+            )
79
+        );
80
+        try {
81
+            $input->bind($inputDefinition);
82
+        } catch (\RuntimeException $e) {
83
+            //expected if there are extra options
84
+        }
85
+        if ($input->getOption('no-warnings')) {
86
+            $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
87
+        }
88
+        require_once __DIR__ . '/../../../core/register_command.php';
89
+        if ($this->config->getSystemValue('installed', false)) {
90
+            if (\OCP\Util::needUpgrade()) {
91
+                $output->writeln("Nextcloud or one of the apps require upgrade - only a limited number of commands are available");
92
+                $output->writeln("You may use your browser or the occ upgrade command to do the upgrade");
93
+            } elseif ($this->config->getSystemValue('maintenance', false)) {
94
+                $output->writeln("Nextcloud is in maintenance mode - no app have been loaded");
95
+            } else {
96
+                OC_App::loadApps();
97
+                foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) {
98
+                    $appPath = \OC_App::getAppPath($app);
99
+                    if($appPath === false) {
100
+                        continue;
101
+                    }
102
+                    \OC::$loader->addValidRoot($appPath);
103
+                    $file = $appPath . '/appinfo/register_command.php';
104
+                    if (file_exists($file)) {
105
+                        require $file;
106
+                    }
107
+                }
108
+            }
109
+        } else {
110
+            $output->writeln("Nextcloud is not installed - only a limited number of commands are available");
111
+        }
112
+        $input = new ArgvInput();
113
+        if ($input->getFirstArgument() !== 'check') {
114
+            $errors = \OC_Util::checkServer(\OC::$server->getConfig());
115
+            if (!empty($errors)) {
116
+                foreach ($errors as $error) {
117
+                    $output->writeln((string)$error['error']);
118
+                    $output->writeln((string)$error['hint']);
119
+                    $output->writeln('');
120
+                }
121
+                throw new \Exception("Environment not properly prepared.");
122
+            }
123
+        }
124
+    }
125 125
 
126
-	/**
127
-	 * Sets whether to automatically exit after a command execution or not.
128
-	 *
129
-	 * @param bool $boolean Whether to automatically exit after a command execution or not
130
-	 */
131
-	public function setAutoExit($boolean) {
132
-		$this->application->setAutoExit($boolean);
133
-	}
126
+    /**
127
+     * Sets whether to automatically exit after a command execution or not.
128
+     *
129
+     * @param bool $boolean Whether to automatically exit after a command execution or not
130
+     */
131
+    public function setAutoExit($boolean) {
132
+        $this->application->setAutoExit($boolean);
133
+    }
134 134
 
135
-	/**
136
-	 * @param InputInterface $input
137
-	 * @param OutputInterface $output
138
-	 * @return int
139
-	 * @throws \Exception
140
-	 */
141
-	public function run(InputInterface $input = null, OutputInterface $output = null) {
142
-		$this->dispatcher->dispatch(ConsoleEvent::EVENT_RUN, new ConsoleEvent(
143
-			ConsoleEvent::EVENT_RUN,
144
-			$this->request->server['argv']
145
-		));
146
-		return $this->application->run($input, $output);
147
-	}
135
+    /**
136
+     * @param InputInterface $input
137
+     * @param OutputInterface $output
138
+     * @return int
139
+     * @throws \Exception
140
+     */
141
+    public function run(InputInterface $input = null, OutputInterface $output = null) {
142
+        $this->dispatcher->dispatch(ConsoleEvent::EVENT_RUN, new ConsoleEvent(
143
+            ConsoleEvent::EVENT_RUN,
144
+            $this->request->server['argv']
145
+        ));
146
+        return $this->application->run($input, $output);
147
+    }
148 148
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
 		if ($input->getOption('no-warnings')) {
86 86
 			$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
87 87
 		}
88
-		require_once __DIR__ . '/../../../core/register_command.php';
88
+		require_once __DIR__.'/../../../core/register_command.php';
89 89
 		if ($this->config->getSystemValue('installed', false)) {
90 90
 			if (\OCP\Util::needUpgrade()) {
91 91
 				$output->writeln("Nextcloud or one of the apps require upgrade - only a limited number of commands are available");
@@ -96,11 +96,11 @@  discard block
 block discarded – undo
96 96
 				OC_App::loadApps();
97 97
 				foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) {
98 98
 					$appPath = \OC_App::getAppPath($app);
99
-					if($appPath === false) {
99
+					if ($appPath === false) {
100 100
 						continue;
101 101
 					}
102 102
 					\OC::$loader->addValidRoot($appPath);
103
-					$file = $appPath . '/appinfo/register_command.php';
103
+					$file = $appPath.'/appinfo/register_command.php';
104 104
 					if (file_exists($file)) {
105 105
 						require $file;
106 106
 					}
@@ -114,8 +114,8 @@  discard block
 block discarded – undo
114 114
 			$errors = \OC_Util::checkServer(\OC::$server->getConfig());
115 115
 			if (!empty($errors)) {
116 116
 				foreach ($errors as $error) {
117
-					$output->writeln((string)$error['error']);
118
-					$output->writeln((string)$error['hint']);
117
+					$output->writeln((string) $error['error']);
118
+					$output->writeln((string) $error['hint']);
119 119
 					$output->writeln('');
120 120
 				}
121 121
 				throw new \Exception("Environment not properly prepared.");
Please login to merge, or discard this patch.
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   +1687 added lines, -1687 removed lines patch added patch discarded remove patch
@@ -45,1435 +45,1435 @@  discard block
 block discarded – undo
45 45
  * @package OCA\user_ldap\lib
46 46
  */
47 47
 class Access extends LDAPUtility implements user\IUserTools {
48
-	/**
49
-	 * @var \OCA\user_ldap\lib\Connection
50
-	 */
51
-	public $connection;
52
-	public $userManager;
53
-	//never ever check this var directly, always use getPagedSearchResultState
54
-	protected $pagedSearchedSuccessful;
55
-
56
-	/**
57
-	 * @var string[] $cookies an array of returned Paged Result cookies
58
-	 */
59
-	protected $cookies = array();
60
-
61
-	/**
62
-	 * @var string $lastCookie the last cookie returned from a Paged Results
63
-	 * operation, defaults to an empty string
64
-	 */
65
-	protected $lastCookie = '';
66
-
67
-	/**
68
-	 * @var AbstractMapping $userMapper
69
-	 */
70
-	protected $userMapper;
71
-
72
-	/**
73
-	* @var AbstractMapping $userMapper
74
-	*/
75
-	protected $groupMapper;
76
-
77
-	public function __construct(Connection $connection, ILDAPWrapper $ldap,
78
-		user\Manager $userManager) {
79
-		parent::__construct($ldap);
80
-		$this->connection = $connection;
81
-		$this->userManager = $userManager;
82
-		$this->userManager->setLdapAccess($this);
83
-	}
84
-
85
-	/**
86
-	 * sets the User Mapper
87
-	 * @param AbstractMapping $mapper
88
-	 */
89
-	public function setUserMapper(AbstractMapping $mapper) {
90
-		$this->userMapper = $mapper;
91
-	}
92
-
93
-	/**
94
-	 * returns the User Mapper
95
-	 * @throws \Exception
96
-	 * @return AbstractMapping
97
-	 */
98
-	public function getUserMapper() {
99
-		if(is_null($this->userMapper)) {
100
-			throw new \Exception('UserMapper was not assigned to this Access instance.');
101
-		}
102
-		return $this->userMapper;
103
-	}
104
-
105
-	/**
106
-	 * sets the Group Mapper
107
-	 * @param AbstractMapping $mapper
108
-	 */
109
-	public function setGroupMapper(AbstractMapping $mapper) {
110
-		$this->groupMapper = $mapper;
111
-	}
112
-
113
-	/**
114
-	 * returns the Group Mapper
115
-	 * @throws \Exception
116
-	 * @return AbstractMapping
117
-	 */
118
-	public function getGroupMapper() {
119
-		if(is_null($this->groupMapper)) {
120
-			throw new \Exception('GroupMapper was not assigned to this Access instance.');
121
-		}
122
-		return $this->groupMapper;
123
-	}
124
-
125
-	/**
126
-	 * @return bool
127
-	 */
128
-	private function checkConnection() {
129
-		return ($this->connection instanceof Connection);
130
-	}
131
-
132
-	/**
133
-	 * returns the Connection instance
134
-	 * @return \OCA\user_ldap\lib\Connection
135
-	 */
136
-	public function getConnection() {
137
-		return $this->connection;
138
-	}
139
-
140
-	/**
141
-	 * reads a given attribute for an LDAP record identified by a DN
142
-	 * @param string $dn the record in question
143
-	 * @param string $attr the attribute that shall be retrieved
144
-	 *        if empty, just check the record's existence
145
-	 * @param string $filter
146
-	 * @return array|false an array of values on success or an empty
147
-	 *          array if $attr is empty, false otherwise
148
-	 */
149
-	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
150
-		if(!$this->checkConnection()) {
151
-			\OCP\Util::writeLog('user_ldap',
152
-				'No LDAP Connector assigned, access impossible for readAttribute.',
153
-				\OCP\Util::WARN);
154
-			return false;
155
-		}
156
-		$cr = $this->connection->getConnectionResource();
157
-		if(!$this->ldap->isResource($cr)) {
158
-			//LDAP not available
159
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
160
-			return false;
161
-		}
162
-		//Cancel possibly running Paged Results operation, otherwise we run in
163
-		//LDAP protocol errors
164
-		$this->abandonPagedSearch();
165
-		// openLDAP requires that we init a new Paged Search. Not needed by AD,
166
-		// but does not hurt either.
167
-		$pagingSize = intval($this->connection->ldapPagingSize);
168
-		// 0 won't result in replies, small numbers may leave out groups
169
-		// (cf. #12306), 500 is default for paging and should work everywhere.
170
-		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
171
-		$this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0);
172
-		$dn = $this->DNasBaseParameter($dn);
173
-		$rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
174
-		if(!$this->ldap->isResource($rr)) {
175
-			if(!empty($attr)) {
176
-				//do not throw this message on userExists check, irritates
177
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
178
-			}
179
-			//in case an error occurs , e.g. object does not exist
180
-			return false;
181
-		}
182
-		if (empty($attr) && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
183
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
184
-			return array();
185
-		}
186
-		$er = $this->ldap->firstEntry($cr, $rr);
187
-		if(!$this->ldap->isResource($er)) {
188
-			//did not match the filter, return false
189
-			return false;
190
-		}
191
-		//LDAP attributes are not case sensitive
192
-		$result = \OCP\Util::mb_array_change_key_case(
193
-				$this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
194
-		$attr = mb_strtolower($attr, 'UTF-8');
195
-
196
-		if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
197
-			$values = array();
198
-			for($i=0;$i<$result[$attr]['count'];$i++) {
199
-				if($this->resemblesDN($attr)) {
200
-					$values[] = $this->sanitizeDN($result[$attr][$i]);
201
-				} elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
202
-					$values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
203
-				} else {
204
-					$values[] = $result[$attr][$i];
205
-				}
206
-			}
207
-			return $values;
208
-		}
209
-		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
210
-		return false;
211
-	}
212
-
213
-	/**
214
-	 * checks whether the given attributes value is probably a DN
215
-	 * @param string $attr the attribute in question
216
-	 * @return boolean if so true, otherwise false
217
-	 */
218
-	private function resemblesDN($attr) {
219
-		$resemblingAttributes = array(
220
-			'dn',
221
-			'uniquemember',
222
-			'member',
223
-			// memberOf is an "operational" attribute, without a definition in any RFC
224
-			'memberof'
225
-		);
226
-		return in_array($attr, $resemblingAttributes);
227
-	}
228
-
229
-	/**
230
-	 * checks whether the given string is probably a DN
231
-	 * @param string $string
232
-	 * @return boolean
233
-	 */
234
-	public function stringResemblesDN($string) {
235
-		$r = $this->ldap->explodeDN($string, 0);
236
-		// if exploding a DN succeeds and does not end up in
237
-		// an empty array except for $r[count] being 0.
238
-		return (is_array($r) && count($r) > 1);
239
-	}
240
-
241
-	/**
242
-	 * sanitizes a DN received from the LDAP server
243
-	 * @param array $dn the DN in question
244
-	 * @return array the sanitized DN
245
-	 */
246
-	private function sanitizeDN($dn) {
247
-		//treating multiple base DNs
248
-		if(is_array($dn)) {
249
-			$result = array();
250
-			foreach($dn as $singleDN) {
251
-				$result[] = $this->sanitizeDN($singleDN);
252
-			}
253
-			return $result;
254
-		}
255
-
256
-		//OID sometimes gives back DNs with whitespace after the comma
257
-		// a la "uid=foo, cn=bar, dn=..." We need to tackle this!
258
-		$dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
259
-
260
-		//make comparisons and everything work
261
-		$dn = mb_strtolower($dn, 'UTF-8');
262
-
263
-		//escape DN values according to RFC 2253 – this is already done by ldap_explode_dn
264
-		//to use the DN in search filters, \ needs to be escaped to \5c additionally
265
-		//to use them in bases, we convert them back to simple backslashes in readAttribute()
266
-		$replacements = array(
267
-			'\,' => '\5c2C',
268
-			'\=' => '\5c3D',
269
-			'\+' => '\5c2B',
270
-			'\<' => '\5c3C',
271
-			'\>' => '\5c3E',
272
-			'\;' => '\5c3B',
273
-			'\"' => '\5c22',
274
-			'\#' => '\5c23',
275
-			'('  => '\28',
276
-			')'  => '\29',
277
-			'*'  => '\2A',
278
-		);
279
-		$dn = str_replace(array_keys($replacements), array_values($replacements), $dn);
280
-
281
-		return $dn;
282
-	}
283
-
284
-	/**
285
-	 * returns a DN-string that is cleaned from not domain parts, e.g.
286
-	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
287
-	 * becomes dc=foobar,dc=server,dc=org
288
-	 * @param string $dn
289
-	 * @return string
290
-	 */
291
-	public function getDomainDNFromDN($dn) {
292
-		$allParts = $this->ldap->explodeDN($dn, 0);
293
-		if($allParts === false) {
294
-			//not a valid DN
295
-			return '';
296
-		}
297
-		$domainParts = array();
298
-		$dcFound = false;
299
-		foreach($allParts as $part) {
300
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
301
-				$dcFound = true;
302
-			}
303
-			if($dcFound) {
304
-				$domainParts[] = $part;
305
-			}
306
-		}
307
-		$domainDN = implode(',', $domainParts);
308
-		return $domainDN;
309
-	}
310
-
311
-	/**
312
-	 * returns the LDAP DN for the given internal ownCloud name of the group
313
-	 * @param string $name the ownCloud name in question
314
-	 * @return string|false LDAP DN on success, otherwise false
315
-	 */
316
-	public function groupname2dn($name) {
317
-		return $this->groupMapper->getDNbyName($name);
318
-	}
319
-
320
-	/**
321
-	 * returns the LDAP DN for the given internal ownCloud name of the user
322
-	 * @param string $name the ownCloud name in question
323
-	 * @return string|false with the LDAP DN on success, otherwise false
324
-	 */
325
-	public function username2dn($name) {
326
-		$fdn = $this->userMapper->getDNbyName($name);
327
-
328
-		//Check whether the DN belongs to the Base, to avoid issues on multi-
329
-		//server setups
330
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
331
-			return $fdn;
332
-		}
333
-
334
-		return false;
335
-	}
336
-
337
-	/**
48
+    /**
49
+     * @var \OCA\user_ldap\lib\Connection
50
+     */
51
+    public $connection;
52
+    public $userManager;
53
+    //never ever check this var directly, always use getPagedSearchResultState
54
+    protected $pagedSearchedSuccessful;
55
+
56
+    /**
57
+     * @var string[] $cookies an array of returned Paged Result cookies
58
+     */
59
+    protected $cookies = array();
60
+
61
+    /**
62
+     * @var string $lastCookie the last cookie returned from a Paged Results
63
+     * operation, defaults to an empty string
64
+     */
65
+    protected $lastCookie = '';
66
+
67
+    /**
68
+     * @var AbstractMapping $userMapper
69
+     */
70
+    protected $userMapper;
71
+
72
+    /**
73
+     * @var AbstractMapping $userMapper
74
+     */
75
+    protected $groupMapper;
76
+
77
+    public function __construct(Connection $connection, ILDAPWrapper $ldap,
78
+        user\Manager $userManager) {
79
+        parent::__construct($ldap);
80
+        $this->connection = $connection;
81
+        $this->userManager = $userManager;
82
+        $this->userManager->setLdapAccess($this);
83
+    }
84
+
85
+    /**
86
+     * sets the User Mapper
87
+     * @param AbstractMapping $mapper
88
+     */
89
+    public function setUserMapper(AbstractMapping $mapper) {
90
+        $this->userMapper = $mapper;
91
+    }
92
+
93
+    /**
94
+     * returns the User Mapper
95
+     * @throws \Exception
96
+     * @return AbstractMapping
97
+     */
98
+    public function getUserMapper() {
99
+        if(is_null($this->userMapper)) {
100
+            throw new \Exception('UserMapper was not assigned to this Access instance.');
101
+        }
102
+        return $this->userMapper;
103
+    }
104
+
105
+    /**
106
+     * sets the Group Mapper
107
+     * @param AbstractMapping $mapper
108
+     */
109
+    public function setGroupMapper(AbstractMapping $mapper) {
110
+        $this->groupMapper = $mapper;
111
+    }
112
+
113
+    /**
114
+     * returns the Group Mapper
115
+     * @throws \Exception
116
+     * @return AbstractMapping
117
+     */
118
+    public function getGroupMapper() {
119
+        if(is_null($this->groupMapper)) {
120
+            throw new \Exception('GroupMapper was not assigned to this Access instance.');
121
+        }
122
+        return $this->groupMapper;
123
+    }
124
+
125
+    /**
126
+     * @return bool
127
+     */
128
+    private function checkConnection() {
129
+        return ($this->connection instanceof Connection);
130
+    }
131
+
132
+    /**
133
+     * returns the Connection instance
134
+     * @return \OCA\user_ldap\lib\Connection
135
+     */
136
+    public function getConnection() {
137
+        return $this->connection;
138
+    }
139
+
140
+    /**
141
+     * reads a given attribute for an LDAP record identified by a DN
142
+     * @param string $dn the record in question
143
+     * @param string $attr the attribute that shall be retrieved
144
+     *        if empty, just check the record's existence
145
+     * @param string $filter
146
+     * @return array|false an array of values on success or an empty
147
+     *          array if $attr is empty, false otherwise
148
+     */
149
+    public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
150
+        if(!$this->checkConnection()) {
151
+            \OCP\Util::writeLog('user_ldap',
152
+                'No LDAP Connector assigned, access impossible for readAttribute.',
153
+                \OCP\Util::WARN);
154
+            return false;
155
+        }
156
+        $cr = $this->connection->getConnectionResource();
157
+        if(!$this->ldap->isResource($cr)) {
158
+            //LDAP not available
159
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
160
+            return false;
161
+        }
162
+        //Cancel possibly running Paged Results operation, otherwise we run in
163
+        //LDAP protocol errors
164
+        $this->abandonPagedSearch();
165
+        // openLDAP requires that we init a new Paged Search. Not needed by AD,
166
+        // but does not hurt either.
167
+        $pagingSize = intval($this->connection->ldapPagingSize);
168
+        // 0 won't result in replies, small numbers may leave out groups
169
+        // (cf. #12306), 500 is default for paging and should work everywhere.
170
+        $maxResults = $pagingSize > 20 ? $pagingSize : 500;
171
+        $this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0);
172
+        $dn = $this->DNasBaseParameter($dn);
173
+        $rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
174
+        if(!$this->ldap->isResource($rr)) {
175
+            if(!empty($attr)) {
176
+                //do not throw this message on userExists check, irritates
177
+                \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
178
+            }
179
+            //in case an error occurs , e.g. object does not exist
180
+            return false;
181
+        }
182
+        if (empty($attr) && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) {
183
+            \OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
184
+            return array();
185
+        }
186
+        $er = $this->ldap->firstEntry($cr, $rr);
187
+        if(!$this->ldap->isResource($er)) {
188
+            //did not match the filter, return false
189
+            return false;
190
+        }
191
+        //LDAP attributes are not case sensitive
192
+        $result = \OCP\Util::mb_array_change_key_case(
193
+                $this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
194
+        $attr = mb_strtolower($attr, 'UTF-8');
195
+
196
+        if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
197
+            $values = array();
198
+            for($i=0;$i<$result[$attr]['count'];$i++) {
199
+                if($this->resemblesDN($attr)) {
200
+                    $values[] = $this->sanitizeDN($result[$attr][$i]);
201
+                } elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
202
+                    $values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
203
+                } else {
204
+                    $values[] = $result[$attr][$i];
205
+                }
206
+            }
207
+            return $values;
208
+        }
209
+        \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
210
+        return false;
211
+    }
212
+
213
+    /**
214
+     * checks whether the given attributes value is probably a DN
215
+     * @param string $attr the attribute in question
216
+     * @return boolean if so true, otherwise false
217
+     */
218
+    private function resemblesDN($attr) {
219
+        $resemblingAttributes = array(
220
+            'dn',
221
+            'uniquemember',
222
+            'member',
223
+            // memberOf is an "operational" attribute, without a definition in any RFC
224
+            'memberof'
225
+        );
226
+        return in_array($attr, $resemblingAttributes);
227
+    }
228
+
229
+    /**
230
+     * checks whether the given string is probably a DN
231
+     * @param string $string
232
+     * @return boolean
233
+     */
234
+    public function stringResemblesDN($string) {
235
+        $r = $this->ldap->explodeDN($string, 0);
236
+        // if exploding a DN succeeds and does not end up in
237
+        // an empty array except for $r[count] being 0.
238
+        return (is_array($r) && count($r) > 1);
239
+    }
240
+
241
+    /**
242
+     * sanitizes a DN received from the LDAP server
243
+     * @param array $dn the DN in question
244
+     * @return array the sanitized DN
245
+     */
246
+    private function sanitizeDN($dn) {
247
+        //treating multiple base DNs
248
+        if(is_array($dn)) {
249
+            $result = array();
250
+            foreach($dn as $singleDN) {
251
+                $result[] = $this->sanitizeDN($singleDN);
252
+            }
253
+            return $result;
254
+        }
255
+
256
+        //OID sometimes gives back DNs with whitespace after the comma
257
+        // a la "uid=foo, cn=bar, dn=..." We need to tackle this!
258
+        $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn);
259
+
260
+        //make comparisons and everything work
261
+        $dn = mb_strtolower($dn, 'UTF-8');
262
+
263
+        //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn
264
+        //to use the DN in search filters, \ needs to be escaped to \5c additionally
265
+        //to use them in bases, we convert them back to simple backslashes in readAttribute()
266
+        $replacements = array(
267
+            '\,' => '\5c2C',
268
+            '\=' => '\5c3D',
269
+            '\+' => '\5c2B',
270
+            '\<' => '\5c3C',
271
+            '\>' => '\5c3E',
272
+            '\;' => '\5c3B',
273
+            '\"' => '\5c22',
274
+            '\#' => '\5c23',
275
+            '('  => '\28',
276
+            ')'  => '\29',
277
+            '*'  => '\2A',
278
+        );
279
+        $dn = str_replace(array_keys($replacements), array_values($replacements), $dn);
280
+
281
+        return $dn;
282
+    }
283
+
284
+    /**
285
+     * returns a DN-string that is cleaned from not domain parts, e.g.
286
+     * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
287
+     * becomes dc=foobar,dc=server,dc=org
288
+     * @param string $dn
289
+     * @return string
290
+     */
291
+    public function getDomainDNFromDN($dn) {
292
+        $allParts = $this->ldap->explodeDN($dn, 0);
293
+        if($allParts === false) {
294
+            //not a valid DN
295
+            return '';
296
+        }
297
+        $domainParts = array();
298
+        $dcFound = false;
299
+        foreach($allParts as $part) {
300
+            if(!$dcFound && strpos($part, 'dc=') === 0) {
301
+                $dcFound = true;
302
+            }
303
+            if($dcFound) {
304
+                $domainParts[] = $part;
305
+            }
306
+        }
307
+        $domainDN = implode(',', $domainParts);
308
+        return $domainDN;
309
+    }
310
+
311
+    /**
312
+     * returns the LDAP DN for the given internal ownCloud name of the group
313
+     * @param string $name the ownCloud name in question
314
+     * @return string|false LDAP DN on success, otherwise false
315
+     */
316
+    public function groupname2dn($name) {
317
+        return $this->groupMapper->getDNbyName($name);
318
+    }
319
+
320
+    /**
321
+     * returns the LDAP DN for the given internal ownCloud name of the user
322
+     * @param string $name the ownCloud name in question
323
+     * @return string|false with the LDAP DN on success, otherwise false
324
+     */
325
+    public function username2dn($name) {
326
+        $fdn = $this->userMapper->getDNbyName($name);
327
+
328
+        //Check whether the DN belongs to the Base, to avoid issues on multi-
329
+        //server setups
330
+        if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
331
+            return $fdn;
332
+        }
333
+
334
+        return false;
335
+    }
336
+
337
+    /**
338 338
 	public function ocname2dn($name, $isUser) {
339
-	 * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
340
-	 * @param string $fdn the dn of the group object
341
-	 * @param string $ldapName optional, the display name of the object
342
-	 * @return string|false with the name to use in ownCloud, false on DN outside of search DN
343
-	 */
344
-	public function dn2groupname($fdn, $ldapName = null) {
345
-		//To avoid bypassing the base DN settings under certain circumstances
346
-		//with the group support, check whether the provided DN matches one of
347
-		//the given Bases
348
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
349
-			return false;
350
-		}
351
-
352
-		return $this->dn2ocname($fdn, $ldapName, false);
353
-	}
354
-
355
-	/**
356
-	 * accepts an array of group DNs and tests whether they match the user
357
-	 * filter by doing read operations against the group entries. Returns an
358
-	 * array of DNs that match the filter.
359
-	 *
360
-	 * @param string[] $groupDNs
361
-	 * @return string[]
362
-	 */
363
-	public function groupsMatchFilter($groupDNs) {
364
-		$validGroupDNs = [];
365
-		foreach($groupDNs as $dn) {
366
-			$cacheKey = 'groupsMatchFilter-'.$dn;
367
-			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
368
-			if(!is_null($groupMatchFilter)) {
369
-				if($groupMatchFilter) {
370
-					$validGroupDNs[] = $dn;
371
-				}
372
-				continue;
373
-			}
374
-
375
-			// Check the base DN first. If this is not met already, we don't
376
-			// need to ask the server at all.
377
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
378
-				$this->connection->writeToCache($cacheKey, false);
379
-				continue;
380
-			}
381
-
382
-			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
383
-			if(is_array($result)) {
384
-				$this->connection->writeToCache($cacheKey, true);
385
-				$validGroupDNs[] = $dn;
386
-			} else {
387
-				$this->connection->writeToCache($cacheKey, false);
388
-			}
389
-
390
-		}
391
-		return $validGroupDNs;
392
-	}
393
-
394
-	/**
395
-	 * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
396
-	 * @param string $dn the dn of the user object
397
-	 * @param string $ldapName optional, the display name of the object
398
-	 * @return string|false with with the name to use in ownCloud
399
-	 */
400
-	public function dn2username($fdn, $ldapName = null) {
401
-		//To avoid bypassing the base DN settings under certain circumstances
402
-		//with the group support, check whether the provided DN matches one of
403
-		//the given Bases
404
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
405
-			return false;
406
-		}
407
-
408
-		return $this->dn2ocname($fdn, $ldapName, true);
409
-	}
410
-
411
-	/**
412
-	 * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
413
-	 * @param string $dn the dn of the user object
414
-	 * @param string $ldapName optional, the display name of the object
415
-	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
416
-	 * @return string|false with with the name to use in ownCloud
417
-	 */
418
-	public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
419
-		if($isUser) {
420
-			$mapper = $this->getUserMapper();
421
-			$nameAttribute = $this->connection->ldapUserDisplayName;
422
-		} else {
423
-			$mapper = $this->getGroupMapper();
424
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
425
-		}
426
-
427
-		//let's try to retrieve the ownCloud name from the mappings table
428
-		$ocName = $mapper->getNameByDN($fdn);
429
-		if(is_string($ocName)) {
430
-			return $ocName;
431
-		}
432
-
433
-		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
434
-		$uuid = $this->getUUID($fdn, $isUser);
435
-		if(is_string($uuid)) {
436
-			$ocName = $mapper->getNameByUUID($uuid);
437
-			if(is_string($ocName)) {
438
-				$mapper->setDNbyUUID($fdn, $uuid);
439
-				return $ocName;
440
-			}
441
-		} else {
442
-			//If the UUID can't be detected something is foul.
443
-			\OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
444
-			return false;
445
-		}
446
-
447
-		if(is_null($ldapName)) {
448
-			$ldapName = $this->readAttribute($fdn, $nameAttribute);
449
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
450
-				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
451
-				return false;
452
-			}
453
-			$ldapName = $ldapName[0];
454
-		}
455
-
456
-		if($isUser) {
457
-			$usernameAttribute = $this->connection->ldapExpertUsernameAttr;
458
-			if(!empty($usernameAttribute)) {
459
-				$username = $this->readAttribute($fdn, $usernameAttribute);
460
-				$username = $username[0];
461
-			} else {
462
-				$username = $uuid;
463
-			}
464
-			$intName = $this->sanitizeUsername($username);
465
-		} else {
466
-			$intName = $ldapName;
467
-		}
468
-
469
-		//a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
470
-		//disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
471
-		//NOTE: mind, disabling cache affects only this instance! Using it
472
-		// outside of core user management will still cache the user as non-existing.
473
-		$originalTTL = $this->connection->ldapCacheTTL;
474
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
475
-		if(($isUser && !\OCP\User::userExists($intName))
476
-			|| (!$isUser && !\OC_Group::groupExists($intName))) {
477
-			if($mapper->map($fdn, $intName, $uuid)) {
478
-				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
479
-				return $intName;
480
-			}
481
-		}
482
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
483
-
484
-		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
485
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
486
-			return $altName;
487
-		}
488
-
489
-		//if everything else did not help..
490
-		\OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
491
-		return false;
492
-	}
493
-
494
-	/**
495
-	 * gives back the user names as they are used ownClod internally
496
-	 * @param array $ldapUsers as returned by fetchList()
497
-	 * @return array an array with the user names to use in ownCloud
498
-	 *
499
-	 * gives back the user names as they are used ownClod internally
500
-	 */
501
-	public function ownCloudUserNames($ldapUsers) {
502
-		return $this->ldap2ownCloudNames($ldapUsers, true);
503
-	}
504
-
505
-	/**
506
-	 * gives back the group names as they are used ownClod internally
507
-	 * @param array $ldapGroups as returned by fetchList()
508
-	 * @return array an array with the group names to use in ownCloud
509
-	 *
510
-	 * gives back the group names as they are used ownClod internally
511
-	 */
512
-	public function ownCloudGroupNames($ldapGroups) {
513
-		return $this->ldap2ownCloudNames($ldapGroups, false);
514
-	}
515
-
516
-	/**
517
-	 * @param array $ldapObjects as returned by fetchList()
518
-	 * @param bool $isUsers
519
-	 * @return array
520
-	 */
521
-	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
522
-		if($isUsers) {
523
-			$nameAttribute = $this->connection->ldapUserDisplayName;
524
-			$sndAttribute  = $this->connection->ldapUserDisplayName2;
525
-		} else {
526
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
527
-		}
528
-		$ownCloudNames = array();
529
-
530
-		foreach($ldapObjects as $ldapObject) {
531
-			$nameByLDAP = null;
532
-			if(    isset($ldapObject[$nameAttribute])
533
-				&& is_array($ldapObject[$nameAttribute])
534
-				&& isset($ldapObject[$nameAttribute][0])
535
-			) {
536
-				// might be set, but not necessarily. if so, we use it.
537
-				$nameByLDAP = $ldapObject[$nameAttribute][0];
538
-			}
539
-
540
-			$ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
541
-			if($ocName) {
542
-				$ownCloudNames[] = $ocName;
543
-				if($isUsers) {
544
-					//cache the user names so it does not need to be retrieved
545
-					//again later (e.g. sharing dialogue).
546
-					if(is_null($nameByLDAP)) {
547
-						continue;
548
-					}
549
-					$sndName = isset($ldapObject[$sndAttribute][0])
550
-						? $ldapObject[$sndAttribute][0] : '';
551
-					$this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
552
-				}
553
-			}
554
-		}
555
-		return $ownCloudNames;
556
-	}
557
-
558
-	/**
559
-	 * caches the user display name
560
-	 * @param string $ocName the internal ownCloud username
561
-	 * @param string|false $home the home directory path
562
-	 */
563
-	public function cacheUserHome($ocName, $home) {
564
-		$cacheKey = 'getHome'.$ocName;
565
-		$this->connection->writeToCache($cacheKey, $home);
566
-	}
567
-
568
-	/**
569
-	 * caches a user as existing
570
-	 * @param string $ocName the internal ownCloud username
571
-	 */
572
-	public function cacheUserExists($ocName) {
573
-		$this->connection->writeToCache('userExists'.$ocName, true);
574
-	}
575
-
576
-	/**
577
-	 * caches the user display name
578
-	 * @param string $ocName the internal ownCloud username
579
-	 * @param string $displayName the display name
580
-	 * @param string $displayName2 the second display name
581
-	 */
582
-	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
583
-		$user = $this->userManager->get($ocName);
584
-		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
585
-		$cacheKeyTrunk = 'getDisplayName';
586
-		$this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
587
-	}
588
-
589
-	/**
590
-	 * creates a unique name for internal ownCloud use for users. Don't call it directly.
591
-	 * @param string $name the display name of the object
592
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful
593
-	 *
594
-	 * Instead of using this method directly, call
595
-	 * createAltInternalOwnCloudName($name, true)
596
-	 */
597
-	private function _createAltInternalOwnCloudNameForUsers($name) {
598
-		$attempts = 0;
599
-		//while loop is just a precaution. If a name is not generated within
600
-		//20 attempts, something else is very wrong. Avoids infinite loop.
601
-		while($attempts < 20){
602
-			$altName = $name . '_' . rand(1000,9999);
603
-			if(!\OCP\User::userExists($altName)) {
604
-				return $altName;
605
-			}
606
-			$attempts++;
607
-		}
608
-		return false;
609
-	}
610
-
611
-	/**
612
-	 * creates a unique name for internal ownCloud use for groups. Don't call it directly.
613
-	 * @param string $name the display name of the object
614
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful.
615
-	 *
616
-	 * Instead of using this method directly, call
617
-	 * createAltInternalOwnCloudName($name, false)
618
-	 *
619
-	 * Group names are also used as display names, so we do a sequential
620
-	 * numbering, e.g. Developers_42 when there are 41 other groups called
621
-	 * "Developers"
622
-	 */
623
-	private function _createAltInternalOwnCloudNameForGroups($name) {
624
-		$usedNames = $this->groupMapper->getNamesBySearch($name.'_%');
625
-		if(!($usedNames) || count($usedNames) === 0) {
626
-			$lastNo = 1; //will become name_2
627
-		} else {
628
-			natsort($usedNames);
629
-			$lastName = array_pop($usedNames);
630
-			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
631
-		}
632
-		$altName = $name.'_'.strval($lastNo+1);
633
-		unset($usedNames);
634
-
635
-		$attempts = 1;
636
-		while($attempts < 21){
637
-			// Check to be really sure it is unique
638
-			// while loop is just a precaution. If a name is not generated within
639
-			// 20 attempts, something else is very wrong. Avoids infinite loop.
640
-			if(!\OC_Group::groupExists($altName)) {
641
-				return $altName;
642
-			}
643
-			$altName = $name . '_' . ($lastNo + $attempts);
644
-			$attempts++;
645
-		}
646
-		return false;
647
-	}
648
-
649
-	/**
650
-	 * creates a unique name for internal ownCloud use.
651
-	 * @param string $name the display name of the object
652
-	 * @param boolean $isUser whether name should be created for a user (true) or a group (false)
653
-	 * @return string|false with with the name to use in ownCloud or false if unsuccessful
654
-	 */
655
-	private function createAltInternalOwnCloudName($name, $isUser) {
656
-		$originalTTL = $this->connection->ldapCacheTTL;
657
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
658
-		if($isUser) {
659
-			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
660
-		} else {
661
-			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
662
-		}
663
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
664
-
665
-		return $altName;
666
-	}
667
-
668
-	/**
669
-	 * fetches a list of users according to a provided loginName and utilizing
670
-	 * the login filter.
671
-	 *
672
-	 * @param string $loginName
673
-	 * @param array $attributes optional, list of attributes to read
674
-	 * @return array
675
-	 */
676
-	public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
677
-		$loginName = $this->escapeFilterPart($loginName);
678
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
679
-		$users = $this->fetchListOfUsers($filter, $attributes);
680
-		return $users;
681
-	}
682
-
683
-	/**
684
-	 * counts the number of users according to a provided loginName and
685
-	 * utilizing the login filter.
686
-	 *
687
-	 * @param string $loginName
688
-	 * @return array
689
-	 */
690
-	public function countUsersByLoginName($loginName) {
691
-		$loginName = $this->escapeFilterPart($loginName);
692
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
693
-		$users = $this->countUsers($filter);
694
-		return $users;
695
-	}
696
-
697
-	/**
698
-	 * @param string $filter
699
-	 * @param string|string[] $attr
700
-	 * @param int $limit
701
-	 * @param int $offset
702
-	 * @return array
703
-	 */
704
-	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
705
-		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
706
-		$this->batchApplyUserAttributes($ldapRecords);
707
-		return $this->fetchList($ldapRecords, (count($attr) > 1));
708
-	}
709
-
710
-	/**
711
-	 * provided with an array of LDAP user records the method will fetch the
712
-	 * user object and requests it to process the freshly fetched attributes and
713
-	 * and their values
714
-	 * @param array $ldapRecords
715
-	 */
716
-	public function batchApplyUserAttributes(array $ldapRecords){
717
-		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
718
-		foreach($ldapRecords as $userRecord) {
719
-			if(!isset($userRecord[$displayNameAttribute])) {
720
-				// displayName is obligatory
721
-				continue;
722
-			}
723
-			$ocName  = $this->dn2ocname($userRecord['dn'][0]);
724
-			if($ocName === false) {
725
-				continue;
726
-			}
727
-			$this->cacheUserExists($ocName);
728
-			$user = $this->userManager->get($ocName);
729
-			if($user instanceof OfflineUser) {
730
-				$user->unmark();
731
-				$user = $this->userManager->get($ocName);
732
-			}
733
-			if ($user !== null) {
734
-				$user->processAttributes($userRecord);
735
-			} else {
736
-				\OC::$server->getLogger()->debug(
737
-					"The ldap user manager returned null for $ocName",
738
-					['app'=>'user_ldap']
739
-				);
740
-			}
741
-		}
742
-	}
743
-
744
-	/**
745
-	 * @param string $filter
746
-	 * @param string|string[] $attr
747
-	 * @param int $limit
748
-	 * @param int $offset
749
-	 * @return array
750
-	 */
751
-	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
752
-		return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
753
-	}
754
-
755
-	/**
756
-	 * @param array $list
757
-	 * @param bool $manyAttributes
758
-	 * @return array
759
-	 */
760
-	private function fetchList($list, $manyAttributes) {
761
-		if(is_array($list)) {
762
-			if($manyAttributes) {
763
-				return $list;
764
-			} else {
765
-				$list = array_reduce($list, function($carry, $item) {
766
-					$attribute = array_keys($item)[0];
767
-					$carry[] = $item[$attribute][0];
768
-					return $carry;
769
-				}, array());
770
-				return array_unique($list, SORT_LOCALE_STRING);
771
-			}
772
-		}
773
-
774
-		//error cause actually, maybe throw an exception in future.
775
-		return array();
776
-	}
777
-
778
-	/**
779
-	 * executes an LDAP search, optimized for Users
780
-	 * @param string $filter the LDAP filter for the search
781
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
782
-	 * @param integer $limit
783
-	 * @param integer $offset
784
-	 * @return array with the search result
785
-	 *
786
-	 * Executes an LDAP search
787
-	 */
788
-	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
789
-		return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
790
-	}
791
-
792
-	/**
793
-	 * @param string $filter
794
-	 * @param string|string[] $attr
795
-	 * @param int $limit
796
-	 * @param int $offset
797
-	 * @return false|int
798
-	 */
799
-	public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
800
-		return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
801
-	}
802
-
803
-	/**
804
-	 * executes an LDAP search, optimized for Groups
805
-	 * @param string $filter the LDAP filter for the search
806
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
807
-	 * @param integer $limit
808
-	 * @param integer $offset
809
-	 * @return array with the search result
810
-	 *
811
-	 * Executes an LDAP search
812
-	 */
813
-	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
814
-		return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
815
-	}
816
-
817
-	/**
818
-	 * returns the number of available groups
819
-	 * @param string $filter the LDAP search filter
820
-	 * @param string[] $attr optional
821
-	 * @param int|null $limit
822
-	 * @param int|null $offset
823
-	 * @return int|bool
824
-	 */
825
-	public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
826
-		return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
827
-	}
828
-
829
-	/**
830
-	 * returns the number of available objects on the base DN
831
-	 *
832
-	 * @param int|null $limit
833
-	 * @param int|null $offset
834
-	 * @return int|bool
835
-	 */
836
-	public function countObjects($limit = null, $offset = null) {
837
-		return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
838
-	}
839
-
840
-	/**
841
-	 * retrieved. Results will according to the order in the array.
842
-	 * @param int $limit optional, maximum results to be counted
843
-	 * @param int $offset optional, a starting point
844
-	 * @return array|false array with the search result as first value and pagedSearchOK as
845
-	 * second | false if not successful
846
-	 */
847
-	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
848
-		if(!is_null($attr) && !is_array($attr)) {
849
-			$attr = array(mb_strtolower($attr, 'UTF-8'));
850
-		}
851
-
852
-		// See if we have a resource, in case not cancel with message
853
-		$cr = $this->connection->getConnectionResource();
854
-		if(!$this->ldap->isResource($cr)) {
855
-			// Seems like we didn't find any resource.
856
-			// Return an empty array just like before.
857
-			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
858
-			return false;
859
-		}
860
-
861
-		//check whether paged search should be attempted
862
-		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
863
-
864
-		$linkResources = array_pad(array(), count($base), $cr);
865
-		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
866
-		$error = $this->ldap->errno($cr);
867
-		if(!is_array($sr) || $error !== 0) {
868
-			\OCP\Util::writeLog('user_ldap',
869
-				'Error when searching: '.$this->ldap->error($cr).
870
-					' code '.$this->ldap->errno($cr),
871
-				\OCP\Util::ERROR);
872
-			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
873
-			return false;
874
-		}
875
-
876
-		return array($sr, $pagedSearchOK);
877
-	}
878
-
879
-	/**
880
-	 * processes an LDAP paged search operation
881
-	 * @param array $sr the array containing the LDAP search resources
882
-	 * @param string $filter the LDAP filter for the search
883
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
884
-	 * @param int $iFoundItems number of results in the search operation
885
-	 * @param int $limit maximum results to be counted
886
-	 * @param int $offset a starting point
887
-	 * @param bool $pagedSearchOK whether a paged search has been executed
888
-	 * @param bool $skipHandling required for paged search when cookies to
889
-	 * prior results need to be gained
890
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
891
-	 */
892
-	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
893
-		$cookie = null;
894
-		if($pagedSearchOK) {
895
-			$cr = $this->connection->getConnectionResource();
896
-			foreach($sr as $key => $res) {
897
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
898
-					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
899
-				}
900
-			}
901
-
902
-			//browsing through prior pages to get the cookie for the new one
903
-			if($skipHandling) {
904
-				return;
905
-			}
906
-			// if count is bigger, then the server does not support
907
-			// paged search. Instead, he did a normal search. We set a
908
-			// flag here, so the callee knows how to deal with it.
909
-			if($iFoundItems <= $limit) {
910
-				$this->pagedSearchedSuccessful = true;
911
-			}
912
-		} else {
913
-			if(!is_null($limit)) {
914
-				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
915
-			}
916
-		}
917
-		/* ++ Fixing RHDS searches with pages with zero results ++
339
+     * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
340
+     * @param string $fdn the dn of the group object
341
+     * @param string $ldapName optional, the display name of the object
342
+     * @return string|false with the name to use in ownCloud, false on DN outside of search DN
343
+     */
344
+    public function dn2groupname($fdn, $ldapName = null) {
345
+        //To avoid bypassing the base DN settings under certain circumstances
346
+        //with the group support, check whether the provided DN matches one of
347
+        //the given Bases
348
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
349
+            return false;
350
+        }
351
+
352
+        return $this->dn2ocname($fdn, $ldapName, false);
353
+    }
354
+
355
+    /**
356
+     * accepts an array of group DNs and tests whether they match the user
357
+     * filter by doing read operations against the group entries. Returns an
358
+     * array of DNs that match the filter.
359
+     *
360
+     * @param string[] $groupDNs
361
+     * @return string[]
362
+     */
363
+    public function groupsMatchFilter($groupDNs) {
364
+        $validGroupDNs = [];
365
+        foreach($groupDNs as $dn) {
366
+            $cacheKey = 'groupsMatchFilter-'.$dn;
367
+            $groupMatchFilter = $this->connection->getFromCache($cacheKey);
368
+            if(!is_null($groupMatchFilter)) {
369
+                if($groupMatchFilter) {
370
+                    $validGroupDNs[] = $dn;
371
+                }
372
+                continue;
373
+            }
374
+
375
+            // Check the base DN first. If this is not met already, we don't
376
+            // need to ask the server at all.
377
+            if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
378
+                $this->connection->writeToCache($cacheKey, false);
379
+                continue;
380
+            }
381
+
382
+            $result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
383
+            if(is_array($result)) {
384
+                $this->connection->writeToCache($cacheKey, true);
385
+                $validGroupDNs[] = $dn;
386
+            } else {
387
+                $this->connection->writeToCache($cacheKey, false);
388
+            }
389
+
390
+        }
391
+        return $validGroupDNs;
392
+    }
393
+
394
+    /**
395
+     * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
396
+     * @param string $dn the dn of the user object
397
+     * @param string $ldapName optional, the display name of the object
398
+     * @return string|false with with the name to use in ownCloud
399
+     */
400
+    public function dn2username($fdn, $ldapName = null) {
401
+        //To avoid bypassing the base DN settings under certain circumstances
402
+        //with the group support, check whether the provided DN matches one of
403
+        //the given Bases
404
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
405
+            return false;
406
+        }
407
+
408
+        return $this->dn2ocname($fdn, $ldapName, true);
409
+    }
410
+
411
+    /**
412
+     * returns an internal ownCloud name for the given LDAP DN, false on DN outside of search DN
413
+     * @param string $dn the dn of the user object
414
+     * @param string $ldapName optional, the display name of the object
415
+     * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
416
+     * @return string|false with with the name to use in ownCloud
417
+     */
418
+    public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
419
+        if($isUser) {
420
+            $mapper = $this->getUserMapper();
421
+            $nameAttribute = $this->connection->ldapUserDisplayName;
422
+        } else {
423
+            $mapper = $this->getGroupMapper();
424
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
425
+        }
426
+
427
+        //let's try to retrieve the ownCloud name from the mappings table
428
+        $ocName = $mapper->getNameByDN($fdn);
429
+        if(is_string($ocName)) {
430
+            return $ocName;
431
+        }
432
+
433
+        //second try: get the UUID and check if it is known. Then, update the DN and return the name.
434
+        $uuid = $this->getUUID($fdn, $isUser);
435
+        if(is_string($uuid)) {
436
+            $ocName = $mapper->getNameByUUID($uuid);
437
+            if(is_string($ocName)) {
438
+                $mapper->setDNbyUUID($fdn, $uuid);
439
+                return $ocName;
440
+            }
441
+        } else {
442
+            //If the UUID can't be detected something is foul.
443
+            \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
444
+            return false;
445
+        }
446
+
447
+        if(is_null($ldapName)) {
448
+            $ldapName = $this->readAttribute($fdn, $nameAttribute);
449
+            if(!isset($ldapName[0]) && empty($ldapName[0])) {
450
+                \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
451
+                return false;
452
+            }
453
+            $ldapName = $ldapName[0];
454
+        }
455
+
456
+        if($isUser) {
457
+            $usernameAttribute = $this->connection->ldapExpertUsernameAttr;
458
+            if(!empty($usernameAttribute)) {
459
+                $username = $this->readAttribute($fdn, $usernameAttribute);
460
+                $username = $username[0];
461
+            } else {
462
+                $username = $uuid;
463
+            }
464
+            $intName = $this->sanitizeUsername($username);
465
+        } else {
466
+            $intName = $ldapName;
467
+        }
468
+
469
+        //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
470
+        //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
471
+        //NOTE: mind, disabling cache affects only this instance! Using it
472
+        // outside of core user management will still cache the user as non-existing.
473
+        $originalTTL = $this->connection->ldapCacheTTL;
474
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
475
+        if(($isUser && !\OCP\User::userExists($intName))
476
+            || (!$isUser && !\OC_Group::groupExists($intName))) {
477
+            if($mapper->map($fdn, $intName, $uuid)) {
478
+                $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
479
+                return $intName;
480
+            }
481
+        }
482
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
483
+
484
+        $altName = $this->createAltInternalOwnCloudName($intName, $isUser);
485
+        if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
486
+            return $altName;
487
+        }
488
+
489
+        //if everything else did not help..
490
+        \OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
491
+        return false;
492
+    }
493
+
494
+    /**
495
+     * gives back the user names as they are used ownClod internally
496
+     * @param array $ldapUsers as returned by fetchList()
497
+     * @return array an array with the user names to use in ownCloud
498
+     *
499
+     * gives back the user names as they are used ownClod internally
500
+     */
501
+    public function ownCloudUserNames($ldapUsers) {
502
+        return $this->ldap2ownCloudNames($ldapUsers, true);
503
+    }
504
+
505
+    /**
506
+     * gives back the group names as they are used ownClod internally
507
+     * @param array $ldapGroups as returned by fetchList()
508
+     * @return array an array with the group names to use in ownCloud
509
+     *
510
+     * gives back the group names as they are used ownClod internally
511
+     */
512
+    public function ownCloudGroupNames($ldapGroups) {
513
+        return $this->ldap2ownCloudNames($ldapGroups, false);
514
+    }
515
+
516
+    /**
517
+     * @param array $ldapObjects as returned by fetchList()
518
+     * @param bool $isUsers
519
+     * @return array
520
+     */
521
+    private function ldap2ownCloudNames($ldapObjects, $isUsers) {
522
+        if($isUsers) {
523
+            $nameAttribute = $this->connection->ldapUserDisplayName;
524
+            $sndAttribute  = $this->connection->ldapUserDisplayName2;
525
+        } else {
526
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
527
+        }
528
+        $ownCloudNames = array();
529
+
530
+        foreach($ldapObjects as $ldapObject) {
531
+            $nameByLDAP = null;
532
+            if(    isset($ldapObject[$nameAttribute])
533
+                && is_array($ldapObject[$nameAttribute])
534
+                && isset($ldapObject[$nameAttribute][0])
535
+            ) {
536
+                // might be set, but not necessarily. if so, we use it.
537
+                $nameByLDAP = $ldapObject[$nameAttribute][0];
538
+            }
539
+
540
+            $ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
541
+            if($ocName) {
542
+                $ownCloudNames[] = $ocName;
543
+                if($isUsers) {
544
+                    //cache the user names so it does not need to be retrieved
545
+                    //again later (e.g. sharing dialogue).
546
+                    if(is_null($nameByLDAP)) {
547
+                        continue;
548
+                    }
549
+                    $sndName = isset($ldapObject[$sndAttribute][0])
550
+                        ? $ldapObject[$sndAttribute][0] : '';
551
+                    $this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
552
+                }
553
+            }
554
+        }
555
+        return $ownCloudNames;
556
+    }
557
+
558
+    /**
559
+     * caches the user display name
560
+     * @param string $ocName the internal ownCloud username
561
+     * @param string|false $home the home directory path
562
+     */
563
+    public function cacheUserHome($ocName, $home) {
564
+        $cacheKey = 'getHome'.$ocName;
565
+        $this->connection->writeToCache($cacheKey, $home);
566
+    }
567
+
568
+    /**
569
+     * caches a user as existing
570
+     * @param string $ocName the internal ownCloud username
571
+     */
572
+    public function cacheUserExists($ocName) {
573
+        $this->connection->writeToCache('userExists'.$ocName, true);
574
+    }
575
+
576
+    /**
577
+     * caches the user display name
578
+     * @param string $ocName the internal ownCloud username
579
+     * @param string $displayName the display name
580
+     * @param string $displayName2 the second display name
581
+     */
582
+    public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
583
+        $user = $this->userManager->get($ocName);
584
+        $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
585
+        $cacheKeyTrunk = 'getDisplayName';
586
+        $this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
587
+    }
588
+
589
+    /**
590
+     * creates a unique name for internal ownCloud use for users. Don't call it directly.
591
+     * @param string $name the display name of the object
592
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful
593
+     *
594
+     * Instead of using this method directly, call
595
+     * createAltInternalOwnCloudName($name, true)
596
+     */
597
+    private function _createAltInternalOwnCloudNameForUsers($name) {
598
+        $attempts = 0;
599
+        //while loop is just a precaution. If a name is not generated within
600
+        //20 attempts, something else is very wrong. Avoids infinite loop.
601
+        while($attempts < 20){
602
+            $altName = $name . '_' . rand(1000,9999);
603
+            if(!\OCP\User::userExists($altName)) {
604
+                return $altName;
605
+            }
606
+            $attempts++;
607
+        }
608
+        return false;
609
+    }
610
+
611
+    /**
612
+     * creates a unique name for internal ownCloud use for groups. Don't call it directly.
613
+     * @param string $name the display name of the object
614
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful.
615
+     *
616
+     * Instead of using this method directly, call
617
+     * createAltInternalOwnCloudName($name, false)
618
+     *
619
+     * Group names are also used as display names, so we do a sequential
620
+     * numbering, e.g. Developers_42 when there are 41 other groups called
621
+     * "Developers"
622
+     */
623
+    private function _createAltInternalOwnCloudNameForGroups($name) {
624
+        $usedNames = $this->groupMapper->getNamesBySearch($name.'_%');
625
+        if(!($usedNames) || count($usedNames) === 0) {
626
+            $lastNo = 1; //will become name_2
627
+        } else {
628
+            natsort($usedNames);
629
+            $lastName = array_pop($usedNames);
630
+            $lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
631
+        }
632
+        $altName = $name.'_'.strval($lastNo+1);
633
+        unset($usedNames);
634
+
635
+        $attempts = 1;
636
+        while($attempts < 21){
637
+            // Check to be really sure it is unique
638
+            // while loop is just a precaution. If a name is not generated within
639
+            // 20 attempts, something else is very wrong. Avoids infinite loop.
640
+            if(!\OC_Group::groupExists($altName)) {
641
+                return $altName;
642
+            }
643
+            $altName = $name . '_' . ($lastNo + $attempts);
644
+            $attempts++;
645
+        }
646
+        return false;
647
+    }
648
+
649
+    /**
650
+     * creates a unique name for internal ownCloud use.
651
+     * @param string $name the display name of the object
652
+     * @param boolean $isUser whether name should be created for a user (true) or a group (false)
653
+     * @return string|false with with the name to use in ownCloud or false if unsuccessful
654
+     */
655
+    private function createAltInternalOwnCloudName($name, $isUser) {
656
+        $originalTTL = $this->connection->ldapCacheTTL;
657
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
658
+        if($isUser) {
659
+            $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
660
+        } else {
661
+            $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
662
+        }
663
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
664
+
665
+        return $altName;
666
+    }
667
+
668
+    /**
669
+     * fetches a list of users according to a provided loginName and utilizing
670
+     * the login filter.
671
+     *
672
+     * @param string $loginName
673
+     * @param array $attributes optional, list of attributes to read
674
+     * @return array
675
+     */
676
+    public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
677
+        $loginName = $this->escapeFilterPart($loginName);
678
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
679
+        $users = $this->fetchListOfUsers($filter, $attributes);
680
+        return $users;
681
+    }
682
+
683
+    /**
684
+     * counts the number of users according to a provided loginName and
685
+     * utilizing the login filter.
686
+     *
687
+     * @param string $loginName
688
+     * @return array
689
+     */
690
+    public function countUsersByLoginName($loginName) {
691
+        $loginName = $this->escapeFilterPart($loginName);
692
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
693
+        $users = $this->countUsers($filter);
694
+        return $users;
695
+    }
696
+
697
+    /**
698
+     * @param string $filter
699
+     * @param string|string[] $attr
700
+     * @param int $limit
701
+     * @param int $offset
702
+     * @return array
703
+     */
704
+    public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null) {
705
+        $ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
706
+        $this->batchApplyUserAttributes($ldapRecords);
707
+        return $this->fetchList($ldapRecords, (count($attr) > 1));
708
+    }
709
+
710
+    /**
711
+     * provided with an array of LDAP user records the method will fetch the
712
+     * user object and requests it to process the freshly fetched attributes and
713
+     * and their values
714
+     * @param array $ldapRecords
715
+     */
716
+    public function batchApplyUserAttributes(array $ldapRecords){
717
+        $displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
718
+        foreach($ldapRecords as $userRecord) {
719
+            if(!isset($userRecord[$displayNameAttribute])) {
720
+                // displayName is obligatory
721
+                continue;
722
+            }
723
+            $ocName  = $this->dn2ocname($userRecord['dn'][0]);
724
+            if($ocName === false) {
725
+                continue;
726
+            }
727
+            $this->cacheUserExists($ocName);
728
+            $user = $this->userManager->get($ocName);
729
+            if($user instanceof OfflineUser) {
730
+                $user->unmark();
731
+                $user = $this->userManager->get($ocName);
732
+            }
733
+            if ($user !== null) {
734
+                $user->processAttributes($userRecord);
735
+            } else {
736
+                \OC::$server->getLogger()->debug(
737
+                    "The ldap user manager returned null for $ocName",
738
+                    ['app'=>'user_ldap']
739
+                );
740
+            }
741
+        }
742
+    }
743
+
744
+    /**
745
+     * @param string $filter
746
+     * @param string|string[] $attr
747
+     * @param int $limit
748
+     * @param int $offset
749
+     * @return array
750
+     */
751
+    public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
752
+        return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
753
+    }
754
+
755
+    /**
756
+     * @param array $list
757
+     * @param bool $manyAttributes
758
+     * @return array
759
+     */
760
+    private function fetchList($list, $manyAttributes) {
761
+        if(is_array($list)) {
762
+            if($manyAttributes) {
763
+                return $list;
764
+            } else {
765
+                $list = array_reduce($list, function($carry, $item) {
766
+                    $attribute = array_keys($item)[0];
767
+                    $carry[] = $item[$attribute][0];
768
+                    return $carry;
769
+                }, array());
770
+                return array_unique($list, SORT_LOCALE_STRING);
771
+            }
772
+        }
773
+
774
+        //error cause actually, maybe throw an exception in future.
775
+        return array();
776
+    }
777
+
778
+    /**
779
+     * executes an LDAP search, optimized for Users
780
+     * @param string $filter the LDAP filter for the search
781
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
782
+     * @param integer $limit
783
+     * @param integer $offset
784
+     * @return array with the search result
785
+     *
786
+     * Executes an LDAP search
787
+     */
788
+    public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
789
+        return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
790
+    }
791
+
792
+    /**
793
+     * @param string $filter
794
+     * @param string|string[] $attr
795
+     * @param int $limit
796
+     * @param int $offset
797
+     * @return false|int
798
+     */
799
+    public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
800
+        return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
801
+    }
802
+
803
+    /**
804
+     * executes an LDAP search, optimized for Groups
805
+     * @param string $filter the LDAP filter for the search
806
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
807
+     * @param integer $limit
808
+     * @param integer $offset
809
+     * @return array with the search result
810
+     *
811
+     * Executes an LDAP search
812
+     */
813
+    public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
814
+        return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
815
+    }
816
+
817
+    /**
818
+     * returns the number of available groups
819
+     * @param string $filter the LDAP search filter
820
+     * @param string[] $attr optional
821
+     * @param int|null $limit
822
+     * @param int|null $offset
823
+     * @return int|bool
824
+     */
825
+    public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
826
+        return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
827
+    }
828
+
829
+    /**
830
+     * returns the number of available objects on the base DN
831
+     *
832
+     * @param int|null $limit
833
+     * @param int|null $offset
834
+     * @return int|bool
835
+     */
836
+    public function countObjects($limit = null, $offset = null) {
837
+        return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
838
+    }
839
+
840
+    /**
841
+     * retrieved. Results will according to the order in the array.
842
+     * @param int $limit optional, maximum results to be counted
843
+     * @param int $offset optional, a starting point
844
+     * @return array|false array with the search result as first value and pagedSearchOK as
845
+     * second | false if not successful
846
+     */
847
+    private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
848
+        if(!is_null($attr) && !is_array($attr)) {
849
+            $attr = array(mb_strtolower($attr, 'UTF-8'));
850
+        }
851
+
852
+        // See if we have a resource, in case not cancel with message
853
+        $cr = $this->connection->getConnectionResource();
854
+        if(!$this->ldap->isResource($cr)) {
855
+            // Seems like we didn't find any resource.
856
+            // Return an empty array just like before.
857
+            \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
858
+            return false;
859
+        }
860
+
861
+        //check whether paged search should be attempted
862
+        $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
863
+
864
+        $linkResources = array_pad(array(), count($base), $cr);
865
+        $sr = $this->ldap->search($linkResources, $base, $filter, $attr);
866
+        $error = $this->ldap->errno($cr);
867
+        if(!is_array($sr) || $error !== 0) {
868
+            \OCP\Util::writeLog('user_ldap',
869
+                'Error when searching: '.$this->ldap->error($cr).
870
+                    ' code '.$this->ldap->errno($cr),
871
+                \OCP\Util::ERROR);
872
+            \OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
873
+            return false;
874
+        }
875
+
876
+        return array($sr, $pagedSearchOK);
877
+    }
878
+
879
+    /**
880
+     * processes an LDAP paged search operation
881
+     * @param array $sr the array containing the LDAP search resources
882
+     * @param string $filter the LDAP filter for the search
883
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
884
+     * @param int $iFoundItems number of results in the search operation
885
+     * @param int $limit maximum results to be counted
886
+     * @param int $offset a starting point
887
+     * @param bool $pagedSearchOK whether a paged search has been executed
888
+     * @param bool $skipHandling required for paged search when cookies to
889
+     * prior results need to be gained
890
+     * @return bool cookie validity, true if we have more pages, false otherwise.
891
+     */
892
+    private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
893
+        $cookie = null;
894
+        if($pagedSearchOK) {
895
+            $cr = $this->connection->getConnectionResource();
896
+            foreach($sr as $key => $res) {
897
+                if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
898
+                    $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
899
+                }
900
+            }
901
+
902
+            //browsing through prior pages to get the cookie for the new one
903
+            if($skipHandling) {
904
+                return;
905
+            }
906
+            // if count is bigger, then the server does not support
907
+            // paged search. Instead, he did a normal search. We set a
908
+            // flag here, so the callee knows how to deal with it.
909
+            if($iFoundItems <= $limit) {
910
+                $this->pagedSearchedSuccessful = true;
911
+            }
912
+        } else {
913
+            if(!is_null($limit)) {
914
+                \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
915
+            }
916
+        }
917
+        /* ++ Fixing RHDS searches with pages with zero results ++
918 918
 		 * Return cookie status. If we don't have more pages, with RHDS
919 919
 		 * cookie is null, with openldap cookie is an empty string and
920 920
 		 * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0
921 921
 		 */
922
-		return !empty($cookie) || $cookie === '0';
923
-	}
924
-
925
-	/**
926
-	 * executes an LDAP search, but counts the results only
927
-	 * @param string $filter the LDAP filter for the search
928
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
929
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
930
-	 * retrieved. Results will according to the order in the array.
931
-	 * @param int $limit optional, maximum results to be counted
932
-	 * @param int $offset optional, a starting point
933
-	 * @param bool $skipHandling indicates whether the pages search operation is
934
-	 * completed
935
-	 * @return int|false Integer or false if the search could not be initialized
936
-	 *
937
-	 */
938
-	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
939
-		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
940
-
941
-		$limitPerPage = intval($this->connection->ldapPagingSize);
942
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
943
-			$limitPerPage = $limit;
944
-		}
945
-
946
-		$counter = 0;
947
-		$count = null;
948
-		$this->connection->getConnectionResource();
949
-
950
-		do {
951
-			$search = $this->executeSearch($filter, $base, $attr,
952
-										   $limitPerPage, $offset);
953
-			if($search === false) {
954
-				return $counter > 0 ? $counter : false;
955
-			}
956
-			list($sr, $pagedSearchOK) = $search;
957
-
958
-			/* ++ Fixing RHDS searches with pages with zero results ++
922
+        return !empty($cookie) || $cookie === '0';
923
+    }
924
+
925
+    /**
926
+     * executes an LDAP search, but counts the results only
927
+     * @param string $filter the LDAP filter for the search
928
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
929
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
930
+     * retrieved. Results will according to the order in the array.
931
+     * @param int $limit optional, maximum results to be counted
932
+     * @param int $offset optional, a starting point
933
+     * @param bool $skipHandling indicates whether the pages search operation is
934
+     * completed
935
+     * @return int|false Integer or false if the search could not be initialized
936
+     *
937
+     */
938
+    private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
939
+        \OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
940
+
941
+        $limitPerPage = intval($this->connection->ldapPagingSize);
942
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
943
+            $limitPerPage = $limit;
944
+        }
945
+
946
+        $counter = 0;
947
+        $count = null;
948
+        $this->connection->getConnectionResource();
949
+
950
+        do {
951
+            $search = $this->executeSearch($filter, $base, $attr,
952
+                                            $limitPerPage, $offset);
953
+            if($search === false) {
954
+                return $counter > 0 ? $counter : false;
955
+            }
956
+            list($sr, $pagedSearchOK) = $search;
957
+
958
+            /* ++ Fixing RHDS searches with pages with zero results ++
959 959
 			 * countEntriesInSearchResults() method signature changed
960 960
 			 * by removing $limit and &$hasHitLimit parameters
961 961
 			 */
962
-			$count = $this->countEntriesInSearchResults($sr);
963
-			$counter += $count;
962
+            $count = $this->countEntriesInSearchResults($sr);
963
+            $counter += $count;
964 964
 
965
-			$hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
966
-										$offset, $pagedSearchOK, $skipHandling);
967
-			$offset += $limitPerPage;
968
-			/* ++ Fixing RHDS searches with pages with zero results ++
965
+            $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
966
+                                        $offset, $pagedSearchOK, $skipHandling);
967
+            $offset += $limitPerPage;
968
+            /* ++ Fixing RHDS searches with pages with zero results ++
969 969
 			 * Continue now depends on $hasMorePages value
970 970
 			 */
971
-			$continue = $pagedSearchOK && $hasMorePages;
972
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
973
-
974
-		return $counter;
975
-	}
976
-
977
-	/**
978
-	 * @param array $searchResults
979
-	 * @return int
980
-	 */
981
-	private function countEntriesInSearchResults($searchResults) {
982
-		$cr = $this->connection->getConnectionResource();
983
-		$counter = 0;
984
-
985
-		foreach($searchResults as $res) {
986
-			$count = intval($this->ldap->countEntries($cr, $res));
987
-			$counter += $count;
988
-		}
989
-
990
-		return $counter;
991
-	}
992
-
993
-	/**
994
-	 * Executes an LDAP search
995
-	 * @param string $filter the LDAP filter for the search
996
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
997
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
998
-	 * @param int $limit
999
-	 * @param int $offset
1000
-	 * @param bool $skipHandling
1001
-	 * @return array with the search result
1002
-	 */
1003
-	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1004
-		if($limit <= 0) {
1005
-			//otherwise search will fail
1006
-			$limit = null;
1007
-		}
1008
-
1009
-		/* ++ Fixing RHDS searches with pages with zero results ++
971
+            $continue = $pagedSearchOK && $hasMorePages;
972
+        } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
973
+
974
+        return $counter;
975
+    }
976
+
977
+    /**
978
+     * @param array $searchResults
979
+     * @return int
980
+     */
981
+    private function countEntriesInSearchResults($searchResults) {
982
+        $cr = $this->connection->getConnectionResource();
983
+        $counter = 0;
984
+
985
+        foreach($searchResults as $res) {
986
+            $count = intval($this->ldap->countEntries($cr, $res));
987
+            $counter += $count;
988
+        }
989
+
990
+        return $counter;
991
+    }
992
+
993
+    /**
994
+     * Executes an LDAP search
995
+     * @param string $filter the LDAP filter for the search
996
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
997
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
998
+     * @param int $limit
999
+     * @param int $offset
1000
+     * @param bool $skipHandling
1001
+     * @return array with the search result
1002
+     */
1003
+    private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1004
+        if($limit <= 0) {
1005
+            //otherwise search will fail
1006
+            $limit = null;
1007
+        }
1008
+
1009
+        /* ++ Fixing RHDS searches with pages with zero results ++
1010 1010
 		 * As we can have pages with zero results and/or pages with less
1011 1011
 		 * than $limit results but with a still valid server 'cookie',
1012 1012
 		 * loops through until we get $continue equals true and
1013 1013
 		 * $findings['count'] < $limit
1014 1014
 		 */
1015
-		$findings = array();
1016
-		$savedoffset = $offset;
1017
-		do {
1018
-			$continue = false;
1019
-			$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
1020
-			if($search === false) {
1021
-				return array();
1022
-			}
1023
-			list($sr, $pagedSearchOK) = $search;
1024
-			$cr = $this->connection->getConnectionResource();
1025
-
1026
-			if($skipHandling) {
1027
-				//i.e. result do not need to be fetched, we just need the cookie
1028
-				//thus pass 1 or any other value as $iFoundItems because it is not
1029
-				//used
1030
-				$this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
1031
-								$offset, $pagedSearchOK,
1032
-								$skipHandling);
1033
-				return array();
1034
-			}
1035
-
1036
-			foreach($sr as $res) {
1037
-				$findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1038
-			}
1039
-
1040
-			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
1041
-								$limit, $offset, $pagedSearchOK,
1042
-										$skipHandling);
1043
-			$offset += $limit;
1044
-		} while ($continue && $pagedSearchOK && $findings['count'] < $limit);
1045
-		// reseting offset
1046
-		$offset = $savedoffset;
1047
-
1048
-		// if we're here, probably no connection resource is returned.
1049
-		// to make ownCloud behave nicely, we simply give back an empty array.
1050
-		if(is_null($findings)) {
1051
-			return array();
1052
-		}
1053
-
1054
-		if(!is_null($attr)) {
1055
-			$selection = array();
1056
-			$i = 0;
1057
-			foreach($findings as $item) {
1058
-				if(!is_array($item)) {
1059
-					continue;
1060
-				}
1061
-				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1062
-				foreach($attr as $key) {
1063
-					$key = mb_strtolower($key, 'UTF-8');
1064
-					if(isset($item[$key])) {
1065
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1066
-							unset($item[$key]['count']);
1067
-						}
1068
-						if($key !== 'dn') {
1069
-							$selection[$i][$key] = $this->resemblesDN($key) ?
1070
-								$this->sanitizeDN($item[$key])
1071
-								: $item[$key];
1072
-						} else {
1073
-							$selection[$i][$key] = [$this->sanitizeDN($item[$key])];
1074
-						}
1075
-					}
1076
-
1077
-				}
1078
-				$i++;
1079
-			}
1080
-			$findings = $selection;
1081
-		}
1082
-		//we slice the findings, when
1083
-		//a) paged search unsuccessful, though attempted
1084
-		//b) no paged search, but limit set
1085
-		if((!$this->getPagedSearchResultState()
1086
-			&& $pagedSearchOK)
1087
-			|| (
1088
-				!$pagedSearchOK
1089
-				&& !is_null($limit)
1090
-			)
1091
-		) {
1092
-			$findings = array_slice($findings, intval($offset), $limit);
1093
-		}
1094
-		return $findings;
1095
-	}
1096
-
1097
-	/**
1098
-	 * @param string $name
1099
-	 * @return bool|mixed|string
1100
-	 */
1101
-	public function sanitizeUsername($name) {
1102
-		if($this->connection->ldapIgnoreNamingRules) {
1103
-			return $name;
1104
-		}
1105
-
1106
-		// Transliteration
1107
-		// latin characters to ASCII
1108
-		$name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1109
-
1110
-		// Replacements
1111
-		$name = str_replace(' ', '_', $name);
1112
-
1113
-		// Every remaining disallowed characters will be removed
1114
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1115
-
1116
-		return $name;
1117
-	}
1118
-
1119
-	/**
1120
-	* escapes (user provided) parts for LDAP filter
1121
-	* @param string $input, the provided value
1122
-	* @param bool $allowAsterisk whether in * at the beginning should be preserved
1123
-	* @return string the escaped string
1124
-	*/
1125
-	public function escapeFilterPart($input, $allowAsterisk = false) {
1126
-		$asterisk = '';
1127
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1128
-			$asterisk = '*';
1129
-			$input = mb_substr($input, 1, null, 'UTF-8');
1130
-		}
1131
-		$search  = array('*', '\\', '(', ')');
1132
-		$replace = array('\\*', '\\\\', '\\(', '\\)');
1133
-		return $asterisk . str_replace($search, $replace, $input);
1134
-	}
1135
-
1136
-	/**
1137
-	 * combines the input filters with AND
1138
-	 * @param string[] $filters the filters to connect
1139
-	 * @return string the combined filter
1140
-	 */
1141
-	public function combineFilterWithAnd($filters) {
1142
-		return $this->combineFilter($filters, '&');
1143
-	}
1144
-
1145
-	/**
1146
-	 * combines the input filters with OR
1147
-	 * @param string[] $filters the filters to connect
1148
-	 * @return string the combined filter
1149
-	 * Combines Filter arguments with OR
1150
-	 */
1151
-	public function combineFilterWithOr($filters) {
1152
-		return $this->combineFilter($filters, '|');
1153
-	}
1154
-
1155
-	/**
1156
-	 * combines the input filters with given operator
1157
-	 * @param string[] $filters the filters to connect
1158
-	 * @param string $operator either & or |
1159
-	 * @return string the combined filter
1160
-	 */
1161
-	private function combineFilter($filters, $operator) {
1162
-		$combinedFilter = '('.$operator;
1163
-		foreach($filters as $filter) {
1164
-			if(!empty($filter) && $filter[0] !== '(') {
1165
-				$filter = '('.$filter.')';
1166
-			}
1167
-			$combinedFilter.=$filter;
1168
-		}
1169
-		$combinedFilter.=')';
1170
-		return $combinedFilter;
1171
-	}
1172
-
1173
-	/**
1174
-	 * creates a filter part for to perform search for users
1175
-	 * @param string $search the search term
1176
-	 * @return string the final filter part to use in LDAP searches
1177
-	 */
1178
-	public function getFilterPartForUserSearch($search) {
1179
-		return $this->getFilterPartForSearch($search,
1180
-			$this->connection->ldapAttributesForUserSearch,
1181
-			$this->connection->ldapUserDisplayName);
1182
-	}
1183
-
1184
-	/**
1185
-	 * creates a filter part for to perform search for groups
1186
-	 * @param string $search the search term
1187
-	 * @return string the final filter part to use in LDAP searches
1188
-	 */
1189
-	public function getFilterPartForGroupSearch($search) {
1190
-		return $this->getFilterPartForSearch($search,
1191
-			$this->connection->ldapAttributesForGroupSearch,
1192
-			$this->connection->ldapGroupDisplayName);
1193
-	}
1194
-
1195
-	/**
1196
-	 * creates a filter part for searches by splitting up the given search
1197
-	 * string into single words
1198
-	 * @param string $search the search term
1199
-	 * @param string[] $searchAttributes needs to have at least two attributes,
1200
-	 * otherwise it does not make sense :)
1201
-	 * @return string the final filter part to use in LDAP searches
1202
-	 * @throws \Exception
1203
-	 */
1204
-	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1205
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1206
-			throw new \Exception('searchAttributes must be an array with at least two string');
1207
-		}
1208
-		$searchWords = explode(' ', trim($search));
1209
-		$wordFilters = array();
1210
-		foreach($searchWords as $word) {
1211
-			$word = $this->prepareSearchTerm($word);
1212
-			//every word needs to appear at least once
1213
-			$wordMatchOneAttrFilters = array();
1214
-			foreach($searchAttributes as $attr) {
1215
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1216
-			}
1217
-			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1218
-		}
1219
-		return $this->combineFilterWithAnd($wordFilters);
1220
-	}
1221
-
1222
-	/**
1223
-	 * creates a filter part for searches
1224
-	 * @param string $search the search term
1225
-	 * @param string[]|null $searchAttributes
1226
-	 * @param string $fallbackAttribute a fallback attribute in case the user
1227
-	 * did not define search attributes. Typically the display name attribute.
1228
-	 * @return string the final filter part to use in LDAP searches
1229
-	 */
1230
-	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1231
-		$filter = array();
1232
-		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1233
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1234
-			try {
1235
-				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1236
-			} catch(\Exception $e) {
1237
-				\OCP\Util::writeLog(
1238
-					'user_ldap',
1239
-					'Creating advanced filter for search failed, falling back to simple method.',
1240
-					\OCP\Util::INFO
1241
-				);
1242
-			}
1243
-		}
1244
-
1245
-		$search = $this->prepareSearchTerm($search);
1246
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1247
-			if(empty($fallbackAttribute)) {
1248
-				return '';
1249
-			}
1250
-			$filter[] = $fallbackAttribute . '=' . $search;
1251
-		} else {
1252
-			foreach($searchAttributes as $attribute) {
1253
-				$filter[] = $attribute . '=' . $search;
1254
-			}
1255
-		}
1256
-		if(count($filter) === 1) {
1257
-			return '('.$filter[0].')';
1258
-		}
1259
-		return $this->combineFilterWithOr($filter);
1260
-	}
1261
-
1262
-	/**
1263
-	 * returns the search term depending on whether we are allowed
1264
-	 * list users found by ldap with the current input appended by
1265
-	 * a *
1266
-	 * @return string
1267
-	 */
1268
-	private function prepareSearchTerm($term) {
1269
-		$config = \OC::$server->getConfig();
1270
-
1271
-		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1272
-
1273
-		$result = empty($term) ? '*' :
1274
-			$allowEnum !== 'no' ? $term . '*' : $term;
1275
-		return $result;
1276
-	}
1277
-
1278
-	/**
1279
-	 * returns the filter used for counting users
1280
-	 * @return string
1281
-	 */
1282
-	public function getFilterForUserCount() {
1283
-		$filter = $this->combineFilterWithAnd(array(
1284
-			$this->connection->ldapUserFilter,
1285
-			$this->connection->ldapUserDisplayName . '=*'
1286
-		));
1287
-
1288
-		return $filter;
1289
-	}
1290
-
1291
-	/**
1292
-	 * @param string $name
1293
-	 * @param string $password
1294
-	 * @return bool
1295
-	 */
1296
-	public function areCredentialsValid($name, $password) {
1297
-		$name = $this->DNasBaseParameter($name);
1298
-		$testConnection = clone $this->connection;
1299
-		$credentials = array(
1300
-			'ldapAgentName' => $name,
1301
-			'ldapAgentPassword' => $password
1302
-		);
1303
-		if(!$testConnection->setConfiguration($credentials)) {
1304
-			return false;
1305
-		}
1306
-		return $testConnection->bind();
1307
-	}
1308
-
1309
-	/**
1310
-	 * reverse lookup of a DN given a known UUID
1311
-	 *
1312
-	 * @param string $uuid
1313
-	 * @return string
1314
-	 * @throws \Exception
1315
-	 */
1316
-	public function getUserDnByUuid($uuid) {
1317
-		$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1318
-		$filter       = $this->connection->ldapUserFilter;
1319
-		$base         = $this->connection->ldapBaseUsers;
1320
-
1321
-		if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1322
-			// Sacrebleu! The UUID attribute is unknown :( We need first an
1323
-			// existing DN to be able to reliably detect it.
1324
-			$result = $this->search($filter, $base, ['dn'], 1);
1325
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1326
-				throw new \Exception('Cannot determine UUID attribute');
1327
-			}
1328
-			$dn = $result[0]['dn'][0];
1329
-			if(!$this->detectUuidAttribute($dn, true)) {
1330
-				throw new \Exception('Cannot determine UUID attribute');
1331
-			}
1332
-		} else {
1333
-			// The UUID attribute is either known or an override is given.
1334
-			// By calling this method we ensure that $this->connection->$uuidAttr
1335
-			// is definitely set
1336
-			if(!$this->detectUuidAttribute('', true)) {
1337
-				throw new \Exception('Cannot determine UUID attribute');
1338
-			}
1339
-		}
1340
-
1341
-		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1342
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1343
-			$uuid = $this->formatGuid2ForFilterUser($uuid);
1344
-		}
1345
-
1346
-		$filter = $uuidAttr . '=' . $uuid;
1347
-		$result = $this->searchUsers($filter, ['dn'], 2);
1348
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1349
-			// we put the count into account to make sure that this is
1350
-			// really unique
1351
-			return $result[0]['dn'][0];
1352
-		}
1353
-
1354
-		throw new \Exception('Cannot determine UUID attribute');
1355
-	}
1356
-
1357
-	/**
1358
-	 * auto-detects the directory's UUID attribute
1359
-	 * @param string $dn a known DN used to check against
1360
-	 * @param bool $isUser
1361
-	 * @param bool $force the detection should be run, even if it is not set to auto
1362
-	 * @return bool true on success, false otherwise
1363
-	 */
1364
-	private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1365
-		if($isUser) {
1366
-			$uuidAttr     = 'ldapUuidUserAttribute';
1367
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1368
-		} else {
1369
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1370
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1371
-		}
1372
-
1373
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1374
-			return true;
1375
-		}
1376
-
1377
-		if(!empty($uuidOverride) && !$force) {
1378
-			$this->connection->$uuidAttr = $uuidOverride;
1379
-			return true;
1380
-		}
1381
-
1382
-		// for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1383
-		$testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1384
-
1385
-		foreach($testAttributes as $attribute) {
1386
-			$value = $this->readAttribute($dn, $attribute);
1387
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1388
-				\OCP\Util::writeLog('user_ldap',
1389
-									'Setting '.$attribute.' as '.$uuidAttr,
1390
-									\OCP\Util::DEBUG);
1391
-				$this->connection->$uuidAttr = $attribute;
1392
-				return true;
1393
-			}
1394
-		}
1395
-		\OCP\Util::writeLog('user_ldap',
1396
-							'Could not autodetect the UUID attribute',
1397
-							\OCP\Util::ERROR);
1398
-
1399
-		return false;
1400
-	}
1401
-
1402
-	/**
1403
-	 * @param string $dn
1404
-	 * @param bool $isUser
1405
-	 * @return string|bool
1406
-	 */
1407
-	public function getUUID($dn, $isUser = true) {
1408
-		if($isUser) {
1409
-			$uuidAttr     = 'ldapUuidUserAttribute';
1410
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1411
-		} else {
1412
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1413
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1414
-		}
1415
-
1416
-		$uuid = false;
1417
-		if($this->detectUuidAttribute($dn, $isUser)) {
1418
-			$uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1419
-			if( !is_array($uuid)
1420
-				&& !empty($uuidOverride)
1421
-				&& $this->detectUuidAttribute($dn, $isUser, true)) {
1422
-					$uuid = $this->readAttribute($dn,
1423
-												 $this->connection->$uuidAttr);
1424
-			}
1425
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1426
-				$uuid = $uuid[0];
1427
-			}
1428
-		}
1429
-
1430
-		return $uuid;
1431
-	}
1432
-
1433
-	/**
1434
-	 * converts a binary ObjectGUID into a string representation
1435
-	 * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1436
-	 * @return string
1437
-	 * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1438
-	 */
1439
-	private function convertObjectGUID2Str($oguid) {
1440
-		$hex_guid = bin2hex($oguid);
1441
-		$hex_guid_to_guid_str = '';
1442
-		for($k = 1; $k <= 4; ++$k) {
1443
-			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1444
-		}
1445
-		$hex_guid_to_guid_str .= '-';
1446
-		for($k = 1; $k <= 2; ++$k) {
1447
-			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1448
-		}
1449
-		$hex_guid_to_guid_str .= '-';
1450
-		for($k = 1; $k <= 2; ++$k) {
1451
-			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1452
-		}
1453
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1454
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1455
-
1456
-		return strtoupper($hex_guid_to_guid_str);
1457
-	}
1458
-
1459
-	/**
1460
-	 * the first three blocks of the string-converted GUID happen to be in
1461
-	 * reverse order. In order to use it in a filter, this needs to be
1462
-	 * corrected. Furthermore the dashes need to be replaced and \\ preprended
1463
-	 * to every two hax figures.
1464
-	 *
1465
-	 * If an invalid string is passed, it will be returned without change.
1466
-	 *
1467
-	 * @param string $guid
1468
-	 * @return string
1469
-	 */
1470
-	public function formatGuid2ForFilterUser($guid) {
1471
-		if(!is_string($guid)) {
1472
-			throw new \InvalidArgumentException('String expected');
1473
-		}
1474
-		$blocks = explode('-', $guid);
1475
-		if(count($blocks) !== 5) {
1476
-			/*
1015
+        $findings = array();
1016
+        $savedoffset = $offset;
1017
+        do {
1018
+            $continue = false;
1019
+            $search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
1020
+            if($search === false) {
1021
+                return array();
1022
+            }
1023
+            list($sr, $pagedSearchOK) = $search;
1024
+            $cr = $this->connection->getConnectionResource();
1025
+
1026
+            if($skipHandling) {
1027
+                //i.e. result do not need to be fetched, we just need the cookie
1028
+                //thus pass 1 or any other value as $iFoundItems because it is not
1029
+                //used
1030
+                $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
1031
+                                $offset, $pagedSearchOK,
1032
+                                $skipHandling);
1033
+                return array();
1034
+            }
1035
+
1036
+            foreach($sr as $res) {
1037
+                $findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1038
+            }
1039
+
1040
+            $continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
1041
+                                $limit, $offset, $pagedSearchOK,
1042
+                                        $skipHandling);
1043
+            $offset += $limit;
1044
+        } while ($continue && $pagedSearchOK && $findings['count'] < $limit);
1045
+        // reseting offset
1046
+        $offset = $savedoffset;
1047
+
1048
+        // if we're here, probably no connection resource is returned.
1049
+        // to make ownCloud behave nicely, we simply give back an empty array.
1050
+        if(is_null($findings)) {
1051
+            return array();
1052
+        }
1053
+
1054
+        if(!is_null($attr)) {
1055
+            $selection = array();
1056
+            $i = 0;
1057
+            foreach($findings as $item) {
1058
+                if(!is_array($item)) {
1059
+                    continue;
1060
+                }
1061
+                $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1062
+                foreach($attr as $key) {
1063
+                    $key = mb_strtolower($key, 'UTF-8');
1064
+                    if(isset($item[$key])) {
1065
+                        if(is_array($item[$key]) && isset($item[$key]['count'])) {
1066
+                            unset($item[$key]['count']);
1067
+                        }
1068
+                        if($key !== 'dn') {
1069
+                            $selection[$i][$key] = $this->resemblesDN($key) ?
1070
+                                $this->sanitizeDN($item[$key])
1071
+                                : $item[$key];
1072
+                        } else {
1073
+                            $selection[$i][$key] = [$this->sanitizeDN($item[$key])];
1074
+                        }
1075
+                    }
1076
+
1077
+                }
1078
+                $i++;
1079
+            }
1080
+            $findings = $selection;
1081
+        }
1082
+        //we slice the findings, when
1083
+        //a) paged search unsuccessful, though attempted
1084
+        //b) no paged search, but limit set
1085
+        if((!$this->getPagedSearchResultState()
1086
+            && $pagedSearchOK)
1087
+            || (
1088
+                !$pagedSearchOK
1089
+                && !is_null($limit)
1090
+            )
1091
+        ) {
1092
+            $findings = array_slice($findings, intval($offset), $limit);
1093
+        }
1094
+        return $findings;
1095
+    }
1096
+
1097
+    /**
1098
+     * @param string $name
1099
+     * @return bool|mixed|string
1100
+     */
1101
+    public function sanitizeUsername($name) {
1102
+        if($this->connection->ldapIgnoreNamingRules) {
1103
+            return $name;
1104
+        }
1105
+
1106
+        // Transliteration
1107
+        // latin characters to ASCII
1108
+        $name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1109
+
1110
+        // Replacements
1111
+        $name = str_replace(' ', '_', $name);
1112
+
1113
+        // Every remaining disallowed characters will be removed
1114
+        $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1115
+
1116
+        return $name;
1117
+    }
1118
+
1119
+    /**
1120
+     * escapes (user provided) parts for LDAP filter
1121
+     * @param string $input, the provided value
1122
+     * @param bool $allowAsterisk whether in * at the beginning should be preserved
1123
+     * @return string the escaped string
1124
+     */
1125
+    public function escapeFilterPart($input, $allowAsterisk = false) {
1126
+        $asterisk = '';
1127
+        if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1128
+            $asterisk = '*';
1129
+            $input = mb_substr($input, 1, null, 'UTF-8');
1130
+        }
1131
+        $search  = array('*', '\\', '(', ')');
1132
+        $replace = array('\\*', '\\\\', '\\(', '\\)');
1133
+        return $asterisk . str_replace($search, $replace, $input);
1134
+    }
1135
+
1136
+    /**
1137
+     * combines the input filters with AND
1138
+     * @param string[] $filters the filters to connect
1139
+     * @return string the combined filter
1140
+     */
1141
+    public function combineFilterWithAnd($filters) {
1142
+        return $this->combineFilter($filters, '&');
1143
+    }
1144
+
1145
+    /**
1146
+     * combines the input filters with OR
1147
+     * @param string[] $filters the filters to connect
1148
+     * @return string the combined filter
1149
+     * Combines Filter arguments with OR
1150
+     */
1151
+    public function combineFilterWithOr($filters) {
1152
+        return $this->combineFilter($filters, '|');
1153
+    }
1154
+
1155
+    /**
1156
+     * combines the input filters with given operator
1157
+     * @param string[] $filters the filters to connect
1158
+     * @param string $operator either & or |
1159
+     * @return string the combined filter
1160
+     */
1161
+    private function combineFilter($filters, $operator) {
1162
+        $combinedFilter = '('.$operator;
1163
+        foreach($filters as $filter) {
1164
+            if(!empty($filter) && $filter[0] !== '(') {
1165
+                $filter = '('.$filter.')';
1166
+            }
1167
+            $combinedFilter.=$filter;
1168
+        }
1169
+        $combinedFilter.=')';
1170
+        return $combinedFilter;
1171
+    }
1172
+
1173
+    /**
1174
+     * creates a filter part for to perform search for users
1175
+     * @param string $search the search term
1176
+     * @return string the final filter part to use in LDAP searches
1177
+     */
1178
+    public function getFilterPartForUserSearch($search) {
1179
+        return $this->getFilterPartForSearch($search,
1180
+            $this->connection->ldapAttributesForUserSearch,
1181
+            $this->connection->ldapUserDisplayName);
1182
+    }
1183
+
1184
+    /**
1185
+     * creates a filter part for to perform search for groups
1186
+     * @param string $search the search term
1187
+     * @return string the final filter part to use in LDAP searches
1188
+     */
1189
+    public function getFilterPartForGroupSearch($search) {
1190
+        return $this->getFilterPartForSearch($search,
1191
+            $this->connection->ldapAttributesForGroupSearch,
1192
+            $this->connection->ldapGroupDisplayName);
1193
+    }
1194
+
1195
+    /**
1196
+     * creates a filter part for searches by splitting up the given search
1197
+     * string into single words
1198
+     * @param string $search the search term
1199
+     * @param string[] $searchAttributes needs to have at least two attributes,
1200
+     * otherwise it does not make sense :)
1201
+     * @return string the final filter part to use in LDAP searches
1202
+     * @throws \Exception
1203
+     */
1204
+    private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1205
+        if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1206
+            throw new \Exception('searchAttributes must be an array with at least two string');
1207
+        }
1208
+        $searchWords = explode(' ', trim($search));
1209
+        $wordFilters = array();
1210
+        foreach($searchWords as $word) {
1211
+            $word = $this->prepareSearchTerm($word);
1212
+            //every word needs to appear at least once
1213
+            $wordMatchOneAttrFilters = array();
1214
+            foreach($searchAttributes as $attr) {
1215
+                $wordMatchOneAttrFilters[] = $attr . '=' . $word;
1216
+            }
1217
+            $wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1218
+        }
1219
+        return $this->combineFilterWithAnd($wordFilters);
1220
+    }
1221
+
1222
+    /**
1223
+     * creates a filter part for searches
1224
+     * @param string $search the search term
1225
+     * @param string[]|null $searchAttributes
1226
+     * @param string $fallbackAttribute a fallback attribute in case the user
1227
+     * did not define search attributes. Typically the display name attribute.
1228
+     * @return string the final filter part to use in LDAP searches
1229
+     */
1230
+    private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1231
+        $filter = array();
1232
+        $haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1233
+        if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1234
+            try {
1235
+                return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1236
+            } catch(\Exception $e) {
1237
+                \OCP\Util::writeLog(
1238
+                    'user_ldap',
1239
+                    'Creating advanced filter for search failed, falling back to simple method.',
1240
+                    \OCP\Util::INFO
1241
+                );
1242
+            }
1243
+        }
1244
+
1245
+        $search = $this->prepareSearchTerm($search);
1246
+        if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1247
+            if(empty($fallbackAttribute)) {
1248
+                return '';
1249
+            }
1250
+            $filter[] = $fallbackAttribute . '=' . $search;
1251
+        } else {
1252
+            foreach($searchAttributes as $attribute) {
1253
+                $filter[] = $attribute . '=' . $search;
1254
+            }
1255
+        }
1256
+        if(count($filter) === 1) {
1257
+            return '('.$filter[0].')';
1258
+        }
1259
+        return $this->combineFilterWithOr($filter);
1260
+    }
1261
+
1262
+    /**
1263
+     * returns the search term depending on whether we are allowed
1264
+     * list users found by ldap with the current input appended by
1265
+     * a *
1266
+     * @return string
1267
+     */
1268
+    private function prepareSearchTerm($term) {
1269
+        $config = \OC::$server->getConfig();
1270
+
1271
+        $allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1272
+
1273
+        $result = empty($term) ? '*' :
1274
+            $allowEnum !== 'no' ? $term . '*' : $term;
1275
+        return $result;
1276
+    }
1277
+
1278
+    /**
1279
+     * returns the filter used for counting users
1280
+     * @return string
1281
+     */
1282
+    public function getFilterForUserCount() {
1283
+        $filter = $this->combineFilterWithAnd(array(
1284
+            $this->connection->ldapUserFilter,
1285
+            $this->connection->ldapUserDisplayName . '=*'
1286
+        ));
1287
+
1288
+        return $filter;
1289
+    }
1290
+
1291
+    /**
1292
+     * @param string $name
1293
+     * @param string $password
1294
+     * @return bool
1295
+     */
1296
+    public function areCredentialsValid($name, $password) {
1297
+        $name = $this->DNasBaseParameter($name);
1298
+        $testConnection = clone $this->connection;
1299
+        $credentials = array(
1300
+            'ldapAgentName' => $name,
1301
+            'ldapAgentPassword' => $password
1302
+        );
1303
+        if(!$testConnection->setConfiguration($credentials)) {
1304
+            return false;
1305
+        }
1306
+        return $testConnection->bind();
1307
+    }
1308
+
1309
+    /**
1310
+     * reverse lookup of a DN given a known UUID
1311
+     *
1312
+     * @param string $uuid
1313
+     * @return string
1314
+     * @throws \Exception
1315
+     */
1316
+    public function getUserDnByUuid($uuid) {
1317
+        $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1318
+        $filter       = $this->connection->ldapUserFilter;
1319
+        $base         = $this->connection->ldapBaseUsers;
1320
+
1321
+        if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1322
+            // Sacrebleu! The UUID attribute is unknown :( We need first an
1323
+            // existing DN to be able to reliably detect it.
1324
+            $result = $this->search($filter, $base, ['dn'], 1);
1325
+            if(!isset($result[0]) || !isset($result[0]['dn'])) {
1326
+                throw new \Exception('Cannot determine UUID attribute');
1327
+            }
1328
+            $dn = $result[0]['dn'][0];
1329
+            if(!$this->detectUuidAttribute($dn, true)) {
1330
+                throw new \Exception('Cannot determine UUID attribute');
1331
+            }
1332
+        } else {
1333
+            // The UUID attribute is either known or an override is given.
1334
+            // By calling this method we ensure that $this->connection->$uuidAttr
1335
+            // is definitely set
1336
+            if(!$this->detectUuidAttribute('', true)) {
1337
+                throw new \Exception('Cannot determine UUID attribute');
1338
+            }
1339
+        }
1340
+
1341
+        $uuidAttr = $this->connection->ldapUuidUserAttribute;
1342
+        if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1343
+            $uuid = $this->formatGuid2ForFilterUser($uuid);
1344
+        }
1345
+
1346
+        $filter = $uuidAttr . '=' . $uuid;
1347
+        $result = $this->searchUsers($filter, ['dn'], 2);
1348
+        if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1349
+            // we put the count into account to make sure that this is
1350
+            // really unique
1351
+            return $result[0]['dn'][0];
1352
+        }
1353
+
1354
+        throw new \Exception('Cannot determine UUID attribute');
1355
+    }
1356
+
1357
+    /**
1358
+     * auto-detects the directory's UUID attribute
1359
+     * @param string $dn a known DN used to check against
1360
+     * @param bool $isUser
1361
+     * @param bool $force the detection should be run, even if it is not set to auto
1362
+     * @return bool true on success, false otherwise
1363
+     */
1364
+    private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1365
+        if($isUser) {
1366
+            $uuidAttr     = 'ldapUuidUserAttribute';
1367
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1368
+        } else {
1369
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1370
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1371
+        }
1372
+
1373
+        if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1374
+            return true;
1375
+        }
1376
+
1377
+        if(!empty($uuidOverride) && !$force) {
1378
+            $this->connection->$uuidAttr = $uuidOverride;
1379
+            return true;
1380
+        }
1381
+
1382
+        // for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1383
+        $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1384
+
1385
+        foreach($testAttributes as $attribute) {
1386
+            $value = $this->readAttribute($dn, $attribute);
1387
+            if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1388
+                \OCP\Util::writeLog('user_ldap',
1389
+                                    'Setting '.$attribute.' as '.$uuidAttr,
1390
+                                    \OCP\Util::DEBUG);
1391
+                $this->connection->$uuidAttr = $attribute;
1392
+                return true;
1393
+            }
1394
+        }
1395
+        \OCP\Util::writeLog('user_ldap',
1396
+                            'Could not autodetect the UUID attribute',
1397
+                            \OCP\Util::ERROR);
1398
+
1399
+        return false;
1400
+    }
1401
+
1402
+    /**
1403
+     * @param string $dn
1404
+     * @param bool $isUser
1405
+     * @return string|bool
1406
+     */
1407
+    public function getUUID($dn, $isUser = true) {
1408
+        if($isUser) {
1409
+            $uuidAttr     = 'ldapUuidUserAttribute';
1410
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1411
+        } else {
1412
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1413
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1414
+        }
1415
+
1416
+        $uuid = false;
1417
+        if($this->detectUuidAttribute($dn, $isUser)) {
1418
+            $uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1419
+            if( !is_array($uuid)
1420
+                && !empty($uuidOverride)
1421
+                && $this->detectUuidAttribute($dn, $isUser, true)) {
1422
+                    $uuid = $this->readAttribute($dn,
1423
+                                                    $this->connection->$uuidAttr);
1424
+            }
1425
+            if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1426
+                $uuid = $uuid[0];
1427
+            }
1428
+        }
1429
+
1430
+        return $uuid;
1431
+    }
1432
+
1433
+    /**
1434
+     * converts a binary ObjectGUID into a string representation
1435
+     * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1436
+     * @return string
1437
+     * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1438
+     */
1439
+    private function convertObjectGUID2Str($oguid) {
1440
+        $hex_guid = bin2hex($oguid);
1441
+        $hex_guid_to_guid_str = '';
1442
+        for($k = 1; $k <= 4; ++$k) {
1443
+            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1444
+        }
1445
+        $hex_guid_to_guid_str .= '-';
1446
+        for($k = 1; $k <= 2; ++$k) {
1447
+            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1448
+        }
1449
+        $hex_guid_to_guid_str .= '-';
1450
+        for($k = 1; $k <= 2; ++$k) {
1451
+            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1452
+        }
1453
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1454
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1455
+
1456
+        return strtoupper($hex_guid_to_guid_str);
1457
+    }
1458
+
1459
+    /**
1460
+     * the first three blocks of the string-converted GUID happen to be in
1461
+     * reverse order. In order to use it in a filter, this needs to be
1462
+     * corrected. Furthermore the dashes need to be replaced and \\ preprended
1463
+     * to every two hax figures.
1464
+     *
1465
+     * If an invalid string is passed, it will be returned without change.
1466
+     *
1467
+     * @param string $guid
1468
+     * @return string
1469
+     */
1470
+    public function formatGuid2ForFilterUser($guid) {
1471
+        if(!is_string($guid)) {
1472
+            throw new \InvalidArgumentException('String expected');
1473
+        }
1474
+        $blocks = explode('-', $guid);
1475
+        if(count($blocks) !== 5) {
1476
+            /*
1477 1477
 			 * Why not throw an Exception instead? This method is a utility
1478 1478
 			 * called only when trying to figure out whether a "missing" known
1479 1479
 			 * LDAP user was or was not renamed on the LDAP server. And this
@@ -1484,284 +1484,284 @@  discard block
 block discarded – undo
1484 1484
 			 * an exception here would kill the experience for a valid, acting
1485 1485
 			 * user. Instead we write a log message.
1486 1486
 			 */
1487
-			\OC::$server->getLogger()->info(
1488
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1489
-				'({uuid}) probably does not match UUID configuration.',
1490
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1491
-			);
1492
-			return $guid;
1493
-		}
1494
-		for($i=0; $i < 3; $i++) {
1495
-			$pairs = str_split($blocks[$i], 2);
1496
-			$pairs = array_reverse($pairs);
1497
-			$blocks[$i] = implode('', $pairs);
1498
-		}
1499
-		for($i=0; $i < 5; $i++) {
1500
-			$pairs = str_split($blocks[$i], 2);
1501
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1502
-		}
1503
-		return implode('', $blocks);
1504
-	}
1505
-
1506
-	/**
1507
-	 * gets a SID of the domain of the given dn
1508
-	 * @param string $dn
1509
-	 * @return string|bool
1510
-	 */
1511
-	public function getSID($dn) {
1512
-		$domainDN = $this->getDomainDNFromDN($dn);
1513
-		$cacheKey = 'getSID-'.$domainDN;
1514
-		$sid = $this->connection->getFromCache($cacheKey);
1515
-		if(!is_null($sid)) {
1516
-			return $sid;
1517
-		}
1518
-
1519
-		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1520
-		if(!is_array($objectSid) || empty($objectSid)) {
1521
-			$this->connection->writeToCache($cacheKey, false);
1522
-			return false;
1523
-		}
1524
-		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
1525
-		$this->connection->writeToCache($cacheKey, $domainObjectSid);
1526
-
1527
-		return $domainObjectSid;
1528
-	}
1529
-
1530
-	/**
1531
-	 * converts a binary SID into a string representation
1532
-	 * @param string $sid
1533
-	 * @return string
1534
-	 */
1535
-	public function convertSID2Str($sid) {
1536
-		// The format of a SID binary string is as follows:
1537
-		// 1 byte for the revision level
1538
-		// 1 byte for the number n of variable sub-ids
1539
-		// 6 bytes for identifier authority value
1540
-		// n*4 bytes for n sub-ids
1541
-		//
1542
-		// Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1543
-		//  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1544
-		$revision = ord($sid[0]);
1545
-		$numberSubID = ord($sid[1]);
1546
-
1547
-		$subIdStart = 8; // 1 + 1 + 6
1548
-		$subIdLength = 4;
1549
-		if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1550
-			// Incorrect number of bytes present.
1551
-			return '';
1552
-		}
1553
-
1554
-		// 6 bytes = 48 bits can be represented using floats without loss of
1555
-		// precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1556
-		$iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1557
-
1558
-		$subIDs = array();
1559
-		for ($i = 0; $i < $numberSubID; $i++) {
1560
-			$subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1561
-			$subIDs[] = sprintf('%u', $subID[1]);
1562
-		}
1563
-
1564
-		// Result for example above: S-1-5-21-249921958-728525901-1594176202
1565
-		return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1566
-	}
1567
-
1568
-	/**
1569
-	 * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
1570
-	 * @param string $dn the DN
1571
-	 * @return string
1572
-	 */
1573
-	private function DNasBaseParameter($dn) {
1574
-		return str_ireplace('\\5c', '\\', $dn);
1575
-	}
1576
-
1577
-	/**
1578
-	 * checks if the given DN is part of the given base DN(s)
1579
-	 * @param string $dn the DN
1580
-	 * @param string[] $bases array containing the allowed base DN or DNs
1581
-	 * @return bool
1582
-	 */
1583
-	public function isDNPartOfBase($dn, $bases) {
1584
-		$belongsToBase = false;
1585
-		$bases = $this->sanitizeDN($bases);
1586
-
1587
-		foreach($bases as $base) {
1588
-			$belongsToBase = true;
1589
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1590
-				$belongsToBase = false;
1591
-			}
1592
-			if($belongsToBase) {
1593
-				break;
1594
-			}
1595
-		}
1596
-		return $belongsToBase;
1597
-	}
1598
-
1599
-	/**
1600
-	 * resets a running Paged Search operation
1601
-	 */
1602
-	private function abandonPagedSearch() {
1603
-		if($this->connection->hasPagedResultSupport) {
1604
-			$cr = $this->connection->getConnectionResource();
1605
-			$this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1606
-			$this->getPagedSearchResultState();
1607
-			$this->lastCookie = '';
1608
-			$this->cookies = array();
1609
-		}
1610
-	}
1611
-
1612
-	/**
1613
-	 * get a cookie for the next LDAP paged search
1614
-	 * @param string $base a string with the base DN for the search
1615
-	 * @param string $filter the search filter to identify the correct search
1616
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1617
-	 * @param int $offset the offset for the new search to identify the correct search really good
1618
-	 * @return string containing the key or empty if none is cached
1619
-	 */
1620
-	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1621
-		if($offset === 0) {
1622
-			return '';
1623
-		}
1624
-		$offset -= $limit;
1625
-		//we work with cache here
1626
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1627
-		$cookie = '';
1628
-		if(isset($this->cookies[$cacheKey])) {
1629
-			$cookie = $this->cookies[$cacheKey];
1630
-			if(is_null($cookie)) {
1631
-				$cookie = '';
1632
-			}
1633
-		}
1634
-		return $cookie;
1635
-	}
1636
-
1637
-	/**
1638
-	 * checks whether an LDAP paged search operation has more pages that can be
1639
-	 * retrieved, typically when offset and limit are provided.
1640
-	 *
1641
-	 * Be very careful to use it: the last cookie value, which is inspected, can
1642
-	 * be reset by other operations. Best, call it immediately after a search(),
1643
-	 * searchUsers() or searchGroups() call. count-methods are probably safe as
1644
-	 * well. Don't rely on it with any fetchList-method.
1645
-	 * @return bool
1646
-	 */
1647
-	public function hasMoreResults() {
1648
-		if(!$this->connection->hasPagedResultSupport) {
1649
-			return false;
1650
-		}
1651
-
1652
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1653
-			// as in RFC 2696, when all results are returned, the cookie will
1654
-			// be empty.
1655
-			return false;
1656
-		}
1657
-
1658
-		return true;
1659
-	}
1660
-
1661
-	/**
1662
-	 * set a cookie for LDAP paged search run
1663
-	 * @param string $base a string with the base DN for the search
1664
-	 * @param string $filter the search filter to identify the correct search
1665
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1666
-	 * @param int $offset the offset for the run search to identify the correct search really good
1667
-	 * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1668
-	 * @return void
1669
-	 */
1670
-	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1671
-		// allow '0' for 389ds
1672
-		if(!empty($cookie) || $cookie === '0') {
1673
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1674
-			$this->cookies[$cacheKey] = $cookie;
1675
-			$this->lastCookie = $cookie;
1676
-		}
1677
-	}
1678
-
1679
-	/**
1680
-	 * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1681
-	 * @return boolean|null true on success, null or false otherwise
1682
-	 */
1683
-	public function getPagedSearchResultState() {
1684
-		$result = $this->pagedSearchedSuccessful;
1685
-		$this->pagedSearchedSuccessful = null;
1686
-		return $result;
1687
-	}
1688
-
1689
-	/**
1690
-	 * Prepares a paged search, if possible
1691
-	 * @param string $filter the LDAP filter for the search
1692
-	 * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1693
-	 * @param string[] $attr optional, when a certain attribute shall be filtered outside
1694
-	 * @param int $limit
1695
-	 * @param int $offset
1696
-	 * @return bool|true
1697
-	 */
1698
-	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1699
-		$pagedSearchOK = false;
1700
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1701
-			$offset = intval($offset); //can be null
1702
-			\OCP\Util::writeLog('user_ldap',
1703
-				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1704
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1705
-				\OCP\Util::DEBUG);
1706
-			//get the cookie from the search for the previous search, required by LDAP
1707
-			foreach($bases as $base) {
1708
-
1709
-				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1710
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1711
-					// no cookie known, although the offset is not 0. Maybe cache run out. We need
1712
-					// to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1713
-					// searching was designed by MSFT?)
1714
-					// 		Lukas: No, but thanks to reading that source I finally know!
1715
-					// '0' is valid, because 389ds
1716
-					$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
1717
-					//a bit recursive, $offset of 0 is the exit
1718
-					\OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
1719
-					$this->search($filter, array($base), $attr, $limit, $reOffset, true);
1720
-					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1721
-					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1722
-					//TODO: remember this, probably does not change in the next request...
1723
-					if(empty($cookie) && $cookie !== '0') {
1724
-						// '0' is valid, because 389ds
1725
-						$cookie = null;
1726
-					}
1727
-				}
1728
-				if(!is_null($cookie)) {
1729
-					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1730
-					$this->abandonPagedSearch();
1731
-					$pagedSearchOK = $this->ldap->controlPagedResult(
1732
-						$this->connection->getConnectionResource(), $limit,
1733
-						false, $cookie);
1734
-					if(!$pagedSearchOK) {
1735
-						return false;
1736
-					}
1737
-					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1738
-				} else {
1739
-					\OCP\Util::writeLog('user_ldap',
1740
-						'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1741
-						\OCP\Util::INFO);
1742
-				}
1743
-
1744
-			}
1745
-		/* ++ Fixing RHDS searches with pages with zero results ++
1487
+            \OC::$server->getLogger()->info(
1488
+                'Passed string does not resemble a valid GUID. Known UUID ' .
1489
+                '({uuid}) probably does not match UUID configuration.',
1490
+                [ 'app' => 'user_ldap', 'uuid' => $guid ]
1491
+            );
1492
+            return $guid;
1493
+        }
1494
+        for($i=0; $i < 3; $i++) {
1495
+            $pairs = str_split($blocks[$i], 2);
1496
+            $pairs = array_reverse($pairs);
1497
+            $blocks[$i] = implode('', $pairs);
1498
+        }
1499
+        for($i=0; $i < 5; $i++) {
1500
+            $pairs = str_split($blocks[$i], 2);
1501
+            $blocks[$i] = '\\' . implode('\\', $pairs);
1502
+        }
1503
+        return implode('', $blocks);
1504
+    }
1505
+
1506
+    /**
1507
+     * gets a SID of the domain of the given dn
1508
+     * @param string $dn
1509
+     * @return string|bool
1510
+     */
1511
+    public function getSID($dn) {
1512
+        $domainDN = $this->getDomainDNFromDN($dn);
1513
+        $cacheKey = 'getSID-'.$domainDN;
1514
+        $sid = $this->connection->getFromCache($cacheKey);
1515
+        if(!is_null($sid)) {
1516
+            return $sid;
1517
+        }
1518
+
1519
+        $objectSid = $this->readAttribute($domainDN, 'objectsid');
1520
+        if(!is_array($objectSid) || empty($objectSid)) {
1521
+            $this->connection->writeToCache($cacheKey, false);
1522
+            return false;
1523
+        }
1524
+        $domainObjectSid = $this->convertSID2Str($objectSid[0]);
1525
+        $this->connection->writeToCache($cacheKey, $domainObjectSid);
1526
+
1527
+        return $domainObjectSid;
1528
+    }
1529
+
1530
+    /**
1531
+     * converts a binary SID into a string representation
1532
+     * @param string $sid
1533
+     * @return string
1534
+     */
1535
+    public function convertSID2Str($sid) {
1536
+        // The format of a SID binary string is as follows:
1537
+        // 1 byte for the revision level
1538
+        // 1 byte for the number n of variable sub-ids
1539
+        // 6 bytes for identifier authority value
1540
+        // n*4 bytes for n sub-ids
1541
+        //
1542
+        // Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1543
+        //  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1544
+        $revision = ord($sid[0]);
1545
+        $numberSubID = ord($sid[1]);
1546
+
1547
+        $subIdStart = 8; // 1 + 1 + 6
1548
+        $subIdLength = 4;
1549
+        if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1550
+            // Incorrect number of bytes present.
1551
+            return '';
1552
+        }
1553
+
1554
+        // 6 bytes = 48 bits can be represented using floats without loss of
1555
+        // precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1556
+        $iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1557
+
1558
+        $subIDs = array();
1559
+        for ($i = 0; $i < $numberSubID; $i++) {
1560
+            $subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1561
+            $subIDs[] = sprintf('%u', $subID[1]);
1562
+        }
1563
+
1564
+        // Result for example above: S-1-5-21-249921958-728525901-1594176202
1565
+        return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1566
+    }
1567
+
1568
+    /**
1569
+     * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
1570
+     * @param string $dn the DN
1571
+     * @return string
1572
+     */
1573
+    private function DNasBaseParameter($dn) {
1574
+        return str_ireplace('\\5c', '\\', $dn);
1575
+    }
1576
+
1577
+    /**
1578
+     * checks if the given DN is part of the given base DN(s)
1579
+     * @param string $dn the DN
1580
+     * @param string[] $bases array containing the allowed base DN or DNs
1581
+     * @return bool
1582
+     */
1583
+    public function isDNPartOfBase($dn, $bases) {
1584
+        $belongsToBase = false;
1585
+        $bases = $this->sanitizeDN($bases);
1586
+
1587
+        foreach($bases as $base) {
1588
+            $belongsToBase = true;
1589
+            if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1590
+                $belongsToBase = false;
1591
+            }
1592
+            if($belongsToBase) {
1593
+                break;
1594
+            }
1595
+        }
1596
+        return $belongsToBase;
1597
+    }
1598
+
1599
+    /**
1600
+     * resets a running Paged Search operation
1601
+     */
1602
+    private function abandonPagedSearch() {
1603
+        if($this->connection->hasPagedResultSupport) {
1604
+            $cr = $this->connection->getConnectionResource();
1605
+            $this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1606
+            $this->getPagedSearchResultState();
1607
+            $this->lastCookie = '';
1608
+            $this->cookies = array();
1609
+        }
1610
+    }
1611
+
1612
+    /**
1613
+     * get a cookie for the next LDAP paged search
1614
+     * @param string $base a string with the base DN for the search
1615
+     * @param string $filter the search filter to identify the correct search
1616
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1617
+     * @param int $offset the offset for the new search to identify the correct search really good
1618
+     * @return string containing the key or empty if none is cached
1619
+     */
1620
+    private function getPagedResultCookie($base, $filter, $limit, $offset) {
1621
+        if($offset === 0) {
1622
+            return '';
1623
+        }
1624
+        $offset -= $limit;
1625
+        //we work with cache here
1626
+        $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1627
+        $cookie = '';
1628
+        if(isset($this->cookies[$cacheKey])) {
1629
+            $cookie = $this->cookies[$cacheKey];
1630
+            if(is_null($cookie)) {
1631
+                $cookie = '';
1632
+            }
1633
+        }
1634
+        return $cookie;
1635
+    }
1636
+
1637
+    /**
1638
+     * checks whether an LDAP paged search operation has more pages that can be
1639
+     * retrieved, typically when offset and limit are provided.
1640
+     *
1641
+     * Be very careful to use it: the last cookie value, which is inspected, can
1642
+     * be reset by other operations. Best, call it immediately after a search(),
1643
+     * searchUsers() or searchGroups() call. count-methods are probably safe as
1644
+     * well. Don't rely on it with any fetchList-method.
1645
+     * @return bool
1646
+     */
1647
+    public function hasMoreResults() {
1648
+        if(!$this->connection->hasPagedResultSupport) {
1649
+            return false;
1650
+        }
1651
+
1652
+        if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1653
+            // as in RFC 2696, when all results are returned, the cookie will
1654
+            // be empty.
1655
+            return false;
1656
+        }
1657
+
1658
+        return true;
1659
+    }
1660
+
1661
+    /**
1662
+     * set a cookie for LDAP paged search run
1663
+     * @param string $base a string with the base DN for the search
1664
+     * @param string $filter the search filter to identify the correct search
1665
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1666
+     * @param int $offset the offset for the run search to identify the correct search really good
1667
+     * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1668
+     * @return void
1669
+     */
1670
+    private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1671
+        // allow '0' for 389ds
1672
+        if(!empty($cookie) || $cookie === '0') {
1673
+            $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1674
+            $this->cookies[$cacheKey] = $cookie;
1675
+            $this->lastCookie = $cookie;
1676
+        }
1677
+    }
1678
+
1679
+    /**
1680
+     * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1681
+     * @return boolean|null true on success, null or false otherwise
1682
+     */
1683
+    public function getPagedSearchResultState() {
1684
+        $result = $this->pagedSearchedSuccessful;
1685
+        $this->pagedSearchedSuccessful = null;
1686
+        return $result;
1687
+    }
1688
+
1689
+    /**
1690
+     * Prepares a paged search, if possible
1691
+     * @param string $filter the LDAP filter for the search
1692
+     * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1693
+     * @param string[] $attr optional, when a certain attribute shall be filtered outside
1694
+     * @param int $limit
1695
+     * @param int $offset
1696
+     * @return bool|true
1697
+     */
1698
+    private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1699
+        $pagedSearchOK = false;
1700
+        if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1701
+            $offset = intval($offset); //can be null
1702
+            \OCP\Util::writeLog('user_ldap',
1703
+                'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1704
+                .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1705
+                \OCP\Util::DEBUG);
1706
+            //get the cookie from the search for the previous search, required by LDAP
1707
+            foreach($bases as $base) {
1708
+
1709
+                $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1710
+                if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1711
+                    // no cookie known, although the offset is not 0. Maybe cache run out. We need
1712
+                    // to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1713
+                    // searching was designed by MSFT?)
1714
+                    // 		Lukas: No, but thanks to reading that source I finally know!
1715
+                    // '0' is valid, because 389ds
1716
+                    $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
1717
+                    //a bit recursive, $offset of 0 is the exit
1718
+                    \OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
1719
+                    $this->search($filter, array($base), $attr, $limit, $reOffset, true);
1720
+                    $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1721
+                    //still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1722
+                    //TODO: remember this, probably does not change in the next request...
1723
+                    if(empty($cookie) && $cookie !== '0') {
1724
+                        // '0' is valid, because 389ds
1725
+                        $cookie = null;
1726
+                    }
1727
+                }
1728
+                if(!is_null($cookie)) {
1729
+                    //since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1730
+                    $this->abandonPagedSearch();
1731
+                    $pagedSearchOK = $this->ldap->controlPagedResult(
1732
+                        $this->connection->getConnectionResource(), $limit,
1733
+                        false, $cookie);
1734
+                    if(!$pagedSearchOK) {
1735
+                        return false;
1736
+                    }
1737
+                    \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1738
+                } else {
1739
+                    \OCP\Util::writeLog('user_ldap',
1740
+                        'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1741
+                        \OCP\Util::INFO);
1742
+                }
1743
+
1744
+            }
1745
+        /* ++ Fixing RHDS searches with pages with zero results ++
1746 1746
 		 * We coudn't get paged searches working with our RHDS for login ($limit = 0),
1747 1747
 		 * due to pages with zero results.
1748 1748
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1749 1749
 		 * if we don't have a previous paged search.
1750 1750
 		 */
1751
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1752
-			// a search without limit was requested. However, if we do use
1753
-			// Paged Search once, we always must do it. This requires us to
1754
-			// initialize it with the configured page size.
1755
-			$this->abandonPagedSearch();
1756
-			// in case someone set it to 0 … use 500, otherwise no results will
1757
-			// be returned.
1758
-			$pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1759
-			$pagedSearchOK = $this->ldap->controlPagedResult(
1760
-				$this->connection->getConnectionResource(), $pageSize, false, ''
1761
-			);
1762
-		}
1763
-
1764
-		return $pagedSearchOK;
1765
-	}
1751
+        } else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1752
+            // a search without limit was requested. However, if we do use
1753
+            // Paged Search once, we always must do it. This requires us to
1754
+            // initialize it with the configured page size.
1755
+            $this->abandonPagedSearch();
1756
+            // in case someone set it to 0 … use 500, otherwise no results will
1757
+            // be returned.
1758
+            $pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1759
+            $pagedSearchOK = $this->ldap->controlPagedResult(
1760
+                $this->connection->getConnectionResource(), $pageSize, false, ''
1761
+            );
1762
+        }
1763
+
1764
+        return $pagedSearchOK;
1765
+    }
1766 1766
 
1767 1767
 }
Please login to merge, or discard this patch.
Spacing   +158 added lines, -159 removed lines patch added patch discarded remove patch
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
 	 * @return AbstractMapping
97 97
 	 */
98 98
 	public function getUserMapper() {
99
-		if(is_null($this->userMapper)) {
99
+		if (is_null($this->userMapper)) {
100 100
 			throw new \Exception('UserMapper was not assigned to this Access instance.');
101 101
 		}
102 102
 		return $this->userMapper;
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
 	 * @return AbstractMapping
117 117
 	 */
118 118
 	public function getGroupMapper() {
119
-		if(is_null($this->groupMapper)) {
119
+		if (is_null($this->groupMapper)) {
120 120
 			throw new \Exception('GroupMapper was not assigned to this Access instance.');
121 121
 		}
122 122
 		return $this->groupMapper;
@@ -147,14 +147,14 @@  discard block
 block discarded – undo
147 147
 	 *          array if $attr is empty, false otherwise
148 148
 	 */
149 149
 	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
150
-		if(!$this->checkConnection()) {
150
+		if (!$this->checkConnection()) {
151 151
 			\OCP\Util::writeLog('user_ldap',
152 152
 				'No LDAP Connector assigned, access impossible for readAttribute.',
153 153
 				\OCP\Util::WARN);
154 154
 			return false;
155 155
 		}
156 156
 		$cr = $this->connection->getConnectionResource();
157
-		if(!$this->ldap->isResource($cr)) {
157
+		if (!$this->ldap->isResource($cr)) {
158 158
 			//LDAP not available
159 159
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
160 160
 			return false;
@@ -171,8 +171,8 @@  discard block
 block discarded – undo
171 171
 		$this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0);
172 172
 		$dn = $this->DNasBaseParameter($dn);
173 173
 		$rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
174
-		if(!$this->ldap->isResource($rr)) {
175
-			if(!empty($attr)) {
174
+		if (!$this->ldap->isResource($rr)) {
175
+			if (!empty($attr)) {
176 176
 				//do not throw this message on userExists check, irritates
177 177
 				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
178 178
 			}
@@ -184,7 +184,7 @@  discard block
 block discarded – undo
184 184
 			return array();
185 185
 		}
186 186
 		$er = $this->ldap->firstEntry($cr, $rr);
187
-		if(!$this->ldap->isResource($er)) {
187
+		if (!$this->ldap->isResource($er)) {
188 188
 			//did not match the filter, return false
189 189
 			return false;
190 190
 		}
@@ -193,12 +193,12 @@  discard block
 block discarded – undo
193 193
 				$this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
194 194
 		$attr = mb_strtolower($attr, 'UTF-8');
195 195
 
196
-		if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
196
+		if (isset($result[$attr]) && $result[$attr]['count'] > 0) {
197 197
 			$values = array();
198
-			for($i=0;$i<$result[$attr]['count'];$i++) {
199
-				if($this->resemblesDN($attr)) {
198
+			for ($i = 0; $i < $result[$attr]['count']; $i++) {
199
+				if ($this->resemblesDN($attr)) {
200 200
 					$values[] = $this->sanitizeDN($result[$attr][$i]);
201
-				} elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
201
+				} elseif (strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') {
202 202
 					$values[] = $this->convertObjectGUID2Str($result[$attr][$i]);
203 203
 				} else {
204 204
 					$values[] = $result[$attr][$i];
@@ -245,9 +245,9 @@  discard block
 block discarded – undo
245 245
 	 */
246 246
 	private function sanitizeDN($dn) {
247 247
 		//treating multiple base DNs
248
-		if(is_array($dn)) {
248
+		if (is_array($dn)) {
249 249
 			$result = array();
250
-			foreach($dn as $singleDN) {
250
+			foreach ($dn as $singleDN) {
251 251
 				$result[] = $this->sanitizeDN($singleDN);
252 252
 			}
253 253
 			return $result;
@@ -290,17 +290,17 @@  discard block
 block discarded – undo
290 290
 	 */
291 291
 	public function getDomainDNFromDN($dn) {
292 292
 		$allParts = $this->ldap->explodeDN($dn, 0);
293
-		if($allParts === false) {
293
+		if ($allParts === false) {
294 294
 			//not a valid DN
295 295
 			return '';
296 296
 		}
297 297
 		$domainParts = array();
298 298
 		$dcFound = false;
299
-		foreach($allParts as $part) {
300
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
299
+		foreach ($allParts as $part) {
300
+			if (!$dcFound && strpos($part, 'dc=') === 0) {
301 301
 				$dcFound = true;
302 302
 			}
303
-			if($dcFound) {
303
+			if ($dcFound) {
304 304
 				$domainParts[] = $part;
305 305
 			}
306 306
 		}
@@ -327,7 +327,7 @@  discard block
 block discarded – undo
327 327
 
328 328
 		//Check whether the DN belongs to the Base, to avoid issues on multi-
329 329
 		//server setups
330
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
330
+		if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
331 331
 			return $fdn;
332 332
 		}
333 333
 
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
 		//To avoid bypassing the base DN settings under certain circumstances
346 346
 		//with the group support, check whether the provided DN matches one of
347 347
 		//the given Bases
348
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
348
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
349 349
 			return false;
350 350
 		}
351 351
 
@@ -362,11 +362,11 @@  discard block
 block discarded – undo
362 362
 	 */
363 363
 	public function groupsMatchFilter($groupDNs) {
364 364
 		$validGroupDNs = [];
365
-		foreach($groupDNs as $dn) {
365
+		foreach ($groupDNs as $dn) {
366 366
 			$cacheKey = 'groupsMatchFilter-'.$dn;
367 367
 			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
368
-			if(!is_null($groupMatchFilter)) {
369
-				if($groupMatchFilter) {
368
+			if (!is_null($groupMatchFilter)) {
369
+				if ($groupMatchFilter) {
370 370
 					$validGroupDNs[] = $dn;
371 371
 				}
372 372
 				continue;
@@ -374,13 +374,13 @@  discard block
 block discarded – undo
374 374
 
375 375
 			// Check the base DN first. If this is not met already, we don't
376 376
 			// need to ask the server at all.
377
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
377
+			if (!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
378 378
 				$this->connection->writeToCache($cacheKey, false);
379 379
 				continue;
380 380
 			}
381 381
 
382 382
 			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
383
-			if(is_array($result)) {
383
+			if (is_array($result)) {
384 384
 				$this->connection->writeToCache($cacheKey, true);
385 385
 				$validGroupDNs[] = $dn;
386 386
 			} else {
@@ -401,7 +401,7 @@  discard block
 block discarded – undo
401 401
 		//To avoid bypassing the base DN settings under certain circumstances
402 402
 		//with the group support, check whether the provided DN matches one of
403 403
 		//the given Bases
404
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
404
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
405 405
 			return false;
406 406
 		}
407 407
 
@@ -416,7 +416,7 @@  discard block
 block discarded – undo
416 416
 	 * @return string|false with with the name to use in ownCloud
417 417
 	 */
418 418
 	public function dn2ocname($fdn, $ldapName = null, $isUser = true) {
419
-		if($isUser) {
419
+		if ($isUser) {
420 420
 			$mapper = $this->getUserMapper();
421 421
 			$nameAttribute = $this->connection->ldapUserDisplayName;
422 422
 		} else {
@@ -426,15 +426,15 @@  discard block
 block discarded – undo
426 426
 
427 427
 		//let's try to retrieve the ownCloud name from the mappings table
428 428
 		$ocName = $mapper->getNameByDN($fdn);
429
-		if(is_string($ocName)) {
429
+		if (is_string($ocName)) {
430 430
 			return $ocName;
431 431
 		}
432 432
 
433 433
 		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
434 434
 		$uuid = $this->getUUID($fdn, $isUser);
435
-		if(is_string($uuid)) {
435
+		if (is_string($uuid)) {
436 436
 			$ocName = $mapper->getNameByUUID($uuid);
437
-			if(is_string($ocName)) {
437
+			if (is_string($ocName)) {
438 438
 				$mapper->setDNbyUUID($fdn, $uuid);
439 439
 				return $ocName;
440 440
 			}
@@ -444,18 +444,18 @@  discard block
 block discarded – undo
444 444
 			return false;
445 445
 		}
446 446
 
447
-		if(is_null($ldapName)) {
447
+		if (is_null($ldapName)) {
448 448
 			$ldapName = $this->readAttribute($fdn, $nameAttribute);
449
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
449
+			if (!isset($ldapName[0]) && empty($ldapName[0])) {
450 450
 				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
451 451
 				return false;
452 452
 			}
453 453
 			$ldapName = $ldapName[0];
454 454
 		}
455 455
 
456
-		if($isUser) {
456
+		if ($isUser) {
457 457
 			$usernameAttribute = $this->connection->ldapExpertUsernameAttr;
458
-			if(!empty($usernameAttribute)) {
458
+			if (!empty($usernameAttribute)) {
459 459
 				$username = $this->readAttribute($fdn, $usernameAttribute);
460 460
 				$username = $username[0];
461 461
 			} else {
@@ -472,9 +472,9 @@  discard block
 block discarded – undo
472 472
 		// outside of core user management will still cache the user as non-existing.
473 473
 		$originalTTL = $this->connection->ldapCacheTTL;
474 474
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
475
-		if(($isUser && !\OCP\User::userExists($intName))
475
+		if (($isUser && !\OCP\User::userExists($intName))
476 476
 			|| (!$isUser && !\OC_Group::groupExists($intName))) {
477
-			if($mapper->map($fdn, $intName, $uuid)) {
477
+			if ($mapper->map($fdn, $intName, $uuid)) {
478 478
 				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
479 479
 				return $intName;
480 480
 			}
@@ -482,7 +482,7 @@  discard block
 block discarded – undo
482 482
 		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
483 483
 
484 484
 		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
485
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
485
+		if (is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
486 486
 			return $altName;
487 487
 		}
488 488
 
@@ -519,7 +519,7 @@  discard block
 block discarded – undo
519 519
 	 * @return array
520 520
 	 */
521 521
 	private function ldap2ownCloudNames($ldapObjects, $isUsers) {
522
-		if($isUsers) {
522
+		if ($isUsers) {
523 523
 			$nameAttribute = $this->connection->ldapUserDisplayName;
524 524
 			$sndAttribute  = $this->connection->ldapUserDisplayName2;
525 525
 		} else {
@@ -527,9 +527,9 @@  discard block
 block discarded – undo
527 527
 		}
528 528
 		$ownCloudNames = array();
529 529
 
530
-		foreach($ldapObjects as $ldapObject) {
530
+		foreach ($ldapObjects as $ldapObject) {
531 531
 			$nameByLDAP = null;
532
-			if(    isset($ldapObject[$nameAttribute])
532
+			if (isset($ldapObject[$nameAttribute])
533 533
 				&& is_array($ldapObject[$nameAttribute])
534 534
 				&& isset($ldapObject[$nameAttribute][0])
535 535
 			) {
@@ -538,12 +538,12 @@  discard block
 block discarded – undo
538 538
 			}
539 539
 
540 540
 			$ocName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
541
-			if($ocName) {
541
+			if ($ocName) {
542 542
 				$ownCloudNames[] = $ocName;
543
-				if($isUsers) {
543
+				if ($isUsers) {
544 544
 					//cache the user names so it does not need to be retrieved
545 545
 					//again later (e.g. sharing dialogue).
546
-					if(is_null($nameByLDAP)) {
546
+					if (is_null($nameByLDAP)) {
547 547
 						continue;
548 548
 					}
549 549
 					$sndName = isset($ldapObject[$sndAttribute][0])
@@ -598,9 +598,9 @@  discard block
 block discarded – undo
598 598
 		$attempts = 0;
599 599
 		//while loop is just a precaution. If a name is not generated within
600 600
 		//20 attempts, something else is very wrong. Avoids infinite loop.
601
-		while($attempts < 20){
602
-			$altName = $name . '_' . rand(1000,9999);
603
-			if(!\OCP\User::userExists($altName)) {
601
+		while ($attempts < 20) {
602
+			$altName = $name.'_'.rand(1000, 9999);
603
+			if (!\OCP\User::userExists($altName)) {
604 604
 				return $altName;
605 605
 			}
606 606
 			$attempts++;
@@ -622,25 +622,25 @@  discard block
 block discarded – undo
622 622
 	 */
623 623
 	private function _createAltInternalOwnCloudNameForGroups($name) {
624 624
 		$usedNames = $this->groupMapper->getNamesBySearch($name.'_%');
625
-		if(!($usedNames) || count($usedNames) === 0) {
625
+		if (!($usedNames) || count($usedNames) === 0) {
626 626
 			$lastNo = 1; //will become name_2
627 627
 		} else {
628 628
 			natsort($usedNames);
629 629
 			$lastName = array_pop($usedNames);
630 630
 			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
631 631
 		}
632
-		$altName = $name.'_'.strval($lastNo+1);
632
+		$altName = $name.'_'.strval($lastNo + 1);
633 633
 		unset($usedNames);
634 634
 
635 635
 		$attempts = 1;
636
-		while($attempts < 21){
636
+		while ($attempts < 21) {
637 637
 			// Check to be really sure it is unique
638 638
 			// while loop is just a precaution. If a name is not generated within
639 639
 			// 20 attempts, something else is very wrong. Avoids infinite loop.
640
-			if(!\OC_Group::groupExists($altName)) {
640
+			if (!\OC_Group::groupExists($altName)) {
641 641
 				return $altName;
642 642
 			}
643
-			$altName = $name . '_' . ($lastNo + $attempts);
643
+			$altName = $name.'_'.($lastNo + $attempts);
644 644
 			$attempts++;
645 645
 		}
646 646
 		return false;
@@ -655,7 +655,7 @@  discard block
 block discarded – undo
655 655
 	private function createAltInternalOwnCloudName($name, $isUser) {
656 656
 		$originalTTL = $this->connection->ldapCacheTTL;
657 657
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
658
-		if($isUser) {
658
+		if ($isUser) {
659 659
 			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
660 660
 		} else {
661 661
 			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
@@ -713,20 +713,20 @@  discard block
 block discarded – undo
713 713
 	 * and their values
714 714
 	 * @param array $ldapRecords
715 715
 	 */
716
-	public function batchApplyUserAttributes(array $ldapRecords){
716
+	public function batchApplyUserAttributes(array $ldapRecords) {
717 717
 		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
718
-		foreach($ldapRecords as $userRecord) {
719
-			if(!isset($userRecord[$displayNameAttribute])) {
718
+		foreach ($ldapRecords as $userRecord) {
719
+			if (!isset($userRecord[$displayNameAttribute])) {
720 720
 				// displayName is obligatory
721 721
 				continue;
722 722
 			}
723
-			$ocName  = $this->dn2ocname($userRecord['dn'][0]);
724
-			if($ocName === false) {
723
+			$ocName = $this->dn2ocname($userRecord['dn'][0]);
724
+			if ($ocName === false) {
725 725
 				continue;
726 726
 			}
727 727
 			$this->cacheUserExists($ocName);
728 728
 			$user = $this->userManager->get($ocName);
729
-			if($user instanceof OfflineUser) {
729
+			if ($user instanceof OfflineUser) {
730 730
 				$user->unmark();
731 731
 				$user = $this->userManager->get($ocName);
732 732
 			}
@@ -758,8 +758,8 @@  discard block
 block discarded – undo
758 758
 	 * @return array
759 759
 	 */
760 760
 	private function fetchList($list, $manyAttributes) {
761
-		if(is_array($list)) {
762
-			if($manyAttributes) {
761
+		if (is_array($list)) {
762
+			if ($manyAttributes) {
763 763
 				return $list;
764 764
 			} else {
765 765
 				$list = array_reduce($list, function($carry, $item) {
@@ -845,13 +845,13 @@  discard block
 block discarded – undo
845 845
 	 * second | false if not successful
846 846
 	 */
847 847
 	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
848
-		if(!is_null($attr) && !is_array($attr)) {
848
+		if (!is_null($attr) && !is_array($attr)) {
849 849
 			$attr = array(mb_strtolower($attr, 'UTF-8'));
850 850
 		}
851 851
 
852 852
 		// See if we have a resource, in case not cancel with message
853 853
 		$cr = $this->connection->getConnectionResource();
854
-		if(!$this->ldap->isResource($cr)) {
854
+		if (!$this->ldap->isResource($cr)) {
855 855
 			// Seems like we didn't find any resource.
856 856
 			// Return an empty array just like before.
857 857
 			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
@@ -864,7 +864,7 @@  discard block
 block discarded – undo
864 864
 		$linkResources = array_pad(array(), count($base), $cr);
865 865
 		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
866 866
 		$error = $this->ldap->errno($cr);
867
-		if(!is_array($sr) || $error !== 0) {
867
+		if (!is_array($sr) || $error !== 0) {
868 868
 			\OCP\Util::writeLog('user_ldap',
869 869
 				'Error when searching: '.$this->ldap->error($cr).
870 870
 					' code '.$this->ldap->errno($cr),
@@ -891,26 +891,26 @@  discard block
 block discarded – undo
891 891
 	 */
892 892
 	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
893 893
 		$cookie = null;
894
-		if($pagedSearchOK) {
894
+		if ($pagedSearchOK) {
895 895
 			$cr = $this->connection->getConnectionResource();
896
-			foreach($sr as $key => $res) {
897
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
896
+			foreach ($sr as $key => $res) {
897
+				if ($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
898 898
 					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
899 899
 				}
900 900
 			}
901 901
 
902 902
 			//browsing through prior pages to get the cookie for the new one
903
-			if($skipHandling) {
903
+			if ($skipHandling) {
904 904
 				return;
905 905
 			}
906 906
 			// if count is bigger, then the server does not support
907 907
 			// paged search. Instead, he did a normal search. We set a
908 908
 			// flag here, so the callee knows how to deal with it.
909
-			if($iFoundItems <= $limit) {
909
+			if ($iFoundItems <= $limit) {
910 910
 				$this->pagedSearchedSuccessful = true;
911 911
 			}
912 912
 		} else {
913
-			if(!is_null($limit)) {
913
+			if (!is_null($limit)) {
914 914
 				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
915 915
 			}
916 916
 		}
@@ -939,7 +939,7 @@  discard block
 block discarded – undo
939 939
 		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
940 940
 
941 941
 		$limitPerPage = intval($this->connection->ldapPagingSize);
942
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
942
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
943 943
 			$limitPerPage = $limit;
944 944
 		}
945 945
 
@@ -950,7 +950,7 @@  discard block
 block discarded – undo
950 950
 		do {
951 951
 			$search = $this->executeSearch($filter, $base, $attr,
952 952
 										   $limitPerPage, $offset);
953
-			if($search === false) {
953
+			if ($search === false) {
954 954
 				return $counter > 0 ? $counter : false;
955 955
 			}
956 956
 			list($sr, $pagedSearchOK) = $search;
@@ -969,7 +969,7 @@  discard block
 block discarded – undo
969 969
 			 * Continue now depends on $hasMorePages value
970 970
 			 */
971 971
 			$continue = $pagedSearchOK && $hasMorePages;
972
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
972
+		} while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
973 973
 
974 974
 		return $counter;
975 975
 	}
@@ -982,7 +982,7 @@  discard block
 block discarded – undo
982 982
 		$cr = $this->connection->getConnectionResource();
983 983
 		$counter = 0;
984 984
 
985
-		foreach($searchResults as $res) {
985
+		foreach ($searchResults as $res) {
986 986
 			$count = intval($this->ldap->countEntries($cr, $res));
987 987
 			$counter += $count;
988 988
 		}
@@ -1001,7 +1001,7 @@  discard block
 block discarded – undo
1001 1001
 	 * @return array with the search result
1002 1002
 	 */
1003 1003
 	private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1004
-		if($limit <= 0) {
1004
+		if ($limit <= 0) {
1005 1005
 			//otherwise search will fail
1006 1006
 			$limit = null;
1007 1007
 		}
@@ -1017,13 +1017,13 @@  discard block
 block discarded – undo
1017 1017
 		do {
1018 1018
 			$continue = false;
1019 1019
 			$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
1020
-			if($search === false) {
1020
+			if ($search === false) {
1021 1021
 				return array();
1022 1022
 			}
1023 1023
 			list($sr, $pagedSearchOK) = $search;
1024 1024
 			$cr = $this->connection->getConnectionResource();
1025 1025
 
1026
-			if($skipHandling) {
1026
+			if ($skipHandling) {
1027 1027
 				//i.e. result do not need to be fetched, we just need the cookie
1028 1028
 				//thus pass 1 or any other value as $iFoundItems because it is not
1029 1029
 				//used
@@ -1033,8 +1033,8 @@  discard block
 block discarded – undo
1033 1033
 				return array();
1034 1034
 			}
1035 1035
 
1036
-			foreach($sr as $res) {
1037
-				$findings = array_merge($findings, $this->ldap->getEntries($cr	, $res ));
1036
+			foreach ($sr as $res) {
1037
+				$findings = array_merge($findings, $this->ldap->getEntries($cr, $res));
1038 1038
 			}
1039 1039
 
1040 1040
 			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'],
@@ -1047,25 +1047,25 @@  discard block
 block discarded – undo
1047 1047
 
1048 1048
 		// if we're here, probably no connection resource is returned.
1049 1049
 		// to make ownCloud behave nicely, we simply give back an empty array.
1050
-		if(is_null($findings)) {
1050
+		if (is_null($findings)) {
1051 1051
 			return array();
1052 1052
 		}
1053 1053
 
1054
-		if(!is_null($attr)) {
1054
+		if (!is_null($attr)) {
1055 1055
 			$selection = array();
1056 1056
 			$i = 0;
1057
-			foreach($findings as $item) {
1058
-				if(!is_array($item)) {
1057
+			foreach ($findings as $item) {
1058
+				if (!is_array($item)) {
1059 1059
 					continue;
1060 1060
 				}
1061 1061
 				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1062
-				foreach($attr as $key) {
1062
+				foreach ($attr as $key) {
1063 1063
 					$key = mb_strtolower($key, 'UTF-8');
1064
-					if(isset($item[$key])) {
1065
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1064
+					if (isset($item[$key])) {
1065
+						if (is_array($item[$key]) && isset($item[$key]['count'])) {
1066 1066
 							unset($item[$key]['count']);
1067 1067
 						}
1068
-						if($key !== 'dn') {
1068
+						if ($key !== 'dn') {
1069 1069
 							$selection[$i][$key] = $this->resemblesDN($key) ?
1070 1070
 								$this->sanitizeDN($item[$key])
1071 1071
 								: $item[$key];
@@ -1082,7 +1082,7 @@  discard block
 block discarded – undo
1082 1082
 		//we slice the findings, when
1083 1083
 		//a) paged search unsuccessful, though attempted
1084 1084
 		//b) no paged search, but limit set
1085
-		if((!$this->getPagedSearchResultState()
1085
+		if ((!$this->getPagedSearchResultState()
1086 1086
 			&& $pagedSearchOK)
1087 1087
 			|| (
1088 1088
 				!$pagedSearchOK
@@ -1099,7 +1099,7 @@  discard block
 block discarded – undo
1099 1099
 	 * @return bool|mixed|string
1100 1100
 	 */
1101 1101
 	public function sanitizeUsername($name) {
1102
-		if($this->connection->ldapIgnoreNamingRules) {
1102
+		if ($this->connection->ldapIgnoreNamingRules) {
1103 1103
 			return $name;
1104 1104
 		}
1105 1105
 
@@ -1124,13 +1124,13 @@  discard block
 block discarded – undo
1124 1124
 	*/
1125 1125
 	public function escapeFilterPart($input, $allowAsterisk = false) {
1126 1126
 		$asterisk = '';
1127
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1127
+		if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1128 1128
 			$asterisk = '*';
1129 1129
 			$input = mb_substr($input, 1, null, 'UTF-8');
1130 1130
 		}
1131 1131
 		$search  = array('*', '\\', '(', ')');
1132 1132
 		$replace = array('\\*', '\\\\', '\\(', '\\)');
1133
-		return $asterisk . str_replace($search, $replace, $input);
1133
+		return $asterisk.str_replace($search, $replace, $input);
1134 1134
 	}
1135 1135
 
1136 1136
 	/**
@@ -1160,13 +1160,13 @@  discard block
 block discarded – undo
1160 1160
 	 */
1161 1161
 	private function combineFilter($filters, $operator) {
1162 1162
 		$combinedFilter = '('.$operator;
1163
-		foreach($filters as $filter) {
1164
-			if(!empty($filter) && $filter[0] !== '(') {
1163
+		foreach ($filters as $filter) {
1164
+			if (!empty($filter) && $filter[0] !== '(') {
1165 1165
 				$filter = '('.$filter.')';
1166 1166
 			}
1167
-			$combinedFilter.=$filter;
1167
+			$combinedFilter .= $filter;
1168 1168
 		}
1169
-		$combinedFilter.=')';
1169
+		$combinedFilter .= ')';
1170 1170
 		return $combinedFilter;
1171 1171
 	}
1172 1172
 
@@ -1202,17 +1202,17 @@  discard block
 block discarded – undo
1202 1202
 	 * @throws \Exception
1203 1203
 	 */
1204 1204
 	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1205
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1205
+		if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1206 1206
 			throw new \Exception('searchAttributes must be an array with at least two string');
1207 1207
 		}
1208 1208
 		$searchWords = explode(' ', trim($search));
1209 1209
 		$wordFilters = array();
1210
-		foreach($searchWords as $word) {
1210
+		foreach ($searchWords as $word) {
1211 1211
 			$word = $this->prepareSearchTerm($word);
1212 1212
 			//every word needs to appear at least once
1213 1213
 			$wordMatchOneAttrFilters = array();
1214
-			foreach($searchAttributes as $attr) {
1215
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1214
+			foreach ($searchAttributes as $attr) {
1215
+				$wordMatchOneAttrFilters[] = $attr.'='.$word;
1216 1216
 			}
1217 1217
 			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1218 1218
 		}
@@ -1230,10 +1230,10 @@  discard block
 block discarded – undo
1230 1230
 	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1231 1231
 		$filter = array();
1232 1232
 		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1233
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1233
+		if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1234 1234
 			try {
1235 1235
 				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1236
-			} catch(\Exception $e) {
1236
+			} catch (\Exception $e) {
1237 1237
 				\OCP\Util::writeLog(
1238 1238
 					'user_ldap',
1239 1239
 					'Creating advanced filter for search failed, falling back to simple method.',
@@ -1243,17 +1243,17 @@  discard block
 block discarded – undo
1243 1243
 		}
1244 1244
 
1245 1245
 		$search = $this->prepareSearchTerm($search);
1246
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1247
-			if(empty($fallbackAttribute)) {
1246
+		if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1247
+			if (empty($fallbackAttribute)) {
1248 1248
 				return '';
1249 1249
 			}
1250
-			$filter[] = $fallbackAttribute . '=' . $search;
1250
+			$filter[] = $fallbackAttribute.'='.$search;
1251 1251
 		} else {
1252
-			foreach($searchAttributes as $attribute) {
1253
-				$filter[] = $attribute . '=' . $search;
1252
+			foreach ($searchAttributes as $attribute) {
1253
+				$filter[] = $attribute.'='.$search;
1254 1254
 			}
1255 1255
 		}
1256
-		if(count($filter) === 1) {
1256
+		if (count($filter) === 1) {
1257 1257
 			return '('.$filter[0].')';
1258 1258
 		}
1259 1259
 		return $this->combineFilterWithOr($filter);
@@ -1270,8 +1270,7 @@  discard block
 block discarded – undo
1270 1270
 
1271 1271
 		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1272 1272
 
1273
-		$result = empty($term) ? '*' :
1274
-			$allowEnum !== 'no' ? $term . '*' : $term;
1273
+		$result = empty($term) ? '*' : $allowEnum !== 'no' ? $term.'*' : $term;
1275 1274
 		return $result;
1276 1275
 	}
1277 1276
 
@@ -1282,7 +1281,7 @@  discard block
 block discarded – undo
1282 1281
 	public function getFilterForUserCount() {
1283 1282
 		$filter = $this->combineFilterWithAnd(array(
1284 1283
 			$this->connection->ldapUserFilter,
1285
-			$this->connection->ldapUserDisplayName . '=*'
1284
+			$this->connection->ldapUserDisplayName.'=*'
1286 1285
 		));
1287 1286
 
1288 1287
 		return $filter;
@@ -1300,7 +1299,7 @@  discard block
 block discarded – undo
1300 1299
 			'ldapAgentName' => $name,
1301 1300
 			'ldapAgentPassword' => $password
1302 1301
 		);
1303
-		if(!$testConnection->setConfiguration($credentials)) {
1302
+		if (!$testConnection->setConfiguration($credentials)) {
1304 1303
 			return false;
1305 1304
 		}
1306 1305
 		return $testConnection->bind();
@@ -1318,34 +1317,34 @@  discard block
 block discarded – undo
1318 1317
 		$filter       = $this->connection->ldapUserFilter;
1319 1318
 		$base         = $this->connection->ldapBaseUsers;
1320 1319
 
1321
-		if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1320
+		if ($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) {
1322 1321
 			// Sacrebleu! The UUID attribute is unknown :( We need first an
1323 1322
 			// existing DN to be able to reliably detect it.
1324 1323
 			$result = $this->search($filter, $base, ['dn'], 1);
1325
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1324
+			if (!isset($result[0]) || !isset($result[0]['dn'])) {
1326 1325
 				throw new \Exception('Cannot determine UUID attribute');
1327 1326
 			}
1328 1327
 			$dn = $result[0]['dn'][0];
1329
-			if(!$this->detectUuidAttribute($dn, true)) {
1328
+			if (!$this->detectUuidAttribute($dn, true)) {
1330 1329
 				throw new \Exception('Cannot determine UUID attribute');
1331 1330
 			}
1332 1331
 		} else {
1333 1332
 			// The UUID attribute is either known or an override is given.
1334 1333
 			// By calling this method we ensure that $this->connection->$uuidAttr
1335 1334
 			// is definitely set
1336
-			if(!$this->detectUuidAttribute('', true)) {
1335
+			if (!$this->detectUuidAttribute('', true)) {
1337 1336
 				throw new \Exception('Cannot determine UUID attribute');
1338 1337
 			}
1339 1338
 		}
1340 1339
 
1341 1340
 		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1342
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1341
+		if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1343 1342
 			$uuid = $this->formatGuid2ForFilterUser($uuid);
1344 1343
 		}
1345 1344
 
1346
-		$filter = $uuidAttr . '=' . $uuid;
1345
+		$filter = $uuidAttr.'='.$uuid;
1347 1346
 		$result = $this->searchUsers($filter, ['dn'], 2);
1348
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1347
+		if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1349 1348
 			// we put the count into account to make sure that this is
1350 1349
 			// really unique
1351 1350
 			return $result[0]['dn'][0];
@@ -1362,7 +1361,7 @@  discard block
 block discarded – undo
1362 1361
 	 * @return bool true on success, false otherwise
1363 1362
 	 */
1364 1363
 	private function detectUuidAttribute($dn, $isUser = true, $force = false) {
1365
-		if($isUser) {
1364
+		if ($isUser) {
1366 1365
 			$uuidAttr     = 'ldapUuidUserAttribute';
1367 1366
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1368 1367
 		} else {
@@ -1370,11 +1369,11 @@  discard block
 block discarded – undo
1370 1369
 			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1371 1370
 		}
1372 1371
 
1373
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1372
+		if (($this->connection->$uuidAttr !== 'auto') && !$force) {
1374 1373
 			return true;
1375 1374
 		}
1376 1375
 
1377
-		if(!empty($uuidOverride) && !$force) {
1376
+		if (!empty($uuidOverride) && !$force) {
1378 1377
 			$this->connection->$uuidAttr = $uuidOverride;
1379 1378
 			return true;
1380 1379
 		}
@@ -1382,9 +1381,9 @@  discard block
 block discarded – undo
1382 1381
 		// for now, supported attributes are entryUUID, nsuniqueid, objectGUID, ipaUniqueID
1383 1382
 		$testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid');
1384 1383
 
1385
-		foreach($testAttributes as $attribute) {
1384
+		foreach ($testAttributes as $attribute) {
1386 1385
 			$value = $this->readAttribute($dn, $attribute);
1387
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1386
+			if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1388 1387
 				\OCP\Util::writeLog('user_ldap',
1389 1388
 									'Setting '.$attribute.' as '.$uuidAttr,
1390 1389
 									\OCP\Util::DEBUG);
@@ -1405,7 +1404,7 @@  discard block
 block discarded – undo
1405 1404
 	 * @return string|bool
1406 1405
 	 */
1407 1406
 	public function getUUID($dn, $isUser = true) {
1408
-		if($isUser) {
1407
+		if ($isUser) {
1409 1408
 			$uuidAttr     = 'ldapUuidUserAttribute';
1410 1409
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1411 1410
 		} else {
@@ -1414,15 +1413,15 @@  discard block
 block discarded – undo
1414 1413
 		}
1415 1414
 
1416 1415
 		$uuid = false;
1417
-		if($this->detectUuidAttribute($dn, $isUser)) {
1416
+		if ($this->detectUuidAttribute($dn, $isUser)) {
1418 1417
 			$uuid = $this->readAttribute($dn, $this->connection->$uuidAttr);
1419
-			if( !is_array($uuid)
1418
+			if (!is_array($uuid)
1420 1419
 				&& !empty($uuidOverride)
1421 1420
 				&& $this->detectUuidAttribute($dn, $isUser, true)) {
1422 1421
 					$uuid = $this->readAttribute($dn,
1423 1422
 												 $this->connection->$uuidAttr);
1424 1423
 			}
1425
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1424
+			if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1426 1425
 				$uuid = $uuid[0];
1427 1426
 			}
1428 1427
 		}
@@ -1439,19 +1438,19 @@  discard block
 block discarded – undo
1439 1438
 	private function convertObjectGUID2Str($oguid) {
1440 1439
 		$hex_guid = bin2hex($oguid);
1441 1440
 		$hex_guid_to_guid_str = '';
1442
-		for($k = 1; $k <= 4; ++$k) {
1441
+		for ($k = 1; $k <= 4; ++$k) {
1443 1442
 			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1444 1443
 		}
1445 1444
 		$hex_guid_to_guid_str .= '-';
1446
-		for($k = 1; $k <= 2; ++$k) {
1445
+		for ($k = 1; $k <= 2; ++$k) {
1447 1446
 			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1448 1447
 		}
1449 1448
 		$hex_guid_to_guid_str .= '-';
1450
-		for($k = 1; $k <= 2; ++$k) {
1449
+		for ($k = 1; $k <= 2; ++$k) {
1451 1450
 			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1452 1451
 		}
1453
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1454
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1452
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 16, 4);
1453
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 20);
1455 1454
 
1456 1455
 		return strtoupper($hex_guid_to_guid_str);
1457 1456
 	}
@@ -1468,11 +1467,11 @@  discard block
 block discarded – undo
1468 1467
 	 * @return string
1469 1468
 	 */
1470 1469
 	public function formatGuid2ForFilterUser($guid) {
1471
-		if(!is_string($guid)) {
1470
+		if (!is_string($guid)) {
1472 1471
 			throw new \InvalidArgumentException('String expected');
1473 1472
 		}
1474 1473
 		$blocks = explode('-', $guid);
1475
-		if(count($blocks) !== 5) {
1474
+		if (count($blocks) !== 5) {
1476 1475
 			/*
1477 1476
 			 * Why not throw an Exception instead? This method is a utility
1478 1477
 			 * called only when trying to figure out whether a "missing" known
@@ -1485,20 +1484,20 @@  discard block
 block discarded – undo
1485 1484
 			 * user. Instead we write a log message.
1486 1485
 			 */
1487 1486
 			\OC::$server->getLogger()->info(
1488
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1487
+				'Passed string does not resemble a valid GUID. Known UUID '.
1489 1488
 				'({uuid}) probably does not match UUID configuration.',
1490
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1489
+				['app' => 'user_ldap', 'uuid' => $guid]
1491 1490
 			);
1492 1491
 			return $guid;
1493 1492
 		}
1494
-		for($i=0; $i < 3; $i++) {
1493
+		for ($i = 0; $i < 3; $i++) {
1495 1494
 			$pairs = str_split($blocks[$i], 2);
1496 1495
 			$pairs = array_reverse($pairs);
1497 1496
 			$blocks[$i] = implode('', $pairs);
1498 1497
 		}
1499
-		for($i=0; $i < 5; $i++) {
1498
+		for ($i = 0; $i < 5; $i++) {
1500 1499
 			$pairs = str_split($blocks[$i], 2);
1501
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1500
+			$blocks[$i] = '\\'.implode('\\', $pairs);
1502 1501
 		}
1503 1502
 		return implode('', $blocks);
1504 1503
 	}
@@ -1512,12 +1511,12 @@  discard block
 block discarded – undo
1512 1511
 		$domainDN = $this->getDomainDNFromDN($dn);
1513 1512
 		$cacheKey = 'getSID-'.$domainDN;
1514 1513
 		$sid = $this->connection->getFromCache($cacheKey);
1515
-		if(!is_null($sid)) {
1514
+		if (!is_null($sid)) {
1516 1515
 			return $sid;
1517 1516
 		}
1518 1517
 
1519 1518
 		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1520
-		if(!is_array($objectSid) || empty($objectSid)) {
1519
+		if (!is_array($objectSid) || empty($objectSid)) {
1521 1520
 			$this->connection->writeToCache($cacheKey, false);
1522 1521
 			return false;
1523 1522
 		}
@@ -1584,12 +1583,12 @@  discard block
 block discarded – undo
1584 1583
 		$belongsToBase = false;
1585 1584
 		$bases = $this->sanitizeDN($bases);
1586 1585
 
1587
-		foreach($bases as $base) {
1586
+		foreach ($bases as $base) {
1588 1587
 			$belongsToBase = true;
1589
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1588
+			if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1590 1589
 				$belongsToBase = false;
1591 1590
 			}
1592
-			if($belongsToBase) {
1591
+			if ($belongsToBase) {
1593 1592
 				break;
1594 1593
 			}
1595 1594
 		}
@@ -1600,7 +1599,7 @@  discard block
 block discarded – undo
1600 1599
 	 * resets a running Paged Search operation
1601 1600
 	 */
1602 1601
 	private function abandonPagedSearch() {
1603
-		if($this->connection->hasPagedResultSupport) {
1602
+		if ($this->connection->hasPagedResultSupport) {
1604 1603
 			$cr = $this->connection->getConnectionResource();
1605 1604
 			$this->ldap->controlPagedResult($cr, 0, false, $this->lastCookie);
1606 1605
 			$this->getPagedSearchResultState();
@@ -1618,16 +1617,16 @@  discard block
 block discarded – undo
1618 1617
 	 * @return string containing the key or empty if none is cached
1619 1618
 	 */
1620 1619
 	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1621
-		if($offset === 0) {
1620
+		if ($offset === 0) {
1622 1621
 			return '';
1623 1622
 		}
1624 1623
 		$offset -= $limit;
1625 1624
 		//we work with cache here
1626
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1625
+		$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1627 1626
 		$cookie = '';
1628
-		if(isset($this->cookies[$cacheKey])) {
1627
+		if (isset($this->cookies[$cacheKey])) {
1629 1628
 			$cookie = $this->cookies[$cacheKey];
1630
-			if(is_null($cookie)) {
1629
+			if (is_null($cookie)) {
1631 1630
 				$cookie = '';
1632 1631
 			}
1633 1632
 		}
@@ -1645,11 +1644,11 @@  discard block
 block discarded – undo
1645 1644
 	 * @return bool
1646 1645
 	 */
1647 1646
 	public function hasMoreResults() {
1648
-		if(!$this->connection->hasPagedResultSupport) {
1647
+		if (!$this->connection->hasPagedResultSupport) {
1649 1648
 			return false;
1650 1649
 		}
1651 1650
 
1652
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1651
+		if (empty($this->lastCookie) && $this->lastCookie !== '0') {
1653 1652
 			// as in RFC 2696, when all results are returned, the cookie will
1654 1653
 			// be empty.
1655 1654
 			return false;
@@ -1669,8 +1668,8 @@  discard block
 block discarded – undo
1669 1668
 	 */
1670 1669
 	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1671 1670
 		// allow '0' for 389ds
1672
-		if(!empty($cookie) || $cookie === '0') {
1673
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1671
+		if (!empty($cookie) || $cookie === '0') {
1672
+			$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1674 1673
 			$this->cookies[$cacheKey] = $cookie;
1675 1674
 			$this->lastCookie = $cookie;
1676 1675
 		}
@@ -1697,17 +1696,17 @@  discard block
 block discarded – undo
1697 1696
 	 */
1698 1697
 	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1699 1698
 		$pagedSearchOK = false;
1700
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1699
+		if ($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1701 1700
 			$offset = intval($offset); //can be null
1702 1701
 			\OCP\Util::writeLog('user_ldap',
1703 1702
 				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1704
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1703
+				.' attr '.print_r($attr, true).' limit '.$limit.' offset '.$offset,
1705 1704
 				\OCP\Util::DEBUG);
1706 1705
 			//get the cookie from the search for the previous search, required by LDAP
1707
-			foreach($bases as $base) {
1706
+			foreach ($bases as $base) {
1708 1707
 
1709 1708
 				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1710
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1709
+				if (empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1711 1710
 					// no cookie known, although the offset is not 0. Maybe cache run out. We need
1712 1711
 					// to start all over *sigh* (btw, Dear Reader, did you know LDAP paged
1713 1712
 					// searching was designed by MSFT?)
@@ -1720,18 +1719,18 @@  discard block
 block discarded – undo
1720 1719
 					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1721 1720
 					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1722 1721
 					//TODO: remember this, probably does not change in the next request...
1723
-					if(empty($cookie) && $cookie !== '0') {
1722
+					if (empty($cookie) && $cookie !== '0') {
1724 1723
 						// '0' is valid, because 389ds
1725 1724
 						$cookie = null;
1726 1725
 					}
1727 1726
 				}
1728
-				if(!is_null($cookie)) {
1727
+				if (!is_null($cookie)) {
1729 1728
 					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1730 1729
 					$this->abandonPagedSearch();
1731 1730
 					$pagedSearchOK = $this->ldap->controlPagedResult(
1732 1731
 						$this->connection->getConnectionResource(), $limit,
1733 1732
 						false, $cookie);
1734
-					if(!$pagedSearchOK) {
1733
+					if (!$pagedSearchOK) {
1735 1734
 						return false;
1736 1735
 					}
1737 1736
 					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
@@ -1748,7 +1747,7 @@  discard block
 block discarded – undo
1748 1747
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1749 1748
 		 * if we don't have a previous paged search.
1750 1749
 		 */
1751
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1750
+		} else if ($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1752 1751
 			// a search without limit was requested. However, if we do use
1753 1752
 			// Paged Search once, we always must do it. This requires us to
1754 1753
 			// 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   +1315 added lines, -1315 removed lines patch added patch discarded remove patch
@@ -35,1321 +35,1321 @@
 block discarded – undo
35 35
 use OC\ServerNotAvailableException;
36 36
 
37 37
 class Wizard extends LDAPUtility {
38
-	static protected $l;
39
-	protected $access;
40
-	protected $cr;
41
-	protected $configuration;
42
-	protected $result;
43
-	protected $resultCache = array();
44
-
45
-	const LRESULT_PROCESSED_OK = 2;
46
-	const LRESULT_PROCESSED_INVALID = 3;
47
-	const LRESULT_PROCESSED_SKIP = 4;
48
-
49
-	const LFILTER_LOGIN      = 2;
50
-	const LFILTER_USER_LIST  = 3;
51
-	const LFILTER_GROUP_LIST = 4;
52
-
53
-	const LFILTER_MODE_ASSISTED = 2;
54
-	const LFILTER_MODE_RAW = 1;
55
-
56
-	const LDAP_NW_TIMEOUT = 4;
57
-
58
-	/**
59
-	 * Constructor
60
-	 * @param Configuration $configuration an instance of Configuration
61
-	 * @param ILDAPWrapper $ldap an instance of ILDAPWrapper
62
-	 */
63
-	public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
64
-		parent::__construct($ldap);
65
-		$this->configuration = $configuration;
66
-		if(is_null(Wizard::$l)) {
67
-			Wizard::$l = \OC::$server->getL10N('user_ldap');
68
-		}
69
-		$this->access = $access;
70
-		$this->result = new WizardResult();
71
-	}
72
-
73
-	public function  __destruct() {
74
-		if($this->result->hasChanges()) {
75
-			$this->configuration->saveConfiguration();
76
-		}
77
-	}
78
-
79
-	/**
80
-	 * counts entries in the LDAP directory
81
-	 *
82
-	 * @param string $filter the LDAP search filter
83
-	 * @param string $type a string being either 'users' or 'groups';
84
-	 * @return bool|int
85
-	 * @throws \Exception
86
-	 */
87
-	public function countEntries($filter, $type) {
88
-		$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
89
-		if($type === 'users') {
90
-			$reqs[] = 'ldapUserFilter';
91
-		}
92
-		if(!$this->checkRequirements($reqs)) {
93
-			throw new \Exception('Requirements not met', 400);
94
-		}
95
-
96
-		$attr = array('dn'); // default
97
-		$limit = 1001;
98
-		if($type === 'groups') {
99
-			$result =  $this->access->countGroups($filter, $attr, $limit);
100
-		} else if($type === 'users') {
101
-			$result = $this->access->countUsers($filter, $attr, $limit);
102
-		} else if ($type === 'objects') {
103
-			$result = $this->access->countObjects($limit);
104
-		} else {
105
-			throw new \Exception('internal error: invalid object type', 500);
106
-		}
107
-
108
-		return $result;
109
-	}
110
-
111
-	/**
112
-	 * formats the return value of a count operation to the string to be
113
-	 * inserted.
114
-	 *
115
-	 * @param bool|int $count
116
-	 * @return int|string
117
-	 */
118
-	private function formatCountResult($count) {
119
-		$formatted = ($count !== false) ? $count : 0;
120
-		if($formatted > 1000) {
121
-			$formatted = '> 1000';
122
-		}
123
-		return $formatted;
124
-	}
125
-
126
-	public function countGroups() {
127
-		$filter = $this->configuration->ldapGroupFilter;
128
-
129
-		if(empty($filter)) {
130
-			$output = self::$l->n('%s group found', '%s groups found', 0, array(0));
131
-			$this->result->addChange('ldap_group_count', $output);
132
-			return $this->result;
133
-		}
134
-
135
-		try {
136
-			$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
137
-		} catch (\Exception $e) {
138
-			//400 can be ignored, 500 is forwarded
139
-			if($e->getCode() === 500) {
140
-				throw $e;
141
-			}
142
-			return false;
143
-		}
144
-		$output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
145
-		$this->result->addChange('ldap_group_count', $output);
146
-		return $this->result;
147
-	}
148
-
149
-	/**
150
-	 * @return WizardResult
151
-	 * @throws \Exception
152
-	 */
153
-	public function countUsers() {
154
-		$filter = $this->access->getFilterForUserCount();
155
-
156
-		$usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
157
-		$output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
158
-		$this->result->addChange('ldap_user_count', $output);
159
-		return $this->result;
160
-	}
161
-
162
-	/**
163
-	 * counts any objects in the currently set base dn
164
-	 *
165
-	 * @return WizardResult
166
-	 * @throws \Exception
167
-	 */
168
-	public function countInBaseDN() {
169
-		// we don't need to provide a filter in this case
170
-		$total = $this->countEntries(null, 'objects');
171
-		if($total === false) {
172
-			throw new \Exception('invalid results received');
173
-		}
174
-		$this->result->addChange('ldap_test_base', $total);
175
-		return $this->result;
176
-	}
177
-
178
-	/**
179
-	 * counts users with a specified attribute
180
-	 * @param string $attr
181
-	 * @param bool $existsCheck
182
-	 * @return int|bool
183
-	 */
184
-	public function countUsersWithAttribute($attr, $existsCheck = false) {
185
-		if(!$this->checkRequirements(array('ldapHost',
186
-										   'ldapPort',
187
-										   'ldapBase',
188
-										   'ldapUserFilter',
189
-										   ))) {
190
-			return  false;
191
-		}
192
-
193
-		$filter = $this->access->combineFilterWithAnd(array(
194
-			$this->configuration->ldapUserFilter,
195
-			$attr . '=*'
196
-		));
197
-
198
-		$limit = ($existsCheck === false) ? null : 1;
199
-
200
-		return $this->access->countUsers($filter, array('dn'), $limit);
201
-	}
202
-
203
-	/**
204
-	 * detects the display name attribute. If a setting is already present that
205
-	 * returns at least one hit, the detection will be canceled.
206
-	 * @return WizardResult|bool
207
-	 * @throws \Exception
208
-	 */
209
-	public function detectUserDisplayNameAttribute() {
210
-		if(!$this->checkRequirements(array('ldapHost',
211
-										'ldapPort',
212
-										'ldapBase',
213
-										'ldapUserFilter',
214
-										))) {
215
-			return  false;
216
-		}
217
-
218
-		$attr = $this->configuration->ldapUserDisplayName;
219
-		if($attr !== 'displayName' && !empty($attr)) {
220
-			// most likely not the default value with upper case N,
221
-			// verify it still produces a result
222
-			$count = intval($this->countUsersWithAttribute($attr, true));
223
-			if($count > 0) {
224
-				//no change, but we sent it back to make sure the user interface
225
-				//is still correct, even if the ajax call was cancelled inbetween
226
-				$this->result->addChange('ldap_display_name', $attr);
227
-				return $this->result;
228
-			}
229
-		}
230
-
231
-		// first attribute that has at least one result wins
232
-		$displayNameAttrs = array('displayname', 'cn');
233
-		foreach ($displayNameAttrs as $attr) {
234
-			$count = intval($this->countUsersWithAttribute($attr, true));
235
-
236
-			if($count > 0) {
237
-				$this->applyFind('ldap_display_name', $attr);
238
-				return $this->result;
239
-			}
240
-		};
241
-
242
-		throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced ldap settings.'));
243
-	}
244
-
245
-	/**
246
-	 * detects the most often used email attribute for users applying to the
247
-	 * user list filter. If a setting is already present that returns at least
248
-	 * one hit, the detection will be canceled.
249
-	 * @return WizardResult|bool
250
-	 */
251
-	public function detectEmailAttribute() {
252
-		if(!$this->checkRequirements(array('ldapHost',
253
-										   'ldapPort',
254
-										   'ldapBase',
255
-										   'ldapUserFilter',
256
-										   ))) {
257
-			return  false;
258
-		}
259
-
260
-		$attr = $this->configuration->ldapEmailAttribute;
261
-		if(!empty($attr)) {
262
-			$count = intval($this->countUsersWithAttribute($attr, true));
263
-			if($count > 0) {
264
-				return false;
265
-			}
266
-			$writeLog = true;
267
-		} else {
268
-			$writeLog = false;
269
-		}
270
-
271
-		$emailAttributes = array('mail', 'mailPrimaryAddress');
272
-		$winner = '';
273
-		$maxUsers = 0;
274
-		foreach($emailAttributes as $attr) {
275
-			$count = $this->countUsersWithAttribute($attr);
276
-			if($count > $maxUsers) {
277
-				$maxUsers = $count;
278
-				$winner = $attr;
279
-			}
280
-		}
281
-
282
-		if($winner !== '') {
283
-			$this->applyFind('ldap_email_attr', $winner);
284
-			if($writeLog) {
285
-				\OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
286
-					'automatically been reset, because the original value ' .
287
-					'did not return any results.', \OCP\Util::INFO);
288
-			}
289
-		}
290
-
291
-		return $this->result;
292
-	}
293
-
294
-	/**
295
-	 * @return WizardResult
296
-	 * @throws \Exception
297
-	 */
298
-	public function determineAttributes() {
299
-		if(!$this->checkRequirements(array('ldapHost',
300
-										   'ldapPort',
301
-										   'ldapBase',
302
-										   'ldapUserFilter',
303
-										   ))) {
304
-			return  false;
305
-		}
306
-
307
-		$attributes = $this->getUserAttributes();
308
-
309
-		natcasesort($attributes);
310
-		$attributes = array_values($attributes);
311
-
312
-		$this->result->addOptions('ldap_loginfilter_attributes', $attributes);
313
-
314
-		$selected = $this->configuration->ldapLoginFilterAttributes;
315
-		if(is_array($selected) && !empty($selected)) {
316
-			$this->result->addChange('ldap_loginfilter_attributes', $selected);
317
-		}
318
-
319
-		return $this->result;
320
-	}
321
-
322
-	/**
323
-	 * detects the available LDAP attributes
324
-	 * @return array|false The instance's WizardResult instance
325
-	 * @throws \Exception
326
-	 */
327
-	private function getUserAttributes() {
328
-		if(!$this->checkRequirements(array('ldapHost',
329
-										   'ldapPort',
330
-										   'ldapBase',
331
-										   'ldapUserFilter',
332
-										   ))) {
333
-			return  false;
334
-		}
335
-		$cr = $this->getConnection();
336
-		if(!$cr) {
337
-			throw new \Exception('Could not connect to LDAP');
338
-		}
339
-
340
-		$base = $this->configuration->ldapBase[0];
341
-		$filter = $this->configuration->ldapUserFilter;
342
-		$rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
343
-		if(!$this->ldap->isResource($rr)) {
344
-			return false;
345
-		}
346
-		$er = $this->ldap->firstEntry($cr, $rr);
347
-		$attributes = $this->ldap->getAttributes($cr, $er);
348
-		$pureAttributes = array();
349
-		for($i = 0; $i < $attributes['count']; $i++) {
350
-			$pureAttributes[] = $attributes[$i];
351
-		}
352
-
353
-		return $pureAttributes;
354
-	}
355
-
356
-	/**
357
-	 * detects the available LDAP groups
358
-	 * @return WizardResult|false the instance's WizardResult instance
359
-	 */
360
-	public function determineGroupsForGroups() {
361
-		return $this->determineGroups('ldap_groupfilter_groups',
362
-									  'ldapGroupFilterGroups',
363
-									  false);
364
-	}
365
-
366
-	/**
367
-	 * detects the available LDAP groups
368
-	 * @return WizardResult|false the instance's WizardResult instance
369
-	 */
370
-	public function determineGroupsForUsers() {
371
-		return $this->determineGroups('ldap_userfilter_groups',
372
-									  'ldapUserFilterGroups');
373
-	}
374
-
375
-	/**
376
-	 * detects the available LDAP groups
377
-	 * @param string $dbKey
378
-	 * @param string $confKey
379
-	 * @param bool $testMemberOf
380
-	 * @return WizardResult|false the instance's WizardResult instance
381
-	 * @throws \Exception
382
-	 */
383
-	private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
384
-		if(!$this->checkRequirements(array('ldapHost',
385
-										   'ldapPort',
386
-										   'ldapBase',
387
-										   ))) {
388
-			return  false;
389
-		}
390
-		$cr = $this->getConnection();
391
-		if(!$cr) {
392
-			throw new \Exception('Could not connect to LDAP');
393
-		}
394
-
395
-		$this->fetchGroups($dbKey, $confKey);
396
-
397
-		if($testMemberOf) {
398
-			$this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
399
-			$this->result->markChange();
400
-			if(!$this->configuration->hasMemberOfFilterSupport) {
401
-				throw new \Exception('memberOf is not supported by the server');
402
-			}
403
-		}
404
-
405
-		return $this->result;
406
-	}
407
-
408
-	/**
409
-	 * fetches all groups from LDAP and adds them to the result object
410
-	 *
411
-	 * @param string $dbKey
412
-	 * @param string $confKey
413
-	 * @return array $groupEntries
414
-	 * @throws \Exception
415
-	 */
416
-	public function fetchGroups($dbKey, $confKey) {
417
-		$obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames');
418
-
419
-		$filterParts = array();
420
-		foreach($obclasses as $obclass) {
421
-			$filterParts[] = 'objectclass='.$obclass;
422
-		}
423
-		//we filter for everything
424
-		//- that looks like a group and
425
-		//- has the group display name set
426
-		$filter = $this->access->combineFilterWithOr($filterParts);
427
-		$filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*'));
428
-
429
-		$groupNames = array();
430
-		$groupEntries = array();
431
-		$limit = 400;
432
-		$offset = 0;
433
-		do {
434
-			// we need to request dn additionally here, otherwise memberOf
435
-			// detection will fail later
436
-			$result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
437
-			foreach($result as $item) {
438
-				if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
439
-					// just in case - no issue known
440
-					continue;
441
-				}
442
-				$groupNames[] = $item['cn'][0];
443
-				$groupEntries[] = $item;
444
-			}
445
-			$offset += $limit;
446
-		} while ($this->access->hasMoreResults());
447
-
448
-		if(count($groupNames) > 0) {
449
-			natsort($groupNames);
450
-			$this->result->addOptions($dbKey, array_values($groupNames));
451
-		} else {
452
-			throw new \Exception(self::$l->t('Could not find the desired feature'));
453
-		}
454
-
455
-		$setFeatures = $this->configuration->$confKey;
456
-		if(is_array($setFeatures) && !empty($setFeatures)) {
457
-			//something is already configured? pre-select it.
458
-			$this->result->addChange($dbKey, $setFeatures);
459
-		}
460
-		return $groupEntries;
461
-	}
462
-
463
-	public function determineGroupMemberAssoc() {
464
-		if(!$this->checkRequirements(array('ldapHost',
465
-										   'ldapPort',
466
-										   'ldapGroupFilter',
467
-										   ))) {
468
-			return  false;
469
-		}
470
-		$attribute = $this->detectGroupMemberAssoc();
471
-		if($attribute === false) {
472
-			return false;
473
-		}
474
-		$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
475
-		$this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
476
-
477
-		return $this->result;
478
-	}
479
-
480
-	/**
481
-	 * Detects the available object classes
482
-	 * @return WizardResult|false the instance's WizardResult instance
483
-	 * @throws \Exception
484
-	 */
485
-	public function determineGroupObjectClasses() {
486
-		if(!$this->checkRequirements(array('ldapHost',
487
-										   'ldapPort',
488
-										   'ldapBase',
489
-										   ))) {
490
-			return  false;
491
-		}
492
-		$cr = $this->getConnection();
493
-		if(!$cr) {
494
-			throw new \Exception('Could not connect to LDAP');
495
-		}
496
-
497
-		$obclasses = array('groupOfNames', 'group', 'posixGroup', '*');
498
-		$this->determineFeature($obclasses,
499
-								'objectclass',
500
-								'ldap_groupfilter_objectclass',
501
-								'ldapGroupFilterObjectclass',
502
-								false);
503
-
504
-		return $this->result;
505
-	}
506
-
507
-	/**
508
-	 * detects the available object classes
509
-	 * @return WizardResult
510
-	 * @throws \Exception
511
-	 */
512
-	public function determineUserObjectClasses() {
513
-		if(!$this->checkRequirements(array('ldapHost',
514
-										   'ldapPort',
515
-										   'ldapBase',
516
-										   ))) {
517
-			return  false;
518
-		}
519
-		$cr = $this->getConnection();
520
-		if(!$cr) {
521
-			throw new \Exception('Could not connect to LDAP');
522
-		}
523
-
524
-		$obclasses = array('inetOrgPerson', 'person', 'organizationalPerson',
525
-						   'user', 'posixAccount', '*');
526
-		$filter = $this->configuration->ldapUserFilter;
527
-		//if filter is empty, it is probably the first time the wizard is called
528
-		//then, apply suggestions.
529
-		$this->determineFeature($obclasses,
530
-								'objectclass',
531
-								'ldap_userfilter_objectclass',
532
-								'ldapUserFilterObjectclass',
533
-								empty($filter));
534
-
535
-		return $this->result;
536
-	}
537
-
538
-	/**
539
-	 * @return WizardResult|false
540
-	 * @throws \Exception
541
-	 */
542
-	public function getGroupFilter() {
543
-		if(!$this->checkRequirements(array('ldapHost',
544
-										   'ldapPort',
545
-										   'ldapBase',
546
-										   ))) {
547
-			return false;
548
-		}
549
-		//make sure the use display name is set
550
-		$displayName = $this->configuration->ldapGroupDisplayName;
551
-		if(empty($displayName)) {
552
-			$d = $this->configuration->getDefaults();
553
-			$this->applyFind('ldap_group_display_name',
554
-							 $d['ldap_group_display_name']);
555
-		}
556
-		$filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST);
557
-
558
-		$this->applyFind('ldap_group_filter', $filter);
559
-		return $this->result;
560
-	}
561
-
562
-	/**
563
-	 * @return WizardResult|false
564
-	 * @throws \Exception
565
-	 */
566
-	public function getUserListFilter() {
567
-		if(!$this->checkRequirements(array('ldapHost',
568
-										   'ldapPort',
569
-										   'ldapBase',
570
-										   ))) {
571
-			return false;
572
-		}
573
-		//make sure the use display name is set
574
-		$displayName = $this->configuration->ldapUserDisplayName;
575
-		if(empty($displayName)) {
576
-			$d = $this->configuration->getDefaults();
577
-			$this->applyFind('ldap_display_name', $d['ldap_display_name']);
578
-		}
579
-		$filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
580
-		if(!$filter) {
581
-			throw new \Exception('Cannot create filter');
582
-		}
583
-
584
-		$this->applyFind('ldap_userlist_filter', $filter);
585
-		return $this->result;
586
-	}
587
-
588
-	/**
589
-	 * @return bool|WizardResult
590
-	 * @throws \Exception
591
-	 */
592
-	public function getUserLoginFilter() {
593
-		if(!$this->checkRequirements(array('ldapHost',
594
-										   'ldapPort',
595
-										   'ldapBase',
596
-										   'ldapUserFilter',
597
-										   ))) {
598
-			return false;
599
-		}
600
-
601
-		$filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
602
-		if(!$filter) {
603
-			throw new \Exception('Cannot create filter');
604
-		}
605
-
606
-		$this->applyFind('ldap_login_filter', $filter);
607
-		return $this->result;
608
-	}
609
-
610
-	/**
611
-	 * @return bool|WizardResult
612
-	 * @param string $loginName
613
-	 * @throws \Exception
614
-	 */
615
-	public function testLoginName($loginName) {
616
-		if(!$this->checkRequirements(array('ldapHost',
617
-			'ldapPort',
618
-			'ldapBase',
619
-			'ldapLoginFilter',
620
-		))) {
621
-			return false;
622
-		}
623
-
624
-		$cr = $this->access->connection->getConnectionResource();
625
-		if(!$this->ldap->isResource($cr)) {
626
-			throw new \Exception('connection error');
627
-		}
628
-
629
-		if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
630
-			=== false) {
631
-			throw new \Exception('missing placeholder');
632
-		}
633
-
634
-		$users = $this->access->countUsersByLoginName($loginName);
635
-		if($this->ldap->errno($cr) !== 0) {
636
-			throw new \Exception($this->ldap->error($cr));
637
-		}
638
-		$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
639
-		$this->result->addChange('ldap_test_loginname', $users);
640
-		$this->result->addChange('ldap_test_effective_filter', $filter);
641
-		return $this->result;
642
-	}
643
-
644
-	/**
645
-	 * Tries to determine the port, requires given Host, User DN and Password
646
-	 * @return WizardResult|false WizardResult on success, false otherwise
647
-	 * @throws \Exception
648
-	 */
649
-	public function guessPortAndTLS() {
650
-		if(!$this->checkRequirements(array('ldapHost',
651
-										   ))) {
652
-			return false;
653
-		}
654
-		$this->checkHost();
655
-		$portSettings = $this->getPortSettingsToTry();
656
-
657
-		if(!is_array($portSettings)) {
658
-			throw new \Exception(print_r($portSettings, true));
659
-		}
660
-
661
-		//proceed from the best configuration and return on first success
662
-		foreach($portSettings as $setting) {
663
-			$p = $setting['port'];
664
-			$t = $setting['tls'];
665
-			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
666
-			//connectAndBind may throw Exception, it needs to be catched by the
667
-			//callee of this method
668
-
669
-			try {
670
-				$settingsFound = $this->connectAndBind($p, $t);
671
-			} catch (\Exception $e) {
672
-				// any reply other than -1 (= cannot connect) is already okay,
673
-				// because then we found the server
674
-				// unavailable startTLS returns -11
675
-				if($e->getCode() > 0) {
676
-					$settingsFound = true;
677
-				} else {
678
-					throw $e;
679
-				}
680
-			}
681
-
682
-			if ($settingsFound === true) {
683
-				$config = array(
684
-					'ldapPort' => $p,
685
-					'ldapTLS' => intval($t)
686
-				);
687
-				$this->configuration->setConfiguration($config);
688
-				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
689
-				$this->result->addChange('ldap_port', $p);
690
-				return $this->result;
691
-			}
692
-		}
693
-
694
-		//custom port, undetected (we do not brute force)
695
-		return false;
696
-	}
697
-
698
-	/**
699
-	 * tries to determine a base dn from User DN or LDAP Host
700
-	 * @return WizardResult|false WizardResult on success, false otherwise
701
-	 */
702
-	public function guessBaseDN() {
703
-		if(!$this->checkRequirements(array('ldapHost',
704
-										   'ldapPort',
705
-										   ))) {
706
-			return false;
707
-		}
708
-
709
-		//check whether a DN is given in the agent name (99.9% of all cases)
710
-		$base = null;
711
-		$i = stripos($this->configuration->ldapAgentName, 'dc=');
712
-		if($i !== false) {
713
-			$base = substr($this->configuration->ldapAgentName, $i);
714
-			if($this->testBaseDN($base)) {
715
-				$this->applyFind('ldap_base', $base);
716
-				return $this->result;
717
-			}
718
-		}
719
-
720
-		//this did not help :(
721
-		//Let's see whether we can parse the Host URL and convert the domain to
722
-		//a base DN
723
-		$helper = new Helper();
724
-		$domain = $helper->getDomainFromURL($this->configuration->ldapHost);
725
-		if(!$domain) {
726
-			return false;
727
-		}
728
-
729
-		$dparts = explode('.', $domain);
730
-		while(count($dparts) > 0) {
731
-			$base2 = 'dc=' . implode(',dc=', $dparts);
732
-			if ($base !== $base2 && $this->testBaseDN($base2)) {
733
-				$this->applyFind('ldap_base', $base2);
734
-				return $this->result;
735
-			}
736
-			array_shift($dparts);
737
-		}
738
-
739
-		return false;
740
-	}
741
-
742
-	/**
743
-	 * sets the found value for the configuration key in the WizardResult
744
-	 * as well as in the Configuration instance
745
-	 * @param string $key the configuration key
746
-	 * @param string $value the (detected) value
747
-	 *
748
-	 */
749
-	private function applyFind($key, $value) {
750
-		$this->result->addChange($key, $value);
751
-		$this->configuration->setConfiguration(array($key => $value));
752
-	}
753
-
754
-	/**
755
-	 * Checks, whether a port was entered in the Host configuration
756
-	 * field. In this case the port will be stripped off, but also stored as
757
-	 * setting.
758
-	 */
759
-	private function checkHost() {
760
-		$host = $this->configuration->ldapHost;
761
-		$hostInfo = parse_url($host);
762
-
763
-		//removes Port from Host
764
-		if(is_array($hostInfo) && isset($hostInfo['port'])) {
765
-			$port = $hostInfo['port'];
766
-			$host = str_replace(':'.$port, '', $host);
767
-			$this->applyFind('ldap_host', $host);
768
-			$this->applyFind('ldap_port', $port);
769
-		}
770
-	}
771
-
772
-	/**
773
-	 * tries to detect the group member association attribute which is
774
-	 * one of 'uniqueMember', 'memberUid', 'member'
775
-	 * @return string|false, string with the attribute name, false on error
776
-	 * @throws \Exception
777
-	 */
778
-	private function detectGroupMemberAssoc() {
779
-		$possibleAttrs = array('uniqueMember', 'memberUid', 'member');
780
-		$filter = $this->configuration->ldapGroupFilter;
781
-		if(empty($filter)) {
782
-			return false;
783
-		}
784
-		$cr = $this->getConnection();
785
-		if(!$cr) {
786
-			throw new \Exception('Could not connect to LDAP');
787
-		}
788
-		$base = $this->configuration->ldapBase[0];
789
-		$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
790
-		if(!$this->ldap->isResource($rr)) {
791
-			return false;
792
-		}
793
-		$er = $this->ldap->firstEntry($cr, $rr);
794
-		while(is_resource($er)) {
795
-			$this->ldap->getDN($cr, $er);
796
-			$attrs = $this->ldap->getAttributes($cr, $er);
797
-			$result = array();
798
-			$possibleAttrsCount = count($possibleAttrs);
799
-			for($i = 0; $i < $possibleAttrsCount; $i++) {
800
-				if(isset($attrs[$possibleAttrs[$i]])) {
801
-					$result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
802
-				}
803
-			}
804
-			if(!empty($result)) {
805
-				natsort($result);
806
-				return key($result);
807
-			}
808
-
809
-			$er = $this->ldap->nextEntry($cr, $er);
810
-		}
811
-
812
-		return false;
813
-	}
814
-
815
-	/**
816
-	 * Checks whether for a given BaseDN results will be returned
817
-	 * @param string $base the BaseDN to test
818
-	 * @return bool true on success, false otherwise
819
-	 * @throws \Exception
820
-	 */
821
-	private function testBaseDN($base) {
822
-		$cr = $this->getConnection();
823
-		if(!$cr) {
824
-			throw new \Exception('Could not connect to LDAP');
825
-		}
826
-
827
-		//base is there, let's validate it. If we search for anything, we should
828
-		//get a result set > 0 on a proper base
829
-		$rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
830
-		if(!$this->ldap->isResource($rr)) {
831
-			$errorNo  = $this->ldap->errno($cr);
832
-			$errorMsg = $this->ldap->error($cr);
833
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
834
-							' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO);
835
-			return false;
836
-		}
837
-		$entries = $this->ldap->countEntries($cr, $rr);
838
-		return ($entries !== false) && ($entries > 0);
839
-	}
840
-
841
-	/**
842
-	 * Checks whether the server supports memberOf in LDAP Filter.
843
-	 * Note: at least in OpenLDAP, availability of memberOf is dependent on
844
-	 * a configured objectClass. I.e. not necessarily for all available groups
845
-	 * memberOf does work.
846
-	 *
847
-	 * @return bool true if it does, false otherwise
848
-	 * @throws \Exception
849
-	 */
850
-	private function testMemberOf() {
851
-		$cr = $this->getConnection();
852
-		if(!$cr) {
853
-			throw new \Exception('Could not connect to LDAP');
854
-		}
855
-		$result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
856
-		if(is_int($result) &&  $result > 0) {
857
-			return true;
858
-		}
859
-		return false;
860
-	}
861
-
862
-	/**
863
-	 * creates an LDAP Filter from given configuration
864
-	 * @param integer $filterType int, for which use case the filter shall be created
865
-	 * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or
866
-	 * self::LFILTER_GROUP_LIST
867
-	 * @return string|false string with the filter on success, false otherwise
868
-	 * @throws \Exception
869
-	 */
870
-	private function composeLdapFilter($filterType) {
871
-		$filter = '';
872
-		$parts = 0;
873
-		switch ($filterType) {
874
-			case self::LFILTER_USER_LIST:
875
-				$objcs = $this->configuration->ldapUserFilterObjectclass;
876
-				//glue objectclasses
877
-				if(is_array($objcs) && count($objcs) > 0) {
878
-					$filter .= '(|';
879
-					foreach($objcs as $objc) {
880
-						$filter .= '(objectclass=' . $objc . ')';
881
-					}
882
-					$filter .= ')';
883
-					$parts++;
884
-				}
885
-				//glue group memberships
886
-				if($this->configuration->hasMemberOfFilterSupport) {
887
-					$cns = $this->configuration->ldapUserFilterGroups;
888
-					if(is_array($cns) && count($cns) > 0) {
889
-						$filter .= '(|';
890
-						$cr = $this->getConnection();
891
-						if(!$cr) {
892
-							throw new \Exception('Could not connect to LDAP');
893
-						}
894
-						$base = $this->configuration->ldapBase[0];
895
-						foreach($cns as $cn) {
896
-							$rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
897
-							if(!$this->ldap->isResource($rr)) {
898
-								continue;
899
-							}
900
-							$er = $this->ldap->firstEntry($cr, $rr);
901
-							$attrs = $this->ldap->getAttributes($cr, $er);
902
-							$dn = $this->ldap->getDN($cr, $er);
903
-							if(empty($dn)) {
904
-								continue;
905
-							}
906
-							$filterPart = '(memberof=' . $dn . ')';
907
-							if(isset($attrs['primaryGroupToken'])) {
908
-								$pgt = $attrs['primaryGroupToken'][0];
909
-								$primaryFilterPart = '(primaryGroupID=' . $pgt .')';
910
-								$filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
911
-							}
912
-							$filter .= $filterPart;
913
-						}
914
-						$filter .= ')';
915
-					}
916
-					$parts++;
917
-				}
918
-				//wrap parts in AND condition
919
-				if($parts > 1) {
920
-					$filter = '(&' . $filter . ')';
921
-				}
922
-				if(empty($filter)) {
923
-					$filter = '(objectclass=*)';
924
-				}
925
-				break;
926
-
927
-			case self::LFILTER_GROUP_LIST:
928
-				$objcs = $this->configuration->ldapGroupFilterObjectclass;
929
-				//glue objectclasses
930
-				if(is_array($objcs) && count($objcs) > 0) {
931
-					$filter .= '(|';
932
-					foreach($objcs as $objc) {
933
-						$filter .= '(objectclass=' . $objc . ')';
934
-					}
935
-					$filter .= ')';
936
-					$parts++;
937
-				}
938
-				//glue group memberships
939
-				$cns = $this->configuration->ldapGroupFilterGroups;
940
-				if(is_array($cns) && count($cns) > 0) {
941
-					$filter .= '(|';
942
-					$base = $this->configuration->ldapBase[0];
943
-					foreach($cns as $cn) {
944
-						$filter .= '(cn=' . $cn . ')';
945
-					}
946
-					$filter .= ')';
947
-				}
948
-				$parts++;
949
-				//wrap parts in AND condition
950
-				if($parts > 1) {
951
-					$filter = '(&' . $filter . ')';
952
-				}
953
-				break;
954
-
955
-			case self::LFILTER_LOGIN:
956
-				$ulf = $this->configuration->ldapUserFilter;
957
-				$loginpart = '=%uid';
958
-				$filterUsername = '';
959
-				$userAttributes = $this->getUserAttributes();
960
-				$userAttributes = array_change_key_case(array_flip($userAttributes));
961
-				$parts = 0;
962
-
963
-				if($this->configuration->ldapLoginFilterUsername === '1') {
964
-					$attr = '';
965
-					if(isset($userAttributes['uid'])) {
966
-						$attr = 'uid';
967
-					} else if(isset($userAttributes['samaccountname'])) {
968
-						$attr = 'samaccountname';
969
-					} else if(isset($userAttributes['cn'])) {
970
-						//fallback
971
-						$attr = 'cn';
972
-					}
973
-					if(!empty($attr)) {
974
-						$filterUsername = '(' . $attr . $loginpart . ')';
975
-						$parts++;
976
-					}
977
-				}
978
-
979
-				$filterEmail = '';
980
-				if($this->configuration->ldapLoginFilterEmail === '1') {
981
-					$filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
982
-					$parts++;
983
-				}
984
-
985
-				$filterAttributes = '';
986
-				$attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
987
-				if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
988
-					$filterAttributes = '(|';
989
-					foreach($attrsToFilter as $attribute) {
990
-						$filterAttributes .= '(' . $attribute . $loginpart . ')';
991
-					}
992
-					$filterAttributes .= ')';
993
-					$parts++;
994
-				}
995
-
996
-				$filterLogin = '';
997
-				if($parts > 1) {
998
-					$filterLogin = '(|';
999
-				}
1000
-				$filterLogin .= $filterUsername;
1001
-				$filterLogin .= $filterEmail;
1002
-				$filterLogin .= $filterAttributes;
1003
-				if($parts > 1) {
1004
-					$filterLogin .= ')';
1005
-				}
1006
-
1007
-				$filter = '(&'.$ulf.$filterLogin.')';
1008
-				break;
1009
-		}
1010
-
1011
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG);
1012
-
1013
-		return $filter;
1014
-	}
1015
-
1016
-	/**
1017
-	 * Connects and Binds to an LDAP Server
1018
-	 * @param int $port the port to connect with
1019
-	 * @param bool $tls whether startTLS is to be used
1020
-	 * @param bool $ncc
1021
-	 * @return bool
1022
-	 * @throws \Exception
1023
-	 */
1024
-	private function connectAndBind($port = 389, $tls = false, $ncc = false) {
1025
-		if($ncc) {
1026
-			//No certificate check
1027
-			//FIXME: undo afterwards
1028
-			putenv('LDAPTLS_REQCERT=never');
1029
-		}
1030
-
1031
-		//connect, does not really trigger any server communication
1032
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG);
1033
-		$host = $this->configuration->ldapHost;
1034
-		$hostInfo = parse_url($host);
1035
-		if(!$hostInfo) {
1036
-			throw new \Exception($this->l->t('Invalid Host'));
1037
-		}
1038
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1039
-		$cr = $this->ldap->connect($host, $port);
1040
-		if(!is_resource($cr)) {
1041
-			throw new \Exception($this->l->t('Invalid Host'));
1042
-		}
1043
-
1044
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG);
1045
-		//set LDAP options
1046
-		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1047
-		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1048
-		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1049
-
1050
-		try {
1051
-			if($tls) {
1052
-				$isTlsWorking = @$this->ldap->startTls($cr);
1053
-				if(!$isTlsWorking) {
1054
-					return false;
1055
-				}
1056
-			}
1057
-
1058
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG);
1059
-			//interesting part: do the bind!
1060
-			$login = $this->ldap->bind($cr,
1061
-				$this->configuration->ldapAgentName,
1062
-				$this->configuration->ldapAgentPassword
1063
-			);
1064
-			$errNo = $this->ldap->errno($cr);
1065
-			$error = ldap_error($cr);
1066
-			$this->ldap->unbind($cr);
1067
-		} catch(ServerNotAvailableException $e) {
1068
-			return false;
1069
-		}
1070
-
1071
-		if($login === true) {
1072
-			$this->ldap->unbind($cr);
1073
-			if($ncc) {
1074
-				throw new \Exception('Certificate cannot be validated.');
1075
-			}
1076
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1077
-			return true;
1078
-		}
1079
-
1080
-		if($errNo === -1 || ($errNo === 2 && $ncc)) {
1081
-			//host, port or TLS wrong
1082
-			return false;
1083
-		} else if ($errNo === 2) {
1084
-			return $this->connectAndBind($port, $tls, true);
1085
-		}
1086
-		throw new \Exception($error, $errNo);
1087
-	}
1088
-
1089
-	/**
1090
-	 * checks whether a valid combination of agent and password has been
1091
-	 * provided (either two values or nothing for anonymous connect)
1092
-	 * @return bool, true if everything is fine, false otherwise
1093
-	 */
1094
-	private function checkAgentRequirements() {
1095
-		$agent = $this->configuration->ldapAgentName;
1096
-		$pwd = $this->configuration->ldapAgentPassword;
1097
-
1098
-		return ( (!empty($agent) && !empty($pwd))
1099
-		       || (empty($agent) &&  empty($pwd)));
1100
-	}
1101
-
1102
-	/**
1103
-	 * @param array $reqs
1104
-	 * @return bool
1105
-	 */
1106
-	private function checkRequirements($reqs) {
1107
-		$this->checkAgentRequirements();
1108
-		foreach($reqs as $option) {
1109
-			$value = $this->configuration->$option;
1110
-			if(empty($value)) {
1111
-				return false;
1112
-			}
1113
-		}
1114
-		return true;
1115
-	}
1116
-
1117
-	/**
1118
-	 * does a cumulativeSearch on LDAP to get different values of a
1119
-	 * specified attribute
1120
-	 * @param string[] $filters array, the filters that shall be used in the search
1121
-	 * @param string $attr the attribute of which a list of values shall be returned
1122
-	 * @param int $dnReadLimit the amount of how many DNs should be analyzed.
1123
-	 * The lower, the faster
1124
-	 * @param string $maxF string. if not null, this variable will have the filter that
1125
-	 * yields most result entries
1126
-	 * @return array|false an array with the values on success, false otherwise
1127
-	 */
1128
-	public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) {
1129
-		$dnRead = array();
1130
-		$foundItems = array();
1131
-		$maxEntries = 0;
1132
-		if(!is_array($this->configuration->ldapBase)
1133
-		   || !isset($this->configuration->ldapBase[0])) {
1134
-			return false;
1135
-		}
1136
-		$base = $this->configuration->ldapBase[0];
1137
-		$cr = $this->getConnection();
1138
-		if(!$this->ldap->isResource($cr)) {
1139
-			return false;
1140
-		}
1141
-		$lastFilter = null;
1142
-		if(isset($filters[count($filters)-1])) {
1143
-			$lastFilter = $filters[count($filters)-1];
1144
-		}
1145
-		foreach($filters as $filter) {
1146
-			if($lastFilter === $filter && count($foundItems) > 0) {
1147
-				//skip when the filter is a wildcard and results were found
1148
-				continue;
1149
-			}
1150
-			// 20k limit for performance and reason
1151
-			$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1152
-			if(!$this->ldap->isResource($rr)) {
1153
-				continue;
1154
-			}
1155
-			$entries = $this->ldap->countEntries($cr, $rr);
1156
-			$getEntryFunc = 'firstEntry';
1157
-			if(($entries !== false) && ($entries > 0)) {
1158
-				if(!is_null($maxF) && $entries > $maxEntries) {
1159
-					$maxEntries = $entries;
1160
-					$maxF = $filter;
1161
-				}
1162
-				$dnReadCount = 0;
1163
-				do {
1164
-					$entry = $this->ldap->$getEntryFunc($cr, $rr);
1165
-					$getEntryFunc = 'nextEntry';
1166
-					if(!$this->ldap->isResource($entry)) {
1167
-						continue 2;
1168
-					}
1169
-					$rr = $entry; //will be expected by nextEntry next round
1170
-					$attributes = $this->ldap->getAttributes($cr, $entry);
1171
-					$dn = $this->ldap->getDN($cr, $entry);
1172
-					if($dn === false || in_array($dn, $dnRead)) {
1173
-						continue;
1174
-					}
1175
-					$newItems = array();
1176
-					$state = $this->getAttributeValuesFromEntry($attributes,
1177
-																$attr,
1178
-																$newItems);
1179
-					$dnReadCount++;
1180
-					$foundItems = array_merge($foundItems, $newItems);
1181
-					$this->resultCache[$dn][$attr] = $newItems;
1182
-					$dnRead[] = $dn;
1183
-				} while(($state === self::LRESULT_PROCESSED_SKIP
1184
-						|| $this->ldap->isResource($entry))
1185
-						&& ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1186
-			}
1187
-		}
1188
-
1189
-		return array_unique($foundItems);
1190
-	}
1191
-
1192
-	/**
1193
-	 * determines if and which $attr are available on the LDAP server
1194
-	 * @param string[] $objectclasses the objectclasses to use as search filter
1195
-	 * @param string $attr the attribute to look for
1196
-	 * @param string $dbkey the dbkey of the setting the feature is connected to
1197
-	 * @param string $confkey the confkey counterpart for the $dbkey as used in the
1198
-	 * Configuration class
1199
-	 * @param bool $po whether the objectClass with most result entries
1200
-	 * shall be pre-selected via the result
1201
-	 * @return array|false list of found items.
1202
-	 * @throws \Exception
1203
-	 */
1204
-	private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1205
-		$cr = $this->getConnection();
1206
-		if(!$cr) {
1207
-			throw new \Exception('Could not connect to LDAP');
1208
-		}
1209
-		$p = 'objectclass=';
1210
-		foreach($objectclasses as $key => $value) {
1211
-			$objectclasses[$key] = $p.$value;
1212
-		}
1213
-		$maxEntryObjC = '';
1214
-
1215
-		//how deep to dig?
1216
-		//When looking for objectclasses, testing few entries is sufficient,
1217
-		$dig = 3;
1218
-
1219
-		$availableFeatures =
1220
-			$this->cumulativeSearchOnAttribute($objectclasses, $attr,
1221
-											   $dig, $maxEntryObjC);
1222
-		if(is_array($availableFeatures)
1223
-		   && count($availableFeatures) > 0) {
1224
-			natcasesort($availableFeatures);
1225
-			//natcasesort keeps indices, but we must get rid of them for proper
1226
-			//sorting in the web UI. Therefore: array_values
1227
-			$this->result->addOptions($dbkey, array_values($availableFeatures));
1228
-		} else {
1229
-			throw new \Exception(self::$l->t('Could not find the desired feature'));
1230
-		}
1231
-
1232
-		$setFeatures = $this->configuration->$confkey;
1233
-		if(is_array($setFeatures) && !empty($setFeatures)) {
1234
-			//something is already configured? pre-select it.
1235
-			$this->result->addChange($dbkey, $setFeatures);
1236
-		} else if($po && !empty($maxEntryObjC)) {
1237
-			//pre-select objectclass with most result entries
1238
-			$maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1239
-			$this->applyFind($dbkey, $maxEntryObjC);
1240
-			$this->result->addChange($dbkey, $maxEntryObjC);
1241
-		}
1242
-
1243
-		return $availableFeatures;
1244
-	}
1245
-
1246
-	/**
1247
-	 * appends a list of values fr
1248
-	 * @param resource $result the return value from ldap_get_attributes
1249
-	 * @param string $attribute the attribute values to look for
1250
-	 * @param array &$known new values will be appended here
1251
-	 * @return int, state on of the class constants LRESULT_PROCESSED_OK,
1252
-	 * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1253
-	 */
1254
-	private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1255
-		if(!is_array($result)
1256
-		   || !isset($result['count'])
1257
-		   || !$result['count'] > 0) {
1258
-			return self::LRESULT_PROCESSED_INVALID;
1259
-		}
1260
-
1261
-		// strtolower on all keys for proper comparison
1262
-		$result = \OCP\Util::mb_array_change_key_case($result);
1263
-		$attribute = strtolower($attribute);
1264
-		if(isset($result[$attribute])) {
1265
-			foreach($result[$attribute] as $key => $val) {
1266
-				if($key === 'count') {
1267
-					continue;
1268
-				}
1269
-				if(!in_array($val, $known)) {
1270
-					$known[] = $val;
1271
-				}
1272
-			}
1273
-			return self::LRESULT_PROCESSED_OK;
1274
-		} else {
1275
-			return self::LRESULT_PROCESSED_SKIP;
1276
-		}
1277
-	}
1278
-
1279
-	/**
1280
-	 * @return bool|mixed
1281
-	 */
1282
-	private function getConnection() {
1283
-		if(!is_null($this->cr)) {
1284
-			return $this->cr;
1285
-		}
1286
-
1287
-		$cr = $this->ldap->connect(
1288
-			$this->configuration->ldapHost,
1289
-			$this->configuration->ldapPort
1290
-		);
1291
-
1292
-		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1293
-		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1294
-		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1295
-		if($this->configuration->ldapTLS === 1) {
1296
-			$this->ldap->startTls($cr);
1297
-		}
1298
-
1299
-		$lo = @$this->ldap->bind($cr,
1300
-								 $this->configuration->ldapAgentName,
1301
-								 $this->configuration->ldapAgentPassword);
1302
-		if($lo === true) {
1303
-			$this->$cr = $cr;
1304
-			return $cr;
1305
-		}
1306
-
1307
-		return false;
1308
-	}
1309
-
1310
-	/**
1311
-	 * @return array
1312
-	 */
1313
-	private function getDefaultLdapPortSettings() {
1314
-		static $settings = array(
1315
-								array('port' => 7636, 'tls' => false),
1316
-								array('port' =>  636, 'tls' => false),
1317
-								array('port' => 7389, 'tls' => true),
1318
-								array('port' =>  389, 'tls' => true),
1319
-								array('port' => 7389, 'tls' => false),
1320
-								array('port' =>  389, 'tls' => false),
1321
-						  );
1322
-		return $settings;
1323
-	}
1324
-
1325
-	/**
1326
-	 * @return array
1327
-	 */
1328
-	private function getPortSettingsToTry() {
1329
-		//389 ← LDAP / Unencrypted or StartTLS
1330
-		//636 ← LDAPS / SSL
1331
-		//7xxx ← UCS. need to be checked first, because both ports may be open
1332
-		$host = $this->configuration->ldapHost;
1333
-		$port = intval($this->configuration->ldapPort);
1334
-		$portSettings = array();
1335
-
1336
-		//In case the port is already provided, we will check this first
1337
-		if($port > 0) {
1338
-			$hostInfo = parse_url($host);
1339
-			if(!(is_array($hostInfo)
1340
-				&& isset($hostInfo['scheme'])
1341
-				&& stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1342
-				$portSettings[] = array('port' => $port, 'tls' => true);
1343
-			}
1344
-			$portSettings[] =array('port' => $port, 'tls' => false);
1345
-		}
1346
-
1347
-		//default ports
1348
-		$portSettings = array_merge($portSettings,
1349
-		                            $this->getDefaultLdapPortSettings());
1350
-
1351
-		return $portSettings;
1352
-	}
38
+    static protected $l;
39
+    protected $access;
40
+    protected $cr;
41
+    protected $configuration;
42
+    protected $result;
43
+    protected $resultCache = array();
44
+
45
+    const LRESULT_PROCESSED_OK = 2;
46
+    const LRESULT_PROCESSED_INVALID = 3;
47
+    const LRESULT_PROCESSED_SKIP = 4;
48
+
49
+    const LFILTER_LOGIN      = 2;
50
+    const LFILTER_USER_LIST  = 3;
51
+    const LFILTER_GROUP_LIST = 4;
52
+
53
+    const LFILTER_MODE_ASSISTED = 2;
54
+    const LFILTER_MODE_RAW = 1;
55
+
56
+    const LDAP_NW_TIMEOUT = 4;
57
+
58
+    /**
59
+     * Constructor
60
+     * @param Configuration $configuration an instance of Configuration
61
+     * @param ILDAPWrapper $ldap an instance of ILDAPWrapper
62
+     */
63
+    public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
64
+        parent::__construct($ldap);
65
+        $this->configuration = $configuration;
66
+        if(is_null(Wizard::$l)) {
67
+            Wizard::$l = \OC::$server->getL10N('user_ldap');
68
+        }
69
+        $this->access = $access;
70
+        $this->result = new WizardResult();
71
+    }
72
+
73
+    public function  __destruct() {
74
+        if($this->result->hasChanges()) {
75
+            $this->configuration->saveConfiguration();
76
+        }
77
+    }
78
+
79
+    /**
80
+     * counts entries in the LDAP directory
81
+     *
82
+     * @param string $filter the LDAP search filter
83
+     * @param string $type a string being either 'users' or 'groups';
84
+     * @return bool|int
85
+     * @throws \Exception
86
+     */
87
+    public function countEntries($filter, $type) {
88
+        $reqs = array('ldapHost', 'ldapPort', 'ldapBase');
89
+        if($type === 'users') {
90
+            $reqs[] = 'ldapUserFilter';
91
+        }
92
+        if(!$this->checkRequirements($reqs)) {
93
+            throw new \Exception('Requirements not met', 400);
94
+        }
95
+
96
+        $attr = array('dn'); // default
97
+        $limit = 1001;
98
+        if($type === 'groups') {
99
+            $result =  $this->access->countGroups($filter, $attr, $limit);
100
+        } else if($type === 'users') {
101
+            $result = $this->access->countUsers($filter, $attr, $limit);
102
+        } else if ($type === 'objects') {
103
+            $result = $this->access->countObjects($limit);
104
+        } else {
105
+            throw new \Exception('internal error: invalid object type', 500);
106
+        }
107
+
108
+        return $result;
109
+    }
110
+
111
+    /**
112
+     * formats the return value of a count operation to the string to be
113
+     * inserted.
114
+     *
115
+     * @param bool|int $count
116
+     * @return int|string
117
+     */
118
+    private function formatCountResult($count) {
119
+        $formatted = ($count !== false) ? $count : 0;
120
+        if($formatted > 1000) {
121
+            $formatted = '> 1000';
122
+        }
123
+        return $formatted;
124
+    }
125
+
126
+    public function countGroups() {
127
+        $filter = $this->configuration->ldapGroupFilter;
128
+
129
+        if(empty($filter)) {
130
+            $output = self::$l->n('%s group found', '%s groups found', 0, array(0));
131
+            $this->result->addChange('ldap_group_count', $output);
132
+            return $this->result;
133
+        }
134
+
135
+        try {
136
+            $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
137
+        } catch (\Exception $e) {
138
+            //400 can be ignored, 500 is forwarded
139
+            if($e->getCode() === 500) {
140
+                throw $e;
141
+            }
142
+            return false;
143
+        }
144
+        $output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
145
+        $this->result->addChange('ldap_group_count', $output);
146
+        return $this->result;
147
+    }
148
+
149
+    /**
150
+     * @return WizardResult
151
+     * @throws \Exception
152
+     */
153
+    public function countUsers() {
154
+        $filter = $this->access->getFilterForUserCount();
155
+
156
+        $usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
157
+        $output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
158
+        $this->result->addChange('ldap_user_count', $output);
159
+        return $this->result;
160
+    }
161
+
162
+    /**
163
+     * counts any objects in the currently set base dn
164
+     *
165
+     * @return WizardResult
166
+     * @throws \Exception
167
+     */
168
+    public function countInBaseDN() {
169
+        // we don't need to provide a filter in this case
170
+        $total = $this->countEntries(null, 'objects');
171
+        if($total === false) {
172
+            throw new \Exception('invalid results received');
173
+        }
174
+        $this->result->addChange('ldap_test_base', $total);
175
+        return $this->result;
176
+    }
177
+
178
+    /**
179
+     * counts users with a specified attribute
180
+     * @param string $attr
181
+     * @param bool $existsCheck
182
+     * @return int|bool
183
+     */
184
+    public function countUsersWithAttribute($attr, $existsCheck = false) {
185
+        if(!$this->checkRequirements(array('ldapHost',
186
+                                            'ldapPort',
187
+                                            'ldapBase',
188
+                                            'ldapUserFilter',
189
+                                            ))) {
190
+            return  false;
191
+        }
192
+
193
+        $filter = $this->access->combineFilterWithAnd(array(
194
+            $this->configuration->ldapUserFilter,
195
+            $attr . '=*'
196
+        ));
197
+
198
+        $limit = ($existsCheck === false) ? null : 1;
199
+
200
+        return $this->access->countUsers($filter, array('dn'), $limit);
201
+    }
202
+
203
+    /**
204
+     * detects the display name attribute. If a setting is already present that
205
+     * returns at least one hit, the detection will be canceled.
206
+     * @return WizardResult|bool
207
+     * @throws \Exception
208
+     */
209
+    public function detectUserDisplayNameAttribute() {
210
+        if(!$this->checkRequirements(array('ldapHost',
211
+                                        'ldapPort',
212
+                                        'ldapBase',
213
+                                        'ldapUserFilter',
214
+                                        ))) {
215
+            return  false;
216
+        }
217
+
218
+        $attr = $this->configuration->ldapUserDisplayName;
219
+        if($attr !== 'displayName' && !empty($attr)) {
220
+            // most likely not the default value with upper case N,
221
+            // verify it still produces a result
222
+            $count = intval($this->countUsersWithAttribute($attr, true));
223
+            if($count > 0) {
224
+                //no change, but we sent it back to make sure the user interface
225
+                //is still correct, even if the ajax call was cancelled inbetween
226
+                $this->result->addChange('ldap_display_name', $attr);
227
+                return $this->result;
228
+            }
229
+        }
230
+
231
+        // first attribute that has at least one result wins
232
+        $displayNameAttrs = array('displayname', 'cn');
233
+        foreach ($displayNameAttrs as $attr) {
234
+            $count = intval($this->countUsersWithAttribute($attr, true));
235
+
236
+            if($count > 0) {
237
+                $this->applyFind('ldap_display_name', $attr);
238
+                return $this->result;
239
+            }
240
+        };
241
+
242
+        throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced ldap settings.'));
243
+    }
244
+
245
+    /**
246
+     * detects the most often used email attribute for users applying to the
247
+     * user list filter. If a setting is already present that returns at least
248
+     * one hit, the detection will be canceled.
249
+     * @return WizardResult|bool
250
+     */
251
+    public function detectEmailAttribute() {
252
+        if(!$this->checkRequirements(array('ldapHost',
253
+                                            'ldapPort',
254
+                                            'ldapBase',
255
+                                            'ldapUserFilter',
256
+                                            ))) {
257
+            return  false;
258
+        }
259
+
260
+        $attr = $this->configuration->ldapEmailAttribute;
261
+        if(!empty($attr)) {
262
+            $count = intval($this->countUsersWithAttribute($attr, true));
263
+            if($count > 0) {
264
+                return false;
265
+            }
266
+            $writeLog = true;
267
+        } else {
268
+            $writeLog = false;
269
+        }
270
+
271
+        $emailAttributes = array('mail', 'mailPrimaryAddress');
272
+        $winner = '';
273
+        $maxUsers = 0;
274
+        foreach($emailAttributes as $attr) {
275
+            $count = $this->countUsersWithAttribute($attr);
276
+            if($count > $maxUsers) {
277
+                $maxUsers = $count;
278
+                $winner = $attr;
279
+            }
280
+        }
281
+
282
+        if($winner !== '') {
283
+            $this->applyFind('ldap_email_attr', $winner);
284
+            if($writeLog) {
285
+                \OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
286
+                    'automatically been reset, because the original value ' .
287
+                    'did not return any results.', \OCP\Util::INFO);
288
+            }
289
+        }
290
+
291
+        return $this->result;
292
+    }
293
+
294
+    /**
295
+     * @return WizardResult
296
+     * @throws \Exception
297
+     */
298
+    public function determineAttributes() {
299
+        if(!$this->checkRequirements(array('ldapHost',
300
+                                            'ldapPort',
301
+                                            'ldapBase',
302
+                                            'ldapUserFilter',
303
+                                            ))) {
304
+            return  false;
305
+        }
306
+
307
+        $attributes = $this->getUserAttributes();
308
+
309
+        natcasesort($attributes);
310
+        $attributes = array_values($attributes);
311
+
312
+        $this->result->addOptions('ldap_loginfilter_attributes', $attributes);
313
+
314
+        $selected = $this->configuration->ldapLoginFilterAttributes;
315
+        if(is_array($selected) && !empty($selected)) {
316
+            $this->result->addChange('ldap_loginfilter_attributes', $selected);
317
+        }
318
+
319
+        return $this->result;
320
+    }
321
+
322
+    /**
323
+     * detects the available LDAP attributes
324
+     * @return array|false The instance's WizardResult instance
325
+     * @throws \Exception
326
+     */
327
+    private function getUserAttributes() {
328
+        if(!$this->checkRequirements(array('ldapHost',
329
+                                            'ldapPort',
330
+                                            'ldapBase',
331
+                                            'ldapUserFilter',
332
+                                            ))) {
333
+            return  false;
334
+        }
335
+        $cr = $this->getConnection();
336
+        if(!$cr) {
337
+            throw new \Exception('Could not connect to LDAP');
338
+        }
339
+
340
+        $base = $this->configuration->ldapBase[0];
341
+        $filter = $this->configuration->ldapUserFilter;
342
+        $rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
343
+        if(!$this->ldap->isResource($rr)) {
344
+            return false;
345
+        }
346
+        $er = $this->ldap->firstEntry($cr, $rr);
347
+        $attributes = $this->ldap->getAttributes($cr, $er);
348
+        $pureAttributes = array();
349
+        for($i = 0; $i < $attributes['count']; $i++) {
350
+            $pureAttributes[] = $attributes[$i];
351
+        }
352
+
353
+        return $pureAttributes;
354
+    }
355
+
356
+    /**
357
+     * detects the available LDAP groups
358
+     * @return WizardResult|false the instance's WizardResult instance
359
+     */
360
+    public function determineGroupsForGroups() {
361
+        return $this->determineGroups('ldap_groupfilter_groups',
362
+                                        'ldapGroupFilterGroups',
363
+                                        false);
364
+    }
365
+
366
+    /**
367
+     * detects the available LDAP groups
368
+     * @return WizardResult|false the instance's WizardResult instance
369
+     */
370
+    public function determineGroupsForUsers() {
371
+        return $this->determineGroups('ldap_userfilter_groups',
372
+                                        'ldapUserFilterGroups');
373
+    }
374
+
375
+    /**
376
+     * detects the available LDAP groups
377
+     * @param string $dbKey
378
+     * @param string $confKey
379
+     * @param bool $testMemberOf
380
+     * @return WizardResult|false the instance's WizardResult instance
381
+     * @throws \Exception
382
+     */
383
+    private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
384
+        if(!$this->checkRequirements(array('ldapHost',
385
+                                            'ldapPort',
386
+                                            'ldapBase',
387
+                                            ))) {
388
+            return  false;
389
+        }
390
+        $cr = $this->getConnection();
391
+        if(!$cr) {
392
+            throw new \Exception('Could not connect to LDAP');
393
+        }
394
+
395
+        $this->fetchGroups($dbKey, $confKey);
396
+
397
+        if($testMemberOf) {
398
+            $this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
399
+            $this->result->markChange();
400
+            if(!$this->configuration->hasMemberOfFilterSupport) {
401
+                throw new \Exception('memberOf is not supported by the server');
402
+            }
403
+        }
404
+
405
+        return $this->result;
406
+    }
407
+
408
+    /**
409
+     * fetches all groups from LDAP and adds them to the result object
410
+     *
411
+     * @param string $dbKey
412
+     * @param string $confKey
413
+     * @return array $groupEntries
414
+     * @throws \Exception
415
+     */
416
+    public function fetchGroups($dbKey, $confKey) {
417
+        $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames');
418
+
419
+        $filterParts = array();
420
+        foreach($obclasses as $obclass) {
421
+            $filterParts[] = 'objectclass='.$obclass;
422
+        }
423
+        //we filter for everything
424
+        //- that looks like a group and
425
+        //- has the group display name set
426
+        $filter = $this->access->combineFilterWithOr($filterParts);
427
+        $filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*'));
428
+
429
+        $groupNames = array();
430
+        $groupEntries = array();
431
+        $limit = 400;
432
+        $offset = 0;
433
+        do {
434
+            // we need to request dn additionally here, otherwise memberOf
435
+            // detection will fail later
436
+            $result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
437
+            foreach($result as $item) {
438
+                if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
439
+                    // just in case - no issue known
440
+                    continue;
441
+                }
442
+                $groupNames[] = $item['cn'][0];
443
+                $groupEntries[] = $item;
444
+            }
445
+            $offset += $limit;
446
+        } while ($this->access->hasMoreResults());
447
+
448
+        if(count($groupNames) > 0) {
449
+            natsort($groupNames);
450
+            $this->result->addOptions($dbKey, array_values($groupNames));
451
+        } else {
452
+            throw new \Exception(self::$l->t('Could not find the desired feature'));
453
+        }
454
+
455
+        $setFeatures = $this->configuration->$confKey;
456
+        if(is_array($setFeatures) && !empty($setFeatures)) {
457
+            //something is already configured? pre-select it.
458
+            $this->result->addChange($dbKey, $setFeatures);
459
+        }
460
+        return $groupEntries;
461
+    }
462
+
463
+    public function determineGroupMemberAssoc() {
464
+        if(!$this->checkRequirements(array('ldapHost',
465
+                                            'ldapPort',
466
+                                            'ldapGroupFilter',
467
+                                            ))) {
468
+            return  false;
469
+        }
470
+        $attribute = $this->detectGroupMemberAssoc();
471
+        if($attribute === false) {
472
+            return false;
473
+        }
474
+        $this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
475
+        $this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
476
+
477
+        return $this->result;
478
+    }
479
+
480
+    /**
481
+     * Detects the available object classes
482
+     * @return WizardResult|false the instance's WizardResult instance
483
+     * @throws \Exception
484
+     */
485
+    public function determineGroupObjectClasses() {
486
+        if(!$this->checkRequirements(array('ldapHost',
487
+                                            'ldapPort',
488
+                                            'ldapBase',
489
+                                            ))) {
490
+            return  false;
491
+        }
492
+        $cr = $this->getConnection();
493
+        if(!$cr) {
494
+            throw new \Exception('Could not connect to LDAP');
495
+        }
496
+
497
+        $obclasses = array('groupOfNames', 'group', 'posixGroup', '*');
498
+        $this->determineFeature($obclasses,
499
+                                'objectclass',
500
+                                'ldap_groupfilter_objectclass',
501
+                                'ldapGroupFilterObjectclass',
502
+                                false);
503
+
504
+        return $this->result;
505
+    }
506
+
507
+    /**
508
+     * detects the available object classes
509
+     * @return WizardResult
510
+     * @throws \Exception
511
+     */
512
+    public function determineUserObjectClasses() {
513
+        if(!$this->checkRequirements(array('ldapHost',
514
+                                            'ldapPort',
515
+                                            'ldapBase',
516
+                                            ))) {
517
+            return  false;
518
+        }
519
+        $cr = $this->getConnection();
520
+        if(!$cr) {
521
+            throw new \Exception('Could not connect to LDAP');
522
+        }
523
+
524
+        $obclasses = array('inetOrgPerson', 'person', 'organizationalPerson',
525
+                            'user', 'posixAccount', '*');
526
+        $filter = $this->configuration->ldapUserFilter;
527
+        //if filter is empty, it is probably the first time the wizard is called
528
+        //then, apply suggestions.
529
+        $this->determineFeature($obclasses,
530
+                                'objectclass',
531
+                                'ldap_userfilter_objectclass',
532
+                                'ldapUserFilterObjectclass',
533
+                                empty($filter));
534
+
535
+        return $this->result;
536
+    }
537
+
538
+    /**
539
+     * @return WizardResult|false
540
+     * @throws \Exception
541
+     */
542
+    public function getGroupFilter() {
543
+        if(!$this->checkRequirements(array('ldapHost',
544
+                                            'ldapPort',
545
+                                            'ldapBase',
546
+                                            ))) {
547
+            return false;
548
+        }
549
+        //make sure the use display name is set
550
+        $displayName = $this->configuration->ldapGroupDisplayName;
551
+        if(empty($displayName)) {
552
+            $d = $this->configuration->getDefaults();
553
+            $this->applyFind('ldap_group_display_name',
554
+                                $d['ldap_group_display_name']);
555
+        }
556
+        $filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST);
557
+
558
+        $this->applyFind('ldap_group_filter', $filter);
559
+        return $this->result;
560
+    }
561
+
562
+    /**
563
+     * @return WizardResult|false
564
+     * @throws \Exception
565
+     */
566
+    public function getUserListFilter() {
567
+        if(!$this->checkRequirements(array('ldapHost',
568
+                                            'ldapPort',
569
+                                            'ldapBase',
570
+                                            ))) {
571
+            return false;
572
+        }
573
+        //make sure the use display name is set
574
+        $displayName = $this->configuration->ldapUserDisplayName;
575
+        if(empty($displayName)) {
576
+            $d = $this->configuration->getDefaults();
577
+            $this->applyFind('ldap_display_name', $d['ldap_display_name']);
578
+        }
579
+        $filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
580
+        if(!$filter) {
581
+            throw new \Exception('Cannot create filter');
582
+        }
583
+
584
+        $this->applyFind('ldap_userlist_filter', $filter);
585
+        return $this->result;
586
+    }
587
+
588
+    /**
589
+     * @return bool|WizardResult
590
+     * @throws \Exception
591
+     */
592
+    public function getUserLoginFilter() {
593
+        if(!$this->checkRequirements(array('ldapHost',
594
+                                            'ldapPort',
595
+                                            'ldapBase',
596
+                                            'ldapUserFilter',
597
+                                            ))) {
598
+            return false;
599
+        }
600
+
601
+        $filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
602
+        if(!$filter) {
603
+            throw new \Exception('Cannot create filter');
604
+        }
605
+
606
+        $this->applyFind('ldap_login_filter', $filter);
607
+        return $this->result;
608
+    }
609
+
610
+    /**
611
+     * @return bool|WizardResult
612
+     * @param string $loginName
613
+     * @throws \Exception
614
+     */
615
+    public function testLoginName($loginName) {
616
+        if(!$this->checkRequirements(array('ldapHost',
617
+            'ldapPort',
618
+            'ldapBase',
619
+            'ldapLoginFilter',
620
+        ))) {
621
+            return false;
622
+        }
623
+
624
+        $cr = $this->access->connection->getConnectionResource();
625
+        if(!$this->ldap->isResource($cr)) {
626
+            throw new \Exception('connection error');
627
+        }
628
+
629
+        if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
630
+            === false) {
631
+            throw new \Exception('missing placeholder');
632
+        }
633
+
634
+        $users = $this->access->countUsersByLoginName($loginName);
635
+        if($this->ldap->errno($cr) !== 0) {
636
+            throw new \Exception($this->ldap->error($cr));
637
+        }
638
+        $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
639
+        $this->result->addChange('ldap_test_loginname', $users);
640
+        $this->result->addChange('ldap_test_effective_filter', $filter);
641
+        return $this->result;
642
+    }
643
+
644
+    /**
645
+     * Tries to determine the port, requires given Host, User DN and Password
646
+     * @return WizardResult|false WizardResult on success, false otherwise
647
+     * @throws \Exception
648
+     */
649
+    public function guessPortAndTLS() {
650
+        if(!$this->checkRequirements(array('ldapHost',
651
+                                            ))) {
652
+            return false;
653
+        }
654
+        $this->checkHost();
655
+        $portSettings = $this->getPortSettingsToTry();
656
+
657
+        if(!is_array($portSettings)) {
658
+            throw new \Exception(print_r($portSettings, true));
659
+        }
660
+
661
+        //proceed from the best configuration and return on first success
662
+        foreach($portSettings as $setting) {
663
+            $p = $setting['port'];
664
+            $t = $setting['tls'];
665
+            \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
666
+            //connectAndBind may throw Exception, it needs to be catched by the
667
+            //callee of this method
668
+
669
+            try {
670
+                $settingsFound = $this->connectAndBind($p, $t);
671
+            } catch (\Exception $e) {
672
+                // any reply other than -1 (= cannot connect) is already okay,
673
+                // because then we found the server
674
+                // unavailable startTLS returns -11
675
+                if($e->getCode() > 0) {
676
+                    $settingsFound = true;
677
+                } else {
678
+                    throw $e;
679
+                }
680
+            }
681
+
682
+            if ($settingsFound === true) {
683
+                $config = array(
684
+                    'ldapPort' => $p,
685
+                    'ldapTLS' => intval($t)
686
+                );
687
+                $this->configuration->setConfiguration($config);
688
+                \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
689
+                $this->result->addChange('ldap_port', $p);
690
+                return $this->result;
691
+            }
692
+        }
693
+
694
+        //custom port, undetected (we do not brute force)
695
+        return false;
696
+    }
697
+
698
+    /**
699
+     * tries to determine a base dn from User DN or LDAP Host
700
+     * @return WizardResult|false WizardResult on success, false otherwise
701
+     */
702
+    public function guessBaseDN() {
703
+        if(!$this->checkRequirements(array('ldapHost',
704
+                                            'ldapPort',
705
+                                            ))) {
706
+            return false;
707
+        }
708
+
709
+        //check whether a DN is given in the agent name (99.9% of all cases)
710
+        $base = null;
711
+        $i = stripos($this->configuration->ldapAgentName, 'dc=');
712
+        if($i !== false) {
713
+            $base = substr($this->configuration->ldapAgentName, $i);
714
+            if($this->testBaseDN($base)) {
715
+                $this->applyFind('ldap_base', $base);
716
+                return $this->result;
717
+            }
718
+        }
719
+
720
+        //this did not help :(
721
+        //Let's see whether we can parse the Host URL and convert the domain to
722
+        //a base DN
723
+        $helper = new Helper();
724
+        $domain = $helper->getDomainFromURL($this->configuration->ldapHost);
725
+        if(!$domain) {
726
+            return false;
727
+        }
728
+
729
+        $dparts = explode('.', $domain);
730
+        while(count($dparts) > 0) {
731
+            $base2 = 'dc=' . implode(',dc=', $dparts);
732
+            if ($base !== $base2 && $this->testBaseDN($base2)) {
733
+                $this->applyFind('ldap_base', $base2);
734
+                return $this->result;
735
+            }
736
+            array_shift($dparts);
737
+        }
738
+
739
+        return false;
740
+    }
741
+
742
+    /**
743
+     * sets the found value for the configuration key in the WizardResult
744
+     * as well as in the Configuration instance
745
+     * @param string $key the configuration key
746
+     * @param string $value the (detected) value
747
+     *
748
+     */
749
+    private function applyFind($key, $value) {
750
+        $this->result->addChange($key, $value);
751
+        $this->configuration->setConfiguration(array($key => $value));
752
+    }
753
+
754
+    /**
755
+     * Checks, whether a port was entered in the Host configuration
756
+     * field. In this case the port will be stripped off, but also stored as
757
+     * setting.
758
+     */
759
+    private function checkHost() {
760
+        $host = $this->configuration->ldapHost;
761
+        $hostInfo = parse_url($host);
762
+
763
+        //removes Port from Host
764
+        if(is_array($hostInfo) && isset($hostInfo['port'])) {
765
+            $port = $hostInfo['port'];
766
+            $host = str_replace(':'.$port, '', $host);
767
+            $this->applyFind('ldap_host', $host);
768
+            $this->applyFind('ldap_port', $port);
769
+        }
770
+    }
771
+
772
+    /**
773
+     * tries to detect the group member association attribute which is
774
+     * one of 'uniqueMember', 'memberUid', 'member'
775
+     * @return string|false, string with the attribute name, false on error
776
+     * @throws \Exception
777
+     */
778
+    private function detectGroupMemberAssoc() {
779
+        $possibleAttrs = array('uniqueMember', 'memberUid', 'member');
780
+        $filter = $this->configuration->ldapGroupFilter;
781
+        if(empty($filter)) {
782
+            return false;
783
+        }
784
+        $cr = $this->getConnection();
785
+        if(!$cr) {
786
+            throw new \Exception('Could not connect to LDAP');
787
+        }
788
+        $base = $this->configuration->ldapBase[0];
789
+        $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
790
+        if(!$this->ldap->isResource($rr)) {
791
+            return false;
792
+        }
793
+        $er = $this->ldap->firstEntry($cr, $rr);
794
+        while(is_resource($er)) {
795
+            $this->ldap->getDN($cr, $er);
796
+            $attrs = $this->ldap->getAttributes($cr, $er);
797
+            $result = array();
798
+            $possibleAttrsCount = count($possibleAttrs);
799
+            for($i = 0; $i < $possibleAttrsCount; $i++) {
800
+                if(isset($attrs[$possibleAttrs[$i]])) {
801
+                    $result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
802
+                }
803
+            }
804
+            if(!empty($result)) {
805
+                natsort($result);
806
+                return key($result);
807
+            }
808
+
809
+            $er = $this->ldap->nextEntry($cr, $er);
810
+        }
811
+
812
+        return false;
813
+    }
814
+
815
+    /**
816
+     * Checks whether for a given BaseDN results will be returned
817
+     * @param string $base the BaseDN to test
818
+     * @return bool true on success, false otherwise
819
+     * @throws \Exception
820
+     */
821
+    private function testBaseDN($base) {
822
+        $cr = $this->getConnection();
823
+        if(!$cr) {
824
+            throw new \Exception('Could not connect to LDAP');
825
+        }
826
+
827
+        //base is there, let's validate it. If we search for anything, we should
828
+        //get a result set > 0 on a proper base
829
+        $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
830
+        if(!$this->ldap->isResource($rr)) {
831
+            $errorNo  = $this->ldap->errno($cr);
832
+            $errorMsg = $this->ldap->error($cr);
833
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
834
+                            ' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO);
835
+            return false;
836
+        }
837
+        $entries = $this->ldap->countEntries($cr, $rr);
838
+        return ($entries !== false) && ($entries > 0);
839
+    }
840
+
841
+    /**
842
+     * Checks whether the server supports memberOf in LDAP Filter.
843
+     * Note: at least in OpenLDAP, availability of memberOf is dependent on
844
+     * a configured objectClass. I.e. not necessarily for all available groups
845
+     * memberOf does work.
846
+     *
847
+     * @return bool true if it does, false otherwise
848
+     * @throws \Exception
849
+     */
850
+    private function testMemberOf() {
851
+        $cr = $this->getConnection();
852
+        if(!$cr) {
853
+            throw new \Exception('Could not connect to LDAP');
854
+        }
855
+        $result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
856
+        if(is_int($result) &&  $result > 0) {
857
+            return true;
858
+        }
859
+        return false;
860
+    }
861
+
862
+    /**
863
+     * creates an LDAP Filter from given configuration
864
+     * @param integer $filterType int, for which use case the filter shall be created
865
+     * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or
866
+     * self::LFILTER_GROUP_LIST
867
+     * @return string|false string with the filter on success, false otherwise
868
+     * @throws \Exception
869
+     */
870
+    private function composeLdapFilter($filterType) {
871
+        $filter = '';
872
+        $parts = 0;
873
+        switch ($filterType) {
874
+            case self::LFILTER_USER_LIST:
875
+                $objcs = $this->configuration->ldapUserFilterObjectclass;
876
+                //glue objectclasses
877
+                if(is_array($objcs) && count($objcs) > 0) {
878
+                    $filter .= '(|';
879
+                    foreach($objcs as $objc) {
880
+                        $filter .= '(objectclass=' . $objc . ')';
881
+                    }
882
+                    $filter .= ')';
883
+                    $parts++;
884
+                }
885
+                //glue group memberships
886
+                if($this->configuration->hasMemberOfFilterSupport) {
887
+                    $cns = $this->configuration->ldapUserFilterGroups;
888
+                    if(is_array($cns) && count($cns) > 0) {
889
+                        $filter .= '(|';
890
+                        $cr = $this->getConnection();
891
+                        if(!$cr) {
892
+                            throw new \Exception('Could not connect to LDAP');
893
+                        }
894
+                        $base = $this->configuration->ldapBase[0];
895
+                        foreach($cns as $cn) {
896
+                            $rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
897
+                            if(!$this->ldap->isResource($rr)) {
898
+                                continue;
899
+                            }
900
+                            $er = $this->ldap->firstEntry($cr, $rr);
901
+                            $attrs = $this->ldap->getAttributes($cr, $er);
902
+                            $dn = $this->ldap->getDN($cr, $er);
903
+                            if(empty($dn)) {
904
+                                continue;
905
+                            }
906
+                            $filterPart = '(memberof=' . $dn . ')';
907
+                            if(isset($attrs['primaryGroupToken'])) {
908
+                                $pgt = $attrs['primaryGroupToken'][0];
909
+                                $primaryFilterPart = '(primaryGroupID=' . $pgt .')';
910
+                                $filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
911
+                            }
912
+                            $filter .= $filterPart;
913
+                        }
914
+                        $filter .= ')';
915
+                    }
916
+                    $parts++;
917
+                }
918
+                //wrap parts in AND condition
919
+                if($parts > 1) {
920
+                    $filter = '(&' . $filter . ')';
921
+                }
922
+                if(empty($filter)) {
923
+                    $filter = '(objectclass=*)';
924
+                }
925
+                break;
926
+
927
+            case self::LFILTER_GROUP_LIST:
928
+                $objcs = $this->configuration->ldapGroupFilterObjectclass;
929
+                //glue objectclasses
930
+                if(is_array($objcs) && count($objcs) > 0) {
931
+                    $filter .= '(|';
932
+                    foreach($objcs as $objc) {
933
+                        $filter .= '(objectclass=' . $objc . ')';
934
+                    }
935
+                    $filter .= ')';
936
+                    $parts++;
937
+                }
938
+                //glue group memberships
939
+                $cns = $this->configuration->ldapGroupFilterGroups;
940
+                if(is_array($cns) && count($cns) > 0) {
941
+                    $filter .= '(|';
942
+                    $base = $this->configuration->ldapBase[0];
943
+                    foreach($cns as $cn) {
944
+                        $filter .= '(cn=' . $cn . ')';
945
+                    }
946
+                    $filter .= ')';
947
+                }
948
+                $parts++;
949
+                //wrap parts in AND condition
950
+                if($parts > 1) {
951
+                    $filter = '(&' . $filter . ')';
952
+                }
953
+                break;
954
+
955
+            case self::LFILTER_LOGIN:
956
+                $ulf = $this->configuration->ldapUserFilter;
957
+                $loginpart = '=%uid';
958
+                $filterUsername = '';
959
+                $userAttributes = $this->getUserAttributes();
960
+                $userAttributes = array_change_key_case(array_flip($userAttributes));
961
+                $parts = 0;
962
+
963
+                if($this->configuration->ldapLoginFilterUsername === '1') {
964
+                    $attr = '';
965
+                    if(isset($userAttributes['uid'])) {
966
+                        $attr = 'uid';
967
+                    } else if(isset($userAttributes['samaccountname'])) {
968
+                        $attr = 'samaccountname';
969
+                    } else if(isset($userAttributes['cn'])) {
970
+                        //fallback
971
+                        $attr = 'cn';
972
+                    }
973
+                    if(!empty($attr)) {
974
+                        $filterUsername = '(' . $attr . $loginpart . ')';
975
+                        $parts++;
976
+                    }
977
+                }
978
+
979
+                $filterEmail = '';
980
+                if($this->configuration->ldapLoginFilterEmail === '1') {
981
+                    $filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
982
+                    $parts++;
983
+                }
984
+
985
+                $filterAttributes = '';
986
+                $attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
987
+                if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
988
+                    $filterAttributes = '(|';
989
+                    foreach($attrsToFilter as $attribute) {
990
+                        $filterAttributes .= '(' . $attribute . $loginpart . ')';
991
+                    }
992
+                    $filterAttributes .= ')';
993
+                    $parts++;
994
+                }
995
+
996
+                $filterLogin = '';
997
+                if($parts > 1) {
998
+                    $filterLogin = '(|';
999
+                }
1000
+                $filterLogin .= $filterUsername;
1001
+                $filterLogin .= $filterEmail;
1002
+                $filterLogin .= $filterAttributes;
1003
+                if($parts > 1) {
1004
+                    $filterLogin .= ')';
1005
+                }
1006
+
1007
+                $filter = '(&'.$ulf.$filterLogin.')';
1008
+                break;
1009
+        }
1010
+
1011
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG);
1012
+
1013
+        return $filter;
1014
+    }
1015
+
1016
+    /**
1017
+     * Connects and Binds to an LDAP Server
1018
+     * @param int $port the port to connect with
1019
+     * @param bool $tls whether startTLS is to be used
1020
+     * @param bool $ncc
1021
+     * @return bool
1022
+     * @throws \Exception
1023
+     */
1024
+    private function connectAndBind($port = 389, $tls = false, $ncc = false) {
1025
+        if($ncc) {
1026
+            //No certificate check
1027
+            //FIXME: undo afterwards
1028
+            putenv('LDAPTLS_REQCERT=never');
1029
+        }
1030
+
1031
+        //connect, does not really trigger any server communication
1032
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG);
1033
+        $host = $this->configuration->ldapHost;
1034
+        $hostInfo = parse_url($host);
1035
+        if(!$hostInfo) {
1036
+            throw new \Exception($this->l->t('Invalid Host'));
1037
+        }
1038
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1039
+        $cr = $this->ldap->connect($host, $port);
1040
+        if(!is_resource($cr)) {
1041
+            throw new \Exception($this->l->t('Invalid Host'));
1042
+        }
1043
+
1044
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG);
1045
+        //set LDAP options
1046
+        $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1047
+        $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1048
+        $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1049
+
1050
+        try {
1051
+            if($tls) {
1052
+                $isTlsWorking = @$this->ldap->startTls($cr);
1053
+                if(!$isTlsWorking) {
1054
+                    return false;
1055
+                }
1056
+            }
1057
+
1058
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG);
1059
+            //interesting part: do the bind!
1060
+            $login = $this->ldap->bind($cr,
1061
+                $this->configuration->ldapAgentName,
1062
+                $this->configuration->ldapAgentPassword
1063
+            );
1064
+            $errNo = $this->ldap->errno($cr);
1065
+            $error = ldap_error($cr);
1066
+            $this->ldap->unbind($cr);
1067
+        } catch(ServerNotAvailableException $e) {
1068
+            return false;
1069
+        }
1070
+
1071
+        if($login === true) {
1072
+            $this->ldap->unbind($cr);
1073
+            if($ncc) {
1074
+                throw new \Exception('Certificate cannot be validated.');
1075
+            }
1076
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1077
+            return true;
1078
+        }
1079
+
1080
+        if($errNo === -1 || ($errNo === 2 && $ncc)) {
1081
+            //host, port or TLS wrong
1082
+            return false;
1083
+        } else if ($errNo === 2) {
1084
+            return $this->connectAndBind($port, $tls, true);
1085
+        }
1086
+        throw new \Exception($error, $errNo);
1087
+    }
1088
+
1089
+    /**
1090
+     * checks whether a valid combination of agent and password has been
1091
+     * provided (either two values or nothing for anonymous connect)
1092
+     * @return bool, true if everything is fine, false otherwise
1093
+     */
1094
+    private function checkAgentRequirements() {
1095
+        $agent = $this->configuration->ldapAgentName;
1096
+        $pwd = $this->configuration->ldapAgentPassword;
1097
+
1098
+        return ( (!empty($agent) && !empty($pwd))
1099
+               || (empty($agent) &&  empty($pwd)));
1100
+    }
1101
+
1102
+    /**
1103
+     * @param array $reqs
1104
+     * @return bool
1105
+     */
1106
+    private function checkRequirements($reqs) {
1107
+        $this->checkAgentRequirements();
1108
+        foreach($reqs as $option) {
1109
+            $value = $this->configuration->$option;
1110
+            if(empty($value)) {
1111
+                return false;
1112
+            }
1113
+        }
1114
+        return true;
1115
+    }
1116
+
1117
+    /**
1118
+     * does a cumulativeSearch on LDAP to get different values of a
1119
+     * specified attribute
1120
+     * @param string[] $filters array, the filters that shall be used in the search
1121
+     * @param string $attr the attribute of which a list of values shall be returned
1122
+     * @param int $dnReadLimit the amount of how many DNs should be analyzed.
1123
+     * The lower, the faster
1124
+     * @param string $maxF string. if not null, this variable will have the filter that
1125
+     * yields most result entries
1126
+     * @return array|false an array with the values on success, false otherwise
1127
+     */
1128
+    public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) {
1129
+        $dnRead = array();
1130
+        $foundItems = array();
1131
+        $maxEntries = 0;
1132
+        if(!is_array($this->configuration->ldapBase)
1133
+           || !isset($this->configuration->ldapBase[0])) {
1134
+            return false;
1135
+        }
1136
+        $base = $this->configuration->ldapBase[0];
1137
+        $cr = $this->getConnection();
1138
+        if(!$this->ldap->isResource($cr)) {
1139
+            return false;
1140
+        }
1141
+        $lastFilter = null;
1142
+        if(isset($filters[count($filters)-1])) {
1143
+            $lastFilter = $filters[count($filters)-1];
1144
+        }
1145
+        foreach($filters as $filter) {
1146
+            if($lastFilter === $filter && count($foundItems) > 0) {
1147
+                //skip when the filter is a wildcard and results were found
1148
+                continue;
1149
+            }
1150
+            // 20k limit for performance and reason
1151
+            $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1152
+            if(!$this->ldap->isResource($rr)) {
1153
+                continue;
1154
+            }
1155
+            $entries = $this->ldap->countEntries($cr, $rr);
1156
+            $getEntryFunc = 'firstEntry';
1157
+            if(($entries !== false) && ($entries > 0)) {
1158
+                if(!is_null($maxF) && $entries > $maxEntries) {
1159
+                    $maxEntries = $entries;
1160
+                    $maxF = $filter;
1161
+                }
1162
+                $dnReadCount = 0;
1163
+                do {
1164
+                    $entry = $this->ldap->$getEntryFunc($cr, $rr);
1165
+                    $getEntryFunc = 'nextEntry';
1166
+                    if(!$this->ldap->isResource($entry)) {
1167
+                        continue 2;
1168
+                    }
1169
+                    $rr = $entry; //will be expected by nextEntry next round
1170
+                    $attributes = $this->ldap->getAttributes($cr, $entry);
1171
+                    $dn = $this->ldap->getDN($cr, $entry);
1172
+                    if($dn === false || in_array($dn, $dnRead)) {
1173
+                        continue;
1174
+                    }
1175
+                    $newItems = array();
1176
+                    $state = $this->getAttributeValuesFromEntry($attributes,
1177
+                                                                $attr,
1178
+                                                                $newItems);
1179
+                    $dnReadCount++;
1180
+                    $foundItems = array_merge($foundItems, $newItems);
1181
+                    $this->resultCache[$dn][$attr] = $newItems;
1182
+                    $dnRead[] = $dn;
1183
+                } while(($state === self::LRESULT_PROCESSED_SKIP
1184
+                        || $this->ldap->isResource($entry))
1185
+                        && ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1186
+            }
1187
+        }
1188
+
1189
+        return array_unique($foundItems);
1190
+    }
1191
+
1192
+    /**
1193
+     * determines if and which $attr are available on the LDAP server
1194
+     * @param string[] $objectclasses the objectclasses to use as search filter
1195
+     * @param string $attr the attribute to look for
1196
+     * @param string $dbkey the dbkey of the setting the feature is connected to
1197
+     * @param string $confkey the confkey counterpart for the $dbkey as used in the
1198
+     * Configuration class
1199
+     * @param bool $po whether the objectClass with most result entries
1200
+     * shall be pre-selected via the result
1201
+     * @return array|false list of found items.
1202
+     * @throws \Exception
1203
+     */
1204
+    private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1205
+        $cr = $this->getConnection();
1206
+        if(!$cr) {
1207
+            throw new \Exception('Could not connect to LDAP');
1208
+        }
1209
+        $p = 'objectclass=';
1210
+        foreach($objectclasses as $key => $value) {
1211
+            $objectclasses[$key] = $p.$value;
1212
+        }
1213
+        $maxEntryObjC = '';
1214
+
1215
+        //how deep to dig?
1216
+        //When looking for objectclasses, testing few entries is sufficient,
1217
+        $dig = 3;
1218
+
1219
+        $availableFeatures =
1220
+            $this->cumulativeSearchOnAttribute($objectclasses, $attr,
1221
+                                                $dig, $maxEntryObjC);
1222
+        if(is_array($availableFeatures)
1223
+           && count($availableFeatures) > 0) {
1224
+            natcasesort($availableFeatures);
1225
+            //natcasesort keeps indices, but we must get rid of them for proper
1226
+            //sorting in the web UI. Therefore: array_values
1227
+            $this->result->addOptions($dbkey, array_values($availableFeatures));
1228
+        } else {
1229
+            throw new \Exception(self::$l->t('Could not find the desired feature'));
1230
+        }
1231
+
1232
+        $setFeatures = $this->configuration->$confkey;
1233
+        if(is_array($setFeatures) && !empty($setFeatures)) {
1234
+            //something is already configured? pre-select it.
1235
+            $this->result->addChange($dbkey, $setFeatures);
1236
+        } else if($po && !empty($maxEntryObjC)) {
1237
+            //pre-select objectclass with most result entries
1238
+            $maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1239
+            $this->applyFind($dbkey, $maxEntryObjC);
1240
+            $this->result->addChange($dbkey, $maxEntryObjC);
1241
+        }
1242
+
1243
+        return $availableFeatures;
1244
+    }
1245
+
1246
+    /**
1247
+     * appends a list of values fr
1248
+     * @param resource $result the return value from ldap_get_attributes
1249
+     * @param string $attribute the attribute values to look for
1250
+     * @param array &$known new values will be appended here
1251
+     * @return int, state on of the class constants LRESULT_PROCESSED_OK,
1252
+     * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1253
+     */
1254
+    private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1255
+        if(!is_array($result)
1256
+           || !isset($result['count'])
1257
+           || !$result['count'] > 0) {
1258
+            return self::LRESULT_PROCESSED_INVALID;
1259
+        }
1260
+
1261
+        // strtolower on all keys for proper comparison
1262
+        $result = \OCP\Util::mb_array_change_key_case($result);
1263
+        $attribute = strtolower($attribute);
1264
+        if(isset($result[$attribute])) {
1265
+            foreach($result[$attribute] as $key => $val) {
1266
+                if($key === 'count') {
1267
+                    continue;
1268
+                }
1269
+                if(!in_array($val, $known)) {
1270
+                    $known[] = $val;
1271
+                }
1272
+            }
1273
+            return self::LRESULT_PROCESSED_OK;
1274
+        } else {
1275
+            return self::LRESULT_PROCESSED_SKIP;
1276
+        }
1277
+    }
1278
+
1279
+    /**
1280
+     * @return bool|mixed
1281
+     */
1282
+    private function getConnection() {
1283
+        if(!is_null($this->cr)) {
1284
+            return $this->cr;
1285
+        }
1286
+
1287
+        $cr = $this->ldap->connect(
1288
+            $this->configuration->ldapHost,
1289
+            $this->configuration->ldapPort
1290
+        );
1291
+
1292
+        $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1293
+        $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1294
+        $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1295
+        if($this->configuration->ldapTLS === 1) {
1296
+            $this->ldap->startTls($cr);
1297
+        }
1298
+
1299
+        $lo = @$this->ldap->bind($cr,
1300
+                                    $this->configuration->ldapAgentName,
1301
+                                    $this->configuration->ldapAgentPassword);
1302
+        if($lo === true) {
1303
+            $this->$cr = $cr;
1304
+            return $cr;
1305
+        }
1306
+
1307
+        return false;
1308
+    }
1309
+
1310
+    /**
1311
+     * @return array
1312
+     */
1313
+    private function getDefaultLdapPortSettings() {
1314
+        static $settings = array(
1315
+                                array('port' => 7636, 'tls' => false),
1316
+                                array('port' =>  636, 'tls' => false),
1317
+                                array('port' => 7389, 'tls' => true),
1318
+                                array('port' =>  389, 'tls' => true),
1319
+                                array('port' => 7389, 'tls' => false),
1320
+                                array('port' =>  389, 'tls' => false),
1321
+                            );
1322
+        return $settings;
1323
+    }
1324
+
1325
+    /**
1326
+     * @return array
1327
+     */
1328
+    private function getPortSettingsToTry() {
1329
+        //389 ← LDAP / Unencrypted or StartTLS
1330
+        //636 ← LDAPS / SSL
1331
+        //7xxx ← UCS. need to be checked first, because both ports may be open
1332
+        $host = $this->configuration->ldapHost;
1333
+        $port = intval($this->configuration->ldapPort);
1334
+        $portSettings = array();
1335
+
1336
+        //In case the port is already provided, we will check this first
1337
+        if($port > 0) {
1338
+            $hostInfo = parse_url($host);
1339
+            if(!(is_array($hostInfo)
1340
+                && isset($hostInfo['scheme'])
1341
+                && stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1342
+                $portSettings[] = array('port' => $port, 'tls' => true);
1343
+            }
1344
+            $portSettings[] =array('port' => $port, 'tls' => false);
1345
+        }
1346
+
1347
+        //default ports
1348
+        $portSettings = array_merge($portSettings,
1349
+                                    $this->getDefaultLdapPortSettings());
1350
+
1351
+        return $portSettings;
1352
+    }
1353 1353
 
1354 1354
 
1355 1355
 }
Please login to merge, or discard this patch.
Spacing   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -63,7 +63,7 @@  discard block
 block discarded – undo
63 63
 	public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
64 64
 		parent::__construct($ldap);
65 65
 		$this->configuration = $configuration;
66
-		if(is_null(Wizard::$l)) {
66
+		if (is_null(Wizard::$l)) {
67 67
 			Wizard::$l = \OC::$server->getL10N('user_ldap');
68 68
 		}
69 69
 		$this->access = $access;
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 	}
72 72
 
73 73
 	public function  __destruct() {
74
-		if($this->result->hasChanges()) {
74
+		if ($this->result->hasChanges()) {
75 75
 			$this->configuration->saveConfiguration();
76 76
 		}
77 77
 	}
@@ -86,18 +86,18 @@  discard block
 block discarded – undo
86 86
 	 */
87 87
 	public function countEntries($filter, $type) {
88 88
 		$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
89
-		if($type === 'users') {
89
+		if ($type === 'users') {
90 90
 			$reqs[] = 'ldapUserFilter';
91 91
 		}
92
-		if(!$this->checkRequirements($reqs)) {
92
+		if (!$this->checkRequirements($reqs)) {
93 93
 			throw new \Exception('Requirements not met', 400);
94 94
 		}
95 95
 
96 96
 		$attr = array('dn'); // default
97 97
 		$limit = 1001;
98
-		if($type === 'groups') {
99
-			$result =  $this->access->countGroups($filter, $attr, $limit);
100
-		} else if($type === 'users') {
98
+		if ($type === 'groups') {
99
+			$result = $this->access->countGroups($filter, $attr, $limit);
100
+		} else if ($type === 'users') {
101 101
 			$result = $this->access->countUsers($filter, $attr, $limit);
102 102
 		} else if ($type === 'objects') {
103 103
 			$result = $this->access->countObjects($limit);
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
 	 */
118 118
 	private function formatCountResult($count) {
119 119
 		$formatted = ($count !== false) ? $count : 0;
120
-		if($formatted > 1000) {
120
+		if ($formatted > 1000) {
121 121
 			$formatted = '> 1000';
122 122
 		}
123 123
 		return $formatted;
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
 	public function countGroups() {
127 127
 		$filter = $this->configuration->ldapGroupFilter;
128 128
 
129
-		if(empty($filter)) {
129
+		if (empty($filter)) {
130 130
 			$output = self::$l->n('%s group found', '%s groups found', 0, array(0));
131 131
 			$this->result->addChange('ldap_group_count', $output);
132 132
 			return $this->result;
@@ -136,7 +136,7 @@  discard block
 block discarded – undo
136 136
 			$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
137 137
 		} catch (\Exception $e) {
138 138
 			//400 can be ignored, 500 is forwarded
139
-			if($e->getCode() === 500) {
139
+			if ($e->getCode() === 500) {
140 140
 				throw $e;
141 141
 			}
142 142
 			return false;
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 	public function countInBaseDN() {
169 169
 		// we don't need to provide a filter in this case
170 170
 		$total = $this->countEntries(null, 'objects');
171
-		if($total === false) {
171
+		if ($total === false) {
172 172
 			throw new \Exception('invalid results received');
173 173
 		}
174 174
 		$this->result->addChange('ldap_test_base', $total);
@@ -182,7 +182,7 @@  discard block
 block discarded – undo
182 182
 	 * @return int|bool
183 183
 	 */
184 184
 	public function countUsersWithAttribute($attr, $existsCheck = false) {
185
-		if(!$this->checkRequirements(array('ldapHost',
185
+		if (!$this->checkRequirements(array('ldapHost',
186 186
 										   'ldapPort',
187 187
 										   'ldapBase',
188 188
 										   'ldapUserFilter',
@@ -192,7 +192,7 @@  discard block
 block discarded – undo
192 192
 
193 193
 		$filter = $this->access->combineFilterWithAnd(array(
194 194
 			$this->configuration->ldapUserFilter,
195
-			$attr . '=*'
195
+			$attr.'=*'
196 196
 		));
197 197
 
198 198
 		$limit = ($existsCheck === false) ? null : 1;
@@ -207,7 +207,7 @@  discard block
 block discarded – undo
207 207
 	 * @throws \Exception
208 208
 	 */
209 209
 	public function detectUserDisplayNameAttribute() {
210
-		if(!$this->checkRequirements(array('ldapHost',
210
+		if (!$this->checkRequirements(array('ldapHost',
211 211
 										'ldapPort',
212 212
 										'ldapBase',
213 213
 										'ldapUserFilter',
@@ -216,11 +216,11 @@  discard block
 block discarded – undo
216 216
 		}
217 217
 
218 218
 		$attr = $this->configuration->ldapUserDisplayName;
219
-		if($attr !== 'displayName' && !empty($attr)) {
219
+		if ($attr !== 'displayName' && !empty($attr)) {
220 220
 			// most likely not the default value with upper case N,
221 221
 			// verify it still produces a result
222 222
 			$count = intval($this->countUsersWithAttribute($attr, true));
223
-			if($count > 0) {
223
+			if ($count > 0) {
224 224
 				//no change, but we sent it back to make sure the user interface
225 225
 				//is still correct, even if the ajax call was cancelled inbetween
226 226
 				$this->result->addChange('ldap_display_name', $attr);
@@ -233,7 +233,7 @@  discard block
 block discarded – undo
233 233
 		foreach ($displayNameAttrs as $attr) {
234 234
 			$count = intval($this->countUsersWithAttribute($attr, true));
235 235
 
236
-			if($count > 0) {
236
+			if ($count > 0) {
237 237
 				$this->applyFind('ldap_display_name', $attr);
238 238
 				return $this->result;
239 239
 			}
@@ -249,7 +249,7 @@  discard block
 block discarded – undo
249 249
 	 * @return WizardResult|bool
250 250
 	 */
251 251
 	public function detectEmailAttribute() {
252
-		if(!$this->checkRequirements(array('ldapHost',
252
+		if (!$this->checkRequirements(array('ldapHost',
253 253
 										   'ldapPort',
254 254
 										   'ldapBase',
255 255
 										   'ldapUserFilter',
@@ -258,9 +258,9 @@  discard block
 block discarded – undo
258 258
 		}
259 259
 
260 260
 		$attr = $this->configuration->ldapEmailAttribute;
261
-		if(!empty($attr)) {
261
+		if (!empty($attr)) {
262 262
 			$count = intval($this->countUsersWithAttribute($attr, true));
263
-			if($count > 0) {
263
+			if ($count > 0) {
264 264
 				return false;
265 265
 			}
266 266
 			$writeLog = true;
@@ -271,19 +271,19 @@  discard block
 block discarded – undo
271 271
 		$emailAttributes = array('mail', 'mailPrimaryAddress');
272 272
 		$winner = '';
273 273
 		$maxUsers = 0;
274
-		foreach($emailAttributes as $attr) {
274
+		foreach ($emailAttributes as $attr) {
275 275
 			$count = $this->countUsersWithAttribute($attr);
276
-			if($count > $maxUsers) {
276
+			if ($count > $maxUsers) {
277 277
 				$maxUsers = $count;
278 278
 				$winner = $attr;
279 279
 			}
280 280
 		}
281 281
 
282
-		if($winner !== '') {
282
+		if ($winner !== '') {
283 283
 			$this->applyFind('ldap_email_attr', $winner);
284
-			if($writeLog) {
285
-				\OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
286
-					'automatically been reset, because the original value ' .
284
+			if ($writeLog) {
285
+				\OCP\Util::writeLog('user_ldap', 'The mail attribute has '.
286
+					'automatically been reset, because the original value '.
287 287
 					'did not return any results.', \OCP\Util::INFO);
288 288
 			}
289 289
 		}
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
 	 * @throws \Exception
297 297
 	 */
298 298
 	public function determineAttributes() {
299
-		if(!$this->checkRequirements(array('ldapHost',
299
+		if (!$this->checkRequirements(array('ldapHost',
300 300
 										   'ldapPort',
301 301
 										   'ldapBase',
302 302
 										   'ldapUserFilter',
@@ -312,7 +312,7 @@  discard block
 block discarded – undo
312 312
 		$this->result->addOptions('ldap_loginfilter_attributes', $attributes);
313 313
 
314 314
 		$selected = $this->configuration->ldapLoginFilterAttributes;
315
-		if(is_array($selected) && !empty($selected)) {
315
+		if (is_array($selected) && !empty($selected)) {
316 316
 			$this->result->addChange('ldap_loginfilter_attributes', $selected);
317 317
 		}
318 318
 
@@ -325,7 +325,7 @@  discard block
 block discarded – undo
325 325
 	 * @throws \Exception
326 326
 	 */
327 327
 	private function getUserAttributes() {
328
-		if(!$this->checkRequirements(array('ldapHost',
328
+		if (!$this->checkRequirements(array('ldapHost',
329 329
 										   'ldapPort',
330 330
 										   'ldapBase',
331 331
 										   'ldapUserFilter',
@@ -333,20 +333,20 @@  discard block
 block discarded – undo
333 333
 			return  false;
334 334
 		}
335 335
 		$cr = $this->getConnection();
336
-		if(!$cr) {
336
+		if (!$cr) {
337 337
 			throw new \Exception('Could not connect to LDAP');
338 338
 		}
339 339
 
340 340
 		$base = $this->configuration->ldapBase[0];
341 341
 		$filter = $this->configuration->ldapUserFilter;
342 342
 		$rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
343
-		if(!$this->ldap->isResource($rr)) {
343
+		if (!$this->ldap->isResource($rr)) {
344 344
 			return false;
345 345
 		}
346 346
 		$er = $this->ldap->firstEntry($cr, $rr);
347 347
 		$attributes = $this->ldap->getAttributes($cr, $er);
348 348
 		$pureAttributes = array();
349
-		for($i = 0; $i < $attributes['count']; $i++) {
349
+		for ($i = 0; $i < $attributes['count']; $i++) {
350 350
 			$pureAttributes[] = $attributes[$i];
351 351
 		}
352 352
 
@@ -381,23 +381,23 @@  discard block
 block discarded – undo
381 381
 	 * @throws \Exception
382 382
 	 */
383 383
 	private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
384
-		if(!$this->checkRequirements(array('ldapHost',
384
+		if (!$this->checkRequirements(array('ldapHost',
385 385
 										   'ldapPort',
386 386
 										   'ldapBase',
387 387
 										   ))) {
388 388
 			return  false;
389 389
 		}
390 390
 		$cr = $this->getConnection();
391
-		if(!$cr) {
391
+		if (!$cr) {
392 392
 			throw new \Exception('Could not connect to LDAP');
393 393
 		}
394 394
 
395 395
 		$this->fetchGroups($dbKey, $confKey);
396 396
 
397
-		if($testMemberOf) {
397
+		if ($testMemberOf) {
398 398
 			$this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
399 399
 			$this->result->markChange();
400
-			if(!$this->configuration->hasMemberOfFilterSupport) {
400
+			if (!$this->configuration->hasMemberOfFilterSupport) {
401 401
 				throw new \Exception('memberOf is not supported by the server');
402 402
 			}
403 403
 		}
@@ -417,7 +417,7 @@  discard block
 block discarded – undo
417 417
 		$obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames');
418 418
 
419 419
 		$filterParts = array();
420
-		foreach($obclasses as $obclass) {
420
+		foreach ($obclasses as $obclass) {
421 421
 			$filterParts[] = 'objectclass='.$obclass;
422 422
 		}
423 423
 		//we filter for everything
@@ -434,8 +434,8 @@  discard block
 block discarded – undo
434 434
 			// we need to request dn additionally here, otherwise memberOf
435 435
 			// detection will fail later
436 436
 			$result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
437
-			foreach($result as $item) {
438
-				if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
437
+			foreach ($result as $item) {
438
+				if (!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
439 439
 					// just in case - no issue known
440 440
 					continue;
441 441
 				}
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
 			$offset += $limit;
446 446
 		} while ($this->access->hasMoreResults());
447 447
 
448
-		if(count($groupNames) > 0) {
448
+		if (count($groupNames) > 0) {
449 449
 			natsort($groupNames);
450 450
 			$this->result->addOptions($dbKey, array_values($groupNames));
451 451
 		} else {
@@ -453,7 +453,7 @@  discard block
 block discarded – undo
453 453
 		}
454 454
 
455 455
 		$setFeatures = $this->configuration->$confKey;
456
-		if(is_array($setFeatures) && !empty($setFeatures)) {
456
+		if (is_array($setFeatures) && !empty($setFeatures)) {
457 457
 			//something is already configured? pre-select it.
458 458
 			$this->result->addChange($dbKey, $setFeatures);
459 459
 		}
@@ -461,14 +461,14 @@  discard block
 block discarded – undo
461 461
 	}
462 462
 
463 463
 	public function determineGroupMemberAssoc() {
464
-		if(!$this->checkRequirements(array('ldapHost',
464
+		if (!$this->checkRequirements(array('ldapHost',
465 465
 										   'ldapPort',
466 466
 										   'ldapGroupFilter',
467 467
 										   ))) {
468 468
 			return  false;
469 469
 		}
470 470
 		$attribute = $this->detectGroupMemberAssoc();
471
-		if($attribute === false) {
471
+		if ($attribute === false) {
472 472
 			return false;
473 473
 		}
474 474
 		$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
@@ -483,14 +483,14 @@  discard block
 block discarded – undo
483 483
 	 * @throws \Exception
484 484
 	 */
485 485
 	public function determineGroupObjectClasses() {
486
-		if(!$this->checkRequirements(array('ldapHost',
486
+		if (!$this->checkRequirements(array('ldapHost',
487 487
 										   'ldapPort',
488 488
 										   'ldapBase',
489 489
 										   ))) {
490 490
 			return  false;
491 491
 		}
492 492
 		$cr = $this->getConnection();
493
-		if(!$cr) {
493
+		if (!$cr) {
494 494
 			throw new \Exception('Could not connect to LDAP');
495 495
 		}
496 496
 
@@ -510,14 +510,14 @@  discard block
 block discarded – undo
510 510
 	 * @throws \Exception
511 511
 	 */
512 512
 	public function determineUserObjectClasses() {
513
-		if(!$this->checkRequirements(array('ldapHost',
513
+		if (!$this->checkRequirements(array('ldapHost',
514 514
 										   'ldapPort',
515 515
 										   'ldapBase',
516 516
 										   ))) {
517 517
 			return  false;
518 518
 		}
519 519
 		$cr = $this->getConnection();
520
-		if(!$cr) {
520
+		if (!$cr) {
521 521
 			throw new \Exception('Could not connect to LDAP');
522 522
 		}
523 523
 
@@ -540,7 +540,7 @@  discard block
 block discarded – undo
540 540
 	 * @throws \Exception
541 541
 	 */
542 542
 	public function getGroupFilter() {
543
-		if(!$this->checkRequirements(array('ldapHost',
543
+		if (!$this->checkRequirements(array('ldapHost',
544 544
 										   'ldapPort',
545 545
 										   'ldapBase',
546 546
 										   ))) {
@@ -548,7 +548,7 @@  discard block
 block discarded – undo
548 548
 		}
549 549
 		//make sure the use display name is set
550 550
 		$displayName = $this->configuration->ldapGroupDisplayName;
551
-		if(empty($displayName)) {
551
+		if (empty($displayName)) {
552 552
 			$d = $this->configuration->getDefaults();
553 553
 			$this->applyFind('ldap_group_display_name',
554 554
 							 $d['ldap_group_display_name']);
@@ -564,7 +564,7 @@  discard block
 block discarded – undo
564 564
 	 * @throws \Exception
565 565
 	 */
566 566
 	public function getUserListFilter() {
567
-		if(!$this->checkRequirements(array('ldapHost',
567
+		if (!$this->checkRequirements(array('ldapHost',
568 568
 										   'ldapPort',
569 569
 										   'ldapBase',
570 570
 										   ))) {
@@ -572,12 +572,12 @@  discard block
 block discarded – undo
572 572
 		}
573 573
 		//make sure the use display name is set
574 574
 		$displayName = $this->configuration->ldapUserDisplayName;
575
-		if(empty($displayName)) {
575
+		if (empty($displayName)) {
576 576
 			$d = $this->configuration->getDefaults();
577 577
 			$this->applyFind('ldap_display_name', $d['ldap_display_name']);
578 578
 		}
579 579
 		$filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
580
-		if(!$filter) {
580
+		if (!$filter) {
581 581
 			throw new \Exception('Cannot create filter');
582 582
 		}
583 583
 
@@ -590,7 +590,7 @@  discard block
 block discarded – undo
590 590
 	 * @throws \Exception
591 591
 	 */
592 592
 	public function getUserLoginFilter() {
593
-		if(!$this->checkRequirements(array('ldapHost',
593
+		if (!$this->checkRequirements(array('ldapHost',
594 594
 										   'ldapPort',
595 595
 										   'ldapBase',
596 596
 										   'ldapUserFilter',
@@ -599,7 +599,7 @@  discard block
 block discarded – undo
599 599
 		}
600 600
 
601 601
 		$filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
602
-		if(!$filter) {
602
+		if (!$filter) {
603 603
 			throw new \Exception('Cannot create filter');
604 604
 		}
605 605
 
@@ -613,7 +613,7 @@  discard block
 block discarded – undo
613 613
 	 * @throws \Exception
614 614
 	 */
615 615
 	public function testLoginName($loginName) {
616
-		if(!$this->checkRequirements(array('ldapHost',
616
+		if (!$this->checkRequirements(array('ldapHost',
617 617
 			'ldapPort',
618 618
 			'ldapBase',
619 619
 			'ldapLoginFilter',
@@ -622,17 +622,17 @@  discard block
 block discarded – undo
622 622
 		}
623 623
 
624 624
 		$cr = $this->access->connection->getConnectionResource();
625
-		if(!$this->ldap->isResource($cr)) {
625
+		if (!$this->ldap->isResource($cr)) {
626 626
 			throw new \Exception('connection error');
627 627
 		}
628 628
 
629
-		if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
629
+		if (mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
630 630
 			=== false) {
631 631
 			throw new \Exception('missing placeholder');
632 632
 		}
633 633
 
634 634
 		$users = $this->access->countUsersByLoginName($loginName);
635
-		if($this->ldap->errno($cr) !== 0) {
635
+		if ($this->ldap->errno($cr) !== 0) {
636 636
 			throw new \Exception($this->ldap->error($cr));
637 637
 		}
638 638
 		$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
@@ -647,22 +647,22 @@  discard block
 block discarded – undo
647 647
 	 * @throws \Exception
648 648
 	 */
649 649
 	public function guessPortAndTLS() {
650
-		if(!$this->checkRequirements(array('ldapHost',
650
+		if (!$this->checkRequirements(array('ldapHost',
651 651
 										   ))) {
652 652
 			return false;
653 653
 		}
654 654
 		$this->checkHost();
655 655
 		$portSettings = $this->getPortSettingsToTry();
656 656
 
657
-		if(!is_array($portSettings)) {
657
+		if (!is_array($portSettings)) {
658 658
 			throw new \Exception(print_r($portSettings, true));
659 659
 		}
660 660
 
661 661
 		//proceed from the best configuration and return on first success
662
-		foreach($portSettings as $setting) {
662
+		foreach ($portSettings as $setting) {
663 663
 			$p = $setting['port'];
664 664
 			$t = $setting['tls'];
665
-			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
665
+			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '.$p.', TLS '.$t, \OCP\Util::DEBUG);
666 666
 			//connectAndBind may throw Exception, it needs to be catched by the
667 667
 			//callee of this method
668 668
 
@@ -672,7 +672,7 @@  discard block
 block discarded – undo
672 672
 				// any reply other than -1 (= cannot connect) is already okay,
673 673
 				// because then we found the server
674 674
 				// unavailable startTLS returns -11
675
-				if($e->getCode() > 0) {
675
+				if ($e->getCode() > 0) {
676 676
 					$settingsFound = true;
677 677
 				} else {
678 678
 					throw $e;
@@ -685,7 +685,7 @@  discard block
 block discarded – undo
685 685
 					'ldapTLS' => intval($t)
686 686
 				);
687 687
 				$this->configuration->setConfiguration($config);
688
-				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
688
+				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port '.$p, \OCP\Util::DEBUG);
689 689
 				$this->result->addChange('ldap_port', $p);
690 690
 				return $this->result;
691 691
 			}
@@ -700,7 +700,7 @@  discard block
 block discarded – undo
700 700
 	 * @return WizardResult|false WizardResult on success, false otherwise
701 701
 	 */
702 702
 	public function guessBaseDN() {
703
-		if(!$this->checkRequirements(array('ldapHost',
703
+		if (!$this->checkRequirements(array('ldapHost',
704 704
 										   'ldapPort',
705 705
 										   ))) {
706 706
 			return false;
@@ -709,9 +709,9 @@  discard block
 block discarded – undo
709 709
 		//check whether a DN is given in the agent name (99.9% of all cases)
710 710
 		$base = null;
711 711
 		$i = stripos($this->configuration->ldapAgentName, 'dc=');
712
-		if($i !== false) {
712
+		if ($i !== false) {
713 713
 			$base = substr($this->configuration->ldapAgentName, $i);
714
-			if($this->testBaseDN($base)) {
714
+			if ($this->testBaseDN($base)) {
715 715
 				$this->applyFind('ldap_base', $base);
716 716
 				return $this->result;
717 717
 			}
@@ -722,13 +722,13 @@  discard block
 block discarded – undo
722 722
 		//a base DN
723 723
 		$helper = new Helper();
724 724
 		$domain = $helper->getDomainFromURL($this->configuration->ldapHost);
725
-		if(!$domain) {
725
+		if (!$domain) {
726 726
 			return false;
727 727
 		}
728 728
 
729 729
 		$dparts = explode('.', $domain);
730
-		while(count($dparts) > 0) {
731
-			$base2 = 'dc=' . implode(',dc=', $dparts);
730
+		while (count($dparts) > 0) {
731
+			$base2 = 'dc='.implode(',dc=', $dparts);
732 732
 			if ($base !== $base2 && $this->testBaseDN($base2)) {
733 733
 				$this->applyFind('ldap_base', $base2);
734 734
 				return $this->result;
@@ -761,7 +761,7 @@  discard block
 block discarded – undo
761 761
 		$hostInfo = parse_url($host);
762 762
 
763 763
 		//removes Port from Host
764
-		if(is_array($hostInfo) && isset($hostInfo['port'])) {
764
+		if (is_array($hostInfo) && isset($hostInfo['port'])) {
765 765
 			$port = $hostInfo['port'];
766 766
 			$host = str_replace(':'.$port, '', $host);
767 767
 			$this->applyFind('ldap_host', $host);
@@ -778,30 +778,30 @@  discard block
 block discarded – undo
778 778
 	private function detectGroupMemberAssoc() {
779 779
 		$possibleAttrs = array('uniqueMember', 'memberUid', 'member');
780 780
 		$filter = $this->configuration->ldapGroupFilter;
781
-		if(empty($filter)) {
781
+		if (empty($filter)) {
782 782
 			return false;
783 783
 		}
784 784
 		$cr = $this->getConnection();
785
-		if(!$cr) {
785
+		if (!$cr) {
786 786
 			throw new \Exception('Could not connect to LDAP');
787 787
 		}
788 788
 		$base = $this->configuration->ldapBase[0];
789 789
 		$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
790
-		if(!$this->ldap->isResource($rr)) {
790
+		if (!$this->ldap->isResource($rr)) {
791 791
 			return false;
792 792
 		}
793 793
 		$er = $this->ldap->firstEntry($cr, $rr);
794
-		while(is_resource($er)) {
794
+		while (is_resource($er)) {
795 795
 			$this->ldap->getDN($cr, $er);
796 796
 			$attrs = $this->ldap->getAttributes($cr, $er);
797 797
 			$result = array();
798 798
 			$possibleAttrsCount = count($possibleAttrs);
799
-			for($i = 0; $i < $possibleAttrsCount; $i++) {
800
-				if(isset($attrs[$possibleAttrs[$i]])) {
799
+			for ($i = 0; $i < $possibleAttrsCount; $i++) {
800
+				if (isset($attrs[$possibleAttrs[$i]])) {
801 801
 					$result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
802 802
 				}
803 803
 			}
804
-			if(!empty($result)) {
804
+			if (!empty($result)) {
805 805
 				natsort($result);
806 806
 				return key($result);
807 807
 			}
@@ -820,14 +820,14 @@  discard block
 block discarded – undo
820 820
 	 */
821 821
 	private function testBaseDN($base) {
822 822
 		$cr = $this->getConnection();
823
-		if(!$cr) {
823
+		if (!$cr) {
824 824
 			throw new \Exception('Could not connect to LDAP');
825 825
 		}
826 826
 
827 827
 		//base is there, let's validate it. If we search for anything, we should
828 828
 		//get a result set > 0 on a proper base
829 829
 		$rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
830
-		if(!$this->ldap->isResource($rr)) {
830
+		if (!$this->ldap->isResource($rr)) {
831 831
 			$errorNo  = $this->ldap->errno($cr);
832 832
 			$errorMsg = $this->ldap->error($cr);
833 833
 			\OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
@@ -849,11 +849,11 @@  discard block
 block discarded – undo
849 849
 	 */
850 850
 	private function testMemberOf() {
851 851
 		$cr = $this->getConnection();
852
-		if(!$cr) {
852
+		if (!$cr) {
853 853
 			throw new \Exception('Could not connect to LDAP');
854 854
 		}
855 855
 		$result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
856
-		if(is_int($result) &&  $result > 0) {
856
+		if (is_int($result) && $result > 0) {
857 857
 			return true;
858 858
 		}
859 859
 		return false;
@@ -874,40 +874,40 @@  discard block
 block discarded – undo
874 874
 			case self::LFILTER_USER_LIST:
875 875
 				$objcs = $this->configuration->ldapUserFilterObjectclass;
876 876
 				//glue objectclasses
877
-				if(is_array($objcs) && count($objcs) > 0) {
877
+				if (is_array($objcs) && count($objcs) > 0) {
878 878
 					$filter .= '(|';
879
-					foreach($objcs as $objc) {
880
-						$filter .= '(objectclass=' . $objc . ')';
879
+					foreach ($objcs as $objc) {
880
+						$filter .= '(objectclass='.$objc.')';
881 881
 					}
882 882
 					$filter .= ')';
883 883
 					$parts++;
884 884
 				}
885 885
 				//glue group memberships
886
-				if($this->configuration->hasMemberOfFilterSupport) {
886
+				if ($this->configuration->hasMemberOfFilterSupport) {
887 887
 					$cns = $this->configuration->ldapUserFilterGroups;
888
-					if(is_array($cns) && count($cns) > 0) {
888
+					if (is_array($cns) && count($cns) > 0) {
889 889
 						$filter .= '(|';
890 890
 						$cr = $this->getConnection();
891
-						if(!$cr) {
891
+						if (!$cr) {
892 892
 							throw new \Exception('Could not connect to LDAP');
893 893
 						}
894 894
 						$base = $this->configuration->ldapBase[0];
895
-						foreach($cns as $cn) {
896
-							$rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
897
-							if(!$this->ldap->isResource($rr)) {
895
+						foreach ($cns as $cn) {
896
+							$rr = $this->ldap->search($cr, $base, 'cn='.$cn, array('dn', 'primaryGroupToken'));
897
+							if (!$this->ldap->isResource($rr)) {
898 898
 								continue;
899 899
 							}
900 900
 							$er = $this->ldap->firstEntry($cr, $rr);
901 901
 							$attrs = $this->ldap->getAttributes($cr, $er);
902 902
 							$dn = $this->ldap->getDN($cr, $er);
903
-							if(empty($dn)) {
903
+							if (empty($dn)) {
904 904
 								continue;
905 905
 							}
906
-							$filterPart = '(memberof=' . $dn . ')';
907
-							if(isset($attrs['primaryGroupToken'])) {
906
+							$filterPart = '(memberof='.$dn.')';
907
+							if (isset($attrs['primaryGroupToken'])) {
908 908
 								$pgt = $attrs['primaryGroupToken'][0];
909
-								$primaryFilterPart = '(primaryGroupID=' . $pgt .')';
910
-								$filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
909
+								$primaryFilterPart = '(primaryGroupID='.$pgt.')';
910
+								$filterPart = '(|'.$filterPart.$primaryFilterPart.')';
911 911
 							}
912 912
 							$filter .= $filterPart;
913 913
 						}
@@ -916,10 +916,10 @@  discard block
 block discarded – undo
916 916
 					$parts++;
917 917
 				}
918 918
 				//wrap parts in AND condition
919
-				if($parts > 1) {
920
-					$filter = '(&' . $filter . ')';
919
+				if ($parts > 1) {
920
+					$filter = '(&'.$filter.')';
921 921
 				}
922
-				if(empty($filter)) {
922
+				if (empty($filter)) {
923 923
 					$filter = '(objectclass=*)';
924 924
 				}
925 925
 				break;
@@ -927,28 +927,28 @@  discard block
 block discarded – undo
927 927
 			case self::LFILTER_GROUP_LIST:
928 928
 				$objcs = $this->configuration->ldapGroupFilterObjectclass;
929 929
 				//glue objectclasses
930
-				if(is_array($objcs) && count($objcs) > 0) {
930
+				if (is_array($objcs) && count($objcs) > 0) {
931 931
 					$filter .= '(|';
932
-					foreach($objcs as $objc) {
933
-						$filter .= '(objectclass=' . $objc . ')';
932
+					foreach ($objcs as $objc) {
933
+						$filter .= '(objectclass='.$objc.')';
934 934
 					}
935 935
 					$filter .= ')';
936 936
 					$parts++;
937 937
 				}
938 938
 				//glue group memberships
939 939
 				$cns = $this->configuration->ldapGroupFilterGroups;
940
-				if(is_array($cns) && count($cns) > 0) {
940
+				if (is_array($cns) && count($cns) > 0) {
941 941
 					$filter .= '(|';
942 942
 					$base = $this->configuration->ldapBase[0];
943
-					foreach($cns as $cn) {
944
-						$filter .= '(cn=' . $cn . ')';
943
+					foreach ($cns as $cn) {
944
+						$filter .= '(cn='.$cn.')';
945 945
 					}
946 946
 					$filter .= ')';
947 947
 				}
948 948
 				$parts++;
949 949
 				//wrap parts in AND condition
950
-				if($parts > 1) {
951
-					$filter = '(&' . $filter . ')';
950
+				if ($parts > 1) {
951
+					$filter = '(&'.$filter.')';
952 952
 				}
953 953
 				break;
954 954
 
@@ -960,47 +960,47 @@  discard block
 block discarded – undo
960 960
 				$userAttributes = array_change_key_case(array_flip($userAttributes));
961 961
 				$parts = 0;
962 962
 
963
-				if($this->configuration->ldapLoginFilterUsername === '1') {
963
+				if ($this->configuration->ldapLoginFilterUsername === '1') {
964 964
 					$attr = '';
965
-					if(isset($userAttributes['uid'])) {
965
+					if (isset($userAttributes['uid'])) {
966 966
 						$attr = 'uid';
967
-					} else if(isset($userAttributes['samaccountname'])) {
967
+					} else if (isset($userAttributes['samaccountname'])) {
968 968
 						$attr = 'samaccountname';
969
-					} else if(isset($userAttributes['cn'])) {
969
+					} else if (isset($userAttributes['cn'])) {
970 970
 						//fallback
971 971
 						$attr = 'cn';
972 972
 					}
973
-					if(!empty($attr)) {
974
-						$filterUsername = '(' . $attr . $loginpart . ')';
973
+					if (!empty($attr)) {
974
+						$filterUsername = '('.$attr.$loginpart.')';
975 975
 						$parts++;
976 976
 					}
977 977
 				}
978 978
 
979 979
 				$filterEmail = '';
980
-				if($this->configuration->ldapLoginFilterEmail === '1') {
980
+				if ($this->configuration->ldapLoginFilterEmail === '1') {
981 981
 					$filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
982 982
 					$parts++;
983 983
 				}
984 984
 
985 985
 				$filterAttributes = '';
986 986
 				$attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
987
-				if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
987
+				if (is_array($attrsToFilter) && count($attrsToFilter) > 0) {
988 988
 					$filterAttributes = '(|';
989
-					foreach($attrsToFilter as $attribute) {
990
-						$filterAttributes .= '(' . $attribute . $loginpart . ')';
989
+					foreach ($attrsToFilter as $attribute) {
990
+						$filterAttributes .= '('.$attribute.$loginpart.')';
991 991
 					}
992 992
 					$filterAttributes .= ')';
993 993
 					$parts++;
994 994
 				}
995 995
 
996 996
 				$filterLogin = '';
997
-				if($parts > 1) {
997
+				if ($parts > 1) {
998 998
 					$filterLogin = '(|';
999 999
 				}
1000 1000
 				$filterLogin .= $filterUsername;
1001 1001
 				$filterLogin .= $filterEmail;
1002 1002
 				$filterLogin .= $filterAttributes;
1003
-				if($parts > 1) {
1003
+				if ($parts > 1) {
1004 1004
 					$filterLogin .= ')';
1005 1005
 				}
1006 1006
 
@@ -1022,7 +1022,7 @@  discard block
 block discarded – undo
1022 1022
 	 * @throws \Exception
1023 1023
 	 */
1024 1024
 	private function connectAndBind($port = 389, $tls = false, $ncc = false) {
1025
-		if($ncc) {
1025
+		if ($ncc) {
1026 1026
 			//No certificate check
1027 1027
 			//FIXME: undo afterwards
1028 1028
 			putenv('LDAPTLS_REQCERT=never');
@@ -1032,12 +1032,12 @@  discard block
 block discarded – undo
1032 1032
 		\OCP\Util::writeLog('user_ldap', 'Wiz: Checking Host Info ', \OCP\Util::DEBUG);
1033 1033
 		$host = $this->configuration->ldapHost;
1034 1034
 		$hostInfo = parse_url($host);
1035
-		if(!$hostInfo) {
1035
+		if (!$hostInfo) {
1036 1036
 			throw new \Exception($this->l->t('Invalid Host'));
1037 1037
 		}
1038 1038
 		\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1039 1039
 		$cr = $this->ldap->connect($host, $port);
1040
-		if(!is_resource($cr)) {
1040
+		if (!is_resource($cr)) {
1041 1041
 			throw new \Exception($this->l->t('Invalid Host'));
1042 1042
 		}
1043 1043
 
@@ -1048,9 +1048,9 @@  discard block
 block discarded – undo
1048 1048
 		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1049 1049
 
1050 1050
 		try {
1051
-			if($tls) {
1051
+			if ($tls) {
1052 1052
 				$isTlsWorking = @$this->ldap->startTls($cr);
1053
-				if(!$isTlsWorking) {
1053
+				if (!$isTlsWorking) {
1054 1054
 					return false;
1055 1055
 				}
1056 1056
 			}
@@ -1064,20 +1064,20 @@  discard block
 block discarded – undo
1064 1064
 			$errNo = $this->ldap->errno($cr);
1065 1065
 			$error = ldap_error($cr);
1066 1066
 			$this->ldap->unbind($cr);
1067
-		} catch(ServerNotAvailableException $e) {
1067
+		} catch (ServerNotAvailableException $e) {
1068 1068
 			return false;
1069 1069
 		}
1070 1070
 
1071
-		if($login === true) {
1071
+		if ($login === true) {
1072 1072
 			$this->ldap->unbind($cr);
1073
-			if($ncc) {
1073
+			if ($ncc) {
1074 1074
 				throw new \Exception('Certificate cannot be validated.');
1075 1075
 			}
1076
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1076
+			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '.$port.' TLS '.intval($tls), \OCP\Util::DEBUG);
1077 1077
 			return true;
1078 1078
 		}
1079 1079
 
1080
-		if($errNo === -1 || ($errNo === 2 && $ncc)) {
1080
+		if ($errNo === -1 || ($errNo === 2 && $ncc)) {
1081 1081
 			//host, port or TLS wrong
1082 1082
 			return false;
1083 1083
 		} else if ($errNo === 2) {
@@ -1095,8 +1095,8 @@  discard block
 block discarded – undo
1095 1095
 		$agent = $this->configuration->ldapAgentName;
1096 1096
 		$pwd = $this->configuration->ldapAgentPassword;
1097 1097
 
1098
-		return ( (!empty($agent) && !empty($pwd))
1099
-		       || (empty($agent) &&  empty($pwd)));
1098
+		return ((!empty($agent) && !empty($pwd))
1099
+		       || (empty($agent) && empty($pwd)));
1100 1100
 	}
1101 1101
 
1102 1102
 	/**
@@ -1105,9 +1105,9 @@  discard block
 block discarded – undo
1105 1105
 	 */
1106 1106
 	private function checkRequirements($reqs) {
1107 1107
 		$this->checkAgentRequirements();
1108
-		foreach($reqs as $option) {
1108
+		foreach ($reqs as $option) {
1109 1109
 			$value = $this->configuration->$option;
1110
-			if(empty($value)) {
1110
+			if (empty($value)) {
1111 1111
 				return false;
1112 1112
 			}
1113 1113
 		}
@@ -1129,33 +1129,33 @@  discard block
 block discarded – undo
1129 1129
 		$dnRead = array();
1130 1130
 		$foundItems = array();
1131 1131
 		$maxEntries = 0;
1132
-		if(!is_array($this->configuration->ldapBase)
1132
+		if (!is_array($this->configuration->ldapBase)
1133 1133
 		   || !isset($this->configuration->ldapBase[0])) {
1134 1134
 			return false;
1135 1135
 		}
1136 1136
 		$base = $this->configuration->ldapBase[0];
1137 1137
 		$cr = $this->getConnection();
1138
-		if(!$this->ldap->isResource($cr)) {
1138
+		if (!$this->ldap->isResource($cr)) {
1139 1139
 			return false;
1140 1140
 		}
1141 1141
 		$lastFilter = null;
1142
-		if(isset($filters[count($filters)-1])) {
1143
-			$lastFilter = $filters[count($filters)-1];
1142
+		if (isset($filters[count($filters) - 1])) {
1143
+			$lastFilter = $filters[count($filters) - 1];
1144 1144
 		}
1145
-		foreach($filters as $filter) {
1146
-			if($lastFilter === $filter && count($foundItems) > 0) {
1145
+		foreach ($filters as $filter) {
1146
+			if ($lastFilter === $filter && count($foundItems) > 0) {
1147 1147
 				//skip when the filter is a wildcard and results were found
1148 1148
 				continue;
1149 1149
 			}
1150 1150
 			// 20k limit for performance and reason
1151 1151
 			$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1152
-			if(!$this->ldap->isResource($rr)) {
1152
+			if (!$this->ldap->isResource($rr)) {
1153 1153
 				continue;
1154 1154
 			}
1155 1155
 			$entries = $this->ldap->countEntries($cr, $rr);
1156 1156
 			$getEntryFunc = 'firstEntry';
1157
-			if(($entries !== false) && ($entries > 0)) {
1158
-				if(!is_null($maxF) && $entries > $maxEntries) {
1157
+			if (($entries !== false) && ($entries > 0)) {
1158
+				if (!is_null($maxF) && $entries > $maxEntries) {
1159 1159
 					$maxEntries = $entries;
1160 1160
 					$maxF = $filter;
1161 1161
 				}
@@ -1163,13 +1163,13 @@  discard block
 block discarded – undo
1163 1163
 				do {
1164 1164
 					$entry = $this->ldap->$getEntryFunc($cr, $rr);
1165 1165
 					$getEntryFunc = 'nextEntry';
1166
-					if(!$this->ldap->isResource($entry)) {
1166
+					if (!$this->ldap->isResource($entry)) {
1167 1167
 						continue 2;
1168 1168
 					}
1169 1169
 					$rr = $entry; //will be expected by nextEntry next round
1170 1170
 					$attributes = $this->ldap->getAttributes($cr, $entry);
1171 1171
 					$dn = $this->ldap->getDN($cr, $entry);
1172
-					if($dn === false || in_array($dn, $dnRead)) {
1172
+					if ($dn === false || in_array($dn, $dnRead)) {
1173 1173
 						continue;
1174 1174
 					}
1175 1175
 					$newItems = array();
@@ -1180,7 +1180,7 @@  discard block
 block discarded – undo
1180 1180
 					$foundItems = array_merge($foundItems, $newItems);
1181 1181
 					$this->resultCache[$dn][$attr] = $newItems;
1182 1182
 					$dnRead[] = $dn;
1183
-				} while(($state === self::LRESULT_PROCESSED_SKIP
1183
+				} while (($state === self::LRESULT_PROCESSED_SKIP
1184 1184
 						|| $this->ldap->isResource($entry))
1185 1185
 						&& ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1186 1186
 			}
@@ -1203,11 +1203,11 @@  discard block
 block discarded – undo
1203 1203
 	 */
1204 1204
 	private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1205 1205
 		$cr = $this->getConnection();
1206
-		if(!$cr) {
1206
+		if (!$cr) {
1207 1207
 			throw new \Exception('Could not connect to LDAP');
1208 1208
 		}
1209 1209
 		$p = 'objectclass=';
1210
-		foreach($objectclasses as $key => $value) {
1210
+		foreach ($objectclasses as $key => $value) {
1211 1211
 			$objectclasses[$key] = $p.$value;
1212 1212
 		}
1213 1213
 		$maxEntryObjC = '';
@@ -1219,7 +1219,7 @@  discard block
 block discarded – undo
1219 1219
 		$availableFeatures =
1220 1220
 			$this->cumulativeSearchOnAttribute($objectclasses, $attr,
1221 1221
 											   $dig, $maxEntryObjC);
1222
-		if(is_array($availableFeatures)
1222
+		if (is_array($availableFeatures)
1223 1223
 		   && count($availableFeatures) > 0) {
1224 1224
 			natcasesort($availableFeatures);
1225 1225
 			//natcasesort keeps indices, but we must get rid of them for proper
@@ -1230,10 +1230,10 @@  discard block
 block discarded – undo
1230 1230
 		}
1231 1231
 
1232 1232
 		$setFeatures = $this->configuration->$confkey;
1233
-		if(is_array($setFeatures) && !empty($setFeatures)) {
1233
+		if (is_array($setFeatures) && !empty($setFeatures)) {
1234 1234
 			//something is already configured? pre-select it.
1235 1235
 			$this->result->addChange($dbkey, $setFeatures);
1236
-		} else if($po && !empty($maxEntryObjC)) {
1236
+		} else if ($po && !empty($maxEntryObjC)) {
1237 1237
 			//pre-select objectclass with most result entries
1238 1238
 			$maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1239 1239
 			$this->applyFind($dbkey, $maxEntryObjC);
@@ -1252,7 +1252,7 @@  discard block
 block discarded – undo
1252 1252
 	 * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1253 1253
 	 */
1254 1254
 	private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1255
-		if(!is_array($result)
1255
+		if (!is_array($result)
1256 1256
 		   || !isset($result['count'])
1257 1257
 		   || !$result['count'] > 0) {
1258 1258
 			return self::LRESULT_PROCESSED_INVALID;
@@ -1261,12 +1261,12 @@  discard block
 block discarded – undo
1261 1261
 		// strtolower on all keys for proper comparison
1262 1262
 		$result = \OCP\Util::mb_array_change_key_case($result);
1263 1263
 		$attribute = strtolower($attribute);
1264
-		if(isset($result[$attribute])) {
1265
-			foreach($result[$attribute] as $key => $val) {
1266
-				if($key === 'count') {
1264
+		if (isset($result[$attribute])) {
1265
+			foreach ($result[$attribute] as $key => $val) {
1266
+				if ($key === 'count') {
1267 1267
 					continue;
1268 1268
 				}
1269
-				if(!in_array($val, $known)) {
1269
+				if (!in_array($val, $known)) {
1270 1270
 					$known[] = $val;
1271 1271
 				}
1272 1272
 			}
@@ -1280,7 +1280,7 @@  discard block
 block discarded – undo
1280 1280
 	 * @return bool|mixed
1281 1281
 	 */
1282 1282
 	private function getConnection() {
1283
-		if(!is_null($this->cr)) {
1283
+		if (!is_null($this->cr)) {
1284 1284
 			return $this->cr;
1285 1285
 		}
1286 1286
 
@@ -1292,14 +1292,14 @@  discard block
 block discarded – undo
1292 1292
 		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1293 1293
 		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1294 1294
 		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1295
-		if($this->configuration->ldapTLS === 1) {
1295
+		if ($this->configuration->ldapTLS === 1) {
1296 1296
 			$this->ldap->startTls($cr);
1297 1297
 		}
1298 1298
 
1299 1299
 		$lo = @$this->ldap->bind($cr,
1300 1300
 								 $this->configuration->ldapAgentName,
1301 1301
 								 $this->configuration->ldapAgentPassword);
1302
-		if($lo === true) {
1302
+		if ($lo === true) {
1303 1303
 			$this->$cr = $cr;
1304 1304
 			return $cr;
1305 1305
 		}
@@ -1334,14 +1334,14 @@  discard block
 block discarded – undo
1334 1334
 		$portSettings = array();
1335 1335
 
1336 1336
 		//In case the port is already provided, we will check this first
1337
-		if($port > 0) {
1337
+		if ($port > 0) {
1338 1338
 			$hostInfo = parse_url($host);
1339
-			if(!(is_array($hostInfo)
1339
+			if (!(is_array($hostInfo)
1340 1340
 				&& isset($hostInfo['scheme'])
1341 1341
 				&& stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1342 1342
 				$portSettings[] = array('port' => $port, 'tls' => true);
1343 1343
 			}
1344
-			$portSettings[] =array('port' => $port, 'tls' => false);
1344
+			$portSettings[] = array('port' => $port, 'tls' => false);
1345 1345
 		}
1346 1346
 
1347 1347
 		//default ports
Please login to merge, or discard this patch.